/* $Id: ioqueuebase.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */ /** @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 . * * 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 #include #include #include #include #include #include #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); }