VirtualBox

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

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

AsyncCompletion: Debug build only debugger command to inject errors into the I/O subsystem

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 41.2 KB
Line 
1/* $Id: PDMAsyncCompletionFile.cpp 34344 2010-11-24 21:39:23Z 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#ifdef DEBUG
286 else
287 {
288 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pTaskFile->Core.pEndpoint;
289
290 /* Overwrite with injected error code. */
291 if (pTask->enmTransferType == PDMACTASKFILETRANSFER_READ)
292 rc = ASMAtomicXchgS32(&pEpFile->rcReqRead, VINF_SUCCESS);
293 else
294 rc = ASMAtomicXchgS32(&pEpFile->rcReqWrite, VINF_SUCCESS);
295
296 if (RT_FAILURE(rc))
297 ASMAtomicCmpXchgS32(&pTaskFile->rc, rc, VINF_SUCCESS);
298 }
299#endif
300
301 if (!(uOld - pTask->DataSeg.cbSeg)
302 && !ASMAtomicXchgBool(&pTaskFile->fCompleted, true))
303 pdmR3AsyncCompletionCompleteTask(&pTaskFile->Core, pTaskFile->rc, true);
304 }
305}
306
307DECLINLINE(void) pdmacFileEpTaskInit(PPDMASYNCCOMPLETIONTASK pTask, size_t cbTransfer)
308{
309 PPDMASYNCCOMPLETIONTASKFILE pTaskFile = (PPDMASYNCCOMPLETIONTASKFILE)pTask;
310
311 Assert((uint32_t)cbTransfer == cbTransfer && (int32_t)cbTransfer >= 0);
312 ASMAtomicWriteS32(&pTaskFile->cbTransferLeft, (int32_t)cbTransfer);
313 ASMAtomicWriteBool(&pTaskFile->fCompleted, false);
314 ASMAtomicWriteS32(&pTaskFile->rc, VINF_SUCCESS);
315}
316
317int pdmacFileEpTaskInitiate(PPDMASYNCCOMPLETIONTASK pTask,
318 PPDMASYNCCOMPLETIONENDPOINT pEndpoint, RTFOFF off,
319 PCRTSGSEG paSegments, size_t cSegments,
320 size_t cbTransfer, PDMACTASKFILETRANSFER enmTransfer)
321{
322 int rc = VINF_SUCCESS;
323 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
324 PPDMASYNCCOMPLETIONTASKFILE pTaskFile = (PPDMASYNCCOMPLETIONTASKFILE)pTask;
325 PPDMACEPFILEMGR pAioMgr = pEpFile->pAioMgr;
326
327 Assert( (enmTransfer == PDMACTASKFILETRANSFER_READ)
328 || (enmTransfer == PDMACTASKFILETRANSFER_WRITE));
329
330 for (unsigned i = 0; i < cSegments; i++)
331 {
332 PPDMACTASKFILE pIoTask = pdmacFileTaskAlloc(pEpFile);
333 AssertPtr(pIoTask);
334
335 pIoTask->pEndpoint = pEpFile;
336 pIoTask->enmTransferType = enmTransfer;
337 pIoTask->Off = off;
338 pIoTask->DataSeg.cbSeg = paSegments[i].cbSeg;
339 pIoTask->DataSeg.pvSeg = paSegments[i].pvSeg;
340 pIoTask->pvUser = pTaskFile;
341 pIoTask->pfnCompleted = pdmacFileEpTaskCompleted;
342
343 /* Send it off to the I/O manager. */
344 pdmacFileEpAddTask(pEpFile, pIoTask);
345 off += paSegments[i].cbSeg;
346 cbTransfer -= paSegments[i].cbSeg;
347 }
348
349 AssertMsg(!cbTransfer, ("Incomplete transfer %u bytes left\n", cbTransfer));
350
351 if (ASMAtomicReadS32(&pTaskFile->cbTransferLeft) == 0
352 && !ASMAtomicXchgBool(&pTaskFile->fCompleted, true))
353 pdmR3AsyncCompletionCompleteTask(pTask, pTaskFile->rc, false);
354 else
355 rc = VINF_AIO_TASK_PENDING;
356
357 return rc;
358}
359
360/**
361 * Creates a new async I/O manager.
362 *
363 * @returns VBox status code.
364 * @param pEpClass Pointer to the endpoint class data.
365 * @param ppAioMgr Where to store the pointer to the new async I/O manager on success.
366 * @param enmMgrType Wanted manager type - can be overwritten by the global override.
367 */
368int pdmacFileAioMgrCreate(PPDMASYNCCOMPLETIONEPCLASSFILE pEpClass, PPPDMACEPFILEMGR ppAioMgr,
369 PDMACEPFILEMGRTYPE enmMgrType)
370{
371 int rc = VINF_SUCCESS;
372 PPDMACEPFILEMGR pAioMgrNew;
373
374 LogFlowFunc((": Entered\n"));
375
376 rc = MMR3HeapAllocZEx(pEpClass->Core.pVM, MM_TAG_PDM_ASYNC_COMPLETION, sizeof(PDMACEPFILEMGR), (void **)&pAioMgrNew);
377 if (RT_SUCCESS(rc))
378 {
379 if (enmMgrType < pEpClass->enmMgrTypeOverride)
380 pAioMgrNew->enmMgrType = enmMgrType;
381 else
382 pAioMgrNew->enmMgrType = pEpClass->enmMgrTypeOverride;
383
384 rc = RTSemEventCreate(&pAioMgrNew->EventSem);
385 if (RT_SUCCESS(rc))
386 {
387 rc = RTSemEventCreate(&pAioMgrNew->EventSemBlock);
388 if (RT_SUCCESS(rc))
389 {
390 rc = RTCritSectInit(&pAioMgrNew->CritSectBlockingEvent);
391 if (RT_SUCCESS(rc))
392 {
393 /* Init the rest of the manager. */
394 if (pAioMgrNew->enmMgrType != PDMACEPFILEMGRTYPE_SIMPLE)
395 rc = pdmacFileAioMgrNormalInit(pAioMgrNew);
396
397 if (RT_SUCCESS(rc))
398 {
399 pAioMgrNew->enmState = PDMACEPFILEMGRSTATE_RUNNING;
400
401 rc = RTThreadCreateF(&pAioMgrNew->Thread,
402 pAioMgrNew->enmMgrType == PDMACEPFILEMGRTYPE_SIMPLE
403 ? pdmacFileAioMgrFailsafe
404 : pdmacFileAioMgrNormal,
405 pAioMgrNew,
406 0,
407 RTTHREADTYPE_IO,
408 0,
409 "AioMgr%d-%s", pEpClass->cAioMgrs,
410 pAioMgrNew->enmMgrType == PDMACEPFILEMGRTYPE_SIMPLE
411 ? "F"
412 : "N");
413 if (RT_SUCCESS(rc))
414 {
415 /* Link it into the list. */
416 RTCritSectEnter(&pEpClass->CritSect);
417 pAioMgrNew->pNext = pEpClass->pAioMgrHead;
418 if (pEpClass->pAioMgrHead)
419 pEpClass->pAioMgrHead->pPrev = pAioMgrNew;
420 pEpClass->pAioMgrHead = pAioMgrNew;
421 pEpClass->cAioMgrs++;
422 RTCritSectLeave(&pEpClass->CritSect);
423
424 *ppAioMgr = pAioMgrNew;
425
426 Log(("PDMAC: Successfully created new file AIO Mgr {%s}\n", RTThreadGetName(pAioMgrNew->Thread)));
427 return VINF_SUCCESS;
428 }
429 pdmacFileAioMgrNormalDestroy(pAioMgrNew);
430 }
431 RTCritSectDelete(&pAioMgrNew->CritSectBlockingEvent);
432 }
433 RTSemEventDestroy(pAioMgrNew->EventSem);
434 }
435 RTSemEventDestroy(pAioMgrNew->EventSemBlock);
436 }
437 MMR3HeapFree(pAioMgrNew);
438 }
439
440 LogFlowFunc((": Leave rc=%Rrc\n", rc));
441
442 return rc;
443}
444
445/**
446 * Destroys a async I/O manager.
447 *
448 * @returns nothing.
449 * @param pAioMgr The async I/O manager to destroy.
450 */
451static void pdmacFileAioMgrDestroy(PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile, PPDMACEPFILEMGR pAioMgr)
452{
453 int rc = pdmacFileAioMgrShutdown(pAioMgr);
454 AssertRC(rc);
455
456 /* Unlink from the list. */
457 rc = RTCritSectEnter(&pEpClassFile->CritSect);
458 AssertRC(rc);
459
460 PPDMACEPFILEMGR pPrev = pAioMgr->pPrev;
461 PPDMACEPFILEMGR pNext = pAioMgr->pNext;
462
463 if (pPrev)
464 pPrev->pNext = pNext;
465 else
466 pEpClassFile->pAioMgrHead = pNext;
467
468 if (pNext)
469 pNext->pPrev = pPrev;
470
471 pEpClassFile->cAioMgrs--;
472 rc = RTCritSectLeave(&pEpClassFile->CritSect);
473 AssertRC(rc);
474
475 /* Free the resources. */
476 RTCritSectDelete(&pAioMgr->CritSectBlockingEvent);
477 RTSemEventDestroy(pAioMgr->EventSem);
478 if (pAioMgr->enmMgrType != PDMACEPFILEMGRTYPE_SIMPLE)
479 pdmacFileAioMgrNormalDestroy(pAioMgr);
480
481 MMR3HeapFree(pAioMgr);
482}
483
484static int pdmacFileMgrTypeFromName(const char *pszVal, PPDMACEPFILEMGRTYPE penmMgrType)
485{
486 int rc = VINF_SUCCESS;
487
488 if (!RTStrCmp(pszVal, "Simple"))
489 *penmMgrType = PDMACEPFILEMGRTYPE_SIMPLE;
490 else if (!RTStrCmp(pszVal, "Async"))
491 *penmMgrType = PDMACEPFILEMGRTYPE_ASYNC;
492 else
493 rc = VERR_CFGM_CONFIG_UNKNOWN_VALUE;
494
495 return rc;
496}
497
498static const char *pdmacFileMgrTypeToName(PDMACEPFILEMGRTYPE enmMgrType)
499{
500 if (enmMgrType == PDMACEPFILEMGRTYPE_SIMPLE)
501 return "Simple";
502 if (enmMgrType == PDMACEPFILEMGRTYPE_ASYNC)
503 return "Async";
504
505 return NULL;
506}
507
508static int pdmacFileBackendTypeFromName(const char *pszVal, PPDMACFILEEPBACKEND penmBackendType)
509{
510 int rc = VINF_SUCCESS;
511
512 if (!RTStrCmp(pszVal, "Buffered"))
513 *penmBackendType = PDMACFILEEPBACKEND_BUFFERED;
514 else if (!RTStrCmp(pszVal, "NonBuffered"))
515 *penmBackendType = PDMACFILEEPBACKEND_NON_BUFFERED;
516 else
517 rc = VERR_CFGM_CONFIG_UNKNOWN_VALUE;
518
519 return rc;
520}
521
522static const char *pdmacFileBackendTypeToName(PDMACFILEEPBACKEND enmBackendType)
523{
524 if (enmBackendType == PDMACFILEEPBACKEND_BUFFERED)
525 return "Buffered";
526 if (enmBackendType == PDMACFILEEPBACKEND_NON_BUFFERED)
527 return "NonBuffered";
528
529 return NULL;
530}
531
532/**
533 * Get the size of the given file.
534 * Works for block devices too.
535 *
536 * @returns VBox status code.
537 * @param hFile The file handle.
538 * @param pcbSize Where to store the size of the file on success.
539 */
540static int pdmacFileEpNativeGetSize(RTFILE hFile, uint64_t *pcbSize)
541{
542 int rc = VINF_SUCCESS;
543 uint64_t cbSize = 0;
544
545 rc = RTFileGetSize(hFile, &cbSize);
546 if (RT_SUCCESS(rc) && (cbSize != 0))
547 *pcbSize = cbSize;
548 else
549 {
550#ifdef RT_OS_WINDOWS
551 DISK_GEOMETRY DriveGeo;
552 DWORD cbDriveGeo;
553 if (DeviceIoControl((HANDLE)hFile,
554 IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
555 &DriveGeo, sizeof(DriveGeo), &cbDriveGeo, NULL))
556 {
557 if ( DriveGeo.MediaType == FixedMedia
558 || DriveGeo.MediaType == RemovableMedia)
559 {
560 cbSize = DriveGeo.Cylinders.QuadPart
561 * DriveGeo.TracksPerCylinder
562 * DriveGeo.SectorsPerTrack
563 * DriveGeo.BytesPerSector;
564
565 GET_LENGTH_INFORMATION DiskLenInfo;
566 DWORD junk;
567 if (DeviceIoControl((HANDLE)hFile,
568 IOCTL_DISK_GET_LENGTH_INFO, NULL, 0,
569 &DiskLenInfo, sizeof(DiskLenInfo), &junk, (LPOVERLAPPED)NULL))
570 {
571 /* IOCTL_DISK_GET_LENGTH_INFO is supported -- override cbSize. */
572 cbSize = DiskLenInfo.Length.QuadPart;
573 }
574
575 rc = VINF_SUCCESS;
576 }
577 else
578 {
579 rc = VERR_INVALID_PARAMETER;
580 }
581 }
582 else
583 {
584 rc = RTErrConvertFromWin32(GetLastError());
585 }
586#else
587 /* Could be a block device */
588 rc = RTFileSeek(hFile, 0, RTFILE_SEEK_END, &cbSize);
589
590#endif
591 if (RT_SUCCESS(rc) && (cbSize != 0))
592 *pcbSize = cbSize;
593 else if (RT_SUCCESS(rc))
594 rc = VERR_NOT_SUPPORTED;
595 }
596
597 return rc;
598}
599
600#ifdef DEBUG
601/**
602 * Error inject callback.
603 *
604 * The argument parsing is quite ugly but it is only a development tool
605 * not compiled into a release build. Will improve some day.
606 */
607static DECLCALLBACK(void) pdmacEpFileErrorInject(void *pvUser, PCDBGFINFOHLP pHlp, const char *pszArgs)
608{
609 bool fWrite;
610 int rcToInject = VINF_SUCCESS;
611 char *pszFilename = NULL;
612 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pvUser;
613
614 /* Syntax is "read|write <filename> <status code>" */
615 if (!RTStrNCmp(pszArgs, "read", 4))
616 fWrite = false;
617 else if (!RTStrNCmp(pszArgs, "write", 5))
618 fWrite = true;
619 else
620 return;
621
622 pszArgs += fWrite ? 5 : 4;
623
624 /* Skip white space. */
625 while ( *pszArgs == ' '
626 && *pszArgs != '\0')
627 pszArgs++;
628
629 if (pszArgs != '\0')
630 {
631 /* Extract the filename */
632 const char *pszPos = pszArgs;
633
634 /* ASSUMPTION: No white space in the filename. */
635 while ( *pszArgs != ' '
636 && *pszArgs != '\0')
637 pszArgs++;
638
639 if (*pszArgs != '\0')
640 {
641 size_t cchFilename = pszArgs - pszPos;
642 pszFilename = RTStrDupN(pszPos, cchFilename);
643 if (pszFilename)
644 {
645 /* Skip white space. */
646 while ( *pszArgs == ' '
647 && *pszArgs != '\0')
648 pszArgs++;
649
650 if (*pszArgs != '\0')
651 {
652 /* Extract error number. */
653 rcToInject = RTStrToInt32(pszArgs);
654 if (rcToInject != 0)
655 {
656 /* Search for the matching endpoint. */
657 RTCritSectEnter(&pEpClassFile->Core.CritSect);
658 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEpClassFile->Core.pEndpointsHead;
659
660 while (pEpFile)
661 {
662 if (!RTStrCmp(pszFilename, RTPathFilename(pEpFile->Core.pszUri)))
663 break;
664 pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEpFile->Core.pNext;
665 }
666
667 if (pEpFile)
668 {
669 if (fWrite)
670 ASMAtomicXchgS32(&pEpFile->rcReqWrite, rcToInject);
671 else
672 ASMAtomicXchgS32(&pEpFile->rcReqRead, rcToInject);
673
674 pHlp->pfnPrintf(pHlp, "Injected %Rrc into '%s' for %s\n",
675 rcToInject, pszFilename, fWrite ? "write" : "read");
676 }
677 else
678 pHlp->pfnPrintf(pHlp, "No file with name '%s' found\n", pszFilename);
679
680 RTCritSectLeave(&pEpClassFile->Core.CritSect);
681 RTStrFree(pszFilename);
682 return;
683 }
684 }
685 RTStrFree(pszFilename);
686 }
687 }
688 }
689 pHlp->pfnPrintf(pHlp, "Error parsing command line\n");
690}
691#endif
692
693static int pdmacFileInitialize(PPDMASYNCCOMPLETIONEPCLASS pClassGlobals, PCFGMNODE pCfgNode)
694{
695 int rc = VINF_SUCCESS;
696 RTFILEAIOLIMITS AioLimits; /** < Async I/O limitations. */
697
698 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pClassGlobals;
699
700 rc = RTFileAioGetLimits(&AioLimits);
701#ifdef DEBUG
702 if (RT_SUCCESS(rc) && RTEnvExist("VBOX_ASYNC_IO_FAILBACK"))
703 rc = VERR_ENV_VAR_NOT_FOUND;
704#endif
705 if (RT_FAILURE(rc))
706 {
707 LogRel(("AIO: Async I/O manager not supported (rc=%Rrc). Falling back to simple manager\n",
708 rc));
709 pEpClassFile->enmMgrTypeOverride = PDMACEPFILEMGRTYPE_SIMPLE;
710 pEpClassFile->enmEpBackendDefault = PDMACFILEEPBACKEND_BUFFERED;
711 }
712 else
713 {
714 pEpClassFile->uBitmaskAlignment = AioLimits.cbBufferAlignment ? ~((RTR3UINTPTR)AioLimits.cbBufferAlignment - 1) : RTR3UINTPTR_MAX;
715 pEpClassFile->cReqsOutstandingMax = AioLimits.cReqsOutstandingMax;
716
717 if (pCfgNode)
718 {
719 /* Query the default manager type */
720 char *pszVal = NULL;
721 rc = CFGMR3QueryStringAllocDef(pCfgNode, "IoMgr", &pszVal, "Async");
722 AssertLogRelRCReturn(rc, rc);
723
724 rc = pdmacFileMgrTypeFromName(pszVal, &pEpClassFile->enmMgrTypeOverride);
725 MMR3HeapFree(pszVal);
726 if (RT_FAILURE(rc))
727 return rc;
728
729 LogRel(("AIOMgr: Default manager type is \"%s\"\n", pdmacFileMgrTypeToName(pEpClassFile->enmMgrTypeOverride)));
730
731 /* Query default backend type */
732 rc = CFGMR3QueryStringAllocDef(pCfgNode, "FileBackend", &pszVal, "NonBuffered");
733 AssertLogRelRCReturn(rc, rc);
734
735 rc = pdmacFileBackendTypeFromName(pszVal, &pEpClassFile->enmEpBackendDefault);
736 MMR3HeapFree(pszVal);
737 if (RT_FAILURE(rc))
738 return rc;
739
740 LogRel(("AIOMgr: Default file backend is \"%s\"\n", pdmacFileBackendTypeToName(pEpClassFile->enmEpBackendDefault)));
741
742#ifdef RT_OS_LINUX
743 if ( pEpClassFile->enmMgrTypeOverride == PDMACEPFILEMGRTYPE_ASYNC
744 && pEpClassFile->enmEpBackendDefault == PDMACFILEEPBACKEND_BUFFERED)
745 {
746 LogRel(("AIOMgr: Linux does not support buffered async I/O, changing to non buffered\n"));
747 pEpClassFile->enmEpBackendDefault = PDMACFILEEPBACKEND_NON_BUFFERED;
748 }
749#endif
750 }
751 else
752 {
753 /* No configuration supplied, set defaults */
754 pEpClassFile->enmEpBackendDefault = PDMACFILEEPBACKEND_NON_BUFFERED;
755 pEpClassFile->enmMgrTypeOverride = PDMACEPFILEMGRTYPE_ASYNC;
756 }
757 }
758
759 /* Init critical section. */
760 rc = RTCritSectInit(&pEpClassFile->CritSect);
761 if (RT_SUCCESS(rc))
762 {
763 /* Check if the cache was disabled by the user. */
764 rc = CFGMR3QueryBoolDef(pCfgNode, "CacheEnabled", &pEpClassFile->fCacheEnabled, true);
765 AssertLogRelRCReturn(rc, rc);
766
767 if (pEpClassFile->fCacheEnabled)
768 {
769 /* Init cache structure */
770 rc = pdmacFileCacheInit(pEpClassFile, pCfgNode);
771 if (RT_FAILURE(rc))
772 {
773 pEpClassFile->fCacheEnabled = false;
774 LogRel(("AIOMgr: Failed to initialise the cache (rc=%Rrc), disabled caching\n"));
775 }
776 }
777 else
778 LogRel(("AIOMgr: Cache was globally disabled\n"));
779 }
780
781#ifdef DEBUG
782 /* Install the error injection handler. */
783 if (RT_SUCCESS(rc))
784 {
785 rc = DBGFR3InfoRegisterExternal(pClassGlobals->pVM, "injecterror",
786 "Inject an error into the async file I/O handling",
787 pdmacEpFileErrorInject, pEpClassFile);
788 AssertRC(rc);
789 }
790#endif
791
792 return rc;
793}
794
795static void pdmacFileTerminate(PPDMASYNCCOMPLETIONEPCLASS pClassGlobals)
796{
797 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pClassGlobals;
798
799 /* All endpoints should be closed at this point. */
800 AssertMsg(!pEpClassFile->Core.pEndpointsHead, ("There are still endpoints left\n"));
801
802 /* Destroy all left async I/O managers. */
803 while (pEpClassFile->pAioMgrHead)
804 pdmacFileAioMgrDestroy(pEpClassFile, pEpClassFile->pAioMgrHead);
805
806 /* Destroy the cache. */
807 if (pEpClassFile->fCacheEnabled)
808 pdmacFileCacheDestroy(pEpClassFile);
809
810 RTCritSectDelete(&pEpClassFile->CritSect);
811}
812
813static int pdmacFileEpInitialize(PPDMASYNCCOMPLETIONENDPOINT pEndpoint,
814 const char *pszUri, uint32_t fFlags)
815{
816 int rc = VINF_SUCCESS;
817 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
818 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pEndpoint->pEpClass;
819 PDMACEPFILEMGRTYPE enmMgrType = pEpClassFile->enmMgrTypeOverride;
820 PDMACFILEEPBACKEND enmEpBackend = pEpClassFile->enmEpBackendDefault;
821
822 AssertMsgReturn((fFlags & ~(PDMACEP_FILE_FLAGS_READ_ONLY | PDMACEP_FILE_FLAGS_CACHING | PDMACEP_FILE_FLAGS_DONT_LOCK)) == 0,
823 ("PDMAsyncCompletion: Invalid flag specified\n"), VERR_INVALID_PARAMETER);
824
825 unsigned fFileFlags = RTFILE_O_OPEN;
826
827 if (fFlags & PDMACEP_FILE_FLAGS_READ_ONLY)
828 fFileFlags |= RTFILE_O_READ | RTFILE_O_DENY_NONE;
829 else
830 {
831 fFileFlags |= RTFILE_O_READWRITE;
832
833 /*
834 * Opened in read/write mode. Check whether the caller wants to
835 * avoid the lock. Return an error in case caching is enabled
836 * because this can lead to data corruption.
837 */
838 if (fFlags & PDMACEP_FILE_FLAGS_DONT_LOCK)
839 {
840 if (fFlags & PDMACEP_FILE_FLAGS_CACHING)
841 return VERR_NOT_SUPPORTED;
842 else
843 fFileFlags |= RTFILE_O_DENY_NONE;
844 }
845 else
846 fFileFlags |= RTFILE_O_DENY_WRITE;
847 }
848
849 if (enmMgrType == PDMACEPFILEMGRTYPE_ASYNC)
850 fFileFlags |= RTFILE_O_ASYNC_IO;
851
852 if (enmEpBackend == PDMACFILEEPBACKEND_NON_BUFFERED)
853 {
854 /*
855 * We only disable the cache if the size of the file is a multiple of 512.
856 * Certain hosts like Windows, Linux and Solaris require that transfer sizes
857 * are aligned to the volume sector size.
858 * If not we just make sure that the data is written to disk with RTFILE_O_WRITE_THROUGH
859 * which will trash the host cache but ensures that the host cache will not
860 * contain dirty buffers.
861 */
862 RTFILE File = NIL_RTFILE;
863
864 rc = RTFileOpen(&File, pszUri, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
865 if (RT_SUCCESS(rc))
866 {
867 uint64_t cbSize;
868
869 rc = pdmacFileEpNativeGetSize(File, &cbSize);
870 Assert(RT_FAILURE(rc) || cbSize != 0);
871
872 if (RT_SUCCESS(rc) && ((cbSize % 512) == 0))
873 fFileFlags |= RTFILE_O_NO_CACHE;
874 else
875 {
876 /* Downgrade to the buffered backend */
877 enmEpBackend = PDMACFILEEPBACKEND_BUFFERED;
878
879#ifdef RT_OS_LINUX
880 fFileFlags &= ~RTFILE_O_ASYNC_IO;
881 enmMgrType = PDMACEPFILEMGRTYPE_SIMPLE;
882#endif
883 }
884 RTFileClose(File);
885 }
886 }
887
888 /* Open with final flags. */
889 rc = RTFileOpen(&pEpFile->File, pszUri, fFileFlags);
890 if ((rc == VERR_INVALID_FUNCTION) || (rc == VERR_INVALID_PARAMETER))
891 {
892 LogRel(("pdmacFileEpInitialize: RTFileOpen %s / %08x failed with %Rrc\n",
893 pszUri, fFileFlags, rc));
894 /*
895 * Solaris doesn't support directio on ZFS so far. :-\
896 * Trying to enable it returns VERR_INVALID_FUNCTION
897 * (ENOTTY). Remove it and hope for the best.
898 * ZFS supports write throttling in case applications
899 * write more data than can be synced to the disk
900 * without blocking the whole application.
901 *
902 * On Linux we have the same problem with cifs.
903 * Have to disable async I/O here too because it requires O_DIRECT.
904 */
905 fFileFlags &= ~RTFILE_O_NO_CACHE;
906 enmEpBackend = PDMACFILEEPBACKEND_BUFFERED;
907
908#ifdef RT_OS_LINUX
909 fFileFlags &= ~RTFILE_O_ASYNC_IO;
910 enmMgrType = PDMACEPFILEMGRTYPE_SIMPLE;
911#endif
912
913 /* Open again. */
914 rc = RTFileOpen(&pEpFile->File, pszUri, fFileFlags);
915
916 if (RT_FAILURE(rc))
917 {
918 LogRel(("pdmacFileEpInitialize: RTFileOpen %s / %08x failed AGAIN(!) with %Rrc\n",
919 pszUri, fFileFlags, rc));
920 }
921 }
922
923 if (RT_SUCCESS(rc))
924 {
925 pEpFile->fFlags = fFileFlags;
926
927 rc = pdmacFileEpNativeGetSize(pEpFile->File, (uint64_t *)&pEpFile->cbFile);
928 Assert(RT_FAILURE(rc) || pEpFile->cbFile != 0);
929
930 if (RT_SUCCESS(rc))
931 {
932 /* Initialize the segment cache */
933 rc = MMR3HeapAllocZEx(pEpClassFile->Core.pVM, MM_TAG_PDM_ASYNC_COMPLETION,
934 sizeof(PDMACTASKFILE),
935 (void **)&pEpFile->pTasksFreeHead);
936 if (RT_SUCCESS(rc))
937 {
938 PPDMACEPFILEMGR pAioMgr = NULL;
939
940 pEpFile->cbEndpoint = pEpFile->cbFile;
941 pEpFile->pTasksFreeTail = pEpFile->pTasksFreeHead;
942 pEpFile->cTasksCached = 0;
943 pEpFile->enmBackendType = enmEpBackend;
944 /*
945 * Disable async flushes on Solaris for now.
946 * They cause weird hangs which needs more investigations.
947 */
948#ifndef RT_OS_SOLARIS
949 pEpFile->fAsyncFlushSupported = true;
950#else
951 pEpFile->fAsyncFlushSupported = false;
952#endif
953
954 if (enmMgrType == PDMACEPFILEMGRTYPE_SIMPLE)
955 {
956 /* Simple mode. Every file has its own async I/O manager. */
957 rc = pdmacFileAioMgrCreate(pEpClassFile, &pAioMgr, PDMACEPFILEMGRTYPE_SIMPLE);
958 AssertRC(rc);
959 }
960 else
961 {
962 if ( (fFlags & PDMACEP_FILE_FLAGS_CACHING)
963 && (pEpClassFile->fCacheEnabled))
964 {
965 pEpFile->fCaching = true;
966 rc = pdmacFileEpCacheInit(pEpFile, pEpClassFile);
967 if (RT_FAILURE(rc))
968 {
969 LogRel(("AIOMgr: Endpoint for \"%s\" was opened with caching but initializing cache failed. Disabled caching\n", pszUri));
970 pEpFile->fCaching = false;
971 }
972 }
973
974 pAioMgr = pEpClassFile->pAioMgrHead;
975
976 /* Check for an idling manager of the same type */
977 while (pAioMgr)
978 {
979 if (pAioMgr->enmMgrType == enmMgrType)
980 break;
981 pAioMgr = pAioMgr->pNext;
982 }
983
984 if (!pAioMgr)
985 {
986 rc = pdmacFileAioMgrCreate(pEpClassFile, &pAioMgr, enmMgrType);
987 AssertRC(rc);
988 }
989 }
990
991 pEpFile->AioMgr.pTreeRangesLocked = (PAVLRFOFFTREE)RTMemAllocZ(sizeof(AVLRFOFFTREE));
992 if (!pEpFile->AioMgr.pTreeRangesLocked)
993 rc = VERR_NO_MEMORY;
994 else
995 {
996 pEpFile->enmState = PDMASYNCCOMPLETIONENDPOINTFILESTATE_ACTIVE;
997
998 /* Assign the endpoint to the thread. */
999 rc = pdmacFileAioMgrAddEndpoint(pAioMgr, pEpFile);
1000 if (RT_FAILURE(rc))
1001 {
1002 RTMemFree(pEpFile->AioMgr.pTreeRangesLocked);
1003 MMR3HeapFree(pEpFile->pTasksFreeHead);
1004 }
1005 }
1006 }
1007 }
1008
1009 if (RT_FAILURE(rc))
1010 RTFileClose(pEpFile->File);
1011 }
1012
1013#ifdef VBOX_WITH_STATISTICS
1014 if (RT_SUCCESS(rc))
1015 {
1016 STAMR3RegisterF(pEpClassFile->Core.pVM, &pEpFile->StatRead,
1017 STAMTYPE_PROFILE_ADV, STAMVISIBILITY_ALWAYS,
1018 STAMUNIT_TICKS_PER_CALL, "Time taken to read from the endpoint",
1019 "/PDM/AsyncCompletion/File/%s/Read", RTPathFilename(pEpFile->Core.pszUri));
1020
1021 STAMR3RegisterF(pEpClassFile->Core.pVM, &pEpFile->StatWrite,
1022 STAMTYPE_PROFILE_ADV, STAMVISIBILITY_ALWAYS,
1023 STAMUNIT_TICKS_PER_CALL, "Time taken to write to the endpoint",
1024 "/PDM/AsyncCompletion/File/%s/Write", RTPathFilename(pEpFile->Core.pszUri));
1025 }
1026#endif
1027
1028 if (RT_SUCCESS(rc))
1029 LogRel(("AIOMgr: Endpoint for file '%s' (flags %08x) created successfully\n", pszUri, pEpFile->fFlags));
1030
1031 return rc;
1032}
1033
1034static int pdmacFileEpRangesLockedDestroy(PAVLRFOFFNODECORE pNode, void *pvUser)
1035{
1036 AssertMsgFailed(("The locked ranges tree should be empty at that point\n"));
1037 return VINF_SUCCESS;
1038}
1039
1040static int pdmacFileEpClose(PPDMASYNCCOMPLETIONENDPOINT pEndpoint)
1041{
1042 int rc = VINF_SUCCESS;
1043 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1044 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pEndpoint->pEpClass;
1045
1046 /* Free the cached data. */
1047 if (pEpFile->fCaching)
1048 {
1049 rc = pdmacFileEpCacheFlush(pEpFile);
1050 AssertRC(rc);
1051 }
1052
1053 /* Make sure that all tasks finished for this endpoint. */
1054 rc = pdmacFileAioMgrCloseEndpoint(pEpFile->pAioMgr, pEpFile);
1055 AssertRC(rc);
1056
1057 /* endpoint and real file size should better be equal now. */
1058 AssertMsg(pEpFile->cbFile == pEpFile->cbEndpoint,
1059 ("Endpoint and real file size should match now!\n"));
1060
1061 /* Destroy any per endpoint cache data */
1062 if (pEpFile->fCaching)
1063 pdmacFileEpCacheDestroy(pEpFile);
1064
1065 /*
1066 * If the async I/O manager is in failsafe mode this is the only endpoint
1067 * he processes and thus can be destroyed now.
1068 */
1069 if (pEpFile->pAioMgr->enmMgrType == PDMACEPFILEMGRTYPE_SIMPLE)
1070 pdmacFileAioMgrDestroy(pEpClassFile, pEpFile->pAioMgr);
1071
1072 /* Free cached tasks. */
1073 PPDMACTASKFILE pTask = pEpFile->pTasksFreeHead;
1074
1075 while (pTask)
1076 {
1077 PPDMACTASKFILE pTaskFree = pTask;
1078 pTask = pTask->pNext;
1079 MMR3HeapFree(pTaskFree);
1080 }
1081
1082 /* Destroy the locked ranges tree now. */
1083 RTAvlrFileOffsetDestroy(pEpFile->AioMgr.pTreeRangesLocked, pdmacFileEpRangesLockedDestroy, NULL);
1084
1085 RTFileClose(pEpFile->File);
1086
1087#ifdef VBOX_WITH_STATISTICS
1088 STAMR3Deregister(pEpClassFile->Core.pVM, &pEpFile->StatRead);
1089 STAMR3Deregister(pEpClassFile->Core.pVM, &pEpFile->StatWrite);
1090#endif
1091
1092 return VINF_SUCCESS;
1093}
1094
1095static int pdmacFileEpRead(PPDMASYNCCOMPLETIONTASK pTask,
1096 PPDMASYNCCOMPLETIONENDPOINT pEndpoint, RTFOFF off,
1097 PCRTSGSEG paSegments, size_t cSegments,
1098 size_t cbRead)
1099{
1100 int rc = VINF_SUCCESS;
1101 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1102
1103 LogFlowFunc(("pTask=%#p pEndpoint=%#p off=%RTfoff paSegments=%#p cSegments=%zu cbRead=%zu\n",
1104 pTask, pEndpoint, off, paSegments, cSegments, cbRead));
1105
1106 STAM_PROFILE_ADV_START(&pEpFile->StatRead, Read);
1107
1108 pdmacFileEpTaskInit(pTask, cbRead);
1109
1110 if (pEpFile->fCaching)
1111 rc = pdmacFileEpCacheRead(pEpFile, (PPDMASYNCCOMPLETIONTASKFILE)pTask,
1112 off, paSegments, cSegments, cbRead);
1113 else
1114 rc = pdmacFileEpTaskInitiate(pTask, pEndpoint, off, paSegments, cSegments, cbRead,
1115 PDMACTASKFILETRANSFER_READ);
1116
1117 STAM_PROFILE_ADV_STOP(&pEpFile->StatRead, Read);
1118
1119 return rc;
1120}
1121
1122static int pdmacFileEpWrite(PPDMASYNCCOMPLETIONTASK pTask,
1123 PPDMASYNCCOMPLETIONENDPOINT pEndpoint, RTFOFF off,
1124 PCRTSGSEG paSegments, size_t cSegments,
1125 size_t cbWrite)
1126{
1127 int rc = VINF_SUCCESS;
1128 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1129
1130 if (RT_UNLIKELY(pEpFile->fReadonly))
1131 return VERR_NOT_SUPPORTED;
1132
1133 STAM_PROFILE_ADV_START(&pEpFile->StatWrite, Write);
1134
1135 pdmacFileEpTaskInit(pTask, cbWrite);
1136
1137 if (pEpFile->fCaching)
1138 rc = pdmacFileEpCacheWrite(pEpFile, (PPDMASYNCCOMPLETIONTASKFILE)pTask,
1139 off, paSegments, cSegments, cbWrite);
1140 else
1141 rc = pdmacFileEpTaskInitiate(pTask, pEndpoint, off, paSegments, cSegments, cbWrite,
1142 PDMACTASKFILETRANSFER_WRITE);
1143
1144 STAM_PROFILE_ADV_STOP(&pEpFile->StatWrite, Write);
1145
1146 /* Increase endpoint size. */
1147 if ( RT_SUCCESS(rc)
1148 && ((uint64_t)off + cbWrite) > pEpFile->cbEndpoint)
1149 ASMAtomicWriteU64(&pEpFile->cbEndpoint, (uint64_t)off + cbWrite);
1150
1151 return rc;
1152}
1153
1154static int pdmacFileEpFlush(PPDMASYNCCOMPLETIONTASK pTask,
1155 PPDMASYNCCOMPLETIONENDPOINT pEndpoint)
1156{
1157 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1158 PPDMASYNCCOMPLETIONTASKFILE pTaskFile = (PPDMASYNCCOMPLETIONTASKFILE)pTask;
1159
1160 if (RT_UNLIKELY(pEpFile->fReadonly))
1161 return VERR_NOT_SUPPORTED;
1162
1163 pdmacFileEpTaskInit(pTask, 0);
1164
1165 if (pEpFile->fCaching)
1166 {
1167 int rc = pdmacFileEpCacheFlush(pEpFile);
1168 AssertRC(rc);
1169 }
1170
1171 PPDMACTASKFILE pIoTask = pdmacFileTaskAlloc(pEpFile);
1172 if (RT_UNLIKELY(!pIoTask))
1173 return VERR_NO_MEMORY;
1174
1175 pIoTask->pEndpoint = pEpFile;
1176 pIoTask->enmTransferType = PDMACTASKFILETRANSFER_FLUSH;
1177 pIoTask->pvUser = pTaskFile;
1178 pIoTask->pfnCompleted = pdmacFileEpTaskCompleted;
1179 pdmacFileEpAddTask(pEpFile, pIoTask);
1180
1181 return VINF_AIO_TASK_PENDING;
1182}
1183
1184static int pdmacFileEpGetSize(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, uint64_t *pcbSize)
1185{
1186 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1187
1188 *pcbSize = ASMAtomicReadU64(&pEpFile->cbEndpoint);
1189
1190 return VINF_SUCCESS;
1191}
1192
1193static int pdmacFileEpSetSize(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, uint64_t cbSize)
1194{
1195 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1196
1197 ASMAtomicWriteU64(&pEpFile->cbEndpoint, cbSize);
1198 return RTFileSetSize(pEpFile->File, cbSize);
1199}
1200
1201const PDMASYNCCOMPLETIONEPCLASSOPS g_PDMAsyncCompletionEndpointClassFile =
1202{
1203 /* u32Version */
1204 PDMAC_EPCLASS_OPS_VERSION,
1205 /* pcszName */
1206 "File",
1207 /* enmClassType */
1208 PDMASYNCCOMPLETIONEPCLASSTYPE_FILE,
1209 /* cbEndpointClassGlobal */
1210 sizeof(PDMASYNCCOMPLETIONEPCLASSFILE),
1211 /* cbEndpoint */
1212 sizeof(PDMASYNCCOMPLETIONENDPOINTFILE),
1213 /* cbTask */
1214 sizeof(PDMASYNCCOMPLETIONTASKFILE),
1215 /* pfnInitialize */
1216 pdmacFileInitialize,
1217 /* pfnTerminate */
1218 pdmacFileTerminate,
1219 /* pfnEpInitialize. */
1220 pdmacFileEpInitialize,
1221 /* pfnEpClose */
1222 pdmacFileEpClose,
1223 /* pfnEpRead */
1224 pdmacFileEpRead,
1225 /* pfnEpWrite */
1226 pdmacFileEpWrite,
1227 /* pfnEpFlush */
1228 pdmacFileEpFlush,
1229 /* pfnEpGetSize */
1230 pdmacFileEpGetSize,
1231 /* pfnEpSetSize */
1232 pdmacFileEpSetSize,
1233 /* u32VersionEnd */
1234 PDMAC_EPCLASS_OPS_VERSION
1235};
1236
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