VirtualBox

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

Last change on this file since 90468 was 87773, checked in by vboxsync, 4 years ago

VMM/TM,Devices: Store the timer name in the TMTIMER structure and limit it to 31 characters. Shortened most timer names. bugref:9943

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