VirtualBox

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

Last change on this file since 80281 was 80281, checked in by vboxsync, 5 years ago

VMM,++: Refactoring code to use VMMC & VMMCPUCC. bugref:9217

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