diff options
Diffstat (limited to 'src/VBox/VMM/VMMAll/PDMAllQueue.cpp')
-rw-r--r-- | src/VBox/VMM/VMMAll/PDMAllQueue.cpp | 313 |
1 files changed, 313 insertions, 0 deletions
diff --git a/src/VBox/VMM/VMMAll/PDMAllQueue.cpp b/src/VBox/VMM/VMMAll/PDMAllQueue.cpp new file mode 100644 index 00000000..f64a198b --- /dev/null +++ b/src/VBox/VMM/VMMAll/PDMAllQueue.cpp @@ -0,0 +1,313 @@ +/* $Id: PDMAllQueue.cpp $ */ +/** @file + * PDM Queue - Transport data and tasks to EMT and R3. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_PDM_QUEUE +#include "PDMInternal.h" +#include <VBox/vmm/pdm.h> +#ifndef IN_RC +# include <VBox/vmm/mm.h> +#endif +#include <VBox/vmm/vmcc.h> +#include <iprt/errcore.h> +#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 + + +/** + * 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 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(int) PDMQueueAllocEx(PVMCC pVM, PDMQUEUEHANDLE hQueue, void *pvOwner, PPDMQUEUEITEMCORE *ppNew) +{ + /* + * Validate and translate input. + */ + *ppNew = NULL; + PDMQUEUE_HANDLE_TO_VARS_RETURN(pVM, hQueue, pvOwner); + + /* + * Do the allocation. + */ + uint32_t cEmptyScans = 0; + for (;;) + { + 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 VERR_OUT_OF_RESOURCES; + } + } +} + + +/** + * Allocate an item from a queue. + * + * The allocated item must be handed on to PDMR3QueueInsert() after the + * data have been filled in. + * + * @returns Pointer to the new item on success, NULL on failure. + * @param pVM Pointer to the cross context VM structure w/ ring-0. + * @param hQueue The queue handle. + * @param pvOwner The queue owner. + * @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 pVM Pointer to the cross context VM structure w/ ring-0. + */ +static void pdmQueueSetFF(PVMCC 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); +#ifdef IN_RING3 + VMR3NotifyGlobalFFU(pVM->pUVM, VMNOTIFYFF_FLAGS_DONE_REM); +#endif +} + + +/** + * 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! + * + * @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(int) PDMQueueInsert(PVMCC pVM, PDMQUEUEHANDLE hQueue, void *pvOwner, PPDMQUEUEITEMCORE pInsert) +{ + /* + * Validate and translate input. + */ + PDMQUEUE_HANDLE_TO_VARS_RETURN(pVM, hQueue, pvOwner); + + 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); + + AssertReturn(ASMBitTest(pQueue->bmAlloc, iInsert) == false, VERR_INVALID_PARAMETER); + + /* + * Append the item to the pending list. + */ + for (;;) + { + uint32_t const iOldPending = ASMAtomicUoReadU32(&pQueue->iPending); + pInsert->iNext = iOldPending; + if (ASMAtomicCmpXchgU32(&pQueue->iPending, iInsert, iOldPending)) + break; + ASMNopPause(); + } + + if (pQueue->hTimer == NIL_TMTIMERHANDLE) + pdmQueueSetFF(pVM); + STAM_REL_COUNTER_INC(&pQueue->StatInsert); + STAM_STATS({ ASMAtomicIncU32(&pQueue->cStatPending); }); + + return VINF_SUCCESS; +} + + +/** + * Schedule the queue for flushing (processing) if necessary. + * + * @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(int) PDMQueueFlushIfNecessary(PVMCC pVM, PDMQUEUEHANDLE hQueue, void *pvOwner) +{ + /* + * Validate input. + */ + PDMQUEUE_HANDLE_TO_VARS_RETURN(pVM, hQueue, pvOwner); + RT_NOREF(offItems); + + /* + * Check and maybe flush. + */ + if (ASMAtomicUoReadU32(&pQueue->iPending) != UINT32_MAX) + { + pdmQueueSetFF(pVM); + return VINF_SUCCESS; + } + return VINF_NO_CHANGE; +} + |