summaryrefslogtreecommitdiffstats
path: root/src/VBox/VMM/VMMAll/PDMAllQueue.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/VBox/VMM/VMMAll/PDMAllQueue.cpp313
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;
+}
+