VirtualBox

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

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

Enable a few assertions temporary

  • 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 37045 2011-05-12 09:27:45Z 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)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)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 {
671 rc = RTErrConvertFromWin32(GetLastError());
672 }
673#elif defined(RT_OS_DARWIN)
674 struct stat DevStat;
675 if (!fstat(hFile, &DevStat) && S_ISBLK(DevStat.st_mode))
676 {
677 uint64_t cBlocks;
678 uint32_t cbBlock;
679 if (!ioctl(hFile, DKIOCGETBLOCKCOUNT, &cBlocks))
680 {
681 if (!ioctl(hFile, DKIOCGETBLOCKSIZE, &cbBlock))
682 cbSize = cBlocks * cbBlock;
683 else
684 rc = RTErrConvertFromErrno(errno);
685 }
686 else
687 rc = RTErrConvertFromErrno(errno);
688 }
689 else
690 rc = VERR_INVALID_PARAMETER;
691#elif defined(RT_OS_SOLARIS)
692 struct stat DevStat;
693 if (!fstat(hFile, &DevStat) && ( S_ISBLK(DevStat.st_mode)
694 || S_ISCHR(DevStat.st_mode)))
695 {
696 struct dk_minfo mediainfo;
697 if (!ioctl(hFile, DKIOCGMEDIAINFO, &mediainfo))
698 cbSize = mediainfo.dki_capacity * mediainfo.dki_lbsize;
699 else
700 rc = RTErrConvertFromErrno(errno);
701 }
702 else
703 rc = VERR_INVALID_PARAMETER;
704#elif defined(RT_OS_FREEBSD)
705 struct stat DevStat;
706 if (!fstat(hFile, &DevStat) && S_ISCHR(DevStat.st_mode))
707 {
708 off_t cbMedia = 0;
709 if (!ioctl(hFile, DIOCGMEDIASIZE, &cbMedia))
710 {
711 cbSize = cbMedia;
712 }
713 else
714 rc = RTErrConvertFromErrno(errno);
715 }
716 else
717 rc = VERR_INVALID_PARAMETER;
718#else
719 /* Could be a block device */
720 rc = RTFileSeek(hFile, 0, RTFILE_SEEK_END, &cbSize);
721#endif
722
723 if (RT_SUCCESS(rc) && (cbSize != 0))
724 *pcbSize = cbSize;
725 else if (RT_SUCCESS(rc))
726 rc = VERR_NOT_SUPPORTED;
727 }
728
729 return rc;
730}
731
732#ifdef VBOX_WITH_DEBUGGER
733/**
734 * Error inject callback.
735 */
736static DECLCALLBACK(int) pdmacEpFileErrorInject(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR pArgs, unsigned cArgs)
737{
738 /*
739 * Validate input.
740 */
741 DBGC_CMDHLP_REQ_VM_RET(pCmdHlp, pCmd, pVM);
742 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, -1, cArgs == 3);
743 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, pArgs[0].enmType == DBGCVAR_TYPE_STRING);
744 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 1, pArgs[1].enmType == DBGCVAR_TYPE_STRING);
745 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 2, pArgs[2].enmType == DBGCVAR_TYPE_NUMBER);
746
747 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile;
748 pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pVM->pUVM->pdm.s.apAsyncCompletionEndpointClass[PDMASYNCCOMPLETIONEPCLASSTYPE_FILE];
749
750 /* Syntax is "read|write <filename> <status code>" */
751 bool fWrite;
752 if (!RTStrCmp(pArgs[0].u.pszString, "read"))
753 fWrite = false;
754 else if (!RTStrCmp(pArgs[0].u.pszString, "write"))
755 fWrite = true;
756 else
757 return DBGCCmdHlpFail(pCmdHlp, pCmd, "invalid transfer direction '%s'", pArgs[0].u.pszString);
758
759 int32_t rcToInject = (int32_t)pArgs[2].u.u64Number;
760 if ((uint64_t)rcToInject != pArgs[2].u.u64Number)
761 return DBGCCmdHlpFail(pCmdHlp, pCmd, "The status code '%lld' is out of range", pArgs[0].u.u64Number);
762
763
764 /*
765 * Search for the matching endpoint.
766 */
767 RTCritSectEnter(&pEpClassFile->Core.CritSect);
768
769 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEpClassFile->Core.pEndpointsHead;
770 while (pEpFile)
771 {
772 if (!RTStrCmp(pArgs[1].u.pszString, RTPathFilename(pEpFile->Core.pszUri)))
773 break;
774 pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEpFile->Core.pNext;
775 }
776
777 if (pEpFile)
778 {
779 /*
780 * Do the job.
781 */
782 if (fWrite)
783 ASMAtomicXchgS32(&pEpFile->rcReqWrite, rcToInject);
784 else
785 ASMAtomicXchgS32(&pEpFile->rcReqRead, rcToInject);
786
787 DBGCCmdHlpPrintf(pCmdHlp, "Injected %Rrc into '%s' for %s\n",
788 (int)rcToInject, pArgs[1].u.pszString, pArgs[0].u.pszString);
789 }
790
791 RTCritSectLeave(&pEpClassFile->Core.CritSect);
792
793 if (!pEpFile)
794 return DBGCCmdHlpFail(pCmdHlp, pCmd, "No file with name '%s' found", pArgs[1].u.pszString);
795 return VINF_SUCCESS;
796}
797
798# ifdef PDM_ASYNC_COMPLETION_FILE_WITH_DELAY
799/**
800 * Delay inject callback.
801 */
802static DECLCALLBACK(int) pdmacEpFileDelayInject(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR pArgs, unsigned cArgs)
803{
804 /*
805 * Validate input.
806 */
807 DBGC_CMDHLP_REQ_VM_RET(pCmdHlp, pCmd, pVM);
808 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, -1, cArgs == 3);
809 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, pArgs[0].enmType == DBGCVAR_TYPE_STRING);
810 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 1, pArgs[1].enmType == DBGCVAR_TYPE_STRING);
811 DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 2, pArgs[2].enmType == DBGCVAR_TYPE_NUMBER);
812
813 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile;
814 pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pVM->pUVM->pdm.s.apAsyncCompletionEndpointClass[PDMASYNCCOMPLETIONEPCLASSTYPE_FILE];
815
816 /* Syntax is "read|write <filename> <status code>" */
817 bool fWrite;
818 if (!RTStrCmp(pArgs[0].u.pszString, "read"))
819 fWrite = false;
820 else if (!RTStrCmp(pArgs[0].u.pszString, "write"))
821 fWrite = true;
822 else
823 return DBGCCmdHlpFail(pCmdHlp, pCmd, "invalid transfer direction '%s'", pArgs[0].u.pszString);
824
825 uint32_t msDelay = (uint32_t)pArgs[2].u.u64Number;
826 if ((uint64_t)msDelay != pArgs[2].u.u64Number)
827 return DBGCCmdHlpFail(pCmdHlp, pCmd, "The delay '%lld' is out of range", pArgs[0].u.u64Number);
828
829
830 /*
831 * Search for the matching endpoint.
832 */
833 RTCritSectEnter(&pEpClassFile->Core.CritSect);
834
835 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEpClassFile->Core.pEndpointsHead;
836 while (pEpFile)
837 {
838 if (!RTStrCmp(pArgs[1].u.pszString, RTPathFilename(pEpFile->Core.pszUri)))
839 break;
840 pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEpFile->Core.pNext;
841 }
842
843 if (pEpFile)
844 {
845 bool fXchg = ASMAtomicCmpXchgU32(&pEpFile->msDelay, msDelay, 0);
846
847 if (fXchg)
848 DBGCCmdHlpPrintf(pCmdHlp, "Injected delay of %u ms into '%s' for %s\n",
849 msDelay, pArgs[1].u.pszString, pArgs[0].u.pszString);
850 else
851 DBGCCmdHlpPrintf(pCmdHlp, "Another delay for '%s' is still active, ignoring\n",
852 pArgs[1].u.pszString);
853 }
854
855 RTCritSectLeave(&pEpClassFile->Core.CritSect);
856
857 if (!pEpFile)
858 return DBGCCmdHlpFail(pCmdHlp, pCmd, "No file with name '%s' found", pArgs[1].u.pszString);
859 return VINF_SUCCESS;
860}
861# endif /* PDM_ASYNC_COMPLETION_FILE_WITH_DELAY */
862
863#endif /* VBOX_WITH_DEBUGGER */
864
865static int pdmacFileInitialize(PPDMASYNCCOMPLETIONEPCLASS pClassGlobals, PCFGMNODE pCfgNode)
866{
867 int rc = VINF_SUCCESS;
868 RTFILEAIOLIMITS AioLimits; /** < Async I/O limitations. */
869
870 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pClassGlobals;
871
872 rc = RTFileAioGetLimits(&AioLimits);
873#ifdef DEBUG
874 if (RT_SUCCESS(rc) && RTEnvExist("VBOX_ASYNC_IO_FAILBACK"))
875 rc = VERR_ENV_VAR_NOT_FOUND;
876#endif
877 if (RT_FAILURE(rc))
878 {
879 LogRel(("AIO: Async I/O manager not supported (rc=%Rrc). Falling back to simple manager\n",
880 rc));
881 pEpClassFile->enmMgrTypeOverride = PDMACEPFILEMGRTYPE_SIMPLE;
882 pEpClassFile->enmEpBackendDefault = PDMACFILEEPBACKEND_BUFFERED;
883 }
884 else
885 {
886 pEpClassFile->uBitmaskAlignment = AioLimits.cbBufferAlignment ? ~((RTR3UINTPTR)AioLimits.cbBufferAlignment - 1) : RTR3UINTPTR_MAX;
887 pEpClassFile->cReqsOutstandingMax = AioLimits.cReqsOutstandingMax;
888
889 if (pCfgNode)
890 {
891 /* Query the default manager type */
892 char *pszVal = NULL;
893 rc = CFGMR3QueryStringAllocDef(pCfgNode, "IoMgr", &pszVal, "Async");
894 AssertLogRelRCReturn(rc, rc);
895
896 rc = pdmacFileMgrTypeFromName(pszVal, &pEpClassFile->enmMgrTypeOverride);
897 MMR3HeapFree(pszVal);
898 if (RT_FAILURE(rc))
899 return rc;
900
901 LogRel(("AIOMgr: Default manager type is \"%s\"\n", pdmacFileMgrTypeToName(pEpClassFile->enmMgrTypeOverride)));
902
903 /* Query default backend type */
904 rc = CFGMR3QueryStringAllocDef(pCfgNode, "FileBackend", &pszVal, "NonBuffered");
905 AssertLogRelRCReturn(rc, rc);
906
907 rc = pdmacFileBackendTypeFromName(pszVal, &pEpClassFile->enmEpBackendDefault);
908 MMR3HeapFree(pszVal);
909 if (RT_FAILURE(rc))
910 return rc;
911
912 LogRel(("AIOMgr: Default file backend is \"%s\"\n", pdmacFileBackendTypeToName(pEpClassFile->enmEpBackendDefault)));
913
914#ifdef RT_OS_LINUX
915 if ( pEpClassFile->enmMgrTypeOverride == PDMACEPFILEMGRTYPE_ASYNC
916 && pEpClassFile->enmEpBackendDefault == PDMACFILEEPBACKEND_BUFFERED)
917 {
918 LogRel(("AIOMgr: Linux does not support buffered async I/O, changing to non buffered\n"));
919 pEpClassFile->enmEpBackendDefault = PDMACFILEEPBACKEND_NON_BUFFERED;
920 }
921#endif
922 }
923 else
924 {
925 /* No configuration supplied, set defaults */
926 pEpClassFile->enmEpBackendDefault = PDMACFILEEPBACKEND_NON_BUFFERED;
927 pEpClassFile->enmMgrTypeOverride = PDMACEPFILEMGRTYPE_ASYNC;
928 }
929 }
930
931 /* Init critical section. */
932 rc = RTCritSectInit(&pEpClassFile->CritSect);
933
934#ifdef VBOX_WITH_DEBUGGER
935 /* Install the error injection handler. */
936 if (RT_SUCCESS(rc))
937 {
938 rc = DBGCRegisterCommands(&g_aCmds[0], RT_ELEMENTS(g_aCmds));
939 AssertRC(rc);
940 }
941#endif
942
943 return rc;
944}
945
946static void pdmacFileTerminate(PPDMASYNCCOMPLETIONEPCLASS pClassGlobals)
947{
948 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pClassGlobals;
949
950 /* All endpoints should be closed at this point. */
951 AssertMsg(!pEpClassFile->Core.pEndpointsHead, ("There are still endpoints left\n"));
952
953 /* Destroy all left async I/O managers. */
954 while (pEpClassFile->pAioMgrHead)
955 pdmacFileAioMgrDestroy(pEpClassFile, pEpClassFile->pAioMgrHead);
956
957 RTCritSectDelete(&pEpClassFile->CritSect);
958}
959
960static int pdmacFileEpInitialize(PPDMASYNCCOMPLETIONENDPOINT pEndpoint,
961 const char *pszUri, uint32_t fFlags)
962{
963 int rc = VINF_SUCCESS;
964 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
965 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pEndpoint->pEpClass;
966 PDMACEPFILEMGRTYPE enmMgrType = pEpClassFile->enmMgrTypeOverride;
967 PDMACFILEEPBACKEND enmEpBackend = pEpClassFile->enmEpBackendDefault;
968
969 AssertMsgReturn((fFlags & ~(PDMACEP_FILE_FLAGS_READ_ONLY | PDMACEP_FILE_FLAGS_DONT_LOCK | PDMACEP_FILE_FLAGS_HOST_CACHE_ENABLED)) == 0,
970 ("PDMAsyncCompletion: Invalid flag specified\n"), VERR_INVALID_PARAMETER);
971
972 unsigned fFileFlags = RTFILE_O_OPEN;
973
974 /*
975 * Revert to the simple manager and the buffered backend if
976 * the host cache should be enabled.
977 */
978 if (fFlags & PDMACEP_FILE_FLAGS_HOST_CACHE_ENABLED)
979 {
980 enmMgrType = PDMACEPFILEMGRTYPE_SIMPLE;
981 enmEpBackend = PDMACFILEEPBACKEND_BUFFERED;
982 }
983
984 if (fFlags & PDMACEP_FILE_FLAGS_READ_ONLY)
985 fFileFlags |= RTFILE_O_READ | RTFILE_O_DENY_NONE;
986 else
987 {
988 fFileFlags |= RTFILE_O_READWRITE;
989
990 /*
991 * Opened in read/write mode. Check whether the caller wants to
992 * avoid the lock. Return an error in case caching is enabled
993 * because this can lead to data corruption.
994 */
995 if (fFlags & PDMACEP_FILE_FLAGS_DONT_LOCK)
996 fFileFlags |= RTFILE_O_DENY_NONE;
997 else
998 fFileFlags |= RTFILE_O_DENY_WRITE;
999 }
1000
1001 if (enmMgrType == PDMACEPFILEMGRTYPE_ASYNC)
1002 fFileFlags |= RTFILE_O_ASYNC_IO;
1003
1004 if (enmEpBackend == PDMACFILEEPBACKEND_NON_BUFFERED)
1005 {
1006 /*
1007 * We only disable the cache if the size of the file is a multiple of 512.
1008 * Certain hosts like Windows, Linux and Solaris require that transfer sizes
1009 * are aligned to the volume sector size.
1010 * If not we just make sure that the data is written to disk with RTFILE_O_WRITE_THROUGH
1011 * which will trash the host cache but ensures that the host cache will not
1012 * contain dirty buffers.
1013 */
1014 RTFILE File = NIL_RTFILE;
1015
1016 rc = RTFileOpen(&File, pszUri, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
1017 if (RT_SUCCESS(rc))
1018 {
1019 uint64_t cbSize;
1020
1021 rc = pdmacFileEpNativeGetSize(File, &cbSize);
1022 Assert(RT_FAILURE(rc) || cbSize != 0);
1023
1024 if (RT_SUCCESS(rc) && ((cbSize % 512) == 0))
1025 fFileFlags |= RTFILE_O_NO_CACHE;
1026 else
1027 {
1028 /* Downgrade to the buffered backend */
1029 enmEpBackend = PDMACFILEEPBACKEND_BUFFERED;
1030
1031#ifdef RT_OS_LINUX
1032 fFileFlags &= ~RTFILE_O_ASYNC_IO;
1033 enmMgrType = PDMACEPFILEMGRTYPE_SIMPLE;
1034#endif
1035 }
1036 RTFileClose(File);
1037 }
1038 }
1039
1040 /* Open with final flags. */
1041 rc = RTFileOpen(&pEpFile->File, pszUri, fFileFlags);
1042 if ((rc == VERR_INVALID_FUNCTION) || (rc == VERR_INVALID_PARAMETER))
1043 {
1044 LogRel(("pdmacFileEpInitialize: RTFileOpen %s / %08x failed with %Rrc\n",
1045 pszUri, fFileFlags, rc));
1046 /*
1047 * Solaris doesn't support directio on ZFS so far. :-\
1048 * Trying to enable it returns VERR_INVALID_FUNCTION
1049 * (ENOTTY). Remove it and hope for the best.
1050 * ZFS supports write throttling in case applications
1051 * write more data than can be synced to the disk
1052 * without blocking the whole application.
1053 *
1054 * On Linux we have the same problem with cifs.
1055 * Have to disable async I/O here too because it requires O_DIRECT.
1056 */
1057 fFileFlags &= ~RTFILE_O_NO_CACHE;
1058 enmEpBackend = PDMACFILEEPBACKEND_BUFFERED;
1059
1060#ifdef RT_OS_LINUX
1061 fFileFlags &= ~RTFILE_O_ASYNC_IO;
1062 enmMgrType = PDMACEPFILEMGRTYPE_SIMPLE;
1063#endif
1064
1065 /* Open again. */
1066 rc = RTFileOpen(&pEpFile->File, pszUri, fFileFlags);
1067
1068 if (RT_FAILURE(rc))
1069 {
1070 LogRel(("pdmacFileEpInitialize: RTFileOpen %s / %08x failed AGAIN(!) with %Rrc\n",
1071 pszUri, fFileFlags, rc));
1072 }
1073 }
1074
1075 if (RT_SUCCESS(rc))
1076 {
1077 pEpFile->fFlags = fFileFlags;
1078
1079 rc = pdmacFileEpNativeGetSize(pEpFile->File, (uint64_t *)&pEpFile->cbFile);
1080 Assert(RT_FAILURE(rc) || pEpFile->cbFile != 0);
1081
1082 if (RT_SUCCESS(rc))
1083 {
1084 /* Initialize the segment cache */
1085 rc = MMR3HeapAllocZEx(pEpClassFile->Core.pVM, MM_TAG_PDM_ASYNC_COMPLETION,
1086 sizeof(PDMACTASKFILE),
1087 (void **)&pEpFile->pTasksFreeHead);
1088 if (RT_SUCCESS(rc))
1089 {
1090 PPDMACEPFILEMGR pAioMgr = NULL;
1091
1092 pEpFile->pTasksFreeTail = pEpFile->pTasksFreeHead;
1093 pEpFile->cTasksCached = 0;
1094 pEpFile->enmBackendType = enmEpBackend;
1095 /*
1096 * Disable async flushes on Solaris for now.
1097 * They cause weird hangs which needs more investigations.
1098 */
1099#ifndef RT_OS_SOLARIS
1100 pEpFile->fAsyncFlushSupported = true;
1101#else
1102 pEpFile->fAsyncFlushSupported = false;
1103#endif
1104
1105 if (enmMgrType == PDMACEPFILEMGRTYPE_SIMPLE)
1106 {
1107 /* Simple mode. Every file has its own async I/O manager. */
1108 rc = pdmacFileAioMgrCreate(pEpClassFile, &pAioMgr, PDMACEPFILEMGRTYPE_SIMPLE);
1109 AssertRC(rc);
1110 }
1111 else
1112 {
1113 pAioMgr = pEpClassFile->pAioMgrHead;
1114
1115 /* Check for an idling manager of the same type */
1116 while (pAioMgr)
1117 {
1118 if (pAioMgr->enmMgrType == enmMgrType)
1119 break;
1120 pAioMgr = pAioMgr->pNext;
1121 }
1122
1123 if (!pAioMgr)
1124 {
1125 rc = pdmacFileAioMgrCreate(pEpClassFile, &pAioMgr, enmMgrType);
1126 AssertRC(rc);
1127 }
1128 }
1129
1130 pEpFile->AioMgr.pTreeRangesLocked = (PAVLRFOFFTREE)RTMemAllocZ(sizeof(AVLRFOFFTREE));
1131 if (!pEpFile->AioMgr.pTreeRangesLocked)
1132 rc = VERR_NO_MEMORY;
1133 else
1134 {
1135 pEpFile->enmState = PDMASYNCCOMPLETIONENDPOINTFILESTATE_ACTIVE;
1136
1137 /* Assign the endpoint to the thread. */
1138 rc = pdmacFileAioMgrAddEndpoint(pAioMgr, pEpFile);
1139 if (RT_FAILURE(rc))
1140 {
1141 RTMemFree(pEpFile->AioMgr.pTreeRangesLocked);
1142 MMR3HeapFree(pEpFile->pTasksFreeHead);
1143 }
1144 }
1145 }
1146 }
1147
1148 if (RT_FAILURE(rc))
1149 RTFileClose(pEpFile->File);
1150 }
1151
1152#ifdef VBOX_WITH_STATISTICS
1153 if (RT_SUCCESS(rc))
1154 {
1155 STAMR3RegisterF(pEpClassFile->Core.pVM, &pEpFile->StatRead,
1156 STAMTYPE_PROFILE_ADV, STAMVISIBILITY_ALWAYS,
1157 STAMUNIT_TICKS_PER_CALL, "Time taken to read from the endpoint",
1158 "/PDM/AsyncCompletion/File/%s/Read", RTPathFilename(pEpFile->Core.pszUri));
1159
1160 STAMR3RegisterF(pEpClassFile->Core.pVM, &pEpFile->StatWrite,
1161 STAMTYPE_PROFILE_ADV, STAMVISIBILITY_ALWAYS,
1162 STAMUNIT_TICKS_PER_CALL, "Time taken to write to the endpoint",
1163 "/PDM/AsyncCompletion/File/%s/Write", RTPathFilename(pEpFile->Core.pszUri));
1164 }
1165#endif
1166
1167 if (RT_SUCCESS(rc))
1168 LogRel(("AIOMgr: Endpoint for file '%s' (flags %08x) created successfully\n", pszUri, pEpFile->fFlags));
1169
1170 return rc;
1171}
1172
1173static int pdmacFileEpRangesLockedDestroy(PAVLRFOFFNODECORE pNode, void *pvUser)
1174{
1175 AssertMsgFailed(("The locked ranges tree should be empty at that point\n"));
1176 return VINF_SUCCESS;
1177}
1178
1179static int pdmacFileEpClose(PPDMASYNCCOMPLETIONENDPOINT pEndpoint)
1180{
1181 int rc = VINF_SUCCESS;
1182 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1183 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pEndpoint->pEpClass;
1184
1185 /* Make sure that all tasks finished for this endpoint. */
1186 rc = pdmacFileAioMgrCloseEndpoint(pEpFile->pAioMgr, pEpFile);
1187 AssertRC(rc);
1188
1189 /*
1190 * If the async I/O manager is in failsafe mode this is the only endpoint
1191 * he processes and thus can be destroyed now.
1192 */
1193 if (pEpFile->pAioMgr->enmMgrType == PDMACEPFILEMGRTYPE_SIMPLE)
1194 pdmacFileAioMgrDestroy(pEpClassFile, pEpFile->pAioMgr);
1195
1196 /* Free cached tasks. */
1197 PPDMACTASKFILE pTask = pEpFile->pTasksFreeHead;
1198
1199 while (pTask)
1200 {
1201 PPDMACTASKFILE pTaskFree = pTask;
1202 pTask = pTask->pNext;
1203 MMR3HeapFree(pTaskFree);
1204 }
1205
1206 /* Destroy the locked ranges tree now. */
1207 RTAvlrFileOffsetDestroy(pEpFile->AioMgr.pTreeRangesLocked, pdmacFileEpRangesLockedDestroy, NULL);
1208
1209 RTFileClose(pEpFile->File);
1210
1211#ifdef VBOX_WITH_STATISTICS
1212 STAMR3Deregister(pEpClassFile->Core.pVM, &pEpFile->StatRead);
1213 STAMR3Deregister(pEpClassFile->Core.pVM, &pEpFile->StatWrite);
1214#endif
1215
1216 return VINF_SUCCESS;
1217}
1218
1219static int pdmacFileEpRead(PPDMASYNCCOMPLETIONTASK pTask,
1220 PPDMASYNCCOMPLETIONENDPOINT pEndpoint, RTFOFF off,
1221 PCRTSGSEG paSegments, size_t cSegments,
1222 size_t cbRead)
1223{
1224 int rc = VINF_SUCCESS;
1225 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1226
1227 LogFlowFunc(("pTask=%#p pEndpoint=%#p off=%RTfoff paSegments=%#p cSegments=%zu cbRead=%zu\n",
1228 pTask, pEndpoint, off, paSegments, cSegments, cbRead));
1229
1230 STAM_PROFILE_ADV_START(&pEpFile->StatRead, Read);
1231
1232 pdmacFileEpTaskInit(pTask, cbRead);
1233
1234 rc = pdmacFileEpTaskInitiate(pTask, pEndpoint, off, paSegments, cSegments, cbRead,
1235 PDMACTASKFILETRANSFER_READ);
1236
1237 STAM_PROFILE_ADV_STOP(&pEpFile->StatRead, Read);
1238
1239 return rc;
1240}
1241
1242static int pdmacFileEpWrite(PPDMASYNCCOMPLETIONTASK pTask,
1243 PPDMASYNCCOMPLETIONENDPOINT pEndpoint, RTFOFF off,
1244 PCRTSGSEG paSegments, size_t cSegments,
1245 size_t cbWrite)
1246{
1247 int rc = VINF_SUCCESS;
1248 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1249
1250 if (RT_UNLIKELY(pEpFile->fReadonly))
1251 return VERR_NOT_SUPPORTED;
1252
1253 STAM_PROFILE_ADV_START(&pEpFile->StatWrite, Write);
1254
1255 pdmacFileEpTaskInit(pTask, cbWrite);
1256
1257 rc = pdmacFileEpTaskInitiate(pTask, pEndpoint, off, paSegments, cSegments, cbWrite,
1258 PDMACTASKFILETRANSFER_WRITE);
1259
1260 STAM_PROFILE_ADV_STOP(&pEpFile->StatWrite, Write);
1261
1262 return rc;
1263}
1264
1265static int pdmacFileEpFlush(PPDMASYNCCOMPLETIONTASK pTask,
1266 PPDMASYNCCOMPLETIONENDPOINT pEndpoint)
1267{
1268 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1269 PPDMASYNCCOMPLETIONTASKFILE pTaskFile = (PPDMASYNCCOMPLETIONTASKFILE)pTask;
1270
1271 if (RT_UNLIKELY(pEpFile->fReadonly))
1272 return VERR_NOT_SUPPORTED;
1273
1274 pdmacFileEpTaskInit(pTask, 0);
1275
1276 PPDMACTASKFILE pIoTask = pdmacFileTaskAlloc(pEpFile);
1277 if (RT_UNLIKELY(!pIoTask))
1278 return VERR_NO_MEMORY;
1279
1280 pIoTask->pEndpoint = pEpFile;
1281 pIoTask->enmTransferType = PDMACTASKFILETRANSFER_FLUSH;
1282 pIoTask->pvUser = pTaskFile;
1283 pIoTask->pfnCompleted = pdmacFileEpTaskCompleted;
1284 pdmacFileEpAddTask(pEpFile, pIoTask);
1285
1286 return VINF_AIO_TASK_PENDING;
1287}
1288
1289static int pdmacFileEpGetSize(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, uint64_t *pcbSize)
1290{
1291 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1292
1293 *pcbSize = ASMAtomicReadU64(&pEpFile->cbFile);
1294
1295 return VINF_SUCCESS;
1296}
1297
1298static int pdmacFileEpSetSize(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, uint64_t cbSize)
1299{
1300 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1301
1302 ASMAtomicWriteU64(&pEpFile->cbFile, cbSize);
1303 return RTFileSetSize(pEpFile->File, cbSize);
1304}
1305
1306const PDMASYNCCOMPLETIONEPCLASSOPS g_PDMAsyncCompletionEndpointClassFile =
1307{
1308 /* u32Version */
1309 PDMAC_EPCLASS_OPS_VERSION,
1310 /* pcszName */
1311 "File",
1312 /* enmClassType */
1313 PDMASYNCCOMPLETIONEPCLASSTYPE_FILE,
1314 /* cbEndpointClassGlobal */
1315 sizeof(PDMASYNCCOMPLETIONEPCLASSFILE),
1316 /* cbEndpoint */
1317 sizeof(PDMASYNCCOMPLETIONENDPOINTFILE),
1318 /* cbTask */
1319 sizeof(PDMASYNCCOMPLETIONTASKFILE),
1320 /* pfnInitialize */
1321 pdmacFileInitialize,
1322 /* pfnTerminate */
1323 pdmacFileTerminate,
1324 /* pfnEpInitialize. */
1325 pdmacFileEpInitialize,
1326 /* pfnEpClose */
1327 pdmacFileEpClose,
1328 /* pfnEpRead */
1329 pdmacFileEpRead,
1330 /* pfnEpWrite */
1331 pdmacFileEpWrite,
1332 /* pfnEpFlush */
1333 pdmacFileEpFlush,
1334 /* pfnEpGetSize */
1335 pdmacFileEpGetSize,
1336 /* pfnEpSetSize */
1337 pdmacFileEpSetSize,
1338 /* u32VersionEnd */
1339 PDMAC_EPCLASS_OPS_VERSION
1340};
1341
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