VirtualBox

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

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

PDMQueue: pQueueFlushR3 isn't necessary.

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