VirtualBox

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

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

Prevent bouncing right back to ring 3.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 27.4 KB
Line 
1/* $Id: PDMQueue.cpp 21039 2009-06-29 15:57:39Z 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 VM_FF_CLEAR(pVM, VM_FF_PDM_QUEUES);
630
631check_queue:
632 /* Prevent other VCPUs from flushing queues at the same time; we'll never flush an item twice, but the order might change. */
633 if (ASMAtomicCmpXchgU32(&pVM->pdm.s.fQueueFlushing, 1, 0))
634 {
635 /* Use atomic test and clear to prevent useless checks; pdmR3QueueFlush is SMP safe. */
636 do
637 {
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 || pCur->pPendingR0))
647 /* new items arrived while flushing. */
648 pdmR3QueueFlush(pCur);
649 }
650 }
651 }
652 while (VM_FF_TESTANDCLEAR(pVM, VM_FF_PDM_QUEUES_BIT));
653
654 ASMAtomicXchgU32(&pVM->pdm.s.fQueueFlushing, 0);
655
656 /* Check if we missed anything. */
657 for (PPDMQUEUE pCur = pVM->pdm.s.pQueuesForced; pCur; pCur = pCur->pNext)
658 {
659 if ( pCur->pPendingR3
660 || pCur->pPendingR0
661 || pCur->pPendingRC)
662 {
663 VM_FF_SET(pVM, VM_FF_PDM_QUEUES);
664 break;
665 }
666 }
667 if (VM_FF_TESTANDCLEAR(pVM, VM_FF_PDM_QUEUES))
668 goto check_queue;
669
670 }
671}
672
673
674/**
675 * Process pending items in one queue.
676 *
677 * @returns Success indicator.
678 * If false the item the consumer said "enough!".
679 * @param pQueue The queue.
680 */
681static bool pdmR3QueueFlush(PPDMQUEUE pQueue)
682{
683 /*
684 * Get the lists.
685 */
686 PPDMQUEUEITEMCORE pItems = (PPDMQUEUEITEMCORE)ASMAtomicXchgPtr((void * volatile *)&pQueue->pPendingR3, NULL);
687 RTRCPTR pItemsRC = ASMAtomicXchgRCPtr(&pQueue->pPendingRC, NIL_RTRCPTR);
688 RTR0PTR pItemsR0 = ASMAtomicXchgR0Ptr(&pQueue->pPendingR0, NIL_RTR0PTR);
689
690 if ( !pItems
691 && !pItemsRC
692 && !pItemsR0)
693 /* Somebody was racing us. */
694 return true;
695
696 /*
697 * Reverse the list (it's inserted in LIFO order to avoid semaphores, remember).
698 */
699 PPDMQUEUEITEMCORE pCur = pItems;
700 pItems = NULL;
701 while (pCur)
702 {
703 PPDMQUEUEITEMCORE pInsert = pCur;
704 pCur = pCur->pNextR3;
705 pInsert->pNextR3 = pItems;
706 pItems = pInsert;
707 }
708
709 /*
710 * Do the same for any pending RC items.
711 */
712 while (pItemsRC)
713 {
714 PPDMQUEUEITEMCORE pInsert = (PPDMQUEUEITEMCORE)MMHyperRCToR3(pQueue->pVMR3, pItemsRC);
715 pItemsRC = pInsert->pNextRC;
716 pInsert->pNextRC = NIL_RTRCPTR;
717 pInsert->pNextR3 = pItems;
718 pItems = pInsert;
719 }
720
721 /*
722 * Do the same for any pending R0 items.
723 */
724 while (pItemsR0)
725 {
726 PPDMQUEUEITEMCORE pInsert = (PPDMQUEUEITEMCORE)MMHyperR0ToR3(pQueue->pVMR3, pItemsR0);
727 pItemsR0 = pInsert->pNextR0;
728 pInsert->pNextR0 = NIL_RTR0PTR;
729 pInsert->pNextR3 = pItems;
730 pItems = pInsert;
731 }
732
733 /*
734 * Feed the items to the consumer function.
735 */
736 Log2(("pdmR3QueueFlush: pQueue=%p enmType=%d pItems=%p\n", pQueue, pQueue->enmType, pItems));
737 switch (pQueue->enmType)
738 {
739 case PDMQUEUETYPE_DEV:
740 while (pItems)
741 {
742 pCur = pItems;
743 pItems = pItems->pNextR3;
744 if (!pQueue->u.Dev.pfnCallback(pQueue->u.Dev.pDevIns, pCur))
745 break;
746 pdmR3QueueFree(pQueue, pCur);
747 }
748 break;
749
750 case PDMQUEUETYPE_DRV:
751 while (pItems)
752 {
753 pCur = pItems;
754 pItems = pItems->pNextR3;
755 if (!pQueue->u.Drv.pfnCallback(pQueue->u.Drv.pDrvIns, pCur))
756 break;
757 pdmR3QueueFree(pQueue, pCur);
758 }
759 break;
760
761 case PDMQUEUETYPE_INTERNAL:
762 while (pItems)
763 {
764 pCur = pItems;
765 pItems = pItems->pNextR3;
766 if (!pQueue->u.Int.pfnCallback(pQueue->pVMR3, pCur))
767 break;
768 pdmR3QueueFree(pQueue, pCur);
769 }
770 break;
771
772 case PDMQUEUETYPE_EXTERNAL:
773 while (pItems)
774 {
775 pCur = pItems;
776 pItems = pItems->pNextR3;
777 if (!pQueue->u.Ext.pfnCallback(pQueue->u.Ext.pvUser, pCur))
778 break;
779 pdmR3QueueFree(pQueue, pCur);
780 }
781 break;
782
783 default:
784 AssertMsgFailed(("Invalid queue type %d\n", pQueue->enmType));
785 break;
786 }
787
788 /*
789 * Success?
790 */
791 if (pItems)
792 {
793 /*
794 * Shit, no!
795 * 1. Insert pCur.
796 * 2. Reverse the list.
797 * 3. Insert the LIFO at the tail of the pending list.
798 */
799 pCur->pNextR3 = pItems;
800 pItems = pCur;
801
802 //pCur = pItems;
803 pItems = NULL;
804 while (pCur)
805 {
806 PPDMQUEUEITEMCORE pInsert = pCur;
807 pCur = pCur->pNextR3;
808 pInsert->pNextR3 = pItems;
809 pItems = pInsert;
810 }
811
812 if (!ASMAtomicCmpXchgPtr((void * volatile *)&pQueue->pPendingR3, pItems, NULL))
813 {
814 pCur = pQueue->pPendingR3;
815 while (pCur->pNextR3)
816 pCur = pCur->pNextR3;
817 pCur->pNextR3 = pItems;
818 }
819 return false;
820 }
821
822 return true;
823}
824
825
826/**
827 * This is a worker function used by PDMQueueFlush to perform the
828 * flush in ring-3.
829 *
830 * The queue which should be flushed is pointed to by either pQueueFlushRC,
831 * pQueueFlushR0, or pQueue. This function will flush that queue and recalc
832 * the queue FF.
833 *
834 * @param pVM The VM handle.
835 * @param pQueue The queue to flush. Only used in Ring-3.
836 */
837VMMR3DECL(void) PDMR3QueueFlushWorker(PVM pVM, PPDMQUEUE pQueue)
838{
839 Assert(pVM->pdm.s.pQueueFlushR0 || pVM->pdm.s.pQueueFlushRC || pQueue);
840 VM_ASSERT_EMT(pVM);
841
842 /*
843 * Flush the queue.
844 */
845 if (!pQueue && pVM->pdm.s.pQueueFlushRC)
846 {
847 pQueue = (PPDMQUEUE)MMHyperRCToR3(pVM, pVM->pdm.s.pQueueFlushRC);
848 pVM->pdm.s.pQueueFlushRC = NIL_RTRCPTR;
849 }
850 else if (!pQueue && pVM->pdm.s.pQueueFlushR0)
851 {
852 pQueue = (PPDMQUEUE)MMHyperR0ToR3(pVM, pVM->pdm.s.pQueueFlushR0);
853 pVM->pdm.s.pQueueFlushR0 = NIL_RTR0PTR;
854 }
855 Assert(!pVM->pdm.s.pQueueFlushR0 && !pVM->pdm.s.pQueueFlushRC);
856
857 if ( !pQueue
858 || pdmR3QueueFlush(pQueue))
859 {
860 /*
861 * Recalc the FF (for the queues using force action).
862 */
863 VM_FF_CLEAR(pVM, VM_FF_PDM_QUEUES);
864 for (pQueue = pVM->pdm.s.pQueuesForced; pQueue; pQueue = pQueue->pNext)
865 if ( pQueue->pPendingRC
866 || pQueue->pPendingR0
867 || pQueue->pPendingR3)
868 {
869 VM_FF_SET(pVM, VM_FF_PDM_QUEUES);
870 break;
871 }
872 }
873}
874
875
876/**
877 * Free an item.
878 *
879 * @param pQueue The queue.
880 * @param pItem The item.
881 */
882DECLINLINE(void) pdmR3QueueFree(PPDMQUEUE pQueue, PPDMQUEUEITEMCORE pItem)
883{
884 VM_ASSERT_EMT(pQueue->pVMR3);
885
886 int i = pQueue->iFreeHead;
887 int iNext = (i + 1) % (pQueue->cItems + PDMQUEUE_FREE_SLACK);
888
889 pQueue->aFreeItems[i].pItemR3 = pItem;
890 if (pQueue->pVMRC)
891 {
892 pQueue->aFreeItems[i].pItemRC = MMHyperR3ToRC(pQueue->pVMR3, pItem);
893 pQueue->aFreeItems[i].pItemR0 = MMHyperR3ToR0(pQueue->pVMR3, pItem);
894 }
895
896 if (!ASMAtomicCmpXchgU32(&pQueue->iFreeHead, iNext, i))
897 AssertMsgFailed(("huh? i=%d iNext=%d iFreeHead=%d iFreeTail=%d\n", i, iNext, pQueue->iFreeHead, pQueue->iFreeTail));
898}
899
900
901/**
902 * Timer handler for PDM queues.
903 * This is called by for a single queue.
904 *
905 * @param pVM VM handle.
906 * @param pTimer Pointer to timer.
907 * @param pvUser Pointer to the queue.
908 */
909static DECLCALLBACK(void) pdmR3QueueTimer(PVM pVM, PTMTIMER pTimer, void *pvUser)
910{
911 PPDMQUEUE pQueue = (PPDMQUEUE)pvUser;
912 Assert(pTimer == pQueue->pTimer); NOREF(pTimer);
913
914 if ( pQueue->pPendingR3
915 || pQueue->pPendingR0
916 || pQueue->pPendingRC)
917 pdmR3QueueFlush(pQueue);
918 int rc = TMTimerSetMillies(pQueue->pTimer, pQueue->cMilliesInterval);
919 AssertRC(rc);
920}
921
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