VirtualBox

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

Last change on this file since 50653 was 46493, checked in by vboxsync, 11 years ago

STAMR3Deregister* optimizations. Relevant for both startup and shutdown times.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use