VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/ioqueue/ioqueuebase.cpp@ 79949

Last change on this file since 79949 was 79949, checked in by vboxsync, 6 years ago

Runtime: bugref:8231 Starting on defining and implementing a new RTIoQueue API to replace RTFileAio mid term.

The RTIoQueue API is meant to be more efficient as it doesn't require allocation
of request structures and is only meant as a thin layer around host dependent APIs.
It will support mutiple providers for different handle types and the best suited provider
supported on a particular host can be selected. This allows multiple implementations
to coexist for the same host in an easy manner.
The providers currently being implemented are (in various stages of the implementation):

  • ioqueue-stdfile-provider.cpp:

A fallback provider if nothing else is available using the synchronous RTFile* APIs
emulating asynchronous behavior by using a dedicated worker thread.

  • ioqueue-aiofile-provider.cpp:

Uses the current RTFileAio* API, will get replaced by dedicated provider implementations for
each host later on.

  • ioqueue-iouringfile-provider.cpp:

Uses the recently added io_uring interface for Linux kernels 5.1 and newer. The new interface
is a great improvement over the old aio interface which is cumbersome to use and has various
restrictions. If not available on a host the code can fall back to one of the other providers.

The I/O queue interface is also meant to be suitable for sockets which can be implemented later.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.6 KB
Line 
1/* $Id: ioqueuebase.cpp 79949 2019-07-24 11:05:45Z vboxsync $ */
2/** @file
3 * IPRT - I/O queue, Base/Public API.
4 */
5
6/*
7 * Copyright (C) 2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_IOQUEUE
32#include <iprt/ioqueue.h>
33
34#include <iprt/asm.h>
35#include <iprt/err.h>
36#include <iprt/log.h>
37#include <iprt/mem.h>
38#include <iprt/semaphore.h>
39#include <iprt/string.h>
40
41#include "internal/ioqueue.h"
42
43
44/*********************************************************************************************************************************
45* Defined Constants And Macros *
46*********************************************************************************************************************************/
47
48
49
50/*********************************************************************************************************************************
51* Structures and Typedefs *
52*********************************************************************************************************************************/
53
54/**
55 * Internal I/O queue instance data.
56 */
57typedef struct RTIOQUEUEINT
58{
59 /** Magic identifying the I/O queue structure. */
60 uint32_t u32Magic;
61 /** Pointer to the provider vtable. */
62 PCRTIOQUEUEPROVVTABLE pVTbl;
63 /** I/O queue provider instance handle. */
64 RTIOQUEUEPROV hIoQueueProv;
65 /** Maximum number of submission queue entries - constant. */
66 uint32_t cSqEntries;
67 /** Maximum number of completion queue entries - constant. */
68 uint32_t cCqEntries;
69 /** Number of currently committed and not completed requests. */
70 volatile uint32_t cReqsCommitted;
71 /** Number of prepared requests. */
72 volatile uint32_t cReqsPrepared;
73 /** Start of the provider specific instance data - vvariable in size. */
74 uint8_t abInst[1];
75} RTIOQUEUEINT;
76/** Pointer to the internal I/O queue instance data. */
77typedef RTIOQUEUEINT *PRTIOQUEUEINT;
78
79
80/*********************************************************************************************************************************
81* Global Variables *
82*********************************************************************************************************************************/
83/** Array of I/O queue providers we know about, order is important for each type.
84 * The best suited ones for each platform should come first.
85 */
86static PCRTIOQUEUEPROVVTABLE g_apIoQueueProviders[] =
87{
88#if defined(RT_OS_LINUX)
89 &g_RTIoQueueLnxIoURingProv,
90#endif
91 &g_RTIoQueueAioFileProv,
92 &g_RTIoQueueStdFileProv
93};
94
95
96/*********************************************************************************************************************************
97* Internal Functions *
98*********************************************************************************************************************************/
99
100
101RTDECL(PCRTIOQUEUEPROVVTABLE) RTIoQueueProviderGetBestForHndType(RTHANDLETYPE enmHnd)
102{
103 /* Go through the array and pick the first supported provider for the given handle type. */
104 for (unsigned i = 0; i < RT_ELEMENTS(g_apIoQueueProviders); i++)
105 {
106 PCRTIOQUEUEPROVVTABLE pIoQueueProv = g_apIoQueueProviders[i];
107 if ( pIoQueueProv->enmHnd == enmHnd
108 && pIoQueueProv->pfnIsSupported())
109 return pIoQueueProv;
110 }
111
112 return NULL;
113}
114
115
116RTDECL(PCRTIOQUEUEPROVVTABLE) RTIoQueueProviderGetById(const char *pszId)
117{
118 for (unsigned i = 0; i < RT_ELEMENTS(g_apIoQueueProviders); i++)
119 {
120 PCRTIOQUEUEPROVVTABLE pIoQueueProv = g_apIoQueueProviders[i];
121 if (!strcmp(pIoQueueProv->pszId, pszId))
122 return pIoQueueProv;
123 }
124
125 return NULL;
126}
127
128
129RTDECL(int) RTIoQueueCreate(PRTIOQUEUE phIoQueue, PCRTIOQUEUEPROVVTABLE pProvVTable,
130 uint32_t fFlags, size_t cSqEntries, size_t cCqEntries)
131{
132 AssertPtrReturn(phIoQueue, VERR_INVALID_POINTER);
133 AssertPtrReturn(pProvVTable, VERR_INVALID_POINTER);
134 AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
135 AssertReturn(cSqEntries > 0, VERR_INVALID_PARAMETER);
136 AssertReturn(cCqEntries > 0, VERR_INVALID_PARAMETER);
137
138 int rc = VINF_SUCCESS;
139 PRTIOQUEUEINT pThis = (PRTIOQUEUEINT)RTMemAllocZ(RT_UOFFSETOF_DYN(RTIOQUEUEINT, abInst[pProvVTable->cbIoQueueProv]));
140 if (RT_LIKELY(pThis))
141 {
142 pThis->pVTbl = pProvVTable;
143 pThis->hIoQueueProv = (RTIOQUEUEPROV)&pThis->abInst[0];
144 pThis->cSqEntries = cSqEntries;
145 pThis->cCqEntries = cCqEntries;
146 pThis->cReqsCommitted = 0;
147 pThis->cReqsPrepared = 0;
148
149 rc = pThis->pVTbl->pfnQueueInit(pThis->hIoQueueProv, fFlags, cSqEntries, cCqEntries);
150 if (RT_SUCCESS(rc))
151 {
152 *phIoQueue = pThis;
153 return VINF_SUCCESS;
154 }
155
156 RTMemFree(pThis);
157 }
158 else
159 rc = VERR_NO_MEMORY;
160
161 return rc;
162}
163
164
165RTDECL(int) RTIoQueueDestroy(RTIOQUEUE hIoQueue)
166{
167 PRTIOQUEUEINT pThis = hIoQueue;
168 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
169 AssertReturn(ASMAtomicReadU32(&pThis->cReqsCommitted) == 0, VERR_IOQUEUE_BUSY);
170
171 pThis->pVTbl->pfnQueueDestroy(pThis->hIoQueueProv);
172 RTMemFree(pThis);
173 return VINF_SUCCESS;
174}
175
176
177RTDECL(int) RTIoQueueHandleRegister(RTIOQUEUE hIoQueue, PCRTHANDLE pHandle)
178{
179 PRTIOQUEUEINT pThis = hIoQueue;
180 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
181
182 /** @todo Efficiently check that handle wasn't registered previously. */
183 return pThis->pVTbl->pfnHandleRegister(pThis->hIoQueueProv, pHandle);
184}
185
186
187RTDECL(int) RTIoQueueHandleDeregister(RTIOQUEUE hIoQueue, PCRTHANDLE pHandle)
188{
189 PRTIOQUEUEINT pThis = hIoQueue;
190 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
191
192 /** @todo Efficiently check that handle was registered previously. */
193 return pThis->pVTbl->pfnHandleDeregister(pThis->hIoQueueProv, pHandle);
194}
195
196
197RTDECL(int) RTIoQueueRequestPrepare(RTIOQUEUE hIoQueue, PCRTHANDLE pHandle, RTIOQUEUEOP enmOp,
198 uint64_t off, void *pvBuf, size_t cbBuf, uint32_t fReqFlags,
199 void *pvUser)
200{
201 PRTIOQUEUEINT pThis = hIoQueue;
202 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
203 AssertReturn(pHandle->enmType == pThis->pVTbl->enmHnd, VERR_INVALID_HANDLE);
204
205 /** @todo Efficiently check that handle was registered previously. */
206 int rc = pThis->pVTbl->pfnReqPrepare(pThis->hIoQueueProv, pHandle, enmOp, off, pvBuf, cbBuf,
207 fReqFlags, pvUser);
208 if (RT_SUCCESS(rc))
209 ASMAtomicIncU32(&pThis->cReqsPrepared);
210
211 return rc;
212}
213
214
215RTDECL(int) RTIoQueueRequestPrepareSg(RTIOQUEUE hIoQueue, PCRTHANDLE pHandle, RTIOQUEUEOP enmOp,
216 uint64_t off, PCRTSGBUF pSgBuf, size_t cbSg, uint32_t fReqFlags,
217 void *pvUser)
218{
219 PRTIOQUEUEINT pThis = hIoQueue;
220 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
221 AssertReturn(pHandle->enmType == pThis->pVTbl->enmHnd, VERR_INVALID_HANDLE);
222
223 /** @todo Efficiently check that handle was registered previously. */
224 int rc = pThis->pVTbl->pfnReqPrepareSg(pThis->hIoQueueProv, pHandle, enmOp, off, pSgBuf, cbSg,
225 fReqFlags, pvUser);
226 if (RT_SUCCESS(rc))
227 ASMAtomicIncU32(&pThis->cReqsPrepared);
228
229 return rc;
230}
231
232
233RTDECL(int) RTIoQueueCommit(RTIOQUEUE hIoQueue)
234{
235 PRTIOQUEUEINT pThis = hIoQueue;
236 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
237 AssertReturn(ASMAtomicReadU32(&pThis->cReqsPrepared) > 0, VERR_IOQUEUE_EMPTY);
238
239 uint32_t cReqsPreparedOld = 0;
240 uint32_t cReqsCommitted = 0;
241 int rc = VINF_SUCCESS;
242 do
243 {
244 rc = pThis->pVTbl->pfnCommit(pThis->hIoQueueProv, &cReqsCommitted);
245 if (RT_SUCCESS(rc))
246 {
247 ASMAtomicAddU32(&pThis->cReqsCommitted, cReqsCommitted);
248 cReqsPreparedOld = ASMAtomicSubU32(&pThis->cReqsPrepared, cReqsCommitted);
249 }
250 } while (RT_SUCCESS(rc) && cReqsPreparedOld - cReqsCommitted > 0);
251
252 return rc;
253}
254
255
256RTDECL(int) RTIoQueueEvtWait(RTIOQUEUE hIoQueue, PRTIOQUEUECEVT paCEvt, uint32_t cCEvt, uint32_t cMinWait,
257 uint32_t *pcCEvt, uint32_t fFlags)
258{
259 PRTIOQUEUEINT pThis = hIoQueue;
260 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
261 AssertPtrReturn(paCEvt, VERR_INVALID_POINTER);
262 AssertReturn(cCEvt > 0, VERR_INVALID_PARAMETER);
263 AssertReturn(cMinWait > 0, VERR_INVALID_PARAMETER);
264 AssertPtrReturn(pcCEvt, VERR_INVALID_POINTER);
265 AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
266 AssertReturn(ASMAtomicReadU32(&pThis->cReqsCommitted) > 0, VERR_IOQUEUE_EMPTY);
267
268 *pcCEvt = 0;
269 int rc = pThis->pVTbl->pfnEvtWait(pThis->hIoQueueProv, paCEvt, cCEvt, cMinWait, pcCEvt, fFlags);
270 if ( (RT_SUCCESS(rc) || rc == VERR_INTERRUPTED)
271 && *pcCEvt > 0)
272 ASMAtomicSubU32(&pThis->cReqsCommitted, *pcCEvt);
273
274 return rc;
275}
276
277
278RTDECL(int) RTIoQueueEvtWaitWakeup(RTIOQUEUE hIoQueue)
279{
280 PRTIOQUEUEINT pThis = hIoQueue;
281 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
282
283 return pThis->pVTbl->pfnEvtWaitWakeup(pThis->hIoQueueProv);
284}
285
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette