VirtualBox

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

最後變更 在這個檔案從99756是 99739,由 vboxsync 提交於 23 月 前

*: doxygen corrections (mostly about removing @returns from functions returning void).

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 47.8 KB
 
1/* $Id: PDMAsyncCompletionFile.cpp 99739 2023-05-11 01:01:08Z 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.alldomusa.eu.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
395int 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
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette