summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/common/ioqueue/ioqueuebase.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Runtime/common/ioqueue/ioqueuebase.cpp')
-rw-r--r--src/VBox/Runtime/common/ioqueue/ioqueuebase.cpp297
1 files changed, 297 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/ioqueue/ioqueuebase.cpp b/src/VBox/Runtime/common/ioqueue/ioqueuebase.cpp
new file mode 100644
index 00000000..f2bcb9da
--- /dev/null
+++ b/src/VBox/Runtime/common/ioqueue/ioqueuebase.cpp
@@ -0,0 +1,297 @@
+/* $Id: ioqueuebase.cpp $ */
+/** @file
+ * IPRT - I/O queue, Base/Public API.
+ */
+
+/*
+ * Copyright (C) 2019-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_IOQUEUE
+#include <iprt/ioqueue.h>
+
+#include <iprt/asm.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/semaphore.h>
+#include <iprt/string.h>
+
+#include "internal/ioqueue.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * Internal I/O queue instance data.
+ */
+typedef struct RTIOQUEUEINT
+{
+ /** Magic identifying the I/O queue structure. */
+ uint32_t u32Magic;
+ /** Pointer to the provider vtable. */
+ PCRTIOQUEUEPROVVTABLE pVTbl;
+ /** I/O queue provider instance handle. */
+ RTIOQUEUEPROV hIoQueueProv;
+ /** Maximum number of submission queue entries - constant. */
+ uint32_t cSqEntries;
+ /** Maximum number of completion queue entries - constant. */
+ uint32_t cCqEntries;
+ /** Number of currently committed and not completed requests. */
+ volatile uint32_t cReqsCommitted;
+ /** Number of prepared requests. */
+ volatile uint32_t cReqsPrepared;
+ /** Start of the provider specific instance data - vvariable in size. */
+ uint8_t abInst[1];
+} RTIOQUEUEINT;
+/** Pointer to the internal I/O queue instance data. */
+typedef RTIOQUEUEINT *PRTIOQUEUEINT;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Array of I/O queue providers we know about, order is important for each type.
+ * The best suited ones for each platform should come first.
+ */
+static PCRTIOQUEUEPROVVTABLE g_apIoQueueProviders[] =
+{
+#if defined(RT_OS_LINUX)
+ &g_RTIoQueueLnxIoURingProv,
+#endif
+#ifndef RT_OS_OS2
+ &g_RTIoQueueAioFileProv,
+#endif
+ &g_RTIoQueueStdFileProv
+};
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+
+RTDECL(PCRTIOQUEUEPROVVTABLE) RTIoQueueProviderGetBestForHndType(RTHANDLETYPE enmHnd)
+{
+ /* Go through the array and pick the first supported provider for the given handle type. */
+ for (unsigned i = 0; i < RT_ELEMENTS(g_apIoQueueProviders); i++)
+ {
+ PCRTIOQUEUEPROVVTABLE pIoQueueProv = g_apIoQueueProviders[i];
+ if ( pIoQueueProv->enmHnd == enmHnd
+ && pIoQueueProv->pfnIsSupported())
+ return pIoQueueProv;
+ }
+
+ return NULL;
+}
+
+
+RTDECL(PCRTIOQUEUEPROVVTABLE) RTIoQueueProviderGetById(const char *pszId)
+{
+ for (unsigned i = 0; i < RT_ELEMENTS(g_apIoQueueProviders); i++)
+ {
+ PCRTIOQUEUEPROVVTABLE pIoQueueProv = g_apIoQueueProviders[i];
+ if (!strcmp(pIoQueueProv->pszId, pszId))
+ return pIoQueueProv;
+ }
+
+ return NULL;
+}
+
+
+RTDECL(int) RTIoQueueCreate(PRTIOQUEUE phIoQueue, PCRTIOQUEUEPROVVTABLE pProvVTable,
+ uint32_t fFlags, uint32_t cSqEntries, uint32_t cCqEntries)
+{
+ AssertPtrReturn(phIoQueue, VERR_INVALID_POINTER);
+ AssertPtrReturn(pProvVTable, VERR_INVALID_POINTER);
+ AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
+ AssertReturn(cSqEntries > 0, VERR_INVALID_PARAMETER);
+ AssertReturn(cCqEntries > 0, VERR_INVALID_PARAMETER);
+
+ int rc = VINF_SUCCESS;
+ PRTIOQUEUEINT pThis = (PRTIOQUEUEINT)RTMemAllocZ(RT_UOFFSETOF_DYN(RTIOQUEUEINT, abInst[pProvVTable->cbIoQueueProv]));
+ if (RT_LIKELY(pThis))
+ {
+ pThis->pVTbl = pProvVTable;
+ pThis->hIoQueueProv = (RTIOQUEUEPROV)&pThis->abInst[0];
+ pThis->cSqEntries = cSqEntries;
+ pThis->cCqEntries = cCqEntries;
+ pThis->cReqsCommitted = 0;
+ pThis->cReqsPrepared = 0;
+
+ rc = pThis->pVTbl->pfnQueueInit(pThis->hIoQueueProv, fFlags, cSqEntries, cCqEntries);
+ if (RT_SUCCESS(rc))
+ {
+ *phIoQueue = pThis;
+ return VINF_SUCCESS;
+ }
+
+ RTMemFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+RTDECL(int) RTIoQueueDestroy(RTIOQUEUE hIoQueue)
+{
+ PRTIOQUEUEINT pThis = hIoQueue;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(ASMAtomicReadU32(&pThis->cReqsCommitted) == 0, VERR_IOQUEUE_BUSY);
+
+ pThis->pVTbl->pfnQueueDestroy(pThis->hIoQueueProv);
+ RTMemFree(pThis);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTIoQueueHandleRegister(RTIOQUEUE hIoQueue, PCRTHANDLE pHandle)
+{
+ PRTIOQUEUEINT pThis = hIoQueue;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+
+ /** @todo Efficiently check that handle wasn't registered previously. */
+ return pThis->pVTbl->pfnHandleRegister(pThis->hIoQueueProv, pHandle);
+}
+
+
+RTDECL(int) RTIoQueueHandleDeregister(RTIOQUEUE hIoQueue, PCRTHANDLE pHandle)
+{
+ PRTIOQUEUEINT pThis = hIoQueue;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+
+ /** @todo Efficiently check that handle was registered previously. */
+ return pThis->pVTbl->pfnHandleDeregister(pThis->hIoQueueProv, pHandle);
+}
+
+
+RTDECL(int) RTIoQueueRequestPrepare(RTIOQUEUE hIoQueue, PCRTHANDLE pHandle, RTIOQUEUEOP enmOp,
+ uint64_t off, void *pvBuf, size_t cbBuf, uint32_t fReqFlags,
+ void *pvUser)
+{
+ PRTIOQUEUEINT pThis = hIoQueue;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pHandle->enmType == pThis->pVTbl->enmHnd, VERR_INVALID_HANDLE);
+
+ /** @todo Efficiently check that handle was registered previously. */
+ int rc = pThis->pVTbl->pfnReqPrepare(pThis->hIoQueueProv, pHandle, enmOp, off, pvBuf, cbBuf,
+ fReqFlags, pvUser);
+ if (RT_SUCCESS(rc))
+ ASMAtomicIncU32(&pThis->cReqsPrepared);
+
+ return rc;
+}
+
+
+RTDECL(int) RTIoQueueRequestPrepareSg(RTIOQUEUE hIoQueue, PCRTHANDLE pHandle, RTIOQUEUEOP enmOp,
+ uint64_t off, PCRTSGBUF pSgBuf, size_t cbSg, uint32_t fReqFlags,
+ void *pvUser)
+{
+ PRTIOQUEUEINT pThis = hIoQueue;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pHandle->enmType == pThis->pVTbl->enmHnd, VERR_INVALID_HANDLE);
+
+ /** @todo Efficiently check that handle was registered previously. */
+ int rc = pThis->pVTbl->pfnReqPrepareSg(pThis->hIoQueueProv, pHandle, enmOp, off, pSgBuf, cbSg,
+ fReqFlags, pvUser);
+ if (RT_SUCCESS(rc))
+ ASMAtomicIncU32(&pThis->cReqsPrepared);
+
+ return rc;
+}
+
+
+RTDECL(int) RTIoQueueCommit(RTIOQUEUE hIoQueue)
+{
+ PRTIOQUEUEINT pThis = hIoQueue;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(ASMAtomicReadU32(&pThis->cReqsPrepared) > 0, VERR_IOQUEUE_EMPTY);
+
+ uint32_t cReqsPreparedOld = 0;
+ uint32_t cReqsCommitted = 0;
+ int rc = VINF_SUCCESS;
+ do
+ {
+ rc = pThis->pVTbl->pfnCommit(pThis->hIoQueueProv, &cReqsCommitted);
+ if (RT_SUCCESS(rc))
+ {
+ ASMAtomicAddU32(&pThis->cReqsCommitted, cReqsCommitted);
+ cReqsPreparedOld = ASMAtomicSubU32(&pThis->cReqsPrepared, cReqsCommitted);
+ }
+ } while (RT_SUCCESS(rc) && cReqsPreparedOld - cReqsCommitted > 0);
+
+ return rc;
+}
+
+
+RTDECL(int) RTIoQueueEvtWait(RTIOQUEUE hIoQueue, PRTIOQUEUECEVT paCEvt, uint32_t cCEvt, uint32_t cMinWait,
+ uint32_t *pcCEvt, uint32_t fFlags)
+{
+ PRTIOQUEUEINT pThis = hIoQueue;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertPtrReturn(paCEvt, VERR_INVALID_POINTER);
+ AssertReturn(cCEvt > 0, VERR_INVALID_PARAMETER);
+ AssertReturn(cMinWait > 0, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pcCEvt, VERR_INVALID_POINTER);
+ AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
+ AssertReturn(ASMAtomicReadU32(&pThis->cReqsCommitted) > 0, VERR_IOQUEUE_EMPTY);
+
+ *pcCEvt = 0;
+ int rc = pThis->pVTbl->pfnEvtWait(pThis->hIoQueueProv, paCEvt, cCEvt, cMinWait, pcCEvt, fFlags);
+ if ( (RT_SUCCESS(rc) || rc == VERR_INTERRUPTED)
+ && *pcCEvt > 0)
+ ASMAtomicSubU32(&pThis->cReqsCommitted, *pcCEvt);
+
+ return rc;
+}
+
+
+RTDECL(int) RTIoQueueEvtWaitWakeup(RTIOQUEUE hIoQueue)
+{
+ PRTIOQUEUEINT pThis = hIoQueue;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+
+ return pThis->pVTbl->pfnEvtWaitWakeup(pThis->hIoQueueProv);
+}
+