VirtualBox

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

Last change on this file since 84044 was 82968, checked in by vboxsync, 4 years ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use