~ubuntu-branches/ubuntu/raring/virtualbox-ose/raring

« back to all changes in this revision

Viewing changes to src/VBox/VMM/VMMR3/PDMAsyncCompletionFile.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Felix Geyer
  • Date: 2011-01-30 23:27:25 UTC
  • mfrom: (0.3.12 upstream)
  • Revision ID: james.westby@ubuntu.com-20110130232725-2ouajjd2ggdet0zd
Tags: 4.0.2-dfsg-1ubuntu1
* Merge from Debian unstable, remaining changes:
  - Add Apport hook.
    - debian/virtualbox-ose.files/source_virtualbox-ose.py
    - debian/virtualbox-ose.install
  - Drop *-source packages.
* Drop ubuntu-01-fix-build-gcc45.patch, fixed upstream.
* Drop ubuntu-02-as-needed.patch, added to the Debian package.

Show diffs side-by-side

added added

removed removed

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