VirtualBox

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

Last change on this file since 6796 was 5999, checked in by vboxsync, 17 years ago

The Giant CDDL Dual-License Header Change.

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