VirtualBox

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

Last change on this file since 92341 was 87766, checked in by vboxsync, 4 years ago

VMM/TM,VMM/*: Refactored the TM timer APIs to use 'handles' and take a pVM parameter. Only internal callbacks have been updated with a hTimer parameter, so far. bugref:9943

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