diff options
author | vboxsync <vboxsync@cfe28804-0f27-0410-a406-dd0f0b0b656f> | 2022-02-05 19:03:08 +0000 |
---|---|---|
committer | vboxsync <vboxsync@cfe28804-0f27-0410-a406-dd0f0b0b656f> | 2022-02-05 19:03:08 +0000 |
commit | 3e8646c1ded46328b99dba1176f1174ef8d89ee8 (patch) | |
tree | 66d280c3a46914f0eba3bd75fdab94fc4545ef5d /src | |
parent | d00b1193f2493e784a090bcd590ee23a21de5797 (diff) | |
download | VirtualBox-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.kmk | 1 | ||||
-rw-r--r-- | src/VBox/VMM/VMMAll/PDMAll.cpp | 8 | ||||
-rw-r--r-- | src/VBox/VMM/VMMAll/PDMAllQueue.cpp | 315 | ||||
-rw-r--r-- | src/VBox/VMM/VMMR0/PDMR0DevHlp.cpp | 33 | ||||
-rw-r--r-- | src/VBox/VMM/VMMR0/PDMR0DevHlpTracing.cpp | 6 | ||||
-rw-r--r-- | src/VBox/VMM/VMMR0/PDMR0Device.cpp | 7 | ||||
-rw-r--r-- | src/VBox/VMM/VMMR0/VMMR0.cpp | 8 | ||||
-rw-r--r-- | src/VBox/VMM/VMMR3/PDMDevHlp.cpp | 22 | ||||
-rw-r--r-- | src/VBox/VMM/VMMR3/PDMDevice.cpp | 5 | ||||
-rw-r--r-- | src/VBox/VMM/VMMR3/PDMDriver.cpp | 24 | ||||
-rw-r--r-- | src/VBox/VMM/VMMR3/PDMQueue.cpp | 795 | ||||
-rw-r--r-- | src/VBox/VMM/include/PDMInternal.h | 193 | ||||
-rw-r--r-- | src/VBox/VMM/testcase/Makefile.kmk | 9 | ||||
-rw-r--r-- | src/VBox/VMM/testcase/tstPDMQueue.cpp | 459 |
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; +} + |