VirtualBox

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

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

PDMR3QueueFlushAll: Fixed race where we could leave unflushed items if an insert was completed right before clearing the active bit. Probably would require us to be preempted after the loop test for it to hit.

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