summaryrefslogtreecommitdiffstats
path: root/src/VBox/VMM/VMMR3/PDMQueue.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/VMM/VMMR3/PDMQueue.cpp')
-rw-r--r--src/VBox/VMM/VMMR3/PDMQueue.cpp880
1 files changed, 880 insertions, 0 deletions
diff --git a/src/VBox/VMM/VMMR3/PDMQueue.cpp b/src/VBox/VMM/VMMR3/PDMQueue.cpp
new file mode 100644
index 00000000..23dc2134
--- /dev/null
+++ b/src/VBox/VMM/VMMR3/PDMQueue.cpp
@@ -0,0 +1,880 @@
+/* $Id: PDMQueue.cpp $ */
+/** @file
+ * PDM Queue - Transport data and tasks to EMT and R3.
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_PDM_QUEUE
+#include "PDMInternal.h"
+#include <VBox/vmm/pdm.h>
+#include <VBox/vmm/mm.h>
+#ifdef VBOX_WITH_REM
+# include <VBox/vmm/rem.h>
+#endif
+#include <VBox/vmm/vm.h>
+#include <VBox/vmm/uvm.h>
+#include <iprt/errcore.h>
+
+#include <VBox/log.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/thread.h>
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+DECLINLINE(void) pdmR3QueueFreeItem(PPDMQUEUE pQueue, PPDMQUEUEITEMCORE pItem);
+static bool pdmR3QueueFlush(PPDMQUEUE pQueue);
+static DECLCALLBACK(void) pdmR3QueueTimer(PVM pVM, PTMTIMER pTimer, void *pvUser);
+
+
+
+/**
+ * Internal worker for the queue creation apis.
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ * @param cbItem Item size.
+ * @param cItems Number of items.
+ * @param cMilliesInterval Number of milliseconds between polling the queue.
+ * If 0 then the emulation thread will be notified whenever an item arrives.
+ * @param fRZEnabled Set if the queue will be used from RC/R0 and need to be allocated from the hyper heap.
+ * @param pszName The queue name. Unique. Not copied.
+ * @param ppQueue Where to store the queue handle.
+ */
+static int pdmR3QueueCreate(PVM pVM, size_t cbItem, uint32_t cItems, uint32_t cMilliesInterval, bool fRZEnabled,
+ const char *pszName, PPDMQUEUE *ppQueue)
+{
+ PUVM pUVM = pVM->pUVM;
+
+ /*
+ * Validate input.
+ */
+ AssertMsgReturn(cbItem >= sizeof(PDMQUEUEITEMCORE) && cbItem < _1M, ("cbItem=%zu\n", cbItem), VERR_OUT_OF_RANGE);
+ AssertMsgReturn(cItems >= 1 && cItems <= _64K, ("cItems=%u\n", cItems), VERR_OUT_OF_RANGE);
+
+ /*
+ * Align the item size and calculate the structure size.
+ */
+ cbItem = RT_ALIGN(cbItem, sizeof(RTUINTPTR));
+ size_t cb = cbItem * cItems + RT_ALIGN_Z(RT_UOFFSETOF_DYN(PDMQUEUE, aFreeItems[cItems + PDMQUEUE_FREE_SLACK]), 16);
+ PPDMQUEUE pQueue;
+ int rc;
+ if (fRZEnabled)
+ rc = MMHyperAlloc(pVM, cb, 0, MM_TAG_PDM_QUEUE, (void **)&pQueue );
+ else
+ rc = MMR3HeapAllocZEx(pVM, MM_TAG_PDM_QUEUE, cb, (void **)&pQueue);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Initialize the data fields.
+ */
+ pQueue->pVMR3 = pVM;
+ pQueue->pVMR0 = fRZEnabled ? pVM->pVMR0 : NIL_RTR0PTR;
+ pQueue->pVMRC = fRZEnabled ? pVM->pVMRC : NIL_RTRCPTR;
+ pQueue->pszName = pszName;
+ pQueue->cMilliesInterval = cMilliesInterval;
+ //pQueue->pTimer = NULL;
+ pQueue->cbItem = (uint32_t)cbItem;
+ pQueue->cItems = cItems;
+ //pQueue->pPendingR3 = NULL;
+ //pQueue->pPendingR0 = NULL;
+ //pQueue->pPendingRC = NULL;
+ pQueue->iFreeHead = cItems;
+ //pQueue->iFreeTail = 0;
+ PPDMQUEUEITEMCORE pItem = (PPDMQUEUEITEMCORE)((char *)pQueue + RT_ALIGN_Z(RT_UOFFSETOF_DYN(PDMQUEUE, aFreeItems[cItems + PDMQUEUE_FREE_SLACK]), 16));
+ for (unsigned i = 0; i < cItems; i++, pItem = (PPDMQUEUEITEMCORE)((char *)pItem + cbItem))
+ {
+ pQueue->aFreeItems[i].pItemR3 = pItem;
+ if (fRZEnabled)
+ {
+ pQueue->aFreeItems[i].pItemR0 = MMHyperR3ToR0(pVM, pItem);
+ pQueue->aFreeItems[i].pItemRC = MMHyperR3ToRC(pVM, pItem);
+ }
+ }
+
+ /*
+ * Create timer?
+ */
+ if (cMilliesInterval)
+ {
+ rc = TMR3TimerCreateInternal(pVM, TMCLOCK_REAL, pdmR3QueueTimer, pQueue, "Queue timer", &pQueue->pTimer);
+ if (RT_SUCCESS(rc))
+ {
+ rc = TMTimerSetMillies(pQueue->pTimer, cMilliesInterval);
+ if (RT_FAILURE(rc))
+ {
+ AssertMsgFailed(("TMTimerSetMillies failed rc=%Rrc\n", rc));
+ int rc2 = TMR3TimerDestroy(pQueue->pTimer); AssertRC(rc2);
+ }
+ }
+ else
+ AssertMsgFailed(("TMR3TimerCreateInternal failed rc=%Rrc\n", rc));
+ if (RT_FAILURE(rc))
+ {
+ if (fRZEnabled)
+ MMHyperFree(pVM, pQueue);
+ else
+ MMR3HeapFree(pQueue);
+ return rc;
+ }
+
+ /*
+ * Insert into the queue list for timer driven queues.
+ */
+ pdmLock(pVM);
+ pQueue->pNext = pUVM->pdm.s.pQueuesTimer;
+ pUVM->pdm.s.pQueuesTimer = pQueue;
+ pdmUnlock(pVM);
+ }
+ else
+ {
+ /*
+ * Insert into the queue list for forced action driven queues.
+ * This is a FIFO, so insert at the end.
+ */
+ /** @todo we should add a priority to the queues so we don't have to rely on
+ * the initialization order to deal with problems like @bugref{1605} (pgm/pcnet
+ * deadlock caused by the critsect queue to be last in the chain).
+ * - Update, the critical sections are no longer using queues, so this isn't a real
+ * problem any longer. The priority might be a nice feature for later though.
+ */
+ pdmLock(pVM);
+ if (!pUVM->pdm.s.pQueuesForced)
+ pUVM->pdm.s.pQueuesForced = pQueue;
+ else
+ {
+ PPDMQUEUE pPrev = pUVM->pdm.s.pQueuesForced;
+ while (pPrev->pNext)
+ pPrev = pPrev->pNext;
+ pPrev->pNext = pQueue;
+ }
+ pdmUnlock(pVM);
+ }
+
+ /*
+ * Register the statistics.
+ */
+ STAMR3RegisterF(pVM, &pQueue->cbItem, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Item size.", "/PDM/Queue/%s/cbItem", pQueue->pszName);
+ STAMR3RegisterF(pVM, &pQueue->cItems, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Queue size.", "/PDM/Queue/%s/cItems", pQueue->pszName);
+ STAMR3RegisterF(pVM, &pQueue->StatAllocFailures, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "PDMQueueAlloc failures.", "/PDM/Queue/%s/AllocFailures", pQueue->pszName);
+ STAMR3RegisterF(pVM, &pQueue->StatInsert, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "Calls to PDMQueueInsert.", "/PDM/Queue/%s/Insert", pQueue->pszName);
+ STAMR3RegisterF(pVM, &pQueue->StatFlush, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "Calls to pdmR3QueueFlush.", "/PDM/Queue/%s/Flush", pQueue->pszName);
+ STAMR3RegisterF(pVM, &pQueue->StatFlushLeftovers, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Left over items after flush.", "/PDM/Queue/%s/FlushLeftovers", pQueue->pszName);
+#ifdef VBOX_WITH_STATISTICS
+ STAMR3RegisterF(pVM, &pQueue->StatFlushPrf, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "Profiling pdmR3QueueFlush.", "/PDM/Queue/%s/FlushPrf", pQueue->pszName);
+ STAMR3RegisterF(pVM, (void *)&pQueue->cStatPending, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Pending items.", "/PDM/Queue/%s/Pending", pQueue->pszName);
+#endif
+
+ *ppQueue = pQueue;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Create a queue with a device owner.
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ * @param pDevIns Device instance.
+ * @param cbItem Size a queue item.
+ * @param cItems Number of items in the queue.
+ * @param cMilliesInterval Number of milliseconds between polling the queue.
+ * If 0 then the emulation thread will be notified whenever an item arrives.
+ * @param pfnCallback The consumer function.
+ * @param fRZEnabled Set if the queue must be usable from RC/R0.
+ * @param pszName The queue name. Unique. Not copied.
+ * @param ppQueue Where to store the queue handle on success.
+ * @thread Emulation thread only.
+ */
+VMMR3_INT_DECL(int) PDMR3QueueCreateDevice(PVM pVM, PPDMDEVINS pDevIns, size_t cbItem, uint32_t cItems, uint32_t cMilliesInterval,
+ PFNPDMQUEUEDEV pfnCallback, bool fRZEnabled, const char *pszName, PPDMQUEUE *ppQueue)
+{
+ LogFlow(("PDMR3QueueCreateDevice: pDevIns=%p cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p fRZEnabled=%RTbool pszName=%s\n",
+ pDevIns, cbItem, cItems, cMilliesInterval, pfnCallback, fRZEnabled, pszName));
+
+ /*
+ * Validate input.
+ */
+ VMCPU_ASSERT_EMT(&pVM->aCpus[0]);
+ if (!pfnCallback)
+ {
+ AssertMsgFailed(("No consumer callback!\n"));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Create the queue.
+ */
+ PPDMQUEUE pQueue;
+ int rc = pdmR3QueueCreate(pVM, cbItem, cItems, cMilliesInterval, fRZEnabled, pszName, &pQueue);
+ if (RT_SUCCESS(rc))
+ {
+ pQueue->enmType = PDMQUEUETYPE_DEV;
+ pQueue->u.Dev.pDevIns = pDevIns;
+ pQueue->u.Dev.pfnCallback = pfnCallback;
+
+ *ppQueue = pQueue;
+ Log(("PDM: Created device queue %p; cbItem=%d cItems=%d cMillies=%d pfnCallback=%p pDevIns=%p\n",
+ pQueue, cbItem, cItems, cMilliesInterval, pfnCallback, pDevIns));
+ }
+ return rc;
+}
+
+
+/**
+ * Create a queue with a driver owner.
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ * @param pDrvIns Driver instance.
+ * @param cbItem Size a queue item.
+ * @param cItems Number of items in the queue.
+ * @param cMilliesInterval Number of milliseconds between polling the queue.
+ * If 0 then the emulation thread will be notified whenever an item arrives.
+ * @param pfnCallback The consumer function.
+ * @param pszName The queue name. Unique. Not copied.
+ * @param ppQueue Where to store the queue handle on success.
+ * @thread Emulation thread only.
+ */
+VMMR3_INT_DECL(int) PDMR3QueueCreateDriver(PVM pVM, PPDMDRVINS pDrvIns, size_t cbItem, uint32_t cItems, uint32_t cMilliesInterval,
+ PFNPDMQUEUEDRV pfnCallback, const char *pszName, PPDMQUEUE *ppQueue)
+{
+ LogFlow(("PDMR3QueueCreateDriver: pDrvIns=%p cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p pszName=%s\n",
+ pDrvIns, cbItem, cItems, cMilliesInterval, pfnCallback, pszName));
+
+ /*
+ * Validate input.
+ */
+ VMCPU_ASSERT_EMT(&pVM->aCpus[0]);
+ AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
+
+ /*
+ * Create the queue.
+ */
+ PPDMQUEUE pQueue;
+ int rc = pdmR3QueueCreate(pVM, cbItem, cItems, cMilliesInterval, false, pszName, &pQueue);
+ if (RT_SUCCESS(rc))
+ {
+ pQueue->enmType = PDMQUEUETYPE_DRV;
+ pQueue->u.Drv.pDrvIns = pDrvIns;
+ pQueue->u.Drv.pfnCallback = pfnCallback;
+
+ *ppQueue = pQueue;
+ Log(("PDM: Created driver queue %p; cbItem=%d cItems=%d cMillies=%d pfnCallback=%p pDrvIns=%p\n",
+ pQueue, cbItem, cItems, cMilliesInterval, pfnCallback, pDrvIns));
+ }
+ return rc;
+}
+
+
+/**
+ * Create a queue with an internal owner.
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ * @param cbItem Size a queue item.
+ * @param cItems Number of items in the queue.
+ * @param cMilliesInterval Number of milliseconds between polling the queue.
+ * If 0 then the emulation thread will be notified whenever an item arrives.
+ * @param pfnCallback The consumer function.
+ * @param fRZEnabled Set if the queue must be usable from RC/R0.
+ * @param pszName The queue name. Unique. Not copied.
+ * @param ppQueue Where to store the queue handle on success.
+ * @thread Emulation thread only.
+ */
+VMMR3_INT_DECL(int) PDMR3QueueCreateInternal(PVM pVM, size_t cbItem, uint32_t cItems, uint32_t cMilliesInterval,
+ PFNPDMQUEUEINT pfnCallback, bool fRZEnabled, const char *pszName, PPDMQUEUE *ppQueue)
+{
+ LogFlow(("PDMR3QueueCreateInternal: cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p fRZEnabled=%RTbool pszName=%s\n",
+ cbItem, cItems, cMilliesInterval, pfnCallback, fRZEnabled, pszName));
+
+ /*
+ * Validate input.
+ */
+ VMCPU_ASSERT_EMT(&pVM->aCpus[0]);
+ AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
+
+ /*
+ * Create the queue.
+ */
+ PPDMQUEUE pQueue;
+ int rc = pdmR3QueueCreate(pVM, cbItem, cItems, cMilliesInterval, fRZEnabled, pszName, &pQueue);
+ if (RT_SUCCESS(rc))
+ {
+ pQueue->enmType = PDMQUEUETYPE_INTERNAL;
+ pQueue->u.Int.pfnCallback = pfnCallback;
+
+ *ppQueue = pQueue;
+ Log(("PDM: Created internal queue %p; cbItem=%d cItems=%d cMillies=%d pfnCallback=%p\n",
+ pQueue, cbItem, cItems, cMilliesInterval, pfnCallback));
+ }
+ return rc;
+}
+
+
+/**
+ * Create a queue with an external owner.
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ * @param cbItem Size a queue item.
+ * @param cItems Number of items in the queue.
+ * @param cMilliesInterval Number of milliseconds between polling the queue.
+ * If 0 then the emulation thread will be notified whenever an item arrives.
+ * @param pfnCallback The consumer function.
+ * @param pvUser The user argument to the consumer function.
+ * @param pszName The queue name. Unique. Not copied.
+ * @param ppQueue Where to store the queue handle on success.
+ * @thread Emulation thread only.
+ */
+VMMR3_INT_DECL(int) PDMR3QueueCreateExternal(PVM pVM, size_t cbItem, uint32_t cItems, uint32_t cMilliesInterval,
+ PFNPDMQUEUEEXT pfnCallback, void *pvUser, const char *pszName, PPDMQUEUE *ppQueue)
+{
+ LogFlow(("PDMR3QueueCreateExternal: cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p pszName=%s\n", cbItem, cItems, cMilliesInterval, pfnCallback, pszName));
+
+ /*
+ * Validate input.
+ */
+ VMCPU_ASSERT_EMT(&pVM->aCpus[0]);
+ AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
+
+ /*
+ * Create the queue.
+ */
+ PPDMQUEUE pQueue;
+ int rc = pdmR3QueueCreate(pVM, cbItem, cItems, cMilliesInterval, false, pszName, &pQueue);
+ if (RT_SUCCESS(rc))
+ {
+ pQueue->enmType = PDMQUEUETYPE_EXTERNAL;
+ pQueue->u.Ext.pvUser = pvUser;
+ pQueue->u.Ext.pfnCallback = pfnCallback;
+
+ *ppQueue = pQueue;
+ Log(("PDM: Created external queue %p; cbItem=%d cItems=%d cMillies=%d pfnCallback=%p pvUser=%p\n",
+ pQueue, cbItem, cItems, cMilliesInterval, pfnCallback, pvUser));
+ }
+ return rc;
+}
+
+
+/**
+ * Destroy a queue.
+ *
+ * @returns VBox status code.
+ * @param pQueue Queue to destroy.
+ * @thread Emulation thread only.
+ */
+VMMR3_INT_DECL(int) PDMR3QueueDestroy(PPDMQUEUE pQueue)
+{
+ LogFlow(("PDMR3QueueDestroy: pQueue=%p\n", pQueue));
+
+ /*
+ * Validate input.
+ */
+ if (!pQueue)
+ return VERR_INVALID_PARAMETER;
+ Assert(pQueue && pQueue->pVMR3);
+ PVM pVM = pQueue->pVMR3;
+ PUVM pUVM = pVM->pUVM;
+
+ pdmLock(pVM);
+
+ /*
+ * Unlink it.
+ */
+ if (pQueue->pTimer)
+ {
+ if (pUVM->pdm.s.pQueuesTimer != pQueue)
+ {
+ PPDMQUEUE pCur = pUVM->pdm.s.pQueuesTimer;
+ while (pCur)
+ {
+ if (pCur->pNext == pQueue)
+ {
+ pCur->pNext = pQueue->pNext;
+ break;
+ }
+ pCur = pCur->pNext;
+ }
+ AssertMsg(pCur, ("Didn't find the queue!\n"));
+ }
+ else
+ pUVM->pdm.s.pQueuesTimer = pQueue->pNext;
+ }
+ else
+ {
+ if (pUVM->pdm.s.pQueuesForced != pQueue)
+ {
+ PPDMQUEUE pCur = pUVM->pdm.s.pQueuesForced;
+ while (pCur)
+ {
+ if (pCur->pNext == pQueue)
+ {
+ pCur->pNext = pQueue->pNext;
+ break;
+ }
+ pCur = pCur->pNext;
+ }
+ AssertMsg(pCur, ("Didn't find the queue!\n"));
+ }
+ else
+ pUVM->pdm.s.pQueuesForced = pQueue->pNext;
+ }
+ pQueue->pNext = NULL;
+ pQueue->pVMR3 = NULL;
+ pdmUnlock(pVM);
+
+ /*
+ * Deregister statistics.
+ */
+ STAMR3DeregisterF(pVM->pUVM, "/PDM/Queue/%s/cbItem", pQueue->pszName);
+
+ /*
+ * Destroy the timer and free it.
+ */
+ if (pQueue->pTimer)
+ {
+ TMR3TimerDestroy(pQueue->pTimer);
+ pQueue->pTimer = NULL;
+ }
+ if (pQueue->pVMRC)
+ {
+ pQueue->pVMRC = NIL_RTRCPTR;
+ pQueue->pVMR0 = NIL_RTR0PTR;
+ MMHyperFree(pVM, pQueue);
+ }
+ else
+ MMR3HeapFree(pQueue);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Destroy a all queues owned by the specified device.
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ * @param pDevIns Device instance.
+ * @thread Emulation thread only.
+ */
+VMMR3_INT_DECL(int) PDMR3QueueDestroyDevice(PVM pVM, PPDMDEVINS pDevIns)
+{
+ LogFlow(("PDMR3QueueDestroyDevice: pDevIns=%p\n", pDevIns));
+
+ /*
+ * Validate input.
+ */
+ if (!pDevIns)
+ return VERR_INVALID_PARAMETER;
+
+ PUVM pUVM = pVM->pUVM;
+ pdmLock(pVM);
+
+ /*
+ * Unlink it.
+ */
+ PPDMQUEUE pQueueNext = pUVM->pdm.s.pQueuesTimer;
+ PPDMQUEUE pQueue = pUVM->pdm.s.pQueuesForced;
+ do
+ {
+ while (pQueue)
+ {
+ if ( pQueue->enmType == PDMQUEUETYPE_DEV
+ && pQueue->u.Dev.pDevIns == pDevIns)
+ {
+ PPDMQUEUE pQueueDestroy = pQueue;
+ pQueue = pQueue->pNext;
+ int rc = PDMR3QueueDestroy(pQueueDestroy);
+ AssertRC(rc);
+ }
+ else
+ pQueue = pQueue->pNext;
+ }
+
+ /* next queue list */
+ pQueue = pQueueNext;
+ pQueueNext = NULL;
+ } while (pQueue);
+
+ pdmUnlock(pVM);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Destroy a all queues owned by the specified driver.
+ *
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ * @param pDrvIns Driver instance.
+ * @thread Emulation thread only.
+ */
+VMMR3_INT_DECL(int) PDMR3QueueDestroyDriver(PVM pVM, PPDMDRVINS pDrvIns)
+{
+ LogFlow(("PDMR3QueueDestroyDriver: pDrvIns=%p\n", pDrvIns));
+
+ /*
+ * Validate input.
+ */
+ if (!pDrvIns)
+ return VERR_INVALID_PARAMETER;
+
+ PUVM pUVM = pVM->pUVM;
+ pdmLock(pVM);
+
+ /*
+ * Unlink it.
+ */
+ PPDMQUEUE pQueueNext = pUVM->pdm.s.pQueuesTimer;
+ PPDMQUEUE pQueue = pUVM->pdm.s.pQueuesForced;
+ do
+ {
+ while (pQueue)
+ {
+ if ( pQueue->enmType == PDMQUEUETYPE_DRV
+ && pQueue->u.Drv.pDrvIns == pDrvIns)
+ {
+ PPDMQUEUE pQueueDestroy = pQueue;
+ pQueue = pQueue->pNext;
+ int rc = PDMR3QueueDestroy(pQueueDestroy);
+ AssertRC(rc);
+ }
+ else
+ pQueue = pQueue->pNext;
+ }
+
+ /* next queue list */
+ pQueue = pQueueNext;
+ pQueueNext = NULL;
+ } while (pQueue);
+
+ pdmUnlock(pVM);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Relocate the queues.
+ *
+ * @param pVM The cross context VM structure.
+ * @param offDelta The relocation delta.
+ */
+void pdmR3QueueRelocate(PVM pVM, RTGCINTPTR offDelta)
+{
+ /*
+ * Process the queues.
+ */
+ PUVM pUVM = pVM->pUVM;
+ PPDMQUEUE pQueueNext = pUVM->pdm.s.pQueuesTimer;
+ PPDMQUEUE pQueue = pUVM->pdm.s.pQueuesForced;
+ do
+ {
+ while (pQueue)
+ {
+ if (pQueue->pVMRC)
+ {
+ pQueue->pVMRC = pVM->pVMRC;
+
+ /* Pending RC items. */
+ if (pQueue->pPendingRC)
+ {
+ pQueue->pPendingRC += offDelta;
+ PPDMQUEUEITEMCORE pCur = (PPDMQUEUEITEMCORE)MMHyperRCToR3(pVM, pQueue->pPendingRC);
+ while (pCur->pNextRC)
+ {
+ pCur->pNextRC += offDelta;
+ pCur = (PPDMQUEUEITEMCORE)MMHyperRCToR3(pVM, pCur->pNextRC);
+ }
+ }
+
+ /* The free items. */
+ uint32_t i = pQueue->iFreeTail;
+ while (i != pQueue->iFreeHead)
+ {
+ pQueue->aFreeItems[i].pItemRC = MMHyperR3ToRC(pVM, pQueue->aFreeItems[i].pItemR3);
+ i = (i + 1) % (pQueue->cItems + PDMQUEUE_FREE_SLACK);
+ }
+ }
+
+ /* next queue */
+ pQueue = pQueue->pNext;
+ }
+
+ /* next queue list */
+ pQueue = pQueueNext;
+ pQueueNext = NULL;
+ } while (pQueue);
+}
+
+
+/**
+ * Flush pending queues.
+ * This is a forced action callback.
+ *
+ * @param pVM The cross context VM structure.
+ * @thread Emulation thread only.
+ */
+VMMR3_INT_DECL(void) PDMR3QueueFlushAll(PVM pVM)
+{
+ VM_ASSERT_EMT(pVM);
+ LogFlow(("PDMR3QueuesFlush:\n"));
+
+ /*
+ * Only let one EMT flushing queues at any one time to preserve the order
+ * and to avoid wasting time. The FF is always cleared here, because it's
+ * only used to get someones attention. Queue inserts occurring during the
+ * flush are caught using the pending bit.
+ *
+ * Note! We must check the force action and pending flags after clearing
+ * the active bit!
+ */
+ VM_FF_CLEAR(pVM, VM_FF_PDM_QUEUES);
+ while (!ASMAtomicBitTestAndSet(&pVM->pdm.s.fQueueFlushing, PDM_QUEUE_FLUSH_FLAG_ACTIVE_BIT))
+ {
+ ASMAtomicBitClear(&pVM->pdm.s.fQueueFlushing, PDM_QUEUE_FLUSH_FLAG_PENDING_BIT);
+
+ for (PPDMQUEUE pCur = pVM->pUVM->pdm.s.pQueuesForced; pCur; pCur = pCur->pNext)
+ if ( pCur->pPendingR3
+ || pCur->pPendingR0
+ || pCur->pPendingRC)
+ pdmR3QueueFlush(pCur);
+
+ ASMAtomicBitClear(&pVM->pdm.s.fQueueFlushing, PDM_QUEUE_FLUSH_FLAG_ACTIVE_BIT);
+
+ /* We're done if there were no inserts while we were busy. */
+ if ( !ASMBitTest(&pVM->pdm.s.fQueueFlushing, PDM_QUEUE_FLUSH_FLAG_PENDING_BIT)
+ && !VM_FF_IS_SET(pVM, VM_FF_PDM_QUEUES))
+ break;
+ VM_FF_CLEAR(pVM, VM_FF_PDM_QUEUES);
+ }
+}
+
+
+/**
+ * Process pending items in one queue.
+ *
+ * @returns Success indicator.
+ * If false the item the consumer said "enough!".
+ * @param pQueue The queue.
+ */
+static bool pdmR3QueueFlush(PPDMQUEUE pQueue)
+{
+ STAM_PROFILE_START(&pQueue->StatFlushPrf,p);
+
+ /*
+ * Get the lists.
+ */
+ PPDMQUEUEITEMCORE pItems = ASMAtomicXchgPtrT(&pQueue->pPendingR3, NULL, PPDMQUEUEITEMCORE);
+ RTRCPTR pItemsRC = ASMAtomicXchgRCPtr(&pQueue->pPendingRC, NIL_RTRCPTR);
+ RTR0PTR pItemsR0 = ASMAtomicXchgR0Ptr(&pQueue->pPendingR0, NIL_RTR0PTR);
+
+ AssertMsgReturn( pItemsR0
+ || pItemsRC
+ || pItems,
+ ("Someone is racing us? This shouldn't happen!\n"),
+ true);
+
+ /*
+ * Reverse the list (it's inserted in LIFO order to avoid semaphores, remember).
+ */
+ PPDMQUEUEITEMCORE pCur = pItems;
+ pItems = NULL;
+ while (pCur)
+ {
+ PPDMQUEUEITEMCORE pInsert = pCur;
+ pCur = pCur->pNextR3;
+ pInsert->pNextR3 = pItems;
+ pItems = pInsert;
+ }
+
+ /*
+ * Do the same for any pending RC items.
+ */
+ while (pItemsRC)
+ {
+ PPDMQUEUEITEMCORE pInsert = (PPDMQUEUEITEMCORE)MMHyperRCToR3(pQueue->pVMR3, pItemsRC);
+ pItemsRC = pInsert->pNextRC;
+ pInsert->pNextRC = NIL_RTRCPTR;
+ pInsert->pNextR3 = pItems;
+ pItems = pInsert;
+ }
+
+ /*
+ * Do the same for any pending R0 items.
+ */
+ while (pItemsR0)
+ {
+ PPDMQUEUEITEMCORE pInsert = (PPDMQUEUEITEMCORE)MMHyperR0ToR3(pQueue->pVMR3, pItemsR0);
+ pItemsR0 = pInsert->pNextR0;
+ pInsert->pNextR0 = NIL_RTR0PTR;
+ pInsert->pNextR3 = pItems;
+ pItems = pInsert;
+ }
+
+ /*
+ * Feed the items to the consumer function.
+ */
+ Log2(("pdmR3QueueFlush: pQueue=%p enmType=%d pItems=%p\n", pQueue, pQueue->enmType, pItems));
+ switch (pQueue->enmType)
+ {
+ case PDMQUEUETYPE_DEV:
+ while (pItems)
+ {
+ if (!pQueue->u.Dev.pfnCallback(pQueue->u.Dev.pDevIns, pItems))
+ break;
+ pCur = pItems;
+ pItems = pItems->pNextR3;
+ pdmR3QueueFreeItem(pQueue, pCur);
+ }
+ break;
+
+ case PDMQUEUETYPE_DRV:
+ while (pItems)
+ {
+ if (!pQueue->u.Drv.pfnCallback(pQueue->u.Drv.pDrvIns, pItems))
+ break;
+ pCur = pItems;
+ pItems = pItems->pNextR3;
+ pdmR3QueueFreeItem(pQueue, pCur);
+ }
+ break;
+
+ case PDMQUEUETYPE_INTERNAL:
+ while (pItems)
+ {
+ if (!pQueue->u.Int.pfnCallback(pQueue->pVMR3, pItems))
+ break;
+ pCur = pItems;
+ pItems = pItems->pNextR3;
+ pdmR3QueueFreeItem(pQueue, pCur);
+ }
+ break;
+
+ case PDMQUEUETYPE_EXTERNAL:
+ while (pItems)
+ {
+ if (!pQueue->u.Ext.pfnCallback(pQueue->u.Ext.pvUser, pItems))
+ break;
+ pCur = pItems;
+ pItems = pItems->pNextR3;
+ pdmR3QueueFreeItem(pQueue, pCur);
+ }
+ break;
+
+ default:
+ AssertMsgFailed(("Invalid queue type %d\n", pQueue->enmType));
+ break;
+ }
+
+ /*
+ * Success?
+ */
+ if (pItems)
+ {
+ /*
+ * Reverse the list.
+ */
+ pCur = pItems;
+ pItems = NULL;
+ while (pCur)
+ {
+ PPDMQUEUEITEMCORE pInsert = pCur;
+ pCur = pInsert->pNextR3;
+ pInsert->pNextR3 = pItems;
+ pItems = pInsert;
+ }
+
+ /*
+ * Insert the list at the tail of the pending list.
+ */
+ for (;;)
+ {
+ if (ASMAtomicCmpXchgPtr(&pQueue->pPendingR3, pItems, NULL))
+ break;
+ PPDMQUEUEITEMCORE pPending = ASMAtomicXchgPtrT(&pQueue->pPendingR3, NULL, PPDMQUEUEITEMCORE);
+ if (pPending)
+ {
+ pCur = pPending;
+ while (pCur->pNextR3)
+ pCur = pCur->pNextR3;
+ pCur->pNextR3 = pItems;
+ pItems = pPending;
+ }
+ }
+
+ STAM_REL_COUNTER_INC(&pQueue->StatFlushLeftovers);
+ STAM_PROFILE_STOP(&pQueue->StatFlushPrf,p);
+ return false;
+ }
+
+ STAM_PROFILE_STOP(&pQueue->StatFlushPrf,p);
+ return true;
+}
+
+
+/**
+ * Free an item.
+ *
+ * @param pQueue The queue.
+ * @param pItem The item.
+ */
+DECLINLINE(void) pdmR3QueueFreeItem(PPDMQUEUE pQueue, PPDMQUEUEITEMCORE pItem)
+{
+ VM_ASSERT_EMT(pQueue->pVMR3);
+
+ int i = pQueue->iFreeHead;
+ int iNext = (i + 1) % (pQueue->cItems + PDMQUEUE_FREE_SLACK);
+
+ pQueue->aFreeItems[i].pItemR3 = pItem;
+ if (pQueue->pVMRC)
+ {
+ pQueue->aFreeItems[i].pItemRC = MMHyperR3ToRC(pQueue->pVMR3, pItem);
+ pQueue->aFreeItems[i].pItemR0 = MMHyperR3ToR0(pQueue->pVMR3, pItem);
+ }
+
+ if (!ASMAtomicCmpXchgU32(&pQueue->iFreeHead, iNext, i))
+ AssertMsgFailed(("huh? i=%d iNext=%d iFreeHead=%d iFreeTail=%d\n", i, iNext, pQueue->iFreeHead, pQueue->iFreeTail));
+ STAM_STATS({ ASMAtomicDecU32(&pQueue->cStatPending); });
+}
+
+
+/**
+ * Timer handler for PDM queues.
+ * This is called by for a single queue.
+ *
+ * @param pVM The cross context VM structure.
+ * @param pTimer Pointer to timer.
+ * @param pvUser Pointer to the queue.
+ */
+static DECLCALLBACK(void) pdmR3QueueTimer(PVM pVM, PTMTIMER pTimer, void *pvUser)
+{
+ PPDMQUEUE pQueue = (PPDMQUEUE)pvUser;
+ Assert(pTimer == pQueue->pTimer); NOREF(pTimer); NOREF(pVM);
+
+ if ( pQueue->pPendingR3
+ || pQueue->pPendingR0
+ || pQueue->pPendingRC)
+ pdmR3QueueFlush(pQueue);
+ int rc = TMTimerSetMillies(pQueue->pTimer, pQueue->cMilliesInterval);
+ AssertRC(rc);
+}
+