VirtualBox

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

Last change on this file since 92386 was 92386, checked in by vboxsync, 3 years ago

VMM,VUSB: Statistics corrections (unit, type).

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