VirtualBox

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

Last change on this file since 36641 was 36285, checked in by vboxsync, 14 years ago

Never complete tasks on the submitting thread using the callback, this can cause problems if the caller doesn't expect it (like VD)

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

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