VirtualBox

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

Last change on this file since 107044 was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

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