VirtualBox

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

Last change on this file since 21990 was 21496, checked in by vboxsync, 15 years ago

VMM/PDMAsyncCompletion: Add basic working manager using RTFileAio API (Only tested on Linux) yet without caching. Splitted normal and failsafe manager into separate files to make the code easier to read

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.6 KB
Line 
1/* $Id: PDMAsyncCompletionFileNormal.cpp 21496 2009-07-10 20:16:09Z 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.virtualbox.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/file.h>
25#include <iprt/mem.h>
26#include <iprt/string.h>
27#include <VBox/log.h>
28
29#include "PDMAsyncCompletionFileInternal.h"
30
31int pdmacFileAioMgrNormalInit(PPDMACEPFILEMGR pAioMgr)
32{
33 int rc = VINF_SUCCESS;
34
35 rc = RTFileAioCtxCreate(&pAioMgr->hAioCtx, RTFILEAIO_UNLIMITED_REQS);
36 if (rc == VERR_OUT_OF_RANGE)
37 rc = RTFileAioCtxCreate(&pAioMgr->hAioCtx, 128); /* @todo: Find better solution wrt. the request number*/
38
39 return rc;
40}
41
42void pdmacFileAioMgrNormalDestroy(PPDMACEPFILEMGR pAioMgr)
43{
44 RTFileAioCtxDestroy(pAioMgr->hAioCtx);
45}
46
47/**
48 * Error handler which will create the failsafe managers and destroy the failed I/O manager.
49 *
50 * @returns VBox status code
51 * @param pAioMgr The I/O manager the error ocurred on.
52 * @param rc The error code.
53 */
54static int pdmacFileAioMgrNormalErrorHandler(PPDMACEPFILEMGR pAioMgr, int rc)
55{
56 AssertMsgFailed(("Implement\n"));
57 return VINF_SUCCESS;
58}
59
60static int pdmacFileAioMgrNormalProcessTaskList(PPDMASYNCCOMPLETIONTASK pTaskHead,
61 PPDMACEPFILEMGR pAioMgr,
62 PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint)
63{
64 RTFILEAIOREQ apReqs[20];
65 unsigned cRequests = 0;
66 int rc = VINF_SUCCESS;
67 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pEndpoint->Core.pEpClass;
68
69 /* Go through the list and queue the requests until we get a flush request */
70 while (pTaskHead && !pEndpoint->pFlushReq)
71 {
72 PPDMASYNCCOMPLETIONTASKFILE pTaskFile = (PPDMASYNCCOMPLETIONTASKFILE)pTaskHead;
73
74 pTaskHead = pTaskHead->pNext;
75
76 switch (pTaskFile->enmTransferType)
77 {
78 case PDMACTASKFILETRANSFER_FLUSH:
79 {
80 /* If there is no data transfer request this flush request finished immediately. */
81 if (!pEndpoint->AioMgr.cRequestsActive)
82 {
83 /* Task completed. Notify owner */
84 pdmR3AsyncCompletionCompleteTask(&pTaskFile->Core);
85 }
86 else
87 {
88 pEndpoint->pFlushReq = pTaskFile;
89
90 if (pTaskHead)
91 {
92 /* Add the rest of the tasks to the pending list */
93 if (!pEndpoint->AioMgr.pReqsPendingHead)
94 {
95 Assert(!pEndpoint->AioMgr.pReqsPendingTail);
96 pEndpoint->AioMgr.pReqsPendingHead = pTaskHead;
97 }
98 else
99 {
100 Assert(pEndpoint->AioMgr.pReqsPendingTail);
101 pEndpoint->AioMgr.pReqsPendingTail->pNext = pTaskHead;
102 }
103
104 /* Update the tail. */
105 while (pTaskHead->pNext)
106 pTaskHead = pTaskHead->pNext;
107
108 pEndpoint->AioMgr.pReqsPendingTail = pTaskHead;
109 }
110 }
111 break;
112 }
113 case PDMACTASKFILETRANSFER_READ:
114 case PDMACTASKFILETRANSFER_WRITE:
115 {
116 PPDMACTASKFILESEG pSeg = pTaskFile->u.DataTransfer.pSegmentsHead;
117 RTFOFF offCurr = pTaskFile->u.DataTransfer.off;
118 size_t cbTransfer = pTaskFile->u.DataTransfer.cbTransfer;
119
120 AssertPtr(pSeg);
121
122 do
123 {
124 void *pvBuf = pSeg->DataSeg.pvSeg;
125
126 rc = RTFileAioReqCreate(&pSeg->hAioReq);
127 AssertRC(rc);
128
129 /* Check if the alignment requirements are met. */
130 if ((pEpClassFile->uBitmaskAlignment & (RTR3UINTPTR)pvBuf) != (RTR3UINTPTR)pvBuf)
131 {
132 /* Create bounce buffer. */
133 pSeg->fBounceBuffer = true;
134
135 /** @todo: I think we need something like a RTMemAllocAligned method here.
136 * Current assumption is that the maximum alignment is 4096byte
137 * (GPT disk on Windows)
138 * so we can use RTMemPageAlloc here.
139 */
140 pSeg->pvBounceBuffer = RTMemPageAlloc(pSeg->DataSeg.cbSeg);
141 AssertPtr(pSeg->pvBounceBuffer);
142 pvBuf = pSeg->pvBounceBuffer;
143
144 if (pTaskFile->enmTransferType == PDMACTASKFILETRANSFER_WRITE)
145 memcpy(pvBuf, pSeg->DataSeg.pvSeg, pSeg->DataSeg.cbSeg);
146 }
147 else
148 pSeg->fBounceBuffer = false;
149
150 AssertMsg((pEpClassFile->uBitmaskAlignment & (RTR3UINTPTR)pvBuf) == (RTR3UINTPTR)pvBuf,
151 ("AIO: Alignment restrictions not met!\n"));
152
153 if (pTaskFile->enmTransferType == PDMACTASKFILETRANSFER_WRITE)
154 rc = RTFileAioReqPrepareWrite(pSeg->hAioReq, pEndpoint->File,
155 offCurr, pvBuf, pSeg->DataSeg.cbSeg, pSeg);
156 else
157 rc = RTFileAioReqPrepareRead(pSeg->hAioReq, pEndpoint->File,
158 offCurr, pvBuf, pSeg->DataSeg.cbSeg, pSeg);
159 AssertRC(rc);
160
161 apReqs[cRequests] = pSeg->hAioReq;
162 cRequests++;
163 if (cRequests == RT_ELEMENTS(apReqs))
164 {
165 pAioMgr->cRequestsActive += cRequests;
166 rc = RTFileAioCtxSubmit(pAioMgr->hAioCtx, apReqs, cRequests);
167 if (RT_FAILURE(rc))
168 {
169 /* @todo implement */
170 AssertMsgFailed(("Implement\n"));
171 }
172
173 cRequests = 0;
174 }
175
176 offCurr += pSeg->DataSeg.cbSeg;
177 cbTransfer -= pSeg->DataSeg.cbSeg;
178 pSeg = pSeg->pNext;
179 } while (pSeg && RT_SUCCESS(rc));
180
181 AssertMsg(!cbTransfer, ("Incomplete transfer cbTransfer=%u\n", cbTransfer));
182
183 break;
184 }
185 default:
186 AssertMsgFailed(("Invalid transfer type %d\n", pTaskFile->enmTransferType));
187 }
188 }
189
190 if (cRequests)
191 {
192 pAioMgr->cRequestsActive += cRequests;
193 rc = RTFileAioCtxSubmit(pAioMgr->hAioCtx, apReqs, cRequests);
194 if (RT_FAILURE(rc))
195 {
196 /* @todo implement */
197 AssertMsgFailed(("Implement\n"));
198 }
199 }
200
201 return rc;
202}
203
204/**
205 * Adds all pending requests for the given endpoint
206 * until a flush request is encountered or there is no
207 * request anymore.
208 *
209 * @returns VBox status code.
210 * @param pAioMgr The async I/O manager for the endpoint
211 * @param pEndpoint The endpoint to get the requests from.
212 */
213static int pdmacFileAioMgrNormalQueueReqs(PPDMACEPFILEMGR pAioMgr,
214 PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint)
215{
216 int rc = VINF_SUCCESS;
217 PPDMASYNCCOMPLETIONTASK pTasksHead = NULL;
218
219 Assert(!pEndpoint->pFlushReq);
220
221 /* Check the pending list first */
222 if (pEndpoint->AioMgr.pReqsPendingHead)
223 {
224 pTasksHead = pEndpoint->AioMgr.pReqsPendingHead;
225 /*
226 * Clear the list as the processing routine will insert them into the list
227 * again if it gets aflush request.
228 */
229 pEndpoint->AioMgr.pReqsPendingHead = NULL;
230 pEndpoint->AioMgr.pReqsPendingTail = NULL;
231 rc = pdmacFileAioMgrNormalProcessTaskList(pTasksHead, pAioMgr, pEndpoint);
232 AssertRC(rc);
233 }
234
235 if (!pEndpoint->pFlushReq)
236 {
237 /* Now the request queue. */
238 pTasksHead = pdmacFileEpGetNewTasks(pEndpoint);
239 if (pTasksHead)
240 {
241 rc = pdmacFileAioMgrNormalProcessTaskList(pTasksHead, pAioMgr, pEndpoint);
242 AssertRC(rc);
243 }
244 }
245
246 return rc;
247}
248
249static int pdmacFileAioMgrNormalProcessBlockingEvent(PPDMACEPFILEMGR pAioMgr)
250{
251 int rc = VINF_SUCCESS;
252 bool fNotifyWaiter = true;
253
254 Assert(pAioMgr->fBlockingEventPending);
255
256 switch (pAioMgr->enmBlockingEvent)
257 {
258 case PDMACEPFILEAIOMGRBLOCKINGEVENT_ADD_ENDPOINT:
259 {
260 PPDMASYNCCOMPLETIONENDPOINTFILE pEndpointNew = (PPDMASYNCCOMPLETIONENDPOINTFILE)ASMAtomicReadPtr((void * volatile *)&pAioMgr->BlockingEventData.AddEndpoint.pEndpoint);
261 AssertMsg(VALID_PTR(pEndpointNew), ("Adding endpoint event without a endpoint to add\n"));
262
263 pEndpointNew->AioMgr.pEndpointNext = pAioMgr->pEndpointsHead;
264 pEndpointNew->AioMgr.pEndpointPrev = NULL;
265 if (pAioMgr->pEndpointsHead)
266 pAioMgr->pEndpointsHead->AioMgr.pEndpointPrev = pEndpointNew;
267 pAioMgr->pEndpointsHead = pEndpointNew;
268
269 /* Assign the completion point to this file. */
270 rc = RTFileAioCtxAssociateWithFile(pAioMgr->hAioCtx, pEndpointNew->File);
271 break;
272 }
273 case PDMACEPFILEAIOMGRBLOCKINGEVENT_REMOVE_ENDPOINT:
274 {
275 PPDMASYNCCOMPLETIONENDPOINTFILE pEndpointRemove = (PPDMASYNCCOMPLETIONENDPOINTFILE)ASMAtomicReadPtr((void * volatile *)&pAioMgr->BlockingEventData.RemoveEndpoint.pEndpoint);
276 AssertMsg(VALID_PTR(pEndpointRemove), ("Removing endpoint event without a endpoint to remove\n"));
277
278 PPDMASYNCCOMPLETIONENDPOINTFILE pPrev = pEndpointRemove->AioMgr.pEndpointPrev;
279 PPDMASYNCCOMPLETIONENDPOINTFILE pNext = pEndpointRemove->AioMgr.pEndpointNext;
280
281 if (pPrev)
282 pPrev->AioMgr.pEndpointNext = pNext;
283 else
284 pAioMgr->pEndpointsHead = pNext;
285
286 if (pNext)
287 pNext->AioMgr.pEndpointPrev = pPrev;
288
289 /* Make sure that there is no request pending on this manager for the endpoint. */
290 if (!pEndpointRemove->AioMgr.cRequestsActive)
291 {
292 Assert(!pEndpointRemove->pFlushReq);
293
294 /* Reopen the file so that the new endpoint can reassociate with the file */
295 RTFileClose(pEndpointRemove->File);
296 rc = RTFileOpen(&pEndpointRemove->File, pEndpointRemove->pszFilename, pEndpointRemove->fFlags);
297 AssertRC(rc);
298 }
299 else
300 {
301 /* Mark the endpoint as removed and wait until all pending requests are finished. */
302 pEndpointRemove->fRemovedOrClosed = true;
303
304 /* We can't release the waiting thread here. */
305 fNotifyWaiter = false;
306 }
307 break;
308 }
309 case PDMACEPFILEAIOMGRBLOCKINGEVENT_CLOSE_ENDPOINT:
310 {
311 PPDMASYNCCOMPLETIONENDPOINTFILE pEndpointClose = (PPDMASYNCCOMPLETIONENDPOINTFILE)ASMAtomicReadPtr((void * volatile *)&pAioMgr->BlockingEventData.CloseEndpoint.pEndpoint);
312 AssertMsg(VALID_PTR(pEndpointClose), ("Close endpoint event without a endpoint to close\n"));
313
314 /* Make sure all tasks finished. Process the queues a last time first. */
315 rc = pdmacFileAioMgrNormalQueueReqs(pAioMgr, pEndpointClose);
316 AssertRC(rc);
317
318 if (!pEndpointClose->AioMgr.cRequestsActive)
319 {
320 Assert(!pEndpointClose->pFlushReq);
321
322 /* Reopen the file to deassociate it from the endpoint. */
323 RTFileClose(pEndpointClose->File);
324 rc = RTFileOpen(&pEndpointClose->File, pEndpointClose->pszFilename, pEndpointClose->fFlags);
325 AssertRC(rc);
326 }
327 else
328 {
329 /* Mark the endpoint as removed and wait until all pending requests are finished. */
330 pEndpointClose->fRemovedOrClosed = true;
331
332 /* We can't release the waiting thread here. */
333 fNotifyWaiter = false;
334 }
335 break;
336 }
337 case PDMACEPFILEAIOMGRBLOCKINGEVENT_SHUTDOWN:
338 break;
339 case PDMACEPFILEAIOMGRBLOCKINGEVENT_SUSPEND:
340 break;
341 default:
342 AssertReleaseMsgFailed(("Invalid event type %d\n", pAioMgr->enmBlockingEvent));
343 }
344
345 if (fNotifyWaiter)
346 {
347 ASMAtomicWriteBool(&pAioMgr->fBlockingEventPending, false);
348
349 /* Release the waiting thread. */
350 rc = RTSemEventSignal(pAioMgr->EventSemBlock);
351 AssertRC(rc);
352 }
353
354 return rc;
355}
356
357/** Helper macro for checking for error codes. */
358#define CHECK_RC(pAioMgr, rc) \
359 if (RT_FAILURE(rc)) \
360 {\
361 int rc2 = pdmacFileAioMgrNormalErrorHandler(pAioMgr, rc);\
362 return rc2;\
363 }
364
365/**
366 * The normal I/O manager using the RTFileAio* API
367 *
368 * @returns VBox status code.
369 * @param ThreadSelf Handle of the thread.
370 * @param pvUser Opaque user data.
371 */
372int pdmacFileAioMgrNormal(RTTHREAD ThreadSelf, void *pvUser)
373{
374 int rc = VINF_SUCCESS;
375 PPDMACEPFILEMGR pAioMgr = (PPDMACEPFILEMGR)pvUser;
376
377 while (!pAioMgr->fShutdown)
378 {
379 ASMAtomicWriteBool(&pAioMgr->fWaitingEventSem, true);
380 if (!ASMAtomicReadBool(&pAioMgr->fWokenUp))
381 rc = RTSemEventWait(pAioMgr->EventSem, RT_INDEFINITE_WAIT);
382 ASMAtomicWriteBool(&pAioMgr->fWaitingEventSem, false);
383 AssertRC(rc);
384
385 LogFlow(("Got woken up\n"));
386
387 /* Check for an external blocking event first. */
388 if (pAioMgr->fBlockingEventPending)
389 {
390 rc = pdmacFileAioMgrNormalProcessBlockingEvent(pAioMgr);
391 CHECK_RC(pAioMgr, rc);
392 }
393
394 /* Check the assigned endpoints for new tasks if there isn't a flush request active at the moment. */
395 PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint = pAioMgr->pEndpointsHead;
396
397 while (pEndpoint)
398 {
399 if (!pEndpoint->pFlushReq)
400 {
401 rc = pdmacFileAioMgrNormalQueueReqs(pAioMgr, pEndpoint);
402 CHECK_RC(pAioMgr, rc);
403 }
404
405 pEndpoint = pEndpoint->AioMgr.pEndpointNext;
406 }
407
408 while (pAioMgr->cRequestsActive)
409 {
410 RTFILEAIOREQ apReqs[20];
411 uint32_t cReqsCompleted = 0;
412
413 rc = RTFileAioCtxWait(pAioMgr->hAioCtx, 1, RT_INDEFINITE_WAIT, apReqs,
414 RT_ELEMENTS(apReqs), &cReqsCompleted);
415 CHECK_RC(pAioMgr, rc);
416
417 for (uint32_t i = 0; i < cReqsCompleted; i++)
418 {
419 size_t cbTransfered = 0;
420 int rcReq = RTFileAioReqGetRC(apReqs[i], &cbTransfered);
421 PPDMACTASKFILESEG pTaskSeg = (PPDMACTASKFILESEG)RTFileAioReqGetUser(apReqs[i]);
422 PPDMASYNCCOMPLETIONTASKFILE pTask = pTaskSeg->pTask;
423 PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint = (PPDMASYNCCOMPLETIONENDPOINTFILE)pTask->Core.pEndpoint;
424
425 AssertMsg( RT_SUCCESS(rcReq)
426 && (cbTransfered == pTaskSeg->DataSeg.cbSeg),
427 ("Task didn't completed successfully (rc=%Rrc) or was incomplete (cbTransfered=%u)\n", rc, cbTransfered));
428
429 if (pTaskSeg->fBounceBuffer)
430 {
431 if (pTask->enmTransferType == PDMACTASKFILETRANSFER_READ)
432 memcpy(pTaskSeg->DataSeg.pvSeg, pTaskSeg->pvBounceBuffer, pTaskSeg->DataSeg.cbSeg);
433
434 RTMemPageFree(pTaskSeg->pvBounceBuffer);
435 }
436
437 pTask->u.DataTransfer.cSegments--;
438 pAioMgr->cRequestsActive--;
439 if (!pTask->u.DataTransfer.cSegments)
440 {
441 /* Free all segments. */
442 PPDMACTASKFILESEG pSegCurr = pTask->u.DataTransfer.pSegmentsHead;
443 while (pSegCurr)
444 {
445 PPDMACTASKFILESEG pSegFree = pSegCurr;
446
447 pSegCurr = pSegCurr->pNext;
448
449 RTFileAioReqDestroy(pSegFree->hAioReq);
450 pdmacFileSegmentFree(pEndpoint, pSegFree);
451 }
452
453 pEndpoint->AioMgr.cRequestsActive--;
454
455 /* Task completed. Notify owner */
456 pdmR3AsyncCompletionCompleteTask(&pTask->Core);
457 }
458
459 /*
460 * If there is no request left on the endpoint but a flush request is set
461 * it completed now and we notify the owner.
462 * Furthermore we look for new requests and continue.
463 */
464 if (!pEndpoint->AioMgr.cRequestsActive && pEndpoint->pFlushReq)
465 {
466 pdmR3AsyncCompletionCompleteTask(&pEndpoint->pFlushReq->Core);
467 pEndpoint->pFlushReq = NULL;
468 }
469
470 if (!pEndpoint->fRemovedOrClosed)
471 {
472 if (!pEndpoint->pFlushReq)
473 {
474 /* Check if there are events on the endpoint. */
475 rc = pdmacFileAioMgrNormalQueueReqs(pAioMgr, pEndpoint);
476 CHECK_RC(pAioMgr, rc);
477 }
478 }
479 else if (!pEndpoint->AioMgr.cRequestsActive)
480 {
481 pEndpoint->fRemovedOrClosed = false;
482
483 Assert(pAioMgr->fBlockingEventPending);
484 ASMAtomicWriteBool(&pAioMgr->fBlockingEventPending, false);
485
486 /* Release the waiting thread. */
487 rc = RTSemEventSignal(pAioMgr->EventSemBlock);
488 AssertRC(rc);
489 }
490 }
491
492 /* Check for an external blocking event before we go to sleep again. */
493 if (pAioMgr->fBlockingEventPending)
494 {
495 rc = pdmacFileAioMgrNormalProcessBlockingEvent(pAioMgr);
496 CHECK_RC(pAioMgr, rc);
497 }
498 }
499 }
500
501 return rc;
502}
503
504#undef CHECK_RC
505
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