VirtualBox

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

最後變更 在這個檔案從22793是 22757,由 vboxsync 提交於 16 年 前

AsyncCompletion: Break the big critical section into smaller parts and let the I/O manager spawn new threads during high I/O load

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 29.7 KB
 
1/* $Id: PDMAsyncCompletionFileNormal.cpp 22757 2009-09-03 17:22:53Z vboxsync $ */
2/** @file
3 * PDM Async I/O - Transport data asynchronous in R3 using EMT.
4 * Async File I/O manager.
5 */
6
7/*
8 * Copyright (C) 2006-2008 Sun Microsystems, Inc.
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.alldomusa.eu.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22#define LOG_GROUP LOG_GROUP_PDM_ASYNC_COMPLETION
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 <VBox/log.h>
29
30#include "PDMAsyncCompletionFileInternal.h"
31
32/** The update period for the I/O load statistics in ms. */
33#define PDMACEPFILEMGR_LOAD_UPDATE_PERIOD 1000
34/** Maximum number of requests a manager will handle. */
35#define PDMACEPFILEMGR_REQS_MAX 512 /* @todo: Find better solution wrt. the request number*/
36
37int pdmacFileAioMgrNormalInit(PPDMACEPFILEMGR pAioMgr)
38{
39 int rc = VINF_SUCCESS;
40
41 rc = RTFileAioCtxCreate(&pAioMgr->hAioCtx, RTFILEAIO_UNLIMITED_REQS);
42 if (rc == VERR_OUT_OF_RANGE)
43 rc = RTFileAioCtxCreate(&pAioMgr->hAioCtx, PDMACEPFILEMGR_REQS_MAX);
44
45 if (RT_SUCCESS(rc))
46 {
47 /* Initialize request handle array. */
48 pAioMgr->iFreeEntryNext = 0;
49 pAioMgr->iFreeReqNext = 0;
50 pAioMgr->cReqEntries = PDMACEPFILEMGR_REQS_MAX + 1;
51 pAioMgr->pahReqsFree = (RTFILEAIOREQ *)RTMemAllocZ(pAioMgr->cReqEntries * sizeof(RTFILEAIOREQ));
52
53 if (pAioMgr->pahReqsFree)
54 {
55 return VINF_SUCCESS;
56 }
57 else
58 {
59 RTFileAioCtxDestroy(pAioMgr->hAioCtx);
60 rc = VERR_NO_MEMORY;
61 }
62 }
63
64 return rc;
65}
66
67void pdmacFileAioMgrNormalDestroy(PPDMACEPFILEMGR pAioMgr)
68{
69 RTFileAioCtxDestroy(pAioMgr->hAioCtx);
70
71 while (pAioMgr->iFreeReqNext != pAioMgr->iFreeEntryNext)
72 {
73 RTFileAioReqDestroy(pAioMgr->pahReqsFree[pAioMgr->iFreeReqNext]);
74 pAioMgr->iFreeReqNext = (pAioMgr->iFreeReqNext + 1) % pAioMgr->cReqEntries;
75 }
76
77 RTMemFree(pAioMgr->pahReqsFree);
78}
79
80/**
81 * Sorts the endpoint list with insertion sort.
82 */
83static void pdmacFileAioMgrNormalEndpointsSortByLoad(PPDMACEPFILEMGR pAioMgr)
84{
85 PPDMASYNCCOMPLETIONENDPOINTFILE pEpPrev, pEpCurr, pEpNextToSort;
86
87 pEpPrev = pAioMgr->pEndpointsHead;
88 pEpCurr = pEpPrev->AioMgr.pEndpointNext;
89
90 while (pEpCurr)
91 {
92 /* Remember the next element to sort because the list might change. */
93 pEpNextToSort = pEpCurr->AioMgr.pEndpointNext;
94
95 /* Unlink the current element from the list. */
96 PPDMASYNCCOMPLETIONENDPOINTFILE pPrev = pEpCurr->AioMgr.pEndpointPrev;
97 PPDMASYNCCOMPLETIONENDPOINTFILE pNext = pEpCurr->AioMgr.pEndpointNext;
98
99 if (pPrev)
100 pPrev->AioMgr.pEndpointNext = pNext;
101 else
102 pAioMgr->pEndpointsHead = pNext;
103
104 if (pNext)
105 pNext->AioMgr.pEndpointPrev = pPrev;
106
107 /* Go back until we reached the place to insert the current endpoint into. */
108 while (pEpPrev && (pEpPrev->AioMgr.cReqsPerSec < pEpCurr->AioMgr.cReqsPerSec))
109 pEpPrev = pEpPrev->AioMgr.pEndpointPrev;
110
111 /* Link the endpoint into the list. */
112 if (pEpPrev)
113 pNext = pEpPrev->AioMgr.pEndpointNext;
114 else
115 pNext = pAioMgr->pEndpointsHead;
116
117 pEpCurr->AioMgr.pEndpointNext = pNext;
118 pEpCurr->AioMgr.pEndpointPrev = pEpPrev;
119 pNext->AioMgr.pEndpointPrev = pEpCurr;
120 if (pEpPrev)
121 pEpPrev->AioMgr.pEndpointNext = pEpCurr;
122 else
123 pAioMgr->pEndpointsHead = pEpCurr;
124
125 pEpCurr = pEpNextToSort;
126 }
127
128#ifdef DEBUG
129 /* Validate sorting alogrithm */
130 unsigned cEndpoints = 0;
131 pEpCurr = pAioMgr->pEndpointsHead;
132
133 AssertMsg(pEpCurr, ("No endpoint in the list?\n"));
134 AssertMsg(!pEpCurr->AioMgr.pEndpointPrev, ("First element in the list points to previous element\n"));
135
136 while (pEpCurr)
137 {
138 cEndpoints++;
139
140 PPDMASYNCCOMPLETIONENDPOINTFILE pNext = pEpCurr->AioMgr.pEndpointNext;
141 PPDMASYNCCOMPLETIONENDPOINTFILE pPrev = pEpCurr->AioMgr.pEndpointPrev;
142
143 Assert(!pNext || pNext->AioMgr.cReqsPerSec <= pEpCurr->AioMgr.cReqsPerSec);
144 Assert(!pPrev || pPrev->AioMgr.cReqsPerSec >= pEpCurr->AioMgr.cReqsPerSec);
145
146 pEpCurr = pNext;
147 }
148
149 AssertMsg(cEndpoints == pAioMgr->cEndpoints, ("Endpoints lost during sort!\n"));
150
151#endif
152}
153
154/**
155 * Removes an endpoint from the currently assigned manager.
156 *
157 * @returns TRUE if there are still requests pending on the current manager for this endpoint.
158 * FALSE otherwise.
159 * @param pEndpointRemove The endpoint to remove.
160 */
161static bool pdmacFileAioMgrNormalRemoveEndpoint(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpointRemove)
162{
163 PPDMASYNCCOMPLETIONENDPOINTFILE pPrev = pEndpointRemove->AioMgr.pEndpointPrev;
164 PPDMASYNCCOMPLETIONENDPOINTFILE pNext = pEndpointRemove->AioMgr.pEndpointNext;
165 PPDMACEPFILEMGR pAioMgr = pEndpointRemove->pAioMgr;
166
167 pAioMgr->cEndpoints--;
168
169 if (pPrev)
170 pPrev->AioMgr.pEndpointNext = pNext;
171 else
172 pAioMgr->pEndpointsHead = pNext;
173
174 if (pNext)
175 pNext->AioMgr.pEndpointPrev = pPrev;
176
177 /* Make sure that there is no request pending on this manager for the endpoint. */
178 if (!pEndpointRemove->AioMgr.cRequestsActive)
179 {
180 Assert(!pEndpointRemove->pFlushReq);
181
182 /* Reopen the file so that the new endpoint can reassociate with the file */
183 RTFileClose(pEndpointRemove->File);
184 int rc = RTFileOpen(&pEndpointRemove->File, pEndpointRemove->Core.pszUri, pEndpointRemove->fFlags);
185 AssertRC(rc);
186 return false;
187 }
188
189 return true;
190}
191
192/**
193 * Creates a new I/O manager and spreads the I/O load of the endpoints
194 * between the given I/O manager and the new one.
195 *
196 * @returns nothing.
197 * @param pAioMgr The I/O manager with high I/O load.
198 */
199static void pdmacFileAioMgrNormalBalanceLoad(PPDMACEPFILEMGR pAioMgr)
200{
201 PPDMACEPFILEMGR pAioMgrNew = NULL;
202 int rc = VINF_SUCCESS;
203
204 /* Splitting can't be done with only one open endpoint. */
205 if (pAioMgr->cEndpoints > 1)
206 {
207 rc = pdmacFileAioMgrCreate((PPDMASYNCCOMPLETIONEPCLASSFILE)pAioMgr->pEndpointsHead->Core.pEpClass,
208 &pAioMgrNew);
209 if (RT_SUCCESS(rc))
210 {
211 /* We will sort the list by request count per second. */
212 pdmacFileAioMgrNormalEndpointsSortByLoad(pAioMgr);
213
214 /* Now move some endpoints to the new manager. */
215 unsigned cReqsHere = pAioMgr->pEndpointsHead->AioMgr.cReqsPerSec;
216 unsigned cReqsOther = 0;
217 PPDMASYNCCOMPLETIONENDPOINTFILE pCurr = pAioMgr->pEndpointsHead->AioMgr.pEndpointNext;
218
219 while (pCurr)
220 {
221 if (cReqsHere <= cReqsOther)
222 {
223 /*
224 * The other manager has more requests to handle now.
225 * We will keep the current endpoint.
226 */
227 Log(("Keeping endpoint %#p{%s} with %u reqs/s\n", pCurr->Core.pszUri, pCurr->AioMgr.cReqsPerSec));
228 cReqsHere += pCurr->AioMgr.cReqsPerSec;
229 pCurr = pCurr->AioMgr.pEndpointNext;
230 }
231 else
232 {
233 /* Move to other endpoint. */
234 Log(("Moving endpoint %#p{%s} with %u reqs/s to other manager\n", pCurr->Core.pszUri, pCurr->AioMgr.cReqsPerSec));
235 cReqsOther += pCurr->AioMgr.cReqsPerSec;
236
237 PPDMASYNCCOMPLETIONENDPOINTFILE pMove = pCurr;
238
239 pCurr = pCurr->AioMgr.pEndpointNext;
240
241 bool fReqsPending = pdmacFileAioMgrNormalRemoveEndpoint(pMove);
242
243 if (fReqsPending)
244 {
245 pMove->enmState = PDMASYNCCOMPLETIONENDPOINTFILESTATE_REMOVING;
246 pMove->AioMgr.fMoving = true;
247 pMove->AioMgr.pAioMgrDst = pAioMgrNew;
248 }
249 else
250 {
251 pMove->AioMgr.fMoving = false;
252 pMove->AioMgr.pAioMgrDst = NULL;
253 pdmacFileAioMgrAddEndpoint(pAioMgrNew, pMove);
254 }
255 }
256 }
257 }
258 else
259 {
260 /* Don't process further but leave a log entry about reduced performance. */
261 LogRel(("AIOMgr: Could not create new I/O manager (rc=%Rrc). Expect reduced performance\n", rc));
262 }
263 }
264}
265
266/**
267 * Error handler which will create the failsafe managers and destroy the failed I/O manager.
268 *
269 * @returns VBox status code
270 * @param pAioMgr The I/O manager the error ocurred on.
271 * @param rc The error code.
272 */
273static int pdmacFileAioMgrNormalErrorHandler(PPDMACEPFILEMGR pAioMgr, int rc, RT_SRC_POS_DECL)
274{
275 LogRel(("AIOMgr: I/O manager %#p encountered a critical error (rc=%Rrc) during operation. Falling back to failsafe mode. Expect reduced performance\n",
276 pAioMgr, rc));
277 LogRel(("AIOMgr: Error happened in %s:(%u){%s}\n", RT_SRC_POS_ARGS));
278 LogRel(("AIOMgr: Please contact the product vendor\n"));
279
280 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pAioMgr->pEndpointsHead->Core.pEpClass;
281
282 pAioMgr->enmState = PDMACEPFILEMGRSTATE_FAULT;
283 ASMAtomicWriteBool(&pEpClassFile->fFailsafe, true);
284
285 AssertMsgFailed(("Implement\n"));
286 return VINF_SUCCESS;
287}
288
289static int pdmacFileAioMgrNormalProcessTaskList(PPDMACTASKFILE pTaskHead,
290 PPDMACEPFILEMGR pAioMgr,
291 PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint)
292{
293 RTFILEAIOREQ apReqs[20];
294 unsigned cRequests = 0;
295 unsigned cMaxRequests = PDMACEPFILEMGR_REQS_MAX - pAioMgr->cRequestsActive;
296 int rc = VINF_SUCCESS;
297 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pEndpoint->Core.pEpClass;
298
299 AssertMsg(pEndpoint->enmState == PDMASYNCCOMPLETIONENDPOINTFILESTATE_ACTIVE,
300 ("Trying to process request lists of a non active endpoint!\n"));
301
302 /* Go through the list and queue the requests until we get a flush request */
303 while (pTaskHead && !pEndpoint->pFlushReq && (cMaxRequests > 0))
304 {
305 PPDMACTASKFILE pCurr = pTaskHead;
306
307 pTaskHead = pTaskHead->pNext;
308
309 AssertMsg(VALID_PTR(pCurr->pEndpoint) && (pCurr->pEndpoint == pEndpoint),
310 ("Endpoints do not match\n"));
311
312 switch (pCurr->enmTransferType)
313 {
314 case PDMACTASKFILETRANSFER_FLUSH:
315 {
316 /* If there is no data transfer request this flush request finished immediately. */
317 if (!pEndpoint->AioMgr.cRequestsActive)
318 {
319 pCurr->pfnCompleted(pCurr, pCurr->pvUser);
320 pdmacFileTaskFree(pEndpoint, pCurr);
321 }
322 else
323 {
324 pEndpoint->pFlushReq = pCurr;
325
326 if (pTaskHead)
327 {
328 /* Add the rest of the tasks to the pending list */
329 if (!pEndpoint->AioMgr.pReqsPendingHead)
330 {
331 Assert(!pEndpoint->AioMgr.pReqsPendingTail);
332 pEndpoint->AioMgr.pReqsPendingHead = pTaskHead;
333 }
334 else
335 {
336 Assert(pEndpoint->AioMgr.pReqsPendingTail);
337 pEndpoint->AioMgr.pReqsPendingTail->pNext = pTaskHead;
338 }
339
340 /* Update the tail. */
341 while (pTaskHead->pNext)
342 pTaskHead = pTaskHead->pNext;
343
344 pEndpoint->AioMgr.pReqsPendingTail = pTaskHead;
345 }
346 }
347 break;
348 }
349 case PDMACTASKFILETRANSFER_READ:
350 case PDMACTASKFILETRANSFER_WRITE:
351 {
352 RTFILEAIOREQ hReq = NIL_RTFILEAIOREQ;
353 void *pvBuf = pCurr->DataSeg.pvSeg;
354
355 /* Get a request handle. */
356 if (pAioMgr->iFreeReqNext != pAioMgr->iFreeEntryNext)
357 {
358 hReq = pAioMgr->pahReqsFree[pAioMgr->iFreeReqNext];
359 pAioMgr->pahReqsFree[pAioMgr->iFreeReqNext] = NIL_RTFILEAIOREQ;
360 pAioMgr->iFreeReqNext = (pAioMgr->iFreeReqNext + 1) % pAioMgr->cReqEntries;
361 }
362 else
363 {
364 rc = RTFileAioReqCreate(&hReq);
365 AssertRC(rc);
366 }
367
368 AssertMsg(hReq != NIL_RTFILEAIOREQ, ("Out of request handles\n"));
369
370 /* Check if the alignment requirements are met. */
371 if ((pEpClassFile->uBitmaskAlignment & (RTR3UINTPTR)pvBuf) != (RTR3UINTPTR)pvBuf)
372 {
373 /* Create bounce buffer. */
374 pCurr->fBounceBuffer = true;
375
376 /** @todo: I think we need something like a RTMemAllocAligned method here.
377 * Current assumption is that the maximum alignment is 4096byte
378 * (GPT disk on Windows)
379 * so we can use RTMemPageAlloc here.
380 */
381 pCurr->pvBounceBuffer = RTMemPageAlloc(pCurr->DataSeg.cbSeg);
382 AssertPtr(pCurr->pvBounceBuffer);
383 pvBuf = pCurr->pvBounceBuffer;
384
385 if (pCurr->enmTransferType == PDMACTASKFILETRANSFER_WRITE)
386 memcpy(pvBuf, pCurr->DataSeg.pvSeg, pCurr->DataSeg.cbSeg);
387 }
388 else
389 pCurr->fBounceBuffer = false;
390
391 AssertMsg((pEpClassFile->uBitmaskAlignment & (RTR3UINTPTR)pvBuf) == (RTR3UINTPTR)pvBuf,
392 ("AIO: Alignment restrictions not met!\n"));
393
394 if (pCurr->enmTransferType == PDMACTASKFILETRANSFER_WRITE)
395 {
396 /* Grow the file if needed. */
397 if (RT_UNLIKELY((pCurr->Off + pCurr->DataSeg.cbSeg) > pEndpoint->cbFile))
398 {
399 ASMAtomicWriteU64(&pEndpoint->cbFile, pCurr->Off + pCurr->DataSeg.cbSeg);
400 RTFileSetSize(pEndpoint->File, pCurr->Off + pCurr->DataSeg.cbSeg);
401 }
402
403 rc = RTFileAioReqPrepareWrite(hReq, pEndpoint->File,
404 pCurr->Off, pvBuf, pCurr->DataSeg.cbSeg, pCurr);
405 }
406 else
407 rc = RTFileAioReqPrepareRead(hReq, pEndpoint->File,
408 pCurr->Off, pvBuf, pCurr->DataSeg.cbSeg, pCurr);
409 AssertRC(rc);
410
411 apReqs[cRequests] = hReq;
412 pEndpoint->AioMgr.cReqsProcessed++;
413 cMaxRequests--;
414 cRequests++;
415 if (cRequests == RT_ELEMENTS(apReqs))
416 {
417 pAioMgr->cRequestsActive += cRequests;
418 rc = RTFileAioCtxSubmit(pAioMgr->hAioCtx, apReqs, cRequests);
419 if (RT_FAILURE(rc))
420 {
421 /* @todo implement */
422 AssertMsgFailed(("Implement\n"));
423 }
424
425 cRequests = 0;
426 }
427 break;
428 }
429 default:
430 AssertMsgFailed(("Invalid transfer type %d\n", pCurr->enmTransferType));
431 }
432 }
433
434 if (cRequests)
435 {
436 pAioMgr->cRequestsActive += cRequests;
437 rc = RTFileAioCtxSubmit(pAioMgr->hAioCtx, apReqs, cRequests);
438 AssertMsgReturn(RT_SUCCESS(rc), ("Could not submit %u requests %Rrc\n", cRequests, rc), rc);
439 }
440
441 if (RT_UNLIKELY(!cMaxRequests && pTaskHead && !pEndpoint->pFlushReq))
442 {
443 /*
444 * The I/O manager has no room left for more requests
445 * but there are still requests to process.
446 * Create a new I/O manager and let it handle some endpoints.
447 */
448
449 /* Add the rest of the tasks to the pending list first */
450 if (!pEndpoint->AioMgr.pReqsPendingHead)
451 {
452 Assert(!pEndpoint->AioMgr.pReqsPendingTail);
453 pEndpoint->AioMgr.pReqsPendingHead = pTaskHead;
454 }
455 else
456 {
457 Assert(pEndpoint->AioMgr.pReqsPendingTail);
458 pEndpoint->AioMgr.pReqsPendingTail->pNext = pTaskHead;
459 }
460
461 /* Update the tail. */
462 while (pTaskHead->pNext)
463 pTaskHead = pTaskHead->pNext;
464
465 pEndpoint->AioMgr.pReqsPendingTail = pTaskHead;
466
467 pdmacFileAioMgrNormalBalanceLoad(pAioMgr);
468 }
469
470 return rc;
471}
472
473/**
474 * Adds all pending requests for the given endpoint
475 * until a flush request is encountered or there is no
476 * request anymore.
477 *
478 * @returns VBox status code.
479 * @param pAioMgr The async I/O manager for the endpoint
480 * @param pEndpoint The endpoint to get the requests from.
481 */
482static int pdmacFileAioMgrNormalQueueReqs(PPDMACEPFILEMGR pAioMgr,
483 PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint)
484{
485 int rc = VINF_SUCCESS;
486 PPDMACTASKFILE pTasksHead = NULL;
487
488 AssertMsg(pEndpoint->enmState == PDMASYNCCOMPLETIONENDPOINTFILESTATE_ACTIVE,
489 ("Trying to process request lists of a non active endpoint!\n"));
490
491 Assert(!pEndpoint->pFlushReq);
492
493 /* Check the pending list first */
494 if (pEndpoint->AioMgr.pReqsPendingHead)
495 {
496 pTasksHead = pEndpoint->AioMgr.pReqsPendingHead;
497 /*
498 * Clear the list as the processing routine will insert them into the list
499 * again if it gets a flush request.
500 */
501 pEndpoint->AioMgr.pReqsPendingHead = NULL;
502 pEndpoint->AioMgr.pReqsPendingTail = NULL;
503 rc = pdmacFileAioMgrNormalProcessTaskList(pTasksHead, pAioMgr, pEndpoint);
504 AssertRC(rc);
505 }
506
507 if (!pEndpoint->pFlushReq)
508 {
509 /* Now the request queue. */
510 pTasksHead = pdmacFileEpGetNewTasks(pEndpoint);
511 if (pTasksHead)
512 {
513 rc = pdmacFileAioMgrNormalProcessTaskList(pTasksHead, pAioMgr, pEndpoint);
514 AssertRC(rc);
515 }
516 }
517
518 return rc;
519}
520
521static int pdmacFileAioMgrNormalProcessBlockingEvent(PPDMACEPFILEMGR pAioMgr)
522{
523 int rc = VINF_SUCCESS;
524 bool fNotifyWaiter = false;
525
526 Assert(pAioMgr->fBlockingEventPending);
527
528 switch (pAioMgr->enmBlockingEvent)
529 {
530 case PDMACEPFILEAIOMGRBLOCKINGEVENT_ADD_ENDPOINT:
531 {
532 PPDMASYNCCOMPLETIONENDPOINTFILE pEndpointNew = (PPDMASYNCCOMPLETIONENDPOINTFILE)ASMAtomicReadPtr((void * volatile *)&pAioMgr->BlockingEventData.AddEndpoint.pEndpoint);
533 AssertMsg(VALID_PTR(pEndpointNew), ("Adding endpoint event without a endpoint to add\n"));
534
535 pEndpointNew->enmState = PDMASYNCCOMPLETIONENDPOINTFILESTATE_ACTIVE;
536
537 pEndpointNew->AioMgr.pEndpointNext = pAioMgr->pEndpointsHead;
538 pEndpointNew->AioMgr.pEndpointPrev = NULL;
539 if (pAioMgr->pEndpointsHead)
540 pAioMgr->pEndpointsHead->AioMgr.pEndpointPrev = pEndpointNew;
541 pAioMgr->pEndpointsHead = pEndpointNew;
542
543 /* Assign the completion point to this file. */
544 rc = RTFileAioCtxAssociateWithFile(pAioMgr->hAioCtx, pEndpointNew->File);
545 fNotifyWaiter = true;
546 pAioMgr->cEndpoints++;
547 break;
548 }
549 case PDMACEPFILEAIOMGRBLOCKINGEVENT_REMOVE_ENDPOINT:
550 {
551 PPDMASYNCCOMPLETIONENDPOINTFILE pEndpointRemove = (PPDMASYNCCOMPLETIONENDPOINTFILE)ASMAtomicReadPtr((void * volatile *)&pAioMgr->BlockingEventData.RemoveEndpoint.pEndpoint);
552 AssertMsg(VALID_PTR(pEndpointRemove), ("Removing endpoint event without a endpoint to remove\n"));
553
554 pEndpointRemove->enmState = PDMASYNCCOMPLETIONENDPOINTFILESTATE_REMOVING;
555 fNotifyWaiter = !pdmacFileAioMgrNormalRemoveEndpoint(pEndpointRemove);
556 break;
557 }
558 case PDMACEPFILEAIOMGRBLOCKINGEVENT_CLOSE_ENDPOINT:
559 {
560 PPDMASYNCCOMPLETIONENDPOINTFILE pEndpointClose = (PPDMASYNCCOMPLETIONENDPOINTFILE)ASMAtomicReadPtr((void * volatile *)&pAioMgr->BlockingEventData.CloseEndpoint.pEndpoint);
561 AssertMsg(VALID_PTR(pEndpointClose), ("Close endpoint event without a endpoint to close\n"));
562
563 /* Make sure all tasks finished. Process the queues a last time first. */
564 rc = pdmacFileAioMgrNormalQueueReqs(pAioMgr, pEndpointClose);
565 AssertRC(rc);
566
567 pEndpointClose->enmState = PDMASYNCCOMPLETIONENDPOINTFILESTATE_CLOSING;
568 fNotifyWaiter = !pdmacFileAioMgrNormalRemoveEndpoint(pEndpointClose);
569 break;
570 }
571 case PDMACEPFILEAIOMGRBLOCKINGEVENT_SHUTDOWN:
572 {
573 pAioMgr->enmState = PDMACEPFILEMGRSTATE_SHUTDOWN;
574 if (!pAioMgr->cRequestsActive)
575 fNotifyWaiter = true;
576 break;
577 }
578 case PDMACEPFILEAIOMGRBLOCKINGEVENT_SUSPEND:
579 {
580 pAioMgr->enmState = PDMACEPFILEMGRSTATE_SUSPENDING;
581 break;
582 }
583 case PDMACEPFILEAIOMGRBLOCKINGEVENT_RESUME:
584 {
585 pAioMgr->enmState = PDMACEPFILEMGRSTATE_RUNNING;
586 fNotifyWaiter = true;
587 break;
588 }
589 default:
590 AssertReleaseMsgFailed(("Invalid event type %d\n", pAioMgr->enmBlockingEvent));
591 }
592
593 if (fNotifyWaiter)
594 {
595 ASMAtomicWriteBool(&pAioMgr->fBlockingEventPending, false);
596 pAioMgr->enmBlockingEvent = PDMACEPFILEAIOMGRBLOCKINGEVENT_INVALID;
597
598 /* Release the waiting thread. */
599 rc = RTSemEventSignal(pAioMgr->EventSemBlock);
600 AssertRC(rc);
601 }
602
603 return rc;
604}
605
606/** Helper macro for checking for error codes. */
607#define CHECK_RC(pAioMgr, rc) \
608 if (RT_FAILURE(rc)) \
609 {\
610 int rc2 = pdmacFileAioMgrNormalErrorHandler(pAioMgr, rc, RT_SRC_POS);\
611 return rc2;\
612 }
613
614/**
615 * The normal I/O manager using the RTFileAio* API
616 *
617 * @returns VBox status code.
618 * @param ThreadSelf Handle of the thread.
619 * @param pvUser Opaque user data.
620 */
621int pdmacFileAioMgrNormal(RTTHREAD ThreadSelf, void *pvUser)
622{
623 int rc = VINF_SUCCESS;
624 PPDMACEPFILEMGR pAioMgr = (PPDMACEPFILEMGR)pvUser;
625 uint64_t uMillisEnd = RTTimeMilliTS() + PDMACEPFILEMGR_LOAD_UPDATE_PERIOD;
626
627 while ( (pAioMgr->enmState == PDMACEPFILEMGRSTATE_RUNNING)
628 || (pAioMgr->enmState == PDMACEPFILEMGRSTATE_SUSPENDING))
629 {
630 ASMAtomicWriteBool(&pAioMgr->fWaitingEventSem, true);
631 if (!ASMAtomicReadBool(&pAioMgr->fWokenUp))
632 rc = RTSemEventWait(pAioMgr->EventSem, RT_INDEFINITE_WAIT);
633 ASMAtomicWriteBool(&pAioMgr->fWaitingEventSem, false);
634 AssertRC(rc);
635
636 LogFlow(("Got woken up\n"));
637 ASMAtomicWriteBool(&pAioMgr->fWokenUp, false);
638
639 /* Check for an external blocking event first. */
640 if (pAioMgr->fBlockingEventPending)
641 {
642 rc = pdmacFileAioMgrNormalProcessBlockingEvent(pAioMgr);
643 CHECK_RC(pAioMgr, rc);
644 }
645
646 if (RT_LIKELY(pAioMgr->enmState == PDMACEPFILEMGRSTATE_RUNNING))
647 {
648 /* Check the assigned endpoints for new tasks if there isn't a flush request active at the moment. */
649 PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint = pAioMgr->pEndpointsHead;
650
651 while (pEndpoint)
652 {
653 if (!pEndpoint->pFlushReq && (pEndpoint->enmState == PDMASYNCCOMPLETIONENDPOINTFILESTATE_ACTIVE))
654 {
655 rc = pdmacFileAioMgrNormalQueueReqs(pAioMgr, pEndpoint);
656 CHECK_RC(pAioMgr, rc);
657 }
658
659 pEndpoint = pEndpoint->AioMgr.pEndpointNext;
660 }
661
662 while (pAioMgr->cRequestsActive)
663 {
664 RTFILEAIOREQ apReqs[20];
665 uint32_t cReqsCompleted = 0;
666 size_t cReqsWait;
667
668 if (pAioMgr->cRequestsActive > RT_ELEMENTS(apReqs))
669 cReqsWait = RT_ELEMENTS(apReqs);
670 else
671 cReqsWait = pAioMgr->cRequestsActive;
672
673 rc = RTFileAioCtxWait(pAioMgr->hAioCtx,
674 cReqsWait,
675 RT_INDEFINITE_WAIT, apReqs,
676 RT_ELEMENTS(apReqs), &cReqsCompleted);
677 if (RT_FAILURE(rc) && (rc != VERR_INTERRUPTED))
678 CHECK_RC(pAioMgr, rc);
679
680 for (uint32_t i = 0; i < cReqsCompleted; i++)
681 {
682 size_t cbTransfered = 0;
683 int rcReq = RTFileAioReqGetRC(apReqs[i], &cbTransfered);
684 PPDMACTASKFILE pTask = (PPDMACTASKFILE)RTFileAioReqGetUser(apReqs[i]);
685
686 pEndpoint = pTask->pEndpoint;
687
688 AssertMsg( RT_SUCCESS(rcReq)
689 && (cbTransfered == pTask->DataSeg.cbSeg),
690 ("Task didn't completed successfully (rc=%Rrc) or was incomplete (cbTransfered=%u)\n", rc, cbTransfered));
691
692 if (pTask->fBounceBuffer)
693 {
694 if (pTask->enmTransferType == PDMACTASKFILETRANSFER_READ)
695 memcpy(pTask->DataSeg.pvSeg, pTask->pvBounceBuffer, pTask->DataSeg.cbSeg);
696
697 RTMemPageFree(pTask->pvBounceBuffer);
698 }
699
700 /* Put the entry on the free array */
701 pAioMgr->pahReqsFree[pAioMgr->iFreeEntryNext] = apReqs[i];
702 pAioMgr->iFreeEntryNext = (pAioMgr->iFreeEntryNext + 1) %pAioMgr->cReqEntries;
703
704 pAioMgr->cRequestsActive--;
705 pEndpoint->AioMgr.cReqsProcessed++;
706
707 /* Call completion callback */
708 pTask->pfnCompleted(pTask, pTask->pvUser);
709 pdmacFileTaskFree(pEndpoint, pTask);
710
711 /*
712 * If there is no request left on the endpoint but a flush request is set
713 * it completed now and we notify the owner.
714 * Furthermore we look for new requests and continue.
715 */
716 if (!pEndpoint->AioMgr.cRequestsActive && pEndpoint->pFlushReq)
717 {
718 /* Call completion callback */
719 pTask = pEndpoint->pFlushReq;
720 pEndpoint->pFlushReq = NULL;
721
722 AssertMsg(pTask->pEndpoint == pEndpoint, ("Endpoint of the flush request does not match assigned one\n"));
723
724 pTask->pfnCompleted(pTask, pTask->pvUser);
725 pdmacFileTaskFree(pEndpoint, pTask);
726 }
727
728 if (pEndpoint->enmState == PDMASYNCCOMPLETIONENDPOINTFILESTATE_ACTIVE)
729 {
730 if (!pEndpoint->pFlushReq)
731 {
732 /* Check if there are events on the endpoint. */
733 rc = pdmacFileAioMgrNormalQueueReqs(pAioMgr, pEndpoint);
734 CHECK_RC(pAioMgr, rc);
735 }
736 }
737 else if (!pEndpoint->AioMgr.cRequestsActive)
738 {
739 /* Reopen the file so that the new endpoint can reassociate with the file */
740 RTFileClose(pEndpoint->File);
741 rc = RTFileOpen(&pEndpoint->File, pEndpoint->Core.pszUri, pEndpoint->fFlags);
742 AssertRC(rc);
743
744 if (pEndpoint->AioMgr.fMoving)
745 {
746 pEndpoint->AioMgr.fMoving = false;
747 pdmacFileAioMgrAddEndpoint(pEndpoint->AioMgr.pAioMgrDst, pEndpoint);
748 }
749 else
750 {
751 Assert(pAioMgr->fBlockingEventPending);
752 ASMAtomicWriteBool(&pAioMgr->fBlockingEventPending, false);
753
754 /* Release the waiting thread. */
755 rc = RTSemEventSignal(pAioMgr->EventSemBlock);
756 AssertRC(rc);
757 }
758 }
759 }
760
761 /* Check for an external blocking event before we go to sleep again. */
762 if (pAioMgr->fBlockingEventPending)
763 {
764 rc = pdmacFileAioMgrNormalProcessBlockingEvent(pAioMgr);
765 CHECK_RC(pAioMgr, rc);
766 }
767
768 /* Update load statistics. */
769 uint64_t uMillisCurr = RTTimeMilliTS();
770 if (uMillisCurr > uMillisEnd)
771 {
772 PPDMASYNCCOMPLETIONENDPOINTFILE pEndpointCurr = pAioMgr->pEndpointsHead;
773
774 /* Calculate timespan. */
775 uMillisCurr -= uMillisEnd;
776
777 while (pEndpointCurr)
778 {
779 pEndpointCurr->AioMgr.cReqsPerSec = pEndpointCurr->AioMgr.cReqsProcessed / (uMillisCurr + PDMACEPFILEMGR_LOAD_UPDATE_PERIOD);
780 pEndpointCurr->AioMgr.cReqsProcessed = 0;
781 pEndpointCurr = pEndpointCurr->AioMgr.pEndpointNext;
782 }
783
784 /* Set new update interval */
785 uMillisEnd = RTTimeMilliTS() + PDMACEPFILEMGR_LOAD_UPDATE_PERIOD;
786 }
787 }
788 }
789 }
790
791 return rc;
792}
793
794#undef CHECK_RC
795
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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