VirtualBox

source: vbox/trunk/src/VBox/VMM/PDMQueue.cpp@ 25414

Last change on this file since 25414 was 25015, checked in by vboxsync, 15 years ago

VMM: a few more -Wshadow fixes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 30.5 KB
Line 
1/* $Id: PDMQueue.cpp 25015 2009-11-26 15:27:42Z vboxsync $ */
2/** @file
3 * PDM Queue - Transport data and tasks to EMT and R3.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#define LOG_GROUP LOG_GROUP_PDM_QUEUE
27#include "PDMInternal.h"
28#include <VBox/pdm.h>
29#include <VBox/mm.h>
30#include <VBox/rem.h>
31#include <VBox/vm.h>
32#include <VBox/err.h>
33
34#include <VBox/log.h>
35#include <iprt/asm.h>
36#include <iprt/assert.h>
37#include <iprt/thread.h>
38
39
40/*******************************************************************************
41* Internal Functions *
42*******************************************************************************/
43DECLINLINE(void) pdmR3QueueFree(PPDMQUEUE pQueue, PPDMQUEUEITEMCORE pItem);
44static bool pdmR3QueueFlush(PPDMQUEUE pQueue);
45static DECLCALLBACK(void) pdmR3QueueTimer(PVM pVM, PTMTIMER pTimer, void *pvUser);
46
47
48
49/**
50 * Internal worker for the queue creation apis.
51 *
52 * @returns VBox status.
53 * @param pVM VM handle.
54 * @param cbItem Item size.
55 * @param cItems Number of items.
56 * @param cMilliesInterval Number of milliseconds between polling the queue.
57 * If 0 then the emulation thread will be notified whenever an item arrives.
58 * @param fRZEnabled Set if the queue will be used from RC/R0 and need to be allocated from the hyper heap.
59 * @param pszName The queue name. Unique. Not copied.
60 * @param ppQueue Where to store the queue handle.
61 */
62static int pdmR3QueueCreate(PVM pVM, RTUINT cbItem, RTUINT cItems, uint32_t cMilliesInterval, bool fRZEnabled,
63 const char *pszName, PPDMQUEUE *ppQueue)
64{
65 /*
66 * Validate input.
67 */
68 if (cbItem < sizeof(PDMQUEUEITEMCORE))
69 {
70 AssertMsgFailed(("cbItem=%d\n", cbItem));
71 return VERR_INVALID_PARAMETER;
72 }
73 if (cItems < 1 || cItems >= 0x10000)
74 {
75 AssertMsgFailed(("cItems=%d valid:[1-65535]\n", cItems));
76 return VERR_INVALID_PARAMETER;
77 }
78
79 /*
80 * Align the item size and calculate the structure size.
81 */
82 cbItem = RT_ALIGN(cbItem, sizeof(RTUINTPTR));
83 size_t cb = cbItem * cItems + RT_ALIGN_Z(RT_OFFSETOF(PDMQUEUE, aFreeItems[cItems + PDMQUEUE_FREE_SLACK]), 16);
84 PPDMQUEUE pQueue;
85 int rc;
86 if (fRZEnabled)
87 rc = MMHyperAlloc(pVM, cb, 0, MM_TAG_PDM_QUEUE, (void **)&pQueue );
88 else
89 rc = MMR3HeapAllocZEx(pVM, MM_TAG_PDM_QUEUE, cb, (void **)&pQueue);
90 if (RT_FAILURE(rc))
91 return rc;
92
93 /*
94 * Initialize the data fields.
95 */
96 pQueue->pVMR3 = pVM;
97 pQueue->pVMR0 = fRZEnabled ? pVM->pVMR0 : NIL_RTR0PTR;
98 pQueue->pVMRC = fRZEnabled ? pVM->pVMRC : NIL_RTRCPTR;
99 pQueue->pszName = pszName;
100 pQueue->cMilliesInterval = cMilliesInterval;
101 //pQueue->pTimer = NULL;
102 pQueue->cbItem = cbItem;
103 pQueue->cItems = cItems;
104 //pQueue->pPendingR3 = NULL;
105 //pQueue->pPendingR0 = NULL;
106 //pQueue->pPendingRC = NULL;
107 pQueue->iFreeHead = cItems;
108 //pQueue->iFreeTail = 0;
109 PPDMQUEUEITEMCORE pItem = (PPDMQUEUEITEMCORE)((char *)pQueue + RT_ALIGN_Z(RT_OFFSETOF(PDMQUEUE, aFreeItems[cItems + PDMQUEUE_FREE_SLACK]), 16));
110 for (unsigned i = 0; i < cItems; i++, pItem = (PPDMQUEUEITEMCORE)((char *)pItem + cbItem))
111 {
112 pQueue->aFreeItems[i].pItemR3 = pItem;
113 if (fRZEnabled)
114 {
115 pQueue->aFreeItems[i].pItemR0 = MMHyperR3ToR0(pVM, pItem);
116 pQueue->aFreeItems[i].pItemRC = MMHyperR3ToRC(pVM, pItem);
117 }
118 }
119
120 /*
121 * Create timer?
122 */
123 if (cMilliesInterval)
124 {
125 rc = TMR3TimerCreateInternal(pVM, TMCLOCK_REAL, pdmR3QueueTimer, pQueue, "Queue timer", &pQueue->pTimer);
126 if (RT_SUCCESS(rc))
127 {
128 rc = TMTimerSetMillies(pQueue->pTimer, cMilliesInterval);
129 if (RT_FAILURE(rc))
130 {
131 AssertMsgFailed(("TMTimerSetMillies failed rc=%Rrc\n", rc));
132 int rc2 = TMR3TimerDestroy(pQueue->pTimer); AssertRC(rc2);
133 }
134 }
135 else
136 AssertMsgFailed(("TMR3TimerCreateInternal failed rc=%Rrc\n", rc));
137 if (RT_FAILURE(rc))
138 {
139 if (fRZEnabled)
140 MMHyperFree(pVM, pQueue);
141 else
142 MMR3HeapFree(pQueue);
143 return rc;
144 }
145
146 /*
147 * Insert into the queue list for timer driven queues.
148 */
149 pdmLock(pVM);
150 pQueue->pNext = pVM->pdm.s.pQueuesTimer;
151 pVM->pdm.s.pQueuesTimer = pQueue;
152 pdmUnlock(pVM);
153 }
154 else
155 {
156 /*
157 * Insert into the queue list for forced action driven queues.
158 * This is a FIFO, so insert at the end.
159 */
160 /** @todo we should add a priority to the queues so we don't have to rely on
161 * the initialization order to deal with problems like #1605 (pgm/pcnet deadlock
162 * caused by the critsect queue to be last in the chain).
163 * - Update, the critical sections are no longer using queues, so this isn't a real
164 * problem any longer. The priority might be a nice feature for later though.
165 */
166 pdmLock(pVM);
167 if (!pVM->pdm.s.pQueuesForced)
168 pVM->pdm.s.pQueuesForced = pQueue;
169 else
170 {
171 PPDMQUEUE pPrev = pVM->pdm.s.pQueuesForced;
172 while (pPrev->pNext)
173 pPrev = pPrev->pNext;
174 pPrev->pNext = pQueue;
175 }
176 pdmUnlock(pVM);
177 }
178
179 /*
180 * Register the statistics.
181 */
182 STAMR3RegisterF(pVM, &pQueue->cbItem, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Item size.", "/PDM/Queue/%s/cbItem", pQueue->pszName);
183 STAMR3RegisterF(pVM, &pQueue->cItems, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Queue size.", "/PDM/Queue/%s/cItems", pQueue->pszName);
184 STAMR3RegisterF(pVM, &pQueue->StatAllocFailures, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "PDMQueueAlloc failures.", "/PDM/Queue/%s/AllocFailures", pQueue->pszName);
185 STAMR3RegisterF(pVM, &pQueue->StatInsert, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "Calls to PDMQueueInsert.", "/PDM/Queue/%s/Insert", pQueue->pszName);
186 STAMR3RegisterF(pVM, &pQueue->StatFlush, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "Calls to pdmR3QueueFlush.", "/PDM/Queue/%s/Flush", pQueue->pszName);
187 STAMR3RegisterF(pVM, &pQueue->StatFlushLeftovers, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Left over items after flush.", "/PDM/Queue/%s/FlushLeftovers", pQueue->pszName);
188#ifdef VBOX_WITH_STATISTICS
189 STAMR3RegisterF(pVM, &pQueue->StatFlushPrf, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "Profiling pdmR3QueueFlush.", "/PDM/Queue/%s/FlushPrf", pQueue->pszName);
190 STAMR3RegisterF(pVM, (void *)&pQueue->cStatPending, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Pending items.", "/PDM/Queue/%s/Pending", pQueue->pszName);
191#endif
192
193 *ppQueue = pQueue;
194 return VINF_SUCCESS;
195}
196
197
198/**
199 * Create a queue with a device owner.
200 *
201 * @returns VBox status code.
202 * @param pVM VM handle.
203 * @param pDevIns Device instance.
204 * @param cbItem Size a queue item.
205 * @param cItems Number of items in the queue.
206 * @param cMilliesInterval Number of milliseconds between polling the queue.
207 * If 0 then the emulation thread will be notified whenever an item arrives.
208 * @param pfnCallback The consumer function.
209 * @param fRZEnabled Set if the queue must be usable from RC/R0.
210 * @param pszName The queue name. Unique. Not copied.
211 * @param ppQueue Where to store the queue handle on success.
212 * @thread Emulation thread only.
213 */
214VMMR3DECL(int) PDMR3QueueCreateDevice(PVM pVM, PPDMDEVINS pDevIns, RTUINT cbItem, RTUINT cItems, uint32_t cMilliesInterval,
215 PFNPDMQUEUEDEV pfnCallback, bool fRZEnabled, const char *pszName, PPDMQUEUE *ppQueue)
216{
217 LogFlow(("PDMR3QueueCreateDevice: pDevIns=%p cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p fRZEnabled=%RTbool pszName=%s\n",
218 pDevIns, cbItem, cItems, cMilliesInterval, pfnCallback, fRZEnabled, pszName));
219
220 /*
221 * Validate input.
222 */
223 VMCPU_ASSERT_EMT(&pVM->aCpus[0]);
224 if (!pfnCallback)
225 {
226 AssertMsgFailed(("No consumer callback!\n"));
227 return VERR_INVALID_PARAMETER;
228 }
229
230 /*
231 * Create the queue.
232 */
233 PPDMQUEUE pQueue;
234 int rc = pdmR3QueueCreate(pVM, cbItem, cItems, cMilliesInterval, fRZEnabled, pszName, &pQueue);
235 if (RT_SUCCESS(rc))
236 {
237 pQueue->enmType = PDMQUEUETYPE_DEV;
238 pQueue->u.Dev.pDevIns = pDevIns;
239 pQueue->u.Dev.pfnCallback = pfnCallback;
240
241 *ppQueue = pQueue;
242 Log(("PDM: Created device queue %p; cbItem=%d cItems=%d cMillies=%d pfnCallback=%p pDevIns=%p\n",
243 cbItem, cItems, cMilliesInterval, pfnCallback, pDevIns));
244 }
245 return rc;
246}
247
248
249/**
250 * Create a queue with a driver owner.
251 *
252 * @returns VBox status code.
253 * @param pVM VM handle.
254 * @param pDrvIns Driver instance.
255 * @param cbItem Size a queue item.
256 * @param cItems Number of items in the queue.
257 * @param cMilliesInterval Number of milliseconds between polling the queue.
258 * If 0 then the emulation thread will be notified whenever an item arrives.
259 * @param pfnCallback The consumer function.
260 * @param pszName The queue name. Unique. Not copied.
261 * @param ppQueue Where to store the queue handle on success.
262 * @thread Emulation thread only.
263 */
264VMMR3DECL(int) PDMR3QueueCreateDriver(PVM pVM, PPDMDRVINS pDrvIns, RTUINT cbItem, RTUINT cItems, uint32_t cMilliesInterval,
265 PFNPDMQUEUEDRV pfnCallback, const char *pszName, PPDMQUEUE *ppQueue)
266{
267 LogFlow(("PDMR3QueueCreateDriver: pDrvIns=%p cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p pszName=%s\n",
268 pDrvIns, cbItem, cItems, cMilliesInterval, pfnCallback, pszName));
269
270 /*
271 * Validate input.
272 */
273 VMCPU_ASSERT_EMT(&pVM->aCpus[0]);
274 if (!pfnCallback)
275 {
276 AssertMsgFailed(("No consumer callback!\n"));
277 return VERR_INVALID_PARAMETER;
278 }
279
280 /*
281 * Create the queue.
282 */
283 PPDMQUEUE pQueue;
284 int rc = pdmR3QueueCreate(pVM, cbItem, cItems, cMilliesInterval, false, pszName, &pQueue);
285 if (RT_SUCCESS(rc))
286 {
287 pQueue->enmType = PDMQUEUETYPE_DRV;
288 pQueue->u.Drv.pDrvIns = pDrvIns;
289 pQueue->u.Drv.pfnCallback = pfnCallback;
290
291 *ppQueue = pQueue;
292 Log(("PDM: Created driver queue %p; cbItem=%d cItems=%d cMillies=%d pfnCallback=%p pDrvIns=%p\n",
293 cbItem, cItems, cMilliesInterval, pfnCallback, pDrvIns));
294 }
295 return rc;
296}
297
298
299/**
300 * Create a queue with an internal owner.
301 *
302 * @returns VBox status code.
303 * @param pVM VM handle.
304 * @param cbItem Size a queue item.
305 * @param cItems Number of items in the queue.
306 * @param cMilliesInterval Number of milliseconds between polling the queue.
307 * If 0 then the emulation thread will be notified whenever an item arrives.
308 * @param pfnCallback The consumer function.
309 * @param fRZEnabled Set if the queue must be usable from RC/R0.
310 * @param pszName The queue name. Unique. Not copied.
311 * @param ppQueue Where to store the queue handle on success.
312 * @thread Emulation thread only.
313 */
314VMMR3DECL(int) PDMR3QueueCreateInternal(PVM pVM, RTUINT cbItem, RTUINT cItems, uint32_t cMilliesInterval,
315 PFNPDMQUEUEINT pfnCallback, bool fRZEnabled, const char *pszName, PPDMQUEUE *ppQueue)
316{
317 LogFlow(("PDMR3QueueCreateInternal: cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p fRZEnabled=%RTbool pszName=%s\n",
318 cbItem, cItems, cMilliesInterval, pfnCallback, fRZEnabled, pszName));
319
320 /*
321 * Validate input.
322 */
323 VMCPU_ASSERT_EMT(&pVM->aCpus[0]);
324 if (!pfnCallback)
325 {
326 AssertMsgFailed(("No consumer callback!\n"));
327 return VERR_INVALID_PARAMETER;
328 }
329
330 /*
331 * Create the queue.
332 */
333 PPDMQUEUE pQueue;
334 int rc = pdmR3QueueCreate(pVM, cbItem, cItems, cMilliesInterval, fRZEnabled, pszName, &pQueue);
335 if (RT_SUCCESS(rc))
336 {
337 pQueue->enmType = PDMQUEUETYPE_INTERNAL;
338 pQueue->u.Int.pfnCallback = pfnCallback;
339
340 *ppQueue = pQueue;
341 Log(("PDM: Created internal queue %p; cbItem=%d cItems=%d cMillies=%d pfnCallback=%p\n",
342 cbItem, cItems, cMilliesInterval, pfnCallback));
343 }
344 return rc;
345}
346
347
348/**
349 * Create a queue with an external owner.
350 *
351 * @returns VBox status code.
352 * @param pVM VM handle.
353 * @param cbItem Size a queue item.
354 * @param cItems Number of items in the queue.
355 * @param cMilliesInterval Number of milliseconds between polling the queue.
356 * If 0 then the emulation thread will be notified whenever an item arrives.
357 * @param pfnCallback The consumer function.
358 * @param pvUser The user argument to the consumer function.
359 * @param pszName The queue name. Unique. Not copied.
360 * @param ppQueue Where to store the queue handle on success.
361 * @thread Emulation thread only.
362 */
363VMMR3DECL(int) PDMR3QueueCreateExternal(PVM pVM, RTUINT cbItem, RTUINT cItems, uint32_t cMilliesInterval, PFNPDMQUEUEEXT pfnCallback, void *pvUser, const char *pszName, PPDMQUEUE *ppQueue)
364{
365 LogFlow(("PDMR3QueueCreateExternal: cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p pszName=%s\n", cbItem, cItems, cMilliesInterval, pfnCallback, pszName));
366
367 /*
368 * Validate input.
369 */
370 VMCPU_ASSERT_EMT(&pVM->aCpus[0]);
371 if (!pfnCallback)
372 {
373 AssertMsgFailed(("No consumer callback!\n"));
374 return VERR_INVALID_PARAMETER;
375 }
376
377 /*
378 * Create the queue.
379 */
380 PPDMQUEUE pQueue;
381 int rc = pdmR3QueueCreate(pVM, cbItem, cItems, cMilliesInterval, false, pszName, &pQueue);
382 if (RT_SUCCESS(rc))
383 {
384 pQueue->enmType = PDMQUEUETYPE_EXTERNAL;
385 pQueue->u.Ext.pvUser = pvUser;
386 pQueue->u.Ext.pfnCallback = pfnCallback;
387
388 *ppQueue = pQueue;
389 Log(("PDM: Created external queue %p; cbItem=%d cItems=%d cMillies=%d pfnCallback=%p pvUser=%p\n",
390 cbItem, cItems, cMilliesInterval, pfnCallback, pvUser));
391 }
392 return rc;
393}
394
395
396/**
397 * Destroy a queue.
398 *
399 * @returns VBox status code.
400 * @param pQueue Queue to destroy.
401 * @thread Emulation thread only.
402 */
403VMMR3DECL(int) PDMR3QueueDestroy(PPDMQUEUE pQueue)
404{
405 LogFlow(("PDMR3QueueDestroy: pQueue=%p\n", pQueue));
406
407 /*
408 * Validate input.
409 */
410 if (!pQueue)
411 return VERR_INVALID_PARAMETER;
412 Assert(pQueue && pQueue->pVMR3);
413 PVM pVM = pQueue->pVMR3;
414
415 pdmLock(pVM);
416
417 /*
418 * Unlink it.
419 */
420 if (pQueue->pTimer)
421 {
422 if (pVM->pdm.s.pQueuesTimer != pQueue)
423 {
424 PPDMQUEUE pCur = pVM->pdm.s.pQueuesTimer;
425 while (pCur)
426 {
427 if (pCur->pNext == pQueue)
428 {
429 pCur->pNext = pQueue->pNext;
430 break;
431 }
432 pCur = pCur->pNext;
433 }
434 AssertMsg(pCur, ("Didn't find the queue!\n"));
435 }
436 else
437 pVM->pdm.s.pQueuesTimer = pQueue->pNext;
438 }
439 else
440 {
441 if (pVM->pdm.s.pQueuesForced != pQueue)
442 {
443 PPDMQUEUE pCur = pVM->pdm.s.pQueuesForced;
444 while (pCur)
445 {
446 if (pCur->pNext == pQueue)
447 {
448 pCur->pNext = pQueue->pNext;
449 break;
450 }
451 pCur = pCur->pNext;
452 }
453 AssertMsg(pCur, ("Didn't find the queue!\n"));
454 }
455 else
456 pVM->pdm.s.pQueuesForced = pQueue->pNext;
457 }
458 pQueue->pNext = NULL;
459 pQueue->pVMR3 = NULL;
460 pdmUnlock(pVM);
461
462 /*
463 * Deregister statistics.
464 */
465 STAMR3Deregister(pVM, &pQueue->cbItem);
466 STAMR3Deregister(pVM, &pQueue->cbItem);
467 STAMR3Deregister(pVM, &pQueue->StatAllocFailures);
468 STAMR3Deregister(pVM, &pQueue->StatInsert);
469 STAMR3Deregister(pVM, &pQueue->StatFlush);
470 STAMR3Deregister(pVM, &pQueue->StatFlushLeftovers);
471#ifdef VBOX_WITH_STATISTICS
472 STAMR3Deregister(pVM, &pQueue->StatFlushPrf);
473 STAMR3Deregister(pVM, (void *)&pQueue->cStatPending);
474#endif
475
476 /*
477 * Destroy the timer and free it.
478 */
479 if (pQueue->pTimer)
480 {
481 TMR3TimerDestroy(pQueue->pTimer);
482 pQueue->pTimer = NULL;
483 }
484 if (pQueue->pVMRC)
485 {
486 pQueue->pVMRC = NIL_RTRCPTR;
487 pQueue->pVMR0 = NIL_RTR0PTR;
488 MMHyperFree(pVM, pQueue);
489 }
490 else
491 MMR3HeapFree(pQueue);
492
493 return VINF_SUCCESS;
494}
495
496
497/**
498 * Destroy a all queues owned by the specified device.
499 *
500 * @returns VBox status code.
501 * @param pVM VM handle.
502 * @param pDevIns Device instance.
503 * @thread Emulation thread only.
504 */
505VMMR3DECL(int) PDMR3QueueDestroyDevice(PVM pVM, PPDMDEVINS pDevIns)
506{
507 LogFlow(("PDMR3QueueDestroyDevice: pDevIns=%p\n", pDevIns));
508
509 /*
510 * Validate input.
511 */
512 if (!pDevIns)
513 return VERR_INVALID_PARAMETER;
514
515 pdmLock(pVM);
516
517 /*
518 * Unlink it.
519 */
520 PPDMQUEUE pQueueNext = pVM->pdm.s.pQueuesTimer;
521 PPDMQUEUE pQueue = pVM->pdm.s.pQueuesForced;
522 do
523 {
524 while (pQueue)
525 {
526 if ( pQueue->enmType == PDMQUEUETYPE_DEV
527 && pQueue->u.Dev.pDevIns == pDevIns)
528 {
529 PPDMQUEUE pQueueDestroy = pQueue;
530 pQueue = pQueue->pNext;
531 int rc = PDMR3QueueDestroy(pQueueDestroy);
532 AssertRC(rc);
533 }
534 else
535 pQueue = pQueue->pNext;
536 }
537
538 /* next queue list */
539 pQueue = pQueueNext;
540 pQueueNext = NULL;
541 } while (pQueue);
542
543 pdmUnlock(pVM);
544 return VINF_SUCCESS;
545}
546
547
548/**
549 * Destroy a all queues owned by the specified driver.
550 *
551 * @returns VBox status code.
552 * @param pVM VM handle.
553 * @param pDrvIns Driver instance.
554 * @thread Emulation thread only.
555 */
556VMMR3DECL(int) PDMR3QueueDestroyDriver(PVM pVM, PPDMDRVINS pDrvIns)
557{
558 LogFlow(("PDMR3QueueDestroyDriver: pDrvIns=%p\n", pDrvIns));
559
560 /*
561 * Validate input.
562 */
563 if (!pDrvIns)
564 return VERR_INVALID_PARAMETER;
565
566 pdmLock(pVM);
567
568 /*
569 * Unlink it.
570 */
571 PPDMQUEUE pQueueNext = pVM->pdm.s.pQueuesTimer;
572 PPDMQUEUE pQueue = pVM->pdm.s.pQueuesForced;
573 do
574 {
575 while (pQueue)
576 {
577 if ( pQueue->enmType == PDMQUEUETYPE_DRV
578 && pQueue->u.Drv.pDrvIns == pDrvIns)
579 {
580 PPDMQUEUE pQueueDestroy = pQueue;
581 pQueue = pQueue->pNext;
582 int rc = PDMR3QueueDestroy(pQueueDestroy);
583 AssertRC(rc);
584 }
585 else
586 pQueue = pQueue->pNext;
587 }
588
589 /* next queue list */
590 pQueue = pQueueNext;
591 pQueueNext = NULL;
592 } while (pQueue);
593
594 pdmUnlock(pVM);
595 return VINF_SUCCESS;
596}
597
598
599/**
600 * Relocate the queues.
601 *
602 * @param pVM The VM handle.
603 * @param offDelta The relocation delta.
604 */
605void pdmR3QueueRelocate(PVM pVM, RTGCINTPTR offDelta)
606{
607 /*
608 * Process the queues.
609 */
610 PPDMQUEUE pQueueNext = pVM->pdm.s.pQueuesTimer;
611 PPDMQUEUE pQueue = pVM->pdm.s.pQueuesForced;
612 do
613 {
614 while (pQueue)
615 {
616 if (pQueue->pVMRC)
617 {
618 pQueue->pVMRC = pVM->pVMRC;
619
620 /* Pending RC items. */
621 if (pQueue->pPendingRC)
622 {
623 pQueue->pPendingRC += offDelta;
624 PPDMQUEUEITEMCORE pCur = (PPDMQUEUEITEMCORE)MMHyperRCToR3(pVM, pQueue->pPendingRC);
625 while (pCur->pNextRC)
626 {
627 pCur->pNextRC += offDelta;
628 pCur = (PPDMQUEUEITEMCORE)MMHyperRCToR3(pVM, pCur->pNextRC);
629 }
630 }
631
632 /* The free items. */
633 uint32_t i = pQueue->iFreeTail;
634 while (i != pQueue->iFreeHead)
635 {
636 pQueue->aFreeItems[i].pItemRC = MMHyperR3ToRC(pVM, pQueue->aFreeItems[i].pItemR3);
637 i = (i + 1) % (pQueue->cItems + PDMQUEUE_FREE_SLACK);
638 }
639 }
640
641 /* next queue */
642 pQueue = pQueue->pNext;
643 }
644
645 /* next queue list */
646 pQueue = pQueueNext;
647 pQueueNext = NULL;
648 } while (pQueue);
649}
650
651
652/**
653 * Flush pending queues.
654 * This is a forced action callback.
655 *
656 * @param pVM VM handle.
657 * @thread Emulation thread only.
658 */
659VMMR3DECL(void) PDMR3QueueFlushAll(PVM pVM)
660{
661 VM_ASSERT_EMT(pVM);
662 LogFlow(("PDMR3QueuesFlush:\n"));
663
664 /*
665 * Only let one EMT flushing queues at any one time to queue preserve order
666 * and to avoid wasting time. The FF is always cleared here, because it's
667 * only used to get someones attention. Queue inserts occuring during the
668 * flush are caught using the pending bit.
669 *
670 * Note. The order in which the FF and pending bit are set and cleared is important.
671 */
672 VM_FF_CLEAR(pVM, VM_FF_PDM_QUEUES);
673 if (!ASMAtomicBitTestAndSet(&pVM->pdm.s.fQueueFlushing, PDM_QUEUE_FLUSH_FLAG_ACTIVE_BIT))
674 {
675 ASMAtomicBitClear(&pVM->pdm.s.fQueueFlushing, PDM_QUEUE_FLUSH_FLAG_PENDING_BIT);
676 do
677 {
678 VM_FF_CLEAR(pVM, VM_FF_PDM_QUEUES);
679 for (PPDMQUEUE pCur = pVM->pdm.s.pQueuesForced; pCur; pCur = pCur->pNext)
680 if ( pCur->pPendingR3
681 || pCur->pPendingR0
682 || pCur->pPendingRC)
683 pdmR3QueueFlush(pCur);
684 } while ( ASMAtomicBitTestAndClear(&pVM->pdm.s.fQueueFlushing, PDM_QUEUE_FLUSH_FLAG_PENDING_BIT)
685 || VM_FF_ISPENDING(pVM, VM_FF_PDM_QUEUES));
686
687 ASMAtomicBitClear(&pVM->pdm.s.fQueueFlushing, PDM_QUEUE_FLUSH_FLAG_ACTIVE_BIT);
688 }
689}
690
691
692/**
693 * Process pending items in one queue.
694 *
695 * @returns Success indicator.
696 * If false the item the consumer said "enough!".
697 * @param pQueue The queue.
698 */
699static bool pdmR3QueueFlush(PPDMQUEUE pQueue)
700{
701 STAM_PROFILE_START(&pQueue->StatFlushPrf,p);
702
703 /*
704 * Get the lists.
705 */
706 PPDMQUEUEITEMCORE pItems = (PPDMQUEUEITEMCORE)ASMAtomicXchgPtr((void * volatile *)&pQueue->pPendingR3, NULL);
707 RTRCPTR pItemsRC = ASMAtomicXchgRCPtr(&pQueue->pPendingRC, NIL_RTRCPTR);
708 RTR0PTR pItemsR0 = ASMAtomicXchgR0Ptr(&pQueue->pPendingR0, NIL_RTR0PTR);
709
710 if ( !pItems
711 && !pItemsRC
712 && !pItemsR0)
713 /* Somebody may be racing us ... never mind. */
714 return true;
715
716 /*
717 * Reverse the list (it's inserted in LIFO order to avoid semaphores, remember).
718 */
719 PPDMQUEUEITEMCORE pCur = pItems;
720 pItems = NULL;
721 while (pCur)
722 {
723 PPDMQUEUEITEMCORE pInsert = pCur;
724 pCur = pCur->pNextR3;
725 pInsert->pNextR3 = pItems;
726 pItems = pInsert;
727 }
728
729 /*
730 * Do the same for any pending RC items.
731 */
732 while (pItemsRC)
733 {
734 PPDMQUEUEITEMCORE pInsert = (PPDMQUEUEITEMCORE)MMHyperRCToR3(pQueue->pVMR3, pItemsRC);
735 pItemsRC = pInsert->pNextRC;
736 pInsert->pNextRC = NIL_RTRCPTR;
737 pInsert->pNextR3 = pItems;
738 pItems = pInsert;
739 }
740
741 /*
742 * Do the same for any pending R0 items.
743 */
744 while (pItemsR0)
745 {
746 PPDMQUEUEITEMCORE pInsert = (PPDMQUEUEITEMCORE)MMHyperR0ToR3(pQueue->pVMR3, pItemsR0);
747 pItemsR0 = pInsert->pNextR0;
748 pInsert->pNextR0 = NIL_RTR0PTR;
749 pInsert->pNextR3 = pItems;
750 pItems = pInsert;
751 }
752
753 /*
754 * Feed the items to the consumer function.
755 */
756 Log2(("pdmR3QueueFlush: pQueue=%p enmType=%d pItems=%p\n", pQueue, pQueue->enmType, pItems));
757 switch (pQueue->enmType)
758 {
759 case PDMQUEUETYPE_DEV:
760 while (pItems)
761 {
762 if (!pQueue->u.Dev.pfnCallback(pQueue->u.Dev.pDevIns, pItems))
763 break;
764 pCur = pItems;
765 pItems = pItems->pNextR3;
766 pdmR3QueueFree(pQueue, pCur);
767 }
768 break;
769
770 case PDMQUEUETYPE_DRV:
771 while (pItems)
772 {
773 if (!pQueue->u.Drv.pfnCallback(pQueue->u.Drv.pDrvIns, pItems))
774 break;
775 pCur = pItems;
776 pItems = pItems->pNextR3;
777 pdmR3QueueFree(pQueue, pCur);
778 }
779 break;
780
781 case PDMQUEUETYPE_INTERNAL:
782 while (pItems)
783 {
784 if (!pQueue->u.Int.pfnCallback(pQueue->pVMR3, pItems))
785 break;
786 pCur = pItems;
787 pItems = pItems->pNextR3;
788 pdmR3QueueFree(pQueue, pCur);
789 }
790 break;
791
792 case PDMQUEUETYPE_EXTERNAL:
793 while (pItems)
794 {
795 if (!pQueue->u.Ext.pfnCallback(pQueue->u.Ext.pvUser, pItems))
796 break;
797 pCur = pItems;
798 pItems = pItems->pNextR3;
799 pdmR3QueueFree(pQueue, pCur);
800 }
801 break;
802
803 default:
804 AssertMsgFailed(("Invalid queue type %d\n", pQueue->enmType));
805 break;
806 }
807
808 /*
809 * Success?
810 */
811 if (pItems)
812 {
813 /*
814 * Reverse the list.
815 */
816 pCur = pItems;
817 pItems = NULL;
818 while (pCur)
819 {
820 PPDMQUEUEITEMCORE pInsert = pCur;
821 pCur = pInsert->pNextR3;
822 pInsert->pNextR3 = pItems;
823 pItems = pInsert;
824 }
825
826 /*
827 * Insert the list at the tail of the pending list.
828 */
829 for (;;)
830 {
831 if (ASMAtomicCmpXchgPtr((void * volatile *)&pQueue->pPendingR3, pItems, NULL))
832 break;
833 PPDMQUEUEITEMCORE pPending = (PPDMQUEUEITEMCORE)ASMAtomicXchgPtr((void * volatile *)&pQueue->pPendingR3, NULL);
834 if (pPending)
835 {
836 pCur = pPending;
837 while (pCur->pNextR3)
838 pCur = pCur->pNextR3;
839 pCur->pNextR3 = pItems;
840 pItems = pPending;
841 }
842 }
843
844 STAM_REL_COUNTER_INC(&pQueue->StatFlushLeftovers);
845 STAM_PROFILE_STOP(&pQueue->StatFlushPrf,p);
846 return false;
847 }
848
849 STAM_PROFILE_STOP(&pQueue->StatFlushPrf,p);
850 return true;
851}
852
853
854/**
855 * This is a worker function used by PDMQueueFlush to perform the
856 * flush in ring-3.
857 *
858 * The queue which should be flushed is pointed to by either pQueueFlushRC,
859 * pQueueFlushR0, or pQueue. This function will flush that queue and recalc
860 * the queue FF.
861 *
862 * @param pVM The VM handle.
863 * @param pQueue The queue to flush. Only used in Ring-3.
864 */
865VMMR3DECL(void) PDMR3QueueFlushWorker(PVM pVM, PPDMQUEUE pQueue)
866{
867 Assert(pVM->pdm.s.pQueueFlushR0 || pVM->pdm.s.pQueueFlushRC || pQueue);
868 VM_ASSERT_EMT(pVM);
869
870 /** @todo This will clash with PDMR3QueueFlushAll (guest SMP)! */
871 Assert(!(pVM->pdm.s.fQueueFlushing & PDM_QUEUE_FLUSH_FLAG_ACTIVE));
872
873 /*
874 * Flush the queue.
875 */
876 if (!pQueue && pVM->pdm.s.pQueueFlushRC)
877 {
878 pQueue = (PPDMQUEUE)MMHyperRCToR3(pVM, pVM->pdm.s.pQueueFlushRC);
879 pVM->pdm.s.pQueueFlushRC = NIL_RTRCPTR;
880 }
881 else if (!pQueue && pVM->pdm.s.pQueueFlushR0)
882 {
883 pQueue = (PPDMQUEUE)MMHyperR0ToR3(pVM, pVM->pdm.s.pQueueFlushR0);
884 pVM->pdm.s.pQueueFlushR0 = NIL_RTR0PTR;
885 }
886 Assert(!pVM->pdm.s.pQueueFlushR0 && !pVM->pdm.s.pQueueFlushRC);
887
888 if ( !pQueue
889 || pdmR3QueueFlush(pQueue))
890 {
891 /*
892 * Recalc the FF (for the queues using force action).
893 */
894 VM_FF_CLEAR(pVM, VM_FF_PDM_QUEUES);
895 for (pQueue = pVM->pdm.s.pQueuesForced; pQueue; pQueue = pQueue->pNext)
896 if ( pQueue->pPendingRC
897 || pQueue->pPendingR0
898 || pQueue->pPendingR3)
899 {
900 VM_FF_SET(pVM, VM_FF_PDM_QUEUES);
901 break;
902 }
903 }
904}
905
906
907/**
908 * Free an item.
909 *
910 * @param pQueue The queue.
911 * @param pItem The item.
912 */
913DECLINLINE(void) pdmR3QueueFree(PPDMQUEUE pQueue, PPDMQUEUEITEMCORE pItem)
914{
915 VM_ASSERT_EMT(pQueue->pVMR3);
916
917 int i = pQueue->iFreeHead;
918 int iNext = (i + 1) % (pQueue->cItems + PDMQUEUE_FREE_SLACK);
919
920 pQueue->aFreeItems[i].pItemR3 = pItem;
921 if (pQueue->pVMRC)
922 {
923 pQueue->aFreeItems[i].pItemRC = MMHyperR3ToRC(pQueue->pVMR3, pItem);
924 pQueue->aFreeItems[i].pItemR0 = MMHyperR3ToR0(pQueue->pVMR3, pItem);
925 }
926
927 if (!ASMAtomicCmpXchgU32(&pQueue->iFreeHead, iNext, i))
928 AssertMsgFailed(("huh? i=%d iNext=%d iFreeHead=%d iFreeTail=%d\n", i, iNext, pQueue->iFreeHead, pQueue->iFreeTail));
929 STAM_STATS({ ASMAtomicDecU32(&pQueue->cStatPending); });
930}
931
932
933/**
934 * Timer handler for PDM queues.
935 * This is called by for a single queue.
936 *
937 * @param pVM VM handle.
938 * @param pTimer Pointer to timer.
939 * @param pvUser Pointer to the queue.
940 */
941static DECLCALLBACK(void) pdmR3QueueTimer(PVM pVM, PTMTIMER pTimer, void *pvUser)
942{
943 PPDMQUEUE pQueue = (PPDMQUEUE)pvUser;
944 Assert(pTimer == pQueue->pTimer); NOREF(pTimer);
945
946 if ( pQueue->pPendingR3
947 || pQueue->pPendingR0
948 || pQueue->pPendingRC)
949 pdmR3QueueFlush(pQueue);
950 int rc = TMTimerSetMillies(pQueue->pTimer, pQueue->cMilliesInterval);
951 AssertRC(rc);
952}
953
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use