VirtualBox

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

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

© 2023 Oracle
ContactPrivacy policyTerms of Use