VirtualBox

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

Last change on this file since 80239 was 80191, checked in by vboxsync, 5 years ago

VMM/r3: Refactored VMCPU enumeration in preparation that aCpus will be replaced with a pointer array. Removed two raw-mode offset members from the CPUM and CPUMCPU sub-structures. bugref:9217 bugref:9517

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