VirtualBox

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

Last change on this file since 35205 was 35190, checked in by vboxsync, 14 years ago

AsyncCompletion: Fixed querying the size of a block device on OS X

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