VirtualBox

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

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

PDMAsyncCompletion: Disable async I/O on linux if opening fails because it needs O_DIRECT too. Disable async I/O and enable the host cache if a request fails and the endpoint is migrated to a failsafe manager

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