VirtualBox

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

Last change on this file since 32319 was 31182, checked in by vboxsync, 14 years ago

AsyncCompletion: Fix flags

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 40.9 KB
Line 
1/* $Id: PDMAsyncCompletionFile.cpp 31182 2010-07-28 18:37:43Z 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 | PDMACEP_FILE_FLAGS_DONT_LOCK)) == 0,
803 ("PDMAsyncCompletion: Invalid flag specified\n"), VERR_INVALID_PARAMETER);
804
805 unsigned fFileFlags = RTFILE_O_OPEN;
806
807 if (fFlags & PDMACEP_FILE_FLAGS_READ_ONLY)
808 fFileFlags |= RTFILE_O_READ | RTFILE_O_DENY_NONE;
809 else
810 {
811 fFileFlags |= RTFILE_O_READWRITE;
812
813 /*
814 * Opened in read/write mode. Check whether the caller wants to
815 * avoid the lock. Return an error in case caching is enabled
816 * because this can lead to data corruption.
817 */
818 if (fFlags & PDMACEP_FILE_FLAGS_DONT_LOCK)
819 {
820 if (fFlags & PDMACEP_FILE_FLAGS_CACHING)
821 return VERR_NOT_SUPPORTED;
822 else
823 fFileFlags |= RTFILE_O_DENY_NONE;
824 }
825 else
826 fFileFlags |= RTFILE_O_DENY_WRITE;
827 }
828
829 if (enmMgrType == PDMACEPFILEMGRTYPE_ASYNC)
830 fFileFlags |= RTFILE_O_ASYNC_IO;
831
832 if (enmEpBackend == PDMACFILEEPBACKEND_NON_BUFFERED)
833 {
834 /*
835 * We only disable the cache if the size of the file is a multiple of 512.
836 * Certain hosts like Windows, Linux and Solaris require that transfer sizes
837 * are aligned to the volume sector size.
838 * If not we just make sure that the data is written to disk with RTFILE_O_WRITE_THROUGH
839 * which will trash the host cache but ensures that the host cache will not
840 * contain dirty buffers.
841 */
842 RTFILE File = NIL_RTFILE;
843
844 rc = RTFileOpen(&File, pszUri, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
845 if (RT_SUCCESS(rc))
846 {
847 uint64_t cbSize;
848
849 rc = pdmacFileEpNativeGetSize(File, &cbSize);
850 Assert(RT_FAILURE(rc) || cbSize != 0);
851
852 if (RT_SUCCESS(rc) && ((cbSize % 512) == 0))
853 fFileFlags |= RTFILE_O_NO_CACHE;
854 else
855 {
856 /* Downgrade to the buffered backend */
857 enmEpBackend = PDMACFILEEPBACKEND_BUFFERED;
858
859#ifdef RT_OS_LINUX
860 fFileFlags &= ~RTFILE_O_ASYNC_IO;
861 enmMgrType = PDMACEPFILEMGRTYPE_SIMPLE;
862#endif
863 }
864 RTFileClose(File);
865 }
866 }
867
868 /* Open with final flags. */
869 rc = RTFileOpen(&pEpFile->File, pszUri, fFileFlags);
870 if ((rc == VERR_INVALID_FUNCTION) || (rc == VERR_INVALID_PARAMETER))
871 {
872 LogRel(("pdmacFileEpInitialize: RTFileOpen %s / %08x failed with %Rrc\n",
873 pszUri, fFileFlags, rc));
874 /*
875 * Solaris doesn't support directio on ZFS so far. :-\
876 * Trying to enable it returns VERR_INVALID_FUNCTION
877 * (ENOTTY). Remove it and hope for the best.
878 * ZFS supports write throttling in case applications
879 * write more data than can be synced to the disk
880 * without blocking the whole application.
881 *
882 * On Linux we have the same problem with cifs.
883 * Have to disable async I/O here too because it requires O_DIRECT.
884 */
885 fFileFlags &= ~RTFILE_O_NO_CACHE;
886 enmEpBackend = PDMACFILEEPBACKEND_BUFFERED;
887
888#ifdef RT_OS_LINUX
889 fFileFlags &= ~RTFILE_O_ASYNC_IO;
890 enmMgrType = PDMACEPFILEMGRTYPE_SIMPLE;
891#endif
892
893 /* Open again. */
894 rc = RTFileOpen(&pEpFile->File, pszUri, fFileFlags);
895
896 if (RT_FAILURE(rc))
897 {
898 LogRel(("pdmacFileEpInitialize: RTFileOpen %s / %08x failed AGAIN(!) with %Rrc\n",
899 pszUri, fFileFlags, rc));
900 }
901 }
902
903 if (RT_SUCCESS(rc))
904 {
905 pEpFile->fFlags = fFileFlags;
906
907 rc = pdmacFileEpNativeGetSize(pEpFile->File, (uint64_t *)&pEpFile->cbFile);
908 Assert(RT_FAILURE(rc) || pEpFile->cbFile != 0);
909
910 if (RT_SUCCESS(rc))
911 {
912 /* Initialize the segment cache */
913 rc = MMR3HeapAllocZEx(pEpClassFile->Core.pVM, MM_TAG_PDM_ASYNC_COMPLETION,
914 sizeof(PDMACTASKFILE),
915 (void **)&pEpFile->pTasksFreeHead);
916 if (RT_SUCCESS(rc))
917 {
918 PPDMACEPFILEMGR pAioMgr = NULL;
919
920 pEpFile->cbEndpoint = pEpFile->cbFile;
921 pEpFile->pTasksFreeTail = pEpFile->pTasksFreeHead;
922 pEpFile->cTasksCached = 0;
923 pEpFile->pBwMgr = pEpClassFile->pBwMgr;
924 pEpFile->enmBackendType = enmEpBackend;
925 /*
926 * Disable async flushes on Solaris for now.
927 * They cause weird hangs which needs more investigations.
928 */
929#ifndef RT_OS_SOLARIS
930 pEpFile->fAsyncFlushSupported = true;
931#else
932 pEpFile->fAsyncFlushSupported = false;
933#endif
934 pdmacFileBwRef(pEpFile->pBwMgr);
935
936 if (enmMgrType == PDMACEPFILEMGRTYPE_SIMPLE)
937 {
938 /* Simple mode. Every file has its own async I/O manager. */
939 rc = pdmacFileAioMgrCreate(pEpClassFile, &pAioMgr, PDMACEPFILEMGRTYPE_SIMPLE);
940 AssertRC(rc);
941 }
942 else
943 {
944 if ( (fFlags & PDMACEP_FILE_FLAGS_CACHING)
945 && (pEpClassFile->fCacheEnabled))
946 {
947 pEpFile->fCaching = true;
948 rc = pdmacFileEpCacheInit(pEpFile, pEpClassFile);
949 if (RT_FAILURE(rc))
950 {
951 LogRel(("AIOMgr: Endpoint for \"%s\" was opened with caching but initializing cache failed. Disabled caching\n", pszUri));
952 pEpFile->fCaching = false;
953 }
954 }
955
956 pAioMgr = pEpClassFile->pAioMgrHead;
957
958 /* Check for an idling manager of the same type */
959 while (pAioMgr)
960 {
961 if (pAioMgr->enmMgrType == enmMgrType)
962 break;
963 pAioMgr = pAioMgr->pNext;
964 }
965
966 if (!pAioMgr)
967 {
968 rc = pdmacFileAioMgrCreate(pEpClassFile, &pAioMgr, enmMgrType);
969 AssertRC(rc);
970 }
971 }
972
973 pEpFile->AioMgr.pTreeRangesLocked = (PAVLRFOFFTREE)RTMemAllocZ(sizeof(AVLRFOFFTREE));
974 if (!pEpFile->AioMgr.pTreeRangesLocked)
975 rc = VERR_NO_MEMORY;
976 else
977 {
978 pEpFile->enmState = PDMASYNCCOMPLETIONENDPOINTFILESTATE_ACTIVE;
979
980 /* Assign the endpoint to the thread. */
981 rc = pdmacFileAioMgrAddEndpoint(pAioMgr, pEpFile);
982 if (RT_FAILURE(rc))
983 {
984 RTMemFree(pEpFile->AioMgr.pTreeRangesLocked);
985 MMR3HeapFree(pEpFile->pTasksFreeHead);
986 pdmacFileBwUnref(pEpFile->pBwMgr);
987 }
988 }
989 }
990 }
991
992 if (RT_FAILURE(rc))
993 RTFileClose(pEpFile->File);
994 }
995
996#ifdef VBOX_WITH_STATISTICS
997 if (RT_SUCCESS(rc))
998 {
999 STAMR3RegisterF(pEpClassFile->Core.pVM, &pEpFile->StatRead,
1000 STAMTYPE_PROFILE_ADV, STAMVISIBILITY_ALWAYS,
1001 STAMUNIT_TICKS_PER_CALL, "Time taken to read from the endpoint",
1002 "/PDM/AsyncCompletion/File/%s/Read", RTPathFilename(pEpFile->Core.pszUri));
1003
1004 STAMR3RegisterF(pEpClassFile->Core.pVM, &pEpFile->StatWrite,
1005 STAMTYPE_PROFILE_ADV, STAMVISIBILITY_ALWAYS,
1006 STAMUNIT_TICKS_PER_CALL, "Time taken to write to the endpoint",
1007 "/PDM/AsyncCompletion/File/%s/Write", RTPathFilename(pEpFile->Core.pszUri));
1008 }
1009#endif
1010
1011 if (RT_SUCCESS(rc))
1012 LogRel(("AIOMgr: Endpoint for file '%s' (flags %08x) created successfully\n", pszUri, pEpFile->fFlags));
1013
1014 return rc;
1015}
1016
1017static int pdmacFileEpRangesLockedDestroy(PAVLRFOFFNODECORE pNode, void *pvUser)
1018{
1019 AssertMsgFailed(("The locked ranges tree should be empty at that point\n"));
1020 return VINF_SUCCESS;
1021}
1022
1023static int pdmacFileEpClose(PPDMASYNCCOMPLETIONENDPOINT pEndpoint)
1024{
1025 int rc = VINF_SUCCESS;
1026 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1027 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pEndpoint->pEpClass;
1028
1029 /* Free the cached data. */
1030 if (pEpFile->fCaching)
1031 {
1032 rc = pdmacFileEpCacheFlush(pEpFile);
1033 AssertRC(rc);
1034 }
1035
1036 /* Make sure that all tasks finished for this endpoint. */
1037 rc = pdmacFileAioMgrCloseEndpoint(pEpFile->pAioMgr, pEpFile);
1038 AssertRC(rc);
1039
1040 /* endpoint and real file size should better be equal now. */
1041 AssertMsg(pEpFile->cbFile == pEpFile->cbEndpoint,
1042 ("Endpoint and real file size should match now!\n"));
1043
1044 /* Destroy any per endpoint cache data */
1045 if (pEpFile->fCaching)
1046 pdmacFileEpCacheDestroy(pEpFile);
1047
1048 /*
1049 * If the async I/O manager is in failsafe mode this is the only endpoint
1050 * he processes and thus can be destroyed now.
1051 */
1052 if (pEpFile->pAioMgr->enmMgrType == PDMACEPFILEMGRTYPE_SIMPLE)
1053 pdmacFileAioMgrDestroy(pEpClassFile, pEpFile->pAioMgr);
1054
1055 /* Free cached tasks. */
1056 PPDMACTASKFILE pTask = pEpFile->pTasksFreeHead;
1057
1058 while (pTask)
1059 {
1060 PPDMACTASKFILE pTaskFree = pTask;
1061 pTask = pTask->pNext;
1062 MMR3HeapFree(pTaskFree);
1063 }
1064
1065 /* Remove from the bandwidth manager */
1066 pdmacFileBwUnref(pEpFile->pBwMgr);
1067
1068 /* Destroy the locked ranges tree now. */
1069 RTAvlrFileOffsetDestroy(pEpFile->AioMgr.pTreeRangesLocked, pdmacFileEpRangesLockedDestroy, NULL);
1070
1071 RTFileClose(pEpFile->File);
1072
1073#ifdef VBOX_WITH_STATISTICS
1074 STAMR3Deregister(pEpClassFile->Core.pVM, &pEpFile->StatRead);
1075 STAMR3Deregister(pEpClassFile->Core.pVM, &pEpFile->StatWrite);
1076#endif
1077
1078 return VINF_SUCCESS;
1079}
1080
1081static int pdmacFileEpRead(PPDMASYNCCOMPLETIONTASK pTask,
1082 PPDMASYNCCOMPLETIONENDPOINT pEndpoint, RTFOFF off,
1083 PCRTSGSEG paSegments, size_t cSegments,
1084 size_t cbRead)
1085{
1086 int rc = VINF_SUCCESS;
1087 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1088
1089 LogFlowFunc(("pTask=%#p pEndpoint=%#p off=%RTfoff paSegments=%#p cSegments=%zu cbRead=%zu\n",
1090 pTask, pEndpoint, off, paSegments, cSegments, cbRead));
1091
1092 STAM_PROFILE_ADV_START(&pEpFile->StatRead, Read);
1093
1094 pdmacFileEpTaskInit(pTask, cbRead);
1095
1096 if (pEpFile->fCaching)
1097 rc = pdmacFileEpCacheRead(pEpFile, (PPDMASYNCCOMPLETIONTASKFILE)pTask,
1098 off, paSegments, cSegments, cbRead);
1099 else
1100 rc = pdmacFileEpTaskInitiate(pTask, pEndpoint, off, paSegments, cSegments, cbRead,
1101 PDMACTASKFILETRANSFER_READ);
1102
1103 STAM_PROFILE_ADV_STOP(&pEpFile->StatRead, Read);
1104
1105 return rc;
1106}
1107
1108static int pdmacFileEpWrite(PPDMASYNCCOMPLETIONTASK pTask,
1109 PPDMASYNCCOMPLETIONENDPOINT pEndpoint, RTFOFF off,
1110 PCRTSGSEG paSegments, size_t cSegments,
1111 size_t cbWrite)
1112{
1113 int rc = VINF_SUCCESS;
1114 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1115
1116 if (RT_UNLIKELY(pEpFile->fReadonly))
1117 return VERR_NOT_SUPPORTED;
1118
1119 STAM_PROFILE_ADV_START(&pEpFile->StatWrite, Write);
1120
1121 pdmacFileEpTaskInit(pTask, cbWrite);
1122
1123 if (pEpFile->fCaching)
1124 rc = pdmacFileEpCacheWrite(pEpFile, (PPDMASYNCCOMPLETIONTASKFILE)pTask,
1125 off, paSegments, cSegments, cbWrite);
1126 else
1127 rc = pdmacFileEpTaskInitiate(pTask, pEndpoint, off, paSegments, cSegments, cbWrite,
1128 PDMACTASKFILETRANSFER_WRITE);
1129
1130 STAM_PROFILE_ADV_STOP(&pEpFile->StatWrite, Write);
1131
1132 /* Increase endpoint size. */
1133 if ( RT_SUCCESS(rc)
1134 && ((uint64_t)off + cbWrite) > pEpFile->cbEndpoint)
1135 ASMAtomicWriteU64(&pEpFile->cbEndpoint, (uint64_t)off + cbWrite);
1136
1137 return rc;
1138}
1139
1140static int pdmacFileEpFlush(PPDMASYNCCOMPLETIONTASK pTask,
1141 PPDMASYNCCOMPLETIONENDPOINT pEndpoint)
1142{
1143 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1144 PPDMASYNCCOMPLETIONTASKFILE pTaskFile = (PPDMASYNCCOMPLETIONTASKFILE)pTask;
1145
1146 if (RT_UNLIKELY(pEpFile->fReadonly))
1147 return VERR_NOT_SUPPORTED;
1148
1149 pdmacFileEpTaskInit(pTask, 0);
1150
1151 if (pEpFile->fCaching)
1152 {
1153 int rc = pdmacFileEpCacheFlush(pEpFile);
1154 AssertRC(rc);
1155 }
1156
1157 PPDMACTASKFILE pIoTask = pdmacFileTaskAlloc(pEpFile);
1158 if (RT_UNLIKELY(!pIoTask))
1159 return VERR_NO_MEMORY;
1160
1161 pIoTask->pEndpoint = pEpFile;
1162 pIoTask->enmTransferType = PDMACTASKFILETRANSFER_FLUSH;
1163 pIoTask->pvUser = pTaskFile;
1164 pIoTask->pfnCompleted = pdmacFileEpTaskCompleted;
1165 pdmacFileEpAddTask(pEpFile, pIoTask);
1166
1167 return VINF_AIO_TASK_PENDING;
1168}
1169
1170static int pdmacFileEpGetSize(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, uint64_t *pcbSize)
1171{
1172 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1173
1174 *pcbSize = ASMAtomicReadU64(&pEpFile->cbEndpoint);
1175
1176 return VINF_SUCCESS;
1177}
1178
1179static int pdmacFileEpSetSize(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, uint64_t cbSize)
1180{
1181 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1182
1183 ASMAtomicWriteU64(&pEpFile->cbEndpoint, cbSize);
1184 return RTFileSetSize(pEpFile->File, cbSize);
1185}
1186
1187const PDMASYNCCOMPLETIONEPCLASSOPS g_PDMAsyncCompletionEndpointClassFile =
1188{
1189 /* u32Version */
1190 PDMAC_EPCLASS_OPS_VERSION,
1191 /* pcszName */
1192 "File",
1193 /* enmClassType */
1194 PDMASYNCCOMPLETIONEPCLASSTYPE_FILE,
1195 /* cbEndpointClassGlobal */
1196 sizeof(PDMASYNCCOMPLETIONEPCLASSFILE),
1197 /* cbEndpoint */
1198 sizeof(PDMASYNCCOMPLETIONENDPOINTFILE),
1199 /* cbTask */
1200 sizeof(PDMASYNCCOMPLETIONTASKFILE),
1201 /* pfnInitialize */
1202 pdmacFileInitialize,
1203 /* pfnTerminate */
1204 pdmacFileTerminate,
1205 /* pfnEpInitialize. */
1206 pdmacFileEpInitialize,
1207 /* pfnEpClose */
1208 pdmacFileEpClose,
1209 /* pfnEpRead */
1210 pdmacFileEpRead,
1211 /* pfnEpWrite */
1212 pdmacFileEpWrite,
1213 /* pfnEpFlush */
1214 pdmacFileEpFlush,
1215 /* pfnEpGetSize */
1216 pdmacFileEpGetSize,
1217 /* pfnEpSetSize */
1218 pdmacFileEpSetSize,
1219 /* u32VersionEnd */
1220 PDMAC_EPCLASS_OPS_VERSION
1221};
1222
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