VirtualBox

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

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

AsyncCompletion/File: Disable async flushes on Solaris hosts for now. They cause strange hangs which needs further investigation

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