VirtualBox

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

Last change on this file since 39034 was 39034, checked in by vboxsync, 13 years ago

VMM,INTNET: Addressing unused variable warnings.

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