VirtualBox

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

Last change on this file since 13538 was 12993, checked in by vboxsync, 16 years ago

PDMQueue: Fix assertion with debug builds on 64bit hosts. A R0 pointer is 64bit wides there but the variable is only 32bit

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 26.2 KB
Line 
1/* $Id: PDMQueue.cpp 12993 2008-10-06 08:32:44Z 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 unsigned 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 (VBOX_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 (VBOX_SUCCESS(rc))
124 {
125 rc = TMTimerSetMillies(pQueue->pTimer, cMilliesInterval);
126 if (VBOX_FAILURE(rc))
127 {
128 AssertMsgFailed(("TMTimerSetMillies failed rc=%Vrc\n", rc));
129 int rc2 = TMTimerDestroy(pQueue->pTimer); AssertRC(rc2);
130 }
131 }
132 else
133 AssertMsgFailed(("TMR3TimerCreateInternal failed rc=%Vrc\n", rc));
134 if (VBOX_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 VM_ASSERT_EMT(pVM);
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 (VBOX_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 VM_ASSERT_EMT(pVM);
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 (VBOX_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 VM_ASSERT_EMT(pVM);
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 (VBOX_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 VM_ASSERT_EMT(pVM);
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 (VBOX_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 VM_ASSERT_EMT(pVM);
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 TMTimerDestroy(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 VM_ASSERT_EMT(pVM);
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 VM_ASSERT_EMT(pVM);
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 VM_FF_CLEAR(pVM, VM_FF_PDM_QUEUES);
620 for (PPDMQUEUE pCur = pVM->pdm.s.pQueuesForced; pCur; pCur = pCur->pNext)
621 {
622 if ( pCur->pPendingR3
623 || pCur->pPendingR0
624 || pCur->pPendingRC)
625 {
626 if ( pdmR3QueueFlush(pCur)
627 && pCur->pPendingR3)
628 /* new items arrived while flushing. */
629 pdmR3QueueFlush(pCur);
630 }
631 }
632}
633
634
635/**
636 * Process pending items in one queue.
637 *
638 * @returns Success indicator.
639 * If false the item the consumer said "enough!".
640 * @param pQueue The queue.
641 */
642static bool pdmR3QueueFlush(PPDMQUEUE pQueue)
643{
644 /*
645 * Get the lists.
646 */
647 PPDMQUEUEITEMCORE pItems = (PPDMQUEUEITEMCORE)ASMAtomicXchgPtr((void * volatile *)&pQueue->pPendingR3, NULL);
648 RTRCPTR pItemsRC;
649 ASMAtomicXchgSizeCorrect(&pQueue->pPendingRC, NIL_RTRCPTR, &pItemsRC);
650 RTR0PTR pItemsR0;
651 ASMAtomicXchgSizeCorrect(&pQueue->pPendingR0, NIL_RTR0PTR, &pItemsR0);
652
653 AssertMsg(pItems || pItemsRC || pItemsR0, ("ERROR: can't all be NULL now!\n"));
654
655
656 /*
657 * Reverse the list (it's inserted in LIFO order to avoid semaphores, remember).
658 */
659 PPDMQUEUEITEMCORE pCur = pItems;
660 pItems = NULL;
661 while (pCur)
662 {
663 PPDMQUEUEITEMCORE pInsert = pCur;
664 pCur = pCur->pNextR3;
665 pInsert->pNextR3 = pItems;
666 pItems = pInsert;
667 }
668
669 /*
670 * Do the same for any pending RC items.
671 */
672 while (pItemsRC)
673 {
674 PPDMQUEUEITEMCORE pInsert = (PPDMQUEUEITEMCORE)MMHyperRCToR3(pQueue->pVMR3, pItemsRC);
675 pItemsRC = pInsert->pNextRC;
676 pInsert->pNextRC = NIL_RTRCPTR;
677 pInsert->pNextR3 = pItems;
678 pItems = pInsert;
679 }
680
681 /*
682 * Do the same for any pending R0 items.
683 */
684 while (pItemsR0)
685 {
686 PPDMQUEUEITEMCORE pInsert = (PPDMQUEUEITEMCORE)MMHyperR0ToR3(pQueue->pVMR3, pItemsR0);
687 pItemsR0 = pInsert->pNextR0;
688 pInsert->pNextR0 = NIL_RTR0PTR;
689 pInsert->pNextR3 = pItems;
690 pItems = pInsert;
691 }
692
693 /*
694 * Feed the items to the consumer function.
695 */
696 Log2(("pdmR3QueueFlush: pQueue=%p enmType=%d pItems=%p\n", pQueue, pQueue->enmType, pItems));
697 switch (pQueue->enmType)
698 {
699 case PDMQUEUETYPE_DEV:
700 while (pItems)
701 {
702 pCur = pItems;
703 pItems = pItems->pNextR3;
704 if (!pQueue->u.Dev.pfnCallback(pQueue->u.Dev.pDevIns, pCur))
705 break;
706 pdmR3QueueFree(pQueue, pCur);
707 }
708 break;
709
710 case PDMQUEUETYPE_DRV:
711 while (pItems)
712 {
713 pCur = pItems;
714 pItems = pItems->pNextR3;
715 if (!pQueue->u.Drv.pfnCallback(pQueue->u.Drv.pDrvIns, pCur))
716 break;
717 pdmR3QueueFree(pQueue, pCur);
718 }
719 break;
720
721 case PDMQUEUETYPE_INTERNAL:
722 while (pItems)
723 {
724 pCur = pItems;
725 pItems = pItems->pNextR3;
726 if (!pQueue->u.Int.pfnCallback(pQueue->pVMR3, pCur))
727 break;
728 pdmR3QueueFree(pQueue, pCur);
729 }
730 break;
731
732 case PDMQUEUETYPE_EXTERNAL:
733 while (pItems)
734 {
735 pCur = pItems;
736 pItems = pItems->pNextR3;
737 if (!pQueue->u.Ext.pfnCallback(pQueue->u.Ext.pvUser, pCur))
738 break;
739 pdmR3QueueFree(pQueue, pCur);
740 }
741 break;
742
743 default:
744 AssertMsgFailed(("Invalid queue type %d\n", pQueue->enmType));
745 break;
746 }
747
748 /*
749 * Success?
750 */
751 if (pItems)
752 {
753 /*
754 * Shit, no!
755 * 1. Insert pCur.
756 * 2. Reverse the list.
757 * 3. Insert the LIFO at the tail of the pending list.
758 */
759 pCur->pNextR3 = pItems;
760 pItems = pCur;
761
762 //pCur = pItems;
763 pItems = NULL;
764 while (pCur)
765 {
766 PPDMQUEUEITEMCORE pInsert = pCur;
767 pCur = pCur->pNextR3;
768 pInsert->pNextR3 = pItems;
769 pItems = pInsert;
770 }
771
772 if (!ASMAtomicCmpXchgPtr((void * volatile *)&pQueue->pPendingR3, pItems, NULL))
773 {
774 pCur = pQueue->pPendingR3;
775 while (pCur->pNextR3)
776 pCur = pCur->pNextR3;
777 pCur->pNextR3 = pItems;
778 }
779 return false;
780 }
781
782 return true;
783}
784
785
786/**
787 * This is a worker function used by PDMQueueFlush to perform the
788 * flush in ring-3.
789 *
790 * The queue which should be flushed is pointed to by either pQueueFlushRC,
791 * pQueueFlushR0, or pQueue. This function will flush that queue and recalc
792 * the queue FF.
793 *
794 * @param pVM The VM handle.
795 * @param pQueue The queue to flush. Only used in Ring-3.
796 */
797VMMR3DECL(void) PDMR3QueueFlushWorker(PVM pVM, PPDMQUEUE pQueue)
798{
799 Assert(pVM->pdm.s.pQueueFlushR0 || pVM->pdm.s.pQueueFlushRC || pQueue);
800 VM_ASSERT_EMT(pVM);
801
802 /*
803 * Flush the queue.
804 */
805 if (!pQueue && pVM->pdm.s.pQueueFlushRC)
806 {
807 pQueue = (PPDMQUEUE)MMHyperRCToR3(pVM, pVM->pdm.s.pQueueFlushRC);
808 pVM->pdm.s.pQueueFlushRC = NIL_RTRCPTR;
809 }
810 else if (!pQueue && pVM->pdm.s.pQueueFlushR0)
811 {
812 pQueue = (PPDMQUEUE)MMHyperR0ToR3(pVM, pVM->pdm.s.pQueueFlushR0);
813 pVM->pdm.s.pQueueFlushR0 = NIL_RTR0PTR;
814 }
815 Assert(!pVM->pdm.s.pQueueFlushR0 && !pVM->pdm.s.pQueueFlushRC);
816
817 if ( !pQueue
818 || pdmR3QueueFlush(pQueue))
819 {
820 /*
821 * Recalc the FF (for the queues using force action).
822 */
823 VM_FF_CLEAR(pVM, VM_FF_PDM_QUEUES);
824 for (pQueue = pVM->pdm.s.pQueuesForced; pQueue; pQueue = pQueue->pNext)
825 if ( pQueue->pPendingRC
826 || pQueue->pPendingR0
827 || pQueue->pPendingR3)
828 {
829 VM_FF_SET(pVM, VM_FF_PDM_QUEUES);
830 break;
831 }
832 }
833}
834
835
836/**
837 * Free an item.
838 *
839 * @param pQueue The queue.
840 * @param pItem The item.
841 */
842DECLINLINE(void) pdmR3QueueFree(PPDMQUEUE pQueue, PPDMQUEUEITEMCORE pItem)
843{
844 VM_ASSERT_EMT(pQueue->pVMR3);
845
846 int i = pQueue->iFreeHead;
847 int iNext = (i + 1) % (pQueue->cItems + PDMQUEUE_FREE_SLACK);
848
849 pQueue->aFreeItems[i].pItemR3 = pItem;
850 if (pQueue->pVMRC)
851 {
852 pQueue->aFreeItems[i].pItemRC = MMHyperR3ToRC(pQueue->pVMR3, pItem);
853 pQueue->aFreeItems[i].pItemR0 = MMHyperR3ToR0(pQueue->pVMR3, pItem);
854 }
855
856 if (!ASMAtomicCmpXchgU32(&pQueue->iFreeHead, iNext, i))
857 AssertMsgFailed(("huh? i=%d iNext=%d iFreeHead=%d iFreeTail=%d\n", i, iNext, pQueue->iFreeHead, pQueue->iFreeTail));
858}
859
860
861/**
862 * Timer handler for PDM queues.
863 * This is called by for a single queue.
864 *
865 * @param pVM VM handle.
866 * @param pTimer Pointer to timer.
867 * @param pvUser Pointer to the queue.
868 */
869static DECLCALLBACK(void) pdmR3QueueTimer(PVM pVM, PTMTIMER pTimer, void *pvUser)
870{
871 PPDMQUEUE pQueue = (PPDMQUEUE)pvUser;
872 Assert(pTimer == pQueue->pTimer); NOREF(pTimer);
873
874 if ( pQueue->pPendingR3
875 || pQueue->pPendingR0
876 || pQueue->pPendingRC)
877 pdmR3QueueFlush(pQueue);
878 int rc = TMTimerSetMillies(pQueue->pTimer, pQueue->cMilliesInterval);
879 AssertRC(rc);
880}
881
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use