VirtualBox

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

Last change on this file since 20774 was 20088, checked in by vboxsync, 16 years ago

PDMQueue.cpp: todo for fixing assertion during DSL shutdown (smp).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 26.5 KB
Line 
1/* $Id: PDMQueue.cpp 20088 2009-05-27 14:39: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 ppQueue Where to store the queue handle.
60 */
61static int pdmR3QueueCreate(PVM pVM, RTUINT cbItem, RTUINT cItems, uint32_t cMilliesInterval, bool fRZEnabled, PPDMQUEUE *ppQueue)
62{
63 /*
64 * Validate input.
65 */
66 if (cbItem < sizeof(PDMQUEUEITEMCORE))
67 {
68 AssertMsgFailed(("cbItem=%d\n", cbItem));
69 return VERR_INVALID_PARAMETER;
70 }
71 if (cItems < 1 || cItems >= 0x10000)
72 {
73 AssertMsgFailed(("cItems=%d valid:[1-65535]\n", cItems));
74 return VERR_INVALID_PARAMETER;
75 }
76
77 /*
78 * Align the item size and calculate the structure size.
79 */
80 cbItem = RT_ALIGN(cbItem, sizeof(RTUINTPTR));
81 size_t cb = cbItem * cItems + RT_ALIGN_Z(RT_OFFSETOF(PDMQUEUE, aFreeItems[cItems + PDMQUEUE_FREE_SLACK]), 16);
82 PPDMQUEUE pQueue;
83 int rc;
84 if (fRZEnabled)
85 rc = MMHyperAlloc(pVM, cb, 0, MM_TAG_PDM_QUEUE, (void **)&pQueue );
86 else
87 rc = MMR3HeapAllocZEx(pVM, MM_TAG_PDM_QUEUE, cb, (void **)&pQueue);
88 if (RT_FAILURE(rc))
89 return rc;
90
91 /*
92 * Initialize the data fields.
93 */
94 pQueue->pVMR3 = pVM;
95 pQueue->pVMR0 = fRZEnabled ? pVM->pVMR0 : NIL_RTR0PTR;
96 pQueue->pVMRC = fRZEnabled ? pVM->pVMRC : NIL_RTRCPTR;
97 pQueue->cMilliesInterval = cMilliesInterval;
98 //pQueue->pTimer = NULL;
99 pQueue->cbItem = cbItem;
100 pQueue->cItems = cItems;
101 //pQueue->pPendingR3 = NULL;
102 //pQueue->pPendingR0 = NULL;
103 //pQueue->pPendingRC = NULL;
104 pQueue->iFreeHead = cItems;
105 //pQueue->iFreeTail = 0;
106 PPDMQUEUEITEMCORE pItem = (PPDMQUEUEITEMCORE)((char *)pQueue + RT_ALIGN_Z(RT_OFFSETOF(PDMQUEUE, aFreeItems[cItems + PDMQUEUE_FREE_SLACK]), 16));
107 for (unsigned i = 0; i < cItems; i++, pItem = (PPDMQUEUEITEMCORE)((char *)pItem + cbItem))
108 {
109 pQueue->aFreeItems[i].pItemR3 = pItem;
110 if (fRZEnabled)
111 {
112 pQueue->aFreeItems[i].pItemR0 = MMHyperR3ToR0(pVM, pItem);
113 pQueue->aFreeItems[i].pItemRC = MMHyperR3ToRC(pVM, pItem);
114 }
115 }
116
117 /*
118 * Create timer?
119 */
120 if (cMilliesInterval)
121 {
122 int rc = TMR3TimerCreateInternal(pVM, TMCLOCK_REAL, pdmR3QueueTimer, pQueue, "Queue timer", &pQueue->pTimer);
123 if (RT_SUCCESS(rc))
124 {
125 rc = TMTimerSetMillies(pQueue->pTimer, cMilliesInterval);
126 if (RT_FAILURE(rc))
127 {
128 AssertMsgFailed(("TMTimerSetMillies failed rc=%Rrc\n", rc));
129 int rc2 = TMR3TimerDestroy(pQueue->pTimer); AssertRC(rc2);
130 }
131 }
132 else
133 AssertMsgFailed(("TMR3TimerCreateInternal failed rc=%Rrc\n", rc));
134 if (RT_FAILURE(rc))
135 {
136 if (fRZEnabled)
137 MMHyperFree(pVM, pQueue);
138 else
139 MMR3HeapFree(pQueue);
140 return rc;
141 }
142
143 /*
144 * Insert into the queue list for timer driven queues.
145 */
146 pQueue->pNext = pVM->pdm.s.pQueuesTimer;
147 pVM->pdm.s.pQueuesTimer = pQueue;
148 }
149 else
150 {
151 /*
152 * Insert into the queue list for forced action driven queues.
153 * This is a FIFO, so insert at the end.
154 */
155 /** @todo we should add a priority priority to the queues so we don't have to rely on
156 * the initialization order to deal with problems like #1605 (pgm/pcnet deadlock
157 * caused by the critsect queue to be last in the chain).
158 * - Update, the critical sections are no longer using queues, so this isn't a real
159 * problem any longer. The priority might be a nice feature for later though.
160 */
161 if (!pVM->pdm.s.pQueuesForced)
162 pVM->pdm.s.pQueuesForced = pQueue;
163 else
164 {
165 PPDMQUEUE pPrev = pVM->pdm.s.pQueuesForced;
166 while (pPrev->pNext)
167 pPrev = pPrev->pNext;
168 pPrev->pNext = pQueue;
169 }
170 }
171
172 *ppQueue = pQueue;
173 return VINF_SUCCESS;
174}
175
176
177/**
178 * Create a queue with a device owner.
179 *
180 * @returns VBox status code.
181 * @param pVM VM handle.
182 * @param pDevIns Device instance.
183 * @param cbItem Size a queue item.
184 * @param cItems Number of items in the queue.
185 * @param cMilliesInterval Number of milliseconds between polling the queue.
186 * If 0 then the emulation thread will be notified whenever an item arrives.
187 * @param pfnCallback The consumer function.
188 * @param fRZEnabled Set if the queue must be usable from RC/R0.
189 * @param ppQueue Where to store the queue handle on success.
190 * @thread Emulation thread only.
191 */
192VMMR3DECL(int) PDMR3QueueCreateDevice(PVM pVM, PPDMDEVINS pDevIns, RTUINT cbItem, RTUINT cItems, uint32_t cMilliesInterval,
193 PFNPDMQUEUEDEV pfnCallback, bool fRZEnabled, PPDMQUEUE *ppQueue)
194{
195 LogFlow(("PDMR3QueueCreateDevice: pDevIns=%p cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p fRZEnabled=%RTbool\n",
196 pDevIns, cbItem, cItems, cMilliesInterval, pfnCallback, fRZEnabled));
197
198 /*
199 * Validate input.
200 */
201 VMCPU_ASSERT_EMT(&pVM->aCpus[0]);
202 if (!pfnCallback)
203 {
204 AssertMsgFailed(("No consumer callback!\n"));
205 return VERR_INVALID_PARAMETER;
206 }
207
208 /*
209 * Create the queue.
210 */
211 PPDMQUEUE pQueue;
212 int rc = pdmR3QueueCreate(pVM, cbItem, cItems, cMilliesInterval, fRZEnabled, &pQueue);
213 if (RT_SUCCESS(rc))
214 {
215 pQueue->enmType = PDMQUEUETYPE_DEV;
216 pQueue->u.Dev.pDevIns = pDevIns;
217 pQueue->u.Dev.pfnCallback = pfnCallback;
218
219 *ppQueue = pQueue;
220 Log(("PDM: Created device queue %p; cbItem=%d cItems=%d cMillies=%d pfnCallback=%p pDevIns=%p\n",
221 cbItem, cItems, cMilliesInterval, pfnCallback, pDevIns));
222 }
223 return rc;
224}
225
226
227/**
228 * Create a queue with a driver owner.
229 *
230 * @returns VBox status code.
231 * @param pVM VM handle.
232 * @param pDrvIns Driver instance.
233 * @param cbItem Size a queue item.
234 * @param cItems Number of items in the queue.
235 * @param cMilliesInterval Number of milliseconds between polling the queue.
236 * If 0 then the emulation thread will be notified whenever an item arrives.
237 * @param pfnCallback The consumer function.
238 * @param ppQueue Where to store the queue handle on success.
239 * @thread Emulation thread only.
240 */
241VMMR3DECL(int) PDMR3QueueCreateDriver(PVM pVM, PPDMDRVINS pDrvIns, RTUINT cbItem, RTUINT cItems, uint32_t cMilliesInterval,
242 PFNPDMQUEUEDRV pfnCallback, PPDMQUEUE *ppQueue)
243{
244 LogFlow(("PDMR3QueueCreateDriver: pDrvIns=%p cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p\n",
245 pDrvIns, cbItem, cItems, cMilliesInterval, pfnCallback));
246
247 /*
248 * Validate input.
249 */
250 VMCPU_ASSERT_EMT(&pVM->aCpus[0]);
251 if (!pfnCallback)
252 {
253 AssertMsgFailed(("No consumer callback!\n"));
254 return VERR_INVALID_PARAMETER;
255 }
256
257 /*
258 * Create the queue.
259 */
260 PPDMQUEUE pQueue;
261 int rc = pdmR3QueueCreate(pVM, cbItem, cItems, cMilliesInterval, false, &pQueue);
262 if (RT_SUCCESS(rc))
263 {
264 pQueue->enmType = PDMQUEUETYPE_DRV;
265 pQueue->u.Drv.pDrvIns = pDrvIns;
266 pQueue->u.Drv.pfnCallback = pfnCallback;
267
268 *ppQueue = pQueue;
269 Log(("PDM: Created driver queue %p; cbItem=%d cItems=%d cMillies=%d pfnCallback=%p pDrvIns=%p\n",
270 cbItem, cItems, cMilliesInterval, pfnCallback, pDrvIns));
271 }
272 return rc;
273}
274
275
276/**
277 * Create a queue with an internal owner.
278 *
279 * @returns VBox status code.
280 * @param pVM VM handle.
281 * @param cbItem Size a queue item.
282 * @param cItems Number of items in the queue.
283 * @param cMilliesInterval Number of milliseconds between polling the queue.
284 * If 0 then the emulation thread will be notified whenever an item arrives.
285 * @param pfnCallback The consumer function.
286 * @param fRZEnabled Set if the queue must be usable from RC/R0.
287 * @param ppQueue Where to store the queue handle on success.
288 * @thread Emulation thread only.
289 */
290VMMR3DECL(int) PDMR3QueueCreateInternal(PVM pVM, RTUINT cbItem, RTUINT cItems, uint32_t cMilliesInterval,
291 PFNPDMQUEUEINT pfnCallback, bool fRZEnabled, PPDMQUEUE *ppQueue)
292{
293 LogFlow(("PDMR3QueueCreateInternal: cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p fRZEnabled=%RTbool\n",
294 cbItem, cItems, cMilliesInterval, pfnCallback, fRZEnabled));
295
296 /*
297 * Validate input.
298 */
299 VMCPU_ASSERT_EMT(&pVM->aCpus[0]);
300 if (!pfnCallback)
301 {
302 AssertMsgFailed(("No consumer callback!\n"));
303 return VERR_INVALID_PARAMETER;
304 }
305
306 /*
307 * Create the queue.
308 */
309 PPDMQUEUE pQueue;
310 int rc = pdmR3QueueCreate(pVM, cbItem, cItems, cMilliesInterval, fRZEnabled, &pQueue);
311 if (RT_SUCCESS(rc))
312 {
313 pQueue->enmType = PDMQUEUETYPE_INTERNAL;
314 pQueue->u.Int.pfnCallback = pfnCallback;
315
316 *ppQueue = pQueue;
317 Log(("PDM: Created internal queue %p; cbItem=%d cItems=%d cMillies=%d pfnCallback=%p\n",
318 cbItem, cItems, cMilliesInterval, pfnCallback));
319 }
320 return rc;
321}
322
323
324/**
325 * Create a queue with an external owner.
326 *
327 * @returns VBox status code.
328 * @param pVM VM handle.
329 * @param cbItem Size a queue item.
330 * @param cItems Number of items in the queue.
331 * @param cMilliesInterval Number of milliseconds between polling the queue.
332 * If 0 then the emulation thread will be notified whenever an item arrives.
333 * @param pfnCallback The consumer function.
334 * @param pvUser The user argument to the consumer function.
335 * @param ppQueue Where to store the queue handle on success.
336 * @thread Emulation thread only.
337 */
338VMMR3DECL(int) PDMR3QueueCreateExternal(PVM pVM, RTUINT cbItem, RTUINT cItems, uint32_t cMilliesInterval, PFNPDMQUEUEEXT pfnCallback, void *pvUser, PPDMQUEUE *ppQueue)
339{
340 LogFlow(("PDMR3QueueCreateExternal: cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p\n", cbItem, cItems, cMilliesInterval, pfnCallback));
341
342 /*
343 * Validate input.
344 */
345 VMCPU_ASSERT_EMT(&pVM->aCpus[0]);
346 if (!pfnCallback)
347 {
348 AssertMsgFailed(("No consumer callback!\n"));
349 return VERR_INVALID_PARAMETER;
350 }
351
352 /*
353 * Create the queue.
354 */
355 PPDMQUEUE pQueue;
356 int rc = pdmR3QueueCreate(pVM, cbItem, cItems, cMilliesInterval, false, &pQueue);
357 if (RT_SUCCESS(rc))
358 {
359 pQueue->enmType = PDMQUEUETYPE_EXTERNAL;
360 pQueue->u.Ext.pvUser = pvUser;
361 pQueue->u.Ext.pfnCallback = pfnCallback;
362
363 *ppQueue = pQueue;
364 Log(("PDM: Created external queue %p; cbItem=%d cItems=%d cMillies=%d pfnCallback=%p pvUser=%p\n",
365 cbItem, cItems, cMilliesInterval, pfnCallback, pvUser));
366 }
367 return rc;
368}
369
370
371/**
372 * Destroy a queue.
373 *
374 * @returns VBox status code.
375 * @param pQueue Queue to destroy.
376 * @thread Emulation thread only.
377 */
378VMMR3DECL(int) PDMR3QueueDestroy(PPDMQUEUE pQueue)
379{
380 LogFlow(("PDMR3QueueDestroy: pQueue=%p\n", pQueue));
381
382 /*
383 * Validate input.
384 */
385 if (!pQueue)
386 return VERR_INVALID_PARAMETER;
387 Assert(pQueue && pQueue->pVMR3);
388 PVM pVM = pQueue->pVMR3;
389 VMCPU_ASSERT_EMT(&pVM->aCpus[0]);
390
391 /*
392 * Unlink it.
393 */
394 if (pQueue->pTimer)
395 {
396 if (pVM->pdm.s.pQueuesTimer != pQueue)
397 {
398 PPDMQUEUE pCur = pVM->pdm.s.pQueuesTimer;
399 while (pCur)
400 {
401 if (pCur->pNext == pQueue)
402 {
403 pCur->pNext = pQueue->pNext;
404 break;
405 }
406 pCur = pCur->pNext;
407 }
408 AssertMsg(pCur, ("Didn't find the queue!\n"));
409 }
410 else
411 pVM->pdm.s.pQueuesTimer = pQueue->pNext;
412 }
413 else
414 {
415 if (pVM->pdm.s.pQueuesForced != pQueue)
416 {
417 PPDMQUEUE pCur = pVM->pdm.s.pQueuesForced;
418 while (pCur)
419 {
420 if (pCur->pNext == pQueue)
421 {
422 pCur->pNext = pQueue->pNext;
423 break;
424 }
425 pCur = pCur->pNext;
426 }
427 AssertMsg(pCur, ("Didn't find the queue!\n"));
428 }
429 else
430 pVM->pdm.s.pQueuesForced = pQueue->pNext;
431 }
432 pQueue->pNext = NULL;
433 pQueue->pVMR3 = NULL;
434
435 /*
436 * Destroy the timer and free it.
437 */
438 if (pQueue->pTimer)
439 {
440 TMR3TimerDestroy(pQueue->pTimer);
441 pQueue->pTimer = NULL;
442 }
443 if (pQueue->pVMRC)
444 {
445 pQueue->pVMRC = NIL_RTRCPTR;
446 pQueue->pVMR0 = NIL_RTR0PTR;
447 MMHyperFree(pVM, pQueue);
448 }
449 else
450 MMR3HeapFree(pQueue);
451
452 return VINF_SUCCESS;
453}
454
455
456/**
457 * Destroy a all queues owned by the specified device.
458 *
459 * @returns VBox status code.
460 * @param pVM VM handle.
461 * @param pDevIns Device instance.
462 * @thread Emulation thread only.
463 */
464VMMR3DECL(int) PDMR3QueueDestroyDevice(PVM pVM, PPDMDEVINS pDevIns)
465{
466 LogFlow(("PDMR3QueueDestroyDevice: pDevIns=%p\n", pDevIns));
467
468 /*
469 * Validate input.
470 */
471 if (!pDevIns)
472 return VERR_INVALID_PARAMETER;
473 VMCPU_ASSERT_EMT(&pVM->aCpus[0]);
474
475 /*
476 * Unlink it.
477 */
478 PPDMQUEUE pQueueNext = pVM->pdm.s.pQueuesTimer;
479 PPDMQUEUE pQueue = pVM->pdm.s.pQueuesForced;
480 do
481 {
482 while (pQueue)
483 {
484 if ( pQueue->enmType == PDMQUEUETYPE_DEV
485 && pQueue->u.Dev.pDevIns == pDevIns)
486 {
487 PPDMQUEUE pQueueDestroy = pQueue;
488 pQueue = pQueue->pNext;
489 int rc = PDMR3QueueDestroy(pQueueDestroy);
490 AssertRC(rc);
491 }
492 else
493 pQueue = pQueue->pNext;
494 }
495
496 /* next queue list */
497 pQueue = pQueueNext;
498 pQueueNext = NULL;
499 } while (pQueue);
500
501 return VINF_SUCCESS;
502}
503
504
505/**
506 * Destroy a all queues owned by the specified driver.
507 *
508 * @returns VBox status code.
509 * @param pVM VM handle.
510 * @param pDrvIns Driver instance.
511 * @thread Emulation thread only.
512 */
513VMMR3DECL(int) PDMR3QueueDestroyDriver(PVM pVM, PPDMDRVINS pDrvIns)
514{
515 LogFlow(("PDMR3QueueDestroyDriver: pDrvIns=%p\n", pDrvIns));
516
517 /*
518 * Validate input.
519 */
520 if (!pDrvIns)
521 return VERR_INVALID_PARAMETER;
522 VMCPU_ASSERT_EMT(&pVM->aCpus[0]); /** @todo fix this using the "Misc" critical section. */
523
524 /*
525 * Unlink it.
526 */
527 PPDMQUEUE pQueueNext = pVM->pdm.s.pQueuesTimer;
528 PPDMQUEUE pQueue = pVM->pdm.s.pQueuesForced;
529 do
530 {
531 while (pQueue)
532 {
533 if ( pQueue->enmType == PDMQUEUETYPE_DRV
534 && pQueue->u.Drv.pDrvIns == pDrvIns)
535 {
536 PPDMQUEUE pQueueDestroy = pQueue;
537 pQueue = pQueue->pNext;
538 int rc = PDMR3QueueDestroy(pQueueDestroy);
539 AssertRC(rc);
540 }
541 else
542 pQueue = pQueue->pNext;
543 }
544
545 /* next queue list */
546 pQueue = pQueueNext;
547 pQueueNext = NULL;
548 } while (pQueue);
549
550 return VINF_SUCCESS;
551}
552
553
554/**
555 * Relocate the queues.
556 *
557 * @param pVM The VM handle.
558 * @param offDelta The relocation delta.
559 */
560void pdmR3QueueRelocate(PVM pVM, RTGCINTPTR offDelta)
561{
562 /*
563 * Process the queues.
564 */
565 PPDMQUEUE pQueueNext = pVM->pdm.s.pQueuesTimer;
566 PPDMQUEUE pQueue = pVM->pdm.s.pQueuesForced;
567 do
568 {
569 while (pQueue)
570 {
571 if (pQueue->pVMRC)
572 {
573 pQueue->pVMRC = pVM->pVMRC;
574
575 /* Pending RC items. */
576 if (pQueue->pPendingRC)
577 {
578 pQueue->pPendingRC += offDelta;
579 PPDMQUEUEITEMCORE pCur = (PPDMQUEUEITEMCORE)MMHyperRCToR3(pVM, pQueue->pPendingRC);
580 while (pCur->pNextRC)
581 {
582 pCur->pNextRC += offDelta;
583 pCur = (PPDMQUEUEITEMCORE)MMHyperRCToR3(pVM, pCur->pNextRC);
584 }
585 }
586
587 /* The free items. */
588 uint32_t i = pQueue->iFreeTail;
589 while (i != pQueue->iFreeHead)
590 {
591 pQueue->aFreeItems[i].pItemRC = MMHyperR3ToRC(pVM, pQueue->aFreeItems[i].pItemR3);
592 i = (i + 1) % (pQueue->cItems + PDMQUEUE_FREE_SLACK);
593 }
594 }
595
596 /* next queue */
597 pQueue = pQueue->pNext;
598 }
599
600 /* next queue list */
601 pQueue = pQueueNext;
602 pQueueNext = NULL;
603 } while (pQueue);
604}
605
606
607/**
608 * Flush pending queues.
609 * This is a forced action callback.
610 *
611 * @param pVM VM handle.
612 * @thread Emulation thread only.
613 */
614VMMR3DECL(void) PDMR3QueueFlushAll(PVM pVM)
615{
616 VM_ASSERT_EMT(pVM);
617 LogFlow(("PDMR3QueuesFlush:\n"));
618
619 /* Use atomic test and clear to prevent useless checks; pdmR3QueueFlush is SMP safe. */
620 if (VM_FF_TESTANDCLEAR(pVM, VM_FF_PDM_QUEUES_BIT))
621 {
622 for (PPDMQUEUE pCur = pVM->pdm.s.pQueuesForced; pCur; pCur = pCur->pNext)
623 {
624 if ( pCur->pPendingR3
625 || pCur->pPendingR0
626 || pCur->pPendingRC)
627 {
628 if ( pdmR3QueueFlush(pCur)
629 && pCur->pPendingR3)
630 /* new items arrived while flushing. */
631 pdmR3QueueFlush(pCur);
632 }
633 }
634 }
635}
636
637
638/**
639 * Process pending items in one queue.
640 *
641 * @returns Success indicator.
642 * If false the item the consumer said "enough!".
643 * @param pQueue The queue.
644 */
645static bool pdmR3QueueFlush(PPDMQUEUE pQueue)
646{
647 /*
648 * Get the lists.
649 */
650 PPDMQUEUEITEMCORE pItems = (PPDMQUEUEITEMCORE)ASMAtomicXchgPtr((void * volatile *)&pQueue->pPendingR3, NULL);
651 RTRCPTR pItemsRC = ASMAtomicXchgRCPtr(&pQueue->pPendingRC, NIL_RTRCPTR);
652 RTR0PTR pItemsR0 = ASMAtomicXchgR0Ptr(&pQueue->pPendingR0, NIL_RTR0PTR);
653
654 if ( !pItems
655 && !pItemsRC
656 && !pItemsR0)
657 /* Somebody was racing us. */
658 return true;
659
660 /*
661 * Reverse the list (it's inserted in LIFO order to avoid semaphores, remember).
662 */
663 PPDMQUEUEITEMCORE pCur = pItems;
664 pItems = NULL;
665 while (pCur)
666 {
667 PPDMQUEUEITEMCORE pInsert = pCur;
668 pCur = pCur->pNextR3;
669 pInsert->pNextR3 = pItems;
670 pItems = pInsert;
671 }
672
673 /*
674 * Do the same for any pending RC items.
675 */
676 while (pItemsRC)
677 {
678 PPDMQUEUEITEMCORE pInsert = (PPDMQUEUEITEMCORE)MMHyperRCToR3(pQueue->pVMR3, pItemsRC);
679 pItemsRC = pInsert->pNextRC;
680 pInsert->pNextRC = NIL_RTRCPTR;
681 pInsert->pNextR3 = pItems;
682 pItems = pInsert;
683 }
684
685 /*
686 * Do the same for any pending R0 items.
687 */
688 while (pItemsR0)
689 {
690 PPDMQUEUEITEMCORE pInsert = (PPDMQUEUEITEMCORE)MMHyperR0ToR3(pQueue->pVMR3, pItemsR0);
691 pItemsR0 = pInsert->pNextR0;
692 pInsert->pNextR0 = NIL_RTR0PTR;
693 pInsert->pNextR3 = pItems;
694 pItems = pInsert;
695 }
696
697 /*
698 * Feed the items to the consumer function.
699 */
700 Log2(("pdmR3QueueFlush: pQueue=%p enmType=%d pItems=%p\n", pQueue, pQueue->enmType, pItems));
701 switch (pQueue->enmType)
702 {
703 case PDMQUEUETYPE_DEV:
704 while (pItems)
705 {
706 pCur = pItems;
707 pItems = pItems->pNextR3;
708 if (!pQueue->u.Dev.pfnCallback(pQueue->u.Dev.pDevIns, pCur))
709 break;
710 pdmR3QueueFree(pQueue, pCur);
711 }
712 break;
713
714 case PDMQUEUETYPE_DRV:
715 while (pItems)
716 {
717 pCur = pItems;
718 pItems = pItems->pNextR3;
719 if (!pQueue->u.Drv.pfnCallback(pQueue->u.Drv.pDrvIns, pCur))
720 break;
721 pdmR3QueueFree(pQueue, pCur);
722 }
723 break;
724
725 case PDMQUEUETYPE_INTERNAL:
726 while (pItems)
727 {
728 pCur = pItems;
729 pItems = pItems->pNextR3;
730 if (!pQueue->u.Int.pfnCallback(pQueue->pVMR3, pCur))
731 break;
732 pdmR3QueueFree(pQueue, pCur);
733 }
734 break;
735
736 case PDMQUEUETYPE_EXTERNAL:
737 while (pItems)
738 {
739 pCur = pItems;
740 pItems = pItems->pNextR3;
741 if (!pQueue->u.Ext.pfnCallback(pQueue->u.Ext.pvUser, pCur))
742 break;
743 pdmR3QueueFree(pQueue, pCur);
744 }
745 break;
746
747 default:
748 AssertMsgFailed(("Invalid queue type %d\n", pQueue->enmType));
749 break;
750 }
751
752 /*
753 * Success?
754 */
755 if (pItems)
756 {
757 /*
758 * Shit, no!
759 * 1. Insert pCur.
760 * 2. Reverse the list.
761 * 3. Insert the LIFO at the tail of the pending list.
762 */
763 pCur->pNextR3 = pItems;
764 pItems = pCur;
765
766 //pCur = pItems;
767 pItems = NULL;
768 while (pCur)
769 {
770 PPDMQUEUEITEMCORE pInsert = pCur;
771 pCur = pCur->pNextR3;
772 pInsert->pNextR3 = pItems;
773 pItems = pInsert;
774 }
775
776 if (!ASMAtomicCmpXchgPtr((void * volatile *)&pQueue->pPendingR3, pItems, NULL))
777 {
778 pCur = pQueue->pPendingR3;
779 while (pCur->pNextR3)
780 pCur = pCur->pNextR3;
781 pCur->pNextR3 = pItems;
782 }
783 return false;
784 }
785
786 return true;
787}
788
789
790/**
791 * This is a worker function used by PDMQueueFlush to perform the
792 * flush in ring-3.
793 *
794 * The queue which should be flushed is pointed to by either pQueueFlushRC,
795 * pQueueFlushR0, or pQueue. This function will flush that queue and recalc
796 * the queue FF.
797 *
798 * @param pVM The VM handle.
799 * @param pQueue The queue to flush. Only used in Ring-3.
800 */
801VMMR3DECL(void) PDMR3QueueFlushWorker(PVM pVM, PPDMQUEUE pQueue)
802{
803 Assert(pVM->pdm.s.pQueueFlushR0 || pVM->pdm.s.pQueueFlushRC || pQueue);
804 VM_ASSERT_EMT(pVM);
805
806 /*
807 * Flush the queue.
808 */
809 if (!pQueue && pVM->pdm.s.pQueueFlushRC)
810 {
811 pQueue = (PPDMQUEUE)MMHyperRCToR3(pVM, pVM->pdm.s.pQueueFlushRC);
812 pVM->pdm.s.pQueueFlushRC = NIL_RTRCPTR;
813 }
814 else if (!pQueue && pVM->pdm.s.pQueueFlushR0)
815 {
816 pQueue = (PPDMQUEUE)MMHyperR0ToR3(pVM, pVM->pdm.s.pQueueFlushR0);
817 pVM->pdm.s.pQueueFlushR0 = NIL_RTR0PTR;
818 }
819 Assert(!pVM->pdm.s.pQueueFlushR0 && !pVM->pdm.s.pQueueFlushRC);
820
821 if ( !pQueue
822 || pdmR3QueueFlush(pQueue))
823 {
824 /*
825 * Recalc the FF (for the queues using force action).
826 */
827 VM_FF_CLEAR(pVM, VM_FF_PDM_QUEUES);
828 for (pQueue = pVM->pdm.s.pQueuesForced; pQueue; pQueue = pQueue->pNext)
829 if ( pQueue->pPendingRC
830 || pQueue->pPendingR0
831 || pQueue->pPendingR3)
832 {
833 VM_FF_SET(pVM, VM_FF_PDM_QUEUES);
834 break;
835 }
836 }
837}
838
839
840/**
841 * Free an item.
842 *
843 * @param pQueue The queue.
844 * @param pItem The item.
845 */
846DECLINLINE(void) pdmR3QueueFree(PPDMQUEUE pQueue, PPDMQUEUEITEMCORE pItem)
847{
848 VM_ASSERT_EMT(pQueue->pVMR3);
849
850 int i = pQueue->iFreeHead;
851 int iNext = (i + 1) % (pQueue->cItems + PDMQUEUE_FREE_SLACK);
852
853 pQueue->aFreeItems[i].pItemR3 = pItem;
854 if (pQueue->pVMRC)
855 {
856 pQueue->aFreeItems[i].pItemRC = MMHyperR3ToRC(pQueue->pVMR3, pItem);
857 pQueue->aFreeItems[i].pItemR0 = MMHyperR3ToR0(pQueue->pVMR3, pItem);
858 }
859
860 if (!ASMAtomicCmpXchgU32(&pQueue->iFreeHead, iNext, i))
861 AssertMsgFailed(("huh? i=%d iNext=%d iFreeHead=%d iFreeTail=%d\n", i, iNext, pQueue->iFreeHead, pQueue->iFreeTail));
862}
863
864
865/**
866 * Timer handler for PDM queues.
867 * This is called by for a single queue.
868 *
869 * @param pVM VM handle.
870 * @param pTimer Pointer to timer.
871 * @param pvUser Pointer to the queue.
872 */
873static DECLCALLBACK(void) pdmR3QueueTimer(PVM pVM, PTMTIMER pTimer, void *pvUser)
874{
875 PPDMQUEUE pQueue = (PPDMQUEUE)pvUser;
876 Assert(pTimer == pQueue->pTimer); NOREF(pTimer);
877
878 if ( pQueue->pPendingR3
879 || pQueue->pPendingR0
880 || pQueue->pPendingRC)
881 pdmR3QueueFlush(pQueue);
882 int rc = TMTimerSetMillies(pQueue->pTimer, pQueue->cMilliesInterval);
883 AssertRC(rc);
884}
885
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette