VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/solaris/fileaio-solaris.cpp@ 76553

Last change on this file since 76553 was 76553, checked in by vboxsync, 5 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.3 KB
Line 
1/* $Id: fileaio-solaris.cpp 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * IPRT - File async I/O, native implementation for the Solaris host platform.
4 */
5
6/*
7 * Copyright (C) 2006-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. 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_FILE
32#include <iprt/asm.h>
33#include <iprt/file.h>
34#include <iprt/mem.h>
35#include <iprt/assert.h>
36#include <iprt/string.h>
37#include <iprt/err.h>
38#include <iprt/log.h>
39#include "internal/fileaio.h"
40
41#include <port.h>
42#include <aio.h>
43#include <errno.h>
44#include <unistd.h>
45
46
47/*********************************************************************************************************************************
48* Structures and Typedefs *
49*********************************************************************************************************************************/
50/**
51 * Async I/O completion context state.
52 */
53typedef struct RTFILEAIOCTXINTERNAL
54{
55 /** Handle to the port. */
56 int iPort;
57 /** Current number of requests active on this context. */
58 volatile int32_t cRequests;
59 /** Flags given during creation. */
60 uint32_t fFlags;
61 /** Magic value (RTFILEAIOCTX_MAGIC). */
62 uint32_t u32Magic;
63} RTFILEAIOCTXINTERNAL;
64/** Pointer to an internal context structure. */
65typedef RTFILEAIOCTXINTERNAL *PRTFILEAIOCTXINTERNAL;
66
67/**
68 * Async I/O request state.
69 */
70typedef struct RTFILEAIOREQINTERNAL
71{
72 /** The aio control block. Must be the FIRST
73 * element. */
74 struct aiocb AioCB;
75 /** Current state the request is in. */
76 RTFILEAIOREQSTATE enmState;
77 /** Flag whether this is a flush request. */
78 bool fFlush;
79 /** Port notifier object to associate a request to a port. */
80 port_notify_t PortNotifier;
81 /** Opaque user data. */
82 void *pvUser;
83 /** Completion context we are assigned to. */
84 PRTFILEAIOCTXINTERNAL pCtxInt;
85 /** Magic value (RTFILEAIOREQ_MAGIC). */
86 uint32_t u32Magic;
87} RTFILEAIOREQINTERNAL;
88/** Pointer to an internal request structure. */
89typedef RTFILEAIOREQINTERNAL *PRTFILEAIOREQINTERNAL;
90
91
92/*********************************************************************************************************************************
93* Defined Constants And Macros *
94*********************************************************************************************************************************/
95/** The max number of events to get in one call. */
96#define AIO_MAXIMUM_REQUESTS_PER_CONTEXT 64
97/** Id for the wakeup event. */
98#define AIO_CONTEXT_WAKEUP_EVENT 1
99
100RTR3DECL(int) RTFileAioGetLimits(PRTFILEAIOLIMITS pAioLimits)
101{
102 int rcBSD = 0;
103 AssertPtrReturn(pAioLimits, VERR_INVALID_POINTER);
104
105 /* No limits known. */
106 pAioLimits->cReqsOutstandingMax = RTFILEAIO_UNLIMITED_REQS;
107 pAioLimits->cbBufferAlignment = 0;
108
109 return VINF_SUCCESS;
110}
111
112RTR3DECL(int) RTFileAioReqCreate(PRTFILEAIOREQ phReq)
113{
114 AssertPtrReturn(phReq, VERR_INVALID_POINTER);
115
116 PRTFILEAIOREQINTERNAL pReqInt = (PRTFILEAIOREQINTERNAL)RTMemAllocZ(sizeof(RTFILEAIOREQINTERNAL));
117 if (RT_UNLIKELY(!pReqInt))
118 return VERR_NO_MEMORY;
119
120 /* Ininitialize static parts. */
121 pReqInt->AioCB.aio_sigevent.sigev_notify = SIGEV_PORT;
122 pReqInt->AioCB.aio_sigevent.sigev_value.sival_ptr = &pReqInt->PortNotifier;
123 pReqInt->PortNotifier.portnfy_user = pReqInt;
124 pReqInt->pCtxInt = NULL;
125 pReqInt->u32Magic = RTFILEAIOREQ_MAGIC;
126 RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
127
128 *phReq = (RTFILEAIOREQ)pReqInt;
129
130 return VINF_SUCCESS;
131}
132
133RTDECL(int) RTFileAioReqDestroy(RTFILEAIOREQ hReq)
134{
135 /*
136 * Validate the handle and ignore nil.
137 */
138 if (hReq == NIL_RTFILEAIOREQ)
139 return VINF_SUCCESS;
140 PRTFILEAIOREQINTERNAL pReqInt = hReq;
141 RTFILEAIOREQ_VALID_RETURN(pReqInt);
142 RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
143
144 /*
145 * Trash the magic and free it.
146 */
147 ASMAtomicUoWriteU32(&pReqInt->u32Magic, ~RTFILEAIOREQ_MAGIC);
148 RTMemFree(pReqInt);
149 return VINF_SUCCESS;
150}
151
152/**
153 * Worker setting up the request.
154 */
155DECLINLINE(int) rtFileAioReqPrepareTransfer(RTFILEAIOREQ hReq, RTFILE hFile,
156 unsigned uTransferDirection,
157 RTFOFF off, void *pvBuf, size_t cbTransfer,
158 void *pvUser)
159{
160 /*
161 * Validate the input.
162 */
163 PRTFILEAIOREQINTERNAL pReqInt = hReq;
164 RTFILEAIOREQ_VALID_RETURN(pReqInt);
165 RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
166 Assert(hFile != NIL_RTFILE);
167 AssertPtr(pvBuf);
168 Assert(off >= 0);
169 Assert(cbTransfer > 0);
170
171 pReqInt->AioCB.aio_lio_opcode = uTransferDirection;
172 pReqInt->AioCB.aio_fildes = RTFileToNative(hFile);
173 pReqInt->AioCB.aio_offset = off;
174 pReqInt->AioCB.aio_nbytes = cbTransfer;
175 pReqInt->AioCB.aio_buf = pvBuf;
176 pReqInt->fFlush = false;
177 pReqInt->pvUser = pvUser;
178 pReqInt->pCtxInt = NULL;
179 RTFILEAIOREQ_SET_STATE(pReqInt, PREPARED);
180
181 return VINF_SUCCESS;
182}
183
184RTDECL(int) RTFileAioReqPrepareRead(RTFILEAIOREQ hReq, RTFILE hFile, RTFOFF off,
185 void *pvBuf, size_t cbRead, void *pvUser)
186{
187 return rtFileAioReqPrepareTransfer(hReq, hFile, LIO_READ,
188 off, pvBuf, cbRead, pvUser);
189}
190
191RTDECL(int) RTFileAioReqPrepareWrite(RTFILEAIOREQ hReq, RTFILE hFile, RTFOFF off,
192 void const *pvBuf, size_t cbWrite, void *pvUser)
193{
194 return rtFileAioReqPrepareTransfer(hReq, hFile, LIO_WRITE,
195 off, (void *)pvBuf, cbWrite, pvUser);
196}
197
198RTDECL(int) RTFileAioReqPrepareFlush(RTFILEAIOREQ hReq, RTFILE hFile, void *pvUser)
199{
200 PRTFILEAIOREQINTERNAL pReqInt = (PRTFILEAIOREQINTERNAL)hReq;
201
202 RTFILEAIOREQ_VALID_RETURN(pReqInt);
203 RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
204 Assert(hFile != NIL_RTFILE);
205
206 pReqInt->fFlush = true;
207 pReqInt->AioCB.aio_fildes = RTFileToNative(hFile);
208 pReqInt->AioCB.aio_offset = 0;
209 pReqInt->AioCB.aio_nbytes = 0;
210 pReqInt->AioCB.aio_buf = NULL;
211 pReqInt->pvUser = pvUser;
212 RTFILEAIOREQ_SET_STATE(pReqInt, PREPARED);
213
214 return VINF_SUCCESS;
215}
216
217RTDECL(void *) RTFileAioReqGetUser(RTFILEAIOREQ hReq)
218{
219 PRTFILEAIOREQINTERNAL pReqInt = hReq;
220 RTFILEAIOREQ_VALID_RETURN_RC(pReqInt, NULL);
221
222 return pReqInt->pvUser;
223}
224
225RTDECL(int) RTFileAioReqCancel(RTFILEAIOREQ hReq)
226{
227 PRTFILEAIOREQINTERNAL pReqInt = hReq;
228 RTFILEAIOREQ_VALID_RETURN(pReqInt);
229 RTFILEAIOREQ_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_NOT_SUBMITTED);
230
231 int rcSolaris = aio_cancel(pReqInt->AioCB.aio_fildes, &pReqInt->AioCB);
232
233 if (rcSolaris == AIO_CANCELED)
234 {
235 /*
236 * Decrement request count because the request will never arrive at the
237 * completion port.
238 */
239 AssertMsg(VALID_PTR(pReqInt->pCtxInt),
240 ("Invalid state. Request was canceled but wasn't submitted\n"));
241
242 ASMAtomicDecS32(&pReqInt->pCtxInt->cRequests);
243 RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
244 return VINF_SUCCESS;
245 }
246 else if (rcSolaris == AIO_ALLDONE)
247 return VERR_FILE_AIO_COMPLETED;
248 else if (rcSolaris == AIO_NOTCANCELED)
249 return VERR_FILE_AIO_IN_PROGRESS;
250 else
251 return RTErrConvertFromErrno(errno);
252}
253
254RTDECL(int) RTFileAioReqGetRC(RTFILEAIOREQ hReq, size_t *pcbTransfered)
255{
256 PRTFILEAIOREQINTERNAL pReqInt = hReq;
257 RTFILEAIOREQ_VALID_RETURN(pReqInt);
258 RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
259 RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, PREPARED, VERR_FILE_AIO_NOT_SUBMITTED);
260 AssertPtrNull(pcbTransfered);
261
262 int rcSol = aio_error(&pReqInt->AioCB);
263 Assert(rcSol != EINPROGRESS); /* Handled by our own state handling. */
264
265 if (rcSol == 0)
266 {
267 if (pcbTransfered)
268 *pcbTransfered = aio_return(&pReqInt->AioCB);
269 return VINF_SUCCESS;
270 }
271
272 /* An error occurred. */
273 return RTErrConvertFromErrno(rcSol);
274}
275
276RTDECL(int) RTFileAioCtxCreate(PRTFILEAIOCTX phAioCtx, uint32_t cAioReqsMax,
277 uint32_t fFlags)
278{
279 int rc = VINF_SUCCESS;
280 PRTFILEAIOCTXINTERNAL pCtxInt;
281 AssertPtrReturn(phAioCtx, VERR_INVALID_POINTER);
282 AssertReturn(!(fFlags & ~RTFILEAIOCTX_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
283
284 pCtxInt = (PRTFILEAIOCTXINTERNAL)RTMemAllocZ(sizeof(RTFILEAIOCTXINTERNAL));
285 if (RT_UNLIKELY(!pCtxInt))
286 return VERR_NO_MEMORY;
287
288 /* Init the event handle. */
289 pCtxInt->iPort = port_create();
290 if (RT_LIKELY(pCtxInt->iPort > 0))
291 {
292 pCtxInt->fFlags = fFlags;
293 pCtxInt->u32Magic = RTFILEAIOCTX_MAGIC;
294 *phAioCtx = (RTFILEAIOCTX)pCtxInt;
295 }
296 else
297 {
298 RTMemFree(pCtxInt);
299 rc = RTErrConvertFromErrno(errno);
300 }
301
302 return rc;
303}
304
305RTDECL(int) RTFileAioCtxDestroy(RTFILEAIOCTX hAioCtx)
306{
307 /* Validate the handle and ignore nil. */
308 if (hAioCtx == NIL_RTFILEAIOCTX)
309 return VINF_SUCCESS;
310 PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
311 RTFILEAIOCTX_VALID_RETURN(pCtxInt);
312
313 /* Cannot destroy a busy context. */
314 if (RT_UNLIKELY(pCtxInt->cRequests))
315 return VERR_FILE_AIO_BUSY;
316
317 close(pCtxInt->iPort);
318 ASMAtomicUoWriteU32(&pCtxInt->u32Magic, RTFILEAIOCTX_MAGIC_DEAD);
319 RTMemFree(pCtxInt);
320
321 return VINF_SUCCESS;
322}
323
324RTDECL(uint32_t) RTFileAioCtxGetMaxReqCount(RTFILEAIOCTX hAioCtx)
325{
326 return RTFILEAIO_UNLIMITED_REQS;
327}
328
329RTDECL(int) RTFileAioCtxAssociateWithFile(RTFILEAIOCTX hAioCtx, RTFILE hFile)
330{
331 return VINF_SUCCESS;
332}
333
334RTDECL(int) RTFileAioCtxSubmit(RTFILEAIOCTX hAioCtx, PRTFILEAIOREQ pahReqs, size_t cReqs)
335{
336 /*
337 * Parameter validation.
338 */
339 int rc = VINF_SUCCESS;
340 PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
341 RTFILEAIOCTX_VALID_RETURN(pCtxInt);
342 AssertReturn(cReqs > 0, VERR_INVALID_PARAMETER);
343 AssertPtrReturn(pahReqs, VERR_INVALID_POINTER);
344 size_t i = cReqs;
345
346 do
347 {
348 int rcSol = 0;
349 size_t cReqsSubmit = 0;
350 PRTFILEAIOREQINTERNAL pReqInt;
351
352 while(i-- > 0)
353 {
354 pReqInt = pahReqs[i];
355 if (RTFILEAIOREQ_IS_NOT_VALID(pReqInt))
356 {
357 /* Undo everything and stop submitting. */
358 for (size_t iUndo = 0; iUndo < i; iUndo++)
359 {
360 pReqInt = pahReqs[iUndo];
361 RTFILEAIOREQ_SET_STATE(pReqInt, PREPARED);
362 pReqInt->pCtxInt = NULL;
363 }
364 rc = VERR_INVALID_HANDLE;
365 break;
366 }
367
368 pReqInt->PortNotifier.portnfy_port = pCtxInt->iPort;
369 pReqInt->pCtxInt = pCtxInt;
370 RTFILEAIOREQ_SET_STATE(pReqInt, SUBMITTED);
371
372 if (pReqInt->fFlush)
373 break;
374
375 cReqsSubmit++;
376 }
377
378 if (cReqsSubmit)
379 {
380 rcSol = lio_listio(LIO_NOWAIT, (struct aiocb **)pahReqs, cReqsSubmit, NULL);
381 if (RT_UNLIKELY(rcSol < 0))
382 {
383 if (rcSol == EAGAIN)
384 rc = VERR_FILE_AIO_INSUFFICIENT_RESSOURCES;
385 else
386 rc = RTErrConvertFromErrno(errno);
387
388 /* Check which requests got actually submitted and which not. */
389 for (i = 0; i < cReqs; i++)
390 {
391 pReqInt = pahReqs[i];
392 rcSol = aio_error(&pReqInt->AioCB);
393 if (rcSol == EINVAL)
394 {
395 /* Was not submitted. */
396 RTFILEAIOREQ_SET_STATE(pReqInt, PREPARED);
397 pReqInt->pCtxInt = NULL;
398 }
399 else if (rcSol != EINPROGRESS)
400 {
401 /* The request encountered an error. */
402 RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
403 }
404 }
405 break;
406 }
407
408 ASMAtomicAddS32(&pCtxInt->cRequests, cReqsSubmit);
409 cReqs -= cReqsSubmit;
410 pahReqs += cReqsSubmit;
411 }
412
413 if (cReqs)
414 {
415 pReqInt = pahReqs[0];
416 RTFILEAIOREQ_VALID_RETURN(pReqInt);
417
418 /*
419 * If there are still requests left we have a flush request.
420 * lio_listio does not work with this requests so
421 * we have to use aio_fsync directly.
422 */
423 rcSol = aio_fsync(O_SYNC, &pReqInt->AioCB);
424 if (RT_UNLIKELY(rcSol < 0))
425 {
426 RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
427 rc = RTErrConvertFromErrno(errno);
428 break;
429 }
430
431 ASMAtomicIncS32(&pCtxInt->cRequests);
432 cReqs--;
433 pahReqs++;
434 }
435 } while (cReqs);
436
437 return rc;
438}
439
440RTDECL(int) RTFileAioCtxWait(RTFILEAIOCTX hAioCtx, size_t cMinReqs, RTMSINTERVAL cMillies,
441 PRTFILEAIOREQ pahReqs, size_t cReqs, uint32_t *pcReqs)
442{
443 int rc = VINF_SUCCESS;
444 int cRequestsCompleted = 0;
445
446 /*
447 * Validate the parameters, making sure to always set pcReqs.
448 */
449 AssertPtrReturn(pcReqs, VERR_INVALID_POINTER);
450 *pcReqs = 0; /* always set */
451 PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
452 RTFILEAIOCTX_VALID_RETURN(pCtxInt);
453 AssertPtrReturn(pahReqs, VERR_INVALID_POINTER);
454 AssertReturn(cReqs != 0, VERR_INVALID_PARAMETER);
455 AssertReturn(cReqs >= cMinReqs, VERR_OUT_OF_RANGE);
456
457 if ( RT_UNLIKELY(ASMAtomicReadS32(&pCtxInt->cRequests) == 0)
458 && !(pCtxInt->fFlags & RTFILEAIOCTX_FLAGS_WAIT_WITHOUT_PENDING_REQUESTS))
459 return VERR_FILE_AIO_NO_REQUEST;
460
461 /*
462 * Convert the timeout if specified.
463 */
464 struct timespec *pTimeout = NULL;
465 struct timespec Timeout = {0,0};
466 uint64_t StartNanoTS = 0;
467 if (cMillies != RT_INDEFINITE_WAIT)
468 {
469 Timeout.tv_sec = cMillies / 1000;
470 Timeout.tv_nsec = cMillies % 1000 * 1000000;
471 pTimeout = &Timeout;
472 StartNanoTS = RTTimeNanoTS();
473 }
474
475 /* Wait for at least one. */
476 if (!cMinReqs)
477 cMinReqs = 1;
478
479 while ( cMinReqs
480 && RT_SUCCESS_NP(rc))
481 {
482 port_event_t aPortEvents[AIO_MAXIMUM_REQUESTS_PER_CONTEXT];
483 uint_t cRequests = cMinReqs;
484 int cRequestsToWait = RT_MIN(cReqs, AIO_MAXIMUM_REQUESTS_PER_CONTEXT);
485 int rcSol;
486 uint64_t StartTime;
487
488 rcSol = port_getn(pCtxInt->iPort, &aPortEvents[0], cRequestsToWait, &cRequests, pTimeout);
489
490 if (RT_UNLIKELY(rcSol < 0))
491 rc = RTErrConvertFromErrno(errno);
492
493 /* Process received events. */
494 for (uint_t i = 0; i < cRequests; i++)
495 {
496 if (aPortEvents[i].portev_source == PORT_SOURCE_ALERT)
497 {
498 Assert(aPortEvents[i].portev_events == AIO_CONTEXT_WAKEUP_EVENT);
499 rc = VERR_INTERRUPTED; /* We've got interrupted. */
500 /* Reset the port. */
501 port_alert(pCtxInt->iPort, PORT_ALERT_SET, 0, NULL);
502 }
503 else
504 {
505 PRTFILEAIOREQINTERNAL pReqInt = (PRTFILEAIOREQINTERNAL)aPortEvents[i].portev_user;
506 AssertPtr(pReqInt);
507 Assert(pReqInt->u32Magic == RTFILEAIOREQ_MAGIC);
508
509 /* A request has finished. */
510 pahReqs[cRequestsCompleted++] = pReqInt;
511
512 /* Mark the request as finished. */
513 RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
514 }
515 }
516
517 /*
518 * Done Yet? If not advance and try again.
519 */
520 if (cRequests >= cMinReqs)
521 break;
522 cMinReqs -= cRequests;
523 cReqs -= cRequests;
524
525 if (cMillies != RT_INDEFINITE_WAIT)
526 {
527 uint64_t NanoTS = RTTimeNanoTS();
528 uint64_t cMilliesElapsed = (NanoTS - StartNanoTS) / 1000000;
529
530 /* The syscall supposedly updates it, but we're paranoid. :-) */
531 if (cMilliesElapsed < cMillies)
532 {
533 Timeout.tv_sec = (cMillies - (RTMSINTERVAL)cMilliesElapsed) / 1000;
534 Timeout.tv_nsec = (cMillies - (RTMSINTERVAL)cMilliesElapsed) % 1000 * 1000000;
535 }
536 else
537 {
538 Timeout.tv_sec = 0;
539 Timeout.tv_nsec = 0;
540 }
541 }
542 }
543
544 /*
545 * Update the context state and set the return value.
546 */
547 *pcReqs = cRequestsCompleted;
548 ASMAtomicSubS32(&pCtxInt->cRequests, cRequestsCompleted);
549
550 return rc;
551}
552
553RTDECL(int) RTFileAioCtxWakeup(RTFILEAIOCTX hAioCtx)
554{
555 int rc = VINF_SUCCESS;
556 PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
557 RTFILEAIOCTX_VALID_RETURN(pCtxInt);
558
559 rc = port_alert(pCtxInt->iPort, PORT_ALERT_UPDATE, AIO_CONTEXT_WAKEUP_EVENT, NULL);
560 if (RT_UNLIKELY((rc < 0) && (errno != EBUSY)))
561 return RTErrConvertFromErrno(errno);
562
563 return VINF_SUCCESS;
564}
565
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use