VirtualBox

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

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

AsyncCompletion: Build fix

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 42.3 KB
Line 
1/* $Id: PDMAsyncCompletionFile.cpp 39681 2011-12-27 21:51:07Z 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/*******************************************************************************
48* Internal Functions *
49*******************************************************************************/
50#ifdef VBOX_WITH_DEBUGGER
51static DECLCALLBACK(int) pdmacEpFileErrorInject(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR pArgs, unsigned cArgs);
52# ifdef PDM_ASYNC_COMPLETION_FILE_WITH_DELAY
53static DECLCALLBACK(int) pdmacEpFileDelayInject(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR pArgs, unsigned cArgs);
54# endif
55#endif
56
57/*******************************************************************************
58* Global Variables *
59*******************************************************************************/
60#ifdef VBOX_WITH_DEBUGGER
61static const DBGCVARDESC g_aInjectErrorArgs[] =
62{
63 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
64 { 1, 1, DBGCVAR_CAT_STRING, 0, "direction", "write/read." },
65 { 1, 1, DBGCVAR_CAT_STRING, 0, "filename", "Filename." },
66 { 1, 1, DBGCVAR_CAT_NUMBER, 0, "errcode", "VBox status code." },
67};
68
69# ifdef PDM_ASYNC_COMPLETION_FILE_WITH_DELAY
70static const DBGCVARDESC g_aInjectDelayArgs[] =
71{
72 /* cTimesMin, cTimesMax, enmCategory, fFlags, pszName, pszDescription */
73 { 1, 1, DBGCVAR_CAT_STRING, 0, "direction", "write/read." },
74 { 1, 1, DBGCVAR_CAT_STRING, 0, "filename", "Filename." },
75 { 1, 1, DBGCVAR_CAT_NUMBER, 0, "delay", "Delay in milliseconds." },
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", 3, 3, &g_aInjectDelayArgs[0], 3, 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 && ASMAtomicCmpXchgPtr(&pEpFile->pReqDelayed, pTaskFile, NULL))
337 {
338 /* Arm the delay. */
339 pEpFile->tsDelayEnd = RTTimeProgramMilliTS() + pEpFile->msDelay;
340 LogRel(("AIOMgr: Delaying request %#p for %u ms\n", pTaskFile, pEpFile->msDelay));
341 return;
342 }
343#endif
344 pdmR3AsyncCompletionCompleteTask(&pTaskFile->Core, pTaskFile->rc, true);
345
346#if PDM_ASYNC_COMPLETION_FILE_WITH_DELAY
347 /* Check for an expired delay. */
348 if ( pEpFile->pReqDelayed != NULL
349 && RTTimeProgramMilliTS() >= pEpFile->tsDelayEnd)
350 {
351 pTaskFile = ASMAtomicXchgPtrT(&pEpFile->pReqDelayed, NULL, PPDMASYNCCOMPLETIONTASKFILE);
352 ASMAtomicXchgU32(&pEpFile->msDelay, 0);
353 LogRel(("AIOMgr: Delayed request %#p completed\n", pTaskFile));
354 pdmR3AsyncCompletionCompleteTask(&pTaskFile->Core, pTaskFile->rc, true);
355 }
356#endif
357 }
358 }
359}
360
361DECLINLINE(void) pdmacFileEpTaskInit(PPDMASYNCCOMPLETIONTASK pTask, size_t cbTransfer)
362{
363 PPDMASYNCCOMPLETIONTASKFILE pTaskFile = (PPDMASYNCCOMPLETIONTASKFILE)pTask;
364
365 Assert((uint32_t)cbTransfer == cbTransfer && (int32_t)cbTransfer >= 0);
366 ASMAtomicWriteS32(&pTaskFile->cbTransferLeft, (int32_t)cbTransfer);
367 ASMAtomicWriteBool(&pTaskFile->fCompleted, false);
368 ASMAtomicWriteS32(&pTaskFile->rc, VINF_SUCCESS);
369}
370
371int pdmacFileEpTaskInitiate(PPDMASYNCCOMPLETIONTASK pTask,
372 PPDMASYNCCOMPLETIONENDPOINT pEndpoint, RTFOFF off,
373 PCRTSGSEG paSegments, size_t cSegments,
374 size_t cbTransfer, PDMACTASKFILETRANSFER enmTransfer)
375{
376 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
377 PPDMASYNCCOMPLETIONTASKFILE pTaskFile = (PPDMASYNCCOMPLETIONTASKFILE)pTask;
378
379 Assert( (enmTransfer == PDMACTASKFILETRANSFER_READ)
380 || (enmTransfer == PDMACTASKFILETRANSFER_WRITE));
381
382 for (size_t i = 0; i < cSegments; i++)
383 {
384 PPDMACTASKFILE pIoTask = pdmacFileTaskAlloc(pEpFile);
385 AssertPtr(pIoTask);
386
387 pIoTask->pEndpoint = pEpFile;
388 pIoTask->enmTransferType = enmTransfer;
389 pIoTask->Off = off;
390 pIoTask->DataSeg.cbSeg = paSegments[i].cbSeg;
391 pIoTask->DataSeg.pvSeg = paSegments[i].pvSeg;
392 pIoTask->pvUser = pTaskFile;
393 pIoTask->pfnCompleted = pdmacFileEpTaskCompleted;
394
395 /* Send it off to the I/O manager. */
396 pdmacFileEpAddTask(pEpFile, pIoTask);
397 off += paSegments[i].cbSeg;
398 cbTransfer -= paSegments[i].cbSeg;
399 }
400
401 AssertMsg(!cbTransfer, ("Incomplete transfer %u bytes left\n", cbTransfer));
402
403 return VINF_AIO_TASK_PENDING;
404}
405
406/**
407 * Creates a new async I/O manager.
408 *
409 * @returns VBox status code.
410 * @param pEpClass Pointer to the endpoint class data.
411 * @param ppAioMgr Where to store the pointer to the new async I/O manager on success.
412 * @param enmMgrType Wanted manager type - can be overwritten by the global override.
413 */
414int pdmacFileAioMgrCreate(PPDMASYNCCOMPLETIONEPCLASSFILE pEpClass, PPPDMACEPFILEMGR ppAioMgr,
415 PDMACEPFILEMGRTYPE enmMgrType)
416{
417 LogFlowFunc((": Entered\n"));
418
419 PPDMACEPFILEMGR pAioMgrNew;
420 int rc = MMR3HeapAllocZEx(pEpClass->Core.pVM, MM_TAG_PDM_ASYNC_COMPLETION, sizeof(PDMACEPFILEMGR), (void **)&pAioMgrNew);
421 if (RT_SUCCESS(rc))
422 {
423 if (enmMgrType < pEpClass->enmMgrTypeOverride)
424 pAioMgrNew->enmMgrType = enmMgrType;
425 else
426 pAioMgrNew->enmMgrType = pEpClass->enmMgrTypeOverride;
427
428 pAioMgrNew->msBwLimitExpired = RT_INDEFINITE_WAIT;
429
430 rc = RTSemEventCreate(&pAioMgrNew->EventSem);
431 if (RT_SUCCESS(rc))
432 {
433 rc = RTSemEventCreate(&pAioMgrNew->EventSemBlock);
434 if (RT_SUCCESS(rc))
435 {
436 rc = RTCritSectInit(&pAioMgrNew->CritSectBlockingEvent);
437 if (RT_SUCCESS(rc))
438 {
439 /* Init the rest of the manager. */
440 if (pAioMgrNew->enmMgrType != PDMACEPFILEMGRTYPE_SIMPLE)
441 rc = pdmacFileAioMgrNormalInit(pAioMgrNew);
442
443 if (RT_SUCCESS(rc))
444 {
445 pAioMgrNew->enmState = PDMACEPFILEMGRSTATE_RUNNING;
446
447 rc = RTThreadCreateF(&pAioMgrNew->Thread,
448 pAioMgrNew->enmMgrType == PDMACEPFILEMGRTYPE_SIMPLE
449 ? pdmacFileAioMgrFailsafe
450 : pdmacFileAioMgrNormal,
451 pAioMgrNew,
452 0,
453 RTTHREADTYPE_IO,
454 0,
455 "AioMgr%d-%s", pEpClass->cAioMgrs,
456 pAioMgrNew->enmMgrType == PDMACEPFILEMGRTYPE_SIMPLE
457 ? "F"
458 : "N");
459 if (RT_SUCCESS(rc))
460 {
461 /* Link it into the list. */
462 RTCritSectEnter(&pEpClass->CritSect);
463 pAioMgrNew->pNext = pEpClass->pAioMgrHead;
464 if (pEpClass->pAioMgrHead)
465 pEpClass->pAioMgrHead->pPrev = pAioMgrNew;
466 pEpClass->pAioMgrHead = pAioMgrNew;
467 pEpClass->cAioMgrs++;
468 RTCritSectLeave(&pEpClass->CritSect);
469
470 *ppAioMgr = pAioMgrNew;
471
472 Log(("PDMAC: Successfully created new file AIO Mgr {%s}\n", RTThreadGetName(pAioMgrNew->Thread)));
473 return VINF_SUCCESS;
474 }
475 pdmacFileAioMgrNormalDestroy(pAioMgrNew);
476 }
477 RTCritSectDelete(&pAioMgrNew->CritSectBlockingEvent);
478 }
479 RTSemEventDestroy(pAioMgrNew->EventSem);
480 }
481 RTSemEventDestroy(pAioMgrNew->EventSemBlock);
482 }
483 MMR3HeapFree(pAioMgrNew);
484 }
485
486 LogFlowFunc((": Leave rc=%Rrc\n", rc));
487
488 return rc;
489}
490
491/**
492 * Destroys a async I/O manager.
493 *
494 * @returns nothing.
495 * @param pAioMgr The async I/O manager to destroy.
496 */
497static void pdmacFileAioMgrDestroy(PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile, PPDMACEPFILEMGR pAioMgr)
498{
499 int rc = pdmacFileAioMgrShutdown(pAioMgr);
500 AssertRC(rc);
501
502 /* Unlink from the list. */
503 rc = RTCritSectEnter(&pEpClassFile->CritSect);
504 AssertRC(rc);
505
506 PPDMACEPFILEMGR pPrev = pAioMgr->pPrev;
507 PPDMACEPFILEMGR pNext = pAioMgr->pNext;
508
509 if (pPrev)
510 pPrev->pNext = pNext;
511 else
512 pEpClassFile->pAioMgrHead = pNext;
513
514 if (pNext)
515 pNext->pPrev = pPrev;
516
517 pEpClassFile->cAioMgrs--;
518 rc = RTCritSectLeave(&pEpClassFile->CritSect);
519 AssertRC(rc);
520
521 /* Free the resources. */
522 RTCritSectDelete(&pAioMgr->CritSectBlockingEvent);
523 RTSemEventDestroy(pAioMgr->EventSem);
524 if (pAioMgr->enmMgrType != PDMACEPFILEMGRTYPE_SIMPLE)
525 pdmacFileAioMgrNormalDestroy(pAioMgr);
526
527 MMR3HeapFree(pAioMgr);
528}
529
530static int pdmacFileMgrTypeFromName(const char *pszVal, PPDMACEPFILEMGRTYPE penmMgrType)
531{
532 int rc = VINF_SUCCESS;
533
534 if (!RTStrCmp(pszVal, "Simple"))
535 *penmMgrType = PDMACEPFILEMGRTYPE_SIMPLE;
536 else if (!RTStrCmp(pszVal, "Async"))
537 *penmMgrType = PDMACEPFILEMGRTYPE_ASYNC;
538 else
539 rc = VERR_CFGM_CONFIG_UNKNOWN_VALUE;
540
541 return rc;
542}
543
544static const char *pdmacFileMgrTypeToName(PDMACEPFILEMGRTYPE enmMgrType)
545{
546 if (enmMgrType == PDMACEPFILEMGRTYPE_SIMPLE)
547 return "Simple";
548 if (enmMgrType == PDMACEPFILEMGRTYPE_ASYNC)
549 return "Async";
550
551 return NULL;
552}
553
554static int pdmacFileBackendTypeFromName(const char *pszVal, PPDMACFILEEPBACKEND penmBackendType)
555{
556 int rc = VINF_SUCCESS;
557
558 if (!RTStrCmp(pszVal, "Buffered"))
559 *penmBackendType = PDMACFILEEPBACKEND_BUFFERED;
560 else if (!RTStrCmp(pszVal, "NonBuffered"))
561 *penmBackendType = PDMACFILEEPBACKEND_NON_BUFFERED;
562 else
563 rc = VERR_CFGM_CONFIG_UNKNOWN_VALUE;
564
565 return rc;
566}
567
568static const char *pdmacFileBackendTypeToName(PDMACFILEEPBACKEND enmBackendType)
569{
570 if (enmBackendType == PDMACFILEEPBACKEND_BUFFERED)
571 return "Buffered";
572 if (enmBackendType == PDMACFILEEPBACKEND_NON_BUFFERED)
573 return "NonBuffered";
574
575 return NULL;
576}
577
578/**
579 * Get the size of the given file.
580 * Works for block devices too.
581 *
582 * @returns VBox status code.
583 * @param hFile The file handle.
584 * @param pcbSize Where to store the size of the file on success.
585 */
586static int pdmacFileEpNativeGetSize(RTFILE hFile, uint64_t *pcbSize)
587{
588 uint64_t cbFile;
589 int rc = RTFileGetSize(hFile, &cbFile);
590 if (RT_SUCCESS(rc))
591 {
592 if (cbFile != 0)
593 *pcbSize = cbFile;
594 else
595 rc = VERR_INVALID_PARAMETER;
596 }
597
598 return rc;
599}
600
601#ifdef VBOX_WITH_DEBUGGER
602
603/**
604 * Error inject callback.
605 */
606static DECLCALLBACK(int) pdmacEpFileErrorInject(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR pArgs, unsigned cArgs)
607{
608 /*
609 * Validate input.
610 */
611 DBGC_CMDHLP_REQ_VM_RET(pCmdHlp, pCmd, pVM);
612 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, -1, cArgs == 3);
613 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, pArgs[0].enmType == DBGCVAR_TYPE_STRING);
614 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 1, pArgs[1].enmType == DBGCVAR_TYPE_STRING);
615 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 2, pArgs[2].enmType == DBGCVAR_TYPE_NUMBER);
616
617 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile;
618 pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pVM->pUVM->pdm.s.apAsyncCompletionEndpointClass[PDMASYNCCOMPLETIONEPCLASSTYPE_FILE];
619
620 /* Syntax is "read|write <filename> <status code>" */
621 bool fWrite;
622 if (!RTStrCmp(pArgs[0].u.pszString, "read"))
623 fWrite = false;
624 else if (!RTStrCmp(pArgs[0].u.pszString, "write"))
625 fWrite = true;
626 else
627 return DBGCCmdHlpFail(pCmdHlp, pCmd, "invalid transfer direction '%s'", pArgs[0].u.pszString);
628
629 int32_t rcToInject = (int32_t)pArgs[2].u.u64Number;
630 if ((uint64_t)rcToInject != pArgs[2].u.u64Number)
631 return DBGCCmdHlpFail(pCmdHlp, pCmd, "The status code '%lld' is out of range", pArgs[0].u.u64Number);
632
633
634 /*
635 * Search for the matching endpoint.
636 */
637 RTCritSectEnter(&pEpClassFile->Core.CritSect);
638
639 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEpClassFile->Core.pEndpointsHead;
640 while (pEpFile)
641 {
642 if (!RTStrCmp(pArgs[1].u.pszString, RTPathFilename(pEpFile->Core.pszUri)))
643 break;
644 pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEpFile->Core.pNext;
645 }
646
647 if (pEpFile)
648 {
649 /*
650 * Do the job.
651 */
652 if (fWrite)
653 ASMAtomicXchgS32(&pEpFile->rcReqWrite, rcToInject);
654 else
655 ASMAtomicXchgS32(&pEpFile->rcReqRead, rcToInject);
656
657 DBGCCmdHlpPrintf(pCmdHlp, "Injected %Rrc into '%s' for %s\n",
658 (int)rcToInject, pArgs[1].u.pszString, pArgs[0].u.pszString);
659 }
660
661 RTCritSectLeave(&pEpClassFile->Core.CritSect);
662
663 if (!pEpFile)
664 return DBGCCmdHlpFail(pCmdHlp, pCmd, "No file with name '%s' found", pArgs[1].u.pszString);
665 return VINF_SUCCESS;
666}
667
668# ifdef PDM_ASYNC_COMPLETION_FILE_WITH_DELAY
669/**
670 * Delay inject callback.
671 */
672static DECLCALLBACK(int) pdmacEpFileDelayInject(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR pArgs, unsigned cArgs)
673{
674 /*
675 * Validate input.
676 */
677 DBGC_CMDHLP_REQ_VM_RET(pCmdHlp, pCmd, pVM);
678 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, -1, cArgs == 3);
679 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, pArgs[0].enmType == DBGCVAR_TYPE_STRING);
680 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 1, pArgs[1].enmType == DBGCVAR_TYPE_STRING);
681 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 2, pArgs[2].enmType == DBGCVAR_TYPE_NUMBER);
682
683 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile;
684 pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pVM->pUVM->pdm.s.apAsyncCompletionEndpointClass[PDMASYNCCOMPLETIONEPCLASSTYPE_FILE];
685
686 /* Syntax is "read|write <filename> <status code>" */
687 bool fWrite;
688 if (!RTStrCmp(pArgs[0].u.pszString, "read"))
689 fWrite = false;
690 else if (!RTStrCmp(pArgs[0].u.pszString, "write"))
691 fWrite = true;
692 else
693 return DBGCCmdHlpFail(pCmdHlp, pCmd, "invalid transfer direction '%s'", pArgs[0].u.pszString);
694
695 uint32_t msDelay = (uint32_t)pArgs[2].u.u64Number;
696 if ((uint64_t)msDelay != pArgs[2].u.u64Number)
697 return DBGCCmdHlpFail(pCmdHlp, pCmd, "The delay '%lld' is out of range", pArgs[0].u.u64Number);
698
699
700 /*
701 * Search for the matching endpoint.
702 */
703 RTCritSectEnter(&pEpClassFile->Core.CritSect);
704
705 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEpClassFile->Core.pEndpointsHead;
706 while (pEpFile)
707 {
708 if (!RTStrCmp(pArgs[1].u.pszString, RTPathFilename(pEpFile->Core.pszUri)))
709 break;
710 pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEpFile->Core.pNext;
711 }
712
713 if (pEpFile)
714 {
715 bool fXchg = ASMAtomicCmpXchgU32(&pEpFile->msDelay, msDelay, 0);
716
717 if (fXchg)
718 DBGCCmdHlpPrintf(pCmdHlp, "Injected delay of %u ms into '%s' for %s\n",
719 msDelay, pArgs[1].u.pszString, pArgs[0].u.pszString);
720 else
721 DBGCCmdHlpPrintf(pCmdHlp, "Another delay for '%s' is still active, ignoring\n",
722 pArgs[1].u.pszString);
723 }
724
725 RTCritSectLeave(&pEpClassFile->Core.CritSect);
726
727 if (!pEpFile)
728 return DBGCCmdHlpFail(pCmdHlp, pCmd, "No file with name '%s' found", pArgs[1].u.pszString);
729 return VINF_SUCCESS;
730}
731# endif /* PDM_ASYNC_COMPLETION_FILE_WITH_DELAY */
732
733#endif /* VBOX_WITH_DEBUGGER */
734
735static int pdmacFileInitialize(PPDMASYNCCOMPLETIONEPCLASS pClassGlobals, PCFGMNODE pCfgNode)
736{
737 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pClassGlobals;
738 RTFILEAIOLIMITS AioLimits; /** < Async I/O limitations. */
739
740 int rc = RTFileAioGetLimits(&AioLimits);
741#ifdef DEBUG
742 if (RT_SUCCESS(rc) && RTEnvExist("VBOX_ASYNC_IO_FAILBACK"))
743 rc = VERR_ENV_VAR_NOT_FOUND;
744#endif
745 if (RT_FAILURE(rc))
746 {
747 LogRel(("AIO: Async I/O manager not supported (rc=%Rrc). Falling back to simple manager\n",
748 rc));
749 pEpClassFile->enmMgrTypeOverride = PDMACEPFILEMGRTYPE_SIMPLE;
750 pEpClassFile->enmEpBackendDefault = PDMACFILEEPBACKEND_BUFFERED;
751 }
752 else
753 {
754 pEpClassFile->uBitmaskAlignment = AioLimits.cbBufferAlignment ? ~((RTR3UINTPTR)AioLimits.cbBufferAlignment - 1) : RTR3UINTPTR_MAX;
755 pEpClassFile->cReqsOutstandingMax = AioLimits.cReqsOutstandingMax;
756
757 if (pCfgNode)
758 {
759 /* Query the default manager type */
760 char *pszVal = NULL;
761 rc = CFGMR3QueryStringAllocDef(pCfgNode, "IoMgr", &pszVal, "Async");
762 AssertLogRelRCReturn(rc, rc);
763
764 rc = pdmacFileMgrTypeFromName(pszVal, &pEpClassFile->enmMgrTypeOverride);
765 MMR3HeapFree(pszVal);
766 if (RT_FAILURE(rc))
767 return rc;
768
769 LogRel(("AIOMgr: Default manager type is \"%s\"\n", pdmacFileMgrTypeToName(pEpClassFile->enmMgrTypeOverride)));
770
771 /* Query default backend type */
772 rc = CFGMR3QueryStringAllocDef(pCfgNode, "FileBackend", &pszVal, "NonBuffered");
773 AssertLogRelRCReturn(rc, rc);
774
775 rc = pdmacFileBackendTypeFromName(pszVal, &pEpClassFile->enmEpBackendDefault);
776 MMR3HeapFree(pszVal);
777 if (RT_FAILURE(rc))
778 return rc;
779
780 LogRel(("AIOMgr: Default file backend is \"%s\"\n", pdmacFileBackendTypeToName(pEpClassFile->enmEpBackendDefault)));
781
782#ifdef RT_OS_LINUX
783 if ( pEpClassFile->enmMgrTypeOverride == PDMACEPFILEMGRTYPE_ASYNC
784 && pEpClassFile->enmEpBackendDefault == PDMACFILEEPBACKEND_BUFFERED)
785 {
786 LogRel(("AIOMgr: Linux does not support buffered async I/O, changing to non buffered\n"));
787 pEpClassFile->enmEpBackendDefault = PDMACFILEEPBACKEND_NON_BUFFERED;
788 }
789#endif
790 }
791 else
792 {
793 /* No configuration supplied, set defaults */
794 pEpClassFile->enmEpBackendDefault = PDMACFILEEPBACKEND_NON_BUFFERED;
795 pEpClassFile->enmMgrTypeOverride = PDMACEPFILEMGRTYPE_ASYNC;
796 }
797 }
798
799 /* Init critical section. */
800 rc = RTCritSectInit(&pEpClassFile->CritSect);
801
802#ifdef VBOX_WITH_DEBUGGER
803 /* Install the error injection handler. */
804 if (RT_SUCCESS(rc))
805 {
806 rc = DBGCRegisterCommands(&g_aCmds[0], RT_ELEMENTS(g_aCmds));
807 AssertRC(rc);
808 }
809#endif
810
811 return rc;
812}
813
814static void pdmacFileTerminate(PPDMASYNCCOMPLETIONEPCLASS pClassGlobals)
815{
816 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pClassGlobals;
817
818 /* All endpoints should be closed at this point. */
819 AssertMsg(!pEpClassFile->Core.pEndpointsHead, ("There are still endpoints left\n"));
820
821 /* Destroy all left async I/O managers. */
822 while (pEpClassFile->pAioMgrHead)
823 pdmacFileAioMgrDestroy(pEpClassFile, pEpClassFile->pAioMgrHead);
824
825 RTCritSectDelete(&pEpClassFile->CritSect);
826}
827
828static int pdmacFileEpInitialize(PPDMASYNCCOMPLETIONENDPOINT pEndpoint,
829 const char *pszUri, uint32_t fFlags)
830{
831 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
832 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pEndpoint->pEpClass;
833 PDMACEPFILEMGRTYPE enmMgrType = pEpClassFile->enmMgrTypeOverride;
834 PDMACFILEEPBACKEND enmEpBackend = pEpClassFile->enmEpBackendDefault;
835
836 AssertMsgReturn((fFlags & ~(PDMACEP_FILE_FLAGS_READ_ONLY | PDMACEP_FILE_FLAGS_DONT_LOCK | PDMACEP_FILE_FLAGS_HOST_CACHE_ENABLED)) == 0,
837 ("PDMAsyncCompletion: Invalid flag specified\n"), VERR_INVALID_PARAMETER);
838
839 unsigned fFileFlags = RTFILE_O_OPEN;
840
841 /*
842 * Revert to the simple manager and the buffered backend if
843 * the host cache should be enabled.
844 */
845 if (fFlags & PDMACEP_FILE_FLAGS_HOST_CACHE_ENABLED)
846 {
847 enmMgrType = PDMACEPFILEMGRTYPE_SIMPLE;
848 enmEpBackend = PDMACFILEEPBACKEND_BUFFERED;
849 }
850
851 if (fFlags & PDMACEP_FILE_FLAGS_READ_ONLY)
852 fFileFlags |= RTFILE_O_READ | RTFILE_O_DENY_NONE;
853 else
854 {
855 fFileFlags |= RTFILE_O_READWRITE;
856
857 /*
858 * Opened in read/write mode. Check whether the caller wants to
859 * avoid the lock. Return an error in case caching is enabled
860 * because this can lead to data corruption.
861 */
862 if (fFlags & PDMACEP_FILE_FLAGS_DONT_LOCK)
863 fFileFlags |= RTFILE_O_DENY_NONE;
864 else
865 fFileFlags |= RTFILE_O_DENY_WRITE;
866 }
867
868 if (enmMgrType == PDMACEPFILEMGRTYPE_ASYNC)
869 fFileFlags |= RTFILE_O_ASYNC_IO;
870
871 int rc;
872 if (enmEpBackend == PDMACFILEEPBACKEND_NON_BUFFERED)
873 {
874 /*
875 * We only disable the cache if the size of the file is a multiple of 512.
876 * Certain hosts like Windows, Linux and Solaris require that transfer sizes
877 * are aligned to the volume sector size.
878 * If not we just make sure that the data is written to disk with RTFILE_O_WRITE_THROUGH
879 * which will trash the host cache but ensures that the host cache will not
880 * contain dirty buffers.
881 */
882 RTFILE hFile;
883 rc = RTFileOpen(&hFile, pszUri, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
884 if (RT_SUCCESS(rc))
885 {
886 uint64_t cbSize;
887
888 rc = pdmacFileEpNativeGetSize(hFile, &cbSize);
889 Assert(RT_FAILURE(rc) || cbSize != 0);
890
891 if (RT_SUCCESS(rc) && ((cbSize % 512) == 0))
892 fFileFlags |= RTFILE_O_NO_CACHE;
893 else
894 {
895 /* Downgrade to the buffered backend */
896 enmEpBackend = PDMACFILEEPBACKEND_BUFFERED;
897
898#ifdef RT_OS_LINUX
899 fFileFlags &= ~RTFILE_O_ASYNC_IO;
900 enmMgrType = PDMACEPFILEMGRTYPE_SIMPLE;
901#endif
902 }
903 RTFileClose(hFile);
904 }
905 }
906
907 /* Open with final flags. */
908 rc = RTFileOpen(&pEpFile->hFile, pszUri, fFileFlags);
909 if ( rc == VERR_INVALID_FUNCTION
910 || rc == VERR_INVALID_PARAMETER)
911 {
912 LogRel(("pdmacFileEpInitialize: RTFileOpen %s / %08x failed with %Rrc\n",
913 pszUri, fFileFlags, rc));
914 /*
915 * Solaris doesn't support directio on ZFS so far. :-\
916 * Trying to enable it returns VERR_INVALID_FUNCTION
917 * (ENOTTY). Remove it and hope for the best.
918 * ZFS supports write throttling in case applications
919 * write more data than can be synced to the disk
920 * without blocking the whole application.
921 *
922 * On Linux we have the same problem with cifs.
923 * Have to disable async I/O here too because it requires O_DIRECT.
924 */
925 fFileFlags &= ~RTFILE_O_NO_CACHE;
926 enmEpBackend = PDMACFILEEPBACKEND_BUFFERED;
927
928#ifdef RT_OS_LINUX
929 fFileFlags &= ~RTFILE_O_ASYNC_IO;
930 enmMgrType = PDMACEPFILEMGRTYPE_SIMPLE;
931#endif
932
933 /* Open again. */
934 rc = RTFileOpen(&pEpFile->hFile, pszUri, fFileFlags);
935
936 if (RT_FAILURE(rc))
937 {
938 LogRel(("pdmacFileEpInitialize: RTFileOpen %s / %08x failed AGAIN(!) with %Rrc\n",
939 pszUri, fFileFlags, rc));
940 }
941 }
942
943 if (RT_SUCCESS(rc))
944 {
945 pEpFile->fFlags = fFileFlags;
946
947 rc = pdmacFileEpNativeGetSize(pEpFile->hFile, (uint64_t *)&pEpFile->cbFile);
948 Assert(RT_FAILURE(rc) || pEpFile->cbFile != 0);
949
950 if (RT_SUCCESS(rc))
951 {
952 /* Initialize the segment cache */
953 rc = MMR3HeapAllocZEx(pEpClassFile->Core.pVM, MM_TAG_PDM_ASYNC_COMPLETION,
954 sizeof(PDMACTASKFILE),
955 (void **)&pEpFile->pTasksFreeHead);
956 if (RT_SUCCESS(rc))
957 {
958 PPDMACEPFILEMGR pAioMgr = NULL;
959
960 pEpFile->pTasksFreeTail = pEpFile->pTasksFreeHead;
961 pEpFile->cTasksCached = 0;
962 pEpFile->enmBackendType = enmEpBackend;
963 /*
964 * Disable async flushes on Solaris for now.
965 * They cause weird hangs which needs more investigations.
966 */
967#ifndef RT_OS_SOLARIS
968 pEpFile->fAsyncFlushSupported = true;
969#else
970 pEpFile->fAsyncFlushSupported = false;
971#endif
972
973 if (enmMgrType == PDMACEPFILEMGRTYPE_SIMPLE)
974 {
975 /* Simple mode. Every file has its own async I/O manager. */
976 rc = pdmacFileAioMgrCreate(pEpClassFile, &pAioMgr, PDMACEPFILEMGRTYPE_SIMPLE);
977 AssertRC(rc);
978 }
979 else
980 {
981 pAioMgr = pEpClassFile->pAioMgrHead;
982
983 /* Check for an idling manager of the same type */
984 while (pAioMgr)
985 {
986 if (pAioMgr->enmMgrType == enmMgrType)
987 break;
988 pAioMgr = pAioMgr->pNext;
989 }
990
991 if (!pAioMgr)
992 {
993 rc = pdmacFileAioMgrCreate(pEpClassFile, &pAioMgr, enmMgrType);
994 AssertRC(rc);
995 }
996 }
997
998 pEpFile->AioMgr.pTreeRangesLocked = (PAVLRFOFFTREE)RTMemAllocZ(sizeof(AVLRFOFFTREE));
999 if (!pEpFile->AioMgr.pTreeRangesLocked)
1000 rc = VERR_NO_MEMORY;
1001 else
1002 {
1003 pEpFile->enmState = PDMASYNCCOMPLETIONENDPOINTFILESTATE_ACTIVE;
1004
1005 /* Assign the endpoint to the thread. */
1006 rc = pdmacFileAioMgrAddEndpoint(pAioMgr, pEpFile);
1007 if (RT_FAILURE(rc))
1008 {
1009 RTMemFree(pEpFile->AioMgr.pTreeRangesLocked);
1010 MMR3HeapFree(pEpFile->pTasksFreeHead);
1011 }
1012 }
1013 }
1014 }
1015
1016 if (RT_FAILURE(rc))
1017 RTFileClose(pEpFile->hFile);
1018 }
1019
1020#ifdef VBOX_WITH_STATISTICS
1021 if (RT_SUCCESS(rc))
1022 {
1023 STAMR3RegisterF(pEpClassFile->Core.pVM, &pEpFile->StatRead,
1024 STAMTYPE_PROFILE_ADV, STAMVISIBILITY_ALWAYS,
1025 STAMUNIT_TICKS_PER_CALL, "Time taken to read from the endpoint",
1026 "/PDM/AsyncCompletion/File/%s/Read", RTPathFilename(pEpFile->Core.pszUri));
1027
1028 STAMR3RegisterF(pEpClassFile->Core.pVM, &pEpFile->StatWrite,
1029 STAMTYPE_PROFILE_ADV, STAMVISIBILITY_ALWAYS,
1030 STAMUNIT_TICKS_PER_CALL, "Time taken to write to the endpoint",
1031 "/PDM/AsyncCompletion/File/%s/Write", RTPathFilename(pEpFile->Core.pszUri));
1032 }
1033#endif
1034
1035 if (RT_SUCCESS(rc))
1036 LogRel(("AIOMgr: Endpoint for file '%s' (flags %08x) created successfully\n", pszUri, pEpFile->fFlags));
1037
1038 return rc;
1039}
1040
1041static int pdmacFileEpRangesLockedDestroy(PAVLRFOFFNODECORE pNode, void *pvUser)
1042{
1043 NOREF(pNode); NOREF(pvUser);
1044 AssertMsgFailed(("The locked ranges tree should be empty at that point\n"));
1045 return VINF_SUCCESS;
1046}
1047
1048static int pdmacFileEpClose(PPDMASYNCCOMPLETIONENDPOINT pEndpoint)
1049{
1050 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1051 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pEndpoint->pEpClass;
1052
1053 /* Make sure that all tasks finished for this endpoint. */
1054 int rc = pdmacFileAioMgrCloseEndpoint(pEpFile->pAioMgr, pEpFile);
1055 AssertRC(rc);
1056
1057 /*
1058 * If the async I/O manager is in failsafe mode this is the only endpoint
1059 * he processes and thus can be destroyed now.
1060 */
1061 if (pEpFile->pAioMgr->enmMgrType == PDMACEPFILEMGRTYPE_SIMPLE)
1062 pdmacFileAioMgrDestroy(pEpClassFile, pEpFile->pAioMgr);
1063
1064 /* Free cached tasks. */
1065 PPDMACTASKFILE pTask = pEpFile->pTasksFreeHead;
1066
1067 while (pTask)
1068 {
1069 PPDMACTASKFILE pTaskFree = pTask;
1070 pTask = pTask->pNext;
1071 MMR3HeapFree(pTaskFree);
1072 }
1073
1074 /* Destroy the locked ranges tree now. */
1075 RTAvlrFileOffsetDestroy(pEpFile->AioMgr.pTreeRangesLocked, pdmacFileEpRangesLockedDestroy, NULL);
1076
1077 RTFileClose(pEpFile->hFile);
1078
1079#ifdef VBOX_WITH_STATISTICS
1080 STAMR3Deregister(pEpClassFile->Core.pVM, &pEpFile->StatRead);
1081 STAMR3Deregister(pEpClassFile->Core.pVM, &pEpFile->StatWrite);
1082#endif
1083
1084 return VINF_SUCCESS;
1085}
1086
1087static int pdmacFileEpRead(PPDMASYNCCOMPLETIONTASK pTask,
1088 PPDMASYNCCOMPLETIONENDPOINT pEndpoint, RTFOFF off,
1089 PCRTSGSEG paSegments, size_t cSegments,
1090 size_t cbRead)
1091{
1092 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1093
1094 LogFlowFunc(("pTask=%#p pEndpoint=%#p off=%RTfoff paSegments=%#p cSegments=%zu cbRead=%zu\n",
1095 pTask, pEndpoint, off, paSegments, cSegments, cbRead));
1096
1097 if (RT_UNLIKELY((uint64_t)off + cbRead > pEpFile->cbFile))
1098 return VERR_EOF;
1099
1100 STAM_PROFILE_ADV_START(&pEpFile->StatRead, Read);
1101 pdmacFileEpTaskInit(pTask, cbRead);
1102 int rc = pdmacFileEpTaskInitiate(pTask, pEndpoint, off, paSegments, cSegments, cbRead,
1103 PDMACTASKFILETRANSFER_READ);
1104 STAM_PROFILE_ADV_STOP(&pEpFile->StatRead, Read);
1105
1106 return rc;
1107}
1108
1109static int pdmacFileEpWrite(PPDMASYNCCOMPLETIONTASK pTask,
1110 PPDMASYNCCOMPLETIONENDPOINT pEndpoint, RTFOFF off,
1111 PCRTSGSEG paSegments, size_t cSegments,
1112 size_t cbWrite)
1113{
1114 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1115
1116 if (RT_UNLIKELY(pEpFile->fReadonly))
1117 return VERR_NOT_SUPPORTED;
1118
1119 STAM_PROFILE_ADV_START(&pEpFile->StatWrite, Write);
1120
1121 pdmacFileEpTaskInit(pTask, cbWrite);
1122
1123 int rc = pdmacFileEpTaskInitiate(pTask, pEndpoint, off, paSegments, cSegments, cbWrite,
1124 PDMACTASKFILETRANSFER_WRITE);
1125
1126 STAM_PROFILE_ADV_STOP(&pEpFile->StatWrite, Write);
1127
1128 return rc;
1129}
1130
1131static int pdmacFileEpFlush(PPDMASYNCCOMPLETIONTASK pTask,
1132 PPDMASYNCCOMPLETIONENDPOINT pEndpoint)
1133{
1134 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1135 PPDMASYNCCOMPLETIONTASKFILE pTaskFile = (PPDMASYNCCOMPLETIONTASKFILE)pTask;
1136
1137 if (RT_UNLIKELY(pEpFile->fReadonly))
1138 return VERR_NOT_SUPPORTED;
1139
1140 pdmacFileEpTaskInit(pTask, 0);
1141
1142 PPDMACTASKFILE pIoTask = pdmacFileTaskAlloc(pEpFile);
1143 if (RT_UNLIKELY(!pIoTask))
1144 return VERR_NO_MEMORY;
1145
1146 pIoTask->pEndpoint = pEpFile;
1147 pIoTask->enmTransferType = PDMACTASKFILETRANSFER_FLUSH;
1148 pIoTask->pvUser = pTaskFile;
1149 pIoTask->pfnCompleted = pdmacFileEpTaskCompleted;
1150 pdmacFileEpAddTask(pEpFile, pIoTask);
1151
1152 return VINF_AIO_TASK_PENDING;
1153}
1154
1155static int pdmacFileEpGetSize(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, uint64_t *pcbSize)
1156{
1157 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1158
1159 *pcbSize = ASMAtomicReadU64(&pEpFile->cbFile);
1160
1161 return VINF_SUCCESS;
1162}
1163
1164static int pdmacFileEpSetSize(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, uint64_t cbSize)
1165{
1166 int rc;
1167 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1168
1169 rc = RTFileSetSize(pEpFile->hFile, cbSize);
1170 if (RT_SUCCESS(rc))
1171 ASMAtomicWriteU64(&pEpFile->cbFile, cbSize);
1172
1173 return rc;
1174}
1175
1176const PDMASYNCCOMPLETIONEPCLASSOPS g_PDMAsyncCompletionEndpointClassFile =
1177{
1178 /* u32Version */
1179 PDMAC_EPCLASS_OPS_VERSION,
1180 /* pcszName */
1181 "File",
1182 /* enmClassType */
1183 PDMASYNCCOMPLETIONEPCLASSTYPE_FILE,
1184 /* cbEndpointClassGlobal */
1185 sizeof(PDMASYNCCOMPLETIONEPCLASSFILE),
1186 /* cbEndpoint */
1187 sizeof(PDMASYNCCOMPLETIONENDPOINTFILE),
1188 /* cbTask */
1189 sizeof(PDMASYNCCOMPLETIONTASKFILE),
1190 /* pfnInitialize */
1191 pdmacFileInitialize,
1192 /* pfnTerminate */
1193 pdmacFileTerminate,
1194 /* pfnEpInitialize. */
1195 pdmacFileEpInitialize,
1196 /* pfnEpClose */
1197 pdmacFileEpClose,
1198 /* pfnEpRead */
1199 pdmacFileEpRead,
1200 /* pfnEpWrite */
1201 pdmacFileEpWrite,
1202 /* pfnEpFlush */
1203 pdmacFileEpFlush,
1204 /* pfnEpGetSize */
1205 pdmacFileEpGetSize,
1206 /* pfnEpSetSize */
1207 pdmacFileEpSetSize,
1208 /* u32VersionEnd */
1209 PDMAC_EPCLASS_OPS_VERSION
1210};
1211
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