VirtualBox

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

Last change on this file since 99949 was 99775, checked in by vboxsync, 21 months ago

*: Mark functions as static if not used outside of a given compilation unit. Enables the compiler to optimize inlining, reduces the symbol tables, exposes unused functions and in some rare cases exposes mismtaches between function declarations and definitions, but most importantly reduces the number of parfait reports for the extern-function-no-forward-declaration category. This should not result in any functional changes, bugref:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 47.8 KB
Line 
1/* $Id: PDMAsyncCompletionFile.cpp 99775 2023-05-12 12:21:58Z 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 * @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 cbTransfer -= paSegments[i].cbSeg;
423 }
424
425 AssertMsg(!cbTransfer, ("Incomplete transfer %u bytes left\n", cbTransfer));
426
427 return VINF_AIO_TASK_PENDING;
428}
429
430/**
431 * Creates a new async I/O manager.
432 *
433 * @returns VBox status code.
434 * @param pEpClass Pointer to the endpoint class data.
435 * @param ppAioMgr Where to store the pointer to the new async I/O manager on success.
436 * @param enmMgrType Wanted manager type - can be overwritten by the global override.
437 */
438int pdmacFileAioMgrCreate(PPDMASYNCCOMPLETIONEPCLASSFILE pEpClass, PPPDMACEPFILEMGR ppAioMgr,
439 PDMACEPFILEMGRTYPE enmMgrType)
440{
441 LogFlowFunc((": Entered\n"));
442
443 PPDMACEPFILEMGR pAioMgrNew;
444 int rc = MMR3HeapAllocZEx(pEpClass->Core.pVM, MM_TAG_PDM_ASYNC_COMPLETION, sizeof(PDMACEPFILEMGR), (void **)&pAioMgrNew);
445 if (RT_SUCCESS(rc))
446 {
447 if (enmMgrType < pEpClass->enmMgrTypeOverride)
448 pAioMgrNew->enmMgrType = enmMgrType;
449 else
450 pAioMgrNew->enmMgrType = pEpClass->enmMgrTypeOverride;
451
452 pAioMgrNew->msBwLimitExpired = RT_INDEFINITE_WAIT;
453
454 rc = RTSemEventCreate(&pAioMgrNew->EventSem);
455 if (RT_SUCCESS(rc))
456 {
457 rc = RTSemEventCreate(&pAioMgrNew->EventSemBlock);
458 if (RT_SUCCESS(rc))
459 {
460 rc = RTCritSectInit(&pAioMgrNew->CritSectBlockingEvent);
461 if (RT_SUCCESS(rc))
462 {
463 /* Init the rest of the manager. */
464 if (pAioMgrNew->enmMgrType != PDMACEPFILEMGRTYPE_SIMPLE)
465 rc = pdmacFileAioMgrNormalInit(pAioMgrNew);
466
467 if (RT_SUCCESS(rc))
468 {
469 pAioMgrNew->enmState = PDMACEPFILEMGRSTATE_RUNNING;
470
471 rc = RTThreadCreateF(&pAioMgrNew->Thread,
472 pAioMgrNew->enmMgrType == PDMACEPFILEMGRTYPE_SIMPLE
473 ? pdmacFileAioMgrFailsafe
474 : pdmacFileAioMgrNormal,
475 pAioMgrNew,
476 0,
477 RTTHREADTYPE_IO,
478 0,
479 "AioMgr%d-%s", pEpClass->cAioMgrs,
480 pAioMgrNew->enmMgrType == PDMACEPFILEMGRTYPE_SIMPLE
481 ? "F"
482 : "N");
483 if (RT_SUCCESS(rc))
484 {
485 /* Link it into the list. */
486 RTCritSectEnter(&pEpClass->CritSect);
487 pAioMgrNew->pNext = pEpClass->pAioMgrHead;
488 if (pEpClass->pAioMgrHead)
489 pEpClass->pAioMgrHead->pPrev = pAioMgrNew;
490 pEpClass->pAioMgrHead = pAioMgrNew;
491 pEpClass->cAioMgrs++;
492 RTCritSectLeave(&pEpClass->CritSect);
493
494 *ppAioMgr = pAioMgrNew;
495
496 Log(("PDMAC: Successfully created new file AIO Mgr {%s}\n", RTThreadGetName(pAioMgrNew->Thread)));
497 return VINF_SUCCESS;
498 }
499 pdmacFileAioMgrNormalDestroy(pAioMgrNew);
500 }
501 RTCritSectDelete(&pAioMgrNew->CritSectBlockingEvent);
502 }
503 RTSemEventDestroy(pAioMgrNew->EventSem);
504 }
505 RTSemEventDestroy(pAioMgrNew->EventSemBlock);
506 }
507 MMR3HeapFree(pAioMgrNew);
508 }
509
510 LogFlowFunc((": Leave rc=%Rrc\n", rc));
511
512 return rc;
513}
514
515/**
516 * Destroys a async I/O manager.
517 *
518 * @param pEpClassFile Pointer to globals for the file endpoint class.
519 * @param pAioMgr The async I/O manager to destroy.
520 */
521static void pdmacFileAioMgrDestroy(PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile, PPDMACEPFILEMGR pAioMgr)
522{
523 int rc = pdmacFileAioMgrShutdown(pAioMgr);
524 AssertRC(rc);
525
526 /* Unlink from the list. */
527 rc = RTCritSectEnter(&pEpClassFile->CritSect);
528 AssertRC(rc);
529
530 PPDMACEPFILEMGR pPrev = pAioMgr->pPrev;
531 PPDMACEPFILEMGR pNext = pAioMgr->pNext;
532
533 if (pPrev)
534 pPrev->pNext = pNext;
535 else
536 pEpClassFile->pAioMgrHead = pNext;
537
538 if (pNext)
539 pNext->pPrev = pPrev;
540
541 pEpClassFile->cAioMgrs--;
542 rc = RTCritSectLeave(&pEpClassFile->CritSect);
543 AssertRC(rc);
544
545 /* Free the resources. */
546 RTCritSectDelete(&pAioMgr->CritSectBlockingEvent);
547 RTSemEventDestroy(pAioMgr->EventSem);
548 RTSemEventDestroy(pAioMgr->EventSemBlock);
549 if (pAioMgr->enmMgrType != PDMACEPFILEMGRTYPE_SIMPLE)
550 pdmacFileAioMgrNormalDestroy(pAioMgr);
551
552 MMR3HeapFree(pAioMgr);
553}
554
555static int pdmacFileMgrTypeFromName(const char *pszVal, PPDMACEPFILEMGRTYPE penmMgrType)
556{
557 int rc = VINF_SUCCESS;
558
559 if (!RTStrCmp(pszVal, "Simple"))
560 *penmMgrType = PDMACEPFILEMGRTYPE_SIMPLE;
561 else if (!RTStrCmp(pszVal, "Async"))
562 *penmMgrType = PDMACEPFILEMGRTYPE_ASYNC;
563 else
564 rc = VERR_CFGM_CONFIG_UNKNOWN_VALUE;
565
566 return rc;
567}
568
569static const char *pdmacFileMgrTypeToName(PDMACEPFILEMGRTYPE enmMgrType)
570{
571 if (enmMgrType == PDMACEPFILEMGRTYPE_SIMPLE)
572 return "Simple";
573 if (enmMgrType == PDMACEPFILEMGRTYPE_ASYNC)
574 return "Async";
575
576 return NULL;
577}
578
579static int pdmacFileBackendTypeFromName(const char *pszVal, PPDMACFILEEPBACKEND penmBackendType)
580{
581 int rc = VINF_SUCCESS;
582
583 if (!RTStrCmp(pszVal, "Buffered"))
584 *penmBackendType = PDMACFILEEPBACKEND_BUFFERED;
585 else if (!RTStrCmp(pszVal, "NonBuffered"))
586 *penmBackendType = PDMACFILEEPBACKEND_NON_BUFFERED;
587 else
588 rc = VERR_CFGM_CONFIG_UNKNOWN_VALUE;
589
590 return rc;
591}
592
593static const char *pdmacFileBackendTypeToName(PDMACFILEEPBACKEND enmBackendType)
594{
595 if (enmBackendType == PDMACFILEEPBACKEND_BUFFERED)
596 return "Buffered";
597 if (enmBackendType == PDMACFILEEPBACKEND_NON_BUFFERED)
598 return "NonBuffered";
599
600 return NULL;
601}
602
603#ifdef VBOX_WITH_DEBUGGER
604
605/**
606 * @callback_method_impl{FNDBGCCMD, The '.injecterror' command.}
607 */
608static DECLCALLBACK(int) pdmacEpFileErrorInject(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR pArgs, unsigned cArgs)
609{
610 /*
611 * Validate input.
612 */
613 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
614 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, -1, cArgs == 3);
615 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, pArgs[0].enmType == DBGCVAR_TYPE_STRING);
616 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 1, pArgs[1].enmType == DBGCVAR_TYPE_STRING);
617 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 2, pArgs[2].enmType == DBGCVAR_TYPE_NUMBER);
618
619 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile;
620 pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pUVM->pdm.s.apAsyncCompletionEndpointClass[PDMASYNCCOMPLETIONEPCLASSTYPE_FILE];
621
622 /* Syntax is "read|write <filename> <status code>" */
623 bool fWrite;
624 if (!RTStrCmp(pArgs[0].u.pszString, "read"))
625 fWrite = false;
626 else if (!RTStrCmp(pArgs[0].u.pszString, "write"))
627 fWrite = true;
628 else
629 return DBGCCmdHlpFail(pCmdHlp, pCmd, "invalid transfer direction '%s'", pArgs[0].u.pszString);
630
631 int32_t rcToInject = (int32_t)pArgs[2].u.u64Number;
632 if ((uint64_t)rcToInject != pArgs[2].u.u64Number)
633 return DBGCCmdHlpFail(pCmdHlp, pCmd, "The status code '%lld' is out of range", pArgs[0].u.u64Number);
634
635 /*
636 * Search for the matching endpoint.
637 */
638 RTCritSectEnter(&pEpClassFile->Core.CritSect);
639
640 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEpClassFile->Core.pEndpointsHead;
641 while (pEpFile)
642 {
643 if (!RTStrCmp(pArgs[1].u.pszString, RTPathFilename(pEpFile->Core.pszUri)))
644 break;
645 pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEpFile->Core.pNext;
646 }
647
648 if (pEpFile)
649 {
650 /*
651 * Do the job.
652 */
653 if (fWrite)
654 ASMAtomicXchgS32(&pEpFile->rcReqWrite, rcToInject);
655 else
656 ASMAtomicXchgS32(&pEpFile->rcReqRead, rcToInject);
657
658 DBGCCmdHlpPrintf(pCmdHlp, "Injected %Rrc into '%s' for %s\n",
659 (int)rcToInject, pArgs[1].u.pszString, pArgs[0].u.pszString);
660 }
661
662 RTCritSectLeave(&pEpClassFile->Core.CritSect);
663
664 if (!pEpFile)
665 return DBGCCmdHlpFail(pCmdHlp, pCmd, "No file with name '%s' found", pArgs[1].u.pszString);
666 return VINF_SUCCESS;
667}
668
669# ifdef PDM_ASYNC_COMPLETION_FILE_WITH_DELAY
670/**
671 * @callback_method_impl{FNDBGCCMD, The '.injectdelay' command.}
672 */
673static DECLCALLBACK(int) pdmacEpFileDelayInject(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR pArgs, unsigned cArgs)
674{
675 /*
676 * Validate input.
677 */
678 DBGC_CMDHLP_REQ_UVM_RET(pCmdHlp, pCmd, pUVM);
679 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, -1, cArgs >= 3);
680 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, pArgs[0].enmType == DBGCVAR_TYPE_STRING);
681 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 1, pArgs[1].enmType == DBGCVAR_TYPE_STRING);
682 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 2, pArgs[2].enmType == DBGCVAR_TYPE_NUMBER);
683
684 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile;
685 pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pUVM->pdm.s.apAsyncCompletionEndpointClass[PDMASYNCCOMPLETIONEPCLASSTYPE_FILE];
686
687 /* Syntax is "read|write|flush|any <filename> <delay> [reqs]" */
688 PDMACFILEREQTYPEDELAY enmDelayType = PDMACFILEREQTYPEDELAY_ANY;
689 if (!RTStrCmp(pArgs[0].u.pszString, "read"))
690 enmDelayType = PDMACFILEREQTYPEDELAY_READ;
691 else if (!RTStrCmp(pArgs[0].u.pszString, "write"))
692 enmDelayType = PDMACFILEREQTYPEDELAY_WRITE;
693 else if (!RTStrCmp(pArgs[0].u.pszString, "flush"))
694 enmDelayType = PDMACFILEREQTYPEDELAY_FLUSH;
695 else if (!RTStrCmp(pArgs[0].u.pszString, "any"))
696 enmDelayType = PDMACFILEREQTYPEDELAY_ANY;
697 else
698 return DBGCCmdHlpFail(pCmdHlp, pCmd, "invalid transfer direction '%s'", pArgs[0].u.pszString);
699
700 uint32_t msDelay = (uint32_t)pArgs[2].u.u64Number;
701 if ((uint64_t)msDelay != pArgs[2].u.u64Number)
702 return DBGCCmdHlpFail(pCmdHlp, pCmd, "The delay '%lld' is out of range", pArgs[0].u.u64Number);
703
704 uint32_t cReqsDelay = 1;
705 uint32_t msJitter = 0;
706 if (cArgs >= 4)
707 msJitter = (uint32_t)pArgs[3].u.u64Number;
708 if (cArgs == 5)
709 cReqsDelay = (uint32_t)pArgs[4].u.u64Number;
710
711 /*
712 * Search for the matching endpoint.
713 */
714 RTCritSectEnter(&pEpClassFile->Core.CritSect);
715
716 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEpClassFile->Core.pEndpointsHead;
717 while (pEpFile)
718 {
719 if (!RTStrCmp(pArgs[1].u.pszString, RTPathFilename(pEpFile->Core.pszUri)))
720 break;
721 pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEpFile->Core.pNext;
722 }
723
724 if (pEpFile)
725 {
726 ASMAtomicWriteSize(&pEpFile->enmTypeDelay, enmDelayType);
727 ASMAtomicWriteU32(&pEpFile->msDelay, msDelay);
728 ASMAtomicWriteU32(&pEpFile->msJitter, msJitter);
729 ASMAtomicWriteU32(&pEpFile->cReqsDelay, cReqsDelay);
730
731 DBGCCmdHlpPrintf(pCmdHlp, "Injected delay for the next %u requests of %u ms into '%s' for %s\n",
732 cReqsDelay, msDelay, pArgs[1].u.pszString, pArgs[0].u.pszString);
733 }
734
735 RTCritSectLeave(&pEpClassFile->Core.CritSect);
736
737 if (!pEpFile)
738 return DBGCCmdHlpFail(pCmdHlp, pCmd, "No file with name '%s' found", pArgs[1].u.pszString);
739 return VINF_SUCCESS;
740}
741
742/**
743 * @callback_method_impl{FNTMTIMERINT, }
744 */
745static DECLCALLBACK(void) pdmacR3TimerCallback(PVM pVM, TMTIMERHANDLE hTimer, void *pvUser)
746{
747 Assert(hTimer == pEpClassFile->hTimer);
748 uint64_t tsCur = RTTimeProgramMilliTS();
749 uint64_t cMilliesNext = UINT64_MAX;
750 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pvUser;
751
752 ASMAtomicWriteU64(&pEpClassFile->cMilliesNext, UINT64_MAX);
753
754 /* Go through all endpoints and check for expired requests. */
755 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEpClassFile->Core.pEndpointsHead;
756
757 while (pEpFile)
758 {
759 /* Check for an expired delay. */
760 if (pEpFile->pDelayedHead != NULL)
761 {
762 PPDMASYNCCOMPLETIONTASKFILE pTaskFile = ASMAtomicXchgPtrT(&pEpFile->pDelayedHead, NULL, PPDMASYNCCOMPLETIONTASKFILE);
763
764 while (pTaskFile)
765 {
766 PPDMASYNCCOMPLETIONTASKFILE pTmp = pTaskFile;
767 pTaskFile = pTaskFile->pDelayedNext;
768
769 if (tsCur >= pTmp->tsDelayEnd)
770 {
771 LogRel(("AIOMgr: Delayed request %#p completed\n", pTmp));
772 pdmR3AsyncCompletionCompleteTask(&pTmp->Core, pTmp->rc, true);
773 }
774 else
775 {
776 /* Prepend to the delayed list again. */
777 PPDMASYNCCOMPLETIONTASKFILE pHead = NULL;
778
779 if (pTmp->tsDelayEnd - tsCur < cMilliesNext)
780 cMilliesNext = pTmp->tsDelayEnd - tsCur;
781
782 do
783 {
784 pHead = ASMAtomicReadPtrT(&pEpFile->pDelayedHead, PPDMASYNCCOMPLETIONTASKFILE);
785 pTmp->pDelayedNext = pHead;
786 } while (!ASMAtomicCmpXchgPtr(&pEpFile->pDelayedHead, pTmp, pHead));
787 }
788 }
789 }
790
791 pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEpFile->Core.pNext;
792 }
793
794 if (cMilliesNext < pEpClassFile->cMilliesNext)
795 {
796 ASMAtomicWriteU64(&pEpClassFile->cMilliesNext, cMilliesNext);
797 TMTimerSetMillies(pVM, hTimer, cMilliesNext);
798 }
799}
800
801# endif /* PDM_ASYNC_COMPLETION_FILE_WITH_DELAY */
802
803#endif /* VBOX_WITH_DEBUGGER */
804
805static DECLCALLBACK(int) pdmacFileInitialize(PPDMASYNCCOMPLETIONEPCLASS pClassGlobals, PCFGMNODE pCfgNode)
806{
807 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pClassGlobals;
808 RTFILEAIOLIMITS AioLimits; /** < Async I/O limitations. */
809
810 int rc = RTFileAioGetLimits(&AioLimits);
811#ifdef DEBUG
812 if (RT_SUCCESS(rc) && RTEnvExist("VBOX_ASYNC_IO_FAILBACK"))
813 rc = VERR_ENV_VAR_NOT_FOUND;
814#endif
815 if (RT_FAILURE(rc))
816 {
817 LogRel(("AIO: Async I/O manager not supported (rc=%Rrc). Falling back to simple manager\n", rc));
818 pEpClassFile->enmMgrTypeOverride = PDMACEPFILEMGRTYPE_SIMPLE;
819 pEpClassFile->enmEpBackendDefault = PDMACFILEEPBACKEND_BUFFERED;
820 }
821 else
822 {
823 pEpClassFile->uBitmaskAlignment = AioLimits.cbBufferAlignment ? ~((RTR3UINTPTR)AioLimits.cbBufferAlignment - 1) : RTR3UINTPTR_MAX;
824 pEpClassFile->cReqsOutstandingMax = AioLimits.cReqsOutstandingMax;
825
826 if (pCfgNode)
827 {
828 /* Query the default manager type */
829 char *pszVal = NULL;
830 rc = CFGMR3QueryStringAllocDef(pCfgNode, "IoMgr", &pszVal, "Async");
831 AssertLogRelRCReturn(rc, rc);
832
833 rc = pdmacFileMgrTypeFromName(pszVal, &pEpClassFile->enmMgrTypeOverride);
834 MMR3HeapFree(pszVal);
835 if (RT_FAILURE(rc))
836 return rc;
837
838 LogRel(("AIOMgr: Default manager type is '%s'\n", pdmacFileMgrTypeToName(pEpClassFile->enmMgrTypeOverride)));
839
840 /* Query default backend type */
841 rc = CFGMR3QueryStringAllocDef(pCfgNode, "FileBackend", &pszVal, "NonBuffered");
842 AssertLogRelRCReturn(rc, rc);
843
844 rc = pdmacFileBackendTypeFromName(pszVal, &pEpClassFile->enmEpBackendDefault);
845 MMR3HeapFree(pszVal);
846 if (RT_FAILURE(rc))
847 return rc;
848
849 LogRel(("AIOMgr: Default file backend is '%s'\n", pdmacFileBackendTypeToName(pEpClassFile->enmEpBackendDefault)));
850
851#ifdef RT_OS_LINUX
852 if ( pEpClassFile->enmMgrTypeOverride == PDMACEPFILEMGRTYPE_ASYNC
853 && pEpClassFile->enmEpBackendDefault == PDMACFILEEPBACKEND_BUFFERED)
854 {
855 LogRel(("AIOMgr: Linux does not support buffered async I/O, changing to non buffered\n"));
856 pEpClassFile->enmEpBackendDefault = PDMACFILEEPBACKEND_NON_BUFFERED;
857 }
858#endif
859 }
860 else
861 {
862 /* No configuration supplied, set defaults */
863 pEpClassFile->enmEpBackendDefault = PDMACFILEEPBACKEND_NON_BUFFERED;
864 pEpClassFile->enmMgrTypeOverride = PDMACEPFILEMGRTYPE_ASYNC;
865 }
866 }
867
868 /* Init critical section. */
869 rc = RTCritSectInit(&pEpClassFile->CritSect);
870
871#ifdef VBOX_WITH_DEBUGGER
872 /* Install the error injection handler. */
873 if (RT_SUCCESS(rc))
874 {
875 rc = DBGCRegisterCommands(&g_aCmds[0], RT_ELEMENTS(g_aCmds));
876 AssertRC(rc);
877 }
878
879# ifdef PDM_ASYNC_COMPLETION_FILE_WITH_DELAY
880 rc = TMR3TimerCreate(pEpClassFile->Core.pVM, TMCLOCK_REAL, pdmacR3TimerCallback, pEpClassFile,
881 TMTIMER_FLAGS_NO_RING0, "AC Delay", &pEpClassFile->hTimer);
882 AssertRC(rc);
883 pEpClassFile->cMilliesNext = UINT64_MAX;
884# endif
885#endif
886
887 return rc;
888}
889
890static DECLCALLBACK(void) pdmacFileTerminate(PPDMASYNCCOMPLETIONEPCLASS pClassGlobals)
891{
892 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pClassGlobals;
893
894 /* All endpoints should be closed at this point. */
895 AssertMsg(!pEpClassFile->Core.pEndpointsHead, ("There are still endpoints left\n"));
896
897 /* Destroy all left async I/O managers. */
898 while (pEpClassFile->pAioMgrHead)
899 pdmacFileAioMgrDestroy(pEpClassFile, pEpClassFile->pAioMgrHead);
900
901 RTCritSectDelete(&pEpClassFile->CritSect);
902}
903
904static DECLCALLBACK(int) pdmacFileEpInitialize(PPDMASYNCCOMPLETIONENDPOINT pEndpoint,
905 const char *pszUri, uint32_t fFlags)
906{
907 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
908 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pEndpoint->pEpClass;
909 PDMACEPFILEMGRTYPE enmMgrType = pEpClassFile->enmMgrTypeOverride;
910 PDMACFILEEPBACKEND enmEpBackend = pEpClassFile->enmEpBackendDefault;
911
912 AssertMsgReturn((fFlags & ~(PDMACEP_FILE_FLAGS_READ_ONLY | PDMACEP_FILE_FLAGS_DONT_LOCK | PDMACEP_FILE_FLAGS_HOST_CACHE_ENABLED)) == 0,
913 ("PDMAsyncCompletion: Invalid flag specified\n"), VERR_INVALID_PARAMETER);
914
915 unsigned fFileFlags = RTFILE_O_OPEN;
916
917 /*
918 * Revert to the simple manager and the buffered backend if
919 * the host cache should be enabled.
920 */
921 if (fFlags & PDMACEP_FILE_FLAGS_HOST_CACHE_ENABLED)
922 {
923 enmMgrType = PDMACEPFILEMGRTYPE_SIMPLE;
924 enmEpBackend = PDMACFILEEPBACKEND_BUFFERED;
925 }
926
927 if (fFlags & PDMACEP_FILE_FLAGS_READ_ONLY)
928 fFileFlags |= RTFILE_O_READ | RTFILE_O_DENY_NONE;
929 else
930 {
931 fFileFlags |= RTFILE_O_READWRITE;
932
933 /*
934 * Opened in read/write mode. Check whether the caller wants to
935 * avoid the lock. Return an error in case caching is enabled
936 * because this can lead to data corruption.
937 */
938 if (fFlags & PDMACEP_FILE_FLAGS_DONT_LOCK)
939 fFileFlags |= RTFILE_O_DENY_NONE;
940 else
941 fFileFlags |= RTFILE_O_DENY_WRITE;
942 }
943
944 if (enmMgrType == PDMACEPFILEMGRTYPE_ASYNC)
945 fFileFlags |= RTFILE_O_ASYNC_IO;
946
947 int rc;
948 if (enmEpBackend == PDMACFILEEPBACKEND_NON_BUFFERED)
949 {
950 /*
951 * We only disable the cache if the size of the file is a multiple of 512.
952 * Certain hosts like Windows, Linux and Solaris require that transfer sizes
953 * are aligned to the volume sector size.
954 * If not we just make sure that the data is written to disk with RTFILE_O_WRITE_THROUGH
955 * which will trash the host cache but ensures that the host cache will not
956 * contain dirty buffers.
957 */
958 RTFILE hFile;
959 rc = RTFileOpen(&hFile, pszUri, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
960 if (RT_SUCCESS(rc))
961 {
962 uint64_t cbSize;
963
964 rc = RTFileQuerySize(hFile, &cbSize);
965
966 if (RT_SUCCESS(rc) && ((cbSize % 512) == 0))
967 fFileFlags |= RTFILE_O_NO_CACHE;
968 else
969 {
970 /* Downgrade to the buffered backend */
971 enmEpBackend = PDMACFILEEPBACKEND_BUFFERED;
972
973#ifdef RT_OS_LINUX
974 fFileFlags &= ~RTFILE_O_ASYNC_IO;
975 enmMgrType = PDMACEPFILEMGRTYPE_SIMPLE;
976#endif
977 }
978 RTFileClose(hFile);
979 }
980 }
981
982 /* Open with final flags. */
983 rc = RTFileOpen(&pEpFile->hFile, pszUri, fFileFlags);
984 if ( rc == VERR_INVALID_FUNCTION
985 || rc == VERR_INVALID_PARAMETER)
986 {
987 LogRel(("AIOMgr: pdmacFileEpInitialize: RTFileOpen %s / %08x failed with %Rrc\n",
988 pszUri, fFileFlags, rc));
989 /*
990 * Solaris doesn't support directio on ZFS so far. :-\
991 * Trying to enable it returns VERR_INVALID_FUNCTION
992 * (ENOTTY). Remove it and hope for the best.
993 * ZFS supports write throttling in case applications
994 * write more data than can be synced to the disk
995 * without blocking the whole application.
996 *
997 * On Linux we have the same problem with cifs.
998 * Have to disable async I/O here too because it requires O_DIRECT.
999 */
1000 fFileFlags &= ~RTFILE_O_NO_CACHE;
1001 enmEpBackend = PDMACFILEEPBACKEND_BUFFERED;
1002
1003#ifdef RT_OS_LINUX
1004 fFileFlags &= ~RTFILE_O_ASYNC_IO;
1005 enmMgrType = PDMACEPFILEMGRTYPE_SIMPLE;
1006#endif
1007
1008 /* Open again. */
1009 rc = RTFileOpen(&pEpFile->hFile, pszUri, fFileFlags);
1010
1011 if (RT_FAILURE(rc))
1012 {
1013 LogRel(("AIOMgr: pdmacFileEpInitialize: RTFileOpen %s / %08x failed AGAIN(!) with %Rrc\n",
1014 pszUri, fFileFlags, rc));
1015 }
1016 }
1017
1018 if (RT_SUCCESS(rc))
1019 {
1020 pEpFile->fFlags = fFileFlags;
1021
1022 rc = RTFileQuerySize(pEpFile->hFile, (uint64_t *)&pEpFile->cbFile);
1023 if (RT_SUCCESS(rc))
1024 {
1025 /* Initialize the segment cache */
1026 rc = MMR3HeapAllocZEx(pEpClassFile->Core.pVM, MM_TAG_PDM_ASYNC_COMPLETION,
1027 sizeof(PDMACTASKFILE),
1028 (void **)&pEpFile->pTasksFreeHead);
1029 if (RT_SUCCESS(rc))
1030 {
1031 PPDMACEPFILEMGR pAioMgr = NULL;
1032
1033 pEpFile->pTasksFreeTail = pEpFile->pTasksFreeHead;
1034 pEpFile->cTasksCached = 0;
1035 pEpFile->enmBackendType = enmEpBackend;
1036 /*
1037 * Disable async flushes on Solaris for now.
1038 * They cause weird hangs which needs more investigations.
1039 */
1040#ifndef RT_OS_SOLARIS
1041 pEpFile->fAsyncFlushSupported = true;
1042#else
1043 pEpFile->fAsyncFlushSupported = false;
1044#endif
1045
1046 if (enmMgrType == PDMACEPFILEMGRTYPE_SIMPLE)
1047 {
1048 /* Simple mode. Every file has its own async I/O manager. */
1049 rc = pdmacFileAioMgrCreate(pEpClassFile, &pAioMgr, PDMACEPFILEMGRTYPE_SIMPLE);
1050 }
1051 else
1052 {
1053 pAioMgr = pEpClassFile->pAioMgrHead;
1054
1055 /* Check for an idling manager of the same type */
1056 while (pAioMgr)
1057 {
1058 if (pAioMgr->enmMgrType == enmMgrType)
1059 break;
1060 pAioMgr = pAioMgr->pNext;
1061 }
1062
1063 if (!pAioMgr)
1064 rc = pdmacFileAioMgrCreate(pEpClassFile, &pAioMgr, enmMgrType);
1065 }
1066
1067 if (RT_SUCCESS(rc))
1068 {
1069 pEpFile->AioMgr.pTreeRangesLocked = (PAVLRFOFFTREE)RTMemAllocZ(sizeof(AVLRFOFFTREE));
1070 if (!pEpFile->AioMgr.pTreeRangesLocked)
1071 rc = VERR_NO_MEMORY;
1072 else
1073 {
1074 pEpFile->enmState = PDMASYNCCOMPLETIONENDPOINTFILESTATE_ACTIVE;
1075
1076 /* Assign the endpoint to the thread. */
1077 rc = pdmacFileAioMgrAddEndpoint(pAioMgr, pEpFile);
1078 if (RT_FAILURE(rc))
1079 {
1080 RTMemFree(pEpFile->AioMgr.pTreeRangesLocked);
1081 MMR3HeapFree(pEpFile->pTasksFreeHead);
1082 }
1083 }
1084 }
1085 else if (rc == VERR_FILE_AIO_INSUFFICIENT_EVENTS)
1086 {
1087 PUVM pUVM = VMR3GetUVM(pEpClassFile->Core.pVM);
1088#if defined(RT_OS_LINUX)
1089 rc = VMR3SetError(pUVM, rc, RT_SRC_POS,
1090 N_("Failed to create I/O manager for VM due to insufficient resources on the host. "
1091 "Either increase the amount of allowed events in /proc/sys/fs/aio-max-nr or enable "
1092 "the host I/O cache"));
1093#else
1094 rc = VMR3SetError(pUVM, rc, RT_SRC_POS,
1095 N_("Failed to create I/O manager for VM due to insufficient resources on the host. "
1096 "Enable the host I/O cache"));
1097#endif
1098 }
1099 else
1100 {
1101 PUVM pUVM = VMR3GetUVM(pEpClassFile->Core.pVM);
1102 rc = VMR3SetError(pUVM, rc, RT_SRC_POS,
1103 N_("Failed to create I/O manager for VM due to an unknown error"));
1104 }
1105 }
1106 }
1107
1108 if (RT_FAILURE(rc))
1109 RTFileClose(pEpFile->hFile);
1110 }
1111
1112#ifdef VBOX_WITH_STATISTICS
1113 if (RT_SUCCESS(rc))
1114 {
1115 STAMR3RegisterF(pEpClassFile->Core.pVM, &pEpFile->StatRead,
1116 STAMTYPE_PROFILE_ADV, STAMVISIBILITY_ALWAYS,
1117 STAMUNIT_TICKS_PER_CALL, "Time taken to read from the endpoint",
1118 "/PDM/AsyncCompletion/File/%s/%d/Read", RTPathFilename(pEpFile->Core.pszUri), pEpFile->Core.iStatId);
1119
1120 STAMR3RegisterF(pEpClassFile->Core.pVM, &pEpFile->StatWrite,
1121 STAMTYPE_PROFILE_ADV, STAMVISIBILITY_ALWAYS,
1122 STAMUNIT_TICKS_PER_CALL, "Time taken to write to the endpoint",
1123 "/PDM/AsyncCompletion/File/%s/%d/Write", RTPathFilename(pEpFile->Core.pszUri), pEpFile->Core.iStatId);
1124 }
1125#endif
1126
1127 if (RT_SUCCESS(rc))
1128 LogRel(("AIOMgr: Endpoint for file '%s' (flags %08x) created successfully\n", pszUri, pEpFile->fFlags));
1129
1130 return rc;
1131}
1132
1133static DECLCALLBACK(int) pdmacFileEpRangesLockedDestroy(PAVLRFOFFNODECORE pNode, void *pvUser)
1134{
1135 NOREF(pNode); NOREF(pvUser);
1136 AssertMsgFailed(("The locked ranges tree should be empty at that point\n"));
1137 return VINF_SUCCESS;
1138}
1139
1140static DECLCALLBACK(int) pdmacFileEpClose(PPDMASYNCCOMPLETIONENDPOINT pEndpoint)
1141{
1142 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1143 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pEndpoint->pEpClass;
1144
1145 /* Make sure that all tasks finished for this endpoint. */
1146 int rc = pdmacFileAioMgrCloseEndpoint(pEpFile->pAioMgr, pEpFile);
1147 AssertRC(rc);
1148
1149 /*
1150 * If the async I/O manager is in failsafe mode this is the only endpoint
1151 * he processes and thus can be destroyed now.
1152 */
1153 if (pEpFile->pAioMgr->enmMgrType == PDMACEPFILEMGRTYPE_SIMPLE)
1154 pdmacFileAioMgrDestroy(pEpClassFile, pEpFile->pAioMgr);
1155
1156 /* Free cached tasks. */
1157 PPDMACTASKFILE pTask = pEpFile->pTasksFreeHead;
1158
1159 while (pTask)
1160 {
1161 PPDMACTASKFILE pTaskFree = pTask;
1162 pTask = pTask->pNext;
1163 MMR3HeapFree(pTaskFree);
1164 }
1165
1166 /* Destroy the locked ranges tree now. */
1167 RTAvlrFileOffsetDestroy(pEpFile->AioMgr.pTreeRangesLocked, pdmacFileEpRangesLockedDestroy, NULL);
1168 RTMemFree(pEpFile->AioMgr.pTreeRangesLocked);
1169 pEpFile->AioMgr.pTreeRangesLocked = NULL;
1170
1171 RTFileClose(pEpFile->hFile);
1172
1173#ifdef VBOX_WITH_STATISTICS
1174 /* Not sure if this might be unnecessary because of similar statement in pdmR3AsyncCompletionStatisticsDeregister? */
1175 STAMR3DeregisterF(pEpClassFile->Core.pVM->pUVM, "/PDM/AsyncCompletion/File/%s/*", RTPathFilename(pEpFile->Core.pszUri));
1176#endif
1177
1178 return VINF_SUCCESS;
1179}
1180
1181static DECLCALLBACK(int) pdmacFileEpRead(PPDMASYNCCOMPLETIONTASK pTask,
1182 PPDMASYNCCOMPLETIONENDPOINT pEndpoint, RTFOFF off,
1183 PCRTSGSEG paSegments, size_t cSegments,
1184 size_t cbRead)
1185{
1186 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1187
1188 LogFlowFunc(("pTask=%#p pEndpoint=%#p off=%RTfoff paSegments=%#p cSegments=%zu cbRead=%zu\n",
1189 pTask, pEndpoint, off, paSegments, cSegments, cbRead));
1190
1191 if (RT_UNLIKELY((uint64_t)off + cbRead > pEpFile->cbFile))
1192 return VERR_EOF;
1193
1194 STAM_PROFILE_ADV_START(&pEpFile->StatRead, Read);
1195 pdmacFileEpTaskInit(pTask, cbRead);
1196 int rc = pdmacFileEpTaskInitiate(pTask, pEndpoint, off, paSegments, cSegments, cbRead,
1197 PDMACTASKFILETRANSFER_READ);
1198 STAM_PROFILE_ADV_STOP(&pEpFile->StatRead, Read);
1199
1200 return rc;
1201}
1202
1203static DECLCALLBACK(int) pdmacFileEpWrite(PPDMASYNCCOMPLETIONTASK pTask,
1204 PPDMASYNCCOMPLETIONENDPOINT pEndpoint, RTFOFF off,
1205 PCRTSGSEG paSegments, size_t cSegments,
1206 size_t cbWrite)
1207{
1208 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1209
1210 if (RT_UNLIKELY(pEpFile->fReadonly))
1211 return VERR_NOT_SUPPORTED;
1212
1213 STAM_PROFILE_ADV_START(&pEpFile->StatWrite, Write);
1214
1215 pdmacFileEpTaskInit(pTask, cbWrite);
1216
1217 int rc = pdmacFileEpTaskInitiate(pTask, pEndpoint, off, paSegments, cSegments, cbWrite,
1218 PDMACTASKFILETRANSFER_WRITE);
1219
1220 STAM_PROFILE_ADV_STOP(&pEpFile->StatWrite, Write);
1221
1222 return rc;
1223}
1224
1225static DECLCALLBACK(int) pdmacFileEpFlush(PPDMASYNCCOMPLETIONTASK pTask,
1226 PPDMASYNCCOMPLETIONENDPOINT pEndpoint)
1227{
1228 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1229 PPDMASYNCCOMPLETIONTASKFILE pTaskFile = (PPDMASYNCCOMPLETIONTASKFILE)pTask;
1230
1231 if (RT_UNLIKELY(pEpFile->fReadonly))
1232 return VERR_NOT_SUPPORTED;
1233
1234 pdmacFileEpTaskInit(pTask, 0);
1235
1236 PPDMACTASKFILE pIoTask = pdmacFileTaskAlloc(pEpFile);
1237 if (RT_UNLIKELY(!pIoTask))
1238 return VERR_NO_MEMORY;
1239
1240 pIoTask->pEndpoint = pEpFile;
1241 pIoTask->enmTransferType = PDMACTASKFILETRANSFER_FLUSH;
1242 pIoTask->pvUser = pTaskFile;
1243 pIoTask->pfnCompleted = pdmacFileEpTaskCompleted;
1244 pdmacFileEpAddTask(pEpFile, pIoTask);
1245
1246 return VINF_AIO_TASK_PENDING;
1247}
1248
1249static DECLCALLBACK(int) pdmacFileEpGetSize(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, uint64_t *pcbSize)
1250{
1251 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1252
1253 *pcbSize = ASMAtomicReadU64(&pEpFile->cbFile);
1254
1255 return VINF_SUCCESS;
1256}
1257
1258static DECLCALLBACK(int) pdmacFileEpSetSize(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, uint64_t cbSize)
1259{
1260 int rc;
1261 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1262
1263 rc = RTFileSetSize(pEpFile->hFile, cbSize);
1264 if (RT_SUCCESS(rc))
1265 ASMAtomicWriteU64(&pEpFile->cbFile, cbSize);
1266
1267 return rc;
1268}
1269
1270const PDMASYNCCOMPLETIONEPCLASSOPS g_PDMAsyncCompletionEndpointClassFile =
1271{
1272 /* u32Version */
1273 PDMAC_EPCLASS_OPS_VERSION,
1274 /* pcszName */
1275 "File",
1276 /* enmClassType */
1277 PDMASYNCCOMPLETIONEPCLASSTYPE_FILE,
1278 /* cbEndpointClassGlobal */
1279 sizeof(PDMASYNCCOMPLETIONEPCLASSFILE),
1280 /* cbEndpoint */
1281 sizeof(PDMASYNCCOMPLETIONENDPOINTFILE),
1282 /* cbTask */
1283 sizeof(PDMASYNCCOMPLETIONTASKFILE),
1284 /* pfnInitialize */
1285 pdmacFileInitialize,
1286 /* pfnTerminate */
1287 pdmacFileTerminate,
1288 /* pfnEpInitialize. */
1289 pdmacFileEpInitialize,
1290 /* pfnEpClose */
1291 pdmacFileEpClose,
1292 /* pfnEpRead */
1293 pdmacFileEpRead,
1294 /* pfnEpWrite */
1295 pdmacFileEpWrite,
1296 /* pfnEpFlush */
1297 pdmacFileEpFlush,
1298 /* pfnEpGetSize */
1299 pdmacFileEpGetSize,
1300 /* pfnEpSetSize */
1301 pdmacFileEpSetSize,
1302 /* u32VersionEnd */
1303 PDMAC_EPCLASS_OPS_VERSION
1304};
1305
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