VirtualBox

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

Last change on this file since 1813 was 509, checked in by vboxsync, 18 years ago

Corrected comment

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