VirtualBox

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

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

PDMAsyncCompletionFile: Allow empty files to be used (fixes tstPDMAsyncCompletionStress)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 42.1 KB
Line 
1/* $Id: PDMAsyncCompletionFile.cpp 41434 2012-05-24 13:17:46Z 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.215389.xyz. 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 *pcbSize = cbFile;
592
593 return rc;
594}
595
596#ifdef VBOX_WITH_DEBUGGER
597
598/**
599 * Error inject callback.
600 */
601static DECLCALLBACK(int) pdmacEpFileErrorInject(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR pArgs, unsigned cArgs)
602{
603 /*
604 * Validate input.
605 */
606 DBGC_CMDHLP_REQ_VM_RET(pCmdHlp, pCmd, pVM);
607 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, -1, cArgs == 3);
608 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, pArgs[0].enmType == DBGCVAR_TYPE_STRING);
609 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 1, pArgs[1].enmType == DBGCVAR_TYPE_STRING);
610 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 2, pArgs[2].enmType == DBGCVAR_TYPE_NUMBER);
611
612 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile;
613 pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pVM->pUVM->pdm.s.apAsyncCompletionEndpointClass[PDMASYNCCOMPLETIONEPCLASSTYPE_FILE];
614
615 /* Syntax is "read|write <filename> <status code>" */
616 bool fWrite;
617 if (!RTStrCmp(pArgs[0].u.pszString, "read"))
618 fWrite = false;
619 else if (!RTStrCmp(pArgs[0].u.pszString, "write"))
620 fWrite = true;
621 else
622 return DBGCCmdHlpFail(pCmdHlp, pCmd, "invalid transfer direction '%s'", pArgs[0].u.pszString);
623
624 int32_t rcToInject = (int32_t)pArgs[2].u.u64Number;
625 if ((uint64_t)rcToInject != pArgs[2].u.u64Number)
626 return DBGCCmdHlpFail(pCmdHlp, pCmd, "The status code '%lld' is out of range", pArgs[0].u.u64Number);
627
628
629 /*
630 * Search for the matching endpoint.
631 */
632 RTCritSectEnter(&pEpClassFile->Core.CritSect);
633
634 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEpClassFile->Core.pEndpointsHead;
635 while (pEpFile)
636 {
637 if (!RTStrCmp(pArgs[1].u.pszString, RTPathFilename(pEpFile->Core.pszUri)))
638 break;
639 pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEpFile->Core.pNext;
640 }
641
642 if (pEpFile)
643 {
644 /*
645 * Do the job.
646 */
647 if (fWrite)
648 ASMAtomicXchgS32(&pEpFile->rcReqWrite, rcToInject);
649 else
650 ASMAtomicXchgS32(&pEpFile->rcReqRead, rcToInject);
651
652 DBGCCmdHlpPrintf(pCmdHlp, "Injected %Rrc into '%s' for %s\n",
653 (int)rcToInject, pArgs[1].u.pszString, pArgs[0].u.pszString);
654 }
655
656 RTCritSectLeave(&pEpClassFile->Core.CritSect);
657
658 if (!pEpFile)
659 return DBGCCmdHlpFail(pCmdHlp, pCmd, "No file with name '%s' found", pArgs[1].u.pszString);
660 return VINF_SUCCESS;
661}
662
663# ifdef PDM_ASYNC_COMPLETION_FILE_WITH_DELAY
664/**
665 * Delay inject callback.
666 */
667static DECLCALLBACK(int) pdmacEpFileDelayInject(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR pArgs, unsigned cArgs)
668{
669 /*
670 * Validate input.
671 */
672 DBGC_CMDHLP_REQ_VM_RET(pCmdHlp, pCmd, pVM);
673 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, -1, cArgs == 3);
674 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, pArgs[0].enmType == DBGCVAR_TYPE_STRING);
675 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 1, pArgs[1].enmType == DBGCVAR_TYPE_STRING);
676 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 2, pArgs[2].enmType == DBGCVAR_TYPE_NUMBER);
677
678 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile;
679 pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pVM->pUVM->pdm.s.apAsyncCompletionEndpointClass[PDMASYNCCOMPLETIONEPCLASSTYPE_FILE];
680
681 /* Syntax is "read|write <filename> <status code>" */
682 bool fWrite;
683 if (!RTStrCmp(pArgs[0].u.pszString, "read"))
684 fWrite = false;
685 else if (!RTStrCmp(pArgs[0].u.pszString, "write"))
686 fWrite = true;
687 else
688 return DBGCCmdHlpFail(pCmdHlp, pCmd, "invalid transfer direction '%s'", pArgs[0].u.pszString);
689
690 uint32_t msDelay = (uint32_t)pArgs[2].u.u64Number;
691 if ((uint64_t)msDelay != pArgs[2].u.u64Number)
692 return DBGCCmdHlpFail(pCmdHlp, pCmd, "The delay '%lld' is out of range", pArgs[0].u.u64Number);
693
694
695 /*
696 * Search for the matching endpoint.
697 */
698 RTCritSectEnter(&pEpClassFile->Core.CritSect);
699
700 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEpClassFile->Core.pEndpointsHead;
701 while (pEpFile)
702 {
703 if (!RTStrCmp(pArgs[1].u.pszString, RTPathFilename(pEpFile->Core.pszUri)))
704 break;
705 pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEpFile->Core.pNext;
706 }
707
708 if (pEpFile)
709 {
710 bool fXchg = ASMAtomicCmpXchgU32(&pEpFile->msDelay, msDelay, 0);
711
712 if (fXchg)
713 DBGCCmdHlpPrintf(pCmdHlp, "Injected delay of %u ms into '%s' for %s\n",
714 msDelay, pArgs[1].u.pszString, pArgs[0].u.pszString);
715 else
716 DBGCCmdHlpPrintf(pCmdHlp, "Another delay for '%s' is still active, ignoring\n",
717 pArgs[1].u.pszString);
718 }
719
720 RTCritSectLeave(&pEpClassFile->Core.CritSect);
721
722 if (!pEpFile)
723 return DBGCCmdHlpFail(pCmdHlp, pCmd, "No file with name '%s' found", pArgs[1].u.pszString);
724 return VINF_SUCCESS;
725}
726# endif /* PDM_ASYNC_COMPLETION_FILE_WITH_DELAY */
727
728#endif /* VBOX_WITH_DEBUGGER */
729
730static int pdmacFileInitialize(PPDMASYNCCOMPLETIONEPCLASS pClassGlobals, PCFGMNODE pCfgNode)
731{
732 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pClassGlobals;
733 RTFILEAIOLIMITS AioLimits; /** < Async I/O limitations. */
734
735 int rc = RTFileAioGetLimits(&AioLimits);
736#ifdef DEBUG
737 if (RT_SUCCESS(rc) && RTEnvExist("VBOX_ASYNC_IO_FAILBACK"))
738 rc = VERR_ENV_VAR_NOT_FOUND;
739#endif
740 if (RT_FAILURE(rc))
741 {
742 LogRel(("AIO: Async I/O manager not supported (rc=%Rrc). Falling back to simple manager\n",
743 rc));
744 pEpClassFile->enmMgrTypeOverride = PDMACEPFILEMGRTYPE_SIMPLE;
745 pEpClassFile->enmEpBackendDefault = PDMACFILEEPBACKEND_BUFFERED;
746 }
747 else
748 {
749 pEpClassFile->uBitmaskAlignment = AioLimits.cbBufferAlignment ? ~((RTR3UINTPTR)AioLimits.cbBufferAlignment - 1) : RTR3UINTPTR_MAX;
750 pEpClassFile->cReqsOutstandingMax = AioLimits.cReqsOutstandingMax;
751
752 if (pCfgNode)
753 {
754 /* Query the default manager type */
755 char *pszVal = NULL;
756 rc = CFGMR3QueryStringAllocDef(pCfgNode, "IoMgr", &pszVal, "Async");
757 AssertLogRelRCReturn(rc, rc);
758
759 rc = pdmacFileMgrTypeFromName(pszVal, &pEpClassFile->enmMgrTypeOverride);
760 MMR3HeapFree(pszVal);
761 if (RT_FAILURE(rc))
762 return rc;
763
764 LogRel(("AIOMgr: Default manager type is \"%s\"\n", pdmacFileMgrTypeToName(pEpClassFile->enmMgrTypeOverride)));
765
766 /* Query default backend type */
767 rc = CFGMR3QueryStringAllocDef(pCfgNode, "FileBackend", &pszVal, "NonBuffered");
768 AssertLogRelRCReturn(rc, rc);
769
770 rc = pdmacFileBackendTypeFromName(pszVal, &pEpClassFile->enmEpBackendDefault);
771 MMR3HeapFree(pszVal);
772 if (RT_FAILURE(rc))
773 return rc;
774
775 LogRel(("AIOMgr: Default file backend is \"%s\"\n", pdmacFileBackendTypeToName(pEpClassFile->enmEpBackendDefault)));
776
777#ifdef RT_OS_LINUX
778 if ( pEpClassFile->enmMgrTypeOverride == PDMACEPFILEMGRTYPE_ASYNC
779 && pEpClassFile->enmEpBackendDefault == PDMACFILEEPBACKEND_BUFFERED)
780 {
781 LogRel(("AIOMgr: Linux does not support buffered async I/O, changing to non buffered\n"));
782 pEpClassFile->enmEpBackendDefault = PDMACFILEEPBACKEND_NON_BUFFERED;
783 }
784#endif
785 }
786 else
787 {
788 /* No configuration supplied, set defaults */
789 pEpClassFile->enmEpBackendDefault = PDMACFILEEPBACKEND_NON_BUFFERED;
790 pEpClassFile->enmMgrTypeOverride = PDMACEPFILEMGRTYPE_ASYNC;
791 }
792 }
793
794 /* Init critical section. */
795 rc = RTCritSectInit(&pEpClassFile->CritSect);
796
797#ifdef VBOX_WITH_DEBUGGER
798 /* Install the error injection handler. */
799 if (RT_SUCCESS(rc))
800 {
801 rc = DBGCRegisterCommands(&g_aCmds[0], RT_ELEMENTS(g_aCmds));
802 AssertRC(rc);
803 }
804#endif
805
806 return rc;
807}
808
809static void pdmacFileTerminate(PPDMASYNCCOMPLETIONEPCLASS pClassGlobals)
810{
811 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pClassGlobals;
812
813 /* All endpoints should be closed at this point. */
814 AssertMsg(!pEpClassFile->Core.pEndpointsHead, ("There are still endpoints left\n"));
815
816 /* Destroy all left async I/O managers. */
817 while (pEpClassFile->pAioMgrHead)
818 pdmacFileAioMgrDestroy(pEpClassFile, pEpClassFile->pAioMgrHead);
819
820 RTCritSectDelete(&pEpClassFile->CritSect);
821}
822
823static int pdmacFileEpInitialize(PPDMASYNCCOMPLETIONENDPOINT pEndpoint,
824 const char *pszUri, uint32_t fFlags)
825{
826 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
827 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pEndpoint->pEpClass;
828 PDMACEPFILEMGRTYPE enmMgrType = pEpClassFile->enmMgrTypeOverride;
829 PDMACFILEEPBACKEND enmEpBackend = pEpClassFile->enmEpBackendDefault;
830
831 AssertMsgReturn((fFlags & ~(PDMACEP_FILE_FLAGS_READ_ONLY | PDMACEP_FILE_FLAGS_DONT_LOCK | PDMACEP_FILE_FLAGS_HOST_CACHE_ENABLED)) == 0,
832 ("PDMAsyncCompletion: Invalid flag specified\n"), VERR_INVALID_PARAMETER);
833
834 unsigned fFileFlags = RTFILE_O_OPEN;
835
836 /*
837 * Revert to the simple manager and the buffered backend if
838 * the host cache should be enabled.
839 */
840 if (fFlags & PDMACEP_FILE_FLAGS_HOST_CACHE_ENABLED)
841 {
842 enmMgrType = PDMACEPFILEMGRTYPE_SIMPLE;
843 enmEpBackend = PDMACFILEEPBACKEND_BUFFERED;
844 }
845
846 if (fFlags & PDMACEP_FILE_FLAGS_READ_ONLY)
847 fFileFlags |= RTFILE_O_READ | RTFILE_O_DENY_NONE;
848 else
849 {
850 fFileFlags |= RTFILE_O_READWRITE;
851
852 /*
853 * Opened in read/write mode. Check whether the caller wants to
854 * avoid the lock. Return an error in case caching is enabled
855 * because this can lead to data corruption.
856 */
857 if (fFlags & PDMACEP_FILE_FLAGS_DONT_LOCK)
858 fFileFlags |= RTFILE_O_DENY_NONE;
859 else
860 fFileFlags |= RTFILE_O_DENY_WRITE;
861 }
862
863 if (enmMgrType == PDMACEPFILEMGRTYPE_ASYNC)
864 fFileFlags |= RTFILE_O_ASYNC_IO;
865
866 int rc;
867 if (enmEpBackend == PDMACFILEEPBACKEND_NON_BUFFERED)
868 {
869 /*
870 * We only disable the cache if the size of the file is a multiple of 512.
871 * Certain hosts like Windows, Linux and Solaris require that transfer sizes
872 * are aligned to the volume sector size.
873 * If not we just make sure that the data is written to disk with RTFILE_O_WRITE_THROUGH
874 * which will trash the host cache but ensures that the host cache will not
875 * contain dirty buffers.
876 */
877 RTFILE hFile;
878 rc = RTFileOpen(&hFile, pszUri, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
879 if (RT_SUCCESS(rc))
880 {
881 uint64_t cbSize;
882
883 rc = pdmacFileEpNativeGetSize(hFile, &cbSize);
884
885 if (RT_SUCCESS(rc) && ((cbSize % 512) == 0))
886 fFileFlags |= RTFILE_O_NO_CACHE;
887 else
888 {
889 /* Downgrade to the buffered backend */
890 enmEpBackend = PDMACFILEEPBACKEND_BUFFERED;
891
892#ifdef RT_OS_LINUX
893 fFileFlags &= ~RTFILE_O_ASYNC_IO;
894 enmMgrType = PDMACEPFILEMGRTYPE_SIMPLE;
895#endif
896 }
897 RTFileClose(hFile);
898 }
899 }
900
901 /* Open with final flags. */
902 rc = RTFileOpen(&pEpFile->hFile, pszUri, fFileFlags);
903 if ( rc == VERR_INVALID_FUNCTION
904 || rc == VERR_INVALID_PARAMETER)
905 {
906 LogRel(("pdmacFileEpInitialize: RTFileOpen %s / %08x failed with %Rrc\n",
907 pszUri, fFileFlags, rc));
908 /*
909 * Solaris doesn't support directio on ZFS so far. :-\
910 * Trying to enable it returns VERR_INVALID_FUNCTION
911 * (ENOTTY). Remove it and hope for the best.
912 * ZFS supports write throttling in case applications
913 * write more data than can be synced to the disk
914 * without blocking the whole application.
915 *
916 * On Linux we have the same problem with cifs.
917 * Have to disable async I/O here too because it requires O_DIRECT.
918 */
919 fFileFlags &= ~RTFILE_O_NO_CACHE;
920 enmEpBackend = PDMACFILEEPBACKEND_BUFFERED;
921
922#ifdef RT_OS_LINUX
923 fFileFlags &= ~RTFILE_O_ASYNC_IO;
924 enmMgrType = PDMACEPFILEMGRTYPE_SIMPLE;
925#endif
926
927 /* Open again. */
928 rc = RTFileOpen(&pEpFile->hFile, pszUri, fFileFlags);
929
930 if (RT_FAILURE(rc))
931 {
932 LogRel(("pdmacFileEpInitialize: RTFileOpen %s / %08x failed AGAIN(!) with %Rrc\n",
933 pszUri, fFileFlags, rc));
934 }
935 }
936
937 if (RT_SUCCESS(rc))
938 {
939 pEpFile->fFlags = fFileFlags;
940
941 rc = pdmacFileEpNativeGetSize(pEpFile->hFile, (uint64_t *)&pEpFile->cbFile);
942 if (RT_SUCCESS(rc))
943 {
944 /* Initialize the segment cache */
945 rc = MMR3HeapAllocZEx(pEpClassFile->Core.pVM, MM_TAG_PDM_ASYNC_COMPLETION,
946 sizeof(PDMACTASKFILE),
947 (void **)&pEpFile->pTasksFreeHead);
948 if (RT_SUCCESS(rc))
949 {
950 PPDMACEPFILEMGR pAioMgr = NULL;
951
952 pEpFile->pTasksFreeTail = pEpFile->pTasksFreeHead;
953 pEpFile->cTasksCached = 0;
954 pEpFile->enmBackendType = enmEpBackend;
955 /*
956 * Disable async flushes on Solaris for now.
957 * They cause weird hangs which needs more investigations.
958 */
959#ifndef RT_OS_SOLARIS
960 pEpFile->fAsyncFlushSupported = true;
961#else
962 pEpFile->fAsyncFlushSupported = false;
963#endif
964
965 if (enmMgrType == PDMACEPFILEMGRTYPE_SIMPLE)
966 {
967 /* Simple mode. Every file has its own async I/O manager. */
968 rc = pdmacFileAioMgrCreate(pEpClassFile, &pAioMgr, PDMACEPFILEMGRTYPE_SIMPLE);
969 AssertRC(rc);
970 }
971 else
972 {
973 pAioMgr = pEpClassFile->pAioMgrHead;
974
975 /* Check for an idling manager of the same type */
976 while (pAioMgr)
977 {
978 if (pAioMgr->enmMgrType == enmMgrType)
979 break;
980 pAioMgr = pAioMgr->pNext;
981 }
982
983 if (!pAioMgr)
984 {
985 rc = pdmacFileAioMgrCreate(pEpClassFile, &pAioMgr, enmMgrType);
986 AssertRC(rc);
987 }
988 }
989
990 pEpFile->AioMgr.pTreeRangesLocked = (PAVLRFOFFTREE)RTMemAllocZ(sizeof(AVLRFOFFTREE));
991 if (!pEpFile->AioMgr.pTreeRangesLocked)
992 rc = VERR_NO_MEMORY;
993 else
994 {
995 pEpFile->enmState = PDMASYNCCOMPLETIONENDPOINTFILESTATE_ACTIVE;
996
997 /* Assign the endpoint to the thread. */
998 rc = pdmacFileAioMgrAddEndpoint(pAioMgr, pEpFile);
999 if (RT_FAILURE(rc))
1000 {
1001 RTMemFree(pEpFile->AioMgr.pTreeRangesLocked);
1002 MMR3HeapFree(pEpFile->pTasksFreeHead);
1003 }
1004 }
1005 }
1006 }
1007
1008 if (RT_FAILURE(rc))
1009 RTFileClose(pEpFile->hFile);
1010 }
1011
1012#ifdef VBOX_WITH_STATISTICS
1013 if (RT_SUCCESS(rc))
1014 {
1015 STAMR3RegisterF(pEpClassFile->Core.pVM, &pEpFile->StatRead,
1016 STAMTYPE_PROFILE_ADV, STAMVISIBILITY_ALWAYS,
1017 STAMUNIT_TICKS_PER_CALL, "Time taken to read from the endpoint",
1018 "/PDM/AsyncCompletion/File/%s/Read", RTPathFilename(pEpFile->Core.pszUri));
1019
1020 STAMR3RegisterF(pEpClassFile->Core.pVM, &pEpFile->StatWrite,
1021 STAMTYPE_PROFILE_ADV, STAMVISIBILITY_ALWAYS,
1022 STAMUNIT_TICKS_PER_CALL, "Time taken to write to the endpoint",
1023 "/PDM/AsyncCompletion/File/%s/Write", RTPathFilename(pEpFile->Core.pszUri));
1024 }
1025#endif
1026
1027 if (RT_SUCCESS(rc))
1028 LogRel(("AIOMgr: Endpoint for file '%s' (flags %08x) created successfully\n", pszUri, pEpFile->fFlags));
1029
1030 return rc;
1031}
1032
1033static int pdmacFileEpRangesLockedDestroy(PAVLRFOFFNODECORE pNode, void *pvUser)
1034{
1035 NOREF(pNode); NOREF(pvUser);
1036 AssertMsgFailed(("The locked ranges tree should be empty at that point\n"));
1037 return VINF_SUCCESS;
1038}
1039
1040static int pdmacFileEpClose(PPDMASYNCCOMPLETIONENDPOINT pEndpoint)
1041{
1042 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1043 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pEndpoint->pEpClass;
1044
1045 /* Make sure that all tasks finished for this endpoint. */
1046 int rc = pdmacFileAioMgrCloseEndpoint(pEpFile->pAioMgr, pEpFile);
1047 AssertRC(rc);
1048
1049 /*
1050 * If the async I/O manager is in failsafe mode this is the only endpoint
1051 * he processes and thus can be destroyed now.
1052 */
1053 if (pEpFile->pAioMgr->enmMgrType == PDMACEPFILEMGRTYPE_SIMPLE)
1054 pdmacFileAioMgrDestroy(pEpClassFile, pEpFile->pAioMgr);
1055
1056 /* Free cached tasks. */
1057 PPDMACTASKFILE pTask = pEpFile->pTasksFreeHead;
1058
1059 while (pTask)
1060 {
1061 PPDMACTASKFILE pTaskFree = pTask;
1062 pTask = pTask->pNext;
1063 MMR3HeapFree(pTaskFree);
1064 }
1065
1066 /* Destroy the locked ranges tree now. */
1067 RTAvlrFileOffsetDestroy(pEpFile->AioMgr.pTreeRangesLocked, pdmacFileEpRangesLockedDestroy, NULL);
1068
1069 RTFileClose(pEpFile->hFile);
1070
1071#ifdef VBOX_WITH_STATISTICS
1072 STAMR3Deregister(pEpClassFile->Core.pVM, &pEpFile->StatRead);
1073 STAMR3Deregister(pEpClassFile->Core.pVM, &pEpFile->StatWrite);
1074#endif
1075
1076 return VINF_SUCCESS;
1077}
1078
1079static int pdmacFileEpRead(PPDMASYNCCOMPLETIONTASK pTask,
1080 PPDMASYNCCOMPLETIONENDPOINT pEndpoint, RTFOFF off,
1081 PCRTSGSEG paSegments, size_t cSegments,
1082 size_t cbRead)
1083{
1084 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1085
1086 LogFlowFunc(("pTask=%#p pEndpoint=%#p off=%RTfoff paSegments=%#p cSegments=%zu cbRead=%zu\n",
1087 pTask, pEndpoint, off, paSegments, cSegments, cbRead));
1088
1089 if (RT_UNLIKELY((uint64_t)off + cbRead > pEpFile->cbFile))
1090 return VERR_EOF;
1091
1092 STAM_PROFILE_ADV_START(&pEpFile->StatRead, Read);
1093 pdmacFileEpTaskInit(pTask, cbRead);
1094 int rc = pdmacFileEpTaskInitiate(pTask, pEndpoint, off, paSegments, cSegments, cbRead,
1095 PDMACTASKFILETRANSFER_READ);
1096 STAM_PROFILE_ADV_STOP(&pEpFile->StatRead, Read);
1097
1098 return rc;
1099}
1100
1101static int pdmacFileEpWrite(PPDMASYNCCOMPLETIONTASK pTask,
1102 PPDMASYNCCOMPLETIONENDPOINT pEndpoint, RTFOFF off,
1103 PCRTSGSEG paSegments, size_t cSegments,
1104 size_t cbWrite)
1105{
1106 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1107
1108 if (RT_UNLIKELY(pEpFile->fReadonly))
1109 return VERR_NOT_SUPPORTED;
1110
1111 STAM_PROFILE_ADV_START(&pEpFile->StatWrite, Write);
1112
1113 pdmacFileEpTaskInit(pTask, cbWrite);
1114
1115 int rc = pdmacFileEpTaskInitiate(pTask, pEndpoint, off, paSegments, cSegments, cbWrite,
1116 PDMACTASKFILETRANSFER_WRITE);
1117
1118 STAM_PROFILE_ADV_STOP(&pEpFile->StatWrite, Write);
1119
1120 return rc;
1121}
1122
1123static int pdmacFileEpFlush(PPDMASYNCCOMPLETIONTASK pTask,
1124 PPDMASYNCCOMPLETIONENDPOINT pEndpoint)
1125{
1126 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1127 PPDMASYNCCOMPLETIONTASKFILE pTaskFile = (PPDMASYNCCOMPLETIONTASKFILE)pTask;
1128
1129 if (RT_UNLIKELY(pEpFile->fReadonly))
1130 return VERR_NOT_SUPPORTED;
1131
1132 pdmacFileEpTaskInit(pTask, 0);
1133
1134 PPDMACTASKFILE pIoTask = pdmacFileTaskAlloc(pEpFile);
1135 if (RT_UNLIKELY(!pIoTask))
1136 return VERR_NO_MEMORY;
1137
1138 pIoTask->pEndpoint = pEpFile;
1139 pIoTask->enmTransferType = PDMACTASKFILETRANSFER_FLUSH;
1140 pIoTask->pvUser = pTaskFile;
1141 pIoTask->pfnCompleted = pdmacFileEpTaskCompleted;
1142 pdmacFileEpAddTask(pEpFile, pIoTask);
1143
1144 return VINF_AIO_TASK_PENDING;
1145}
1146
1147static int pdmacFileEpGetSize(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, uint64_t *pcbSize)
1148{
1149 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1150
1151 *pcbSize = ASMAtomicReadU64(&pEpFile->cbFile);
1152
1153 return VINF_SUCCESS;
1154}
1155
1156static int pdmacFileEpSetSize(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, uint64_t cbSize)
1157{
1158 int rc;
1159 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1160
1161 rc = RTFileSetSize(pEpFile->hFile, cbSize);
1162 if (RT_SUCCESS(rc))
1163 ASMAtomicWriteU64(&pEpFile->cbFile, cbSize);
1164
1165 return rc;
1166}
1167
1168const PDMASYNCCOMPLETIONEPCLASSOPS g_PDMAsyncCompletionEndpointClassFile =
1169{
1170 /* u32Version */
1171 PDMAC_EPCLASS_OPS_VERSION,
1172 /* pcszName */
1173 "File",
1174 /* enmClassType */
1175 PDMASYNCCOMPLETIONEPCLASSTYPE_FILE,
1176 /* cbEndpointClassGlobal */
1177 sizeof(PDMASYNCCOMPLETIONEPCLASSFILE),
1178 /* cbEndpoint */
1179 sizeof(PDMASYNCCOMPLETIONENDPOINTFILE),
1180 /* cbTask */
1181 sizeof(PDMASYNCCOMPLETIONTASKFILE),
1182 /* pfnInitialize */
1183 pdmacFileInitialize,
1184 /* pfnTerminate */
1185 pdmacFileTerminate,
1186 /* pfnEpInitialize. */
1187 pdmacFileEpInitialize,
1188 /* pfnEpClose */
1189 pdmacFileEpClose,
1190 /* pfnEpRead */
1191 pdmacFileEpRead,
1192 /* pfnEpWrite */
1193 pdmacFileEpWrite,
1194 /* pfnEpFlush */
1195 pdmacFileEpFlush,
1196 /* pfnEpGetSize */
1197 pdmacFileEpGetSize,
1198 /* pfnEpSetSize */
1199 pdmacFileEpSetSize,
1200 /* u32VersionEnd */
1201 PDMAC_EPCLASS_OPS_VERSION
1202};
1203
Note: See TracBrowser for help on using the repository browser.

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