VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/PDMAsyncCompletionFile.cpp@ 41783

Last change on this file since 41783 was 41469, checked in by vboxsync, 13 years ago

AsyncCompletion: enhance injectdelay debugger command to delay multiple requests at once

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 43.8 KB
Line 
1/* $Id: PDMAsyncCompletionFile.cpp 41469 2012-05-28 22:58:48Z vboxsync $ */
2/** @file
3 * PDM Async I/O - Transport data asynchronous in R3 using EMT.
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/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#define LOG_GROUP LOG_GROUP_PDM_ASYNC_COMPLETION
23#define RT_STRICT
24#include "PDMInternal.h"
25#include <VBox/vmm/pdm.h>
26#include <VBox/vmm/mm.h>
27#include <VBox/vmm/vm.h>
28#include <VBox/err.h>
29#include <VBox/log.h>
30#include <VBox/dbg.h>
31#include <VBox/vmm/uvm.h>
32
33#include <iprt/asm.h>
34#include <iprt/assert.h>
35#include <iprt/critsect.h>
36#include <iprt/env.h>
37#include <iprt/file.h>
38#include <iprt/mem.h>
39#include <iprt/semaphore.h>
40#include <iprt/string.h>
41#include <iprt/thread.h>
42#include <iprt/path.h>
43
44#include "PDMAsyncCompletionFileInternal.h"
45
46/*******************************************************************************
47* Internal Functions *
48*******************************************************************************/
49#ifdef VBOX_WITH_DEBUGGER
50static DECLCALLBACK(int) pdmacEpFileErrorInject(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR pArgs, unsigned cArgs);
51# ifdef PDM_ASYNC_COMPLETION_FILE_WITH_DELAY
52static DECLCALLBACK(int) pdmacEpFileDelayInject(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR pArgs, unsigned cArgs);
53# endif
54#endif
55
56/*******************************************************************************
57* Global Variables *
58*******************************************************************************/
59#ifdef VBOX_WITH_DEBUGGER
60static const DBGCVARDESC g_aInjectErrorArgs[] =
61{
62 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
63 { 1, 1, DBGCVAR_CAT_STRING, 0, "direction", "write/read." },
64 { 1, 1, DBGCVAR_CAT_STRING, 0, "filename", "Filename." },
65 { 1, 1, DBGCVAR_CAT_NUMBER, 0, "errcode", "VBox status code." },
66};
67
68# ifdef PDM_ASYNC_COMPLETION_FILE_WITH_DELAY
69static const DBGCVARDESC g_aInjectDelayArgs[] =
70{
71 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
72 { 1, 1, DBGCVAR_CAT_STRING, 0, "direction", "write|read|flush|any." },
73 { 1, 1, DBGCVAR_CAT_STRING, 0, "filename", "Filename." },
74 { 1, 1, DBGCVAR_CAT_NUMBER, 0, "delay", "Delay in milliseconds." },
75 { 1, 1, DBGCVAR_CAT_NUMBER, 0, "reqs", "Number of requests to delay." },
76};
77# endif
78
79/** Command descriptors. */
80static const DBGCCMD g_aCmds[] =
81{
82 /* pszCmd, cArgsMin, cArgsMax, paArgDesc, cArgDescs, fFlags, pfnHandler pszSyntax, ....pszDescription */
83 { "injecterror", 3, 3, &g_aInjectErrorArgs[0], 3, 0, pdmacEpFileErrorInject, "", "Inject error into I/O subsystem." }
84# ifdef PDM_ASYNC_COMPLETION_FILE_WITH_DELAY
85 ,{ "injectdelay", 4, 4, &g_aInjectDelayArgs[0], 4, 0, pdmacEpFileDelayInject, "", "Inject a delay of a request." }
86# endif
87};
88#endif
89
90
91/**
92 * Frees a task.
93 *
94 * @returns nothing.
95 * @param pEndpoint Pointer to the endpoint the segment was for.
96 * @param pTask The task to free.
97 */
98void pdmacFileTaskFree(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint, PPDMACTASKFILE pTask)
99{
100 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClass = (PPDMASYNCCOMPLETIONEPCLASSFILE)pEndpoint->Core.pEpClass;
101
102 LogFlowFunc((": pEndpoint=%p pTask=%p\n", pEndpoint, pTask));
103
104 /* Try the per endpoint cache first. */
105 if (pEndpoint->cTasksCached < pEpClass->cTasksCacheMax)
106 {
107 /* Add it to the list. */
108 pEndpoint->pTasksFreeTail->pNext = pTask;
109 pEndpoint->pTasksFreeTail = pTask;
110 ASMAtomicIncU32(&pEndpoint->cTasksCached);
111 }
112 else
113 {
114 Log(("Freeing task %p because all caches are full\n", pTask));
115 MMR3HeapFree(pTask);
116 }
117}
118
119/**
120 * Allocates a task segment
121 *
122 * @returns Pointer to the new task segment or NULL
123 * @param pEndpoint Pointer to the endpoint
124 */
125PPDMACTASKFILE pdmacFileTaskAlloc(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint)
126{
127 PPDMACTASKFILE pTask = NULL;
128
129 /* Try the small per endpoint cache first. */
130 if (pEndpoint->pTasksFreeHead == pEndpoint->pTasksFreeTail)
131 {
132 /* Try the bigger endpoint class cache. */
133 PPDMASYNCCOMPLETIONEPCLASSFILE pEndpointClass = (PPDMASYNCCOMPLETIONEPCLASSFILE)pEndpoint->Core.pEpClass;
134
135 /*
136 * Allocate completely new.
137 * If this fails we return NULL.
138 */
139 int rc = MMR3HeapAllocZEx(pEndpointClass->Core.pVM, MM_TAG_PDM_ASYNC_COMPLETION,
140 sizeof(PDMACTASKFILE),
141 (void **)&pTask);
142 if (RT_FAILURE(rc))
143 pTask = NULL;
144
145 LogFlow(("Allocated task %p\n", pTask));
146 }
147 else
148 {
149 /* Grab a free task from the head. */
150 AssertMsg(pEndpoint->cTasksCached > 0, ("No tasks cached but list contains more than one element\n"));
151
152 pTask = pEndpoint->pTasksFreeHead;
153 pEndpoint->pTasksFreeHead = pTask->pNext;
154 ASMAtomicDecU32(&pEndpoint->cTasksCached);
155 }
156
157 pTask->pNext = NULL;
158
159 return pTask;
160}
161
162PPDMACTASKFILE pdmacFileEpGetNewTasks(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint)
163{
164 /*
165 * Get pending tasks.
166 */
167 PPDMACTASKFILE pTasks = ASMAtomicXchgPtrT(&pEndpoint->pTasksNewHead, NULL, PPDMACTASKFILE);
168
169 /* Reverse the list to process in FIFO order. */
170 if (pTasks)
171 {
172 PPDMACTASKFILE pTask = pTasks;
173
174 pTasks = NULL;
175
176 while (pTask)
177 {
178 PPDMACTASKFILE pCur = pTask;
179 pTask = pTask->pNext;
180 pCur->pNext = pTasks;
181 pTasks = pCur;
182 }
183 }
184
185 return pTasks;
186}
187
188static void pdmacFileAioMgrWakeup(PPDMACEPFILEMGR pAioMgr)
189{
190 bool fWokenUp = ASMAtomicXchgBool(&pAioMgr->fWokenUp, true);
191 if (!fWokenUp)
192 {
193 bool fWaitingEventSem = ASMAtomicReadBool(&pAioMgr->fWaitingEventSem);
194 if (fWaitingEventSem)
195 {
196 int rc = RTSemEventSignal(pAioMgr->EventSem);
197 AssertRC(rc);
198 }
199 }
200}
201
202static int pdmacFileAioMgrWaitForBlockingEvent(PPDMACEPFILEMGR pAioMgr, PDMACEPFILEAIOMGRBLOCKINGEVENT enmEvent)
203{
204 ASMAtomicWriteU32((volatile uint32_t *)&pAioMgr->enmBlockingEvent, enmEvent);
205 Assert(!pAioMgr->fBlockingEventPending);
206 ASMAtomicXchgBool(&pAioMgr->fBlockingEventPending, true);
207
208 /* Wakeup the async I/O manager */
209 pdmacFileAioMgrWakeup(pAioMgr);
210
211 /* Wait for completion. */
212 int rc = RTSemEventWait(pAioMgr->EventSemBlock, RT_INDEFINITE_WAIT);
213 AssertRC(rc);
214
215 ASMAtomicXchgBool(&pAioMgr->fBlockingEventPending, false);
216 ASMAtomicWriteU32((volatile uint32_t *)&pAioMgr->enmBlockingEvent, PDMACEPFILEAIOMGRBLOCKINGEVENT_INVALID);
217
218 return rc;
219}
220
221int pdmacFileAioMgrAddEndpoint(PPDMACEPFILEMGR pAioMgr, PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint)
222{
223 LogFlowFunc(("pAioMgr=%#p pEndpoint=%#p{%s}\n", pAioMgr, pEndpoint, pEndpoint->Core.pszUri));
224
225 /* Update the assigned I/O manager. */
226 ASMAtomicWritePtr(&pEndpoint->pAioMgr, pAioMgr);
227
228 int rc = RTCritSectEnter(&pAioMgr->CritSectBlockingEvent);
229 AssertRCReturn(rc, rc);
230
231 ASMAtomicWritePtr(&pAioMgr->BlockingEventData.AddEndpoint.pEndpoint, pEndpoint);
232 rc = pdmacFileAioMgrWaitForBlockingEvent(pAioMgr, PDMACEPFILEAIOMGRBLOCKINGEVENT_ADD_ENDPOINT);
233 ASMAtomicWriteNullPtr(&pAioMgr->BlockingEventData.AddEndpoint.pEndpoint);
234
235 RTCritSectLeave(&pAioMgr->CritSectBlockingEvent);
236
237 return rc;
238}
239
240#ifdef SOME_UNUSED_FUNCTION
241static int pdmacFileAioMgrRemoveEndpoint(PPDMACEPFILEMGR pAioMgr, PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint)
242{
243 int rc = RTCritSectEnter(&pAioMgr->CritSectBlockingEvent);
244 AssertRCReturn(rc, rc);
245
246 ASMAtomicWritePtr(&pAioMgr->BlockingEventData.RemoveEndpoint.pEndpoint, pEndpoint);
247 rc = pdmacFileAioMgrWaitForBlockingEvent(pAioMgr, PDMACEPFILEAIOMGRBLOCKINGEVENT_REMOVE_ENDPOINT);
248 ASMAtomicWriteNullPtr(&pAioMgr->BlockingEventData.RemoveEndpoint.pEndpoint);
249
250 RTCritSectLeave(&pAioMgr->CritSectBlockingEvent);
251
252 return rc;
253}
254#endif
255
256static int pdmacFileAioMgrCloseEndpoint(PPDMACEPFILEMGR pAioMgr, PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint)
257{
258 int rc = RTCritSectEnter(&pAioMgr->CritSectBlockingEvent);
259 AssertRCReturn(rc, rc);
260
261 ASMAtomicWritePtr(&pAioMgr->BlockingEventData.CloseEndpoint.pEndpoint, pEndpoint);
262 rc = pdmacFileAioMgrWaitForBlockingEvent(pAioMgr, PDMACEPFILEAIOMGRBLOCKINGEVENT_CLOSE_ENDPOINT);
263 ASMAtomicWriteNullPtr(&pAioMgr->BlockingEventData.CloseEndpoint.pEndpoint);
264
265 RTCritSectLeave(&pAioMgr->CritSectBlockingEvent);
266
267 return rc;
268}
269
270static int pdmacFileAioMgrShutdown(PPDMACEPFILEMGR pAioMgr)
271{
272 int rc = RTCritSectEnter(&pAioMgr->CritSectBlockingEvent);
273 AssertRCReturn(rc, rc);
274
275 rc = pdmacFileAioMgrWaitForBlockingEvent(pAioMgr, PDMACEPFILEAIOMGRBLOCKINGEVENT_SHUTDOWN);
276
277 RTCritSectLeave(&pAioMgr->CritSectBlockingEvent);
278
279 return rc;
280}
281
282int pdmacFileEpAddTask(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint, PPDMACTASKFILE pTask)
283{
284 PPDMACTASKFILE pNext;
285 do
286 {
287 pNext = pEndpoint->pTasksNewHead;
288 pTask->pNext = pNext;
289 } while (!ASMAtomicCmpXchgPtr(&pEndpoint->pTasksNewHead, pTask, pNext));
290
291 pdmacFileAioMgrWakeup(ASMAtomicReadPtrT(&pEndpoint->pAioMgr, PPDMACEPFILEMGR));
292
293 return VINF_SUCCESS;
294}
295
296void pdmacFileEpTaskCompleted(PPDMACTASKFILE pTask, void *pvUser, int rc)
297{
298 PPDMASYNCCOMPLETIONTASKFILE pTaskFile = (PPDMASYNCCOMPLETIONTASKFILE)pvUser;
299
300 LogFlowFunc(("pTask=%#p pvUser=%#p rc=%Rrc\n", pTask, pvUser, rc));
301
302 if (pTask->enmTransferType == PDMACTASKFILETRANSFER_FLUSH)
303 pdmR3AsyncCompletionCompleteTask(&pTaskFile->Core, rc, true);
304 else
305 {
306 Assert((uint32_t)pTask->DataSeg.cbSeg == pTask->DataSeg.cbSeg && (int32_t)pTask->DataSeg.cbSeg >= 0);
307 uint32_t uOld = ASMAtomicSubS32(&pTaskFile->cbTransferLeft, (int32_t)pTask->DataSeg.cbSeg);
308
309 /* The first error will be returned. */
310 if (RT_FAILURE(rc))
311 ASMAtomicCmpXchgS32(&pTaskFile->rc, rc, VINF_SUCCESS);
312#ifdef VBOX_WITH_DEBUGGER
313 else
314 {
315 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pTaskFile->Core.pEndpoint;
316
317 /* Overwrite with injected error code. */
318 if (pTask->enmTransferType == PDMACTASKFILETRANSFER_READ)
319 rc = ASMAtomicXchgS32(&pEpFile->rcReqRead, VINF_SUCCESS);
320 else
321 rc = ASMAtomicXchgS32(&pEpFile->rcReqWrite, VINF_SUCCESS);
322
323 if (RT_FAILURE(rc))
324 ASMAtomicCmpXchgS32(&pTaskFile->rc, rc, VINF_SUCCESS);
325 }
326#endif
327
328 if (!(uOld - pTask->DataSeg.cbSeg)
329 && !ASMAtomicXchgBool(&pTaskFile->fCompleted, true))
330 {
331#ifdef PDM_ASYNC_COMPLETION_FILE_WITH_DELAY
332 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pTaskFile->Core.pEndpoint;
333
334 /* Check if we should delay completion of the request. */
335 if ( ASMAtomicReadU32(&pEpFile->msDelay) > 0
336 && ASMAtomicReadU32(&pEpFile->cReqsDelay) > 0)
337 {
338 ASMAtomicDecU32(&pEpFile->cReqsDelay);
339
340 /* Arm the delay. */
341 pTaskFile->tsDelayEnd = RTTimeProgramMilliTS() + pEpFile->msDelay;
342
343 /* Append to the list. */
344 PPDMASYNCCOMPLETIONTASKFILE pHead = NULL;
345 do
346 {
347 pHead = ASMAtomicReadPtrT(&pEpFile->pDelayedHead, PPDMASYNCCOMPLETIONTASKFILE);
348 pTaskFile->pDelayedNext = pHead;
349 } while (!ASMAtomicCmpXchgPtr(&pEpFile->pDelayedHead, pTaskFile, pHead));
350
351 LogRel(("AIOMgr: Delaying request %#p for %u ms\n", pTaskFile, pEpFile->msDelay));
352 return;
353 }
354#endif
355 pdmR3AsyncCompletionCompleteTask(&pTaskFile->Core, pTaskFile->rc, true);
356
357#ifdef PDM_ASYNC_COMPLETION_FILE_WITH_DELAY
358 /* Check for an expired delay. */
359 if (pEpFile->pDelayedHead != NULL)
360 {
361 uint64_t tsCur = RTTimeProgramMilliTS();
362 pTaskFile = ASMAtomicXchgPtrT(&pEpFile->pDelayedHead, NULL, PPDMASYNCCOMPLETIONTASKFILE);
363
364 while (pTaskFile)
365 {
366 PPDMASYNCCOMPLETIONTASKFILE pTmp = pTaskFile;
367 pTaskFile = pTaskFile->pDelayedNext;
368
369 if (tsCur >= pTmp->tsDelayEnd)
370 {
371 LogRel(("AIOMgr: Delayed request %#p completed\n", pTmp));
372 pdmR3AsyncCompletionCompleteTask(&pTmp->Core, pTmp->rc, true);
373 }
374 else
375 {
376 /* Prepend to the delayed list again. */
377 PPDMASYNCCOMPLETIONTASKFILE pHead = NULL;
378 do
379 {
380 pHead = ASMAtomicReadPtrT(&pEpFile->pDelayedHead, PPDMASYNCCOMPLETIONTASKFILE);
381 pTmp->pDelayedNext = pHead;
382 } while (!ASMAtomicCmpXchgPtr(&pEpFile->pDelayedHead, pTmp, pHead));
383 }
384 }
385 }
386#endif
387 }
388 }
389}
390
391DECLINLINE(void) pdmacFileEpTaskInit(PPDMASYNCCOMPLETIONTASK pTask, size_t cbTransfer)
392{
393 PPDMASYNCCOMPLETIONTASKFILE pTaskFile = (PPDMASYNCCOMPLETIONTASKFILE)pTask;
394
395 Assert((uint32_t)cbTransfer == cbTransfer && (int32_t)cbTransfer >= 0);
396 ASMAtomicWriteS32(&pTaskFile->cbTransferLeft, (int32_t)cbTransfer);
397 ASMAtomicWriteBool(&pTaskFile->fCompleted, false);
398 ASMAtomicWriteS32(&pTaskFile->rc, VINF_SUCCESS);
399}
400
401int pdmacFileEpTaskInitiate(PPDMASYNCCOMPLETIONTASK pTask,
402 PPDMASYNCCOMPLETIONENDPOINT pEndpoint, RTFOFF off,
403 PCRTSGSEG paSegments, size_t cSegments,
404 size_t cbTransfer, PDMACTASKFILETRANSFER enmTransfer)
405{
406 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
407 PPDMASYNCCOMPLETIONTASKFILE pTaskFile = (PPDMASYNCCOMPLETIONTASKFILE)pTask;
408
409 Assert( (enmTransfer == PDMACTASKFILETRANSFER_READ)
410 || (enmTransfer == PDMACTASKFILETRANSFER_WRITE));
411
412 for (size_t i = 0; i < cSegments; i++)
413 {
414 PPDMACTASKFILE pIoTask = pdmacFileTaskAlloc(pEpFile);
415 AssertPtr(pIoTask);
416
417 pIoTask->pEndpoint = pEpFile;
418 pIoTask->enmTransferType = enmTransfer;
419 pIoTask->Off = off;
420 pIoTask->DataSeg.cbSeg = paSegments[i].cbSeg;
421 pIoTask->DataSeg.pvSeg = paSegments[i].pvSeg;
422 pIoTask->pvUser = pTaskFile;
423 pIoTask->pfnCompleted = pdmacFileEpTaskCompleted;
424
425 /* Send it off to the I/O manager. */
426 pdmacFileEpAddTask(pEpFile, pIoTask);
427 off += paSegments[i].cbSeg;
428 cbTransfer -= paSegments[i].cbSeg;
429 }
430
431 AssertMsg(!cbTransfer, ("Incomplete transfer %u bytes left\n", cbTransfer));
432
433 return VINF_AIO_TASK_PENDING;
434}
435
436/**
437 * Creates a new async I/O manager.
438 *
439 * @returns VBox status code.
440 * @param pEpClass Pointer to the endpoint class data.
441 * @param ppAioMgr Where to store the pointer to the new async I/O manager on success.
442 * @param enmMgrType Wanted manager type - can be overwritten by the global override.
443 */
444int pdmacFileAioMgrCreate(PPDMASYNCCOMPLETIONEPCLASSFILE pEpClass, PPPDMACEPFILEMGR ppAioMgr,
445 PDMACEPFILEMGRTYPE enmMgrType)
446{
447 LogFlowFunc((": Entered\n"));
448
449 PPDMACEPFILEMGR pAioMgrNew;
450 int rc = MMR3HeapAllocZEx(pEpClass->Core.pVM, MM_TAG_PDM_ASYNC_COMPLETION, sizeof(PDMACEPFILEMGR), (void **)&pAioMgrNew);
451 if (RT_SUCCESS(rc))
452 {
453 if (enmMgrType < pEpClass->enmMgrTypeOverride)
454 pAioMgrNew->enmMgrType = enmMgrType;
455 else
456 pAioMgrNew->enmMgrType = pEpClass->enmMgrTypeOverride;
457
458 pAioMgrNew->msBwLimitExpired = RT_INDEFINITE_WAIT;
459
460 rc = RTSemEventCreate(&pAioMgrNew->EventSem);
461 if (RT_SUCCESS(rc))
462 {
463 rc = RTSemEventCreate(&pAioMgrNew->EventSemBlock);
464 if (RT_SUCCESS(rc))
465 {
466 rc = RTCritSectInit(&pAioMgrNew->CritSectBlockingEvent);
467 if (RT_SUCCESS(rc))
468 {
469 /* Init the rest of the manager. */
470 if (pAioMgrNew->enmMgrType != PDMACEPFILEMGRTYPE_SIMPLE)
471 rc = pdmacFileAioMgrNormalInit(pAioMgrNew);
472
473 if (RT_SUCCESS(rc))
474 {
475 pAioMgrNew->enmState = PDMACEPFILEMGRSTATE_RUNNING;
476
477 rc = RTThreadCreateF(&pAioMgrNew->Thread,
478 pAioMgrNew->enmMgrType == PDMACEPFILEMGRTYPE_SIMPLE
479 ? pdmacFileAioMgrFailsafe
480 : pdmacFileAioMgrNormal,
481 pAioMgrNew,
482 0,
483 RTTHREADTYPE_IO,
484 0,
485 "AioMgr%d-%s", pEpClass->cAioMgrs,
486 pAioMgrNew->enmMgrType == PDMACEPFILEMGRTYPE_SIMPLE
487 ? "F"
488 : "N");
489 if (RT_SUCCESS(rc))
490 {
491 /* Link it into the list. */
492 RTCritSectEnter(&pEpClass->CritSect);
493 pAioMgrNew->pNext = pEpClass->pAioMgrHead;
494 if (pEpClass->pAioMgrHead)
495 pEpClass->pAioMgrHead->pPrev = pAioMgrNew;
496 pEpClass->pAioMgrHead = pAioMgrNew;
497 pEpClass->cAioMgrs++;
498 RTCritSectLeave(&pEpClass->CritSect);
499
500 *ppAioMgr = pAioMgrNew;
501
502 Log(("PDMAC: Successfully created new file AIO Mgr {%s}\n", RTThreadGetName(pAioMgrNew->Thread)));
503 return VINF_SUCCESS;
504 }
505 pdmacFileAioMgrNormalDestroy(pAioMgrNew);
506 }
507 RTCritSectDelete(&pAioMgrNew->CritSectBlockingEvent);
508 }
509 RTSemEventDestroy(pAioMgrNew->EventSem);
510 }
511 RTSemEventDestroy(pAioMgrNew->EventSemBlock);
512 }
513 MMR3HeapFree(pAioMgrNew);
514 }
515
516 LogFlowFunc((": Leave rc=%Rrc\n", rc));
517
518 return rc;
519}
520
521/**
522 * Destroys a async I/O manager.
523 *
524 * @returns nothing.
525 * @param pAioMgr The async I/O manager to destroy.
526 */
527static void pdmacFileAioMgrDestroy(PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile, PPDMACEPFILEMGR pAioMgr)
528{
529 int rc = pdmacFileAioMgrShutdown(pAioMgr);
530 AssertRC(rc);
531
532 /* Unlink from the list. */
533 rc = RTCritSectEnter(&pEpClassFile->CritSect);
534 AssertRC(rc);
535
536 PPDMACEPFILEMGR pPrev = pAioMgr->pPrev;
537 PPDMACEPFILEMGR pNext = pAioMgr->pNext;
538
539 if (pPrev)
540 pPrev->pNext = pNext;
541 else
542 pEpClassFile->pAioMgrHead = pNext;
543
544 if (pNext)
545 pNext->pPrev = pPrev;
546
547 pEpClassFile->cAioMgrs--;
548 rc = RTCritSectLeave(&pEpClassFile->CritSect);
549 AssertRC(rc);
550
551 /* Free the resources. */
552 RTCritSectDelete(&pAioMgr->CritSectBlockingEvent);
553 RTSemEventDestroy(pAioMgr->EventSem);
554 if (pAioMgr->enmMgrType != PDMACEPFILEMGRTYPE_SIMPLE)
555 pdmacFileAioMgrNormalDestroy(pAioMgr);
556
557 MMR3HeapFree(pAioMgr);
558}
559
560static int pdmacFileMgrTypeFromName(const char *pszVal, PPDMACEPFILEMGRTYPE penmMgrType)
561{
562 int rc = VINF_SUCCESS;
563
564 if (!RTStrCmp(pszVal, "Simple"))
565 *penmMgrType = PDMACEPFILEMGRTYPE_SIMPLE;
566 else if (!RTStrCmp(pszVal, "Async"))
567 *penmMgrType = PDMACEPFILEMGRTYPE_ASYNC;
568 else
569 rc = VERR_CFGM_CONFIG_UNKNOWN_VALUE;
570
571 return rc;
572}
573
574static const char *pdmacFileMgrTypeToName(PDMACEPFILEMGRTYPE enmMgrType)
575{
576 if (enmMgrType == PDMACEPFILEMGRTYPE_SIMPLE)
577 return "Simple";
578 if (enmMgrType == PDMACEPFILEMGRTYPE_ASYNC)
579 return "Async";
580
581 return NULL;
582}
583
584static int pdmacFileBackendTypeFromName(const char *pszVal, PPDMACFILEEPBACKEND penmBackendType)
585{
586 int rc = VINF_SUCCESS;
587
588 if (!RTStrCmp(pszVal, "Buffered"))
589 *penmBackendType = PDMACFILEEPBACKEND_BUFFERED;
590 else if (!RTStrCmp(pszVal, "NonBuffered"))
591 *penmBackendType = PDMACFILEEPBACKEND_NON_BUFFERED;
592 else
593 rc = VERR_CFGM_CONFIG_UNKNOWN_VALUE;
594
595 return rc;
596}
597
598static const char *pdmacFileBackendTypeToName(PDMACFILEEPBACKEND enmBackendType)
599{
600 if (enmBackendType == PDMACFILEEPBACKEND_BUFFERED)
601 return "Buffered";
602 if (enmBackendType == PDMACFILEEPBACKEND_NON_BUFFERED)
603 return "NonBuffered";
604
605 return NULL;
606}
607
608/**
609 * Get the size of the given file.
610 * Works for block devices too.
611 *
612 * @returns VBox status code.
613 * @param hFile The file handle.
614 * @param pcbSize Where to store the size of the file on success.
615 */
616static int pdmacFileEpNativeGetSize(RTFILE hFile, uint64_t *pcbSize)
617{
618 uint64_t cbFile;
619 int rc = RTFileGetSize(hFile, &cbFile);
620 if (RT_SUCCESS(rc))
621 *pcbSize = cbFile;
622
623 return rc;
624}
625
626#ifdef VBOX_WITH_DEBUGGER
627
628/**
629 * Error inject callback.
630 */
631static DECLCALLBACK(int) pdmacEpFileErrorInject(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR pArgs, unsigned cArgs)
632{
633 /*
634 * Validate input.
635 */
636 DBGC_CMDHLP_REQ_VM_RET(pCmdHlp, pCmd, pVM);
637 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, -1, cArgs == 3);
638 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, pArgs[0].enmType == DBGCVAR_TYPE_STRING);
639 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 1, pArgs[1].enmType == DBGCVAR_TYPE_STRING);
640 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 2, pArgs[2].enmType == DBGCVAR_TYPE_NUMBER);
641
642 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile;
643 pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pVM->pUVM->pdm.s.apAsyncCompletionEndpointClass[PDMASYNCCOMPLETIONEPCLASSTYPE_FILE];
644
645 /* Syntax is "read|write <filename> <status code>" */
646 bool fWrite;
647 if (!RTStrCmp(pArgs[0].u.pszString, "read"))
648 fWrite = false;
649 else if (!RTStrCmp(pArgs[0].u.pszString, "write"))
650 fWrite = true;
651 else
652 return DBGCCmdHlpFail(pCmdHlp, pCmd, "invalid transfer direction '%s'", pArgs[0].u.pszString);
653
654 int32_t rcToInject = (int32_t)pArgs[2].u.u64Number;
655 if ((uint64_t)rcToInject != pArgs[2].u.u64Number)
656 return DBGCCmdHlpFail(pCmdHlp, pCmd, "The status code '%lld' is out of range", pArgs[0].u.u64Number);
657
658
659 /*
660 * Search for the matching endpoint.
661 */
662 RTCritSectEnter(&pEpClassFile->Core.CritSect);
663
664 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEpClassFile->Core.pEndpointsHead;
665 while (pEpFile)
666 {
667 if (!RTStrCmp(pArgs[1].u.pszString, RTPathFilename(pEpFile->Core.pszUri)))
668 break;
669 pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEpFile->Core.pNext;
670 }
671
672 if (pEpFile)
673 {
674 /*
675 * Do the job.
676 */
677 if (fWrite)
678 ASMAtomicXchgS32(&pEpFile->rcReqWrite, rcToInject);
679 else
680 ASMAtomicXchgS32(&pEpFile->rcReqRead, rcToInject);
681
682 DBGCCmdHlpPrintf(pCmdHlp, "Injected %Rrc into '%s' for %s\n",
683 (int)rcToInject, pArgs[1].u.pszString, pArgs[0].u.pszString);
684 }
685
686 RTCritSectLeave(&pEpClassFile->Core.CritSect);
687
688 if (!pEpFile)
689 return DBGCCmdHlpFail(pCmdHlp, pCmd, "No file with name '%s' found", pArgs[1].u.pszString);
690 return VINF_SUCCESS;
691}
692
693# ifdef PDM_ASYNC_COMPLETION_FILE_WITH_DELAY
694/**
695 * Delay inject callback.
696 */
697static DECLCALLBACK(int) pdmacEpFileDelayInject(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR pArgs, unsigned cArgs)
698{
699 /*
700 * Validate input.
701 */
702 DBGC_CMDHLP_REQ_VM_RET(pCmdHlp, pCmd, pVM);
703 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, -1, cArgs >= 3);
704 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, pArgs[0].enmType == DBGCVAR_TYPE_STRING);
705 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 1, pArgs[1].enmType == DBGCVAR_TYPE_STRING);
706 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 2, pArgs[2].enmType == DBGCVAR_TYPE_NUMBER);
707
708 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile;
709 pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pVM->pUVM->pdm.s.apAsyncCompletionEndpointClass[PDMASYNCCOMPLETIONEPCLASSTYPE_FILE];
710
711 /* Syntax is "read|write|flush|any <filename> <delay> [reqs]" */
712 PDMACFILEREQTYPEDELAY enmDelayType = PDMACFILEREQTYPEDELAY_ANY;
713 if (!RTStrCmp(pArgs[0].u.pszString, "read"))
714 enmDelayType = PDMACFILEREQTYPEDELAY_READ;
715 else if (!RTStrCmp(pArgs[0].u.pszString, "write"))
716 enmDelayType = PDMACFILEREQTYPEDELAY_WRITE;
717 else if (!RTStrCmp(pArgs[0].u.pszString, "flush"))
718 enmDelayType = PDMACFILEREQTYPEDELAY_FLUSH;
719 else if (!RTStrCmp(pArgs[0].u.pszString, "any"))
720 enmDelayType = PDMACFILEREQTYPEDELAY_ANY;
721 else
722 return DBGCCmdHlpFail(pCmdHlp, pCmd, "invalid transfer direction '%s'", pArgs[0].u.pszString);
723
724 uint32_t msDelay = (uint32_t)pArgs[2].u.u64Number;
725 if ((uint64_t)msDelay != pArgs[2].u.u64Number)
726 return DBGCCmdHlpFail(pCmdHlp, pCmd, "The delay '%lld' is out of range", pArgs[0].u.u64Number);
727
728 uint32_t cReqsDelay = 1;
729 if (cArgs == 4)
730 cReqsDelay = (uint32_t)pArgs[3].u.u64Number;
731
732 /*
733 * Search for the matching endpoint.
734 */
735 RTCritSectEnter(&pEpClassFile->Core.CritSect);
736
737 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEpClassFile->Core.pEndpointsHead;
738 while (pEpFile)
739 {
740 if (!RTStrCmp(pArgs[1].u.pszString, RTPathFilename(pEpFile->Core.pszUri)))
741 break;
742 pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEpFile->Core.pNext;
743 }
744
745 if (pEpFile)
746 {
747 ASMAtomicWriteSize(&pEpFile->enmTypeDelay, enmDelayType);
748 ASMAtomicWriteU32(&pEpFile->msDelay, msDelay);
749 ASMAtomicWriteU32(&pEpFile->cReqsDelay, cReqsDelay);
750
751 DBGCCmdHlpPrintf(pCmdHlp, "Injected delay for the next %u requests of %u ms into '%s' for %s\n",
752 cReqsDelay, msDelay, pArgs[1].u.pszString, pArgs[0].u.pszString);
753 }
754
755 RTCritSectLeave(&pEpClassFile->Core.CritSect);
756
757 if (!pEpFile)
758 return DBGCCmdHlpFail(pCmdHlp, pCmd, "No file with name '%s' found", pArgs[1].u.pszString);
759 return VINF_SUCCESS;
760}
761# endif /* PDM_ASYNC_COMPLETION_FILE_WITH_DELAY */
762
763#endif /* VBOX_WITH_DEBUGGER */
764
765static int pdmacFileInitialize(PPDMASYNCCOMPLETIONEPCLASS pClassGlobals, PCFGMNODE pCfgNode)
766{
767 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pClassGlobals;
768 RTFILEAIOLIMITS AioLimits; /** < Async I/O limitations. */
769
770 int rc = RTFileAioGetLimits(&AioLimits);
771#ifdef DEBUG
772 if (RT_SUCCESS(rc) && RTEnvExist("VBOX_ASYNC_IO_FAILBACK"))
773 rc = VERR_ENV_VAR_NOT_FOUND;
774#endif
775 if (RT_FAILURE(rc))
776 {
777 LogRel(("AIO: Async I/O manager not supported (rc=%Rrc). Falling back to simple manager\n",
778 rc));
779 pEpClassFile->enmMgrTypeOverride = PDMACEPFILEMGRTYPE_SIMPLE;
780 pEpClassFile->enmEpBackendDefault = PDMACFILEEPBACKEND_BUFFERED;
781 }
782 else
783 {
784 pEpClassFile->uBitmaskAlignment = AioLimits.cbBufferAlignment ? ~((RTR3UINTPTR)AioLimits.cbBufferAlignment - 1) : RTR3UINTPTR_MAX;
785 pEpClassFile->cReqsOutstandingMax = AioLimits.cReqsOutstandingMax;
786
787 if (pCfgNode)
788 {
789 /* Query the default manager type */
790 char *pszVal = NULL;
791 rc = CFGMR3QueryStringAllocDef(pCfgNode, "IoMgr", &pszVal, "Async");
792 AssertLogRelRCReturn(rc, rc);
793
794 rc = pdmacFileMgrTypeFromName(pszVal, &pEpClassFile->enmMgrTypeOverride);
795 MMR3HeapFree(pszVal);
796 if (RT_FAILURE(rc))
797 return rc;
798
799 LogRel(("AIOMgr: Default manager type is \"%s\"\n", pdmacFileMgrTypeToName(pEpClassFile->enmMgrTypeOverride)));
800
801 /* Query default backend type */
802 rc = CFGMR3QueryStringAllocDef(pCfgNode, "FileBackend", &pszVal, "NonBuffered");
803 AssertLogRelRCReturn(rc, rc);
804
805 rc = pdmacFileBackendTypeFromName(pszVal, &pEpClassFile->enmEpBackendDefault);
806 MMR3HeapFree(pszVal);
807 if (RT_FAILURE(rc))
808 return rc;
809
810 LogRel(("AIOMgr: Default file backend is \"%s\"\n", pdmacFileBackendTypeToName(pEpClassFile->enmEpBackendDefault)));
811
812#ifdef RT_OS_LINUX
813 if ( pEpClassFile->enmMgrTypeOverride == PDMACEPFILEMGRTYPE_ASYNC
814 && pEpClassFile->enmEpBackendDefault == PDMACFILEEPBACKEND_BUFFERED)
815 {
816 LogRel(("AIOMgr: Linux does not support buffered async I/O, changing to non buffered\n"));
817 pEpClassFile->enmEpBackendDefault = PDMACFILEEPBACKEND_NON_BUFFERED;
818 }
819#endif
820 }
821 else
822 {
823 /* No configuration supplied, set defaults */
824 pEpClassFile->enmEpBackendDefault = PDMACFILEEPBACKEND_NON_BUFFERED;
825 pEpClassFile->enmMgrTypeOverride = PDMACEPFILEMGRTYPE_ASYNC;
826 }
827 }
828
829 /* Init critical section. */
830 rc = RTCritSectInit(&pEpClassFile->CritSect);
831
832#ifdef VBOX_WITH_DEBUGGER
833 /* Install the error injection handler. */
834 if (RT_SUCCESS(rc))
835 {
836 rc = DBGCRegisterCommands(&g_aCmds[0], RT_ELEMENTS(g_aCmds));
837 AssertRC(rc);
838 }
839#endif
840
841 return rc;
842}
843
844static void pdmacFileTerminate(PPDMASYNCCOMPLETIONEPCLASS pClassGlobals)
845{
846 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pClassGlobals;
847
848 /* All endpoints should be closed at this point. */
849 AssertMsg(!pEpClassFile->Core.pEndpointsHead, ("There are still endpoints left\n"));
850
851 /* Destroy all left async I/O managers. */
852 while (pEpClassFile->pAioMgrHead)
853 pdmacFileAioMgrDestroy(pEpClassFile, pEpClassFile->pAioMgrHead);
854
855 RTCritSectDelete(&pEpClassFile->CritSect);
856}
857
858static int pdmacFileEpInitialize(PPDMASYNCCOMPLETIONENDPOINT pEndpoint,
859 const char *pszUri, uint32_t fFlags)
860{
861 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
862 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pEndpoint->pEpClass;
863 PDMACEPFILEMGRTYPE enmMgrType = pEpClassFile->enmMgrTypeOverride;
864 PDMACFILEEPBACKEND enmEpBackend = pEpClassFile->enmEpBackendDefault;
865
866 AssertMsgReturn((fFlags & ~(PDMACEP_FILE_FLAGS_READ_ONLY | PDMACEP_FILE_FLAGS_DONT_LOCK | PDMACEP_FILE_FLAGS_HOST_CACHE_ENABLED)) == 0,
867 ("PDMAsyncCompletion: Invalid flag specified\n"), VERR_INVALID_PARAMETER);
868
869 unsigned fFileFlags = RTFILE_O_OPEN;
870
871 /*
872 * Revert to the simple manager and the buffered backend if
873 * the host cache should be enabled.
874 */
875 if (fFlags & PDMACEP_FILE_FLAGS_HOST_CACHE_ENABLED)
876 {
877 enmMgrType = PDMACEPFILEMGRTYPE_SIMPLE;
878 enmEpBackend = PDMACFILEEPBACKEND_BUFFERED;
879 }
880
881 if (fFlags & PDMACEP_FILE_FLAGS_READ_ONLY)
882 fFileFlags |= RTFILE_O_READ | RTFILE_O_DENY_NONE;
883 else
884 {
885 fFileFlags |= RTFILE_O_READWRITE;
886
887 /*
888 * Opened in read/write mode. Check whether the caller wants to
889 * avoid the lock. Return an error in case caching is enabled
890 * because this can lead to data corruption.
891 */
892 if (fFlags & PDMACEP_FILE_FLAGS_DONT_LOCK)
893 fFileFlags |= RTFILE_O_DENY_NONE;
894 else
895 fFileFlags |= RTFILE_O_DENY_WRITE;
896 }
897
898 if (enmMgrType == PDMACEPFILEMGRTYPE_ASYNC)
899 fFileFlags |= RTFILE_O_ASYNC_IO;
900
901 int rc;
902 if (enmEpBackend == PDMACFILEEPBACKEND_NON_BUFFERED)
903 {
904 /*
905 * We only disable the cache if the size of the file is a multiple of 512.
906 * Certain hosts like Windows, Linux and Solaris require that transfer sizes
907 * are aligned to the volume sector size.
908 * If not we just make sure that the data is written to disk with RTFILE_O_WRITE_THROUGH
909 * which will trash the host cache but ensures that the host cache will not
910 * contain dirty buffers.
911 */
912 RTFILE hFile;
913 rc = RTFileOpen(&hFile, pszUri, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
914 if (RT_SUCCESS(rc))
915 {
916 uint64_t cbSize;
917
918 rc = pdmacFileEpNativeGetSize(hFile, &cbSize);
919
920 if (RT_SUCCESS(rc) && ((cbSize % 512) == 0))
921 fFileFlags |= RTFILE_O_NO_CACHE;
922 else
923 {
924 /* Downgrade to the buffered backend */
925 enmEpBackend = PDMACFILEEPBACKEND_BUFFERED;
926
927#ifdef RT_OS_LINUX
928 fFileFlags &= ~RTFILE_O_ASYNC_IO;
929 enmMgrType = PDMACEPFILEMGRTYPE_SIMPLE;
930#endif
931 }
932 RTFileClose(hFile);
933 }
934 }
935
936 /* Open with final flags. */
937 rc = RTFileOpen(&pEpFile->hFile, pszUri, fFileFlags);
938 if ( rc == VERR_INVALID_FUNCTION
939 || rc == VERR_INVALID_PARAMETER)
940 {
941 LogRel(("pdmacFileEpInitialize: RTFileOpen %s / %08x failed with %Rrc\n",
942 pszUri, fFileFlags, rc));
943 /*
944 * Solaris doesn't support directio on ZFS so far. :-\
945 * Trying to enable it returns VERR_INVALID_FUNCTION
946 * (ENOTTY). Remove it and hope for the best.
947 * ZFS supports write throttling in case applications
948 * write more data than can be synced to the disk
949 * without blocking the whole application.
950 *
951 * On Linux we have the same problem with cifs.
952 * Have to disable async I/O here too because it requires O_DIRECT.
953 */
954 fFileFlags &= ~RTFILE_O_NO_CACHE;
955 enmEpBackend = PDMACFILEEPBACKEND_BUFFERED;
956
957#ifdef RT_OS_LINUX
958 fFileFlags &= ~RTFILE_O_ASYNC_IO;
959 enmMgrType = PDMACEPFILEMGRTYPE_SIMPLE;
960#endif
961
962 /* Open again. */
963 rc = RTFileOpen(&pEpFile->hFile, pszUri, fFileFlags);
964
965 if (RT_FAILURE(rc))
966 {
967 LogRel(("pdmacFileEpInitialize: RTFileOpen %s / %08x failed AGAIN(!) with %Rrc\n",
968 pszUri, fFileFlags, rc));
969 }
970 }
971
972 if (RT_SUCCESS(rc))
973 {
974 pEpFile->fFlags = fFileFlags;
975
976 rc = pdmacFileEpNativeGetSize(pEpFile->hFile, (uint64_t *)&pEpFile->cbFile);
977 if (RT_SUCCESS(rc))
978 {
979 /* Initialize the segment cache */
980 rc = MMR3HeapAllocZEx(pEpClassFile->Core.pVM, MM_TAG_PDM_ASYNC_COMPLETION,
981 sizeof(PDMACTASKFILE),
982 (void **)&pEpFile->pTasksFreeHead);
983 if (RT_SUCCESS(rc))
984 {
985 PPDMACEPFILEMGR pAioMgr = NULL;
986
987 pEpFile->pTasksFreeTail = pEpFile->pTasksFreeHead;
988 pEpFile->cTasksCached = 0;
989 pEpFile->enmBackendType = enmEpBackend;
990 /*
991 * Disable async flushes on Solaris for now.
992 * They cause weird hangs which needs more investigations.
993 */
994#ifndef RT_OS_SOLARIS
995 pEpFile->fAsyncFlushSupported = true;
996#else
997 pEpFile->fAsyncFlushSupported = false;
998#endif
999
1000 if (enmMgrType == PDMACEPFILEMGRTYPE_SIMPLE)
1001 {
1002 /* Simple mode. Every file has its own async I/O manager. */
1003 rc = pdmacFileAioMgrCreate(pEpClassFile, &pAioMgr, PDMACEPFILEMGRTYPE_SIMPLE);
1004 AssertRC(rc);
1005 }
1006 else
1007 {
1008 pAioMgr = pEpClassFile->pAioMgrHead;
1009
1010 /* Check for an idling manager of the same type */
1011 while (pAioMgr)
1012 {
1013 if (pAioMgr->enmMgrType == enmMgrType)
1014 break;
1015 pAioMgr = pAioMgr->pNext;
1016 }
1017
1018 if (!pAioMgr)
1019 {
1020 rc = pdmacFileAioMgrCreate(pEpClassFile, &pAioMgr, enmMgrType);
1021 AssertRC(rc);
1022 }
1023 }
1024
1025 pEpFile->AioMgr.pTreeRangesLocked = (PAVLRFOFFTREE)RTMemAllocZ(sizeof(AVLRFOFFTREE));
1026 if (!pEpFile->AioMgr.pTreeRangesLocked)
1027 rc = VERR_NO_MEMORY;
1028 else
1029 {
1030 pEpFile->enmState = PDMASYNCCOMPLETIONENDPOINTFILESTATE_ACTIVE;
1031
1032 /* Assign the endpoint to the thread. */
1033 rc = pdmacFileAioMgrAddEndpoint(pAioMgr, pEpFile);
1034 if (RT_FAILURE(rc))
1035 {
1036 RTMemFree(pEpFile->AioMgr.pTreeRangesLocked);
1037 MMR3HeapFree(pEpFile->pTasksFreeHead);
1038 }
1039 }
1040 }
1041 }
1042
1043 if (RT_FAILURE(rc))
1044 RTFileClose(pEpFile->hFile);
1045 }
1046
1047#ifdef VBOX_WITH_STATISTICS
1048 if (RT_SUCCESS(rc))
1049 {
1050 STAMR3RegisterF(pEpClassFile->Core.pVM, &pEpFile->StatRead,
1051 STAMTYPE_PROFILE_ADV, STAMVISIBILITY_ALWAYS,
1052 STAMUNIT_TICKS_PER_CALL, "Time taken to read from the endpoint",
1053 "/PDM/AsyncCompletion/File/%s/Read", RTPathFilename(pEpFile->Core.pszUri));
1054
1055 STAMR3RegisterF(pEpClassFile->Core.pVM, &pEpFile->StatWrite,
1056 STAMTYPE_PROFILE_ADV, STAMVISIBILITY_ALWAYS,
1057 STAMUNIT_TICKS_PER_CALL, "Time taken to write to the endpoint",
1058 "/PDM/AsyncCompletion/File/%s/Write", RTPathFilename(pEpFile->Core.pszUri));
1059 }
1060#endif
1061
1062 if (RT_SUCCESS(rc))
1063 LogRel(("AIOMgr: Endpoint for file '%s' (flags %08x) created successfully\n", pszUri, pEpFile->fFlags));
1064
1065 return rc;
1066}
1067
1068static int pdmacFileEpRangesLockedDestroy(PAVLRFOFFNODECORE pNode, void *pvUser)
1069{
1070 NOREF(pNode); NOREF(pvUser);
1071 AssertMsgFailed(("The locked ranges tree should be empty at that point\n"));
1072 return VINF_SUCCESS;
1073}
1074
1075static int pdmacFileEpClose(PPDMASYNCCOMPLETIONENDPOINT pEndpoint)
1076{
1077 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1078 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pEndpoint->pEpClass;
1079
1080 /* Make sure that all tasks finished for this endpoint. */
1081 int rc = pdmacFileAioMgrCloseEndpoint(pEpFile->pAioMgr, pEpFile);
1082 AssertRC(rc);
1083
1084 /*
1085 * If the async I/O manager is in failsafe mode this is the only endpoint
1086 * he processes and thus can be destroyed now.
1087 */
1088 if (pEpFile->pAioMgr->enmMgrType == PDMACEPFILEMGRTYPE_SIMPLE)
1089 pdmacFileAioMgrDestroy(pEpClassFile, pEpFile->pAioMgr);
1090
1091 /* Free cached tasks. */
1092 PPDMACTASKFILE pTask = pEpFile->pTasksFreeHead;
1093
1094 while (pTask)
1095 {
1096 PPDMACTASKFILE pTaskFree = pTask;
1097 pTask = pTask->pNext;
1098 MMR3HeapFree(pTaskFree);
1099 }
1100
1101 /* Destroy the locked ranges tree now. */
1102 RTAvlrFileOffsetDestroy(pEpFile->AioMgr.pTreeRangesLocked, pdmacFileEpRangesLockedDestroy, NULL);
1103
1104 RTFileClose(pEpFile->hFile);
1105
1106#ifdef VBOX_WITH_STATISTICS
1107 STAMR3Deregister(pEpClassFile->Core.pVM, &pEpFile->StatRead);
1108 STAMR3Deregister(pEpClassFile->Core.pVM, &pEpFile->StatWrite);
1109#endif
1110
1111 return VINF_SUCCESS;
1112}
1113
1114static int pdmacFileEpRead(PPDMASYNCCOMPLETIONTASK pTask,
1115 PPDMASYNCCOMPLETIONENDPOINT pEndpoint, RTFOFF off,
1116 PCRTSGSEG paSegments, size_t cSegments,
1117 size_t cbRead)
1118{
1119 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1120
1121 LogFlowFunc(("pTask=%#p pEndpoint=%#p off=%RTfoff paSegments=%#p cSegments=%zu cbRead=%zu\n",
1122 pTask, pEndpoint, off, paSegments, cSegments, cbRead));
1123
1124 if (RT_UNLIKELY((uint64_t)off + cbRead > pEpFile->cbFile))
1125 return VERR_EOF;
1126
1127 STAM_PROFILE_ADV_START(&pEpFile->StatRead, Read);
1128 pdmacFileEpTaskInit(pTask, cbRead);
1129 int rc = pdmacFileEpTaskInitiate(pTask, pEndpoint, off, paSegments, cSegments, cbRead,
1130 PDMACTASKFILETRANSFER_READ);
1131 STAM_PROFILE_ADV_STOP(&pEpFile->StatRead, Read);
1132
1133 return rc;
1134}
1135
1136static int pdmacFileEpWrite(PPDMASYNCCOMPLETIONTASK pTask,
1137 PPDMASYNCCOMPLETIONENDPOINT pEndpoint, RTFOFF off,
1138 PCRTSGSEG paSegments, size_t cSegments,
1139 size_t cbWrite)
1140{
1141 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1142
1143 if (RT_UNLIKELY(pEpFile->fReadonly))
1144 return VERR_NOT_SUPPORTED;
1145
1146 STAM_PROFILE_ADV_START(&pEpFile->StatWrite, Write);
1147
1148 pdmacFileEpTaskInit(pTask, cbWrite);
1149
1150 int rc = pdmacFileEpTaskInitiate(pTask, pEndpoint, off, paSegments, cSegments, cbWrite,
1151 PDMACTASKFILETRANSFER_WRITE);
1152
1153 STAM_PROFILE_ADV_STOP(&pEpFile->StatWrite, Write);
1154
1155 return rc;
1156}
1157
1158static int pdmacFileEpFlush(PPDMASYNCCOMPLETIONTASK pTask,
1159 PPDMASYNCCOMPLETIONENDPOINT pEndpoint)
1160{
1161 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1162 PPDMASYNCCOMPLETIONTASKFILE pTaskFile = (PPDMASYNCCOMPLETIONTASKFILE)pTask;
1163
1164 if (RT_UNLIKELY(pEpFile->fReadonly))
1165 return VERR_NOT_SUPPORTED;
1166
1167 pdmacFileEpTaskInit(pTask, 0);
1168
1169 PPDMACTASKFILE pIoTask = pdmacFileTaskAlloc(pEpFile);
1170 if (RT_UNLIKELY(!pIoTask))
1171 return VERR_NO_MEMORY;
1172
1173 pIoTask->pEndpoint = pEpFile;
1174 pIoTask->enmTransferType = PDMACTASKFILETRANSFER_FLUSH;
1175 pIoTask->pvUser = pTaskFile;
1176 pIoTask->pfnCompleted = pdmacFileEpTaskCompleted;
1177 pdmacFileEpAddTask(pEpFile, pIoTask);
1178
1179 return VINF_AIO_TASK_PENDING;
1180}
1181
1182static int pdmacFileEpGetSize(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, uint64_t *pcbSize)
1183{
1184 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1185
1186 *pcbSize = ASMAtomicReadU64(&pEpFile->cbFile);
1187
1188 return VINF_SUCCESS;
1189}
1190
1191static int pdmacFileEpSetSize(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, uint64_t cbSize)
1192{
1193 int rc;
1194 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1195
1196 rc = RTFileSetSize(pEpFile->hFile, cbSize);
1197 if (RT_SUCCESS(rc))
1198 ASMAtomicWriteU64(&pEpFile->cbFile, cbSize);
1199
1200 return rc;
1201}
1202
1203const PDMASYNCCOMPLETIONEPCLASSOPS g_PDMAsyncCompletionEndpointClassFile =
1204{
1205 /* u32Version */
1206 PDMAC_EPCLASS_OPS_VERSION,
1207 /* pcszName */
1208 "File",
1209 /* enmClassType */
1210 PDMASYNCCOMPLETIONEPCLASSTYPE_FILE,
1211 /* cbEndpointClassGlobal */
1212 sizeof(PDMASYNCCOMPLETIONEPCLASSFILE),
1213 /* cbEndpoint */
1214 sizeof(PDMASYNCCOMPLETIONENDPOINTFILE),
1215 /* cbTask */
1216 sizeof(PDMASYNCCOMPLETIONTASKFILE),
1217 /* pfnInitialize */
1218 pdmacFileInitialize,
1219 /* pfnTerminate */
1220 pdmacFileTerminate,
1221 /* pfnEpInitialize. */
1222 pdmacFileEpInitialize,
1223 /* pfnEpClose */
1224 pdmacFileEpClose,
1225 /* pfnEpRead */
1226 pdmacFileEpRead,
1227 /* pfnEpWrite */
1228 pdmacFileEpWrite,
1229 /* pfnEpFlush */
1230 pdmacFileEpFlush,
1231 /* pfnEpGetSize */
1232 pdmacFileEpGetSize,
1233 /* pfnEpSetSize */
1234 pdmacFileEpSetSize,
1235 /* u32VersionEnd */
1236 PDMAC_EPCLASS_OPS_VERSION
1237};
1238
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