VirtualBox

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

Last change on this file since 50653 was 46493, checked in by vboxsync, 11 years ago

STAMR3Deregister* optimizations. Relevant for both startup and shutdown times.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use