VirtualBox

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

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

Use PDM lock for protecting pdm queue management.

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