summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorvboxsync <vboxsync@cfe28804-0f27-0410-a406-dd0f0b0b656f>2022-02-05 19:03:08 +0000
committervboxsync <vboxsync@cfe28804-0f27-0410-a406-dd0f0b0b656f>2022-02-05 19:03:08 +0000
commit3e8646c1ded46328b99dba1176f1174ef8d89ee8 (patch)
tree66d280c3a46914f0eba3bd75fdab94fc4545ef5d /src
parentd00b1193f2493e784a090bcd590ee23a21de5797 (diff)
downloadVirtualBox-svn-3e8646c1ded46328b99dba1176f1174ef8d89ee8.tar.gz
VMM/PDMQueue: Rewrote the queue code to not use the hyper heap and be a bit safer. Added a testcase (driverless). bugref:10093
git-svn-id: https://www.virtualbox.org/svn/vbox/trunk@93609 cfe28804-0f27-0410-a406-dd0f0b0b656f
Diffstat (limited to 'src')
-rw-r--r--src/VBox/VMM/Makefile.kmk1
-rw-r--r--src/VBox/VMM/VMMAll/PDMAll.cpp8
-rw-r--r--src/VBox/VMM/VMMAll/PDMAllQueue.cpp315
-rw-r--r--src/VBox/VMM/VMMR0/PDMR0DevHlp.cpp33
-rw-r--r--src/VBox/VMM/VMMR0/PDMR0DevHlpTracing.cpp6
-rw-r--r--src/VBox/VMM/VMMR0/PDMR0Device.cpp7
-rw-r--r--src/VBox/VMM/VMMR0/VMMR0.cpp8
-rw-r--r--src/VBox/VMM/VMMR3/PDMDevHlp.cpp22
-rw-r--r--src/VBox/VMM/VMMR3/PDMDevice.cpp5
-rw-r--r--src/VBox/VMM/VMMR3/PDMDriver.cpp24
-rw-r--r--src/VBox/VMM/VMMR3/PDMQueue.cpp795
-rw-r--r--src/VBox/VMM/include/PDMInternal.h193
-rw-r--r--src/VBox/VMM/testcase/Makefile.kmk9
-rw-r--r--src/VBox/VMM/testcase/tstPDMQueue.cpp459
14 files changed, 1252 insertions, 633 deletions
diff --git a/src/VBox/VMM/Makefile.kmk b/src/VBox/VMM/Makefile.kmk
index fea5e027c15..9e4c85ffc90 100644
--- a/src/VBox/VMM/Makefile.kmk
+++ b/src/VBox/VMM/Makefile.kmk
@@ -487,6 +487,7 @@ if defined(VBOX_WITH_R0_MODULES) && !defined(VBOX_ONLY_EXTPACKS)
VMMR0/PDMR0DevHlp.cpp \
$(if-expr defined(VBOX_WITH_DBGF_TRACING), VMMR0/PDMR0DevHlpTracing.cpp,) \
VMMR0/PDMR0Driver.cpp \
+ VMMR0/PDMR0Queue.cpp \
VMMR0/PGMR0.cpp \
VMMR0/PGMR0Pool.cpp \
VMMR0/PGMR0SharedPage.cpp \
diff --git a/src/VBox/VMM/VMMAll/PDMAll.cpp b/src/VBox/VMM/VMMAll/PDMAll.cpp
index bb743ce7303..f9d9480b1d8 100644
--- a/src/VBox/VMM/VMMAll/PDMAll.cpp
+++ b/src/VBox/VMM/VMMAll/PDMAll.cpp
@@ -211,13 +211,13 @@ VMM_INT_DECL(void) PDMIoApicBroadcastEoi(PVMCC pVM, uint8_t uVector)
else if (pIoApic->pDevInsR3)
{
/* Queue for ring-3 execution. */
- PPDMDEVHLPTASK pTask = (PPDMDEVHLPTASK)PDMQueueAlloc(pVM->pdm.s.pDevHlpQueueR0);
+ PPDMDEVHLPTASK pTask = (PPDMDEVHLPTASK)PDMQueueAlloc(pVM, pVM->pdm.s.hDevHlpQueue, pVM);
if (pTask)
{
pTask->enmOp = PDMDEVHLPTASKOP_IOAPIC_SET_EOI;
pTask->pDevInsR3 = NIL_RTR3PTR; /* not required */
pTask->u.IoApicSetEoi.uVector = uVector;
- PDMQueueInsertEx(pVM->pdm.s.pDevHlpQueueR0, &pTask->Core, 0);
+ PDMQueueInsert(pVM, pVM->pdm.s.hDevHlpQueue, pVM, &pTask->Core);
}
else
AssertMsgFailed(("We're out of devhlp queue items!!!\n"));
@@ -249,7 +249,7 @@ VMM_INT_DECL(void) PDMIoApicSendMsi(PVMCC pVM, PCIBDF uBusDevFn, PCMSIMSG pMsi,
else if (pIoApic->pDevInsR3)
{
/* Queue for ring-3 execution. */
- PPDMDEVHLPTASK pTask = (PPDMDEVHLPTASK)PDMQueueAlloc(pVM->pdm.s.pDevHlpQueueR0);
+ PPDMDEVHLPTASK pTask = (PPDMDEVHLPTASK)PDMQueueAlloc(pVM, pVM->pdm.s.hDevHlpQueue, pVM);
if (pTask)
{
pTask->enmOp = PDMDEVHLPTASKOP_IOAPIC_SEND_MSI;
@@ -257,7 +257,7 @@ VMM_INT_DECL(void) PDMIoApicSendMsi(PVMCC pVM, PCIBDF uBusDevFn, PCMSIMSG pMsi,
pTask->u.IoApicSendMsi.uBusDevFn = uBusDevFn;
pTask->u.IoApicSendMsi.Msi = *pMsi;
pTask->u.IoApicSendMsi.uTagSrc = uTagSrc;
- PDMQueueInsertEx(pVM->pdm.s.pDevHlpQueueR0, &pTask->Core, 0);
+ PDMQueueInsert(pVM, pVM->pdm.s.hDevHlpQueue, pVM, &pTask->Core);
}
else
AssertMsgFailed(("We're out of devhlp queue items!!!\n"));
diff --git a/src/VBox/VMM/VMMAll/PDMAllQueue.cpp b/src/VBox/VMM/VMMAll/PDMAllQueue.cpp
index 3eb841a876e..45e5624c8d6 100644
--- a/src/VBox/VMM/VMMAll/PDMAllQueue.cpp
+++ b/src/VBox/VMM/VMMAll/PDMAllQueue.cpp
@@ -30,47 +30,190 @@
#include <VBox/log.h>
#include <iprt/asm.h>
#include <iprt/assert.h>
+#include <iprt/string.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/*
+ * Macros for thoroughly validating a queue handle and ownership.
+ */
+#define PDMQUEUE_HANDLE_TO_VARS_RETURN_COMMON(a_cbMax, a_cbTotalMax) \
+ AssertReturn(cbItem >= sizeof(PDMQUEUEITEMCORE), pQueue->rcOkay = VERR_INTERNAL_ERROR_4); \
+ AssertReturn(cbItem <= (a_cbMax), pQueue->rcOkay = VERR_INTERNAL_ERROR_4); \
+ \
+ /* paranoia^3: */ \
+ AssertReturn(cItems > 0, pQueue->rcOkay = VERR_INTERNAL_ERROR_4); \
+ AssertReturn(cItems <= PDMQUEUE_MAX_ITEMS, pQueue->rcOkay = VERR_INTERNAL_ERROR_4); \
+ AssertReturn(cbItem * cItems <= (a_cbTotalMax), pQueue->rcOkay = VERR_INTERNAL_ERROR_4)
+
+#ifdef IN_RING0
+# define PDMQUEUE_HANDLE_TO_VARS_RETURN(a_pVM, a_hQueue, a_pvOwner) \
+ AssertPtrReturn((a_pvOwner), VERR_INVALID_PARAMETER); \
+ \
+ AssertCompile(RT_ELEMENTS((a_pVM)->pdm.s.apRing0Queues) == RT_ELEMENTS((a_pVM)->pdmr0.s.aQueues)); \
+ AssertReturn((a_hQueue) < RT_ELEMENTS((a_pVM)->pdmr0.s.aQueues), VERR_INVALID_HANDLE); \
+ AssertReturn((a_hQueue) < (a_pVM)->pdmr0.s.cQueues, VERR_INVALID_HANDLE); \
+ AssertReturn((a_pVM)->pdmr0.s.aQueues[(a_hQueue)].pvOwner == (a_pvOwner), VERR_INVALID_HANDLE); \
+ PPDMQUEUE pQueue = (a_pVM)->pdmr0.s.aQueues[(a_hQueue)].pQueue; \
+ AssertPtrReturn(pQueue, VERR_INVALID_HANDLE); \
+ AssertReturn(pQueue->u32Magic == PDMQUEUE_MAGIC, VERR_INVALID_HANDLE); \
+ AssertReturn(pQueue->rcOkay == VINF_SUCCESS, pQueue->rcOkay); \
+ \
+ uint32_t const cbItem = (a_pVM)->pdmr0.s.aQueues[(a_hQueue)].cbItem; \
+ uint32_t const cItems = (a_pVM)->pdmr0.s.aQueues[(a_hQueue)].cItems; \
+ uint32_t const offItems = (a_pVM)->pdmr0.s.aQueues[(a_hQueue)].offItems; \
+ \
+ /* paranoia^2: */ \
+ AssertReturn(pQueue->cbItem == cbItem, pQueue->rcOkay = VERR_INTERNAL_ERROR_3); \
+ AssertReturn(pQueue->cItems == cItems, pQueue->rcOkay = VERR_INTERNAL_ERROR_3); \
+ AssertReturn(pQueue->offItems == offItems, pQueue->rcOkay = VERR_INTERNAL_ERROR_3); \
+ \
+ PDMQUEUE_HANDLE_TO_VARS_RETURN_COMMON(PDMQUEUE_MAX_ITEM_SIZE, PDMQUEUE_MAX_TOTAL_SIZE_R0)
+
+#else
+# define PDMQUEUE_HANDLE_TO_VARS_RETURN(a_pVM, a_hQueue, a_pvOwner) \
+ AssertPtrReturn((a_pvOwner), VERR_INVALID_PARAMETER); \
+ \
+ PPDMQUEUE pQueue; \
+ if ((a_hQueue) < RT_ELEMENTS((a_pVM)->pdm.s.apRing0Queues)) \
+ pQueue = (a_pVM)->pdm.s.apRing0Queues[(a_hQueue)]; \
+ else \
+ { \
+ (a_hQueue) -= RT_ELEMENTS((a_pVM)->pdm.s.apRing0Queues); \
+ AssertReturn((a_pVM)->pdm.s.cRing3Queues, VERR_INVALID_HANDLE); \
+ pQueue = (a_pVM)->pdm.s.papRing3Queues[(a_hQueue)]; \
+ } \
+ AssertPtrReturn(pQueue, VERR_INVALID_HANDLE); \
+ AssertReturn(pQueue->u32Magic == PDMQUEUE_MAGIC, VERR_INVALID_HANDLE); \
+ AssertReturn(pQueue->u.Gen.pvOwner == (a_pvOwner), VERR_INVALID_HANDLE); \
+ AssertReturn(pQueue->rcOkay == VINF_SUCCESS, pQueue->rcOkay); \
+ \
+ uint32_t const cbItem = pQueue->cbItem; \
+ uint32_t const cItems = pQueue->cItems; \
+ uint32_t const offItems = pQueue->offItems; \
+ \
+ PDMQUEUE_HANDLE_TO_VARS_RETURN_COMMON(PDMQUEUE_MAX_ITEM_SIZE, PDMQUEUE_MAX_TOTAL_SIZE_R3)
+
+#endif
/**
- * Allocate an item from a queue.
+ * Commmon function for initializing the shared queue structure.
+ */
+void pdmQueueInit(PPDMQUEUE pQueue, uint32_t cbBitmap, uint32_t cbItem, uint32_t cItems,
+ const char *pszName, PDMQUEUETYPE enmType, RTR3PTR pfnCallback, RTR3PTR pvOwner)
+{
+ Assert(cbBitmap * 8 >= cItems);
+
+ pQueue->u32Magic = PDMQUEUE_MAGIC;
+ pQueue->cbItem = cbItem;
+ pQueue->cItems = cItems;
+ pQueue->offItems = RT_UOFFSETOF(PDMQUEUE, bmAlloc) + cbBitmap;
+ pQueue->rcOkay = VINF_SUCCESS;
+ pQueue->u32Padding = 0;
+ pQueue->hTimer = NIL_TMTIMERHANDLE;
+ pQueue->cMilliesInterval = 0;
+ pQueue->enmType = enmType;
+ pQueue->u.Gen.pfnCallback = pfnCallback;
+ pQueue->u.Gen.pvOwner = pvOwner;
+ RTStrCopy(pQueue->szName, sizeof(pQueue->szName), pszName);
+ pQueue->iPending = UINT32_MAX;
+ RT_BZERO(pQueue->bmAlloc, cbBitmap);
+ ASMBitSetRange(pQueue->bmAlloc, 0, cItems);
+
+ uint8_t *pbItem = (uint8_t *)&pQueue->bmAlloc[0] + cbBitmap;
+ while (cItems-- > 0)
+ {
+ ((PPDMQUEUEITEMCORE)pbItem)->u64View = UINT64_C(0xfeedfeedfeedfeed);
+
+ /* next */
+ pbItem += cbItem;
+ }
+}
+
+
+/**
+ * Allocate an item from a queue, extended version.
+ *
* The allocated item must be handed on to PDMR3QueueInsert() after the
* data have been filled in.
*
- * @returns Pointer to allocated queue item.
- * @returns NULL on failure. The queue is exhausted.
- * @param pQueue The queue handle.
+ * @returns VBox status code.
+ * @param pVM Pointer to the cross context VM structure w/ ring-0.
+ * @param hQueue The queue handle.
+ * @param pvOwner The queue owner.
+ * @param ppNew Where to return the item pointer on success.
* @thread Any thread.
*/
-VMMDECL(PPDMQUEUEITEMCORE) PDMQueueAlloc(PPDMQUEUE pQueue)
+VMMDECL(int) PDMQueueAllocEx(PVMCC pVM, PDMQUEUEHANDLE hQueue, void *pvOwner, PPDMQUEUEITEMCORE *ppNew)
{
- Assert(RT_VALID_PTR(pQueue) && pQueue->CTX_SUFF(pVM));
- PPDMQUEUEITEMCORE pNew;
- uint32_t iNext;
- uint32_t i;
- do
+ /*
+ * Validate and translate input.
+ */
+ *ppNew = NULL;
+ PDMQUEUE_HANDLE_TO_VARS_RETURN(pVM, hQueue, pvOwner);
+
+ /*
+ * Do the allocation.
+ */
+ uint32_t cEmptyScans = 0;
+ for (;;)
{
- i = pQueue->iFreeTail;
- if (i == pQueue->iFreeHead)
+ int32_t iBit = ASMBitFirstSet(pQueue->bmAlloc, cItems);
+ if (iBit >= 0)
+ {
+ if (ASMAtomicBitTestAndClear(pQueue->bmAlloc, iBit))
+ {
+ PPDMQUEUEITEMCORE pNew = (PPDMQUEUEITEMCORE)&((uint8_t *)pQueue)[offItems + iBit * cbItem];
+ pNew->u64View = UINT64_C(0xbeefbeefbeefbeef);
+ *ppNew = pNew;
+ return VINF_SUCCESS;
+ }
+ cEmptyScans = 0;
+ }
+ else if (++cEmptyScans < 16)
+ ASMNopPause();
+ else
{
STAM_REL_COUNTER_INC(&pQueue->StatAllocFailures);
- return NULL;
+ return VERR_OUT_OF_RESOURCES;
}
- pNew = pQueue->aFreeItems[i].CTX_SUFF(pItem);
- iNext = (i + 1) % (pQueue->cItems + PDMQUEUE_FREE_SLACK);
- } while (!ASMAtomicCmpXchgU32(&pQueue->iFreeTail, iNext, i));
- return pNew;
+ }
+}
+
+
+/**
+ * Allocate an item from a queue.
+ *
+ * The allocated item must be handed on to PDMR3QueueInsert() after the
+ * data have been filled in.
+ *
+ * @returns VBox status code.
+ * @param pVM Pointer to the cross context VM structure w/ ring-0.
+ * @param hQueue The queue handle.
+ * @param pvOwner The queue owner.
+ * @param ppNew Where to return the item pointer on success.
+ * @thread Any thread.
+ */
+VMMDECL(PPDMQUEUEITEMCORE) PDMQueueAlloc(PVMCC pVM, PDMQUEUEHANDLE hQueue, void *pvOwner)
+{
+ PPDMQUEUEITEMCORE pNew = NULL;
+ int rc = PDMQueueAllocEx(pVM, hQueue, pvOwner, &pNew);
+ if (RT_SUCCESS(rc))
+ return pNew;
+ return NULL;
}
/**
* Sets the FFs and fQueueFlushed.
*
- * @param pQueue The PDM queue.
+ * @param pVM Pointer to the cross context VM structure w/ ring-0.
*/
-static void pdmQueueSetFF(PPDMQUEUE pQueue)
+static void pdmQueueSetFF(PVMCC pVM)
{
- PVM pVM = pQueue->CTX_SUFF(pVM);
Log2(("PDMQueueInsert: VM_FF_PDM_QUEUES %d -> 1\n", VM_FF_IS_SET(pVM, VM_FF_PDM_QUEUES)));
VM_FF_SET(pVM, VM_FF_PDM_QUEUES);
ASMAtomicBitSet(&pVM->pdm.s.fQueueFlushing, PDM_QUEUE_FLUSH_FLAG_PENDING_BIT);
@@ -86,106 +229,76 @@ static void pdmQueueSetFF(PPDMQUEUE pQueue)
* The item must have been obtained using PDMQueueAlloc(). Once the item
* have been passed to this function it must not be touched!
*
- * @param pQueue The queue handle.
- * @param pItem The item to insert.
+ * @returns VBox status code.
+ * @param pVM Pointer to the cross context VM structure w/ ring-0.
+ * @param hQueue The queue handle.
+ * @param pvOwner The queue owner.
+ * @param pInsert The item to insert.
* @thread Any thread.
*/
-VMMDECL(void) PDMQueueInsert(PPDMQUEUE pQueue, PPDMQUEUEITEMCORE pItem)
+VMMDECL(int) PDMQueueInsert(PVMCC pVM, PDMQUEUEHANDLE hQueue, void *pvOwner, PPDMQUEUEITEMCORE pInsert)
{
- Assert(RT_VALID_PTR(pQueue) && pQueue->CTX_SUFF(pVM));
- AssertPtr(pItem);
+ /*
+ * Validate and translate input.
+ */
+ PDMQUEUE_HANDLE_TO_VARS_RETURN(pVM, hQueue, pvOwner);
-#if 0 /* the paranoid android version: */
- void *pvNext;
- do
- {
- pvNext = ASMAtomicUoReadPtr((void * volatile *)&pQueue->CTX_SUFF(pPending));
- ASMAtomicUoWritePtr((void * volatile *)&pItem->CTX_SUFF(pNext), pvNext);
- } while (!ASMAtomicCmpXchgPtr(&pQueue->CTX_SUFF(pPending), pItem, pvNext));
-#else
- PPDMQUEUEITEMCORE pNext;
- do
- {
- pNext = pQueue->CTX_SUFF(pPending);
- pItem->CTX_SUFF(pNext) = pNext;
- } while (!ASMAtomicCmpXchgPtr(&pQueue->CTX_SUFF(pPending), pItem, pNext));
-#endif
+ uint8_t * const pbItems = (uint8_t *)pQueue + offItems;
+ uintptr_t const offInsert = (uintptr_t)pInsert - (uintptr_t)pbItems;
+ uintptr_t const iInsert = offInsert / cbItem;
+ AssertReturn(iInsert < cItems, VERR_INVALID_PARAMETER);
+ AssertReturn(iInsert * cbItem == offInsert, VERR_INVALID_PARAMETER);
- if (pQueue->hTimer == NIL_TMTIMERHANDLE)
- pdmQueueSetFF(pQueue);
- STAM_REL_COUNTER_INC(&pQueue->StatInsert);
- STAM_STATS({ ASMAtomicIncU32(&pQueue->cStatPending); });
-}
-
-
-/**
- * Queue an item.
- *
- * The item must have been obtained using PDMQueueAlloc(). Once the item
- * have been passed to this function it must not be touched!
- *
- * @param pQueue The queue handle.
- * @param pItem The item to insert.
- * @param NanoMaxDelay The maximum delay before processing the queue, in nanoseconds.
- * This applies only to GC.
- * @thread Any thread.
- */
-VMMDECL(void) PDMQueueInsertEx(PPDMQUEUE pQueue, PPDMQUEUEITEMCORE pItem, uint64_t NanoMaxDelay)
-{
- NOREF(NanoMaxDelay);
- PDMQueueInsert(pQueue, pItem);
-#ifdef IN_RC
- PVM pVM = pQueue->CTX_SUFF(pVM);
- /** @todo figure out where to put this, the next bit should go there too.
- if (NanoMaxDelay)
- {
+ AssertReturn(ASMBitTest(pQueue->bmAlloc, iInsert) == false, VERR_INVALID_PARAMETER);
- }
- else */
+ /*
+ * Append the item to the pending list.
+ */
+ for (;;)
{
- VMCPU_FF_SET(VMMGetCpu0(pVM), VMCPU_FF_TO_R3);
- Log2(("PDMQueueInsertEx: Setting VMCPU_FF_TO_R3\n"));
+ uint32_t const iOldPending = ASMAtomicUoReadU32(&pQueue->iPending);
+ pInsert->iNext = iOldPending;
+ if (ASMAtomicCmpXchgU32(&pQueue->iPending, iInsert, iOldPending))
+ break;
+ ASMNopPause();
}
-#endif
-}
+ if (pQueue->hTimer == NIL_TMTIMERHANDLE)
+ pdmQueueSetFF(pVM);
+ STAM_REL_COUNTER_INC(&pQueue->StatInsert);
+ STAM_STATS({ ASMAtomicIncU32(&pQueue->cStatPending); });
-/**
- * Gets the ring-0 pointer for the specified queue.
- *
- * @returns The ring-0 address of the queue.
- * @returns NULL if pQueue is invalid.
- * @param pQueue The queue handle.
- */
-VMMDECL(R0PTRTYPE(PPDMQUEUE)) PDMQueueR0Ptr(PPDMQUEUE pQueue)
-{
- AssertPtr(pQueue);
- Assert(pQueue->pVMR3);
-#ifdef IN_RING0
- AssertPtr(pQueue->pVMR0);
- return pQueue;
-#else
- Assert(pQueue->pVMR0 || SUPR3IsDriverless());
- return MMHyperCCToR0(pQueue->CTX_SUFF(pVM), pQueue);
-#endif
+ return VINF_SUCCESS;
}
/**
* Schedule the queue for flushing (processing) if necessary.
*
- * @returns @c true if necessary, @c false if not.
- * @param pQueue The queue.
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS if a flush was necessary.
+ * @retval VINF_NO_CHANGE if no flushing needed.
+ *
+ * @param pVM The cross context VM structure.
+ * @param pvOwner The alleged queue owner.
+ * @param hQueue The queueu to maybe flush.
*/
-VMMDECL(bool) PDMQueueFlushIfNecessary(PPDMQUEUE pQueue)
+VMMDECL(int) PDMQueueFlushIfNecessary(PVMCC pVM, PDMQUEUEHANDLE hQueue, void *pvOwner)
{
- AssertPtr(pQueue);
- if ( pQueue->pPendingR3 != NIL_RTR3PTR
- || pQueue->pPendingR0 != NIL_RTR0PTR)
+ /*
+ * Validate input.
+ */
+ PDMQUEUE_HANDLE_TO_VARS_RETURN(pVM, hQueue, pvOwner);
+ RT_NOREF(offItems);
+
+ /*
+ * Check and maybe flush.
+ */
+ if (ASMAtomicUoReadU32(&pQueue->iPending) != UINT32_MAX)
{
- pdmQueueSetFF(pQueue);
- return false;
+ pdmQueueSetFF(pVM);
+ return VINF_SUCCESS;
}
- return false;
+ return VINF_NO_CHANGE;
}
diff --git a/src/VBox/VMM/VMMR0/PDMR0DevHlp.cpp b/src/VBox/VMM/VMMR0/PDMR0DevHlp.cpp
index 1da83c73865..729ebb11579 100644
--- a/src/VBox/VMM/VMMR0/PDMR0DevHlp.cpp
+++ b/src/VBox/VMM/VMMR0/PDMR0DevHlp.cpp
@@ -244,7 +244,7 @@ static DECLCALLBACK(void) pdmR0DevHlp_PCISetIrq(PPDMDEVINS pDevIns, PPDMPCIDEV p
pdmUnlock(pGVM);
/* queue for ring-3 execution. */
- PPDMDEVHLPTASK pTask = (PPDMDEVHLPTASK)PDMQueueAlloc(pGVM->pdm.s.pDevHlpQueueR0);
+ PPDMDEVHLPTASK pTask = (PPDMDEVHLPTASK)PDMQueueAlloc(pGVM, pGVM->pdm.s.hDevHlpQueue, pGVM);
AssertReturnVoid(pTask);
pTask->enmOp = PDMDEVHLPTASKOP_PCI_SET_IRQ;
@@ -252,9 +252,9 @@ static DECLCALLBACK(void) pdmR0DevHlp_PCISetIrq(PPDMDEVINS pDevIns, PPDMPCIDEV p
pTask->u.PciSetIrq.iIrq = iIrq;
pTask->u.PciSetIrq.iLevel = iLevel;
pTask->u.PciSetIrq.uTagSrc = uTagSrc;
- pTask->u.PciSetIrq.pPciDevR3 = MMHyperR0ToR3(pGVM, pPciDev);
+ pTask->u.PciSetIrq.idxPciDev = pPciDev->Int.s.idxSubDev;
- PDMQueueInsertEx(pGVM->pdm.s.pDevHlpQueueR0, &pTask->Core, 0);
+ PDMQueueInsert(pGVM, pGVM->pdm.s.hDevHlpQueue, pGVM, &pTask->Core);
}
LogFlow(("pdmR0DevHlp_PCISetIrq: caller=%p/%d: returns void; uTagSrc=%#x\n", pDevIns, pDevIns->iInstance, uTagSrc));
@@ -580,32 +580,27 @@ static DECLCALLBACK(uint64_t) pdmR0DevHlp_TMTimeVirtGetNano(PPDMDEVINS pDevIns)
}
-/** Converts a queue handle to a ring-0 queue pointer. */
-DECLINLINE(PPDMQUEUE) pdmR0DevHlp_QueueToPtr(PPDMDEVINS pDevIns, PDMQUEUEHANDLE hQueue)
-{
- PDMDEV_ASSERT_DEVINS(pDevIns);
- return (PPDMQUEUE)MMHyperR3ToCC(pDevIns->Internal.s.pGVM, hQueue);
-}
-
-
/** @interface_method_impl{PDMDEVHLPR0,pfnQueueAlloc} */
static DECLCALLBACK(PPDMQUEUEITEMCORE) pdmR0DevHlp_QueueAlloc(PPDMDEVINS pDevIns, PDMQUEUEHANDLE hQueue)
{
- return PDMQueueAlloc(pdmR0DevHlp_QueueToPtr(pDevIns, hQueue));
+ PDMDEV_ASSERT_DEVINS(pDevIns);
+ return PDMQueueAlloc(pDevIns->Internal.s.pGVM, hQueue, pDevIns);
}
/** @interface_method_impl{PDMDEVHLPR0,pfnQueueInsert} */
-static DECLCALLBACK(void) pdmR0DevHlp_QueueInsert(PPDMDEVINS pDevIns, PDMQUEUEHANDLE hQueue, PPDMQUEUEITEMCORE pItem)
+static DECLCALLBACK(int) pdmR0DevHlp_QueueInsert(PPDMDEVINS pDevIns, PDMQUEUEHANDLE hQueue, PPDMQUEUEITEMCORE pItem)
{
- return PDMQueueInsert(pdmR0DevHlp_QueueToPtr(pDevIns, hQueue), pItem);
+ PDMDEV_ASSERT_DEVINS(pDevIns);
+ return PDMQueueInsert(pDevIns->Internal.s.pGVM, hQueue, pDevIns, pItem);
}
/** @interface_method_impl{PDMDEVHLPR0,pfnQueueFlushIfNecessary} */
static DECLCALLBACK(bool) pdmR0DevHlp_QueueFlushIfNecessary(PPDMDEVINS pDevIns, PDMQUEUEHANDLE hQueue)
{
- return PDMQueueFlushIfNecessary(pdmR0DevHlp_QueueToPtr(pDevIns, hQueue));
+ PDMDEV_ASSERT_DEVINS(pDevIns);
+ return PDMQueueFlushIfNecessary(pDevIns->Internal.s.pGVM, hQueue, pDevIns) == VINF_SUCCESS;
}
@@ -1785,7 +1780,7 @@ static DECLCALLBACK(void) pdmR0PciHlp_IoApicSetIrq(PPDMDEVINS pDevIns, PCIBDF uB
else if (pGVM->pdm.s.IoApic.pDevInsR3)
{
/* queue for ring-3 execution. */
- PPDMDEVHLPTASK pTask = (PPDMDEVHLPTASK)PDMQueueAlloc(pGVM->pdm.s.pDevHlpQueueR0);
+ PPDMDEVHLPTASK pTask = (PPDMDEVHLPTASK)PDMQueueAlloc(pGVM, pGVM->pdm.s.hDevHlpQueue, pGVM);
if (pTask)
{
pTask->enmOp = PDMDEVHLPTASKOP_IOAPIC_SET_IRQ;
@@ -1795,7 +1790,7 @@ static DECLCALLBACK(void) pdmR0PciHlp_IoApicSetIrq(PPDMDEVINS pDevIns, PCIBDF uB
pTask->u.IoApicSetIrq.iLevel = iLevel;
pTask->u.IoApicSetIrq.uTagSrc = uTagSrc;
- PDMQueueInsertEx(pGVM->pdm.s.pDevHlpQueueR0, &pTask->Core, 0);
+ PDMQueueInsert(pGVM, pGVM->pdm.s.hDevHlpQueue, pGVM, &pTask->Core);
}
else
AssertMsgFailed(("We're out of devhlp queue items!!!\n"));
@@ -1974,7 +1969,7 @@ DECLHIDDEN(bool) pdmR0IsaSetIrq(PGVM pGVM, int iIrq, int iLevel, uint32_t uTagSr
}
/* queue for ring-3 execution. */
- PPDMDEVHLPTASK pTask = (PPDMDEVHLPTASK)PDMQueueAlloc(pGVM->pdm.s.pDevHlpQueueR0);
+ PPDMDEVHLPTASK pTask = (PPDMDEVHLPTASK)PDMQueueAlloc(pGVM, pGVM->pdm.s.hDevHlpQueue, pGVM);
AssertReturn(pTask, false);
pTask->enmOp = PDMDEVHLPTASKOP_ISA_SET_IRQ;
@@ -1984,7 +1979,7 @@ DECLHIDDEN(bool) pdmR0IsaSetIrq(PGVM pGVM, int iIrq, int iLevel, uint32_t uTagSr
pTask->u.IsaSetIrq.iLevel = iLevel;
pTask->u.IsaSetIrq.uTagSrc = uTagSrc;
- PDMQueueInsertEx(pGVM->pdm.s.pDevHlpQueueR0, &pTask->Core, 0);
+ PDMQueueInsert(pGVM, pGVM->pdm.s.hDevHlpQueue, pGVM, &pTask->Core);
return false;
}
diff --git a/src/VBox/VMM/VMMR0/PDMR0DevHlpTracing.cpp b/src/VBox/VMM/VMMR0/PDMR0DevHlpTracing.cpp
index d93e7c358e8..1508b0a5fc5 100644
--- a/src/VBox/VMM/VMMR0/PDMR0DevHlpTracing.cpp
+++ b/src/VBox/VMM/VMMR0/PDMR0DevHlpTracing.cpp
@@ -407,7 +407,7 @@ DECL_HIDDEN_CALLBACK(void) pdmR0DevHlpTracing_PCISetIrq(PPDMDEVINS pDevIns, PPDM
pdmUnlock(pGVM);
/* queue for ring-3 execution. */
- PPDMDEVHLPTASK pTask = (PPDMDEVHLPTASK)PDMQueueAlloc(pGVM->pdm.s.pDevHlpQueueR0);
+ PPDMDEVHLPTASK pTask = (PPDMDEVHLPTASK)PDMQueueAlloc(pGVM, pGVM->pdm.s.hDevHlpQueue, pGVM);
AssertReturnVoid(pTask);
pTask->enmOp = PDMDEVHLPTASKOP_PCI_SET_IRQ;
@@ -415,9 +415,9 @@ DECL_HIDDEN_CALLBACK(void) pdmR0DevHlpTracing_PCISetIrq(PPDMDEVINS pDevIns, PPDM
pTask->u.PciSetIrq.iIrq = iIrq;
pTask->u.PciSetIrq.iLevel = iLevel;
pTask->u.PciSetIrq.uTagSrc = uTagSrc;
- pTask->u.PciSetIrq.pPciDevR3 = MMHyperR0ToR3(pGVM, pPciDev);
+ pTask->u.PciSetIrq.idxPciDev = pPciDev->Int.s.idxSubDev;
- PDMQueueInsertEx(pGVM->pdm.s.pDevHlpQueueR0, &pTask->Core, 0);
+ PDMQueueInsert(pGVM, pGVM->pdm.s.hDevHlpQueue, pGVM, &pTask->Core);
}
LogFlow(("pdmR0DevHlpTracing_PCISetIrq: caller=%p/%d: returns void; uTagSrc=%#x\n", pDevIns, pDevIns->iInstance, uTagSrc));
diff --git a/src/VBox/VMM/VMMR0/PDMR0Device.cpp b/src/VBox/VMM/VMMR0/PDMR0Device.cpp
index 4aeba45a87d..e2bcf2b862d 100644
--- a/src/VBox/VMM/VMMR0/PDMR0Device.cpp
+++ b/src/VBox/VMM/VMMR0/PDMR0Device.cpp
@@ -193,6 +193,13 @@ VMMR0_INT_DECL(void) PDMR0CleanupVM(PGVM pGVM)
if (pDevIns)
pdmR0DeviceDestroy(pGVM, pDevIns, i);
}
+
+ i = pGVM->pdmr0.s.cQueues;
+ while (i-- > 0)
+ {
+ if (pGVM->pdmr0.s.aQueues[i].pQueue != NULL)
+ pdmR0QueueDestroy(pGVM, i);
+ }
}
diff --git a/src/VBox/VMM/VMMR0/VMMR0.cpp b/src/VBox/VMM/VMMR0/VMMR0.cpp
index 2cf8369a61a..e5f257182c4 100644
--- a/src/VBox/VMM/VMMR0/VMMR0.cpp
+++ b/src/VBox/VMM/VMMR0/VMMR0.cpp
@@ -2071,6 +2071,14 @@ DECL_NO_INLINE(static, int) vmmR0EntryExWorker(PGVM pGVM, VMCPUID idCpu, VMMR0OP
break;
}
+ case VMMR0_DO_PDM_QUEUE_CREATE:
+ {
+ if (!pReqHdr || u64Arg || idCpu != 0)
+ return VERR_INVALID_PARAMETER;
+ rc = PDMR0QueueCreateReqHandler(pGVM, (PPDMQUEUECREATEREQ)pReqHdr);
+ break;
+ }
+
/*
* Requests to the internal networking service.
*/
diff --git a/src/VBox/VMM/VMMR3/PDMDevHlp.cpp b/src/VBox/VMM/VMMR3/PDMDevHlp.cpp
index c92591847a3..cc6ca5814c1 100644
--- a/src/VBox/VMM/VMMR3/PDMDevHlp.cpp
+++ b/src/VBox/VMM/VMMR3/PDMDevHlp.cpp
@@ -2746,11 +2746,10 @@ static DECLCALLBACK(int) pdmR3DevHlp_QueueCreate(PPDMDEVINS pDevIns, size_t cbIt
AssertLogRelReturn(pszName, VERR_NO_MEMORY);
}
- PPDMQUEUE pQueue = NULL;
- int rc = PDMR3QueueCreateDevice(pVM, pDevIns, cbItem, cItems, cMilliesInterval, pfnCallback, fRZEnabled, pszName, &pQueue);
- *phQueue = (uintptr_t)pQueue;
+ int rc = PDMR3QueueCreateDevice(pVM, pDevIns, cbItem, cItems, cMilliesInterval, pfnCallback, fRZEnabled, pszName, phQueue);
- LogFlow(("pdmR3DevHlp_QueueCreate: caller='%s'/%d: returns %Rrc *ppQueue=%p\n", pDevIns->pReg->szName, pDevIns->iInstance, rc, *phQueue));
+ LogFlow(("pdmR3DevHlp_QueueCreate: caller='%s'/%d: returns %Rrc *phQueue=%p\n",
+ pDevIns->pReg->szName, pDevIns->iInstance, rc, *phQueue));
return rc;
}
@@ -2769,21 +2768,22 @@ DECLINLINE(PPDMQUEUE) pdmR3DevHlp_QueueToPtr(PPDMDEVINS pDevIns, PDMQUEUEHANDLE
/** @interface_method_impl{PDMDEVHLPR3,pfnQueueAlloc} */
static DECLCALLBACK(PPDMQUEUEITEMCORE) pdmR3DevHlp_QueueAlloc(PPDMDEVINS pDevIns, PDMQUEUEHANDLE hQueue)
{
- return PDMQueueAlloc(pdmR3DevHlp_QueueToPtr(pDevIns, hQueue));
+ PDMDEV_ASSERT_DEVINS(pDevIns);
+ return PDMQueueAlloc(pDevIns->Internal.s.pVMR3, hQueue, pDevIns);
}
/** @interface_method_impl{PDMDEVHLPR3,pfnQueueInsert} */
-static DECLCALLBACK(void) pdmR3DevHlp_QueueInsert(PPDMDEVINS pDevIns, PDMQUEUEHANDLE hQueue, PPDMQUEUEITEMCORE pItem)
+static DECLCALLBACK(int) pdmR3DevHlp_QueueInsert(PPDMDEVINS pDevIns, PDMQUEUEHANDLE hQueue, PPDMQUEUEITEMCORE pItem)
{
- return PDMQueueInsert(pdmR3DevHlp_QueueToPtr(pDevIns, hQueue), pItem);
+ return PDMQueueInsert(pDevIns->Internal.s.pVMR3, hQueue, pDevIns, pItem);
}
/** @interface_method_impl{PDMDEVHLPR3,pfnQueueFlushIfNecessary} */
static DECLCALLBACK(bool) pdmR3DevHlp_QueueFlushIfNecessary(PPDMDEVINS pDevIns, PDMQUEUEHANDLE hQueue)
{
- return PDMQueueFlushIfNecessary(pdmR3DevHlp_QueueToPtr(pDevIns, hQueue));
+ return PDMQueueFlushIfNecessary(pDevIns->Internal.s.pVMR3, hQueue, pDevIns) == VINF_SUCCESS;
}
@@ -6359,7 +6359,9 @@ DECLCALLBACK(bool) pdmR3DevHlpQueueConsumer(PVM pVM, PPDMQUEUEITEMCORE pItem)
case PDMDEVHLPTASKOP_PCI_SET_IRQ:
{
/* Same as pdmR3DevHlp_PCISetIrq, except we've got a tag already. */
- PPDMPCIDEV pPciDev = pTask->u.PciSetIrq.pPciDevR3;
+ PPDMDEVINSR3 pDevIns = pTask->pDevInsR3;
+ PPDMPCIDEV pPciDev = pTask->u.PciSetIrq.idxPciDev < RT_ELEMENTS(pDevIns->apPciDevs)
+ ? pDevIns->apPciDevs[pTask->u.PciSetIrq.idxPciDev] : NULL;
if (pPciDev)
{
size_t const idxBus = pPciDev->Int.s.idxPdmBus;
@@ -6372,7 +6374,7 @@ DECLCALLBACK(bool) pdmR3DevHlpQueueConsumer(PVM pVM, PPDMQUEUEITEMCORE pItem)
pdmUnlock(pVM);
}
else
- AssertReleaseMsgFailed(("No PCI device registered!\n"));
+ AssertReleaseMsgFailed(("No PCI device given! (%#x)\n", pPciDev->Int.s.idxSubDev));
break;
}
diff --git a/src/VBox/VMM/VMMR3/PDMDevice.cpp b/src/VBox/VMM/VMMR3/PDMDevice.cpp
index ec49a35e5c1..60bc61bf47d 100644
--- a/src/VBox/VMM/VMMR3/PDMDevice.cpp
+++ b/src/VBox/VMM/VMMR3/PDMDevice.cpp
@@ -130,10 +130,9 @@ int pdmR3DevInit(PVM pVM)
/*
* Get the RC & R0 devhlps and create the devhlp R3 task queue.
*/
- rc = PDMR3QueueCreateInternal(pVM, sizeof(PDMDEVHLPTASK), 8, 0, pdmR3DevHlpQueueConsumer, true, "DevHlp",
- &pVM->pdm.s.pDevHlpQueueR3);
+ rc = PDMR3QueueCreateInternal(pVM, sizeof(PDMDEVHLPTASK), pVM->cCpus * 8, 0, pdmR3DevHlpQueueConsumer, true, "DevHlp",
+ &pVM->pdm.s.hDevHlpQueue);
AssertRCReturn(rc, rc);
- pVM->pdm.s.pDevHlpQueueR0 = PDMQueueR0Ptr(pVM->pdm.s.pDevHlpQueueR3);
/*
*
diff --git a/src/VBox/VMM/VMMR3/PDMDriver.cpp b/src/VBox/VMM/VMMR3/PDMDriver.cpp
index cd7a261e5b2..53f4ffee02c 100644
--- a/src/VBox/VMM/VMMR3/PDMDriver.cpp
+++ b/src/VBox/VMM/VMMR3/PDMDriver.cpp
@@ -1254,17 +1254,6 @@ static DECLCALLBACK(PSUPDRVSESSION) pdmR3DrvHlp_GetSupDrvSession(PPDMDRVINS pDrv
}
-/**
- * Conversion from handle to queue pointer (temporary).
- */
-DECLINLINE(PPDMQUEUE) pdmR3DrvHlp_QueueToPtr(PPDMDRVINS pDrvIns, PDMQUEUEHANDLE hQueue)
-{
- PDMDRV_ASSERT_DRVINS(pDrvIns);
- RT_NOREF(pDrvIns);
- return (PPDMQUEUE)hQueue;
-}
-
-
/** @interface_method_impl{PDMDRVHLPR3,pfnQueueCreate} */
static DECLCALLBACK(int) pdmR3DrvHlp_QueueCreate(PPDMDRVINS pDrvIns, uint32_t cbItem, uint32_t cItems, uint32_t cMilliesInterval,
PFNPDMQUEUEDRV pfnCallback, const char *pszName, PDMQUEUEHANDLE *phQueue)
@@ -1281,9 +1270,7 @@ static DECLCALLBACK(int) pdmR3DrvHlp_QueueCreate(PPDMDRVINS pDrvIns, uint32_t cb
AssertLogRelReturn(pszName, VERR_NO_MEMORY);
}
- PPDMQUEUE pQueue = NULL;
- int rc = PDMR3QueueCreateDriver(pVM, pDrvIns, cbItem, cItems, cMilliesInterval, pfnCallback, pszName, &pQueue);
- *phQueue = (PDMQUEUEHANDLE)pQueue;
+ int rc = PDMR3QueueCreateDriver(pVM, pDrvIns, cbItem, cItems, cMilliesInterval, pfnCallback, pszName, phQueue);
LogFlow(("pdmR3DrvHlp_PDMQueueCreate: caller='%s'/%d: returns %Rrc *phQueue=%p\n", pDrvIns->pReg->szName, pDrvIns->iInstance, rc, *phQueue));
return rc;
@@ -1293,25 +1280,24 @@ static DECLCALLBACK(int) pdmR3DrvHlp_QueueCreate(PPDMDRVINS pDrvIns, uint32_t cb
/** @interface_method_impl{PDMDRVHLPR3,pfnQueueAlloc} */
static DECLCALLBACK(PPDMQUEUEITEMCORE) pdmR3DrvHlp_QueueAlloc(PPDMDRVINS pDrvIns, PDMQUEUEHANDLE hQueue)
{
- return PDMQueueAlloc(pdmR3DrvHlp_QueueToPtr(pDrvIns, hQueue));
+ return PDMQueueAlloc(pDrvIns->Internal.s.pVMR3, hQueue, pDrvIns);
}
/** @interface_method_impl{PDMDRVHLPR3,pfnQueueInsert} */
-static DECLCALLBACK(void) pdmR3DrvHlp_QueueInsert(PPDMDRVINS pDrvIns, PDMQUEUEHANDLE hQueue, PPDMQUEUEITEMCORE pItem)
+static DECLCALLBACK(int) pdmR3DrvHlp_QueueInsert(PPDMDRVINS pDrvIns, PDMQUEUEHANDLE hQueue, PPDMQUEUEITEMCORE pItem)
{
- return PDMQueueInsert(pdmR3DrvHlp_QueueToPtr(pDrvIns, hQueue), pItem);
+ return PDMQueueInsert(pDrvIns->Internal.s.pVMR3, hQueue, pDrvIns, pItem);
}
/** @interface_method_impl{PDMDRVHLPR3,pfnQueueFlushIfNecessary} */
static DECLCALLBACK(bool) pdmR3DrvHlp_QueueFlushIfNecessary(PPDMDRVINS pDrvIns, PDMQUEUEHANDLE hQueue)
{
- return PDMQueueFlushIfNecessary(pdmR3DrvHlp_QueueToPtr(pDrvIns, hQueue));
+ return PDMQueueFlushIfNecessary(pDrvIns->Internal.s.pVMR3, hQueue, pDrvIns) == VINF_SUCCESS;
}
-
/** @interface_method_impl{PDMDRVHLPR3,pfnTMGetVirtualFreq} */
static DECLCALLBACK(uint64_t) pdmR3DrvHlp_TMGetVirtualFreq(PPDMDRVINS pDrvIns)
{
diff --git a/src/VBox/VMM/VMMR3/PDMQueue.cpp b/src/VBox/VMM/VMMR3/PDMQueue.cpp
index 3c43513134a..577724fc69f 100644
--- a/src/VBox/VMM/VMMR3/PDMQueue.cpp
+++ b/src/VBox/VMM/VMMR3/PDMQueue.cpp
@@ -30,14 +30,13 @@
#include <VBox/log.h>
#include <iprt/asm.h>
#include <iprt/assert.h>
+#include <iprt/mem.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, TMTIMERHANDLE hTimer, void *pvUser);
@@ -53,54 +52,108 @@ static DECLCALLBACK(void) pdmR3QueueTimer(PVM pVM, TMTIMERHANDLE hTimer, void
* 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.
+ * @param enmType Owner type.
+ * @param pvOwner The queue owner pointer.
+ * @param uCallback Callback function.
+ * @param phQueue 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)
+ const char *pszName, PDMQUEUETYPE enmType, void *pvOwner, uintptr_t uCallback,
+ PDMQUEUEHANDLE *phQueue)
{
- PUVM pUVM = pVM->pUVM;
-
/*
- * Validate input.
+ * Validate and adjust the 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);
+ VM_ASSERT_EMT0_RETURN(pVM, VERR_VM_THREAD_NOT_EMT); /* serialization by exclusivity */
+
+ cbItem = RT_ALIGN(cbItem, sizeof(uint64_t));
+ AssertMsgReturn(cbItem >= sizeof(PDMQUEUEITEMCORE) && cbItem < PDMQUEUE_MAX_ITEM_SIZE, ("cbItem=%zu\n", cbItem),
+ VERR_OUT_OF_RANGE);
+ AssertMsgReturn(cItems >= 1 && cItems <= PDMQUEUE_MAX_ITEMS, ("cItems=%u\n", cItems), VERR_OUT_OF_RANGE);
+ AssertMsgReturn((uint64_t)cbItem * cItems <= (fRZEnabled ? PDMQUEUE_MAX_TOTAL_SIZE_R0 : PDMQUEUE_MAX_TOTAL_SIZE_R3),
+ ("cItems=%u cbItem=%#x -> %#RX64, max %'u\n", cItems, cbItem, (uint64_t)cbItem * cItems,
+ fRZEnabled ? PDMQUEUE_MAX_TOTAL_SIZE_R0 : PDMQUEUE_MAX_TOTAL_SIZE_R3),
+ VERR_OUT_OF_RANGE);
+ AssertReturn(!fRZEnabled || enmType == PDMQUEUETYPE_INTERNAL || enmType == PDMQUEUETYPE_DEV, VERR_INVALID_PARAMETER);
+ if (SUPR3IsDriverless())
+ fRZEnabled = false;
+
+ /* Unqiue name that fits within the szName field: */
+ size_t cchName = strlen(pszName);
+ AssertReturn(cchName > 0, VERR_INVALID_NAME);
+ AssertMsgReturn(cchName < RT_SIZEOFMEMB(PDMQUEUE, szName), ("'%s' is too long\n", pszName), VERR_INVALID_NAME);
+ size_t i = pVM->pdm.s.cRing3Queues;
+ while (i-- > 0 )
+ AssertMsgReturn(strcmp(pVM->pdm.s.papRing3Queues[i]->szName, pszName) != 0, ("%s\n", pszName), VERR_DUPLICATE);
+ i = pVM->pdm.s.cRing0Queues;
+ while (i-- > 0 )
+ AssertMsgReturn(strcmp(pVM->pdm.s.apRing0Queues[i]->szName, pszName) != 0, ("%s\n", pszName), VERR_DUPLICATE);
/*
* 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;
+ PPDMQUEUE pQueue;
+ PDMQUEUEHANDLE hQueue;
if (fRZEnabled)
- rc = MMHyperAlloc(pVM, cb, 0, MM_TAG_PDM_QUEUE, (void **)&pQueue );
+ {
+ /* Call ring-0 to allocate and create the queue: */
+ PDMQUEUECREATEREQ Req;
+ Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+ Req.Hdr.cbReq = sizeof(Req);
+ Req.cItems = cItems;
+ Req.cbItem = (uint32_t)cbItem;
+ Req.enmType = enmType;
+ Req.pvOwner = pvOwner;
+ Req.pfnCallback = (RTR3PTR)uCallback;
+ RTStrCopy(Req.szName, sizeof(Req.szName), pszName);
+ AssertCompileMembersSameSize(PDMQUEUECREATEREQ, szName, PDMQUEUE, szName);
+ Req.hQueue = NIL_PDMQUEUEHANDLE;
+
+ int rc = VMMR3CallR0(pVM, VMMR0_DO_PDM_QUEUE_CREATE, 0, &Req.Hdr);
+ if (RT_FAILURE(rc))
+ return rc;
+ hQueue = Req.hQueue;
+ AssertReturn(hQueue < RT_ELEMENTS(pVM->pdm.s.apRing0Queues), VERR_INTERNAL_ERROR_2);
+ pQueue = pVM->pdm.s.apRing0Queues[hQueue];
+ AssertPtrReturn(pQueue, VERR_INTERNAL_ERROR_3);
+ AssertReturn(pQueue->u32Magic == PDMQUEUE_MAGIC, VERR_INTERNAL_ERROR_4);
+ AssertReturn(pQueue->cbItem == cbItem, VERR_INTERNAL_ERROR_4);
+ AssertReturn(pQueue->cItems == cItems, VERR_INTERNAL_ERROR_4);
+ AssertReturn(pQueue->enmType == enmType, VERR_INTERNAL_ERROR_4);
+ AssertReturn(pQueue->u.Gen.pvOwner == pvOwner, VERR_INTERNAL_ERROR_4);
+ AssertReturn(pQueue->u.Gen.pfnCallback == (RTR3PTR)uCallback, VERR_INTERNAL_ERROR_4);
+ }
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->pVMR0ForCall : NIL_RTR0PTR;
- pQueue->pszName = pszName;
- pQueue->cMilliesInterval = cMilliesInterval;
- pQueue->hTimer = NIL_TMTIMERHANDLE;
- 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);
+ /* Do it here using the paged heap: */
+ uint32_t const cbBitmap = RT_ALIGN_32(RT_ALIGN_32(cItems, 64) / 8, 64); /* keep bitmap in it's own cacheline */
+ uint32_t const cbQueue = RT_OFFSETOF(PDMQUEUE, bmAlloc)
+ + cbBitmap
+ + (uint32_t)cbItem * cItems;
+ pQueue = (PPDMQUEUE)RTMemPageAllocZ(cbQueue);
+ if (!pQueue)
+ return VERR_NO_PAGE_MEMORY;
+ pdmQueueInit(pQueue, cbBitmap, (uint32_t)cbItem, cItems, pszName, enmType, (RTR3PTR)uCallback, pvOwner);
+
+ uint32_t iQueue = pVM->pdm.s.cRing3Queues;
+ if (iQueue >= pVM->pdm.s.cRing3QueuesAlloc)
+ {
+ AssertLogRelMsgReturnStmt(iQueue < _16K, ("%#x\n", iQueue), RTMemPageFree(pQueue, cbQueue), VERR_TOO_MANY_OPENS);
+
+ uint32_t const cNewAlloc = RT_ALIGN_32(iQueue, 64) + 64;
+ PPDMQUEUE *papQueuesNew = (PPDMQUEUE *)RTMemAllocZ(cNewAlloc * sizeof(papQueuesNew[0]));
+ AssertLogRelMsgReturnStmt(papQueuesNew, ("cNewAlloc=%u\n", cNewAlloc), RTMemPageFree(pQueue, cbQueue), VERR_NO_MEMORY);
+
+ if (iQueue)
+ memcpy(papQueuesNew, pVM->pdm.s.papRing3Queues, iQueue * sizeof(papQueuesNew[0]));
+ PPDMQUEUE *papQueuesOld = ASMAtomicXchgPtrT(&pVM->pdm.s.papRing3Queues, papQueuesNew, PPDMQUEUE *);
+ pVM->pdm.s.cRing3QueuesAlloc = cNewAlloc;
+ RTMemFree(papQueuesOld);
+ }
+
+ pVM->pdm.s.papRing3Queues[iQueue] = pQueue;
+ pVM->pdm.s.cRing3Queues = iQueue + 1;
+ hQueue = iQueue + RT_ELEMENTS(pVM->pdm.s.apRing0Queues);
}
/*
@@ -108,77 +161,57 @@ static int pdmR3QueueCreate(PVM pVM, size_t cbItem, uint32_t cItems, uint32_t cM
*/
if (cMilliesInterval)
{
- char szName[32];
- RTStrPrintf(szName, sizeof(szName), "Queue %s", pQueue->pszName);
- rc = TMR3TimerCreate(pVM, TMCLOCK_REAL, pdmR3QueueTimer, pQueue, TMTIMER_FLAGS_NO_RING0, szName, &pQueue->hTimer);
+ char szName[48+6];
+ RTStrPrintf(szName, sizeof(szName), "Que/%s", pQueue->szName);
+ int rc = TMR3TimerCreate(pVM, TMCLOCK_REAL, pdmR3QueueTimer, pQueue, TMTIMER_FLAGS_NO_RING0, szName, &pQueue->hTimer);
if (RT_SUCCESS(rc))
{
rc = TMTimerSetMillies(pVM, pQueue->hTimer, cMilliesInterval);
- if (RT_FAILURE(rc))
+ if (RT_SUCCESS(rc))
+ pQueue->cMilliesInterval = cMilliesInterval;
+ else
{
AssertMsgFailed(("TMTimerSetMillies failed rc=%Rrc\n", rc));
int rc2 = TMR3TimerDestroy(pVM, pQueue->hTimer); AssertRC(rc2);
+ pQueue->hTimer = NIL_TMTIMERHANDLE;
}
}
else
AssertMsgFailed(("TMR3TimerCreateInternal failed rc=%Rrc\n", rc));
if (RT_FAILURE(rc))
{
- if (fRZEnabled)
- MMHyperFree(pVM, pQueue);
- else
- MMR3HeapFree(pQueue);
+ if (!fRZEnabled)
+ PDMR3QueueDestroy(pVM, hQueue, pvOwner);
+ /* else: will clean up queue when VM is destroyed */
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);
+ STAMR3RegisterF(pVM, &pQueue->cbItem, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
+ "Item size.", "/PDM/Queue/%s/cbItem", pQueue->szName);
+ STAMR3RegisterF(pVM, &pQueue->cItems, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
+ "Queue size.", "/PDM/Queue/%s/cItems", pQueue->szName);
+ STAMR3RegisterF(pVM, &pQueue->rcOkay, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE,
+ "Non-zero means queue is busted.", "/PDM/Queue/%s/rcOkay", pQueue->szName);
+ STAMR3RegisterF(pVM, &pQueue->StatAllocFailures, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES,
+ "PDMQueueAlloc failures.", "/PDM/Queue/%s/AllocFailures", pQueue->szName);
+ STAMR3RegisterF(pVM, &pQueue->StatInsert, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS,
+ "Calls to PDMQueueInsert.", "/PDM/Queue/%s/Insert", pQueue->szName);
+ STAMR3RegisterF(pVM, &pQueue->StatFlush, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS,
+ "Calls to pdmR3QueueFlush.", "/PDM/Queue/%s/Flush", pQueue->szName);
+ STAMR3RegisterF(pVM, &pQueue->StatFlushLeftovers, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES,
+ "Left over items after flush.", "/PDM/Queue/%s/FlushLeftovers", pQueue->szName);
#ifdef VBOX_WITH_STATISTICS
- STAMR3RegisterF(pVM, &pQueue->StatFlushPrf, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "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);
+ STAMR3RegisterF(pVM, &pQueue->StatFlushPrf, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL,
+ "Profiling pdmR3QueueFlush.", "/PDM/Queue/%s/FlushPrf", pQueue->szName);
+ STAMR3RegisterF(pVM, (void *)&pQueue->cStatPending, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
+ "Pending items.", "/PDM/Queue/%s/Pending", pQueue->szName);
#endif
- *ppQueue = pQueue;
+ *phQueue = hQueue;
return VINF_SUCCESS;
}
@@ -195,12 +228,13 @@ static int pdmR3QueueCreate(PVM pVM, size_t cbItem, uint32_t cItems, uint32_t cM
* 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.
+ * @param pszName The queue name. Unique. Copied.
+ * @param phQueue 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)
+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, PDMQUEUEHANDLE *phQueue)
{
LogFlow(("PDMR3QueueCreateDevice: pDevIns=%p cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p fRZEnabled=%RTbool pszName=%s\n",
pDevIns, cbItem, cItems, cMilliesInterval, pfnCallback, fRZEnabled, pszName));
@@ -209,27 +243,20 @@ VMMR3_INT_DECL(int) PDMR3QueueCreateDevice(PVM pVM, PPDMDEVINS pDevIns, size_t c
* Validate input.
*/
VM_ASSERT_EMT0(pVM);
- if (!pfnCallback)
- {
- AssertMsgFailed(("No consumer callback!\n"));
- return VERR_INVALID_PARAMETER;
- }
+ AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
+ AssertPtrReturn(pDevIns, VERR_INVALID_POINTER);
+
+ if (!(pDevIns->Internal.s.fIntFlags & PDMDEVINSINT_FLAGS_R0_ENABLED))
+ fRZEnabled = false;
/*
* Create the queue.
*/
- PPDMQUEUE pQueue;
- int rc = pdmR3QueueCreate(pVM, cbItem, cItems, cMilliesInterval, fRZEnabled, pszName, &pQueue);
+ int rc = pdmR3QueueCreate(pVM, cbItem, cItems, cMilliesInterval, fRZEnabled, pszName,
+ PDMQUEUETYPE_DEV, pDevIns, (uintptr_t)pfnCallback, phQueue);
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));
- }
+ Log(("PDM: Created device queue %#RX64; cbItem=%d cItems=%d cMillies=%d pfnCallback=%p pDevIns=%p\n",
+ *phQueue, cbItem, cItems, cMilliesInterval, pfnCallback, pDevIns));
return rc;
}
@@ -245,12 +272,12 @@ VMMR3_INT_DECL(int) PDMR3QueueCreateDevice(PVM pVM, PPDMDEVINS pDevIns, size_t c
* @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.
+ * @param pszName The queue name. Unique. Copied.
+ * @param phQueue 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)
+ PFNPDMQUEUEDRV pfnCallback, const char *pszName, PDMQUEUEHANDLE *phQueue)
{
LogFlow(("PDMR3QueueCreateDriver: pDrvIns=%p cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p pszName=%s\n",
pDrvIns, cbItem, cItems, cMilliesInterval, pfnCallback, pszName));
@@ -260,22 +287,16 @@ VMMR3_INT_DECL(int) PDMR3QueueCreateDriver(PVM pVM, PPDMDRVINS pDrvIns, size_t c
*/
VM_ASSERT_EMT0(pVM);
AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
+ AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
/*
* Create the queue.
*/
- PPDMQUEUE pQueue;
- int rc = pdmR3QueueCreate(pVM, cbItem, cItems, cMilliesInterval, false, pszName, &pQueue);
+ int rc = pdmR3QueueCreate(pVM, cbItem, cItems, cMilliesInterval, false /*fRZEnabled*/, pszName,
+ PDMQUEUETYPE_DRV, pDrvIns, (uintptr_t)pfnCallback, phQueue);
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));
- }
+ Log(("PDM: Created driver queue %#RX64; cbItem=%d cItems=%d cMillies=%d pfnCallback=%p pDrvIns=%p\n",
+ *phQueue, cbItem, cItems, cMilliesInterval, pfnCallback, pDrvIns));
return rc;
}
@@ -291,12 +312,13 @@ VMMR3_INT_DECL(int) PDMR3QueueCreateDriver(PVM pVM, PPDMDRVINS pDrvIns, size_t c
* 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.
+ * @param pszName The queue name. Unique. Copied.
+ * @param phQueue 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)
+ PFNPDMQUEUEINT pfnCallback, bool fRZEnabled,
+ const char *pszName, PDMQUEUEHANDLE *phQueue)
{
LogFlow(("PDMR3QueueCreateInternal: cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p fRZEnabled=%RTbool pszName=%s\n",
cbItem, cItems, cMilliesInterval, pfnCallback, fRZEnabled, pszName));
@@ -310,17 +332,11 @@ VMMR3_INT_DECL(int) PDMR3QueueCreateInternal(PVM pVM, size_t cbItem, uint32_t cI
/*
* Create the queue.
*/
- PPDMQUEUE pQueue;
- int rc = pdmR3QueueCreate(pVM, cbItem, cItems, cMilliesInterval, fRZEnabled, pszName, &pQueue);
+ int rc = pdmR3QueueCreate(pVM, cbItem, cItems, cMilliesInterval, fRZEnabled, pszName,
+ PDMQUEUETYPE_INTERNAL, pVM, (uintptr_t)pfnCallback, phQueue);
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));
- }
+ *phQueue, cbItem, cItems, cMilliesInterval, pfnCallback));
return rc;
}
@@ -337,13 +353,15 @@ VMMR3_INT_DECL(int) PDMR3QueueCreateInternal(PVM pVM, size_t cbItem, uint32_t cI
* @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.
+ * @param phQueue 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)
+VMMR3DECL(int) PDMR3QueueCreateExternal(PVM pVM, size_t cbItem, uint32_t cItems, uint32_t cMilliesInterval,
+ PFNPDMQUEUEEXT pfnCallback, void *pvUser,
+ const char *pszName, PDMQUEUEHANDLE *phQueue)
{
- LogFlow(("PDMR3QueueCreateExternal: cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p pszName=%s\n", cbItem, cItems, cMilliesInterval, pfnCallback, pszName));
+ LogFlow(("PDMR3QueueCreateExternal: cbItem=%d cItems=%d cMilliesInterval=%d pfnCallback=%p pszName=%s\n",
+ cbItem, cItems, cMilliesInterval, pfnCallback, pszName));
/*
* Validate input.
@@ -354,18 +372,11 @@ VMMR3_INT_DECL(int) PDMR3QueueCreateExternal(PVM pVM, size_t cbItem, uint32_t cI
/*
* Create the queue.
*/
- PPDMQUEUE pQueue;
- int rc = pdmR3QueueCreate(pVM, cbItem, cItems, cMilliesInterval, false, pszName, &pQueue);
+ int rc = pdmR3QueueCreate(pVM, cbItem, cItems, cMilliesInterval, false /*fRZEnabled*/, pszName,
+ PDMQUEUETYPE_EXTERNAL, pvUser, (uintptr_t)pfnCallback, phQueue);
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));
- }
+ *phQueue, cbItem, cItems, cMilliesInterval, pfnCallback, pvUser));
return rc;
}
@@ -374,73 +385,62 @@ VMMR3_INT_DECL(int) PDMR3QueueCreateExternal(PVM pVM, size_t cbItem, uint32_t cI
* Destroy a queue.
*
* @returns VBox status code.
- * @param pQueue Queue to destroy.
- * @thread Emulation thread only.
+ * @param pVM Pointer to the cross context VM structure.
+ * @param hQueue Handle to the queue that should be destroyed.
+ * @param pvOwner The owner address.
+ * @thread EMT(0)
+ * @note Externally visible mainly for testing purposes.
*/
-VMMR3_INT_DECL(int) PDMR3QueueDestroy(PPDMQUEUE pQueue)
+VMMR3DECL(int) PDMR3QueueDestroy(PVM pVM, PDMQUEUEHANDLE hQueue, void *pvOwner)
{
- LogFlow(("PDMR3QueueDestroy: pQueue=%p\n", pQueue));
+ LogFlow(("PDMR3QueueDestroy: hQueue=%p pvOwner=%p\n", hQueue, pvOwner));
/*
* Validate input.
*/
- if (!pQueue)
- return VERR_INVALID_PARAMETER;
- Assert(pQueue && pQueue->pVMR3);
- PVM pVM = pQueue->pVMR3;
- PUVM pUVM = pVM->pUVM;
-
- pdmLock(pVM);
+ VM_ASSERT_EMT0_RETURN(pVM, VERR_VM_THREAD_NOT_EMT); /* serialization */
+ if (hQueue == NIL_PDMQUEUEHANDLE)
+ return VINF_SUCCESS;
- /*
- * Unlink it.
- */
- if (pQueue->hTimer != NIL_TMTIMERHANDLE)
+ PPDMQUEUE pQueue;
+ bool fRZEnabled = false;
+ if (hQueue < RT_ELEMENTS(pVM->pdm.s.apRing0Queues))
{
- 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;
+ AssertReturn(hQueue < pVM->pdm.s.cRing0Queues, VERR_INVALID_HANDLE);
+ pQueue = pVM->pdm.s.apRing0Queues[hQueue];
+ AssertPtrReturn(pQueue, VERR_INVALID_HANDLE);
+ AssertReturn(pQueue->u32Magic == PDMQUEUE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(pQueue->u.Gen.pvOwner == pvOwner, VERR_INVALID_HANDLE);
+
+ /* Lazy bird: Cannot dynamically delete ring-0 capable queues. */
+ AssertFailedReturn(VERR_NOT_SUPPORTED);
}
else
{
- if (pUVM->pdm.s.pQueuesForced != pQueue)
+ hQueue -= RT_ELEMENTS(pVM->pdm.s.apRing0Queues);
+ AssertReturn(hQueue < pVM->pdm.s.cRing3Queues, VERR_INVALID_HANDLE);
+ pQueue = pVM->pdm.s.papRing3Queues[hQueue];
+ AssertPtrReturn(pQueue, VERR_INVALID_HANDLE);
+ AssertReturn(pQueue->u32Magic == PDMQUEUE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(pQueue->u.Gen.pvOwner == pvOwner, VERR_INVALID_HANDLE);
+
+ /* Enter the lock here to serialize with other EMTs traversing the handles. */
+ pdmLock(pVM);
+ pVM->pdm.s.papRing3Queues[hQueue] = NULL;
+ if (hQueue + 1 == pVM->pdm.s.cRing3Queues)
{
- 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"));
+ while (hQueue > 0 && pVM->pdm.s.papRing3Queues[hQueue - 1] == NULL)
+ hQueue--;
+ pVM->pdm.s.cRing3Queues = hQueue;
}
- else
- pUVM->pdm.s.pQueuesForced = pQueue->pNext;
+ pQueue->u32Magic = PDMQUEUE_MAGIC_DEAD;
+ pdmUnlock(pVM);
}
- pQueue->pNext = NULL;
- pQueue->pVMR3 = NULL;
- pdmUnlock(pVM);
/*
* Deregister statistics.
*/
- STAMR3DeregisterF(pVM->pUVM, "/PDM/Queue/%s/cbItem", pQueue->pszName);
+ STAMR3DeregisterF(pVM->pUVM, "/PDM/Queue/%s/*", pQueue->szName);
/*
* Destroy the timer and free it.
@@ -450,258 +450,201 @@ VMMR3_INT_DECL(int) PDMR3QueueDestroy(PPDMQUEUE pQueue)
TMR3TimerDestroy(pVM, pQueue->hTimer);
pQueue->hTimer = NIL_TMTIMERHANDLE;
}
- if (pQueue->pVMR0)
- {
- pQueue->pVMR0 = NIL_RTR0PTR;
- MMHyperFree(pVM, pQueue);
- }
- else
- MMR3HeapFree(pQueue);
+ if (!fRZEnabled)
+ RTMemPageFree(pQueue, pQueue->offItems + pQueue->cbItem * pQueue->cItems);
return VINF_SUCCESS;
}
/**
- * Destroy a all queues owned by the specified device.
+ * Destroy a all queues with a given owner.
*
* @returns VBox status code.
* @param pVM The cross context VM structure.
- * @param pDevIns Device instance.
- * @thread Emulation thread only.
+ * @param pvOwner The owner pointer.
+ * @param enmType Owner type.
+ * @thread EMT(0)
*/
-VMMR3_INT_DECL(int) PDMR3QueueDestroyDevice(PVM pVM, PPDMDEVINS pDevIns)
+static int pdmR3QueueDestroyByOwner(PVM pVM, void *pvOwner, PDMQUEUETYPE enmType)
{
- LogFlow(("PDMR3QueueDestroyDevice: pDevIns=%p\n", pDevIns));
+ LogFlow(("pdmR3QueueDestroyByOwner: pvOwner=%p enmType=%d\n", pvOwner, enmType));
/*
* Validate input.
*/
- if (!pDevIns)
- return VERR_INVALID_PARAMETER;
-
- PUVM pUVM = pVM->pUVM;
- pdmLock(pVM);
+ AssertPtrReturn(pvOwner, VERR_INVALID_PARAMETER);
+ AssertReturn(pvOwner != pVM, VERR_INVALID_PARAMETER);
+ VM_ASSERT_EMT0_RETURN(pVM, VERR_VM_THREAD_NOT_EMT); /* serialization */
/*
- * Unlink it.
+ * Scan and destroy.
*/
- PPDMQUEUE pQueueNext = pUVM->pdm.s.pQueuesTimer;
- PPDMQUEUE pQueue = pUVM->pdm.s.pQueuesForced;
- do
+ uint32_t i = pVM->pdm.s.cRing0Queues;
+ while (i-- > 0)
{
- while (pQueue)
+ PPDMQUEUE pQueue = pVM->pdm.s.apRing0Queues[i];
+ if ( pQueue
+ && pQueue->u.Gen.pvOwner == pvOwner
+ && pQueue->enmType == enmType)
{
- 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;
+ /* Not supported at runtime. */
+ VM_ASSERT_STATE_RETURN(pVM, VMSTATE_DESTROYING, VERR_WRONG_ORDER);
}
+ }
- /* next queue list */
- pQueue = pQueueNext;
- pQueueNext = NULL;
- } while (pQueue);
+ i = pVM->pdm.s.cRing3Queues;
+ while (i-- > 0)
+ {
+ PPDMQUEUE pQueue = pVM->pdm.s.papRing3Queues[i];
+ if ( pQueue
+ && pQueue->u.Gen.pvOwner == pvOwner
+ && pQueue->enmType == enmType)
+ PDMR3QueueDestroy(pVM, i + RT_ELEMENTS(pVM->pdm.s.apRing0Queues), pvOwner);
+ }
- pdmUnlock(pVM);
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 EMT(0)
+ */
+VMMR3_INT_DECL(int) PDMR3QueueDestroyDevice(PVM pVM, PPDMDEVINS pDevIns)
+{
+ LogFlow(("PDMR3QueueDestroyDevice: pDevIns=%p\n", pDevIns));
+ return pdmR3QueueDestroyByOwner(pVM, pDevIns, PDMQUEUETYPE_DEV);
+}
+
+
+/**
* 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.
+ * @thread EMT(0)
*/
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;
+ return pdmR3QueueDestroyByOwner(pVM, pDrvIns, PDMQUEUETYPE_DRV);
}
/**
- * Flush pending queues.
- * This is a forced action callback.
+ * Free an item.
*
- * @param pVM The cross context VM structure.
- * @thread Emulation thread only.
+ * @param pQueue The queue.
+ * @param pItem The item.
*/
-VMMR3_INT_DECL(void) PDMR3QueueFlushAll(PVM pVM)
+DECLINLINE(void) pdmR3QueueFreeItem(PPDMQUEUE pQueue, uint8_t *pbItems, uint32_t cbItem, PPDMQUEUEITEMCORE pItem)
{
- 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)
- pdmR3QueueFlush(pCur);
+ pItem->u64View = UINT64_C(0xfeedfeedfeedfeed);
- 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);
- }
+ uintptr_t const offItem = (uintptr_t)pItem - (uintptr_t)pbItems;
+ uintptr_t const iItem = offItem / cbItem;
+ Assert(!(offItem % cbItem));
+ Assert(iItem < pQueue->cItems);
+ AssertReturnVoidStmt(ASMAtomicBitTestAndSet(pQueue->bmAlloc, iItem) == false, pQueue->rcOkay = VERR_INTERNAL_ERROR_4);
+ STAM_STATS({ ASMAtomicDecU32(&pQueue->cStatPending); });
}
+
/**
* Process pending items in one queue.
*
- * @returns Success indicator.
- * If false the item the consumer said "enough!".
- * @param pQueue The queue.
+ * @returns VBox status code.
+ * @param pVM The cross context VM structure.
+ * @param pQueue The queue needing flushing.
*/
-static bool pdmR3QueueFlush(PPDMQUEUE pQueue)
+static int pdmR3QueueFlush(PVM pVM, PPDMQUEUE pQueue)
{
STAM_PROFILE_START(&pQueue->StatFlushPrf,p);
- /*
- * Get the lists.
- */
- PPDMQUEUEITEMCORE pItems = ASMAtomicXchgPtrT(&pQueue->pPendingR3, NULL, PPDMQUEUEITEMCORE);
- RTR0PTR pItemsR0 = ASMAtomicXchgR0Ptr(&pQueue->pPendingR0, NIL_RTR0PTR);
-
- AssertMsgReturn( pItemsR0
- || 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;
- }
+ uint32_t const cbItem = pQueue->cbItem;
+ uint32_t const cItems = pQueue->cItems;
+ uint8_t * const pbItems = (uint8_t *)pQueue + pQueue->offItems;
/*
- * Do the same for any pending R0 items.
+ * Get the list and reverse it into a pointer list (inserted in LIFO order to avoid locking).
*/
- while (pItemsR0)
+ uint32_t cPending = 0;
+ PPDMQUEUEITEMCORE pHead = NULL;
{
- PPDMQUEUEITEMCORE pInsert = (PPDMQUEUEITEMCORE)MMHyperR0ToR3(pQueue->pVMR3, pItemsR0);
- pItemsR0 = pInsert->pNextR0;
- pInsert->pNextR0 = NIL_RTR0PTR;
- pInsert->pNextR3 = pItems;
- pItems = pInsert;
+ uint32_t iCur = ASMAtomicXchgU32(&pQueue->iPending, UINT32_MAX);
+ do
+ {
+ AssertMsgReturn(iCur < cItems, ("%#x vs %#x\n", iCur, cItems), pQueue->rcOkay = VERR_INTERNAL_ERROR_5);
+ AssertReturn(ASMBitTest(pQueue->bmAlloc, iCur) == false, pQueue->rcOkay = VERR_INTERNAL_ERROR_3);
+ PPDMQUEUEITEMCORE pCur = (PPDMQUEUEITEMCORE)&pbItems[iCur * cbItem];
+
+ iCur = pCur->iNext;
+ ASMCompilerBarrier(); /* paranoia */
+ pCur->pNext = pHead;
+ pHead = pCur;
+ cPending++;
+ } while (iCur != UINT32_MAX);
}
+ RT_NOREF(cPending);
/*
* Feed the items to the consumer function.
*/
- Log2(("pdmR3QueueFlush: pQueue=%p enmType=%d pItems=%p\n", pQueue, pQueue->enmType, pItems));
+ Log2(("pdmR3QueueFlush: pQueue=%p enmType=%d pHead=%p cItems=%u\n", pQueue, pQueue->enmType, pHead, cPending));
switch (pQueue->enmType)
{
case PDMQUEUETYPE_DEV:
- while (pItems)
+ while (pHead)
{
- if (!pQueue->u.Dev.pfnCallback(pQueue->u.Dev.pDevIns, pItems))
+ if (!pQueue->u.Dev.pfnCallback(pQueue->u.Dev.pDevIns, pHead))
break;
- pCur = pItems;
- pItems = pItems->pNextR3;
- pdmR3QueueFreeItem(pQueue, pCur);
+ PPDMQUEUEITEMCORE pFree = pHead;
+ pHead = pHead->pNext;
+ ASMCompilerBarrier(); /* paranoia */
+ pdmR3QueueFreeItem(pQueue, pbItems, cbItem, pFree);
}
break;
case PDMQUEUETYPE_DRV:
- while (pItems)
+ while (pHead)
{
- if (!pQueue->u.Drv.pfnCallback(pQueue->u.Drv.pDrvIns, pItems))
+ if (!pQueue->u.Drv.pfnCallback(pQueue->u.Drv.pDrvIns, pHead))
break;
- pCur = pItems;
- pItems = pItems->pNextR3;
- pdmR3QueueFreeItem(pQueue, pCur);
+ PPDMQUEUEITEMCORE pFree = pHead;
+ pHead = pHead->pNext;
+ ASMCompilerBarrier(); /* paranoia */
+ pdmR3QueueFreeItem(pQueue, pbItems, cbItem, pFree);
}
break;
case PDMQUEUETYPE_INTERNAL:
- while (pItems)
+ while (pHead)
{
- if (!pQueue->u.Int.pfnCallback(pQueue->pVMR3, pItems))
+ if (!pQueue->u.Int.pfnCallback(pVM, pHead))
break;
- pCur = pItems;
- pItems = pItems->pNextR3;
- pdmR3QueueFreeItem(pQueue, pCur);
+ PPDMQUEUEITEMCORE pFree = pHead;
+ pHead = pHead->pNext;
+ ASMCompilerBarrier(); /* paranoia */
+ pdmR3QueueFreeItem(pQueue, pbItems, cbItem, pFree);
}
break;
case PDMQUEUETYPE_EXTERNAL:
- while (pItems)
+ while (pHead)
{
- if (!pQueue->u.Ext.pfnCallback(pQueue->u.Ext.pvUser, pItems))
+ if (!pQueue->u.Ext.pfnCallback(pQueue->u.Ext.pvUser, pHead))
break;
- pCur = pItems;
- pItems = pItems->pNextR3;
- pdmR3QueueFreeItem(pQueue, pCur);
+ PPDMQUEUEITEMCORE pFree = pHead;
+ pHead = pHead->pNext;
+ ASMCompilerBarrier(); /* paranoia */
+ pdmR3QueueFreeItem(pQueue, pbItems, cbItem, pFree);
}
break;
@@ -713,72 +656,126 @@ static bool pdmR3QueueFlush(PPDMQUEUE pQueue)
/*
* Success?
*/
- if (pItems)
+ if (!pHead)
+ { /* likely */ }
+ else
{
/*
- * Reverse the list.
+ * Reverse the list and turn it back into index chain.
*/
- pCur = pItems;
- pItems = NULL;
- while (pCur)
+ uint32_t iPendingHead = UINT32_MAX;
+ do
{
- PPDMQUEUEITEMCORE pInsert = pCur;
- pCur = pInsert->pNextR3;
- pInsert->pNextR3 = pItems;
- pItems = pInsert;
- }
+ PPDMQUEUEITEMCORE pInsert = pHead;
+ pHead = pHead->pNext;
+ ASMCompilerBarrier(); /* paranoia */
+ pInsert->iNext = iPendingHead;
+ iPendingHead = ((uintptr_t)pInsert - (uintptr_t)pbItems) / cbItem;
+ } while (pHead);
/*
- * Insert the list at the tail of the pending list.
+ * Insert the list at the tail of the pending list. If someone races
+ * us there, we have to join the new LIFO with the old.
*/
for (;;)
{
- if (ASMAtomicCmpXchgPtr(&pQueue->pPendingR3, pItems, NULL))
+ if (ASMAtomicCmpXchgU32(&pQueue->iPending, iPendingHead, UINT32_MAX))
break;
- PPDMQUEUEITEMCORE pPending = ASMAtomicXchgPtrT(&pQueue->pPendingR3, NULL, PPDMQUEUEITEMCORE);
- if (pPending)
+
+ uint32_t const iNewPending = ASMAtomicXchgU32(&pQueue->iPending, UINT32_MAX);
+ if (iNewPending != UINT32_MAX)
{
- pCur = pPending;
- while (pCur->pNextR3)
- pCur = pCur->pNextR3;
- pCur->pNextR3 = pItems;
- pItems = pPending;
+ /* Find the last entry and chain iPendingHead onto it. */
+ uint32_t iCur = iNewPending;
+ for (;;)
+ {
+ AssertReturn(iCur < cItems, pQueue->rcOkay = VERR_INTERNAL_ERROR_2);
+ AssertReturn(ASMBitTest(pQueue->bmAlloc, iCur) == false, pQueue->rcOkay = VERR_INTERNAL_ERROR_3);
+ PPDMQUEUEITEMCORE pCur = (PPDMQUEUEITEMCORE)&pbItems[iCur * cbItem];
+ iCur = pCur->iNext;
+ if (iCur == UINT32_MAX)
+ {
+ pCur->iNext = iPendingHead;
+ break;
+ }
+ }
+
+ iPendingHead = iNewPending;
}
}
STAM_REL_COUNTER_INC(&pQueue->StatFlushLeftovers);
- STAM_PROFILE_STOP(&pQueue->StatFlushPrf,p);
- return false;
}
STAM_PROFILE_STOP(&pQueue->StatFlushPrf,p);
- return true;
+ return VINF_SUCCESS;
}
/**
- * Free an item.
+ * Flush pending queues.
+ * This is a forced action callback.
*
- * @param pQueue The queue.
- * @param pItem The item.
+ * @param pVM The cross context VM structure.
+ * @thread Emulation thread only.
+ * @note Internal, but exported for use in the testcase.
*/
-DECLINLINE(void) pdmR3QueueFreeItem(PPDMQUEUE pQueue, PPDMQUEUEITEMCORE pItem)
+VMMR3DECL(void) PDMR3QueueFlushAll(PVM pVM)
{
- VM_ASSERT_EMT(pQueue->pVMR3);
+ VM_ASSERT_EMT(pVM);
+ LogFlow(("PDMR3QueuesFlush:\n"));
- int i = pQueue->iFreeHead;
- int iNext = (i + 1) % (pQueue->cItems + PDMQUEUE_FREE_SLACK);
+ /*
+ * 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);
- pQueue->aFreeItems[i].pItemR3 = pItem;
- if (pQueue->pVMR0)
- pQueue->aFreeItems[i].pItemR0 = MMHyperR3ToR0(pQueue->pVMR3, pItem);
+ /* Scan the ring-0 queues: */
+ size_t i = pVM->pdm.s.cRing0Queues;
+ while (i-- > 0)
+ {
+ PPDMQUEUE pQueue = pVM->pdm.s.apRing0Queues[i];
+ if ( pQueue
+ && pQueue->iPending != UINT32_MAX
+ && pQueue->hTimer == NIL_TMTIMERHANDLE
+ && pQueue->rcOkay == VINF_SUCCESS)
+ pdmR3QueueFlush(pVM, pQueue);
+ }
- 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); });
+ /* Scan the ring-3 queues: */
+/** @todo Deal with destroy concurrency issues. */
+ i = pVM->pdm.s.cRing3Queues;
+ while (i-- > 0)
+ {
+ PPDMQUEUE pQueue = pVM->pdm.s.papRing3Queues[i];
+ if ( pQueue
+ && pQueue->iPending != UINT32_MAX
+ && pQueue->hTimer == NIL_TMTIMERHANDLE
+ && pQueue->rcOkay == VINF_SUCCESS)
+ pdmR3QueueFlush(pVM, pQueue);
+ }
+
+ 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);
+ }
}
+
/**
* @callback_method_impl{FNTMTIMERINT, Timer handler for one PDM queue.}
*/
@@ -787,9 +784,9 @@ static DECLCALLBACK(void) pdmR3QueueTimer(PVM pVM, TMTIMERHANDLE hTimer, void *p
PPDMQUEUE pQueue = (PPDMQUEUE)pvUser;
Assert(hTimer == pQueue->hTimer);
- if ( pQueue->pPendingR3
- || pQueue->pPendingR0)
- pdmR3QueueFlush(pQueue);
+ if (pQueue->iPending != UINT32_MAX)
+ pdmR3QueueFlush(pVM, pQueue);
+
int rc = TMTimerSetMillies(pVM, hTimer, pQueue->cMilliesInterval);
AssertRC(rc);
}
diff --git a/src/VBox/VMM/include/PDMInternal.h b/src/VBox/VMM/include/PDMInternal.h
index 6f1832a8ddb..86909589d5f 100644
--- a/src/VBox/VMM/include/PDMInternal.h
+++ b/src/VBox/VMM/include/PDMInternal.h
@@ -984,8 +984,14 @@ typedef PDMMOD *PPDMMOD;
-/** Extra space in the free array. */
-#define PDMQUEUE_FREE_SLACK 16
+/** Max number of items in a queue. */
+#define PDMQUEUE_MAX_ITEMS _16K
+/** Max item size. */
+#define PDMQUEUE_MAX_ITEM_SIZE _1M
+/** Max total queue item size for ring-0 capable queues. */
+#define PDMQUEUE_MAX_TOTAL_SIZE_R0 _8M
+/** Max total queue item size for ring-3 only queues. */
+#define PDMQUEUE_MAX_TOTAL_SIZE_R3 _32M
/**
* Queue type.
@@ -1002,16 +1008,33 @@ typedef enum PDMQUEUETYPE
PDMQUEUETYPE_EXTERNAL
} PDMQUEUETYPE;
-/** Pointer to a PDM Queue. */
-typedef struct PDMQUEUE *PPDMQUEUE;
-
/**
* PDM Queue.
*/
typedef struct PDMQUEUE
{
- /** Pointer to the next queue in the list. */
- R3PTRTYPE(PPDMQUEUE) pNext;
+ /** Magic value (PDMQUEUE_MAGIC). */
+ uint32_t u32Magic;
+ /** Item size (bytes). */
+ uint32_t cbItem;
+ /** Number of items in the queue. */
+ uint32_t cItems;
+ /** Offset of the the queue items relative to the PDMQUEUE structure. */
+ uint32_t offItems;
+
+ /** Interval timer. Only used if cMilliesInterval is non-zero. */
+ TMTIMERHANDLE hTimer;
+ /** The interval between checking the queue for events.
+ * The realtime timer below is used to do the waiting.
+ * If 0, the queue will use the VM_FF_PDM_QUEUE forced action. */
+ uint32_t cMilliesInterval;
+
+ /** This is VINF_SUCCESS if the queue is okay, error status if not. */
+ int32_t rcOkay;
+ uint32_t u32Padding;
+
+ /** Queue type. */
+ PDMQUEUETYPE enmType;
/** Type specific data. */
union
{
@@ -1045,38 +1068,23 @@ typedef struct PDMQUEUE
/** Pointer to user argument. */
R3PTRTYPE(void *) pvUser;
} Ext;
+ struct
+ {
+ /** Generic callback pointer. */
+ RTR3PTR pfnCallback;
+ /** Generic owner pointer. */
+ RTR3PTR pvOwner;
+ } Gen;
} u;
- /** Queue type. */
- PDMQUEUETYPE enmType;
- /** The interval between checking the queue for events.
- * The realtime timer below is used to do the waiting.
- * If 0, the queue will use the VM_FF_PDM_QUEUE forced action. */
- uint32_t cMilliesInterval;
- /** Interval timer. Only used if cMilliesInterval is non-zero. */
- TMTIMERHANDLE hTimer;
- /** Pointer to the VM - R3. */
- PVMR3 pVMR3;
- /** LIFO of pending items - R3. */
- R3PTRTYPE(PPDMQUEUEITEMCORE) volatile pPendingR3;
- /** Pointer to the VM - R0. */
- PVMR0 pVMR0;
- /** LIFO of pending items - R0. */
- R0PTRTYPE(PPDMQUEUEITEMCORE) volatile pPendingR0;
-
- /** Item size (bytes). */
- uint32_t cbItem;
- /** Number of items in the queue. */
- uint32_t cItems;
- /** Index to the free head (where we insert). */
- uint32_t volatile iFreeHead;
- /** Index to the free tail (where we remove). */
- uint32_t volatile iFreeTail;
/** Unique queue name. */
- R3PTRTYPE(const char *) pszName;
-#if HC_ARCH_BITS == 32
- RTR3PTR Alignment1;
-#endif
+ char szName[40];
+
+ /** LIFO of pending items (item index), UINT32_MAX if empty. */
+ uint32_t volatile iPending;
+
+ /** State: Pending items. */
+ uint32_t volatile cStatPending;
/** Stat: Times PDMQueueAlloc fails. */
STAMCOUNTER StatAllocFailures;
/** Stat: PDMQueueInsert calls. */
@@ -1085,23 +1093,23 @@ typedef struct PDMQUEUE
STAMCOUNTER StatFlush;
/** Stat: Queue flushes with pending items left over. */
STAMCOUNTER StatFlushLeftovers;
-#ifdef VBOX_WITH_STATISTICS
/** State: Profiling the flushing. */
STAMPROFILE StatFlushPrf;
- /** State: Pending items. */
- uint32_t volatile cStatPending;
- uint32_t volatile cAlignment;
-#endif
+ uint64_t au64Padding[3];
- /** Array of pointers to free items. Variable size. */
- struct PDMQUEUEFREEITEM
- {
- /** Pointer to the free item - HC Ptr. */
- R3PTRTYPE(PPDMQUEUEITEMCORE) volatile pItemR3;
- /** Pointer to the free item - HC Ptr. */
- R0PTRTYPE(PPDMQUEUEITEMCORE) volatile pItemR0;
- } aFreeItems[1];
+ /** Allocation bitmap: Set bits means free, clear means allocated. */
+ RT_FLEXIBLE_ARRAY_EXTENSION
+ uint64_t bmAlloc[RT_FLEXIBLE_ARRAY];
+ /* The items follows after the end of the bitmap */
} PDMQUEUE;
+AssertCompileMemberAlignment(PDMQUEUE, bmAlloc, 64);
+/** Pointer to a PDM Queue. */
+typedef struct PDMQUEUE *PPDMQUEUE;
+
+/** Magic value PDMQUEUE::u32Magic (Bud Powell). */
+#define PDMQUEUE_MAGIC UINT32_C(0x19240927)
+/** Magic value PDMQUEUE::u32Magic after destroy. */
+#define PDMQUEUE_MAGIC_DEAD UINT32_C(0x19660731)
/** @name PDM::fQueueFlushing
* @{ */
@@ -1115,6 +1123,30 @@ typedef struct PDMQUEUE
#define PDM_QUEUE_FLUSH_FLAG_PENDING_BIT 1
/** @} */
+/**
+ * Ring-0 queue
+ *
+ * @author bird (2022-02-04)
+ */
+typedef struct PDMQUEUER0
+{
+ /** Pointer to the shared queue data. */
+ R0PTRTYPE(PPDMQUEUE) pQueue;
+ /** The memory allocation. */
+ RTR0MEMOBJ hMemObj;
+ /** The ring-3 mapping object. */
+ RTR0MEMOBJ hMapObj;
+ /** The owner pointer. This is NULL if not allocated. */
+ RTR0PTR pvOwner;
+ /** Queue item size. */
+ uint32_t cbItem;
+ /** Number of queue items. */
+ uint32_t cItems;
+ /** Offset of the the queue items relative to the PDMQUEUE structure. */
+ uint32_t offItems;
+ uint32_t u32Reserved;
+} PDMQUEUER0;
+
/** @name PDM task structures.
* @{ */
@@ -1186,8 +1218,8 @@ typedef struct PDMTASKSET
} PDMTASKSET;
AssertCompileMemberAlignment(PDMTASKSET, fTriggered, 64);
AssertCompileMemberAlignment(PDMTASKSET, aTasks, 64);
-/** Magic value for PDMTASKSET::u32Magic. */
-#define PDMTASKSET_MAGIC UINT32_C(0x19320314)
+/** Magic value for PDMTASKSET::u32Magic (Quincy Delight Jones Jr.). */
+#define PDMTASKSET_MAGIC UINT32_C(0x19330314)
/** Pointer to a task set. */
typedef PDMTASKSET *PPDMTASKSET;
@@ -1252,12 +1284,12 @@ typedef struct PDMDEVHLPTASK
*/
struct PDMDEVHLPTASKPCISETIRQ
{
- /** Pointer to the PCI device (R3 Ptr). */
- R3PTRTYPE(PPDMPCIDEV) pPciDevR3;
+ /** Index of the PCI device (into PDMDEVINSR3::apPciDevs). */
+ uint32_t idxPciDev;
/** The IRQ */
- int iIrq;
+ int32_t iIrq;
/** The new level. */
- int iLevel;
+ int32_t iLevel;
/** The IRQ tag and source. */
uint32_t uTagSrc;
} PciSetIrq;
@@ -1418,13 +1450,20 @@ typedef struct PDM
/** @name Queues
* @{ */
- /** Queue in which devhlp tasks are queued for R3 execution - R3 Ptr. */
- R3PTRTYPE(PPDMQUEUE) pDevHlpQueueR3;
- /** Queue in which devhlp tasks are queued for R3 execution - R0 Ptr. */
- R0PTRTYPE(PPDMQUEUE) pDevHlpQueueR0;
- /** Pointer to the queue which should be manually flushed - R0 Ptr.
- * Only touched by EMT. */
- R0PTRTYPE(struct PDMQUEUE *) pQueueFlushR0;
+ /** Number of ring-0 capable queues in apQueues. */
+ uint32_t cRing0Queues;
+ uint32_t u32Padding1;
+ /** Array of ring-0 capable queues running in parallel to PDMR0PERVM::aQueues. */
+ R3PTRTYPE(PPDMQUEUE) apRing0Queues[16];
+ /** Number of ring-3 only queues */
+ uint32_t cRing3Queues;
+ /** The allocation size of the ring-3 queue handle table. */
+ uint32_t cRing3QueuesAlloc;
+ /** Handle table for the ring-3 only queues. */
+ R3PTRTYPE(PPDMQUEUE *) papRing3Queues;
+
+ /** Queue in which devhlp tasks are queued for R3 execution. */
+ PDMQUEUEHANDLE hDevHlpQueue;
/** Bitmask controlling the queue flushing.
* See PDM_QUEUE_FLUSH_FLAG_ACTIVE and PDM_QUEUE_FLUSH_FLAG_PENDING. */
uint32_t volatile fQueueFlushing;
@@ -1506,9 +1545,14 @@ typedef struct PDMR0PERVM
PDMIOMMUR0 aIommus[PDM_IOMMUS_MAX];
/** Number of valid ring-0 device instances (apDevInstances). */
uint32_t cDevInstances;
- uint32_t u32Padding;
+ uint32_t u32Padding1;
/** Pointer to ring-0 device instances. */
R0PTRTYPE(struct PDMDEVINSR0 *) apDevInstances[190];
+ /** Number of valid ring-0 queue instances (aQueues). */
+ uint32_t cQueues;
+ uint32_t u32Padding2;
+ /** Array of ring-0 queues. */
+ PDMQUEUER0 aQueues[16];
} PDMR0PERVM;
@@ -1519,13 +1563,6 @@ typedef struct PDMUSERPERVM
{
/** @todo move more stuff over here. */
- /** Linked list of timer driven PDM queues.
- * Currently serialized by PDM::CritSect. */
- R3PTRTYPE(struct PDMQUEUE *) pQueuesTimer;
- /** Linked list of force action driven PDM queues.
- * Currently serialized by PDM::CritSect. */
- R3PTRTYPE(struct PDMQUEUE *) pQueuesForced;
-
/** Lock protecting the lists below it. */
RTCRITSECT ListCritSect;
/** Pointer to list of loaded modules. */
@@ -1659,7 +1696,12 @@ int pdmR3LdrInitU(PUVM pUVM);
void pdmR3LdrTermU(PUVM pUVM, bool fFinal);
char *pdmR3FileR3(const char *pszFile, bool fShared);
int pdmR3LoadR3U(PUVM pUVM, const char *pszFilename, const char *pszName);
+#endif /* IN_RING3 */
+void pdmQueueInit(PPDMQUEUE pQueue, uint32_t cbBitmap, uint32_t cbItem, uint32_t cItems,
+ const char *pszName, PDMQUEUETYPE enmType, RTR3PTR pfnCallback, RTR3PTR pvOwner);
+
+#ifdef IN_RING3
int pdmR3TaskInit(PVM pVM);
void pdmR3TaskTerm(PVM pVM);
@@ -1676,7 +1718,7 @@ void pdmR3ThreadDestroyAll(PVM pVM);
int pdmR3ThreadResumeAll(PVM pVM);
int pdmR3ThreadSuspendAll(PVM pVM);
-#ifdef VBOX_WITH_PDM_ASYNC_COMPLETION
+# ifdef VBOX_WITH_PDM_ASYNC_COMPLETION
int pdmR3AsyncCompletionInit(PVM pVM);
int pdmR3AsyncCompletionTerm(PVM pVM);
void pdmR3AsyncCompletionResume(PVM pVM);
@@ -1687,17 +1729,16 @@ int pdmR3AsyncCompletionTemplateCreateUsb(PVM pVM, PPDMUSBINS pUsbIns, P
int pdmR3AsyncCompletionTemplateDestroyDevice(PVM pVM, PPDMDEVINS pDevIns);
int pdmR3AsyncCompletionTemplateDestroyDriver(PVM pVM, PPDMDRVINS pDrvIns);
int pdmR3AsyncCompletionTemplateDestroyUsb(PVM pVM, PPDMUSBINS pUsbIns);
-#endif
+# endif
-#ifdef VBOX_WITH_NETSHAPER
+# ifdef VBOX_WITH_NETSHAPER
int pdmR3NetShaperInit(PVM pVM);
int pdmR3NetShaperTerm(PVM pVM);
-#endif
+# endif
int pdmR3BlkCacheInit(PVM pVM);
void pdmR3BlkCacheTerm(PVM pVM);
int pdmR3BlkCacheResume(PVM pVM);
-
#endif /* IN_RING3 */
void pdmLock(PVMCC pVM);
@@ -1725,6 +1766,8 @@ void pdmCritSectRwLeaveExclQueued(PVMCC pVM, PPDMCRITSECTRW pThis);
#ifdef IN_RING0
DECLHIDDEN(bool) pdmR0IsaSetIrq(PGVM pGVM, int iIrq, int iLevel, uint32_t uTagSrc);
+DECLHIDDEN(void) pdmR0QueueDestroy(PGVM pGVM, uint32_t iQueue);
+
#endif
#ifdef VBOX_WITH_DBGF_TRACING
diff --git a/src/VBox/VMM/testcase/Makefile.kmk b/src/VBox/VMM/testcase/Makefile.kmk
index ebca5958201..0357441def8 100644
--- a/src/VBox/VMM/testcase/Makefile.kmk
+++ b/src/VBox/VMM/testcase/Makefile.kmk
@@ -68,6 +68,7 @@ PROGRAMS += tstCFGM tstVMREQ tstMMHyperHeap tstAnimate
PROGRAMS += \
tstCompressionBenchmark \
tstIEMCheckMc \
+ tstPDMQueue \
tstSSM \
tstVMMR0CallHost-1 \
tstX86-FpuSaveRestore
@@ -459,6 +460,14 @@ NemRawBench-1_LDFLAGS.darwin = \
endif
endif
+#
+# PDM Queue tests.
+#
+tstPDMQueue_TEMPLATE := VBOXR3EXE
+tstPDMQueue_DEFS = $(VMM_COMMON_DEFS)
+tstPDMQueue_SOURCES := tstPDMQueue.cpp
+tstPDMQueue_LIBS := $(LIB_VMM) $(LIB_RUNTIME)
+
ifdef VBOX_WITH_PDM_ASYNC_COMPLETION
#
diff --git a/src/VBox/VMM/testcase/tstPDMQueue.cpp b/src/VBox/VMM/testcase/tstPDMQueue.cpp
new file mode 100644
index 00000000000..e3a12e80657
--- /dev/null
+++ b/src/VBox/VMM/testcase/tstPDMQueue.cpp
@@ -0,0 +1,459 @@
+/* $Id$ */
+/** @file
+ * PDM Queue Testcase.
+ */
+
+/*
+ * Copyright (C) 2022 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
+#define VBOX_IN_VMM
+
+#include <VBox/vmm/pdmqueue.h>
+
+#include <VBox/vmm/vm.h>
+#include <VBox/vmm/uvm.h>
+#include <VBox/vmm/vmm.h>
+
+#include <iprt/errcore.h>
+#include <VBox/log.h>
+#include <iprt/assert.h>
+#include <iprt/initterm.h>
+#include <iprt/message.h>
+#include <iprt/rand.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include <iprt/test.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static RTTEST g_hTest;
+
+
+/*********************************************************************************************************************************
+* Test #2 - Threading *
+*********************************************************************************************************************************/
+typedef struct TEST2ITEM
+{
+ PDMQUEUEITEMCORE Core;
+ uint32_t iSeqNo;
+ uint32_t iThreadNo;
+ /* Pad it up to two cachelines to reduce noise. */
+ uint8_t abPadding[128 - sizeof(PDMQUEUEITEMCORE) - sizeof(uint32_t) * 2];
+} TEST2ITEM;
+typedef TEST2ITEM *PTEST2ITEM;
+
+static struct TEST2THREAD
+{
+ RTTHREAD hThread;
+ uint32_t iThreadNo;
+ uint32_t cMaxPending;
+ /* Pad one cache line. */
+ uint8_t abPadding1[64];
+ uint32_t volatile cPending;
+ uint32_t volatile iReceiveSeqNo;
+ /* Pad structure size to three cache lines. */
+ uint8_t abPadding2[64 * 2 - sizeof(uint32_t) * 4 - sizeof(RTTHREAD)];
+} g_aTest2Threads[16];
+
+static bool volatile g_fTest2Terminate = false;
+static uint32_t volatile g_cTest2Threads = 0;
+static uint32_t g_cTest2Received = 0;
+static bool volatile g_fTest2PushBack = false;
+static PVM volatile g_pTest2VM = NULL; /**< Volatile to force local copy in thread function. */
+static PDMQUEUEHANDLE volatile g_hTest2Queue = NIL_PDMQUEUEHANDLE; /**< Ditto. */
+
+
+
+/**
+ * @callback_method_impl{FNPDMQUEUEEXT, Consumer callback}
+ */
+static DECLCALLBACK(bool) Test2ConsumerCallback(void *pvUser, PPDMQUEUEITEMCORE pItem)
+{
+ PTEST2ITEM pMyItem = (PTEST2ITEM)pItem;
+ size_t const iThread = pMyItem->iThreadNo;
+ RTTEST_CHECK_RET(g_hTest, iThread < RT_ELEMENTS(g_aTest2Threads), true);
+
+ /*
+ * Start pushing back after the first million or when the
+ * control thread decide it's time for it:
+ */
+ uint32_t cReceived = ++g_cTest2Received;
+ if (g_fTest2PushBack)
+ {
+ if ((cReceived & 3) == 3 && cReceived > _1M)
+ return false;
+ }
+ else if (cReceived < _1M )
+ { /* likely */ }
+ else
+ g_fTest2PushBack = true;
+
+ /*
+ * Process the item:
+ */
+ uint32_t iCallbackNo = ASMAtomicIncU32(&g_aTest2Threads[iThread].iReceiveSeqNo);
+ if (pMyItem->iSeqNo != iCallbackNo)
+ RTTestFailed(g_hTest, "iThread=%#x: iSeqNo=%#x, expected %#x\n", iThread, pMyItem->iSeqNo, iCallbackNo);
+
+ ASMAtomicDecU32(&g_aTest2Threads[iThread].cPending);
+
+ RT_NOREF(pvUser);
+ return true;
+}
+
+
+/**
+ * @callback_method_impl{FNRTTHREAD, Producer thread}
+ */
+static DECLCALLBACK(int) Test2Thread(RTTHREAD hThreadSelf, void *pvUser)
+{
+ PVM const pVM = g_pTest2VM;
+ PDMQUEUEHANDLE const hQueue = g_hTest2Queue;
+ size_t const iThread = (size_t)pvUser;
+ RTTEST_CHECK_RET(g_hTest, iThread < RT_ELEMENTS(g_aTest2Threads), VERR_INVALID_PARAMETER);
+
+ uint32_t iSendSeqNo = 0;
+ uint32_t cSpinLoops = 0;
+ while (!g_fTest2Terminate && iSendSeqNo < _64M)
+ {
+ if (g_aTest2Threads[iThread].cPending < g_aTest2Threads[iThread].cMaxPending)
+ {
+ PTEST2ITEM pMyItem = (PTEST2ITEM)PDMQueueAlloc(pVM, hQueue, pVM);
+ if (pMyItem)
+ {
+ pMyItem->iSeqNo = ++iSendSeqNo;
+ pMyItem->iThreadNo = (uint32_t)iThread;
+ RTTEST_CHECK_RC(g_hTest, PDMQueueInsert(pVM, hQueue, pVM, &pMyItem->Core), VINF_SUCCESS);
+ ASMAtomicIncU32(&g_aTest2Threads[iThread].cPending);
+ }
+ else
+ {
+ RTTestFailed(g_hTest, "iThread=%u: PDMQueueAlloc failed: cPending=%u cMaxPending=%u iSendSeqNo=%u",
+ iThread, g_aTest2Threads[iThread].cPending, g_aTest2Threads[iThread].cMaxPending, iSendSeqNo);
+ ASMAtomicWriteBool(&g_fTest2Terminate, true);
+ break;
+ }
+
+ cSpinLoops = 0;
+ }
+ else if (cSpinLoops++ < 1024)
+ ASMNopPause();
+ else
+ {
+ RTThreadYield();
+ cSpinLoops = 0;
+ }
+ }
+
+ ASMAtomicDecU32(&g_cTest2Threads);
+
+ RT_NOREF(hThreadSelf);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @callback_method_impl{FNRTTHREAD, Control thread}
+ */
+static DECLCALLBACK(int) Test2ControlThread(RTTHREAD hThreadSelf, void *pvUser)
+{
+ RT_NOREF(hThreadSelf, pvUser);
+
+ RTThreadSleep(RT_MS_5SEC);
+ ASMAtomicWriteBool(&g_fTest2PushBack, true);
+
+ RTThreadSleep(RT_MS_30SEC);
+ ASMAtomicWriteBool(&g_fTest2Terminate, true);
+
+ ASMAtomicDecU32(&g_cTest2Threads);
+ return VINF_SUCCESS;
+}
+
+
+static DECLCALLBACK(int) Test2Emt(PVM pVM, PUVM pUVM)
+{
+ uint32_t cThreads = 2;
+ RTTestSubF(g_hTest, "%u Threads", cThreads);
+ RTTEST_CHECK_RET(g_hTest, cThreads < RT_ELEMENTS(g_aTest2Threads) /* last entry is control thread*/, VERR_OUT_OF_RANGE);
+
+ PDMQUEUEHANDLE hQueue;
+ RTTEST_CHECK_RC_RET(g_hTest, PDMR3QueueCreateExternal(pVM, sizeof(TEST2ITEM), cThreads * 128 + 16, 0 /*cMilliesInterval*/,
+ Test2ConsumerCallback, pVM /*pvUser*/, "Test2", &hQueue),
+ VINF_SUCCESS, VINF_SUCCESS);
+
+ /* Init the thread data: */
+ g_fTest2Terminate = false;
+ g_pTest2VM = pVM;
+ g_hTest2Queue = hQueue;
+ g_fTest2PushBack = false;
+ g_cTest2Received = 0;
+ for (uint32_t i = 0; i < cThreads; i++)
+ {
+ g_aTest2Threads[i].hThread = NIL_RTTHREAD;
+ g_aTest2Threads[i].iThreadNo = i;
+ g_aTest2Threads[i].cMaxPending = 64 + i % 16;
+ g_aTest2Threads[i].cPending = 0;
+ g_aTest2Threads[i].iReceiveSeqNo = 0;
+ }
+
+ /* Start the threads: */
+ for (uint32_t i = 0; i < cThreads; i++)
+ {
+ RTTEST_CHECK_RC_BREAK(g_hTest, RTThreadCreateF(&g_aTest2Threads[i].hThread, Test2Thread, (void *)(uintptr_t)i, 0,
+ RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "test2-t%u", i),
+ VINF_SUCCESS);
+ ASMAtomicIncU32(&g_cTest2Threads);
+ }
+
+ int rc;
+ RTTEST_CHECK_RC(g_hTest, rc = RTThreadCreate(&g_aTest2Threads[cThreads].hThread, Test2ControlThread, NULL, 0,
+ RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "test2-ctl"),
+ VINF_SUCCESS);
+ if (RT_SUCCESS(rc))
+ ASMAtomicIncU32(&g_cTest2Threads);
+
+ /* Process the queue till all threads have quit or termination is triggered: */
+ while ( ASMAtomicUoReadU32(&g_cTest2Threads) != 0
+ && !g_fTest2Terminate)
+ {
+ PDMR3QueueFlushAll(pVM);
+ }
+
+ /* Wait for the threads. */
+ ASMAtomicWriteBool(&g_fTest2Terminate, true);
+ for (uint32_t i = 0; i <= cThreads; i++)
+ {
+ if (g_aTest2Threads[i].hThread != NIL_RTTHREAD)
+ {
+ int rcThread = VERR_GENERAL_FAILURE;
+ RTTEST_CHECK_RC(g_hTest, RTThreadWait(g_aTest2Threads[i].hThread, RT_MS_30SEC, &rcThread), VINF_SUCCESS);
+ RTTEST_CHECK_RC(g_hTest, rcThread, VINF_SUCCESS);
+ }
+ }
+
+ STAMR3Print(pUVM, "/PDM/Queue/Test2/*");
+
+ /* Cleanup: */
+ RTTEST_CHECK_RC(g_hTest, PDMR3QueueDestroy(pVM, hQueue, pVM), VINF_SUCCESS);
+ RTTestSubDone(g_hTest);
+ return VINF_SUCCESS;
+}
+
+
+/*********************************************************************************************************************************
+* Test #1 - Basics *
+*********************************************************************************************************************************/
+static uint32_t volatile g_cTest1Callbacks = 0;
+static int32_t volatile g_cTest1Pushback = INT32_MAX;
+
+typedef struct TEST1ITEM
+{
+ PDMQUEUEITEMCORE Core;
+ uint32_t iSeqNo;
+} TEST1ITEM;
+typedef TEST1ITEM *PTEST1ITEM;
+
+
+/** @callback_method_impl{FNPDMQUEUEEXT} */
+static DECLCALLBACK(bool) Test1ConsumerCallback(void *pvUser, PPDMQUEUEITEMCORE pItem)
+{
+ if (ASMAtomicDecS32(&g_cTest1Pushback) < 0)
+ return false;
+
+ PTEST1ITEM pMyItem = (PTEST1ITEM)pItem;
+ uint32_t iCallbackNo = ASMAtomicIncU32(&g_cTest1Callbacks);
+ if (pMyItem->iSeqNo != iCallbackNo)
+ RTTestFailed(g_hTest, "iSeqNo=%#x, expected %#x\n", pMyItem->iSeqNo, iCallbackNo);
+
+ RT_NOREF(pvUser);
+ return true;
+}
+
+
+static DECLCALLBACK(int) Test1Emt(PVM pVM)
+{
+ RTTestSub(g_hTest, "Basics");
+
+ PDMQUEUEHANDLE hQueue;
+ RTTEST_CHECK_RC_RET(g_hTest, PDMR3QueueCreateExternal(pVM, sizeof(TEST1ITEM), 16, 0 /*cMilliesInterval*/,
+ Test1ConsumerCallback, pVM /*pvUser*/, "Test1", &hQueue),
+ VINF_SUCCESS, VINF_SUCCESS);
+
+ PDMQUEUEHANDLE const hQueueFirst = hQueue; /* Save the handle value so we can check that it's correctly reused. */
+
+ /*
+ * Single item:
+ */
+ PTEST1ITEM pMyItem = (PTEST1ITEM)PDMQueueAlloc(pVM, hQueue, pVM);
+ RTTEST_CHECK(g_hTest, pMyItem);
+ pMyItem->iSeqNo = 1;
+ RTTEST_CHECK_RC(g_hTest, PDMQueueInsert(pVM, hQueue, pVM, &pMyItem->Core), VINF_SUCCESS);
+
+ PDMR3QueueFlushAll(pVM);
+ RTTEST_CHECK(g_hTest, g_cTest1Callbacks == 1);
+
+ /*
+ * All items:
+ */
+ for (uint32_t i = 0; i < 16; i++)
+ {
+ pMyItem = (PTEST1ITEM)PDMQueueAlloc(pVM, hQueue, pVM);
+ RTTEST_CHECK_BREAK(g_hTest, pMyItem);
+ pMyItem->iSeqNo = i + 2;
+ RTTEST_CHECK_RC(g_hTest, PDMQueueInsert(pVM, hQueue, pVM, &pMyItem->Core), VINF_SUCCESS);
+ }
+
+ pMyItem = (PTEST1ITEM)PDMQueueAlloc(pVM, hQueue, pVM);
+ RTTEST_CHECK(g_hTest, pMyItem == NULL);
+
+ PDMR3QueueFlushAll(pVM);
+ RTTEST_CHECK(g_hTest, g_cTest1Callbacks == 17);
+
+ /*
+ * Push back.
+ * 1. First queue all items.
+ * 2. Process half of them.
+ * 3. The process one by one.
+ */
+ g_cTest1Callbacks = 0;
+ g_cTest1Pushback = 8;
+
+ for (uint32_t i = 0; i < 16; i++)
+ {
+ pMyItem = (PTEST1ITEM)PDMQueueAlloc(pVM, hQueue, pVM);
+ RTTEST_CHECK_BREAK(g_hTest, pMyItem);
+ pMyItem->iSeqNo = i + 1;
+ RTTEST_CHECK_RC(g_hTest, PDMQueueInsert(pVM, hQueue, pVM, &pMyItem->Core), VINF_SUCCESS);
+ }
+
+ pMyItem = (PTEST1ITEM)PDMQueueAlloc(pVM, hQueue, pVM);
+ RTTEST_CHECK(g_hTest, pMyItem == NULL);
+
+ PDMR3QueueFlushAll(pVM);
+ RTTEST_CHECK(g_hTest, g_cTest1Callbacks == 8);
+
+ for (uint32_t i = 0; i < 8; i++)
+ {
+ g_cTest1Pushback = 1;
+ PDMR3QueueFlushAll(pVM);
+ RTTEST_CHECK(g_hTest, g_cTest1Callbacks == 8 + 1 + i);
+ }
+
+ /*
+ * Cleanup.
+ */
+ RTTEST_CHECK_RC(g_hTest, PDMR3QueueDestroy(pVM, hQueue, pVM), VINF_SUCCESS);
+
+ /*
+ * Do some creation/deletion ordering checks.
+ */
+ RTTestSub(g_hTest, "Cleanup & handle reuse");
+ PDMQUEUEHANDLE ahQueues[168];
+ for (size_t i = 0; i < RT_ELEMENTS(ahQueues); i++)
+ ahQueues[i] = NIL_PDMQUEUEHANDLE;
+ for (uint32_t i = 0; i < RT_ELEMENTS(ahQueues); i++)
+ {
+ char szQueueNm[32];
+ RTStrPrintf(szQueueNm, sizeof(szQueueNm), "Test1b-%u", i);
+ RTTEST_CHECK_RC(g_hTest, PDMR3QueueCreateExternal(pVM, sizeof(TEST1ITEM), i + 1, 0 /*cMilliesInterval*/,
+ Test1ConsumerCallback, pVM /*pvUser*/, szQueueNm, &ahQueues[i]),
+ VINF_SUCCESS);
+ if (i == 0 && ahQueues[0] != hQueueFirst)
+ RTTestFailed(g_hTest, "Queue handle value not reused: %#RX64, expected %#RX64", ahQueues[0], hQueueFirst);
+ }
+
+ /* Delete them in random order. */
+ for (uint32_t i = 0; i < RT_ELEMENTS(ahQueues); i++)
+ {
+ uint32_t iDelete = RTRandU32Ex(0, RT_ELEMENTS(ahQueues) - 1);
+ if (ahQueues[iDelete] != NIL_PDMQUEUEHANDLE)
+ {
+ RTTEST_CHECK_RC(g_hTest, PDMR3QueueDestroy(pVM, ahQueues[iDelete], pVM), VINF_SUCCESS);
+ ahQueues[iDelete] = NIL_PDMQUEUEHANDLE;
+ }
+ }
+
+ /* Delete remainder in ascending order, creating a array shrinking at the end. */
+ for (uint32_t i = 0; i < RT_ELEMENTS(ahQueues); i++)
+ if (ahQueues[i] != NIL_PDMQUEUEHANDLE)
+ {
+ RTTEST_CHECK_RC(g_hTest, PDMR3QueueDestroy(pVM, ahQueues[i], pVM), VINF_SUCCESS);
+ ahQueues[i] = NIL_PDMQUEUEHANDLE;
+ }
+
+ /* Create one more queue and check that we get the first queue handle again. */
+ RTTEST_CHECK_RC(g_hTest, PDMR3QueueCreateExternal(pVM, sizeof(TEST1ITEM), 1, 0 /*cMilliesInterval*/,
+ Test1ConsumerCallback, pVM /*pvUser*/, "Test1c", &hQueue), VINF_SUCCESS);
+ if (hQueue != hQueueFirst)
+ RTTestFailed(g_hTest, "Queue handle value not reused: %#RX64, expected %#RX64", hQueue, hQueueFirst);
+ RTTEST_CHECK_RC(g_hTest, PDMR3QueueDestroy(pVM, hQueue, pVM), VINF_SUCCESS);
+
+ RTTestSubDone(g_hTest);
+ return VINF_SUCCESS;
+}
+
+
+static void DoTests(void)
+{
+ PVM pVM;
+ PUVM pUVM;
+ RTTESTI_CHECK_RC_OK_RETV(VMR3Create(1, NULL, NULL, NULL, NULL, NULL, &pVM, &pUVM));
+
+ /*
+ * Do the tests.
+ */
+ RTTESTI_CHECK_RC(VMR3ReqCallWaitU(pUVM, 0, (PFNRT)Test1Emt, 1, pVM), VINF_SUCCESS);
+ if (RTTestErrorCount(g_hTest) == 0)
+ {
+ RTTESTI_CHECK_RC(VMR3ReqCallWaitU(pUVM, 0, (PFNRT)Test2Emt, 2, pVM, pUVM), VINF_SUCCESS);
+ }
+
+ /*
+ * Clean up.
+ */
+ RTTESTI_CHECK_RC_OK_RETV(VMR3PowerOff(pUVM));
+ RTTESTI_CHECK_RC_OK_RETV(VMR3Destroy(pUVM));
+ VMR3ReleaseUVM(pUVM);
+}
+
+
+int main(int argc, char **argv)
+{
+ /*
+ * We run the VMM in driverless mode to avoid needing to hardened the testcase
+ */
+ RTEXITCODE rcExit;
+ int rc = RTR3InitExe(argc, &argv, SUPR3INIT_F_DRIVERLESS << RTR3INIT_FLAGS_SUPLIB_SHIFT);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTTestCreate("tstPDMQueue", &g_hTest);
+ if (RT_SUCCESS(rc))
+ {
+ RTTestBanner(g_hTest);
+ DoTests();
+ rcExit = RTTestSummaryAndDestroy(g_hTest);
+ }
+ else
+ rcExit = RTMsgErrorExitFailure("RTTestCreate failed: %Rrc", rc);
+ }
+ else
+ rcExit = RTMsgInitFailure(rc);
+ return rcExit;
+}
+