VirtualBox

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

Last change on this file since 34949 was 30238, checked in by vboxsync, 15 years ago

fileaio-solaris.cpp: Variable mixup. Fixes hang during async I/O

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