VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/PDMAsyncCompletionFileNormal.cpp@ 39944

Last change on this file since 39944 was 39078, checked in by vboxsync, 13 years ago

VMM: -Wunused-parameter

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 64.4 KB
Line 
1/* $Id: PDMAsyncCompletionFileNormal.cpp 39078 2011-10-21 14:18:22Z vboxsync $ */
2/** @file
3 * PDM Async I/O - Async File I/O manager.
4 */
5
6/*
7 * Copyright (C) 2006-2011 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
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#define LOG_GROUP LOG_GROUP_PDM_ASYNC_COMPLETION
22#define RT_STRICT
23#include <iprt/types.h>
24#include <iprt/asm.h>
25#include <iprt/file.h>
26#include <iprt/mem.h>
27#include <iprt/string.h>
28#include <iprt/assert.h>
29#include <VBox/log.h>
30
31#include "PDMAsyncCompletionFileInternal.h"
32
33/** The update period for the I/O load statistics in ms. */
34#define PDMACEPFILEMGR_LOAD_UPDATE_PERIOD 1000
35/** Maximum number of requests a manager will handle. */
36#define PDMACEPFILEMGR_REQS_STEP 512
37
38
39/*******************************************************************************
40* Internal functions *
41*******************************************************************************/
42static int pdmacFileAioMgrNormalProcessTaskList(PPDMACTASKFILE pTaskHead,
43 PPDMACEPFILEMGR pAioMgr,
44 PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint);
45
46static PPDMACTASKFILE pdmacFileAioMgrNormalRangeLockFree(PPDMACEPFILEMGR pAioMgr,
47 PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint,
48 PPDMACFILERANGELOCK pRangeLock);
49
50static void pdmacFileAioMgrNormalReqCompleteRc(PPDMACEPFILEMGR pAioMgr, RTFILEAIOREQ hReq,
51 int rc, size_t cbTransfered);
52
53
54int pdmacFileAioMgrNormalInit(PPDMACEPFILEMGR pAioMgr)
55{
56 pAioMgr->cRequestsActiveMax = PDMACEPFILEMGR_REQS_STEP;
57
58 int rc = RTFileAioCtxCreate(&pAioMgr->hAioCtx, RTFILEAIO_UNLIMITED_REQS);
59 if (rc == VERR_OUT_OF_RANGE)
60 rc = RTFileAioCtxCreate(&pAioMgr->hAioCtx, pAioMgr->cRequestsActiveMax);
61
62 if (RT_SUCCESS(rc))
63 {
64 /* Initialize request handle array. */
65 pAioMgr->iFreeEntry = 0;
66 pAioMgr->cReqEntries = pAioMgr->cRequestsActiveMax;
67 pAioMgr->pahReqsFree = (RTFILEAIOREQ *)RTMemAllocZ(pAioMgr->cReqEntries * sizeof(RTFILEAIOREQ));
68
69 if (pAioMgr->pahReqsFree)
70 {
71 /* Create the range lock memcache. */
72 rc = RTMemCacheCreate(&pAioMgr->hMemCacheRangeLocks, sizeof(PDMACFILERANGELOCK),
73 0, UINT32_MAX, NULL, NULL, NULL, 0);
74 if (RT_SUCCESS(rc))
75 return VINF_SUCCESS;
76
77 RTMemFree(pAioMgr->pahReqsFree);
78 }
79 else
80 {
81 RTFileAioCtxDestroy(pAioMgr->hAioCtx);
82 rc = VERR_NO_MEMORY;
83 }
84 }
85
86 return rc;
87}
88
89void pdmacFileAioMgrNormalDestroy(PPDMACEPFILEMGR pAioMgr)
90{
91 RTFileAioCtxDestroy(pAioMgr->hAioCtx);
92
93 while (pAioMgr->iFreeEntry > 0)
94 {
95 pAioMgr->iFreeEntry--;
96 Assert(pAioMgr->pahReqsFree[pAioMgr->iFreeEntry] != NIL_RTFILEAIOREQ);
97 RTFileAioReqDestroy(pAioMgr->pahReqsFree[pAioMgr->iFreeEntry]);
98 }
99
100 RTMemFree(pAioMgr->pahReqsFree);
101 RTMemCacheDestroy(pAioMgr->hMemCacheRangeLocks);
102}
103
104#if 0 /* currently unused */
105/**
106 * Sorts the endpoint list with insertion sort.
107 */
108static void pdmacFileAioMgrNormalEndpointsSortByLoad(PPDMACEPFILEMGR pAioMgr)
109{
110 PPDMASYNCCOMPLETIONENDPOINTFILE pEpPrev, pEpCurr, pEpNextToSort;
111
112 pEpPrev = pAioMgr->pEndpointsHead;
113 pEpCurr = pEpPrev->AioMgr.pEndpointNext;
114
115 while (pEpCurr)
116 {
117 /* Remember the next element to sort because the list might change. */
118 pEpNextToSort = pEpCurr->AioMgr.pEndpointNext;
119
120 /* Unlink the current element from the list. */
121 PPDMASYNCCOMPLETIONENDPOINTFILE pPrev = pEpCurr->AioMgr.pEndpointPrev;
122 PPDMASYNCCOMPLETIONENDPOINTFILE pNext = pEpCurr->AioMgr.pEndpointNext;
123
124 if (pPrev)
125 pPrev->AioMgr.pEndpointNext = pNext;
126 else
127 pAioMgr->pEndpointsHead = pNext;
128
129 if (pNext)
130 pNext->AioMgr.pEndpointPrev = pPrev;
131
132 /* Go back until we reached the place to insert the current endpoint into. */
133 while (pEpPrev && (pEpPrev->AioMgr.cReqsPerSec < pEpCurr->AioMgr.cReqsPerSec))
134 pEpPrev = pEpPrev->AioMgr.pEndpointPrev;
135
136 /* Link the endpoint into the list. */
137 if (pEpPrev)
138 pNext = pEpPrev->AioMgr.pEndpointNext;
139 else
140 pNext = pAioMgr->pEndpointsHead;
141
142 pEpCurr->AioMgr.pEndpointNext = pNext;
143 pEpCurr->AioMgr.pEndpointPrev = pEpPrev;
144
145 if (pNext)
146 pNext->AioMgr.pEndpointPrev = pEpCurr;
147
148 if (pEpPrev)
149 pEpPrev->AioMgr.pEndpointNext = pEpCurr;
150 else
151 pAioMgr->pEndpointsHead = pEpCurr;
152
153 pEpCurr = pEpNextToSort;
154 }
155
156#ifdef DEBUG
157 /* Validate sorting algorithm */
158 unsigned cEndpoints = 0;
159 pEpCurr = pAioMgr->pEndpointsHead;
160
161 AssertMsg(pEpCurr, ("No endpoint in the list?\n"));
162 AssertMsg(!pEpCurr->AioMgr.pEndpointPrev, ("First element in the list points to previous element\n"));
163
164 while (pEpCurr)
165 {
166 cEndpoints++;
167
168 PPDMASYNCCOMPLETIONENDPOINTFILE pNext = pEpCurr->AioMgr.pEndpointNext;
169 PPDMASYNCCOMPLETIONENDPOINTFILE pPrev = pEpCurr->AioMgr.pEndpointPrev;
170
171 Assert(!pNext || pNext->AioMgr.cReqsPerSec <= pEpCurr->AioMgr.cReqsPerSec);
172 Assert(!pPrev || pPrev->AioMgr.cReqsPerSec >= pEpCurr->AioMgr.cReqsPerSec);
173
174 pEpCurr = pNext;
175 }
176
177 AssertMsg(cEndpoints == pAioMgr->cEndpoints, ("Endpoints lost during sort!\n"));
178
179#endif
180}
181#endif /* currently unused */
182
183/**
184 * Removes an endpoint from the currently assigned manager.
185 *
186 * @returns TRUE if there are still requests pending on the current manager for this endpoint.
187 * FALSE otherwise.
188 * @param pEndpointRemove The endpoint to remove.
189 */
190static bool pdmacFileAioMgrNormalRemoveEndpoint(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpointRemove)
191{
192 PPDMASYNCCOMPLETIONENDPOINTFILE pPrev = pEndpointRemove->AioMgr.pEndpointPrev;
193 PPDMASYNCCOMPLETIONENDPOINTFILE pNext = pEndpointRemove->AioMgr.pEndpointNext;
194 PPDMACEPFILEMGR pAioMgr = pEndpointRemove->pAioMgr;
195
196 pAioMgr->cEndpoints--;
197
198 if (pPrev)
199 pPrev->AioMgr.pEndpointNext = pNext;
200 else
201 pAioMgr->pEndpointsHead = pNext;
202
203 if (pNext)
204 pNext->AioMgr.pEndpointPrev = pPrev;
205
206 /* Make sure that there is no request pending on this manager for the endpoint. */
207 if (!pEndpointRemove->AioMgr.cRequestsActive)
208 {
209 Assert(!pEndpointRemove->pFlushReq);
210
211 /* Reopen the file so that the new endpoint can re-associate with the file */
212 RTFileClose(pEndpointRemove->hFile);
213 int rc = RTFileOpen(&pEndpointRemove->hFile, pEndpointRemove->Core.pszUri, pEndpointRemove->fFlags);
214 AssertRC(rc);
215 return false;
216 }
217
218 return true;
219}
220
221#if 0 /* currently unused */
222
223static bool pdmacFileAioMgrNormalIsBalancePossible(PPDMACEPFILEMGR pAioMgr)
224{
225 /* Balancing doesn't make sense with only one endpoint. */
226 if (pAioMgr->cEndpoints == 1)
227 return false;
228
229 /* Doesn't make sens to move endpoints if only one produces the whole load */
230 unsigned cEndpointsWithLoad = 0;
231
232 PPDMASYNCCOMPLETIONENDPOINTFILE pCurr = pAioMgr->pEndpointsHead;
233
234 while (pCurr)
235 {
236 if (pCurr->AioMgr.cReqsPerSec)
237 cEndpointsWithLoad++;
238
239 pCurr = pCurr->AioMgr.pEndpointNext;
240 }
241
242 return (cEndpointsWithLoad > 1);
243}
244
245/**
246 * Creates a new I/O manager and spreads the I/O load of the endpoints
247 * between the given I/O manager and the new one.
248 *
249 * @returns nothing.
250 * @param pAioMgr The I/O manager with high I/O load.
251 */
252static void pdmacFileAioMgrNormalBalanceLoad(PPDMACEPFILEMGR pAioMgr)
253{
254 /*
255 * Check if balancing would improve the situation.
256 */
257 if (pdmacFileAioMgrNormalIsBalancePossible(pAioMgr))
258 {
259 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pAioMgr->pEndpointsHead->Core.pEpClass;
260 PPDMACEPFILEMGR pAioMgrNew = NULL;
261
262 int rc = pdmacFileAioMgrCreate(pEpClassFile, &pAioMgrNew, PDMACEPFILEMGRTYPE_ASYNC);
263 if (RT_SUCCESS(rc))
264 {
265 /* We will sort the list by request count per second. */
266 pdmacFileAioMgrNormalEndpointsSortByLoad(pAioMgr);
267
268 /* Now move some endpoints to the new manager. */
269 unsigned cReqsHere = pAioMgr->pEndpointsHead->AioMgr.cReqsPerSec;
270 unsigned cReqsOther = 0;
271 PPDMASYNCCOMPLETIONENDPOINTFILE pCurr = pAioMgr->pEndpointsHead->AioMgr.pEndpointNext;
272
273 while (pCurr)
274 {
275 if (cReqsHere <= cReqsOther)
276 {
277 /*
278 * The other manager has more requests to handle now.
279 * We will keep the current endpoint.
280 */
281 Log(("Keeping endpoint %#p{%s} with %u reqs/s\n", pCurr->Core.pszUri, pCurr->AioMgr.cReqsPerSec));
282 cReqsHere += pCurr->AioMgr.cReqsPerSec;
283 pCurr = pCurr->AioMgr.pEndpointNext;
284 }
285 else
286 {
287 /* Move to other endpoint. */
288 Log(("Moving endpoint %#p{%s} with %u reqs/s to other manager\n", pCurr, pCurr->Core.pszUri, pCurr->AioMgr.cReqsPerSec));
289 cReqsOther += pCurr->AioMgr.cReqsPerSec;
290
291 PPDMASYNCCOMPLETIONENDPOINTFILE pMove = pCurr;
292
293 pCurr = pCurr->AioMgr.pEndpointNext;
294
295 bool fReqsPending = pdmacFileAioMgrNormalRemoveEndpoint(pMove);
296
297 if (fReqsPending)
298 {
299 pMove->enmState = PDMASYNCCOMPLETIONENDPOINTFILESTATE_REMOVING;
300 pMove->AioMgr.fMoving = true;
301 pMove->AioMgr.pAioMgrDst = pAioMgrNew;
302 }
303 else
304 {
305 pMove->AioMgr.fMoving = false;
306 pMove->AioMgr.pAioMgrDst = NULL;
307 pdmacFileAioMgrAddEndpoint(pAioMgrNew, pMove);
308 }
309 }
310 }
311 }
312 else
313 {
314 /* Don't process further but leave a log entry about reduced performance. */
315 LogRel(("AIOMgr: Could not create new I/O manager (rc=%Rrc). Expect reduced performance\n", rc));
316 }
317 }
318 else
319 Log(("AIOMgr: Load balancing would not improve anything\n"));
320}
321
322#endif /* unused */
323
324/**
325 * Increase the maximum number of active requests for the given I/O manager.
326 *
327 * @returns VBox status code.
328 * @param pAioMgr The I/O manager to grow.
329 */
330static int pdmacFileAioMgrNormalGrow(PPDMACEPFILEMGR pAioMgr)
331{
332 LogFlowFunc(("pAioMgr=%#p\n", pAioMgr));
333
334 AssertMsg( pAioMgr->enmState == PDMACEPFILEMGRSTATE_GROWING
335 && !pAioMgr->cRequestsActive,
336 ("Invalid state of the I/O manager\n"));
337
338#ifdef RT_OS_WINDOWS
339 /*
340 * Reopen the files of all assigned endpoints first so we can assign them to the new
341 * I/O context.
342 */
343 PPDMASYNCCOMPLETIONENDPOINTFILE pCurr = pAioMgr->pEndpointsHead;
344
345 while (pCurr)
346 {
347 RTFileClose(pCurr->hFile);
348 int rc2 = RTFileOpen(&pCurr->hFile, pCurr->Core.pszUri, pCurr->fFlags); AssertRC(rc2);
349
350 pCurr = pCurr->AioMgr.pEndpointNext;
351 }
352#endif
353
354 /* Create the new bigger context. */
355 pAioMgr->cRequestsActiveMax += PDMACEPFILEMGR_REQS_STEP;
356
357 RTFILEAIOCTX hAioCtxNew = NIL_RTFILEAIOCTX;
358 int rc = RTFileAioCtxCreate(&hAioCtxNew, RTFILEAIO_UNLIMITED_REQS);
359 if (rc == VERR_OUT_OF_RANGE)
360 rc = RTFileAioCtxCreate(&hAioCtxNew, pAioMgr->cRequestsActiveMax);
361
362 if (RT_SUCCESS(rc))
363 {
364 /* Close the old context. */
365 rc = RTFileAioCtxDestroy(pAioMgr->hAioCtx);
366 AssertRC(rc); /** @todo r=bird: Ignoring error code, will propagate. */
367
368 pAioMgr->hAioCtx = hAioCtxNew;
369
370 /* Create a new I/O task handle array */
371 uint32_t cReqEntriesNew = pAioMgr->cRequestsActiveMax + 1;
372 RTFILEAIOREQ *pahReqNew = (RTFILEAIOREQ *)RTMemAllocZ(cReqEntriesNew * sizeof(RTFILEAIOREQ));
373
374 if (pahReqNew)
375 {
376 /* Copy the cached request handles. */
377 for (uint32_t iReq = 0; iReq < pAioMgr->cReqEntries; iReq++)
378 pahReqNew[iReq] = pAioMgr->pahReqsFree[iReq];
379
380 RTMemFree(pAioMgr->pahReqsFree);
381 pAioMgr->pahReqsFree = pahReqNew;
382 pAioMgr->cReqEntries = cReqEntriesNew;
383 LogFlowFunc(("I/O manager increased to handle a maximum of %u requests\n",
384 pAioMgr->cRequestsActiveMax));
385 }
386 else
387 rc = VERR_NO_MEMORY;
388 }
389
390#ifdef RT_OS_WINDOWS
391 /* Assign the file to the new context. */
392 pCurr = pAioMgr->pEndpointsHead;
393 while (pCurr)
394 {
395 rc = RTFileAioCtxAssociateWithFile(pAioMgr->hAioCtx, pCurr->hFile);
396 AssertRC(rc); /** @todo r=bird: Ignoring error code, will propagate. */
397
398 pCurr = pCurr->AioMgr.pEndpointNext;
399 }
400#endif
401
402 if (RT_FAILURE(rc))
403 {
404 LogFlow(("Increasing size of the I/O manager failed with rc=%Rrc\n", rc));
405 pAioMgr->cRequestsActiveMax -= PDMACEPFILEMGR_REQS_STEP;
406 }
407
408 pAioMgr->enmState = PDMACEPFILEMGRSTATE_RUNNING;
409 LogFlowFunc(("returns rc=%Rrc\n", rc));
410
411 return rc;
412}
413
414/**
415 * Checks if a given status code is fatal.
416 * Non fatal errors can be fixed by migrating the endpoint to a
417 * failsafe manager.
418 *
419 * @returns true If the error is fatal and migrating to a failsafe manager doesn't help
420 * false If the error can be fixed by a migration. (image on NFS disk for example)
421 * @param rcReq The status code to check.
422 */
423DECLINLINE(bool) pdmacFileAioMgrNormalRcIsFatal(int rcReq)
424{
425 return rcReq == VERR_DEV_IO_ERROR
426 || rcReq == VERR_FILE_IO_ERROR
427 || rcReq == VERR_DISK_IO_ERROR
428 || rcReq == VERR_DISK_FULL
429 || rcReq == VERR_FILE_TOO_BIG;
430}
431
432/**
433 * Error handler which will create the failsafe managers and destroy the failed I/O manager.
434 *
435 * @returns VBox status code
436 * @param pAioMgr The I/O manager the error occurred on.
437 * @param rc The error code.
438 */
439static int pdmacFileAioMgrNormalErrorHandler(PPDMACEPFILEMGR pAioMgr, int rc, RT_SRC_POS_DECL)
440{
441 LogRel(("AIOMgr: I/O manager %#p encountered a critical error (rc=%Rrc) during operation. Falling back to failsafe mode. Expect reduced performance\n",
442 pAioMgr, rc));
443 LogRel(("AIOMgr: Error happened in %s:(%u){%s}\n", RT_SRC_POS_ARGS));
444 LogRel(("AIOMgr: Please contact the product vendor\n"));
445
446 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pAioMgr->pEndpointsHead->Core.pEpClass;
447
448 pAioMgr->enmState = PDMACEPFILEMGRSTATE_FAULT;
449 ASMAtomicWriteU32((volatile uint32_t *)&pEpClassFile->enmMgrTypeOverride, PDMACEPFILEMGRTYPE_SIMPLE);
450
451 AssertMsgFailed(("Implement\n"));
452 return VINF_SUCCESS;
453}
454
455/**
456 * Put a list of tasks in the pending request list of an endpoint.
457 */
458DECLINLINE(void) pdmacFileAioMgrEpAddTaskList(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint, PPDMACTASKFILE pTaskHead)
459{
460 /* Add the rest of the tasks to the pending list */
461 if (!pEndpoint->AioMgr.pReqsPendingHead)
462 {
463 Assert(!pEndpoint->AioMgr.pReqsPendingTail);
464 pEndpoint->AioMgr.pReqsPendingHead = pTaskHead;
465 }
466 else
467 {
468 Assert(pEndpoint->AioMgr.pReqsPendingTail);
469 pEndpoint->AioMgr.pReqsPendingTail->pNext = pTaskHead;
470 }
471
472 /* Update the tail. */
473 while (pTaskHead->pNext)
474 pTaskHead = pTaskHead->pNext;
475
476 pEndpoint->AioMgr.pReqsPendingTail = pTaskHead;
477 pTaskHead->pNext = NULL;
478}
479
480/**
481 * Put one task in the pending request list of an endpoint.
482 */
483DECLINLINE(void) pdmacFileAioMgrEpAddTask(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint, PPDMACTASKFILE pTask)
484{
485 /* Add the rest of the tasks to the pending list */
486 if (!pEndpoint->AioMgr.pReqsPendingHead)
487 {
488 Assert(!pEndpoint->AioMgr.pReqsPendingTail);
489 pEndpoint->AioMgr.pReqsPendingHead = pTask;
490 }
491 else
492 {
493 Assert(pEndpoint->AioMgr.pReqsPendingTail);
494 pEndpoint->AioMgr.pReqsPendingTail->pNext = pTask;
495 }
496
497 pEndpoint->AioMgr.pReqsPendingTail = pTask;
498 pTask->pNext = NULL;
499}
500
501/**
502 * Allocates a async I/O request.
503 *
504 * @returns Handle to the request.
505 * @param pAioMgr The I/O manager.
506 */
507static RTFILEAIOREQ pdmacFileAioMgrNormalRequestAlloc(PPDMACEPFILEMGR pAioMgr)
508{
509 /* Get a request handle. */
510 RTFILEAIOREQ hReq;
511 if (pAioMgr->iFreeEntry > 0)
512 {
513 pAioMgr->iFreeEntry--;
514 hReq = pAioMgr->pahReqsFree[pAioMgr->iFreeEntry];
515 pAioMgr->pahReqsFree[pAioMgr->iFreeEntry] = NIL_RTFILEAIOREQ;
516 Assert(hReq != NIL_RTFILEAIOREQ);
517 }
518 else
519 {
520 int rc = RTFileAioReqCreate(&hReq);
521 AssertRCReturn(rc, NIL_RTFILEAIOREQ);
522 }
523
524 return hReq;
525}
526
527/**
528 * Frees a async I/O request handle.
529 *
530 * @returns nothing.
531 * @param pAioMgr The I/O manager.
532 * @param hReq The I/O request handle to free.
533 */
534static void pdmacFileAioMgrNormalRequestFree(PPDMACEPFILEMGR pAioMgr, RTFILEAIOREQ hReq)
535{
536 Assert(pAioMgr->iFreeEntry < pAioMgr->cReqEntries);
537 Assert(pAioMgr->pahReqsFree[pAioMgr->iFreeEntry] == NIL_RTFILEAIOREQ);
538
539 pAioMgr->pahReqsFree[pAioMgr->iFreeEntry] = hReq;
540 pAioMgr->iFreeEntry++;
541}
542
543/**
544 * Wrapper around RTFIleAioCtxSubmit() which is also doing error handling.
545 */
546static int pdmacFileAioMgrNormalReqsEnqueue(PPDMACEPFILEMGR pAioMgr,
547 PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint,
548 PRTFILEAIOREQ pahReqs, unsigned cReqs)
549{
550 pAioMgr->cRequestsActive += cReqs;
551 pEndpoint->AioMgr.cRequestsActive += cReqs;
552
553 LogFlow(("Enqueuing %d requests. I/O manager has a total of %d active requests now\n", cReqs, pAioMgr->cRequestsActive));
554 LogFlow(("Endpoint has a total of %d active requests now\n", pEndpoint->AioMgr.cRequestsActive));
555
556 int rc = RTFileAioCtxSubmit(pAioMgr->hAioCtx, pahReqs, cReqs);
557 if (RT_FAILURE(rc))
558 {
559 if (rc == VERR_FILE_AIO_INSUFFICIENT_RESSOURCES)
560 {
561 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClass = (PPDMASYNCCOMPLETIONEPCLASSFILE)pEndpoint->Core.pEpClass;
562
563 /* Append any not submitted task to the waiting list. */
564 for (size_t i = 0; i < cReqs; i++)
565 {
566 int rcReq = RTFileAioReqGetRC(pahReqs[i], NULL);
567
568 if (rcReq != VERR_FILE_AIO_IN_PROGRESS)
569 {
570 PPDMACTASKFILE pTask = (PPDMACTASKFILE)RTFileAioReqGetUser(pahReqs[i]);
571
572 Assert(pTask->hReq == pahReqs[i]);
573 pdmacFileAioMgrEpAddTask(pEndpoint, pTask);
574 pAioMgr->cRequestsActive--;
575 pEndpoint->AioMgr.cRequestsActive--;
576
577 if (pTask->enmTransferType == PDMACTASKFILETRANSFER_FLUSH)
578 {
579 /* Clear the pending flush */
580 Assert(pEndpoint->pFlushReq == pTask);
581 pEndpoint->pFlushReq = NULL;
582 }
583 }
584 }
585
586 pAioMgr->cRequestsActiveMax = pAioMgr->cRequestsActive;
587
588 /* Print an entry in the release log */
589 if (RT_UNLIKELY(!pEpClass->fOutOfResourcesWarningPrinted))
590 {
591 pEpClass->fOutOfResourcesWarningPrinted = true;
592 LogRel(("AIOMgr: Host limits number of active IO requests to %u. Expect a performance impact.\n",
593 pAioMgr->cRequestsActive));
594 }
595
596 LogFlow(("Removed requests. I/O manager has a total of %u active requests now\n", pAioMgr->cRequestsActive));
597 LogFlow(("Endpoint has a total of %u active requests now\n", pEndpoint->AioMgr.cRequestsActive));
598 rc = VINF_SUCCESS;
599 }
600 else /* Another kind of error happened (full disk, ...) */
601 {
602 /* An error happened. Find out which one caused the error and resubmit all other tasks. */
603 for (size_t i = 0; i < cReqs; i++)
604 {
605 int rcReq = RTFileAioReqGetRC(pahReqs[i], NULL);
606
607 if (rcReq == VERR_FILE_AIO_NOT_SUBMITTED)
608 {
609 /* We call ourself again to do any error handling which might come up now. */
610 rc = pdmacFileAioMgrNormalReqsEnqueue(pAioMgr, pEndpoint, &pahReqs[i], 1);
611 AssertRC(rc);
612 }
613 else if (rcReq != VERR_FILE_AIO_IN_PROGRESS)
614 pdmacFileAioMgrNormalReqCompleteRc(pAioMgr, pahReqs[i], rcReq, 0);
615 }
616
617
618 if ( pEndpoint->pFlushReq
619 && !pAioMgr->cRequestsActive
620 && !pEndpoint->fAsyncFlushSupported)
621 {
622 /*
623 * Complete a pending flush if we don't have requests enqueued and the host doesn't support
624 * the async flush API.
625 * Happens only if this we just noticed that this is not supported
626 * and the only active request was a flush.
627 */
628 PPDMACTASKFILE pFlush = pEndpoint->pFlushReq;
629 pEndpoint->pFlushReq = NULL;
630 pFlush->pfnCompleted(pFlush, pFlush->pvUser, VINF_SUCCESS);
631 pdmacFileTaskFree(pEndpoint, pFlush);
632 }
633 }
634 }
635
636 return VINF_SUCCESS;
637}
638
639static bool pdmacFileAioMgrNormalIsRangeLocked(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint,
640 RTFOFF offStart, size_t cbRange,
641 PPDMACTASKFILE pTask)
642{
643 AssertMsg( pTask->enmTransferType == PDMACTASKFILETRANSFER_WRITE
644 || pTask->enmTransferType == PDMACTASKFILETRANSFER_READ,
645 ("Invalid task type %d\n", pTask->enmTransferType));
646
647 PPDMACFILERANGELOCK pRangeLock;
648 pRangeLock = (PPDMACFILERANGELOCK)RTAvlrFileOffsetRangeGet(pEndpoint->AioMgr.pTreeRangesLocked, offStart);
649 if (!pRangeLock)
650 {
651 pRangeLock = (PPDMACFILERANGELOCK)RTAvlrFileOffsetGetBestFit(pEndpoint->AioMgr.pTreeRangesLocked, offStart, true);
652 /* Check if we intersect with the range. */
653 if ( !pRangeLock
654 || !( (pRangeLock->Core.Key) <= (offStart + (RTFOFF)cbRange - 1)
655 && (pRangeLock->Core.KeyLast) >= offStart))
656 {
657 pRangeLock = NULL; /* False alarm */
658 }
659 }
660
661 /* Check whether we have one of the situations explained below */
662 if ( pRangeLock
663#if 0 /** @todo later. For now we will just block all requests if they interfere */
664 && ( (pRangeLock->fReadLock && pTask->enmTransferType == PDMACTASKFILETRANSFER_WRITE)
665 || (!pRangeLock->fReadLock)
666#endif
667 )
668 {
669 /* Add to the list. */
670 pTask->pNext = NULL;
671
672 if (!pRangeLock->pWaitingTasksHead)
673 {
674 Assert(!pRangeLock->pWaitingTasksTail);
675 pRangeLock->pWaitingTasksHead = pTask;
676 pRangeLock->pWaitingTasksTail = pTask;
677 }
678 else
679 {
680 AssertPtr(pRangeLock->pWaitingTasksTail);
681 pRangeLock->pWaitingTasksTail->pNext = pTask;
682 pRangeLock->pWaitingTasksTail = pTask;
683 }
684 return true;
685 }
686
687 return false;
688}
689
690static int pdmacFileAioMgrNormalRangeLock(PPDMACEPFILEMGR pAioMgr,
691 PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint,
692 RTFOFF offStart, size_t cbRange,
693 PPDMACTASKFILE pTask)
694{
695 AssertMsg(!pdmacFileAioMgrNormalIsRangeLocked(pEndpoint, offStart, cbRange, pTask),
696 ("Range is already locked offStart=%RTfoff cbRange=%u\n",
697 offStart, cbRange));
698
699 PPDMACFILERANGELOCK pRangeLock = (PPDMACFILERANGELOCK)RTMemCacheAlloc(pAioMgr->hMemCacheRangeLocks);
700 if (!pRangeLock)
701 return VERR_NO_MEMORY;
702
703 /* Init the lock. */
704 pRangeLock->Core.Key = offStart;
705 pRangeLock->Core.KeyLast = offStart + cbRange - 1;
706 pRangeLock->cRefs = 1;
707 pRangeLock->fReadLock = pTask->enmTransferType == PDMACTASKFILETRANSFER_READ;
708 pRangeLock->pWaitingTasksHead = NULL;
709 pRangeLock->pWaitingTasksTail = NULL;
710
711 bool fInserted = RTAvlrFileOffsetInsert(pEndpoint->AioMgr.pTreeRangesLocked, &pRangeLock->Core);
712 AssertMsg(fInserted, ("Range lock was not inserted!\n"));
713
714 /* Let the task point to its lock. */
715 pTask->pRangeLock = pRangeLock;
716
717 return VINF_SUCCESS;
718}
719
720static PPDMACTASKFILE pdmacFileAioMgrNormalRangeLockFree(PPDMACEPFILEMGR pAioMgr,
721 PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint,
722 PPDMACFILERANGELOCK pRangeLock)
723{
724 PPDMACTASKFILE pTasksWaitingHead;
725
726 AssertPtr(pRangeLock);
727 Assert(pRangeLock->cRefs == 1);
728
729 RTAvlrFileOffsetRemove(pEndpoint->AioMgr.pTreeRangesLocked, pRangeLock->Core.Key);
730 pTasksWaitingHead = pRangeLock->pWaitingTasksHead;
731 pRangeLock->pWaitingTasksHead = NULL;
732 pRangeLock->pWaitingTasksTail = NULL;
733 RTMemCacheFree(pAioMgr->hMemCacheRangeLocks, pRangeLock);
734
735 return pTasksWaitingHead;
736}
737
738static int pdmacFileAioMgrNormalTaskPrepareBuffered(PPDMACEPFILEMGR pAioMgr,
739 PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint,
740 PPDMACTASKFILE pTask, PRTFILEAIOREQ phReq)
741{
742 AssertMsg( pTask->enmTransferType == PDMACTASKFILETRANSFER_WRITE
743 || (uint64_t)(pTask->Off + pTask->DataSeg.cbSeg) <= pEndpoint->cbFile,
744 ("Read exceeds file size offStart=%RTfoff cbToTransfer=%d cbFile=%llu\n",
745 pTask->Off, pTask->DataSeg.cbSeg, pEndpoint->cbFile));
746
747 pTask->fPrefetch = false;
748 pTask->cbBounceBuffer = 0;
749
750 /*
751 * Before we start to setup the request we have to check whether there is a task
752 * already active which range intersects with ours. We have to defer execution
753 * of this task in two cases:
754 * - The pending task is a write and the current is either read or write
755 * - The pending task is a read and the current task is a write task.
756 *
757 * To check whether a range is currently "locked" we use the AVL tree where every pending task
758 * is stored by its file offset range. The current task will be added to the active task
759 * and will be executed when the active one completes. (The method below
760 * which checks whether a range is already used will add the task)
761 *
762 * This is necessary because of the requirement to align all requests to a 512 boundary
763 * which is enforced by the host OS (Linux and Windows atm). It is possible that
764 * we have to process unaligned tasks and need to align them using bounce buffers.
765 * While the data is fetched from the file another request might arrive writing to
766 * the same range. This will result in data corruption if both are executed concurrently.
767 */
768 int rc = VINF_SUCCESS;
769 bool fLocked = pdmacFileAioMgrNormalIsRangeLocked(pEndpoint, pTask->Off, pTask->DataSeg.cbSeg, pTask);
770 if (!fLocked)
771 {
772 /* Get a request handle. */
773 RTFILEAIOREQ hReq = pdmacFileAioMgrNormalRequestAlloc(pAioMgr);
774 AssertMsg(hReq != NIL_RTFILEAIOREQ, ("Out of request handles\n"));
775
776 if (pTask->enmTransferType == PDMACTASKFILETRANSFER_WRITE)
777 {
778 /* Grow the file if needed. */
779 if (RT_UNLIKELY((uint64_t)(pTask->Off + pTask->DataSeg.cbSeg) > pEndpoint->cbFile))
780 {
781 ASMAtomicWriteU64(&pEndpoint->cbFile, pTask->Off + pTask->DataSeg.cbSeg);
782 RTFileSetSize(pEndpoint->hFile, pTask->Off + pTask->DataSeg.cbSeg);
783 }
784
785 rc = RTFileAioReqPrepareWrite(hReq, pEndpoint->hFile,
786 pTask->Off, pTask->DataSeg.pvSeg,
787 pTask->DataSeg.cbSeg, pTask);
788 }
789 else
790 rc = RTFileAioReqPrepareRead(hReq, pEndpoint->hFile,
791 pTask->Off, pTask->DataSeg.pvSeg,
792 pTask->DataSeg.cbSeg, pTask);
793 AssertRC(rc);
794
795 rc = pdmacFileAioMgrNormalRangeLock(pAioMgr, pEndpoint, pTask->Off,
796 pTask->DataSeg.cbSeg,
797 pTask);
798
799 if (RT_SUCCESS(rc))
800 {
801 pTask->hReq = hReq;
802 *phReq = hReq;
803 }
804 }
805 else
806 LogFlow(("Task %#p was deferred because the access range is locked\n", pTask));
807
808 return rc;
809}
810
811static int pdmacFileAioMgrNormalTaskPrepareNonBuffered(PPDMACEPFILEMGR pAioMgr,
812 PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint,
813 PPDMACTASKFILE pTask, PRTFILEAIOREQ phReq)
814{
815 /*
816 * Check if the alignment requirements are met.
817 * Offset, transfer size and buffer address
818 * need to be on a 512 boundary.
819 */
820 RTFOFF offStart = pTask->Off & ~(RTFOFF)(512-1);
821 size_t cbToTransfer = RT_ALIGN_Z(pTask->DataSeg.cbSeg + (pTask->Off - offStart), 512);
822 PDMACTASKFILETRANSFER enmTransferType = pTask->enmTransferType;
823
824 AssertMsg( pTask->enmTransferType == PDMACTASKFILETRANSFER_WRITE
825 || (uint64_t)(offStart + cbToTransfer) <= pEndpoint->cbFile,
826 ("Read exceeds file size offStart=%RTfoff cbToTransfer=%d cbFile=%llu\n",
827 offStart, cbToTransfer, pEndpoint->cbFile));
828
829 pTask->fPrefetch = false;
830
831 /*
832 * Before we start to setup the request we have to check whether there is a task
833 * already active which range intersects with ours. We have to defer execution
834 * of this task in two cases:
835 * - The pending task is a write and the current is either read or write
836 * - The pending task is a read and the current task is a write task.
837 *
838 * To check whether a range is currently "locked" we use the AVL tree where every pending task
839 * is stored by its file offset range. The current task will be added to the active task
840 * and will be executed when the active one completes. (The method below
841 * which checks whether a range is already used will add the task)
842 *
843 * This is necessary because of the requirement to align all requests to a 512 boundary
844 * which is enforced by the host OS (Linux and Windows atm). It is possible that
845 * we have to process unaligned tasks and need to align them using bounce buffers.
846 * While the data is fetched from the file another request might arrive writing to
847 * the same range. This will result in data corruption if both are executed concurrently.
848 */
849 int rc = VINF_SUCCESS;
850 bool fLocked = pdmacFileAioMgrNormalIsRangeLocked(pEndpoint, offStart, cbToTransfer, pTask);
851 if (!fLocked)
852 {
853 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pEndpoint->Core.pEpClass;
854 void *pvBuf = pTask->DataSeg.pvSeg;
855
856 /* Get a request handle. */
857 RTFILEAIOREQ hReq = pdmacFileAioMgrNormalRequestAlloc(pAioMgr);
858 AssertMsg(hReq != NIL_RTFILEAIOREQ, ("Out of request handles\n"));
859
860 if ( RT_UNLIKELY(cbToTransfer != pTask->DataSeg.cbSeg)
861 || RT_UNLIKELY(offStart != pTask->Off)
862 || ((pEpClassFile->uBitmaskAlignment & (RTR3UINTPTR)pvBuf) != (RTR3UINTPTR)pvBuf))
863 {
864 LogFlow(("Using bounce buffer for task %#p cbToTransfer=%zd cbSeg=%zd offStart=%RTfoff off=%RTfoff\n",
865 pTask, cbToTransfer, pTask->DataSeg.cbSeg, offStart, pTask->Off));
866
867 /* Create bounce buffer. */
868 pTask->cbBounceBuffer = cbToTransfer;
869
870 AssertMsg(pTask->Off >= offStart, ("Overflow in calculation Off=%llu offStart=%llu\n",
871 pTask->Off, offStart));
872 pTask->offBounceBuffer = pTask->Off - offStart;
873
874 /** @todo: I think we need something like a RTMemAllocAligned method here.
875 * Current assumption is that the maximum alignment is 4096byte
876 * (GPT disk on Windows)
877 * so we can use RTMemPageAlloc here.
878 */
879 pTask->pvBounceBuffer = RTMemPageAlloc(cbToTransfer);
880 if (RT_LIKELY(pTask->pvBounceBuffer))
881 {
882 pvBuf = pTask->pvBounceBuffer;
883
884 if (pTask->enmTransferType == PDMACTASKFILETRANSFER_WRITE)
885 {
886 if ( RT_UNLIKELY(cbToTransfer != pTask->DataSeg.cbSeg)
887 || RT_UNLIKELY(offStart != pTask->Off))
888 {
889 /* We have to fill the buffer first before we can update the data. */
890 LogFlow(("Prefetching data for task %#p\n", pTask));
891 pTask->fPrefetch = true;
892 enmTransferType = PDMACTASKFILETRANSFER_READ;
893 }
894 else
895 memcpy(pvBuf, pTask->DataSeg.pvSeg, pTask->DataSeg.cbSeg);
896 }
897 }
898 else
899 rc = VERR_NO_MEMORY;
900 }
901 else
902 pTask->cbBounceBuffer = 0;
903
904 if (RT_SUCCESS(rc))
905 {
906 AssertMsg((pEpClassFile->uBitmaskAlignment & (RTR3UINTPTR)pvBuf) == (RTR3UINTPTR)pvBuf,
907 ("AIO: Alignment restrictions not met! pvBuf=%p uBitmaskAlignment=%p\n", pvBuf, pEpClassFile->uBitmaskAlignment));
908
909 if (enmTransferType == PDMACTASKFILETRANSFER_WRITE)
910 {
911 /* Grow the file if needed. */
912 if (RT_UNLIKELY((uint64_t)(pTask->Off + pTask->DataSeg.cbSeg) > pEndpoint->cbFile))
913 {
914 ASMAtomicWriteU64(&pEndpoint->cbFile, pTask->Off + pTask->DataSeg.cbSeg);
915 RTFileSetSize(pEndpoint->hFile, pTask->Off + pTask->DataSeg.cbSeg);
916 }
917
918 rc = RTFileAioReqPrepareWrite(hReq, pEndpoint->hFile,
919 offStart, pvBuf, cbToTransfer, pTask);
920 }
921 else
922 rc = RTFileAioReqPrepareRead(hReq, pEndpoint->hFile,
923 offStart, pvBuf, cbToTransfer, pTask);
924 AssertRC(rc);
925
926 rc = pdmacFileAioMgrNormalRangeLock(pAioMgr, pEndpoint, offStart, cbToTransfer, pTask);
927
928 if (RT_SUCCESS(rc))
929 {
930 pTask->hReq = hReq;
931 *phReq = hReq;
932 }
933 else
934 {
935 /* Cleanup */
936 if (pTask->cbBounceBuffer)
937 RTMemPageFree(pTask->pvBounceBuffer, pTask->cbBounceBuffer);
938 }
939 }
940 }
941 else
942 LogFlow(("Task %#p was deferred because the access range is locked\n", pTask));
943
944 return rc;
945}
946
947static int pdmacFileAioMgrNormalProcessTaskList(PPDMACTASKFILE pTaskHead,
948 PPDMACEPFILEMGR pAioMgr,
949 PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint)
950{
951 RTFILEAIOREQ apReqs[20];
952 unsigned cRequests = 0;
953 int rc = VINF_SUCCESS;
954
955 AssertMsg(pEndpoint->enmState == PDMASYNCCOMPLETIONENDPOINTFILESTATE_ACTIVE,
956 ("Trying to process request lists of a non active endpoint!\n"));
957
958 /* Go through the list and queue the requests until we get a flush request */
959 while ( pTaskHead
960 && !pEndpoint->pFlushReq
961 && (pAioMgr->cRequestsActive + cRequests < pAioMgr->cRequestsActiveMax)
962 && RT_SUCCESS(rc))
963 {
964 RTMSINTERVAL msWhenNext;
965 PPDMACTASKFILE pCurr = pTaskHead;
966
967 if (!pdmacEpIsTransferAllowed(&pEndpoint->Core, (uint32_t)pCurr->DataSeg.cbSeg, &msWhenNext))
968 {
969 pAioMgr->msBwLimitExpired = RT_MIN(pAioMgr->msBwLimitExpired, msWhenNext);
970 break;
971 }
972
973 pTaskHead = pTaskHead->pNext;
974
975 pCurr->pNext = NULL;
976
977 AssertMsg(VALID_PTR(pCurr->pEndpoint) && (pCurr->pEndpoint == pEndpoint),
978 ("Endpoints do not match\n"));
979
980 switch (pCurr->enmTransferType)
981 {
982 case PDMACTASKFILETRANSFER_FLUSH:
983 {
984 /* If there is no data transfer request this flush request finished immediately. */
985 if (pEndpoint->fAsyncFlushSupported)
986 {
987 /* Issue a flush to the host. */
988 RTFILEAIOREQ hReq = pdmacFileAioMgrNormalRequestAlloc(pAioMgr);
989 AssertMsg(hReq != NIL_RTFILEAIOREQ, ("Out of request handles\n"));
990
991 LogFlow(("Flush request %#p\n", hReq));
992
993 rc = RTFileAioReqPrepareFlush(hReq, pEndpoint->hFile, pCurr);
994 if (RT_FAILURE(rc))
995 {
996 pEndpoint->fAsyncFlushSupported = false;
997 pdmacFileAioMgrNormalRequestFree(pAioMgr, hReq);
998 rc = VINF_SUCCESS; /* Fake success */
999 }
1000 else
1001 {
1002 pCurr->hReq = hReq;
1003 apReqs[cRequests] = hReq;
1004 pEndpoint->AioMgr.cReqsProcessed++;
1005 cRequests++;
1006 }
1007 }
1008
1009 if ( !pEndpoint->AioMgr.cRequestsActive
1010 && !pEndpoint->fAsyncFlushSupported)
1011 {
1012 pCurr->pfnCompleted(pCurr, pCurr->pvUser, VINF_SUCCESS);
1013 pdmacFileTaskFree(pEndpoint, pCurr);
1014 }
1015 else
1016 {
1017 Assert(!pEndpoint->pFlushReq);
1018 pEndpoint->pFlushReq = pCurr;
1019 }
1020 break;
1021 }
1022 case PDMACTASKFILETRANSFER_READ:
1023 case PDMACTASKFILETRANSFER_WRITE:
1024 {
1025 RTFILEAIOREQ hReq = NIL_RTFILEAIOREQ;
1026
1027 if (pCurr->hReq == NIL_RTFILEAIOREQ)
1028 {
1029 if (pEndpoint->enmBackendType == PDMACFILEEPBACKEND_BUFFERED)
1030 rc = pdmacFileAioMgrNormalTaskPrepareBuffered(pAioMgr, pEndpoint, pCurr, &hReq);
1031 else if (pEndpoint->enmBackendType == PDMACFILEEPBACKEND_NON_BUFFERED)
1032 rc = pdmacFileAioMgrNormalTaskPrepareNonBuffered(pAioMgr, pEndpoint, pCurr, &hReq);
1033 else
1034 AssertMsgFailed(("Invalid backend type %d\n", pEndpoint->enmBackendType));
1035
1036 AssertRC(rc);
1037 }
1038 else
1039 {
1040 LogFlow(("Task %#p has I/O request %#p already\n", pCurr, pCurr->hReq));
1041 hReq = pCurr->hReq;
1042 }
1043
1044 LogFlow(("Read/Write request %#p\n", hReq));
1045
1046 if (hReq != NIL_RTFILEAIOREQ)
1047 {
1048 apReqs[cRequests] = hReq;
1049 cRequests++;
1050 }
1051 break;
1052 }
1053 default:
1054 AssertMsgFailed(("Invalid transfer type %d\n", pCurr->enmTransferType));
1055 } /* switch transfer type */
1056
1057 /* Queue the requests if the array is full. */
1058 if (cRequests == RT_ELEMENTS(apReqs))
1059 {
1060 rc = pdmacFileAioMgrNormalReqsEnqueue(pAioMgr, pEndpoint, apReqs, cRequests);
1061 cRequests = 0;
1062 AssertMsg(RT_SUCCESS(rc) || (rc == VERR_FILE_AIO_INSUFFICIENT_RESSOURCES),
1063 ("Unexpected return code\n"));
1064 }
1065 }
1066
1067 if (cRequests)
1068 {
1069 rc = pdmacFileAioMgrNormalReqsEnqueue(pAioMgr, pEndpoint, apReqs, cRequests);
1070 AssertMsg(RT_SUCCESS(rc) || (rc == VERR_FILE_AIO_INSUFFICIENT_RESSOURCES),
1071 ("Unexpected return code rc=%Rrc\n", rc));
1072 }
1073
1074 if (pTaskHead)
1075 {
1076 /* Add the rest of the tasks to the pending list */
1077 pdmacFileAioMgrEpAddTaskList(pEndpoint, pTaskHead);
1078
1079 if (RT_UNLIKELY( pAioMgr->cRequestsActiveMax == pAioMgr->cRequestsActive
1080 && !pEndpoint->pFlushReq))
1081 {
1082#if 0
1083 /*
1084 * The I/O manager has no room left for more requests
1085 * but there are still requests to process.
1086 * Create a new I/O manager and let it handle some endpoints.
1087 */
1088 pdmacFileAioMgrNormalBalanceLoad(pAioMgr);
1089#else
1090 /* Grow the I/O manager */
1091 pAioMgr->enmState = PDMACEPFILEMGRSTATE_GROWING;
1092#endif
1093 }
1094 }
1095
1096 /* Insufficient resources are not fatal. */
1097 if (rc == VERR_FILE_AIO_INSUFFICIENT_RESSOURCES)
1098 rc = VINF_SUCCESS;
1099
1100 return rc;
1101}
1102
1103/**
1104 * Adds all pending requests for the given endpoint
1105 * until a flush request is encountered or there is no
1106 * request anymore.
1107 *
1108 * @returns VBox status code.
1109 * @param pAioMgr The async I/O manager for the endpoint
1110 * @param pEndpoint The endpoint to get the requests from.
1111 */
1112static int pdmacFileAioMgrNormalQueueReqs(PPDMACEPFILEMGR pAioMgr,
1113 PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint)
1114{
1115 int rc = VINF_SUCCESS;
1116 PPDMACTASKFILE pTasksHead = NULL;
1117
1118 AssertMsg(pEndpoint->enmState == PDMASYNCCOMPLETIONENDPOINTFILESTATE_ACTIVE,
1119 ("Trying to process request lists of a non active endpoint!\n"));
1120
1121 Assert(!pEndpoint->pFlushReq);
1122
1123 /* Check the pending list first */
1124 if (pEndpoint->AioMgr.pReqsPendingHead)
1125 {
1126 LogFlow(("Queuing pending requests first\n"));
1127
1128 pTasksHead = pEndpoint->AioMgr.pReqsPendingHead;
1129 /*
1130 * Clear the list as the processing routine will insert them into the list
1131 * again if it gets a flush request.
1132 */
1133 pEndpoint->AioMgr.pReqsPendingHead = NULL;
1134 pEndpoint->AioMgr.pReqsPendingTail = NULL;
1135 rc = pdmacFileAioMgrNormalProcessTaskList(pTasksHead, pAioMgr, pEndpoint);
1136 AssertRC(rc); /** @todo r=bird: status code potentially overwritten. */
1137 }
1138
1139 if (!pEndpoint->pFlushReq && !pEndpoint->AioMgr.pReqsPendingHead)
1140 {
1141 /* Now the request queue. */
1142 pTasksHead = pdmacFileEpGetNewTasks(pEndpoint);
1143 if (pTasksHead)
1144 {
1145 rc = pdmacFileAioMgrNormalProcessTaskList(pTasksHead, pAioMgr, pEndpoint);
1146 AssertRC(rc);
1147 }
1148 }
1149
1150 return rc;
1151}
1152
1153static int pdmacFileAioMgrNormalProcessBlockingEvent(PPDMACEPFILEMGR pAioMgr)
1154{
1155 int rc = VINF_SUCCESS;
1156 bool fNotifyWaiter = false;
1157
1158 LogFlowFunc((": Enter\n"));
1159
1160 Assert(pAioMgr->fBlockingEventPending);
1161
1162 switch (pAioMgr->enmBlockingEvent)
1163 {
1164 case PDMACEPFILEAIOMGRBLOCKINGEVENT_ADD_ENDPOINT:
1165 {
1166 PPDMASYNCCOMPLETIONENDPOINTFILE pEndpointNew = ASMAtomicReadPtrT(&pAioMgr->BlockingEventData.AddEndpoint.pEndpoint, PPDMASYNCCOMPLETIONENDPOINTFILE);
1167 AssertMsg(VALID_PTR(pEndpointNew), ("Adding endpoint event without a endpoint to add\n"));
1168
1169 pEndpointNew->enmState = PDMASYNCCOMPLETIONENDPOINTFILESTATE_ACTIVE;
1170
1171 pEndpointNew->AioMgr.pEndpointNext = pAioMgr->pEndpointsHead;
1172 pEndpointNew->AioMgr.pEndpointPrev = NULL;
1173 if (pAioMgr->pEndpointsHead)
1174 pAioMgr->pEndpointsHead->AioMgr.pEndpointPrev = pEndpointNew;
1175 pAioMgr->pEndpointsHead = pEndpointNew;
1176
1177 /* Assign the completion point to this file. */
1178 rc = RTFileAioCtxAssociateWithFile(pAioMgr->hAioCtx, pEndpointNew->hFile);
1179 fNotifyWaiter = true;
1180 pAioMgr->cEndpoints++;
1181 break;
1182 }
1183 case PDMACEPFILEAIOMGRBLOCKINGEVENT_REMOVE_ENDPOINT:
1184 {
1185 PPDMASYNCCOMPLETIONENDPOINTFILE pEndpointRemove = ASMAtomicReadPtrT(&pAioMgr->BlockingEventData.RemoveEndpoint.pEndpoint, PPDMASYNCCOMPLETIONENDPOINTFILE);
1186 AssertMsg(VALID_PTR(pEndpointRemove), ("Removing endpoint event without a endpoint to remove\n"));
1187
1188 pEndpointRemove->enmState = PDMASYNCCOMPLETIONENDPOINTFILESTATE_REMOVING;
1189 fNotifyWaiter = !pdmacFileAioMgrNormalRemoveEndpoint(pEndpointRemove);
1190 break;
1191 }
1192 case PDMACEPFILEAIOMGRBLOCKINGEVENT_CLOSE_ENDPOINT:
1193 {
1194 PPDMASYNCCOMPLETIONENDPOINTFILE pEndpointClose = ASMAtomicReadPtrT(&pAioMgr->BlockingEventData.CloseEndpoint.pEndpoint, PPDMASYNCCOMPLETIONENDPOINTFILE);
1195 AssertMsg(VALID_PTR(pEndpointClose), ("Close endpoint event without a endpoint to close\n"));
1196
1197 if (pEndpointClose->enmState == PDMASYNCCOMPLETIONENDPOINTFILESTATE_ACTIVE)
1198 {
1199 LogFlowFunc((": Closing endpoint %#p{%s}\n", pEndpointClose, pEndpointClose->Core.pszUri));
1200
1201 /* Make sure all tasks finished. Process the queues a last time first. */
1202 rc = pdmacFileAioMgrNormalQueueReqs(pAioMgr, pEndpointClose);
1203 AssertRC(rc);
1204
1205 pEndpointClose->enmState = PDMASYNCCOMPLETIONENDPOINTFILESTATE_CLOSING;
1206 fNotifyWaiter = !pdmacFileAioMgrNormalRemoveEndpoint(pEndpointClose);
1207 }
1208 else if ( (pEndpointClose->enmState == PDMASYNCCOMPLETIONENDPOINTFILESTATE_CLOSING)
1209 && (!pEndpointClose->AioMgr.cRequestsActive))
1210 fNotifyWaiter = true;
1211 break;
1212 }
1213 case PDMACEPFILEAIOMGRBLOCKINGEVENT_SHUTDOWN:
1214 {
1215 pAioMgr->enmState = PDMACEPFILEMGRSTATE_SHUTDOWN;
1216 if (!pAioMgr->cRequestsActive)
1217 fNotifyWaiter = true;
1218 break;
1219 }
1220 case PDMACEPFILEAIOMGRBLOCKINGEVENT_SUSPEND:
1221 {
1222 pAioMgr->enmState = PDMACEPFILEMGRSTATE_SUSPENDING;
1223 break;
1224 }
1225 case PDMACEPFILEAIOMGRBLOCKINGEVENT_RESUME:
1226 {
1227 pAioMgr->enmState = PDMACEPFILEMGRSTATE_RUNNING;
1228 fNotifyWaiter = true;
1229 break;
1230 }
1231 default:
1232 AssertReleaseMsgFailed(("Invalid event type %d\n", pAioMgr->enmBlockingEvent));
1233 }
1234
1235 if (fNotifyWaiter)
1236 {
1237 ASMAtomicWriteBool(&pAioMgr->fBlockingEventPending, false);
1238 pAioMgr->enmBlockingEvent = PDMACEPFILEAIOMGRBLOCKINGEVENT_INVALID;
1239
1240 /* Release the waiting thread. */
1241 LogFlow(("Signalling waiter\n"));
1242 rc = RTSemEventSignal(pAioMgr->EventSemBlock);
1243 AssertRC(rc);
1244 }
1245
1246 LogFlowFunc((": Leave\n"));
1247 return rc;
1248}
1249
1250/**
1251 * Checks all endpoints for pending events or new requests.
1252 *
1253 * @returns VBox status code.
1254 * @param pAioMgr The I/O manager handle.
1255 */
1256static int pdmacFileAioMgrNormalCheckEndpoints(PPDMACEPFILEMGR pAioMgr)
1257{
1258 /* Check the assigned endpoints for new tasks if there isn't a flush request active at the moment. */
1259 int rc = VINF_SUCCESS;
1260 PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint = pAioMgr->pEndpointsHead;
1261
1262 pAioMgr->msBwLimitExpired = RT_INDEFINITE_WAIT;
1263
1264 while (pEndpoint)
1265 {
1266 if (!pEndpoint->pFlushReq
1267 && (pEndpoint->enmState == PDMASYNCCOMPLETIONENDPOINTFILESTATE_ACTIVE)
1268 && !pEndpoint->AioMgr.fMoving)
1269 {
1270 rc = pdmacFileAioMgrNormalQueueReqs(pAioMgr, pEndpoint);
1271 if (RT_FAILURE(rc))
1272 return rc;
1273 }
1274 else if ( !pEndpoint->AioMgr.cRequestsActive
1275 && pEndpoint->enmState != PDMASYNCCOMPLETIONENDPOINTFILESTATE_ACTIVE)
1276 {
1277 /* Reopen the file so that the new endpoint can re-associate with the file */
1278 RTFileClose(pEndpoint->hFile);
1279 rc = RTFileOpen(&pEndpoint->hFile, pEndpoint->Core.pszUri, pEndpoint->fFlags);
1280 AssertRC(rc);
1281
1282 if (pEndpoint->AioMgr.fMoving)
1283 {
1284 pEndpoint->AioMgr.fMoving = false;
1285 pdmacFileAioMgrAddEndpoint(pEndpoint->AioMgr.pAioMgrDst, pEndpoint);
1286 }
1287 else
1288 {
1289 Assert(pAioMgr->fBlockingEventPending);
1290 ASMAtomicWriteBool(&pAioMgr->fBlockingEventPending, false);
1291
1292 /* Release the waiting thread. */
1293 LogFlow(("Signalling waiter\n"));
1294 rc = RTSemEventSignal(pAioMgr->EventSemBlock);
1295 AssertRC(rc);
1296 }
1297 }
1298
1299 pEndpoint = pEndpoint->AioMgr.pEndpointNext;
1300 }
1301
1302 return rc;
1303}
1304
1305/**
1306 * Wrapper around pdmacFileAioMgrNormalReqCompleteRc().
1307 */
1308static void pdmacFileAioMgrNormalReqComplete(PPDMACEPFILEMGR pAioMgr, RTFILEAIOREQ hReq)
1309{
1310 size_t cbTransfered = 0;
1311 int rcReq = RTFileAioReqGetRC(hReq, &cbTransfered);
1312
1313 pdmacFileAioMgrNormalReqCompleteRc(pAioMgr, hReq, rcReq, cbTransfered);
1314}
1315
1316static void pdmacFileAioMgrNormalReqCompleteRc(PPDMACEPFILEMGR pAioMgr, RTFILEAIOREQ hReq,
1317 int rcReq, size_t cbTransfered)
1318{
1319 int rc = VINF_SUCCESS;
1320 PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint;
1321 PPDMACTASKFILE pTask = (PPDMACTASKFILE)RTFileAioReqGetUser(hReq);
1322 PPDMACTASKFILE pTasksWaiting;
1323
1324 LogFlowFunc(("pAioMgr=%#p hReq=%#p\n", pAioMgr, hReq));
1325
1326 pEndpoint = pTask->pEndpoint;
1327
1328 pTask->hReq = NIL_RTFILEAIOREQ;
1329
1330 pAioMgr->cRequestsActive--;
1331 pEndpoint->AioMgr.cRequestsActive--;
1332 pEndpoint->AioMgr.cReqsProcessed++;
1333
1334 /*
1335 * It is possible that the request failed on Linux with kernels < 2.6.23
1336 * if the passed buffer was allocated with remap_pfn_range or if the file
1337 * is on an NFS endpoint which does not support async and direct I/O at the same time.
1338 * The endpoint will be migrated to a failsafe manager in case a request fails.
1339 */
1340 if (RT_FAILURE(rcReq))
1341 {
1342 /* Free bounce buffers and the IPRT request. */
1343 pdmacFileAioMgrNormalRequestFree(pAioMgr, hReq);
1344
1345 if (pTask->enmTransferType == PDMACTASKFILETRANSFER_FLUSH)
1346 {
1347 LogFlow(("Async flushes are not supported for this endpoint, disabling\n"));
1348 pEndpoint->fAsyncFlushSupported = false;
1349 AssertMsg(pEndpoint->pFlushReq == pTask, ("Failed flush request doesn't match active one\n"));
1350 /* The other method will take over now. */
1351 }
1352 else
1353 {
1354 /* Free the lock and process pending tasks if necessary */
1355 pTasksWaiting = pdmacFileAioMgrNormalRangeLockFree(pAioMgr, pEndpoint, pTask->pRangeLock);
1356 rc = pdmacFileAioMgrNormalProcessTaskList(pTasksWaiting, pAioMgr, pEndpoint);
1357 AssertRC(rc);
1358
1359 if (pTask->cbBounceBuffer)
1360 RTMemPageFree(pTask->pvBounceBuffer, pTask->cbBounceBuffer);
1361
1362 /*
1363 * Fatal errors are reported to the guest and non-fatal errors
1364 * will cause a migration to the failsafe manager in the hope
1365 * that the error disappears.
1366 */
1367 if (!pdmacFileAioMgrNormalRcIsFatal(rcReq))
1368 {
1369 /* Queue the request on the pending list. */
1370 pTask->pNext = pEndpoint->AioMgr.pReqsPendingHead;
1371 pEndpoint->AioMgr.pReqsPendingHead = pTask;
1372
1373 /* Create a new failsafe manager if necessary. */
1374 if (!pEndpoint->AioMgr.fMoving)
1375 {
1376 PPDMACEPFILEMGR pAioMgrFailsafe;
1377
1378 LogRel(("%s: Request %#p failed with rc=%Rrc, migrating endpoint %s to failsafe manager.\n",
1379 RTThreadGetName(pAioMgr->Thread), pTask, rcReq, pEndpoint->Core.pszUri));
1380
1381 pEndpoint->AioMgr.fMoving = true;
1382
1383 rc = pdmacFileAioMgrCreate((PPDMASYNCCOMPLETIONEPCLASSFILE)pEndpoint->Core.pEpClass,
1384 &pAioMgrFailsafe, PDMACEPFILEMGRTYPE_SIMPLE);
1385 AssertRC(rc);
1386
1387 pEndpoint->AioMgr.pAioMgrDst = pAioMgrFailsafe;
1388
1389 /* Update the flags to open the file with. Disable async I/O and enable the host cache. */
1390 pEndpoint->fFlags &= ~(RTFILE_O_ASYNC_IO | RTFILE_O_NO_CACHE);
1391 }
1392
1393 /* If this was the last request for the endpoint migrate it to the new manager. */
1394 if (!pEndpoint->AioMgr.cRequestsActive)
1395 {
1396 bool fReqsPending = pdmacFileAioMgrNormalRemoveEndpoint(pEndpoint);
1397 Assert(!fReqsPending);
1398
1399 rc = pdmacFileAioMgrAddEndpoint(pEndpoint->AioMgr.pAioMgrDst, pEndpoint);
1400 AssertRC(rc);
1401 }
1402 }
1403 else
1404 {
1405 pTask->pfnCompleted(pTask, pTask->pvUser, rcReq);
1406 pdmacFileTaskFree(pEndpoint, pTask);
1407 }
1408 }
1409 }
1410 else
1411 {
1412 if (pTask->enmTransferType == PDMACTASKFILETRANSFER_FLUSH)
1413 {
1414 /* Clear pending flush */
1415 AssertMsg(pEndpoint->pFlushReq == pTask, ("Completed flush request doesn't match active one\n"));
1416 pEndpoint->pFlushReq = NULL;
1417 pdmacFileAioMgrNormalRequestFree(pAioMgr, hReq);
1418
1419 /* Call completion callback */
1420 LogFlow(("Flush task=%#p completed with %Rrc\n", pTask, rcReq));
1421 pTask->pfnCompleted(pTask, pTask->pvUser, rcReq);
1422 pdmacFileTaskFree(pEndpoint, pTask);
1423 }
1424 else
1425 {
1426 /*
1427 * Restart an incomplete transfer.
1428 * This usually means that the request will return an error now
1429 * but to get the cause of the error (disk full, file too big, I/O error, ...)
1430 * the transfer needs to be continued.
1431 */
1432 if (RT_UNLIKELY( cbTransfered < pTask->DataSeg.cbSeg
1433 || ( pTask->cbBounceBuffer
1434 && cbTransfered < pTask->cbBounceBuffer)))
1435 {
1436 RTFOFF offStart;
1437 size_t cbToTransfer;
1438 uint8_t *pbBuf = NULL;
1439
1440 LogFlow(("Restarting incomplete transfer %#p (%zu bytes transferred)\n",
1441 pTask, cbTransfered));
1442 Assert(cbTransfered % 512 == 0);
1443
1444 if (pTask->cbBounceBuffer)
1445 {
1446 AssertPtr(pTask->pvBounceBuffer);
1447 offStart = (pTask->Off & ~((RTFOFF)512-1)) + cbTransfered;
1448 cbToTransfer = pTask->cbBounceBuffer - cbTransfered;
1449 pbBuf = (uint8_t *)pTask->pvBounceBuffer + cbTransfered;
1450 }
1451 else
1452 {
1453 Assert(!pTask->pvBounceBuffer);
1454 offStart = pTask->Off + cbTransfered;
1455 cbToTransfer = pTask->DataSeg.cbSeg - cbTransfered;
1456 pbBuf = (uint8_t *)pTask->DataSeg.pvSeg + cbTransfered;
1457 }
1458
1459 if (pTask->fPrefetch || pTask->enmTransferType == PDMACTASKFILETRANSFER_READ)
1460 {
1461 rc = RTFileAioReqPrepareRead(hReq, pEndpoint->hFile, offStart,
1462 pbBuf, cbToTransfer, pTask);
1463 }
1464 else
1465 {
1466 AssertMsg(pTask->enmTransferType == PDMACTASKFILETRANSFER_WRITE,
1467 ("Invalid transfer type\n"));
1468 rc = RTFileAioReqPrepareWrite(hReq, pEndpoint->hFile, offStart,
1469 pbBuf, cbToTransfer, pTask);
1470 }
1471 AssertRC(rc);
1472
1473 pTask->hReq = hReq;
1474 rc = pdmacFileAioMgrNormalReqsEnqueue(pAioMgr, pEndpoint, &hReq, 1);
1475 AssertMsg(RT_SUCCESS(rc) || (rc == VERR_FILE_AIO_INSUFFICIENT_RESSOURCES),
1476 ("Unexpected return code rc=%Rrc\n", rc));
1477 }
1478 else if (pTask->fPrefetch)
1479 {
1480 Assert(pTask->enmTransferType == PDMACTASKFILETRANSFER_WRITE);
1481 Assert(pTask->cbBounceBuffer);
1482
1483 memcpy(((uint8_t *)pTask->pvBounceBuffer) + pTask->offBounceBuffer,
1484 pTask->DataSeg.pvSeg,
1485 pTask->DataSeg.cbSeg);
1486
1487 /* Write it now. */
1488 pTask->fPrefetch = false;
1489 size_t cbToTransfer = RT_ALIGN_Z(pTask->DataSeg.cbSeg, 512);
1490 RTFOFF offStart = pTask->Off & ~(RTFOFF)(512-1);
1491
1492 /* Grow the file if needed. */
1493 if (RT_UNLIKELY((uint64_t)(pTask->Off + pTask->DataSeg.cbSeg) > pEndpoint->cbFile))
1494 {
1495 ASMAtomicWriteU64(&pEndpoint->cbFile, pTask->Off + pTask->DataSeg.cbSeg);
1496 RTFileSetSize(pEndpoint->hFile, pTask->Off + pTask->DataSeg.cbSeg);
1497 }
1498
1499 rc = RTFileAioReqPrepareWrite(hReq, pEndpoint->hFile,
1500 offStart, pTask->pvBounceBuffer, cbToTransfer, pTask);
1501 AssertRC(rc);
1502 pTask->hReq = hReq;
1503 rc = pdmacFileAioMgrNormalReqsEnqueue(pAioMgr, pEndpoint, &hReq, 1);
1504 AssertMsg(RT_SUCCESS(rc) || (rc == VERR_FILE_AIO_INSUFFICIENT_RESSOURCES),
1505 ("Unexpected return code rc=%Rrc\n", rc));
1506 }
1507 else
1508 {
1509 if (RT_SUCCESS(rc) && pTask->cbBounceBuffer)
1510 {
1511 if (pTask->enmTransferType == PDMACTASKFILETRANSFER_READ)
1512 memcpy(pTask->DataSeg.pvSeg,
1513 ((uint8_t *)pTask->pvBounceBuffer) + pTask->offBounceBuffer,
1514 pTask->DataSeg.cbSeg);
1515
1516 RTMemPageFree(pTask->pvBounceBuffer, pTask->cbBounceBuffer);
1517 }
1518
1519 pdmacFileAioMgrNormalRequestFree(pAioMgr, hReq);
1520
1521 /* Free the lock and process pending tasks if necessary */
1522 pTasksWaiting = pdmacFileAioMgrNormalRangeLockFree(pAioMgr, pEndpoint, pTask->pRangeLock);
1523 if (pTasksWaiting)
1524 {
1525 rc = pdmacFileAioMgrNormalProcessTaskList(pTasksWaiting, pAioMgr, pEndpoint);
1526 AssertRC(rc);
1527 }
1528
1529 /* Call completion callback */
1530 LogFlow(("Task=%#p completed with %Rrc\n", pTask, rcReq));
1531 pTask->pfnCompleted(pTask, pTask->pvUser, rcReq);
1532 pdmacFileTaskFree(pEndpoint, pTask);
1533
1534 /*
1535 * If there is no request left on the endpoint but a flush request is set
1536 * it completed now and we notify the owner.
1537 * Furthermore we look for new requests and continue.
1538 */
1539 if (!pEndpoint->AioMgr.cRequestsActive && pEndpoint->pFlushReq)
1540 {
1541 /* Call completion callback */
1542 pTask = pEndpoint->pFlushReq;
1543 pEndpoint->pFlushReq = NULL;
1544
1545 AssertMsg(pTask->pEndpoint == pEndpoint, ("Endpoint of the flush request does not match assigned one\n"));
1546
1547 pTask->pfnCompleted(pTask, pTask->pvUser, VINF_SUCCESS);
1548 pdmacFileTaskFree(pEndpoint, pTask);
1549 }
1550 else if (RT_UNLIKELY(!pEndpoint->AioMgr.cRequestsActive && pEndpoint->AioMgr.fMoving))
1551 {
1552 /* If the endpoint is about to be migrated do it now. */
1553 bool fReqsPending = pdmacFileAioMgrNormalRemoveEndpoint(pEndpoint);
1554 Assert(!fReqsPending);
1555
1556 rc = pdmacFileAioMgrAddEndpoint(pEndpoint->AioMgr.pAioMgrDst, pEndpoint);
1557 AssertRC(rc);
1558 }
1559 }
1560 } /* Not a flush request */
1561 } /* request completed successfully */
1562}
1563
1564/** Helper macro for checking for error codes. */
1565#define CHECK_RC(pAioMgr, rc) \
1566 if (RT_FAILURE(rc)) \
1567 {\
1568 int rc2 = pdmacFileAioMgrNormalErrorHandler(pAioMgr, rc, RT_SRC_POS);\
1569 return rc2;\
1570 }
1571
1572/**
1573 * The normal I/O manager using the RTFileAio* API
1574 *
1575 * @returns VBox status code.
1576 * @param hThreadSelf Handle of the thread.
1577 * @param pvUser Opaque user data.
1578 */
1579DECLCALLBACK(int) pdmacFileAioMgrNormal(RTTHREAD hThreadSelf, void *pvUser)
1580{
1581 int rc = VINF_SUCCESS;
1582 PPDMACEPFILEMGR pAioMgr = (PPDMACEPFILEMGR)pvUser;
1583 uint64_t uMillisEnd = RTTimeMilliTS() + PDMACEPFILEMGR_LOAD_UPDATE_PERIOD;
1584 NOREF(hThreadSelf);
1585
1586 while ( pAioMgr->enmState == PDMACEPFILEMGRSTATE_RUNNING
1587 || pAioMgr->enmState == PDMACEPFILEMGRSTATE_SUSPENDING
1588 || pAioMgr->enmState == PDMACEPFILEMGRSTATE_GROWING)
1589 {
1590 if (!pAioMgr->cRequestsActive)
1591 {
1592 ASMAtomicWriteBool(&pAioMgr->fWaitingEventSem, true);
1593 if (!ASMAtomicReadBool(&pAioMgr->fWokenUp))
1594 rc = RTSemEventWait(pAioMgr->EventSem, pAioMgr->msBwLimitExpired);
1595 ASMAtomicWriteBool(&pAioMgr->fWaitingEventSem, false);
1596 Assert(RT_SUCCESS(rc) || rc == VERR_TIMEOUT);
1597
1598 LogFlow(("Got woken up\n"));
1599 ASMAtomicWriteBool(&pAioMgr->fWokenUp, false);
1600 }
1601
1602 /* Check for an external blocking event first. */
1603 if (pAioMgr->fBlockingEventPending)
1604 {
1605 rc = pdmacFileAioMgrNormalProcessBlockingEvent(pAioMgr);
1606 CHECK_RC(pAioMgr, rc);
1607 }
1608
1609 if (RT_LIKELY( pAioMgr->enmState == PDMACEPFILEMGRSTATE_RUNNING
1610 || pAioMgr->enmState == PDMACEPFILEMGRSTATE_GROWING))
1611 {
1612 /* We got woken up because an endpoint issued new requests. Queue them. */
1613 rc = pdmacFileAioMgrNormalCheckEndpoints(pAioMgr);
1614 CHECK_RC(pAioMgr, rc);
1615
1616 while (pAioMgr->cRequestsActive)
1617 {
1618 RTFILEAIOREQ apReqs[20];
1619 uint32_t cReqsCompleted = 0;
1620 size_t cReqsWait;
1621
1622 if (pAioMgr->cRequestsActive > RT_ELEMENTS(apReqs))
1623 cReqsWait = RT_ELEMENTS(apReqs);
1624 else
1625 cReqsWait = pAioMgr->cRequestsActive;
1626
1627 LogFlow(("Waiting for %d of %d tasks to complete\n", 1, cReqsWait));
1628
1629 rc = RTFileAioCtxWait(pAioMgr->hAioCtx,
1630 1,
1631 RT_INDEFINITE_WAIT, apReqs,
1632 cReqsWait, &cReqsCompleted);
1633 if (RT_FAILURE(rc) && (rc != VERR_INTERRUPTED))
1634 CHECK_RC(pAioMgr, rc);
1635
1636 LogFlow(("%d tasks completed\n", cReqsCompleted));
1637
1638 for (uint32_t i = 0; i < cReqsCompleted; i++)
1639 pdmacFileAioMgrNormalReqComplete(pAioMgr, apReqs[i]);
1640
1641 /* Check for an external blocking event before we go to sleep again. */
1642 if (pAioMgr->fBlockingEventPending)
1643 {
1644 rc = pdmacFileAioMgrNormalProcessBlockingEvent(pAioMgr);
1645 CHECK_RC(pAioMgr, rc);
1646 }
1647
1648 /* Update load statistics. */
1649 uint64_t uMillisCurr = RTTimeMilliTS();
1650 if (uMillisCurr > uMillisEnd)
1651 {
1652 PPDMASYNCCOMPLETIONENDPOINTFILE pEndpointCurr = pAioMgr->pEndpointsHead;
1653
1654 /* Calculate timespan. */
1655 uMillisCurr -= uMillisEnd;
1656
1657 while (pEndpointCurr)
1658 {
1659 pEndpointCurr->AioMgr.cReqsPerSec = pEndpointCurr->AioMgr.cReqsProcessed / (uMillisCurr + PDMACEPFILEMGR_LOAD_UPDATE_PERIOD);
1660 pEndpointCurr->AioMgr.cReqsProcessed = 0;
1661 pEndpointCurr = pEndpointCurr->AioMgr.pEndpointNext;
1662 }
1663
1664 /* Set new update interval */
1665 uMillisEnd = RTTimeMilliTS() + PDMACEPFILEMGR_LOAD_UPDATE_PERIOD;
1666 }
1667
1668 /* Check endpoints for new requests. */
1669 if (pAioMgr->enmState != PDMACEPFILEMGRSTATE_GROWING)
1670 {
1671 rc = pdmacFileAioMgrNormalCheckEndpoints(pAioMgr);
1672 CHECK_RC(pAioMgr, rc);
1673 }
1674 } /* while requests are active. */
1675
1676 if (pAioMgr->enmState == PDMACEPFILEMGRSTATE_GROWING)
1677 {
1678 rc = pdmacFileAioMgrNormalGrow(pAioMgr);
1679 AssertRC(rc);
1680 Assert(pAioMgr->enmState == PDMACEPFILEMGRSTATE_RUNNING);
1681
1682 rc = pdmacFileAioMgrNormalCheckEndpoints(pAioMgr);
1683 CHECK_RC(pAioMgr, rc);
1684 }
1685 } /* if still running */
1686 } /* while running */
1687
1688 LogFlowFunc(("rc=%Rrc\n", rc));
1689 return rc;
1690}
1691
1692#undef CHECK_RC
1693
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