VirtualBox

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

Last change on this file since 23958 was 23958, checked in by vboxsync, 16 years ago

temporary debugging code

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 27.4 KB
Line 
1/* $Id: PDMAsyncCompletionFile.cpp 23958 2009-10-22 06:19:07Z 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.215389.xyz. 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
558 AssertMsgReturn((fFlags & ~(PDMACEP_FILE_FLAGS_READ_ONLY | PDMACEP_FILE_FLAGS_CACHING)) == 0,
559 ("PDMAsyncCompletion: Invalid flag specified\n"), VERR_INVALID_PARAMETER);
560
561 unsigned fFileFlags = fFlags & PDMACEP_FILE_FLAGS_READ_ONLY
562 ? RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE
563 : RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE;
564
565 if (!pEpClassFile->fFailsafe)
566 {
567 fFileFlags |= (RTFILE_O_ASYNC_IO | RTFILE_O_WRITE_THROUGH);
568
569 /*
570 * We only disable the cache if the size of the file is a multiple of 512.
571 * Certain hosts like Windows, Linux and Solaris require that transfer sizes
572 * are aligned to the volume sector size.
573 * If not we just make sure that the data is written to disk with RTFILE_O_WRITE_THROUGH
574 * which will trash the host cache but ensures that the host cache will not
575 * contain dirty buffers.
576 */
577 RTFILE File = NIL_RTFILE;
578
579 rc = RTFileOpen(&File, pszUri, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
580 if (RT_SUCCESS(rc))
581 {
582 uint64_t cbSize;
583
584 rc = RTFileGetSize(File, &cbSize);
585 if (RT_SUCCESS(rc) && ((cbSize % 512) == 0))
586 {
587 fFileFlags &= ~RTFILE_O_WRITE_THROUGH;
588 fFileFlags |= RTFILE_O_NO_CACHE;
589 }
590
591 pEpFile->cbFile = cbSize;
592
593 RTFileClose(File);
594 }
595 }
596
597 /* Open with final flags. */
598 rc = RTFileOpen(&pEpFile->File, pszUri, fFileFlags);
599 if ((rc == VERR_INVALID_FUNCTION) || (rc == VERR_INVALID_PARAMETER))
600 {
601 LogRel(("pdmacFileEpInitialize: RTFileOpen %s / %08x failed with %Rrc\n",
602 pszUri, fFileFlags, rc));
603 /*
604 * Solaris doesn't support directio on ZFS so far. :-\
605 * Trying to enable it returns VERR_INVALID_FUNCTION
606 * (ENOTTY). Remove it and hope for the best.
607 * ZFS supports write throttling in case applications
608 * write more data than can be synced to the disk
609 * without blocking the whole application.
610 *
611 * On Linux we have the same problem with cifs.
612 * Shouldn't be a big problem here either because
613 * it's a network filesystem and the data is on another
614 * computer.
615 */
616 fFileFlags &= ~RTFILE_O_NO_CACHE;
617
618 /* Open again. */
619 rc = RTFileOpen(&pEpFile->File, pszUri, fFileFlags);
620
621 if (RT_FAILURE(rc))
622 {
623 LogRel(("pdmacFileEpInitialize: RTFileOpen %s / %08x failed AGAIN(!) with %Rrc\n",
624 pszUri, fFileFlags, rc));
625 }
626 }
627
628 if (RT_SUCCESS(rc))
629 {
630 pEpFile->fFlags = fFileFlags;
631
632 rc = RTFileGetSize(pEpFile->File, (uint64_t *)&pEpFile->cbFile);
633 if (RT_SUCCESS(rc))
634 {
635 /* Initialize the segment cache */
636 rc = MMR3HeapAllocZEx(pEpClassFile->Core.pVM, MM_TAG_PDM_ASYNC_COMPLETION,
637 sizeof(PDMACTASKFILE),
638 (void **)&pEpFile->pTasksFreeHead);
639 if (RT_SUCCESS(rc))
640 {
641 PPDMACEPFILEMGR pAioMgr = NULL;
642
643 pEpFile->pTasksFreeTail = pEpFile->pTasksFreeHead;
644 pEpFile->cTasksCached = 0;
645
646 if (pEpClassFile->fFailsafe)
647 {
648 /* Safe mode. Every file has its own async I/O manager. */
649 rc = pdmacFileAioMgrCreate(pEpClassFile, &pAioMgr, false);
650 AssertRC(rc);
651 }
652 else
653 {
654 if (fFlags & PDMACEP_FILE_FLAGS_CACHING)
655 {
656 pEpFile->fCaching = true;
657 rc = pdmacFileEpCacheInit(pEpFile, pEpClassFile);
658 if (RT_FAILURE(rc))
659 {
660 LogRel(("AIOMgr: Endpoint for \"%s\" was opened with caching but initializing cache failed. Disabled caching\n", pszUri));
661 pEpFile->fCaching = false;
662 }
663 }
664
665 /* Check for an idling one or create new if not found */
666 if (!pEpClassFile->pAioMgrHead)
667 {
668 rc = pdmacFileAioMgrCreate(pEpClassFile, &pAioMgr, false);
669 AssertRC(rc);
670 }
671 else
672 {
673 pAioMgr = pEpClassFile->pAioMgrHead;
674 }
675 }
676
677 pEpFile->enmState = PDMASYNCCOMPLETIONENDPOINTFILESTATE_ACTIVE;
678
679 /* Assign the endpoint to the thread. */
680 rc = pdmacFileAioMgrAddEndpoint(pAioMgr, pEpFile);
681 if (RT_FAILURE(rc))
682 MMR3HeapFree(pEpFile->pTasksFreeHead);
683 }
684 }
685
686 if (RT_FAILURE(rc))
687 RTFileClose(pEpFile->File);
688 }
689
690 return rc;
691}
692
693static int pdmacFileEpClose(PPDMASYNCCOMPLETIONENDPOINT pEndpoint)
694{
695 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
696 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pEndpoint->pEpClass;
697
698 /* Make sure that all tasks finished for this endpoint. */
699 int rc = pdmacFileAioMgrCloseEndpoint(pEpFile->pAioMgr, pEpFile);
700 AssertRC(rc);
701
702 /*
703 * If the async I/O manager is in failsafe mode this is the only endpoint
704 * he processes and thus can be destroyed now.
705 */
706 if (pEpFile->pAioMgr->fFailsafe)
707 pdmacFileAioMgrDestroy(pEpClassFile, pEpFile->pAioMgr);
708
709 /* Free cached tasks. */
710 PPDMACTASKFILE pTask = pEpFile->pTasksFreeHead;
711
712 while (pTask)
713 {
714 PPDMACTASKFILE pTaskFree = pTask;
715 pTask = pTask->pNext;
716 MMR3HeapFree(pTaskFree);
717 }
718
719 /* Free the cached data. */
720 if (pEpFile->fCaching)
721 pdmacFileEpCacheDestroy(pEpFile);
722
723 return VINF_SUCCESS;
724}
725
726static int pdmacFileEpRead(PPDMASYNCCOMPLETIONTASK pTask,
727 PPDMASYNCCOMPLETIONENDPOINT pEndpoint, RTFOFF off,
728 PCPDMDATASEG paSegments, size_t cSegments,
729 size_t cbRead)
730{
731 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
732
733 if (pEpFile->fCaching)
734 return pdmacFileEpCacheRead(pEpFile, (PPDMASYNCCOMPLETIONTASKFILE)pTask,
735 off, paSegments, cSegments, cbRead);
736 else
737 return pdmacFileEpTaskInitiate(pTask, pEndpoint, off, paSegments, cSegments, cbRead,
738 PDMACTASKFILETRANSFER_READ);
739}
740
741static int pdmacFileEpWrite(PPDMASYNCCOMPLETIONTASK pTask,
742 PPDMASYNCCOMPLETIONENDPOINT pEndpoint, RTFOFF off,
743 PCPDMDATASEG paSegments, size_t cSegments,
744 size_t cbWrite)
745{
746 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
747
748 if (RT_UNLIKELY(pEpFile->fReadonly))
749 return VERR_NOT_SUPPORTED;
750
751 if (pEpFile->fCaching)
752 return pdmacFileEpCacheWrite(pEpFile, (PPDMASYNCCOMPLETIONTASKFILE)pTask,
753 off, paSegments, cSegments, cbWrite);
754 else
755 return pdmacFileEpTaskInitiate(pTask, pEndpoint, off, paSegments, cSegments, cbWrite,
756 PDMACTASKFILETRANSFER_WRITE);
757}
758
759static int pdmacFileEpFlush(PPDMASYNCCOMPLETIONTASK pTask,
760 PPDMASYNCCOMPLETIONENDPOINT pEndpoint)
761{
762 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
763 PPDMASYNCCOMPLETIONTASKFILE pTaskFile = (PPDMASYNCCOMPLETIONTASKFILE)pTask;
764
765 if (RT_UNLIKELY(pEpFile->fReadonly))
766 return VERR_NOT_SUPPORTED;
767
768 pTaskFile->cbTransferLeft = 0;
769
770 PPDMACTASKFILE pIoTask = pdmacFileTaskAlloc(pEpFile);
771 AssertPtr(pIoTask);
772
773 pIoTask->pEndpoint = pEpFile;
774 pIoTask->enmTransferType = PDMACTASKFILETRANSFER_FLUSH;
775 pIoTask->pvUser = pTaskFile;
776 pIoTask->pfnCompleted = pdmacFileEpTaskCompleted;
777 pdmacFileEpAddTask(pEpFile, pIoTask);
778
779 return VINF_SUCCESS;
780}
781
782static int pdmacFileEpGetSize(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, uint64_t *pcbSize)
783{
784 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
785
786 *pcbSize = ASMAtomicReadU64(&pEpFile->cbFile);
787
788 return VINF_SUCCESS;
789}
790
791const PDMASYNCCOMPLETIONEPCLASSOPS g_PDMAsyncCompletionEndpointClassFile =
792{
793 /* u32Version */
794 PDMAC_EPCLASS_OPS_VERSION,
795 /* pcszName */
796 "File",
797 /* enmClassType */
798 PDMASYNCCOMPLETIONEPCLASSTYPE_FILE,
799 /* cbEndpointClassGlobal */
800 sizeof(PDMASYNCCOMPLETIONEPCLASSFILE),
801 /* cbEndpoint */
802 sizeof(PDMASYNCCOMPLETIONENDPOINTFILE),
803 /* cbTask */
804 sizeof(PDMASYNCCOMPLETIONTASKFILE),
805 /* pfnInitialize */
806 pdmacFileInitialize,
807 /* pfnTerminate */
808 pdmacFileTerminate,
809 /* pfnEpInitialize. */
810 pdmacFileEpInitialize,
811 /* pfnEpClose */
812 pdmacFileEpClose,
813 /* pfnEpRead */
814 pdmacFileEpRead,
815 /* pfnEpWrite */
816 pdmacFileEpWrite,
817 /* pfnEpFlush */
818 pdmacFileEpFlush,
819 /* pfnEpGetSize */
820 pdmacFileEpGetSize,
821 /* u32VersionEnd */
822 PDMAC_EPCLASS_OPS_VERSION
823};
824
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette