VirtualBox

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

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

AsyncCompletion: Add debugger command to inject delays. Disabled by default

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