VirtualBox

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

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

VMM/r3: Refactored VMCPU enumeration in preparation that aCpus will be replaced with a pointer array. Removed two raw-mode offset members from the CPUM and CPUMCPU sub-structures. bugref:9217 bugref:9517

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