VirtualBox

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

Last change on this file since 31026 was 30952, checked in by vboxsync, 14 years ago

AsyncCompletion: Don't do the credit updating if the bandwidth is unlimited. Saves a few CPU cycles and might fix I/O timeouts.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 40.4 KB
Line 
1/* $Id: PDMAsyncCompletionFile.cpp 30952 2010-07-21 11:44:27Z 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 = true;
521
522 LogFlowFunc(("pBwMgr=%p cbTransfer=%u\n", pBwMgr, cbTransfer));
523
524 if (pBwMgr->cbVMTransferPerSecMax != UINT32_MAX) /* No need to check if bandwidth is unlimited. */
525 {
526 uint32_t cbOld = ASMAtomicSubU32(&pBwMgr->cbVMTransferAllowed, cbTransfer);
527 if (RT_LIKELY(cbOld >= cbTransfer))
528 fAllowed = true;
529 else
530 {
531 fAllowed = false;
532
533 /* We are out of ressources Check if we can update again. */
534 uint64_t tsNow = RTTimeSystemNanoTS();
535 uint64_t tsUpdatedLast = ASMAtomicUoReadU64(&pBwMgr->tsUpdatedLast);
536
537 if (tsNow - tsUpdatedLast >= (1000*1000*1000))
538 {
539 if (ASMAtomicCmpXchgU64(&pBwMgr->tsUpdatedLast, tsNow, tsUpdatedLast))
540 {
541 if (pBwMgr->cbVMTransferPerSecStart < pBwMgr->cbVMTransferPerSecMax)
542 {
543 pBwMgr->cbVMTransferPerSecStart = RT_MIN(pBwMgr->cbVMTransferPerSecMax, pBwMgr->cbVMTransferPerSecStart + pBwMgr->cbVMTransferPerSecStep);
544 LogFlow(("AIOMgr: Increasing maximum bandwidth to %u bytes/sec\n", pBwMgr->cbVMTransferPerSecStart));
545 }
546
547 /* Update */
548 ASMAtomicWriteU32(&pBwMgr->cbVMTransferAllowed, pBwMgr->cbVMTransferPerSecStart - cbTransfer);
549 fAllowed = true;
550 LogFlow(("AIOMgr: Refreshed bandwidth\n"));
551 }
552 }
553 else
554 ASMAtomicAddU32(&pBwMgr->cbVMTransferAllowed, cbTransfer);
555 }
556 }
557
558 LogFlowFunc(("fAllowed=%RTbool\n", fAllowed));
559
560 return fAllowed;
561}
562
563static int pdmacFileMgrTypeFromName(const char *pszVal, PPDMACEPFILEMGRTYPE penmMgrType)
564{
565 int rc = VINF_SUCCESS;
566
567 if (!RTStrCmp(pszVal, "Simple"))
568 *penmMgrType = PDMACEPFILEMGRTYPE_SIMPLE;
569 else if (!RTStrCmp(pszVal, "Async"))
570 *penmMgrType = PDMACEPFILEMGRTYPE_ASYNC;
571 else
572 rc = VERR_CFGM_CONFIG_UNKNOWN_VALUE;
573
574 return rc;
575}
576
577static const char *pdmacFileMgrTypeToName(PDMACEPFILEMGRTYPE enmMgrType)
578{
579 if (enmMgrType == PDMACEPFILEMGRTYPE_SIMPLE)
580 return "Simple";
581 if (enmMgrType == PDMACEPFILEMGRTYPE_ASYNC)
582 return "Async";
583
584 return NULL;
585}
586
587static int pdmacFileBackendTypeFromName(const char *pszVal, PPDMACFILEEPBACKEND penmBackendType)
588{
589 int rc = VINF_SUCCESS;
590
591 if (!RTStrCmp(pszVal, "Buffered"))
592 *penmBackendType = PDMACFILEEPBACKEND_BUFFERED;
593 else if (!RTStrCmp(pszVal, "NonBuffered"))
594 *penmBackendType = PDMACFILEEPBACKEND_NON_BUFFERED;
595 else
596 rc = VERR_CFGM_CONFIG_UNKNOWN_VALUE;
597
598 return rc;
599}
600
601static const char *pdmacFileBackendTypeToName(PDMACFILEEPBACKEND enmBackendType)
602{
603 if (enmBackendType == PDMACFILEEPBACKEND_BUFFERED)
604 return "Buffered";
605 if (enmBackendType == PDMACFILEEPBACKEND_NON_BUFFERED)
606 return "NonBuffered";
607
608 return NULL;
609}
610
611/**
612 * Get the size of the given file.
613 * Works for block devices too.
614 *
615 * @returns VBox status code.
616 * @param hFile The file handle.
617 * @param pcbSize Where to store the size of the file on success.
618 */
619static int pdmacFileEpNativeGetSize(RTFILE hFile, uint64_t *pcbSize)
620{
621 int rc = VINF_SUCCESS;
622 uint64_t cbSize = 0;
623
624 rc = RTFileGetSize(hFile, &cbSize);
625 if (RT_SUCCESS(rc) && (cbSize != 0))
626 *pcbSize = cbSize;
627 else
628 {
629#ifdef RT_OS_WINDOWS
630 DISK_GEOMETRY DriveGeo;
631 DWORD cbDriveGeo;
632 if (DeviceIoControl((HANDLE)hFile,
633 IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
634 &DriveGeo, sizeof(DriveGeo), &cbDriveGeo, NULL))
635 {
636 if ( DriveGeo.MediaType == FixedMedia
637 || DriveGeo.MediaType == RemovableMedia)
638 {
639 cbSize = DriveGeo.Cylinders.QuadPart
640 * DriveGeo.TracksPerCylinder
641 * DriveGeo.SectorsPerTrack
642 * DriveGeo.BytesPerSector;
643
644 GET_LENGTH_INFORMATION DiskLenInfo;
645 DWORD junk;
646 if (DeviceIoControl((HANDLE)hFile,
647 IOCTL_DISK_GET_LENGTH_INFO, NULL, 0,
648 &DiskLenInfo, sizeof(DiskLenInfo), &junk, (LPOVERLAPPED)NULL))
649 {
650 /* IOCTL_DISK_GET_LENGTH_INFO is supported -- override cbSize. */
651 cbSize = DiskLenInfo.Length.QuadPart;
652 }
653
654 rc = VINF_SUCCESS;
655 }
656 else
657 {
658 rc = VERR_INVALID_PARAMETER;
659 }
660 }
661 else
662 {
663 rc = RTErrConvertFromWin32(GetLastError());
664 }
665#else
666 /* Could be a block device */
667 rc = RTFileSeek(hFile, 0, RTFILE_SEEK_END, &cbSize);
668
669#endif
670 if (RT_SUCCESS(rc) && (cbSize != 0))
671 *pcbSize = cbSize;
672 else if (RT_SUCCESS(rc))
673 rc = VERR_NOT_SUPPORTED;
674 }
675
676 return rc;
677}
678
679static int pdmacFileInitialize(PPDMASYNCCOMPLETIONEPCLASS pClassGlobals, PCFGMNODE pCfgNode)
680{
681 int rc = VINF_SUCCESS;
682 RTFILEAIOLIMITS AioLimits; /** < Async I/O limitations. */
683
684 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pClassGlobals;
685
686 rc = RTFileAioGetLimits(&AioLimits);
687#ifdef DEBUG
688 if (RT_SUCCESS(rc) && RTEnvExist("VBOX_ASYNC_IO_FAILBACK"))
689 rc = VERR_ENV_VAR_NOT_FOUND;
690#endif
691 if (RT_FAILURE(rc))
692 {
693 LogRel(("AIO: Async I/O manager not supported (rc=%Rrc). Falling back to simple manager\n",
694 rc));
695 pEpClassFile->enmMgrTypeOverride = PDMACEPFILEMGRTYPE_SIMPLE;
696 pEpClassFile->enmEpBackendDefault = PDMACFILEEPBACKEND_BUFFERED;
697 }
698 else
699 {
700 pEpClassFile->uBitmaskAlignment = AioLimits.cbBufferAlignment ? ~((RTR3UINTPTR)AioLimits.cbBufferAlignment - 1) : RTR3UINTPTR_MAX;
701 pEpClassFile->cReqsOutstandingMax = AioLimits.cReqsOutstandingMax;
702
703 if (pCfgNode)
704 {
705 /* Query the default manager type */
706 char *pszVal = NULL;
707 rc = CFGMR3QueryStringAllocDef(pCfgNode, "IoMgr", &pszVal, "Async");
708 AssertLogRelRCReturn(rc, rc);
709
710 rc = pdmacFileMgrTypeFromName(pszVal, &pEpClassFile->enmMgrTypeOverride);
711 MMR3HeapFree(pszVal);
712 if (RT_FAILURE(rc))
713 return rc;
714
715 LogRel(("AIOMgr: Default manager type is \"%s\"\n", pdmacFileMgrTypeToName(pEpClassFile->enmMgrTypeOverride)));
716
717 /* Query default backend type */
718 rc = CFGMR3QueryStringAllocDef(pCfgNode, "FileBackend", &pszVal, "NonBuffered");
719 AssertLogRelRCReturn(rc, rc);
720
721 rc = pdmacFileBackendTypeFromName(pszVal, &pEpClassFile->enmEpBackendDefault);
722 MMR3HeapFree(pszVal);
723 if (RT_FAILURE(rc))
724 return rc;
725
726 LogRel(("AIOMgr: Default file backend is \"%s\"\n", pdmacFileBackendTypeToName(pEpClassFile->enmEpBackendDefault)));
727
728#ifdef RT_OS_LINUX
729 if ( pEpClassFile->enmMgrTypeOverride == PDMACEPFILEMGRTYPE_ASYNC
730 && pEpClassFile->enmEpBackendDefault == PDMACFILEEPBACKEND_BUFFERED)
731 {
732 LogRel(("AIOMgr: Linux does not support buffered async I/O, changing to non buffered\n"));
733 pEpClassFile->enmEpBackendDefault = PDMACFILEEPBACKEND_NON_BUFFERED;
734 }
735#endif
736 }
737 else
738 {
739 /* No configuration supplied, set defaults */
740 pEpClassFile->enmEpBackendDefault = PDMACFILEEPBACKEND_NON_BUFFERED;
741 pEpClassFile->enmMgrTypeOverride = PDMACEPFILEMGRTYPE_ASYNC;
742 }
743 }
744
745 /* Init critical section. */
746 rc = RTCritSectInit(&pEpClassFile->CritSect);
747 if (RT_SUCCESS(rc))
748 {
749 /* Check if the cache was disabled by the user. */
750 rc = CFGMR3QueryBoolDef(pCfgNode, "CacheEnabled", &pEpClassFile->fCacheEnabled, true);
751 AssertLogRelRCReturn(rc, rc);
752
753 if (pEpClassFile->fCacheEnabled)
754 {
755 /* Init cache structure */
756 rc = pdmacFileCacheInit(pEpClassFile, pCfgNode);
757 if (RT_FAILURE(rc))
758 {
759 pEpClassFile->fCacheEnabled = false;
760 LogRel(("AIOMgr: Failed to initialise the cache (rc=%Rrc), disabled caching\n"));
761 }
762 }
763 else
764 LogRel(("AIOMgr: Cache was globally disabled\n"));
765
766 rc = pdmacFileBwMgrInitialize(pEpClassFile, pCfgNode, &pEpClassFile->pBwMgr);
767 if (RT_FAILURE(rc))
768 RTCritSectDelete(&pEpClassFile->CritSect);
769 }
770
771 return rc;
772}
773
774static void pdmacFileTerminate(PPDMASYNCCOMPLETIONEPCLASS pClassGlobals)
775{
776 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pClassGlobals;
777
778 /* All endpoints should be closed at this point. */
779 AssertMsg(!pEpClassFile->Core.pEndpointsHead, ("There are still endpoints left\n"));
780
781 /* Destroy all left async I/O managers. */
782 while (pEpClassFile->pAioMgrHead)
783 pdmacFileAioMgrDestroy(pEpClassFile, pEpClassFile->pAioMgrHead);
784
785 /* Destroy the cache. */
786 if (pEpClassFile->fCacheEnabled)
787 pdmacFileCacheDestroy(pEpClassFile);
788
789 RTCritSectDelete(&pEpClassFile->CritSect);
790 pdmacFileBwMgrDestroy(pEpClassFile->pBwMgr);
791}
792
793static int pdmacFileEpInitialize(PPDMASYNCCOMPLETIONENDPOINT pEndpoint,
794 const char *pszUri, uint32_t fFlags)
795{
796 int rc = VINF_SUCCESS;
797 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
798 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pEndpoint->pEpClass;
799 PDMACEPFILEMGRTYPE enmMgrType = pEpClassFile->enmMgrTypeOverride;
800 PDMACFILEEPBACKEND enmEpBackend = pEpClassFile->enmEpBackendDefault;
801
802 AssertMsgReturn((fFlags & ~(PDMACEP_FILE_FLAGS_READ_ONLY | PDMACEP_FILE_FLAGS_CACHING)) == 0,
803 ("PDMAsyncCompletion: Invalid flag specified\n"), VERR_INVALID_PARAMETER);
804
805 unsigned fFileFlags = fFlags & PDMACEP_FILE_FLAGS_READ_ONLY
806 ? RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE
807 : RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE;
808
809 if (enmMgrType == PDMACEPFILEMGRTYPE_ASYNC)
810 fFileFlags |= RTFILE_O_ASYNC_IO;
811
812 if (enmEpBackend == PDMACFILEEPBACKEND_NON_BUFFERED)
813 {
814 /*
815 * We only disable the cache if the size of the file is a multiple of 512.
816 * Certain hosts like Windows, Linux and Solaris require that transfer sizes
817 * are aligned to the volume sector size.
818 * If not we just make sure that the data is written to disk with RTFILE_O_WRITE_THROUGH
819 * which will trash the host cache but ensures that the host cache will not
820 * contain dirty buffers.
821 */
822 RTFILE File = NIL_RTFILE;
823
824 rc = RTFileOpen(&File, pszUri, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
825 if (RT_SUCCESS(rc))
826 {
827 uint64_t cbSize;
828
829 rc = pdmacFileEpNativeGetSize(File, &cbSize);
830 Assert(RT_FAILURE(rc) || cbSize != 0);
831
832 if (RT_SUCCESS(rc) && ((cbSize % 512) == 0))
833 fFileFlags |= RTFILE_O_NO_CACHE;
834 else
835 {
836 /* Downgrade to the buffered backend */
837 enmEpBackend = PDMACFILEEPBACKEND_BUFFERED;
838
839#ifdef RT_OS_LINUX
840 fFileFlags &= ~RTFILE_O_ASYNC_IO;
841 enmMgrType = PDMACEPFILEMGRTYPE_SIMPLE;
842#endif
843 }
844 RTFileClose(File);
845 }
846 }
847
848 /* Open with final flags. */
849 rc = RTFileOpen(&pEpFile->File, pszUri, fFileFlags);
850 if ((rc == VERR_INVALID_FUNCTION) || (rc == VERR_INVALID_PARAMETER))
851 {
852 LogRel(("pdmacFileEpInitialize: RTFileOpen %s / %08x failed with %Rrc\n",
853 pszUri, fFileFlags, rc));
854 /*
855 * Solaris doesn't support directio on ZFS so far. :-\
856 * Trying to enable it returns VERR_INVALID_FUNCTION
857 * (ENOTTY). Remove it and hope for the best.
858 * ZFS supports write throttling in case applications
859 * write more data than can be synced to the disk
860 * without blocking the whole application.
861 *
862 * On Linux we have the same problem with cifs.
863 * Have to disable async I/O here too because it requires O_DIRECT.
864 */
865 fFileFlags &= ~RTFILE_O_NO_CACHE;
866 enmEpBackend = PDMACFILEEPBACKEND_BUFFERED;
867
868#ifdef RT_OS_LINUX
869 fFileFlags &= ~RTFILE_O_ASYNC_IO;
870 enmMgrType = PDMACEPFILEMGRTYPE_SIMPLE;
871#endif
872
873 /* Open again. */
874 rc = RTFileOpen(&pEpFile->File, pszUri, fFileFlags);
875
876 if (RT_FAILURE(rc))
877 {
878 LogRel(("pdmacFileEpInitialize: RTFileOpen %s / %08x failed AGAIN(!) with %Rrc\n",
879 pszUri, fFileFlags, rc));
880 }
881 }
882
883 if (RT_SUCCESS(rc))
884 {
885 pEpFile->fFlags = fFileFlags;
886
887 rc = pdmacFileEpNativeGetSize(pEpFile->File, (uint64_t *)&pEpFile->cbFile);
888 Assert(RT_FAILURE(rc) || pEpFile->cbFile != 0);
889
890 if (RT_SUCCESS(rc))
891 {
892 /* Initialize the segment cache */
893 rc = MMR3HeapAllocZEx(pEpClassFile->Core.pVM, MM_TAG_PDM_ASYNC_COMPLETION,
894 sizeof(PDMACTASKFILE),
895 (void **)&pEpFile->pTasksFreeHead);
896 if (RT_SUCCESS(rc))
897 {
898 PPDMACEPFILEMGR pAioMgr = NULL;
899
900 pEpFile->cbEndpoint = pEpFile->cbFile;
901 pEpFile->pTasksFreeTail = pEpFile->pTasksFreeHead;
902 pEpFile->cTasksCached = 0;
903 pEpFile->pBwMgr = pEpClassFile->pBwMgr;
904 pEpFile->enmBackendType = enmEpBackend;
905 /*
906 * Disable async flushes on Solaris for now.
907 * They cause weird hangs which needs more investigations.
908 */
909#ifndef RT_OS_SOLARIS
910 pEpFile->fAsyncFlushSupported = true;
911#else
912 pEpFile->fAsyncFlushSupported = false;
913#endif
914 pdmacFileBwRef(pEpFile->pBwMgr);
915
916 if (enmMgrType == PDMACEPFILEMGRTYPE_SIMPLE)
917 {
918 /* Simple mode. Every file has its own async I/O manager. */
919 rc = pdmacFileAioMgrCreate(pEpClassFile, &pAioMgr, PDMACEPFILEMGRTYPE_SIMPLE);
920 AssertRC(rc);
921 }
922 else
923 {
924 if ( (fFlags & PDMACEP_FILE_FLAGS_CACHING)
925 && (pEpClassFile->fCacheEnabled))
926 {
927 pEpFile->fCaching = true;
928 rc = pdmacFileEpCacheInit(pEpFile, pEpClassFile);
929 if (RT_FAILURE(rc))
930 {
931 LogRel(("AIOMgr: Endpoint for \"%s\" was opened with caching but initializing cache failed. Disabled caching\n", pszUri));
932 pEpFile->fCaching = false;
933 }
934 }
935
936 pAioMgr = pEpClassFile->pAioMgrHead;
937
938 /* Check for an idling manager of the same type */
939 while (pAioMgr)
940 {
941 if (pAioMgr->enmMgrType == enmMgrType)
942 break;
943 pAioMgr = pAioMgr->pNext;
944 }
945
946 if (!pAioMgr)
947 {
948 rc = pdmacFileAioMgrCreate(pEpClassFile, &pAioMgr, enmMgrType);
949 AssertRC(rc);
950 }
951 }
952
953 pEpFile->AioMgr.pTreeRangesLocked = (PAVLRFOFFTREE)RTMemAllocZ(sizeof(AVLRFOFFTREE));
954 if (!pEpFile->AioMgr.pTreeRangesLocked)
955 rc = VERR_NO_MEMORY;
956 else
957 {
958 pEpFile->enmState = PDMASYNCCOMPLETIONENDPOINTFILESTATE_ACTIVE;
959
960 /* Assign the endpoint to the thread. */
961 rc = pdmacFileAioMgrAddEndpoint(pAioMgr, pEpFile);
962 if (RT_FAILURE(rc))
963 {
964 RTMemFree(pEpFile->AioMgr.pTreeRangesLocked);
965 MMR3HeapFree(pEpFile->pTasksFreeHead);
966 pdmacFileBwUnref(pEpFile->pBwMgr);
967 }
968 }
969 }
970 }
971
972 if (RT_FAILURE(rc))
973 RTFileClose(pEpFile->File);
974 }
975
976#ifdef VBOX_WITH_STATISTICS
977 if (RT_SUCCESS(rc))
978 {
979 STAMR3RegisterF(pEpClassFile->Core.pVM, &pEpFile->StatRead,
980 STAMTYPE_PROFILE_ADV, STAMVISIBILITY_ALWAYS,
981 STAMUNIT_TICKS_PER_CALL, "Time taken to read from the endpoint",
982 "/PDM/AsyncCompletion/File/%s/Read", RTPathFilename(pEpFile->Core.pszUri));
983
984 STAMR3RegisterF(pEpClassFile->Core.pVM, &pEpFile->StatWrite,
985 STAMTYPE_PROFILE_ADV, STAMVISIBILITY_ALWAYS,
986 STAMUNIT_TICKS_PER_CALL, "Time taken to write to the endpoint",
987 "/PDM/AsyncCompletion/File/%s/Write", RTPathFilename(pEpFile->Core.pszUri));
988 }
989#endif
990
991 if (RT_SUCCESS(rc))
992 LogRel(("AIOMgr: Endpoint for file '%s' (flags %08x) created successfully\n", pszUri, pEpFile->fFlags));
993
994 return rc;
995}
996
997static int pdmacFileEpRangesLockedDestroy(PAVLRFOFFNODECORE pNode, void *pvUser)
998{
999 AssertMsgFailed(("The locked ranges tree should be empty at that point\n"));
1000 return VINF_SUCCESS;
1001}
1002
1003static int pdmacFileEpClose(PPDMASYNCCOMPLETIONENDPOINT pEndpoint)
1004{
1005 int rc = VINF_SUCCESS;
1006 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1007 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pEndpoint->pEpClass;
1008
1009 /* Free the cached data. */
1010 if (pEpFile->fCaching)
1011 {
1012 rc = pdmacFileEpCacheFlush(pEpFile);
1013 AssertRC(rc);
1014 }
1015
1016 /* Make sure that all tasks finished for this endpoint. */
1017 rc = pdmacFileAioMgrCloseEndpoint(pEpFile->pAioMgr, pEpFile);
1018 AssertRC(rc);
1019
1020 /* endpoint and real file size should better be equal now. */
1021 AssertMsg(pEpFile->cbFile == pEpFile->cbEndpoint,
1022 ("Endpoint and real file size should match now!\n"));
1023
1024 /* Destroy any per endpoint cache data */
1025 if (pEpFile->fCaching)
1026 pdmacFileEpCacheDestroy(pEpFile);
1027
1028 /*
1029 * If the async I/O manager is in failsafe mode this is the only endpoint
1030 * he processes and thus can be destroyed now.
1031 */
1032 if (pEpFile->pAioMgr->enmMgrType == PDMACEPFILEMGRTYPE_SIMPLE)
1033 pdmacFileAioMgrDestroy(pEpClassFile, pEpFile->pAioMgr);
1034
1035 /* Free cached tasks. */
1036 PPDMACTASKFILE pTask = pEpFile->pTasksFreeHead;
1037
1038 while (pTask)
1039 {
1040 PPDMACTASKFILE pTaskFree = pTask;
1041 pTask = pTask->pNext;
1042 MMR3HeapFree(pTaskFree);
1043 }
1044
1045 /* Remove from the bandwidth manager */
1046 pdmacFileBwUnref(pEpFile->pBwMgr);
1047
1048 /* Destroy the locked ranges tree now. */
1049 RTAvlrFileOffsetDestroy(pEpFile->AioMgr.pTreeRangesLocked, pdmacFileEpRangesLockedDestroy, NULL);
1050
1051 RTFileClose(pEpFile->File);
1052
1053#ifdef VBOX_WITH_STATISTICS
1054 STAMR3Deregister(pEpClassFile->Core.pVM, &pEpFile->StatRead);
1055 STAMR3Deregister(pEpClassFile->Core.pVM, &pEpFile->StatWrite);
1056#endif
1057
1058 return VINF_SUCCESS;
1059}
1060
1061static int pdmacFileEpRead(PPDMASYNCCOMPLETIONTASK pTask,
1062 PPDMASYNCCOMPLETIONENDPOINT pEndpoint, RTFOFF off,
1063 PCRTSGSEG paSegments, size_t cSegments,
1064 size_t cbRead)
1065{
1066 int rc = VINF_SUCCESS;
1067 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1068
1069 LogFlowFunc(("pTask=%#p pEndpoint=%#p off=%RTfoff paSegments=%#p cSegments=%zu cbRead=%zu\n",
1070 pTask, pEndpoint, off, paSegments, cSegments, cbRead));
1071
1072 STAM_PROFILE_ADV_START(&pEpFile->StatRead, Read);
1073
1074 pdmacFileEpTaskInit(pTask, cbRead);
1075
1076 if (pEpFile->fCaching)
1077 rc = pdmacFileEpCacheRead(pEpFile, (PPDMASYNCCOMPLETIONTASKFILE)pTask,
1078 off, paSegments, cSegments, cbRead);
1079 else
1080 rc = pdmacFileEpTaskInitiate(pTask, pEndpoint, off, paSegments, cSegments, cbRead,
1081 PDMACTASKFILETRANSFER_READ);
1082
1083 STAM_PROFILE_ADV_STOP(&pEpFile->StatRead, Read);
1084
1085 return rc;
1086}
1087
1088static int pdmacFileEpWrite(PPDMASYNCCOMPLETIONTASK pTask,
1089 PPDMASYNCCOMPLETIONENDPOINT pEndpoint, RTFOFF off,
1090 PCRTSGSEG paSegments, size_t cSegments,
1091 size_t cbWrite)
1092{
1093 int rc = VINF_SUCCESS;
1094 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1095
1096 if (RT_UNLIKELY(pEpFile->fReadonly))
1097 return VERR_NOT_SUPPORTED;
1098
1099 STAM_PROFILE_ADV_START(&pEpFile->StatWrite, Write);
1100
1101 pdmacFileEpTaskInit(pTask, cbWrite);
1102
1103 if (pEpFile->fCaching)
1104 rc = pdmacFileEpCacheWrite(pEpFile, (PPDMASYNCCOMPLETIONTASKFILE)pTask,
1105 off, paSegments, cSegments, cbWrite);
1106 else
1107 rc = pdmacFileEpTaskInitiate(pTask, pEndpoint, off, paSegments, cSegments, cbWrite,
1108 PDMACTASKFILETRANSFER_WRITE);
1109
1110 STAM_PROFILE_ADV_STOP(&pEpFile->StatWrite, Write);
1111
1112 /* Increase endpoint size. */
1113 if ( RT_SUCCESS(rc)
1114 && ((uint64_t)off + cbWrite) > pEpFile->cbEndpoint)
1115 ASMAtomicWriteU64(&pEpFile->cbEndpoint, (uint64_t)off + cbWrite);
1116
1117 return rc;
1118}
1119
1120static int pdmacFileEpFlush(PPDMASYNCCOMPLETIONTASK pTask,
1121 PPDMASYNCCOMPLETIONENDPOINT pEndpoint)
1122{
1123 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1124 PPDMASYNCCOMPLETIONTASKFILE pTaskFile = (PPDMASYNCCOMPLETIONTASKFILE)pTask;
1125
1126 if (RT_UNLIKELY(pEpFile->fReadonly))
1127 return VERR_NOT_SUPPORTED;
1128
1129 pdmacFileEpTaskInit(pTask, 0);
1130
1131 if (pEpFile->fCaching)
1132 {
1133 int rc = pdmacFileEpCacheFlush(pEpFile);
1134 AssertRC(rc);
1135 }
1136
1137 PPDMACTASKFILE pIoTask = pdmacFileTaskAlloc(pEpFile);
1138 if (RT_UNLIKELY(!pIoTask))
1139 return VERR_NO_MEMORY;
1140
1141 pIoTask->pEndpoint = pEpFile;
1142 pIoTask->enmTransferType = PDMACTASKFILETRANSFER_FLUSH;
1143 pIoTask->pvUser = pTaskFile;
1144 pIoTask->pfnCompleted = pdmacFileEpTaskCompleted;
1145 pdmacFileEpAddTask(pEpFile, pIoTask);
1146
1147 return VINF_AIO_TASK_PENDING;
1148}
1149
1150static int pdmacFileEpGetSize(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, uint64_t *pcbSize)
1151{
1152 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1153
1154 *pcbSize = ASMAtomicReadU64(&pEpFile->cbEndpoint);
1155
1156 return VINF_SUCCESS;
1157}
1158
1159static int pdmacFileEpSetSize(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, uint64_t cbSize)
1160{
1161 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1162
1163 ASMAtomicWriteU64(&pEpFile->cbEndpoint, cbSize);
1164 return RTFileSetSize(pEpFile->File, cbSize);
1165}
1166
1167const PDMASYNCCOMPLETIONEPCLASSOPS g_PDMAsyncCompletionEndpointClassFile =
1168{
1169 /* u32Version */
1170 PDMAC_EPCLASS_OPS_VERSION,
1171 /* pcszName */
1172 "File",
1173 /* enmClassType */
1174 PDMASYNCCOMPLETIONEPCLASSTYPE_FILE,
1175 /* cbEndpointClassGlobal */
1176 sizeof(PDMASYNCCOMPLETIONEPCLASSFILE),
1177 /* cbEndpoint */
1178 sizeof(PDMASYNCCOMPLETIONENDPOINTFILE),
1179 /* cbTask */
1180 sizeof(PDMASYNCCOMPLETIONTASKFILE),
1181 /* pfnInitialize */
1182 pdmacFileInitialize,
1183 /* pfnTerminate */
1184 pdmacFileTerminate,
1185 /* pfnEpInitialize. */
1186 pdmacFileEpInitialize,
1187 /* pfnEpClose */
1188 pdmacFileEpClose,
1189 /* pfnEpRead */
1190 pdmacFileEpRead,
1191 /* pfnEpWrite */
1192 pdmacFileEpWrite,
1193 /* pfnEpFlush */
1194 pdmacFileEpFlush,
1195 /* pfnEpGetSize */
1196 pdmacFileEpGetSize,
1197 /* pfnEpSetSize */
1198 pdmacFileEpSetSize,
1199 /* u32VersionEnd */
1200 PDMAC_EPCLASS_OPS_VERSION
1201};
1202
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