VirtualBox

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

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

Build fix

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.5 KB
Line 
1/* $Id: PDMAsyncCompletionFile.cpp 24443 2009-11-06 13:52:43Z vboxsync $ */
2/** @file
3 * PDM Async I/O - Transport data asynchronous in R3 using EMT.
4 */
5
6/*
7 * Copyright (C) 2006-2009 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#define LOG_GROUP LOG_GROUP_PDM_ASYNC_COMPLETION
27#define RT_STRICT
28#include "PDMInternal.h"
29#include <VBox/pdm.h>
30#include <VBox/mm.h>
31#include <VBox/vm.h>
32#include <VBox/err.h>
33#include <VBox/log.h>
34
35#include <iprt/asm.h>
36#include <iprt/assert.h>
37#include <iprt/critsect.h>
38#include <iprt/env.h>
39#include <iprt/file.h>
40#include <iprt/mem.h>
41#include <iprt/semaphore.h>
42#include <iprt/string.h>
43#include <iprt/thread.h>
44#include <iprt/path.h>
45
46#include "PDMAsyncCompletionFileInternal.h"
47
48/**
49 * Frees a task.
50 *
51 * @returns nothing.
52 * @param pEndpoint Pointer to the endpoint the segment was for.
53 * @param pTask The task to free.
54 */
55void pdmacFileTaskFree(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint,
56 PPDMACTASKFILE pTask)
57{
58 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClass = (PPDMASYNCCOMPLETIONEPCLASSFILE)pEndpoint->Core.pEpClass;
59
60 LogFlowFunc((": pEndpoint=%p pTask=%p\n", pEndpoint, pTask));
61
62 /* Try the per endpoint cache first. */
63 if (pEndpoint->cTasksCached < pEpClass->cTasksCacheMax)
64 {
65 /* Add it to the list. */
66 pEndpoint->pTasksFreeTail->pNext = pTask;
67 pEndpoint->pTasksFreeTail = pTask;
68 ASMAtomicIncU32(&pEndpoint->cTasksCached);
69 }
70 else if (false)
71 {
72 /* Bigger class cache */
73 }
74 else
75 {
76 Log(("Freeing task %p because all caches are full\n", pTask));
77 MMR3HeapFree(pTask);
78 }
79}
80
81/**
82 * Allocates a task segment
83 *
84 * @returns Pointer to the new task segment or NULL
85 * @param pEndpoint Pointer to the endpoint
86 */
87PPDMACTASKFILE pdmacFileTaskAlloc(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint)
88{
89 PPDMACTASKFILE pTask = NULL;
90
91 /* Try the small per endpoint cache first. */
92 if (pEndpoint->pTasksFreeHead == pEndpoint->pTasksFreeTail)
93 {
94 /* Try the bigger endpoint class cache. */
95 PPDMASYNCCOMPLETIONEPCLASSFILE pEndpointClass = (PPDMASYNCCOMPLETIONEPCLASSFILE)pEndpoint->Core.pEpClass;
96
97#if 0
98 /* We start with the assigned slot id to distribute the load when allocating new tasks. */
99 unsigned iSlot = pEndpoint->iSlotStart;
100 do
101 {
102 pTask = (PPDMASYNCCOMPLETIONTASK)ASMAtomicXchgPtr((void * volatile *)&pEndpointClass->apTaskCache[iSlot], NULL);
103 if (pTask)
104 break;
105
106 iSlot = (iSlot + 1) % RT_ELEMENTS(pEndpointClass->apTaskCache);
107 } while (iSlot != pEndpoint->iSlotStart);
108#endif
109 if (!pTask)
110 {
111 /*
112 * Allocate completely new.
113 * If this fails we return NULL.
114 */
115 int rc = MMR3HeapAllocZEx(pEndpointClass->Core.pVM, MM_TAG_PDM_ASYNC_COMPLETION,
116 sizeof(PDMACTASKFILE),
117 (void **)&pTask);
118 if (RT_FAILURE(rc))
119 pTask = NULL;
120
121 LogFlow(("Allocated task %p\n", pTask));
122 }
123#if 0
124 else
125 {
126 /* Remove the first element and put the rest into the slot again. */
127 PPDMASYNCCOMPLETIONTASK pTaskHeadNew = pTask->pNext;
128
129 pTaskHeadNew->pPrev = NULL;
130
131 /* Put back into the list adding any new tasks. */
132 while (true)
133 {
134 bool fChanged = ASMAtomicCmpXchgPtr((void * volatile *)&pEndpointClass->apTaskCache[iSlot], pTaskHeadNew, NULL);
135
136 if (fChanged)
137 break;
138
139 PPDMASYNCCOMPLETIONTASK pTaskHead = (PPDMASYNCCOMPLETIONTASK)ASMAtomicXchgPtr((void * volatile *)&pEndpointClass->apTaskCache[iSlot], NULL);
140
141 /* The new task could be taken inbetween */
142 if (pTaskHead)
143 {
144 /* Go to the end of the probably much shorter new list. */
145 PPDMASYNCCOMPLETIONTASK pTaskTail = pTaskHead;
146 while (pTaskTail->pNext)
147 pTaskTail = pTaskTail->pNext;
148
149 /* Concatenate */
150 pTaskTail->pNext = pTaskHeadNew;
151
152 pTaskHeadNew = pTaskHead;
153 }
154 /* Another round trying to change the list. */
155 }
156 /* We got a task from the global cache so decrement the counter */
157 ASMAtomicDecU32(&pEndpointClass->cTasksCached);
158 }
159#endif
160 }
161 else
162 {
163 /* Grab a free task from the head. */
164 AssertMsg(pEndpoint->cTasksCached > 0, ("No tasks cached but list contains more than one element\n"));
165
166 pTask = pEndpoint->pTasksFreeHead;
167 pEndpoint->pTasksFreeHead = pTask->pNext;
168 ASMAtomicDecU32(&pEndpoint->cTasksCached);
169 }
170
171 pTask->pNext = NULL;
172
173 return pTask;
174}
175
176PPDMACTASKFILE pdmacFileEpGetNewTasks(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint)
177{
178 PPDMACTASKFILE pTasks = NULL;
179
180 /*
181 * Get pending tasks.
182 */
183 pTasks = (PPDMACTASKFILE)ASMAtomicXchgPtr((void * volatile *)&pEndpoint->pTasksNewHead, NULL);
184
185 /* Reverse the list to process in FIFO order. */
186 if (pTasks)
187 {
188 PPDMACTASKFILE pTask = pTasks;
189
190 pTasks = NULL;
191
192 while (pTask)
193 {
194 PPDMACTASKFILE pCur = pTask;
195 pTask = pTask->pNext;
196 pCur->pNext = pTasks;
197 pTasks = pCur;
198 }
199 }
200
201 return pTasks;
202}
203
204static void pdmacFileAioMgrWakeup(PPDMACEPFILEMGR pAioMgr)
205{
206 bool fWokenUp = ASMAtomicXchgBool(&pAioMgr->fWokenUp, true);
207
208 if (!fWokenUp)
209 {
210 int rc = VINF_SUCCESS;
211 bool fWaitingEventSem = ASMAtomicReadBool(&pAioMgr->fWaitingEventSem);
212
213 if (fWaitingEventSem)
214 rc = RTSemEventSignal(pAioMgr->EventSem);
215
216 AssertRC(rc);
217 }
218}
219
220static int pdmacFileAioMgrWaitForBlockingEvent(PPDMACEPFILEMGR pAioMgr, PDMACEPFILEAIOMGRBLOCKINGEVENT enmEvent)
221{
222 int rc = VINF_SUCCESS;
223
224 ASMAtomicWriteU32((volatile uint32_t *)&pAioMgr->enmBlockingEvent, enmEvent);
225 Assert(!pAioMgr->fBlockingEventPending);
226 ASMAtomicXchgBool(&pAioMgr->fBlockingEventPending, true);
227
228 /* Wakeup the async I/O manager */
229 pdmacFileAioMgrWakeup(pAioMgr);
230
231 /* Wait for completion. */
232 rc = RTSemEventWait(pAioMgr->EventSemBlock, RT_INDEFINITE_WAIT);
233 AssertRC(rc);
234
235 ASMAtomicXchgBool(&pAioMgr->fBlockingEventPending, false);
236 ASMAtomicWriteU32((volatile uint32_t *)&pAioMgr->enmBlockingEvent, PDMACEPFILEAIOMGRBLOCKINGEVENT_INVALID);
237
238 return rc;
239}
240
241int pdmacFileAioMgrAddEndpoint(PPDMACEPFILEMGR pAioMgr, PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint)
242{
243 int rc;
244
245 rc = RTCritSectEnter(&pAioMgr->CritSectBlockingEvent);
246 AssertRCReturn(rc, rc);
247
248 ASMAtomicWritePtr((void * volatile *)&pAioMgr->BlockingEventData.AddEndpoint.pEndpoint, pEndpoint);
249 rc = pdmacFileAioMgrWaitForBlockingEvent(pAioMgr, PDMACEPFILEAIOMGRBLOCKINGEVENT_ADD_ENDPOINT);
250
251 RTCritSectLeave(&pAioMgr->CritSectBlockingEvent);
252
253 if (RT_SUCCESS(rc))
254 ASMAtomicWritePtr((void * volatile *)&pEndpoint->pAioMgr, pAioMgr);
255
256 return rc;
257}
258
259static int pdmacFileAioMgrRemoveEndpoint(PPDMACEPFILEMGR pAioMgr, PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint)
260{
261 int rc;
262
263 rc = RTCritSectEnter(&pAioMgr->CritSectBlockingEvent);
264 AssertRCReturn(rc, rc);
265
266 ASMAtomicWritePtr((void * volatile *)&pAioMgr->BlockingEventData.RemoveEndpoint.pEndpoint, pEndpoint);
267 rc = pdmacFileAioMgrWaitForBlockingEvent(pAioMgr, PDMACEPFILEAIOMGRBLOCKINGEVENT_REMOVE_ENDPOINT);
268
269 RTCritSectLeave(&pAioMgr->CritSectBlockingEvent);
270
271 return rc;
272}
273
274static int pdmacFileAioMgrCloseEndpoint(PPDMACEPFILEMGR pAioMgr, PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint)
275{
276 int rc;
277
278 rc = RTCritSectEnter(&pAioMgr->CritSectBlockingEvent);
279 AssertRCReturn(rc, rc);
280
281 ASMAtomicWritePtr((void * volatile *)&pAioMgr->BlockingEventData.CloseEndpoint.pEndpoint, pEndpoint);
282 rc = pdmacFileAioMgrWaitForBlockingEvent(pAioMgr, PDMACEPFILEAIOMGRBLOCKINGEVENT_CLOSE_ENDPOINT);
283
284 RTCritSectLeave(&pAioMgr->CritSectBlockingEvent);
285
286 return rc;
287}
288
289static int pdmacFileAioMgrShutdown(PPDMACEPFILEMGR pAioMgr)
290{
291 int rc;
292
293 rc = RTCritSectEnter(&pAioMgr->CritSectBlockingEvent);
294 AssertRCReturn(rc, rc);
295
296 rc = pdmacFileAioMgrWaitForBlockingEvent(pAioMgr, PDMACEPFILEAIOMGRBLOCKINGEVENT_SHUTDOWN);
297
298 RTCritSectLeave(&pAioMgr->CritSectBlockingEvent);
299
300 return rc;
301}
302
303int pdmacFileEpAddTask(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint, PPDMACTASKFILE pTask)
304{
305 PPDMACTASKFILE pNext;
306 do
307 {
308 pNext = pEndpoint->pTasksNewHead;
309 pTask->pNext = pNext;
310 } while (!ASMAtomicCmpXchgPtr((void * volatile *)&pEndpoint->pTasksNewHead, (void *)pTask, (void *)pNext));
311
312 pdmacFileAioMgrWakeup((PPDMACEPFILEMGR)ASMAtomicReadPtr((void * volatile *)&pEndpoint->pAioMgr));
313
314 return VINF_SUCCESS;
315}
316
317void pdmacFileEpTaskCompleted(PPDMACTASKFILE pTask, void *pvUser)
318{
319 PPDMASYNCCOMPLETIONTASKFILE pTaskFile = (PPDMASYNCCOMPLETIONTASKFILE)pvUser;
320
321 if (pTask->enmTransferType == PDMACTASKFILETRANSFER_FLUSH)
322 {
323 pdmR3AsyncCompletionCompleteTask(&pTaskFile->Core);
324 }
325 else
326 {
327 uint32_t uOld = ASMAtomicSubU32(&pTaskFile->cbTransferLeft, pTask->DataSeg.cbSeg);
328
329 if (!(uOld - pTask->DataSeg.cbSeg)
330 && !ASMAtomicXchgBool(&pTaskFile->fCompleted, true))
331 pdmR3AsyncCompletionCompleteTask(&pTaskFile->Core);
332 }
333}
334
335int pdmacFileEpTaskInitiate(PPDMASYNCCOMPLETIONTASK pTask,
336 PPDMASYNCCOMPLETIONENDPOINT pEndpoint, RTFOFF off,
337 PCPDMDATASEG paSegments, size_t cSegments,
338 size_t cbTransfer, PDMACTASKFILETRANSFER enmTransfer)
339{
340 int rc = VINF_SUCCESS;
341 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
342 PPDMASYNCCOMPLETIONTASKFILE pTaskFile = (PPDMASYNCCOMPLETIONTASKFILE)pTask;
343 PPDMACEPFILEMGR pAioMgr = pEpFile->pAioMgr;
344
345 Assert( (enmTransfer == PDMACTASKFILETRANSFER_READ)
346 || (enmTransfer == PDMACTASKFILETRANSFER_WRITE));
347
348 ASMAtomicWriteS32(&pTaskFile->cbTransferLeft, cbTransfer);
349 ASMAtomicWriteBool(&pTaskFile->fCompleted, false);
350
351 for (unsigned i = 0; i < cSegments; i++)
352 {
353 PPDMACTASKFILE pIoTask = pdmacFileTaskAlloc(pEpFile);
354 AssertPtr(pIoTask);
355
356 pIoTask->pEndpoint = pEpFile;
357 pIoTask->enmTransferType = enmTransfer;
358 pIoTask->Off = off;
359 pIoTask->DataSeg.cbSeg = paSegments[i].cbSeg;
360 pIoTask->DataSeg.pvSeg = paSegments[i].pvSeg;
361 pIoTask->pvUser = pTaskFile;
362 pIoTask->pfnCompleted = pdmacFileEpTaskCompleted;
363
364 /* Send it off to the I/O manager. */
365 pdmacFileEpAddTask(pEpFile, pIoTask);
366 off += paSegments[i].cbSeg;
367 cbTransfer -= paSegments[i].cbSeg;
368 }
369
370 AssertMsg(!cbTransfer, ("Incomplete transfer %u bytes left\n", cbTransfer));
371
372 if (ASMAtomicReadS32(&pTaskFile->cbTransferLeft) == 0
373 && !ASMAtomicXchgBool(&pTaskFile->fCompleted, true))
374 pdmR3AsyncCompletionCompleteTask(pTask);
375
376 return VINF_SUCCESS;
377}
378
379/**
380 * Creates a new async I/O manager.
381 *
382 * @returns VBox status code.
383 * @param pEpClass Pointer to the endpoint class data.
384 * @param ppAioMgr Where to store the pointer to the new async I/O manager on success.
385 * @param fFailsafe Flag to force a failsafe manager even if the global flag is not set.
386 */
387int pdmacFileAioMgrCreate(PPDMASYNCCOMPLETIONEPCLASSFILE pEpClass, PPPDMACEPFILEMGR ppAioMgr, bool fFailsafe)
388{
389 int rc = VINF_SUCCESS;
390 PPDMACEPFILEMGR pAioMgrNew;
391
392 LogFlowFunc((": Entered\n"));
393
394 rc = MMR3HeapAllocZEx(pEpClass->Core.pVM, MM_TAG_PDM_ASYNC_COMPLETION, sizeof(PDMACEPFILEMGR), (void **)&pAioMgrNew);
395 if (RT_SUCCESS(rc))
396 {
397 pAioMgrNew->fFailsafe = fFailsafe ? true : pEpClass->fFailsafe;
398
399 rc = RTSemEventCreate(&pAioMgrNew->EventSem);
400 if (RT_SUCCESS(rc))
401 {
402 rc = RTSemEventCreate(&pAioMgrNew->EventSemBlock);
403 if (RT_SUCCESS(rc))
404 {
405 rc = RTCritSectInit(&pAioMgrNew->CritSectBlockingEvent);
406 if (RT_SUCCESS(rc))
407 {
408 /* Init the rest of the manager. */
409 if (!pAioMgrNew->fFailsafe)
410 rc = pdmacFileAioMgrNormalInit(pAioMgrNew);
411
412 if (RT_SUCCESS(rc))
413 {
414 pAioMgrNew->enmState = PDMACEPFILEMGRSTATE_RUNNING;
415
416 rc = RTThreadCreateF(&pAioMgrNew->Thread,
417 pAioMgrNew->fFailsafe
418 ? pdmacFileAioMgrFailsafe
419 : pdmacFileAioMgrNormal,
420 pAioMgrNew,
421 0,
422 RTTHREADTYPE_IO,
423 0,
424 "AioMgr%d-%s", pEpClass->cAioMgrs,
425 pAioMgrNew->fFailsafe
426 ? "F"
427 : "N");
428 if (RT_SUCCESS(rc))
429 {
430 /* Link it into the list. */
431 RTCritSectEnter(&pEpClass->CritSect);
432 pAioMgrNew->pNext = pEpClass->pAioMgrHead;
433 if (pEpClass->pAioMgrHead)
434 pEpClass->pAioMgrHead->pPrev = pAioMgrNew;
435 pEpClass->pAioMgrHead = pAioMgrNew;
436 pEpClass->cAioMgrs++;
437 RTCritSectLeave(&pEpClass->CritSect);
438
439 *ppAioMgr = pAioMgrNew;
440
441 Log(("PDMAC: Successfully created new file AIO Mgr {%s}\n", RTThreadGetName(pAioMgrNew->Thread)));
442 return VINF_SUCCESS;
443 }
444 pdmacFileAioMgrNormalDestroy(pAioMgrNew);
445 }
446 RTCritSectDelete(&pAioMgrNew->CritSectBlockingEvent);
447 }
448 RTSemEventDestroy(pAioMgrNew->EventSem);
449 }
450 RTSemEventDestroy(pAioMgrNew->EventSemBlock);
451 }
452 MMR3HeapFree(pAioMgrNew);
453 }
454
455 LogFlowFunc((": Leave rc=%Rrc\n", rc));
456
457 return rc;
458}
459
460/**
461 * Destroys a async I/O manager.
462 *
463 * @returns nothing.
464 * @param pAioMgr The async I/O manager to destroy.
465 */
466static void pdmacFileAioMgrDestroy(PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile, PPDMACEPFILEMGR pAioMgr)
467{
468 int rc = pdmacFileAioMgrShutdown(pAioMgr);
469 AssertRC(rc);
470
471 /* Unlink from the list. */
472 rc = RTCritSectEnter(&pEpClassFile->CritSect);
473 AssertRC(rc);
474
475 PPDMACEPFILEMGR pPrev = pAioMgr->pPrev;
476 PPDMACEPFILEMGR pNext = pAioMgr->pNext;
477
478 if (pPrev)
479 pPrev->pNext = pNext;
480 else
481 pEpClassFile->pAioMgrHead = pNext;
482
483 if (pNext)
484 pNext->pPrev = pPrev;
485
486 pEpClassFile->cAioMgrs--;
487
488 rc = RTCritSectLeave(&pEpClassFile->CritSect);
489 AssertRC(rc);
490
491 /* Free the ressources. */
492 RTCritSectDelete(&pAioMgr->CritSectBlockingEvent);
493 RTSemEventDestroy(pAioMgr->EventSem);
494 if (!pAioMgr->fFailsafe)
495 pdmacFileAioMgrNormalDestroy(pAioMgr);
496
497 MMR3HeapFree(pAioMgr);
498}
499
500static int pdmacFileInitialize(PPDMASYNCCOMPLETIONEPCLASS pClassGlobals, PCFGMNODE pCfgNode)
501{
502 int rc = VINF_SUCCESS;
503 RTFILEAIOLIMITS AioLimits; /** < Async I/O limitations. */
504
505 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pClassGlobals;
506
507 rc = RTFileAioGetLimits(&AioLimits);
508#ifdef DEBUG
509 if (RT_SUCCESS(rc) && RTEnvExist("VBOX_ASYNC_IO_FAILBACK"))
510 rc = VERR_ENV_VAR_NOT_FOUND;
511#endif
512 if (RT_FAILURE(rc))
513 {
514 LogRel(("AIO: Async I/O manager not supported (rc=%Rrc). Falling back to failsafe manager\n",
515 rc));
516 pEpClassFile->fFailsafe = true;
517 }
518 else
519 {
520 pEpClassFile->uBitmaskAlignment = AioLimits.cbBufferAlignment ? ~((RTR3UINTPTR)AioLimits.cbBufferAlignment - 1) : RTR3UINTPTR_MAX;
521 pEpClassFile->cReqsOutstandingMax = AioLimits.cReqsOutstandingMax;
522
523 /* The user can force the failsafe manager. */
524 rc = CFGMR3QueryBoolDef(pCfgNode, "UseFailsafeIo", &pEpClassFile->fFailsafe, false);
525 AssertLogRelRCReturn(rc, rc);
526
527 if (pEpClassFile->fFailsafe)
528 LogRel(("AIOMgr: Failsafe I/O was requested by user\n"));
529 }
530
531 /* Init critical section. */
532 rc = RTCritSectInit(&pEpClassFile->CritSect);
533 if (RT_SUCCESS(rc))
534 {
535 /* Check if the host cache should be used too. */
536#ifndef RT_OS_LINUX
537 rc = CFGMR3QueryBoolDef(pCfgNode, "HostCacheEnabled", &pEpClassFile->fHostCacheEnabled, false);
538 AssertLogRelRCReturn(rc, rc);
539#else
540 /*
541 * Host cache + async I/O is not supported on Linux. Check if the user enabled the cache,
542 * leave a warning and disable it always.
543 */
544 bool fDummy;
545 rc = CFGMR3QueryBool(pCfgNode, "HostCacheEnabled", &fDummy);
546 if (RT_SUCCESS(rc))
547 LogRel(("AIOMgr: The host cache is not supported with async I/O on Linux\n"));
548
549 pEpClassFile->fHostCacheEnabled = false;
550#endif
551
552 /* Check if the cache was disabled by the user. */
553 rc = CFGMR3QueryBoolDef(pCfgNode, "CacheEnabled", &pEpClassFile->fCacheEnabled, true);
554 AssertLogRelRCReturn(rc, rc);
555
556 if (pEpClassFile->fCacheEnabled)
557 {
558 /* Init cache structure */
559 rc = pdmacFileCacheInit(pEpClassFile, pCfgNode);
560 if (RT_FAILURE(rc))
561 {
562 RTCritSectDelete(&pEpClassFile->CritSect);
563 pEpClassFile->fCacheEnabled = false;
564 LogRel(("AIOMgr: Failed to initialise the cache (rc=%Rrc), disabled caching\n"));
565 }
566 }
567 else
568 LogRel(("AIOMgr: Cache was globally disabled\n"));
569 }
570
571 return rc;
572}
573
574static void pdmacFileTerminate(PPDMASYNCCOMPLETIONEPCLASS pClassGlobals)
575{
576 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pClassGlobals;
577
578 /* All endpoints should be closed at this point. */
579 AssertMsg(!pEpClassFile->Core.pEndpointsHead, ("There are still endpoints left\n"));
580
581 /* Destroy all left async I/O managers. */
582 while (pEpClassFile->pAioMgrHead)
583 pdmacFileAioMgrDestroy(pEpClassFile, pEpClassFile->pAioMgrHead);
584
585 /* Destroy the cache. */
586 if (pEpClassFile->fCacheEnabled)
587 pdmacFileCacheDestroy(pEpClassFile);
588
589 RTCritSectDelete(&pEpClassFile->CritSect);
590}
591
592static int pdmacFileEpInitialize(PPDMASYNCCOMPLETIONENDPOINT pEndpoint,
593 const char *pszUri, uint32_t fFlags)
594{
595 int rc = VINF_SUCCESS;
596 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
597 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pEndpoint->pEpClass;
598 bool fUseFailsafeManager = pEpClassFile->fFailsafe;
599
600 AssertMsgReturn((fFlags & ~(PDMACEP_FILE_FLAGS_READ_ONLY | PDMACEP_FILE_FLAGS_CACHING)) == 0,
601 ("PDMAsyncCompletion: Invalid flag specified\n"), VERR_INVALID_PARAMETER);
602
603 unsigned fFileFlags = fFlags & PDMACEP_FILE_FLAGS_READ_ONLY
604 ? RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE
605 : RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE;
606
607 if (!pEpClassFile->fFailsafe)
608 {
609 fFileFlags |= (RTFILE_O_ASYNC_IO | RTFILE_O_WRITE_THROUGH);
610
611 /*
612 * We only disable the cache if the size of the file is a multiple of 512.
613 * Certain hosts like Windows, Linux and Solaris require that transfer sizes
614 * are aligned to the volume sector size.
615 * If not we just make sure that the data is written to disk with RTFILE_O_WRITE_THROUGH
616 * which will trash the host cache but ensures that the host cache will not
617 * contain dirty buffers.
618 */
619 RTFILE File = NIL_RTFILE;
620
621 rc = RTFileOpen(&File, pszUri, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
622 if (RT_SUCCESS(rc))
623 {
624 uint64_t cbSize;
625
626 rc = RTFileGetSize(File, &cbSize);
627 if (RT_SUCCESS(rc) && ((cbSize % 512) == 0))
628 {
629 fFileFlags &= ~RTFILE_O_WRITE_THROUGH;
630
631#if defined(RT_OS_LINUX)
632 AssertMsg(!pEpClassFile->fHostCacheEnabled, ("Host cache + async I/O is not supported on Linux\n"));
633 fFileFlags |= RTFILE_O_NO_CACHE;
634#else
635 if (!pEpClassFile->fHostCacheEnabled)
636 fFileFlags |= RTFILE_O_NO_CACHE;
637#endif
638 }
639
640 pEpFile->cbFile = cbSize;
641
642 RTFileClose(File);
643 }
644 }
645
646 /* Open with final flags. */
647 rc = RTFileOpen(&pEpFile->File, pszUri, fFileFlags);
648 if ((rc == VERR_INVALID_FUNCTION) || (rc == VERR_INVALID_PARAMETER))
649 {
650 LogRel(("pdmacFileEpInitialize: RTFileOpen %s / %08x failed with %Rrc\n",
651 pszUri, fFileFlags, rc));
652 /*
653 * Solaris doesn't support directio on ZFS so far. :-\
654 * Trying to enable it returns VERR_INVALID_FUNCTION
655 * (ENOTTY). Remove it and hope for the best.
656 * ZFS supports write throttling in case applications
657 * write more data than can be synced to the disk
658 * without blocking the whole application.
659 *
660 * On Linux we have the same problem with cifs.
661 * Have to disable async I/O here too because it requires O_DIRECT.
662 */
663 fFileFlags &= ~RTFILE_O_NO_CACHE;
664
665#ifdef RT_OS_LINUX
666 fFileFlags &= ~RTFILE_O_ASYNC_IO;
667 fUseFailsafeManager = true;
668#endif
669
670 /* Open again. */
671 rc = RTFileOpen(&pEpFile->File, pszUri, fFileFlags);
672
673 if (RT_FAILURE(rc))
674 {
675 LogRel(("pdmacFileEpInitialize: RTFileOpen %s / %08x failed AGAIN(!) with %Rrc\n",
676 pszUri, fFileFlags, rc));
677 }
678 }
679
680 if (RT_SUCCESS(rc))
681 {
682 pEpFile->fFlags = fFileFlags;
683
684 rc = RTFileGetSize(pEpFile->File, (uint64_t *)&pEpFile->cbFile);
685 if (RT_SUCCESS(rc))
686 {
687 /* Initialize the segment cache */
688 rc = MMR3HeapAllocZEx(pEpClassFile->Core.pVM, MM_TAG_PDM_ASYNC_COMPLETION,
689 sizeof(PDMACTASKFILE),
690 (void **)&pEpFile->pTasksFreeHead);
691 if (RT_SUCCESS(rc))
692 {
693 PPDMACEPFILEMGR pAioMgr = NULL;
694
695 pEpFile->pTasksFreeTail = pEpFile->pTasksFreeHead;
696 pEpFile->cTasksCached = 0;
697
698 if (fUseFailsafeManager)
699 {
700 /* Safe mode. Every file has its own async I/O manager. */
701 rc = pdmacFileAioMgrCreate(pEpClassFile, &pAioMgr, true);
702 AssertRC(rc);
703 }
704 else
705 {
706 if ( (fFlags & PDMACEP_FILE_FLAGS_CACHING)
707 && (pEpClassFile->fCacheEnabled))
708 {
709 pEpFile->fCaching = true;
710 rc = pdmacFileEpCacheInit(pEpFile, pEpClassFile);
711 if (RT_FAILURE(rc))
712 {
713 LogRel(("AIOMgr: Endpoint for \"%s\" was opened with caching but initializing cache failed. Disabled caching\n", pszUri));
714 pEpFile->fCaching = false;
715 }
716 }
717
718 pAioMgr = pEpClassFile->pAioMgrHead;
719
720 /* Check for an idling not failsafe one or create new if not found */
721 while (pAioMgr && pAioMgr->fFailsafe)
722 pAioMgr = pAioMgr->pNext;
723
724 if (!pAioMgr)
725 {
726 rc = pdmacFileAioMgrCreate(pEpClassFile, &pAioMgr, false);
727 AssertRC(rc);
728 }
729 }
730
731 pEpFile->enmState = PDMASYNCCOMPLETIONENDPOINTFILESTATE_ACTIVE;
732
733 /* Assign the endpoint to the thread. */
734 rc = pdmacFileAioMgrAddEndpoint(pAioMgr, pEpFile);
735 if (RT_FAILURE(rc))
736 MMR3HeapFree(pEpFile->pTasksFreeHead);
737 }
738 }
739
740 if (RT_FAILURE(rc))
741 RTFileClose(pEpFile->File);
742 }
743
744#ifdef VBOX_WITH_STATISTICS
745 if (RT_SUCCESS(rc))
746 {
747 STAMR3RegisterF(pEpClassFile->Core.pVM, &pEpFile->StatRead,
748 STAMTYPE_PROFILE_ADV, STAMVISIBILITY_ALWAYS,
749 STAMUNIT_TICKS_PER_CALL, "Time taken to read from the endpoint",
750 "/PDM/AsyncCompletion/File/%s/Read", RTPathFilename(pEpFile->Core.pszUri));
751
752 STAMR3RegisterF(pEpClassFile->Core.pVM, &pEpFile->StatWrite,
753 STAMTYPE_PROFILE_ADV, STAMVISIBILITY_ALWAYS,
754 STAMUNIT_TICKS_PER_CALL, "Time taken to write to the endpoint",
755 "/PDM/AsyncCompletion/File/%s/Write", RTPathFilename(pEpFile->Core.pszUri));
756 }
757#endif
758
759 return rc;
760}
761
762static int pdmacFileEpClose(PPDMASYNCCOMPLETIONENDPOINT pEndpoint)
763{
764 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
765 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pEndpoint->pEpClass;
766
767 /* Make sure that all tasks finished for this endpoint. */
768 int rc = pdmacFileAioMgrCloseEndpoint(pEpFile->pAioMgr, pEpFile);
769 AssertRC(rc);
770
771 /*
772 * If the async I/O manager is in failsafe mode this is the only endpoint
773 * he processes and thus can be destroyed now.
774 */
775 if (pEpFile->pAioMgr->fFailsafe)
776 pdmacFileAioMgrDestroy(pEpClassFile, pEpFile->pAioMgr);
777
778 /* Free cached tasks. */
779 PPDMACTASKFILE pTask = pEpFile->pTasksFreeHead;
780
781 while (pTask)
782 {
783 PPDMACTASKFILE pTaskFree = pTask;
784 pTask = pTask->pNext;
785 MMR3HeapFree(pTaskFree);
786 }
787
788 /* Free the cached data. */
789 if (pEpFile->fCaching)
790 pdmacFileEpCacheDestroy(pEpFile);
791
792 RTFileClose(pEpFile->File);
793
794#ifdef VBOX_WITH_STATISTICS
795 STAMR3Deregister(pEpClassFile->Core.pVM, &pEpFile->StatRead);
796 STAMR3Deregister(pEpClassFile->Core.pVM, &pEpFile->StatWrite);
797#endif
798
799 return VINF_SUCCESS;
800}
801
802static int pdmacFileEpRead(PPDMASYNCCOMPLETIONTASK pTask,
803 PPDMASYNCCOMPLETIONENDPOINT pEndpoint, RTFOFF off,
804 PCPDMDATASEG paSegments, size_t cSegments,
805 size_t cbRead)
806{
807 int rc = VINF_SUCCESS;
808 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
809
810 STAM_PROFILE_ADV_START(&pEpFile->StatRead, Read);
811
812 if (pEpFile->fCaching)
813 rc = pdmacFileEpCacheRead(pEpFile, (PPDMASYNCCOMPLETIONTASKFILE)pTask,
814 off, paSegments, cSegments, cbRead);
815 else
816 rc = pdmacFileEpTaskInitiate(pTask, pEndpoint, off, paSegments, cSegments, cbRead,
817 PDMACTASKFILETRANSFER_READ);
818
819 STAM_PROFILE_ADV_STOP(&pEpFile->StatRead, Read);
820
821 return rc;
822}
823
824static int pdmacFileEpWrite(PPDMASYNCCOMPLETIONTASK pTask,
825 PPDMASYNCCOMPLETIONENDPOINT pEndpoint, RTFOFF off,
826 PCPDMDATASEG paSegments, size_t cSegments,
827 size_t cbWrite)
828{
829 int rc = VINF_SUCCESS;
830 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
831
832 if (RT_UNLIKELY(pEpFile->fReadonly))
833 return VERR_NOT_SUPPORTED;
834
835 STAM_PROFILE_ADV_START(&pEpFile->StatWrite, Write);
836
837 if (pEpFile->fCaching)
838 rc = pdmacFileEpCacheWrite(pEpFile, (PPDMASYNCCOMPLETIONTASKFILE)pTask,
839 off, paSegments, cSegments, cbWrite);
840 else
841 rc = pdmacFileEpTaskInitiate(pTask, pEndpoint, off, paSegments, cSegments, cbWrite,
842 PDMACTASKFILETRANSFER_WRITE);
843
844 STAM_PROFILE_ADV_STOP(&pEpFile->StatWrite, Write);
845
846 return rc;
847}
848
849static int pdmacFileEpFlush(PPDMASYNCCOMPLETIONTASK pTask,
850 PPDMASYNCCOMPLETIONENDPOINT pEndpoint)
851{
852 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
853 PPDMASYNCCOMPLETIONTASKFILE pTaskFile = (PPDMASYNCCOMPLETIONTASKFILE)pTask;
854
855 if (RT_UNLIKELY(pEpFile->fReadonly))
856 return VERR_NOT_SUPPORTED;
857
858 pTaskFile->cbTransferLeft = 0;
859
860 PPDMACTASKFILE pIoTask = pdmacFileTaskAlloc(pEpFile);
861 AssertPtr(pIoTask);
862
863 pIoTask->pEndpoint = pEpFile;
864 pIoTask->enmTransferType = PDMACTASKFILETRANSFER_FLUSH;
865 pIoTask->pvUser = pTaskFile;
866 pIoTask->pfnCompleted = pdmacFileEpTaskCompleted;
867 pdmacFileEpAddTask(pEpFile, pIoTask);
868
869 return VINF_SUCCESS;
870}
871
872static int pdmacFileEpGetSize(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, uint64_t *pcbSize)
873{
874 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
875
876 *pcbSize = ASMAtomicReadU64(&pEpFile->cbFile);
877
878 return VINF_SUCCESS;
879}
880
881const PDMASYNCCOMPLETIONEPCLASSOPS g_PDMAsyncCompletionEndpointClassFile =
882{
883 /* u32Version */
884 PDMAC_EPCLASS_OPS_VERSION,
885 /* pcszName */
886 "File",
887 /* enmClassType */
888 PDMASYNCCOMPLETIONEPCLASSTYPE_FILE,
889 /* cbEndpointClassGlobal */
890 sizeof(PDMASYNCCOMPLETIONEPCLASSFILE),
891 /* cbEndpoint */
892 sizeof(PDMASYNCCOMPLETIONENDPOINTFILE),
893 /* cbTask */
894 sizeof(PDMASYNCCOMPLETIONTASKFILE),
895 /* pfnInitialize */
896 pdmacFileInitialize,
897 /* pfnTerminate */
898 pdmacFileTerminate,
899 /* pfnEpInitialize. */
900 pdmacFileEpInitialize,
901 /* pfnEpClose */
902 pdmacFileEpClose,
903 /* pfnEpRead */
904 pdmacFileEpRead,
905 /* pfnEpWrite */
906 pdmacFileEpWrite,
907 /* pfnEpFlush */
908 pdmacFileEpFlush,
909 /* pfnEpGetSize */
910 pdmacFileEpGetSize,
911 /* u32VersionEnd */
912 PDMAC_EPCLASS_OPS_VERSION
913};
914
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use