VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/ioqueue/ioqueue-aiofile-provider.cpp

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.7 KB
Line 
1/* $Id: ioqueue-aiofile-provider.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * IPRT - I/O queue, Async I/O file provider.
4 */
5
6/*
7 * Copyright (C) 2019-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#define LOG_GROUP RTLOGGROUP_IOQUEUE
42#include <iprt/ioqueue.h>
43
44#include <iprt/asm.h>
45#include <iprt/errcore.h>
46#include <iprt/file.h>
47#include <iprt/log.h>
48#include <iprt/mem.h>
49#include <iprt/semaphore.h>
50#include <iprt/string.h>
51
52#include "internal/ioqueue.h"
53
54
55/*********************************************************************************************************************************
56* Structures and Typedefs *
57*********************************************************************************************************************************/
58
59
60/**
61 * Internal I/O queue provider instance data.
62 */
63typedef struct RTIOQUEUEPROVINT
64{
65 /** The async I/O context handle. */
66 RTFILEAIOCTX hAioCtx;
67 /** Pointer to the array of requests waiting for commit. */
68 PRTFILEAIOREQ pahReqsToCommit;
69 /** Maximum number of requests to wait for commit.. */
70 size_t cReqsToCommitMax;
71 /** Number of requests waiting for commit. */
72 uint32_t cReqsToCommit;
73 /** Array of free cached request handles. */
74 PRTFILEAIOREQ pahReqsFree;
75 /** Maximum number of cached requests. */
76 uint32_t cReqsFreeMax;
77 /** Number of free cached requests. */
78 volatile uint32_t cReqsFree;
79} RTIOQUEUEPROVINT;
80/** Pointer to the internal I/O queue provider instance data. */
81typedef RTIOQUEUEPROVINT *PRTIOQUEUEPROVINT;
82
83
84/*********************************************************************************************************************************
85* Internal Functions *
86*********************************************************************************************************************************/
87
88
89/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnIsSupported} */
90static DECLCALLBACK(bool) rtIoQueueAioFileProv_IsSupported(void)
91{
92 /* The common code/public API already checked for the proper handle type. */
93 /** @todo Check that the file was opened with async I/O enabled on some platforms? */
94 return true;
95}
96
97
98/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnQueueInit} */
99static DECLCALLBACK(int) rtIoQueueAioFileProv_QueueInit(RTIOQUEUEPROV hIoQueueProv, uint32_t fFlags,
100 uint32_t cSqEntries, uint32_t cCqEntries)
101{
102 RT_NOREF(fFlags, cCqEntries);
103
104 PRTIOQUEUEPROVINT pThis = hIoQueueProv;
105 int rc = VINF_SUCCESS;
106
107 pThis->cReqsToCommitMax = cSqEntries;
108 pThis->cReqsFreeMax = cSqEntries;
109 pThis->cReqsFree = 0;
110
111 pThis->pahReqsToCommit = (PRTFILEAIOREQ)RTMemAllocZ(cSqEntries * sizeof(PRTFILEAIOREQ));
112 if (RT_LIKELY(pThis->pahReqsToCommit))
113 {
114 pThis->pahReqsFree = (PRTFILEAIOREQ)RTMemAllocZ(cSqEntries * sizeof(PRTFILEAIOREQ));
115 if (RT_LIKELY(pThis->pahReqsFree))
116 {
117 rc = RTFileAioCtxCreate(&pThis->hAioCtx, cSqEntries, RTFILEAIOCTX_FLAGS_WAIT_WITHOUT_PENDING_REQUESTS);
118 if (RT_SUCCESS(rc))
119 return VINF_SUCCESS;
120
121 RTMemFree(pThis->pahReqsFree);
122 }
123 else
124 rc = VERR_NO_MEMORY;
125
126 RTMemFree(pThis->pahReqsToCommit);
127 }
128 else
129 rc = VERR_NO_MEMORY;
130
131 return rc;
132}
133
134
135/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnQueueDestroy} */
136static DECLCALLBACK(void) rtIoQueueAioFileProv_QueueDestroy(RTIOQUEUEPROV hIoQueueProv)
137{
138 PRTIOQUEUEPROVINT pThis = hIoQueueProv;
139
140 RTFileAioCtxDestroy(pThis->hAioCtx);
141
142 while (pThis->cReqsFree--)
143 {
144 RTFILEAIOREQ hReq = pThis->pahReqsFree[pThis->cReqsFree];
145 RTFileAioReqDestroy(hReq);
146 pThis->pahReqsFree[pThis->cReqsFree] = NULL;
147 }
148
149 RTMemFree(pThis->pahReqsFree);
150 RTMemFree(pThis->pahReqsToCommit);
151 RT_BZERO(pThis, sizeof(*pThis));
152}
153
154
155/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnHandleRegister} */
156static DECLCALLBACK(int) rtIoQueueAioFileProv_HandleRegister(RTIOQUEUEPROV hIoQueueProv, PCRTHANDLE pHandle)
157{
158 PRTIOQUEUEPROVINT pThis = hIoQueueProv;
159
160 return RTFileAioCtxAssociateWithFile(pThis->hAioCtx, pHandle->u.hFile);
161}
162
163
164/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnHandleDeregister} */
165static DECLCALLBACK(int) rtIoQueueAioFileProv_HandleDeregister(RTIOQUEUEPROV hIoQueueProv, PCRTHANDLE pHandle)
166{
167 RT_NOREF(hIoQueueProv, pHandle);
168
169 /** @todo For Windows there doesn't seem to be a way to deregister the file handle without reopening the file,
170 *.for all other hosts this is a nop, just like the register method.
171 */
172 return VINF_SUCCESS;
173}
174
175
176/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnReqPrepare} */
177static DECLCALLBACK(int) rtIoQueueAioFileProv_ReqPrepare(RTIOQUEUEPROV hIoQueueProv, PCRTHANDLE pHandle, RTIOQUEUEOP enmOp,
178 uint64_t off, void *pvBuf, size_t cbBuf, uint32_t fReqFlags,
179 void *pvUser)
180{
181 RT_NOREF(fReqFlags);
182
183 PRTIOQUEUEPROVINT pThis = hIoQueueProv;
184
185 /* Try to grab a free request structure from the cache. */
186 RTFILEAIOREQ hReq = NIL_RTFILEAIOREQ;
187 int rc = VINF_SUCCESS;
188 uint32_t cReqsFree = ASMAtomicReadU32(&pThis->cReqsFree);
189 if (cReqsFree)
190 {
191 do
192 {
193 cReqsFree = ASMAtomicReadU32(&pThis->cReqsFree);
194 hReq = pThis->pahReqsFree[pThis->cReqsFree - 1];
195 } while (!ASMAtomicCmpXchgU32(&pThis->cReqsFree, cReqsFree - 1, cReqsFree));
196 }
197 else
198 rc = RTFileAioReqCreate(&hReq);
199
200 if (RT_SUCCESS(rc))
201 {
202 switch (enmOp)
203 {
204 case RTIOQUEUEOP_READ:
205 rc = RTFileAioReqPrepareRead(hReq, pHandle->u.hFile, (RTFOFF)off, pvBuf, cbBuf, pvUser);
206 break;
207 case RTIOQUEUEOP_WRITE:
208 rc = RTFileAioReqPrepareWrite(hReq, pHandle->u.hFile, (RTFOFF)off, pvBuf, cbBuf, pvUser);
209 break;
210 case RTIOQUEUEOP_SYNC:
211 rc = RTFileAioReqPrepareFlush(hReq, pHandle->u.hFile, pvUser);
212 break;
213 default:
214 AssertMsgFailedReturn(("Invalid I/O queue operation: %d\n", enmOp), VERR_INTERNAL_ERROR);
215 }
216
217 if (RT_SUCCESS(rc))
218 pThis->pahReqsToCommit[pThis->cReqsToCommit++] = hReq;
219 else
220 {
221 int rc2 = RTFileAioReqDestroy(hReq);
222 Assert(rc2); RT_NOREF(rc2);
223 }
224 }
225
226 return rc;
227}
228
229
230/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnCommit} */
231static DECLCALLBACK(int) rtIoQueueAioFileProv_Commit(RTIOQUEUEPROV hIoQueueProv, uint32_t *pcReqsCommitted)
232{
233 PRTIOQUEUEPROVINT pThis = hIoQueueProv;
234
235 int rc = RTFileAioCtxSubmit(pThis->hAioCtx, pThis->pahReqsToCommit, pThis->cReqsToCommit);
236 if (RT_SUCCESS(rc))
237 {
238 *pcReqsCommitted = pThis->cReqsToCommit;
239 pThis->cReqsToCommit = 0;
240 }
241
242 return rc;
243}
244
245
246/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnEvtWait} */
247static DECLCALLBACK(int) rtIoQueueAioFileProv_EvtWait(RTIOQUEUEPROV hIoQueueProv, PRTIOQUEUECEVT paCEvt, uint32_t cCEvt,
248 uint32_t cMinWait, uint32_t *pcCEvt, uint32_t fFlags)
249{
250 RT_NOREF(fFlags);
251
252 PRTIOQUEUEPROVINT pThis = hIoQueueProv;
253 int rc = VINF_SUCCESS;
254 uint32_t idxCEvt = 0;
255
256 while ( RT_SUCCESS(rc)
257 && cMinWait
258 && cCEvt)
259 {
260 RTFILEAIOREQ ahReqs[64];
261 uint32_t cReqsCompleted = 0;
262
263 rc = RTFileAioCtxWait(pThis->hAioCtx, cMinWait, RT_INDEFINITE_WAIT,
264 &ahReqs[0], RT_MIN(RT_ELEMENTS(ahReqs), cCEvt), &cReqsCompleted);
265 if (RT_SUCCESS(rc))
266 {
267 for (unsigned i = 0; i < cReqsCompleted; i++)
268 {
269 RTFILEAIOREQ hReq = ahReqs[i];
270
271 paCEvt[idxCEvt].rcReq = RTFileAioReqGetRC(hReq, &paCEvt[idxCEvt].cbXfered);
272 paCEvt[idxCEvt].pvUser = RTFileAioReqGetUser(hReq);
273 idxCEvt++;
274
275 /* Try to insert the free request into the cache. */
276 uint32_t cReqsFree = ASMAtomicReadU32(&pThis->cReqsFree);
277 if (cReqsFree < pThis->cReqsFreeMax)
278 {
279 do
280 {
281 cReqsFree = ASMAtomicReadU32(&pThis->cReqsFree);
282 pThis->pahReqsFree[pThis->cReqsFree] = hReq;
283 } while (!ASMAtomicCmpXchgU32(&pThis->cReqsFree, cReqsFree + 1, cReqsFree));
284 }
285 else
286 rc = RTFileAioReqDestroy(hReq);
287 }
288
289 cCEvt -= cReqsCompleted;
290 cMinWait -= RT_MIN(cMinWait, cReqsCompleted);
291 }
292 }
293
294 *pcCEvt = idxCEvt;
295 return rc;
296}
297
298
299/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnEvtWaitWakeup} */
300static DECLCALLBACK(int) rtIoQueueAioFileProv_EvtWaitWakeup(RTIOQUEUEPROV hIoQueueProv)
301{
302 PRTIOQUEUEPROVINT pThis = hIoQueueProv;
303
304 return RTFileAioCtxWakeup(pThis->hAioCtx);
305}
306
307
308/**
309 * Async file I/O queue provider virtual method table.
310 */
311RT_DECL_DATA_CONST(RTIOQUEUEPROVVTABLE const) g_RTIoQueueAioFileProv =
312{
313 /** uVersion */
314 RTIOQUEUEPROVVTABLE_VERSION,
315 /** pszId */
316 "AioFile",
317 /** cbIoQueueProv */
318 sizeof(RTIOQUEUEPROVINT),
319 /** enmHnd */
320 RTHANDLETYPE_FILE,
321 /** fFlags */
322 0,
323 /** pfnIsSupported */
324 rtIoQueueAioFileProv_IsSupported,
325 /** pfnQueueInit */
326 rtIoQueueAioFileProv_QueueInit,
327 /** pfnQueueDestroy */
328 rtIoQueueAioFileProv_QueueDestroy,
329 /** pfnHandleRegister */
330 rtIoQueueAioFileProv_HandleRegister,
331 /** pfnHandleDeregister */
332 rtIoQueueAioFileProv_HandleDeregister,
333 /** pfnReqPrepare */
334 rtIoQueueAioFileProv_ReqPrepare,
335 /** pfnReqPrepareSg */
336 NULL,
337 /** pfnCommit */
338 rtIoQueueAioFileProv_Commit,
339 /** pfnEvtWait */
340 rtIoQueueAioFileProv_EvtWait,
341 /** pfnEvtWaitWakeup */
342 rtIoQueueAioFileProv_EvtWaitWakeup,
343 /** uEndMarker */
344 RTIOQUEUEPROVVTABLE_VERSION
345};
346
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use