VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/linux/fileaio-linux.cpp@ 32292

Last change on this file since 32292 was 29111, checked in by vboxsync, 15 years ago

IPRT/fileaio-linux: Support flushes, not supported by any filesystem yet (returns an error)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 25.7 KB
Line 
1/* $Id: fileaio-linux.cpp 29111 2010-05-05 20:28:30Z vboxsync $ */
2/** @file
3 * IPRT - File async I/O, native implementation for the Linux host platform.
4 */
5
6/*
7 * Copyright (C) 2006-2007 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/** @page pg_rtfileaio_linux RTFile Async I/O - Linux Implementation Notes
28 * @internal
29 *
30 * Linux implements the kernel async I/O API through the io_* syscalls. They are
31 * not exposed in the glibc (the aio_* API uses userspace threads and blocking
32 * I/O operations to simulate async behavior). There is an external library
33 * called libaio which implements these syscalls but because we don't want to
34 * have another dependency and this library is not installed by default and the
35 * interface is really simple we use the kernel interface directly using wrapper
36 * functions.
37 *
38 * The interface has some limitations. The first one is that the file must be
39 * opened with O_DIRECT. This disables caching done by the kernel which can be
40 * compensated if the user of this API implements caching itself. The next
41 * limitation is that data buffers must be aligned at a 512 byte boundary or the
42 * request will fail.
43 */
44/** @todo r=bird: What's this about "must be opened with O_DIRECT"? An
45 * explanation would be nice, esp. seeing what Linus is quoted saying
46 * about it in the open man page... */
47
48/*******************************************************************************
49* Header Files *
50*******************************************************************************/
51#define LOG_GROUP RTLOGGROUP_FILE
52#include <iprt/asm.h>
53#include <iprt/mem.h>
54#include <iprt/assert.h>
55#include <iprt/string.h>
56#include <iprt/err.h>
57#include <iprt/log.h>
58#include <iprt/thread.h>
59#include "internal/fileaio.h"
60
61#include <unistd.h>
62#include <sys/syscall.h>
63#include <errno.h>
64
65#include <iprt/file.h>
66
67
68/*******************************************************************************
69* Structures and Typedefs *
70*******************************************************************************/
71/** The async I/O context handle */
72typedef unsigned long LNXKAIOCONTEXT;
73
74/**
75 * Supported commands for the iocbs
76 */
77enum
78{
79 LNXKAIO_IOCB_CMD_READ = 0,
80 LNXKAIO_IOCB_CMD_WRITE = 1,
81 LNXKAIO_IOCB_CMD_FSYNC = 2,
82 LNXKAIO_IOCB_CMD_FDSYNC = 3
83};
84
85/**
86 * The iocb structure of a request which is passed to the kernel.
87 *
88 * We redefined this here because the version in the header lacks padding
89 * for 32bit.
90 */
91typedef struct LNXKAIOIOCB
92{
93 /** Opaque pointer to data which is returned on an I/O event. */
94 void *pvUser;
95#ifdef RT_ARCH_X86
96 uint32_t u32Padding0;
97#endif
98 /** Contains the request number and is set by the kernel. */
99 uint32_t u32Key;
100 /** Reserved. */
101 uint32_t u32Reserved0;
102 /** The I/O opcode. */
103 uint16_t u16IoOpCode;
104 /** Request priority. */
105 int16_t i16Priority;
106 /** The file descriptor. */
107 uint32_t File;
108 /** The userspace pointer to the buffer containing/receiving the data. */
109 void *pvBuf;
110#ifdef RT_ARCH_X86
111 uint32_t u32Padding1;
112#endif
113 /** How many bytes to transfer. */
114#ifdef RT_ARCH_X86
115 uint32_t cbTransfer;
116 uint32_t u32Padding2;
117#elif defined(RT_ARCH_AMD64)
118 uint64_t cbTransfer;
119#else
120# error "Unknown architecture"
121#endif
122 /** At which offset to start the transfer. */
123 int64_t off;
124 /** Reserved. */
125 uint64_t u64Reserved1;
126 /** Flags */
127 uint32_t fFlags;
128 /** Readyness signal file descriptor. */
129 uint32_t u32ResFd;
130} LNXKAIOIOCB, *PLNXKAIOIOCB;
131
132/**
133 * I/O event structure to notify about completed requests.
134 * Redefined here too because of the padding.
135 */
136typedef struct LNXKAIOIOEVENT
137{
138 /** The pvUser field from the iocb. */
139 void *pvUser;
140#ifdef RT_ARCH_X86
141 uint32_t u32Padding0;
142#endif
143 /** The LNXKAIOIOCB object this event is for. */
144 PLNXKAIOIOCB *pIoCB;
145#ifdef RT_ARCH_X86
146 uint32_t u32Padding1;
147#endif
148 /** The result code of the operation .*/
149#ifdef RT_ARCH_X86
150 int32_t rc;
151 uint32_t u32Padding2;
152#elif defined(RT_ARCH_AMD64)
153 int64_t rc;
154#else
155# error "Unknown architecture"
156#endif
157 /** Secondary result code. */
158#ifdef RT_ARCH_X86
159 int32_t rc2;
160 uint32_t u32Padding3;
161#elif defined(RT_ARCH_AMD64)
162 int64_t rc2;
163#else
164# error "Unknown architecture"
165#endif
166} LNXKAIOIOEVENT, *PLNXKAIOIOEVENT;
167
168
169/**
170 * Async I/O completion context state.
171 */
172typedef struct RTFILEAIOCTXINTERNAL
173{
174 /** Handle to the async I/O context. */
175 LNXKAIOCONTEXT AioContext;
176 /** Maximum number of requests this context can handle. */
177 int cRequestsMax;
178 /** Current number of requests active on this context. */
179 volatile int32_t cRequests;
180 /** The ID of the thread which is currently waiting for requests. */
181 volatile RTTHREAD hThreadWait;
182 /** Flag whether the thread was woken up. */
183 volatile bool fWokenUp;
184 /** Flag whether the thread is currently waiting in the syscall. */
185 volatile bool fWaiting;
186 /** Magic value (RTFILEAIOCTX_MAGIC). */
187 uint32_t u32Magic;
188} RTFILEAIOCTXINTERNAL;
189/** Pointer to an internal context structure. */
190typedef RTFILEAIOCTXINTERNAL *PRTFILEAIOCTXINTERNAL;
191
192/**
193 * Async I/O request state.
194 */
195typedef struct RTFILEAIOREQINTERNAL
196{
197 /** The aio control block. This must be the FIRST elment in
198 * the structure! (see notes below) */
199 LNXKAIOIOCB AioCB;
200 /** Current state the request is in. */
201 RTFILEAIOREQSTATE enmState;
202 /** The I/O context this request is associated with. */
203 LNXKAIOCONTEXT AioContext;
204 /** Return code the request completed with. */
205 int Rc;
206 /** Number of bytes actually trasnfered. */
207 size_t cbTransfered;
208 /** Completion context we are assigned to. */
209 PRTFILEAIOCTXINTERNAL pCtxInt;
210 /** Magic value (RTFILEAIOREQ_MAGIC). */
211 uint32_t u32Magic;
212} RTFILEAIOREQINTERNAL;
213/** Pointer to an internal request structure. */
214typedef RTFILEAIOREQINTERNAL *PRTFILEAIOREQINTERNAL;
215
216
217/*******************************************************************************
218* Defined Constants And Macros *
219*******************************************************************************/
220/** The max number of events to get in one call. */
221#define AIO_MAXIMUM_REQUESTS_PER_CONTEXT 64
222
223
224/**
225 * Creates a new async I/O context.
226 */
227DECLINLINE(int) rtFileAsyncIoLinuxCreate(unsigned cEvents, LNXKAIOCONTEXT *pAioContext)
228{
229 int rc = syscall(__NR_io_setup, cEvents, pAioContext);
230 if (RT_UNLIKELY(rc == -1))
231 return RTErrConvertFromErrno(errno);
232
233 return VINF_SUCCESS;
234}
235
236/**
237 * Destroys a async I/O context.
238 */
239DECLINLINE(int) rtFileAsyncIoLinuxDestroy(LNXKAIOCONTEXT AioContext)
240{
241 int rc = syscall(__NR_io_destroy, AioContext);
242 if (RT_UNLIKELY(rc == -1))
243 return RTErrConvertFromErrno(errno);
244
245 return VINF_SUCCESS;
246}
247
248/**
249 * Submits an array of I/O requests to the kernel.
250 */
251DECLINLINE(int) rtFileAsyncIoLinuxSubmit(LNXKAIOCONTEXT AioContext, long cReqs, LNXKAIOIOCB **ppIoCB, int *pcSubmitted)
252{
253 int rc = syscall(__NR_io_submit, AioContext, cReqs, ppIoCB);
254 if (RT_UNLIKELY(rc == -1))
255 return RTErrConvertFromErrno(errno);
256
257 *pcSubmitted = rc;
258
259 return VINF_SUCCESS;
260}
261
262/**
263 * Cancels a I/O request.
264 */
265DECLINLINE(int) rtFileAsyncIoLinuxCancel(LNXKAIOCONTEXT AioContext, PLNXKAIOIOCB pIoCB, PLNXKAIOIOEVENT pIoResult)
266{
267 int rc = syscall(__NR_io_cancel, AioContext, pIoCB, pIoResult);
268 if (RT_UNLIKELY(rc == -1))
269 return RTErrConvertFromErrno(errno);
270
271 return VINF_SUCCESS;
272}
273
274/**
275 * Waits for I/O events.
276 * @returns Number of events (natural number w/ 0), IPRT error code (negative).
277 */
278DECLINLINE(int) rtFileAsyncIoLinuxGetEvents(LNXKAIOCONTEXT AioContext, long cReqsMin, long cReqs,
279 PLNXKAIOIOEVENT paIoResults, struct timespec *pTimeout)
280{
281 int rc = syscall(__NR_io_getevents, AioContext, cReqsMin, cReqs, paIoResults, pTimeout);
282 if (RT_UNLIKELY(rc == -1))
283 return RTErrConvertFromErrno(errno);
284
285 return rc;
286}
287
288RTR3DECL(int) RTFileAioGetLimits(PRTFILEAIOLIMITS pAioLimits)
289{
290 int rc = VINF_SUCCESS;
291 AssertPtrReturn(pAioLimits, VERR_INVALID_POINTER);
292
293 /*
294 * Check if the API is implemented by creating a
295 * completion port.
296 */
297 LNXKAIOCONTEXT AioContext = 0;
298 rc = rtFileAsyncIoLinuxCreate(1, &AioContext);
299 if (RT_FAILURE(rc))
300 return rc;
301
302 rc = rtFileAsyncIoLinuxDestroy(AioContext);
303 if (RT_FAILURE(rc))
304 return rc;
305
306 /* Supported - fill in the limits. The alignment is the only restriction. */
307 pAioLimits->cReqsOutstandingMax = RTFILEAIO_UNLIMITED_REQS;
308 pAioLimits->cbBufferAlignment = 512;
309
310 return VINF_SUCCESS;
311}
312
313
314RTR3DECL(int) RTFileAioReqCreate(PRTFILEAIOREQ phReq)
315{
316 AssertPtrReturn(phReq, VERR_INVALID_POINTER);
317
318 /*
319 * Allocate a new request and initialize it.
320 */
321 PRTFILEAIOREQINTERNAL pReqInt = (PRTFILEAIOREQINTERNAL)RTMemAllocZ(sizeof(*pReqInt));
322 if (RT_UNLIKELY(!pReqInt))
323 return VERR_NO_MEMORY;
324
325 pReqInt->pCtxInt = NULL;
326 pReqInt->u32Magic = RTFILEAIOREQ_MAGIC;
327 RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
328
329 *phReq = (RTFILEAIOREQ)pReqInt;
330 return VINF_SUCCESS;
331}
332
333
334RTDECL(int) RTFileAioReqDestroy(RTFILEAIOREQ hReq)
335{
336 /*
337 * Validate the handle and ignore nil.
338 */
339 if (hReq == NIL_RTFILEAIOREQ)
340 return VINF_SUCCESS;
341 PRTFILEAIOREQINTERNAL pReqInt = hReq;
342 RTFILEAIOREQ_VALID_RETURN(pReqInt);
343 RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
344
345 /*
346 * Trash the magic and free it.
347 */
348 ASMAtomicUoWriteU32(&pReqInt->u32Magic, ~RTFILEAIOREQ_MAGIC);
349 RTMemFree(pReqInt);
350 return VINF_SUCCESS;
351}
352
353
354/**
355 * Worker setting up the request.
356 */
357DECLINLINE(int) rtFileAioReqPrepareTransfer(RTFILEAIOREQ hReq, RTFILE hFile,
358 uint16_t uTransferDirection,
359 RTFOFF off, void *pvBuf, size_t cbTransfer,
360 void *pvUser)
361{
362 /*
363 * Validate the input.
364 */
365 PRTFILEAIOREQINTERNAL pReqInt = hReq;
366 RTFILEAIOREQ_VALID_RETURN(pReqInt);
367 RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
368 Assert(hFile != NIL_RTFILE);
369
370 if (uTransferDirection != LNXKAIO_IOCB_CMD_FSYNC)
371 {
372 AssertPtr(pvBuf);
373 Assert(off >= 0);
374 Assert(cbTransfer > 0);
375 }
376
377 /*
378 * Setup the control block and clear the finished flag.
379 */
380 pReqInt->AioCB.u16IoOpCode = uTransferDirection;
381 pReqInt->AioCB.File = (uint32_t)hFile;
382 pReqInt->AioCB.off = off;
383 pReqInt->AioCB.cbTransfer = cbTransfer;
384 pReqInt->AioCB.pvBuf = pvBuf;
385 pReqInt->AioCB.pvUser = pvUser;
386
387 pReqInt->pCtxInt = NULL;
388 RTFILEAIOREQ_SET_STATE(pReqInt, PREPARED);
389
390 return VINF_SUCCESS;
391}
392
393
394RTDECL(int) RTFileAioReqPrepareRead(RTFILEAIOREQ hReq, RTFILE hFile, RTFOFF off,
395 void *pvBuf, size_t cbRead, void *pvUser)
396{
397 return rtFileAioReqPrepareTransfer(hReq, hFile, LNXKAIO_IOCB_CMD_READ,
398 off, pvBuf, cbRead, pvUser);
399}
400
401
402RTDECL(int) RTFileAioReqPrepareWrite(RTFILEAIOREQ hReq, RTFILE hFile, RTFOFF off,
403 void const *pvBuf, size_t cbWrite, void *pvUser)
404{
405 return rtFileAioReqPrepareTransfer(hReq, hFile, LNXKAIO_IOCB_CMD_WRITE,
406 off, (void *)pvBuf, cbWrite, pvUser);
407}
408
409
410RTDECL(int) RTFileAioReqPrepareFlush(RTFILEAIOREQ hReq, RTFILE hFile, void *pvUser)
411{
412 PRTFILEAIOREQINTERNAL pReqInt = hReq;
413 RTFILEAIOREQ_VALID_RETURN(pReqInt);
414 AssertReturn(hFile != NIL_RTFILE, VERR_INVALID_HANDLE);
415 RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
416
417 return rtFileAioReqPrepareTransfer(pReqInt, hFile, LNXKAIO_IOCB_CMD_FSYNC,
418 0, NULL, 0, pvUser);
419}
420
421
422RTDECL(void *) RTFileAioReqGetUser(RTFILEAIOREQ hReq)
423{
424 PRTFILEAIOREQINTERNAL pReqInt = hReq;
425 RTFILEAIOREQ_VALID_RETURN_RC(pReqInt, NULL);
426
427 return pReqInt->AioCB.pvUser;
428}
429
430
431RTDECL(int) RTFileAioReqCancel(RTFILEAIOREQ hReq)
432{
433 PRTFILEAIOREQINTERNAL pReqInt = hReq;
434 RTFILEAIOREQ_VALID_RETURN(pReqInt);
435 RTFILEAIOREQ_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_NOT_SUBMITTED);
436
437 LNXKAIOIOEVENT AioEvent;
438 int rc = rtFileAsyncIoLinuxCancel(pReqInt->AioContext, &pReqInt->AioCB, &AioEvent);
439 if (RT_SUCCESS(rc))
440 {
441 /*
442 * Decrement request count because the request will never arrive at the
443 * completion port.
444 */
445 AssertMsg(VALID_PTR(pReqInt->pCtxInt),
446 ("Invalid state. Request was canceled but wasn't submitted\n"));
447
448 ASMAtomicDecS32(&pReqInt->pCtxInt->cRequests);
449 pReqInt->Rc = VERR_FILE_AIO_CANCELED;
450 RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
451 return VINF_SUCCESS;
452 }
453 if (rc == VERR_TRY_AGAIN)
454 return VERR_FILE_AIO_IN_PROGRESS;
455 return rc;
456}
457
458
459RTDECL(int) RTFileAioReqGetRC(RTFILEAIOREQ hReq, size_t *pcbTransfered)
460{
461 PRTFILEAIOREQINTERNAL pReqInt = hReq;
462 RTFILEAIOREQ_VALID_RETURN(pReqInt);
463 AssertPtrNull(pcbTransfered);
464 RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
465 RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, PREPARED, VERR_FILE_AIO_NOT_SUBMITTED);
466
467 if ( pcbTransfered
468 && RT_SUCCESS(pReqInt->Rc))
469 *pcbTransfered = pReqInt->cbTransfered;
470
471 return pReqInt->Rc;
472}
473
474
475RTDECL(int) RTFileAioCtxCreate(PRTFILEAIOCTX phAioCtx, uint32_t cAioReqsMax)
476{
477 PRTFILEAIOCTXINTERNAL pCtxInt;
478 AssertPtrReturn(phAioCtx, VERR_INVALID_POINTER);
479
480 /* The kernel interface needs a maximum. */
481 if (cAioReqsMax == RTFILEAIO_UNLIMITED_REQS)
482 return VERR_OUT_OF_RANGE;
483
484 pCtxInt = (PRTFILEAIOCTXINTERNAL)RTMemAllocZ(sizeof(RTFILEAIOCTXINTERNAL));
485 if (RT_UNLIKELY(!pCtxInt))
486 return VERR_NO_MEMORY;
487
488 /* Init the event handle. */
489 int rc = rtFileAsyncIoLinuxCreate(cAioReqsMax, &pCtxInt->AioContext);
490 if (RT_SUCCESS(rc))
491 {
492 pCtxInt->fWokenUp = false;
493 pCtxInt->fWaiting = false;
494 pCtxInt->hThreadWait = NIL_RTTHREAD;
495 pCtxInt->cRequestsMax = cAioReqsMax;
496 pCtxInt->u32Magic = RTFILEAIOCTX_MAGIC;
497 *phAioCtx = (RTFILEAIOCTX)pCtxInt;
498 }
499 else
500 RTMemFree(pCtxInt);
501
502 return rc;
503}
504
505
506RTDECL(int) RTFileAioCtxDestroy(RTFILEAIOCTX hAioCtx)
507{
508 /* Validate the handle and ignore nil. */
509 if (hAioCtx == NIL_RTFILEAIOCTX)
510 return VINF_SUCCESS;
511 PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
512 RTFILEAIOCTX_VALID_RETURN(pCtxInt);
513
514 /* Cannot destroy a busy context. */
515 if (RT_UNLIKELY(pCtxInt->cRequests))
516 return VERR_FILE_AIO_BUSY;
517
518 /* The native bit first, then mark it as dead and free it. */
519 int rc = rtFileAsyncIoLinuxDestroy(pCtxInt->AioContext);
520 if (RT_FAILURE(rc))
521 return rc;
522 ASMAtomicUoWriteU32(&pCtxInt->u32Magic, RTFILEAIOCTX_MAGIC_DEAD);
523 RTMemFree(pCtxInt);
524
525 return VINF_SUCCESS;
526}
527
528
529RTDECL(uint32_t) RTFileAioCtxGetMaxReqCount(RTFILEAIOCTX hAioCtx)
530{
531 /* Nil means global here. */
532 if (hAioCtx == NIL_RTFILEAIOCTX)
533 return RTFILEAIO_UNLIMITED_REQS; /** @todo r=bird: I'm a bit puzzled by this return value since it
534 * is completely useless in RTFileAioCtxCreate. */
535
536 /* Return 0 if the handle is invalid, it's better than garbage I think... */
537 PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
538 RTFILEAIOCTX_VALID_RETURN_RC(pCtxInt, 0);
539
540 return pCtxInt->cRequestsMax;
541}
542
543RTDECL(int) RTFileAioCtxAssociateWithFile(RTFILEAIOCTX hAioCtx, RTFILE hFile)
544{
545 /* Nothing to do. */
546 return VINF_SUCCESS;
547}
548
549RTDECL(int) RTFileAioCtxSubmit(RTFILEAIOCTX hAioCtx, PRTFILEAIOREQ pahReqs, size_t cReqs)
550{
551 int rc = VINF_SUCCESS;
552
553 /*
554 * Parameter validation.
555 */
556 PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
557 RTFILEAIOCTX_VALID_RETURN(pCtxInt);
558 AssertReturn(cReqs > 0, VERR_INVALID_PARAMETER);
559 AssertPtrReturn(pahReqs, VERR_INVALID_POINTER);
560 uint32_t i = cReqs;
561 PRTFILEAIOREQINTERNAL pReqInt = NULL;
562
563 /*
564 * Vaildate requests and associate with the context.
565 */
566 while (i-- > 0)
567 {
568 pReqInt = pahReqs[i];
569 if (RTFILEAIOREQ_IS_NOT_VALID(pReqInt))
570 {
571 /* Undo everything and stop submitting. */
572 size_t iUndo = cReqs;
573 while (iUndo-- > i)
574 {
575 pReqInt = pahReqs[iUndo];
576 RTFILEAIOREQ_SET_STATE(pReqInt, PREPARED);
577 pReqInt->pCtxInt = NULL;
578 }
579 return VERR_INVALID_HANDLE;
580 }
581
582 pReqInt->AioContext = pCtxInt->AioContext;
583 pReqInt->pCtxInt = pCtxInt;
584 RTFILEAIOREQ_SET_STATE(pReqInt, SUBMITTED);
585 }
586
587 do
588 {
589 /*
590 * We cast pahReqs to the Linux iocb structure to avoid copying the requests
591 * into a temporary array. This is possible because the iocb structure is
592 * the first element in the request structure (see PRTFILEAIOCTXINTERNAL).
593 */
594 int cReqsSubmitted = 0;
595 rc = rtFileAsyncIoLinuxSubmit(pCtxInt->AioContext, cReqs,
596 (PLNXKAIOIOCB *)pahReqs,
597 &cReqsSubmitted);
598 if (RT_FAILURE(rc))
599 {
600 /*
601 * We encountered an error.
602 * This means that the first IoCB
603 * is not correctly initialized
604 * (invalid buffer alignment or bad file descriptor).
605 * Revert every request into the prepared state except
606 * the first one which will switch to completed.
607 * Another reason could be insuffidient ressources.
608 */
609 i = cReqs;
610 while (i-- > 0)
611 {
612 /* Already validated. */
613 pReqInt = pahReqs[i];
614 pReqInt->pCtxInt = NULL;
615 pReqInt->AioContext = 0;
616 RTFILEAIOREQ_SET_STATE(pReqInt, PREPARED);
617 }
618
619 if (rc == VERR_TRY_AGAIN)
620 return VERR_FILE_AIO_INSUFFICIENT_RESSOURCES;
621 else
622 {
623 /* The first request failed. */
624 pReqInt = pahReqs[0];
625 RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
626 pReqInt->Rc = rc;
627 pReqInt->cbTransfered = 0;
628 return rc;
629 }
630 }
631
632 /* Advance. */
633 cReqs -= cReqsSubmitted;
634 pahReqs += cReqsSubmitted;
635 ASMAtomicAddS32(&pCtxInt->cRequests, cReqsSubmitted);
636
637 } while (cReqs);
638
639 return rc;
640}
641
642
643RTDECL(int) RTFileAioCtxWait(RTFILEAIOCTX hAioCtx, size_t cMinReqs, RTMSINTERVAL cMillies,
644 PRTFILEAIOREQ pahReqs, size_t cReqs, uint32_t *pcReqs)
645{
646 /*
647 * Validate the parameters, making sure to always set pcReqs.
648 */
649 AssertPtrReturn(pcReqs, VERR_INVALID_POINTER);
650 *pcReqs = 0; /* always set */
651 PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
652 RTFILEAIOCTX_VALID_RETURN(pCtxInt);
653 AssertPtrReturn(pahReqs, VERR_INVALID_POINTER);
654 AssertReturn(cReqs != 0, VERR_INVALID_PARAMETER);
655 AssertReturn(cReqs >= cMinReqs, VERR_OUT_OF_RANGE);
656
657 /*
658 * Can't wait if there are not requests around.
659 */
660 if (RT_UNLIKELY(ASMAtomicUoReadS32(&pCtxInt->cRequests) == 0))
661 return VERR_FILE_AIO_NO_REQUEST;
662
663 /*
664 * Convert the timeout if specified.
665 */
666 struct timespec *pTimeout = NULL;
667 struct timespec Timeout = {0,0};
668 uint64_t StartNanoTS = 0;
669 if (cMillies != RT_INDEFINITE_WAIT)
670 {
671 Timeout.tv_sec = cMillies / 1000;
672 Timeout.tv_nsec = cMillies % 1000 * 1000000;
673 pTimeout = &Timeout;
674 StartNanoTS = RTTimeNanoTS();
675 }
676
677 /* Wait for at least one. */
678 if (!cMinReqs)
679 cMinReqs = 1;
680
681 /* For the wakeup call. */
682 Assert(pCtxInt->hThreadWait == NIL_RTTHREAD);
683 ASMAtomicWriteHandle(&pCtxInt->hThreadWait, RTThreadSelf());
684
685 /*
686 * Loop until we're woken up, hit an error (incl timeout), or
687 * have collected the desired number of requests.
688 */
689 int rc = VINF_SUCCESS;
690 int cRequestsCompleted = 0;
691 while (!pCtxInt->fWokenUp)
692 {
693 LNXKAIOIOEVENT aPortEvents[AIO_MAXIMUM_REQUESTS_PER_CONTEXT];
694 int cRequestsToWait = RT_MIN(cReqs, AIO_MAXIMUM_REQUESTS_PER_CONTEXT);
695 ASMAtomicXchgBool(&pCtxInt->fWaiting, true);
696 rc = rtFileAsyncIoLinuxGetEvents(pCtxInt->AioContext, cMinReqs, cRequestsToWait, &aPortEvents[0], pTimeout);
697 ASMAtomicXchgBool(&pCtxInt->fWaiting, false);
698 if (RT_FAILURE(rc))
699 break;
700 uint32_t const cDone = rc;
701 rc = VINF_SUCCESS;
702
703 /*
704 * Process received events / requests.
705 */
706 for (uint32_t i = 0; i < cDone; i++)
707 {
708 /*
709 * The iocb is the first element in our request structure.
710 * So we can safely cast it directly to the handle (see above)
711 */
712 PRTFILEAIOREQINTERNAL pReqInt = (PRTFILEAIOREQINTERNAL)aPortEvents[i].pIoCB;
713 AssertPtr(pReqInt);
714 Assert(pReqInt->u32Magic == RTFILEAIOREQ_MAGIC);
715
716 /** @todo aeichner: The rc field contains the result code
717 * like you can find in errno for the normal read/write ops.
718 * But there is a second field called rc2. I don't know the
719 * purpose for it yet.
720 */
721 if (RT_UNLIKELY(aPortEvents[i].rc < 0))
722 pReqInt->Rc = RTErrConvertFromErrno(-aPortEvents[i].rc); /* Convert to positive value. */
723 else
724 {
725 pReqInt->Rc = VINF_SUCCESS;
726 pReqInt->cbTransfered = aPortEvents[i].rc;
727 }
728
729 /* Mark the request as finished. */
730 RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
731
732 pahReqs[cRequestsCompleted++] = (RTFILEAIOREQ)pReqInt;
733 }
734
735 /*
736 * Done Yet? If not advance and try again.
737 */
738 if (cDone >= cMinReqs)
739 break;
740 cMinReqs -= cDone;
741 cReqs -= cDone;
742
743 if (cMillies != RT_INDEFINITE_WAIT)
744 {
745 /* The API doesn't return ETIMEDOUT, so we have to fix that ourselves. */
746 uint64_t NanoTS = RTTimeNanoTS();
747 uint64_t cMilliesElapsed = (NanoTS - StartNanoTS) / 1000000;
748 if (cMilliesElapsed >= cMillies)
749 {
750 rc = VERR_TIMEOUT;
751 break;
752 }
753
754 /* The syscall supposedly updates it, but we're paranoid. :-) */
755 Timeout.tv_sec = (cMillies - (RTMSINTERVAL)cMilliesElapsed) / 1000;
756 Timeout.tv_nsec = (cMillies - (RTMSINTERVAL)cMilliesElapsed) % 1000 * 1000000;
757 }
758 }
759
760 /*
761 * Update the context state and set the return value.
762 */
763 *pcReqs = cRequestsCompleted;
764 ASMAtomicSubS32(&pCtxInt->cRequests, cRequestsCompleted);
765 Assert(pCtxInt->hThreadWait == RTThreadSelf());
766 ASMAtomicWriteHandle(&pCtxInt->hThreadWait, NIL_RTTHREAD);
767
768 /*
769 * Clear the wakeup flag and set rc.
770 */
771 if ( pCtxInt->fWokenUp
772 && RT_SUCCESS(rc))
773 {
774 ASMAtomicXchgBool(&pCtxInt->fWokenUp, false);
775 rc = VERR_INTERRUPTED;
776 }
777
778 return rc;
779}
780
781
782RTDECL(int) RTFileAioCtxWakeup(RTFILEAIOCTX hAioCtx)
783{
784 PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
785 RTFILEAIOCTX_VALID_RETURN(pCtxInt);
786
787 /** @todo r=bird: Define the protocol for how to resume work after calling
788 * this function. */
789
790 bool fWokenUp = ASMAtomicXchgBool(&pCtxInt->fWokenUp, true);
791
792 /*
793 * Read the thread handle before the status flag.
794 * If we read the handle after the flag we might
795 * end up with an invalid handle because the thread
796 * waiting in RTFileAioCtxWakeup() might get scheduled
797 * before we read the flag and returns.
798 * We can ensure that the handle is valid if fWaiting is true
799 * when reading the handle before the status flag.
800 */
801 RTTHREAD hThread;
802 ASMAtomicReadHandle(&pCtxInt->hThreadWait, &hThread);
803 bool fWaiting = ASMAtomicReadBool(&pCtxInt->fWaiting);
804 if ( !fWokenUp
805 && fWaiting)
806 {
807 /*
808 * If a thread waits the handle must be valid.
809 * It is possible that the thread returns from
810 * rtFileAsyncIoLinuxGetEvents() before the signal
811 * is send.
812 * This is no problem because we already set fWokenUp
813 * to true which will let the thread return VERR_INTERRUPTED
814 * and the next call to RTFileAioCtxWait() will not
815 * return VERR_INTERRUPTED because signals are not saved
816 * and will simply vanish if the destination thread can't
817 * receive it.
818 */
819 Assert(hThread != NIL_RTTHREAD);
820 RTThreadPoke(hThread);
821 }
822
823 return VINF_SUCCESS;
824}
825
Note: See TracBrowser for help on using the repository browser.

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