VirtualBox

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

Last change on this file since 34891 was 34612, checked in by vboxsync, 14 years ago

rcReqRead and rcReqWrite are VBOX_WITH_DEBUGGER, not DEBUG.

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