VirtualBox

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

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

PDMAsyncCompletion: Return an error for a read beyond the end of the file

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