VirtualBox

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

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

r62531 improved

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