VirtualBox

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

Last change on this file since 27920 was 27920, checked in by vboxsync, 15 years ago

AsyncCompletion: Return error code for completed requests

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette