VirtualBox

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

Last change on this file since 20889 was 20889, checked in by vboxsync, 15 years ago

PDM queue locking

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