VirtualBox

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

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

Fix windows burns

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.0 KB
Line 
1/* $Id: PDMAsyncCompletionFile.cpp 20168 2009-06-01 20:31:31Z vboxsync $ */
2/** @file
3 * PDM Async I/O - Transport data asynchronous in R3 using EMT.
4 */
5
6/*
7 * Copyright (C) 2006-2008 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/*******************************************************************************
25* Header Files *
26*******************************************************************************/
27#define LOG_GROUP LOG_GROUP_PDM_ASYNC_COMPLETION
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
34#include <VBox/log.h>
35#include <iprt/asm.h>
36#include <iprt/assert.h>
37#include <iprt/thread.h>
38#include <iprt/mem.h>
39#include <iprt/critsect.h>
40#include <iprt/file.h>
41#include <iprt/semaphore.h>
42
43#include "PDMAsyncCompletionInternal.h"
44
45/** @todo: Revise the caching of tasks. We have currently four caches:
46 * Per endpoint task cache
47 * Per class cache
48 * Per endpoint task segment cache
49 * Per class task segment cache
50 *
51 * We could use the RT heap for this probably or extend MMR3Heap (uses RTMemAlloc
52 * instead of managing larger blocks) to have this global for the whole VM.
53 */
54
55/**
56 * A few forward declerations.
57 */
58typedef struct PDMASYNCCOMPLETIONENDPOINTFILE *PPDMASYNCCOMPLETIONENDPOINTFILE;
59/** Pointer to a request segment. */
60typedef struct PDMACTASKFILESEG *PPDMACTASKFILESEG;
61
62/**
63 * Blocking event types.
64 */
65typedef enum PDMACEPFILEAIOMGRBLOCKINGEVENT
66{
67 /** Invalid tye */
68 PDMACEPFILEAIOMGRBLOCKINGEVENT_INVALID = 0,
69 /** An endpoint is added to the manager. */
70 PDMACEPFILEAIOMGRBLOCKINGEVENT_ADD_ENDPOINT,
71 /** An endpoint is removed from the manager. */
72 PDMACEPFILEAIOMGRBLOCKINGEVENT_REMOVE_ENDPOINT,
73 /** An endpoint is about to be closed. */
74 PDMACEPFILEAIOMGRBLOCKINGEVENT_CLOSE_ENDPOINT,
75 /** The manager is requested to terminate */
76 PDMACEPFILEAIOMGRBLOCKINGEVENT_SHUTDOWN,
77 /** The manager is requested to suspend */
78 PDMACEPFILEAIOMGRBLOCKINGEVENT_SUSPEND,
79 /** 32bit hack */
80 PDMACEPFILEAIOMGRBLOCKINGEVENT_32BIT_HACK = 0x7fffffff
81} PDMACEPFILEAIOMGRBLOCKINGEVENT;
82
83/**
84 * State of a async I/O manager.
85 */
86typedef struct PDMACEPFILEMGR
87{
88 /** Next Aio manager in the list. */
89 R3PTRTYPE(struct PDMACEPFILEMGR *) pNext;
90 /** Previous Aio manager in the list. */
91 R3PTRTYPE(struct PDMACEPFILEMGR *) pPrev;
92 /** Event semaphore the manager sleeps on when waiting for new requests. */
93 RTSEMEVENT EventSem;
94 /** Flag whether the thread waits in the event semaphore. */
95 volatile bool fWaitingEventSem;
96 /** Flag whether this manager uses the failsafe method. */
97 bool fFailsafe;
98 /** Flag whether the thread is waiting for I/O to complete. */
99 volatile bool fWaitingForIo;
100 /** Thread data */
101 RTTHREAD Thread;
102 /** Flag whether the I/O manager is requested to terminate */
103 volatile bool fShutdown;
104 /** Flag whether the I/O manager was woken up. */
105 volatile bool fWokenUp;
106 /** List of endpoints assigned to this manager. */
107 R3PTRTYPE(PPDMASYNCCOMPLETIONENDPOINTFILE) pEndpointsHead;
108 /** Critical section protecting the blocking event handling. */
109 RTCRITSECT CritSectBlockingEvent;
110 /** Event sempahore for blocking external events.
111 * The caller waits on it until the async I/O manager
112 * finished processing the event. */
113 RTSEMEVENT EventSemBlock;
114 /** Flag whether a blocking event is pending and needs
115 * processing by the I/O manager. */
116 bool fBlockingEventPending;
117 /** Blocking event type */
118 PDMACEPFILEAIOMGRBLOCKINGEVENT enmBlockingEvent;
119 /** Event type data */
120 union
121 {
122 /** Add endpoint event. */
123 struct
124 {
125 /** The endpoint to be added */
126 PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint;
127 } AddEndpoint;
128 /** Remove endpoint event. */
129 struct
130 {
131 /** The endpoint to be added */
132 PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint;
133 } RemoveEndpoint;
134 /** Close endpoint event. */
135 struct
136 {
137 /** The endpoint to be closed */
138 PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint;
139 } CloseEndpoint;
140 } BlockingEventData;
141} PDMACEPFILEMGR;
142/** Pointer to a async I/O manager state. */
143typedef PDMACEPFILEMGR *PPDMACEPFILEMGR;
144/** Pointer to a async I/O manager state pointer. */
145typedef PPDMACEPFILEMGR *PPPDMACEPFILEMGR;
146
147/**
148 * Global data for the file endpoint class.
149 */
150typedef struct PDMASYNCCOMPLETIONEPCLASSFILE
151{
152 /** Common data. */
153 PDMASYNCCOMPLETIONEPCLASS Core;
154 /** Flag whether we use the failsafe method. */
155 bool fFailsafe;
156 /** Critical section protecting the list of async I/O managers. */
157 RTCRITSECT CritSect;
158 /** Pointer to the head of the async I/O managers. */
159 R3PTRTYPE(PPDMACEPFILEMGR) pAioMgrHead;
160 /** Number of async I/O managers currently running. */
161 unsigned cAioMgrs;
162 /** Maximum number of segments to cache per endpoint */
163 unsigned cSegmentsCacheMax;
164} PDMASYNCCOMPLETIONEPCLASSFILE;
165/** Pointer to the endpoint class data. */
166typedef PDMASYNCCOMPLETIONEPCLASSFILE *PPDMASYNCCOMPLETIONEPCLASSFILE;
167
168typedef enum PDMACEPFILEBLOCKINGEVENT
169{
170 /** The invalid event type */
171 PDMACEPFILEBLOCKINGEVENT_INVALID = 0,
172 /** A task is about to be canceled */
173 PDMACEPFILEBLOCKINGEVENT_CANCEL,
174 /** Usual 32bit hack */
175 PDMACEPFILEBLOCKINGEVENT_32BIT_HACK = 0x7fffffff
176} PDMACEPFILEBLOCKINGEVENT;
177
178/**
179 * Data for the file endpoint.
180 */
181typedef struct PDMASYNCCOMPLETIONENDPOINTFILE
182{
183 /** Common data. */
184 PDMASYNCCOMPLETIONENDPOINT Core;
185 /** async I/O manager this endpoint is assigned to. */
186 R3PTRTYPE(PPDMACEPFILEMGR) pAioMgr;
187 /** File handle. */
188 RTFILE File;
189 /** Flag whether caching is enabled for this file. */
190 bool fCaching;
191 /** List of new tasks. */
192 R3PTRTYPE(volatile PPDMASYNCCOMPLETIONTASK) pTasksNewHead;
193
194 /** Head of the small cache for allocated task segments for exclusive
195 * use by this endpoint. */
196 R3PTRTYPE(volatile PPDMACTASKFILESEG) pSegmentsFreeHead;
197 /** Tail of the small cache for allocated task segments for exclusive
198 * use by this endpoint. */
199 R3PTRTYPE(volatile PPDMACTASKFILESEG) pSegmentsFreeTail;
200 /** Number of elements in the cache. */
201 volatile uint32_t cSegmentsCached;
202
203 /** Event sempahore for blocking external events.
204 * The caller waits on it until the async I/O manager
205 * finished processing the event. */
206 RTSEMEVENT EventSemBlock;
207 /** Flag whether a blocking event is pending and needs
208 * processing by the I/O manager. */
209 bool fBlockingEventPending;
210 /** Blocking event type */
211 PDMACEPFILEBLOCKINGEVENT enmBlockingEvent;
212 /** Additional data needed for the event types. */
213 union
214 {
215 /** Cancelation event. */
216 struct
217 {
218 /** The task to cancel. */
219 PPDMASYNCCOMPLETIONTASK pTask;
220 } Cancel;
221 } BlockingEventData;
222 /** Data for exclusive use by the assigned async I/O manager. */
223 struct
224 {
225 /** Pointer to the next endpoint assigned to the manager. */
226 R3PTRTYPE(PPDMASYNCCOMPLETIONENDPOINTFILE) pEndpointNext;
227 /** Pointer to the previous endpoint assigned to the manager. */
228 R3PTRTYPE(PPDMASYNCCOMPLETIONENDPOINTFILE) pEndpointPrev;
229 } AioMgr;
230} PDMASYNCCOMPLETIONENDPOINTFILE;
231/** Pointer to the endpoint class data. */
232typedef PDMASYNCCOMPLETIONENDPOINTFILE *PPDMASYNCCOMPLETIONENDPOINTFILE;
233
234/**
235 * Segment data of a request.
236 */
237typedef struct PDMACTASKFILESEG
238{
239 /** Pointer to the next segment in the list. */
240 R3PTRTYPE(struct PDMACTASKFILESEG *) pNext;
241 /** Pointer to the previous segment in the list. */
242 R3PTRTYPE(struct PDMACTASKFILESEG *) pPrev;
243 /** Data for the filesafe and normal manager. */
244 union
245 {
246 /** AIO request */
247 RTFILEAIOREQ AioReq;
248 /** Data for the failsafe manager. */
249 struct
250 {
251 /** Flag whether this is a re request. False for write */
252 bool fRead;
253 /** Offset to start from */
254 RTFOFF off;
255 /** Size of the transfer */
256 size_t cbTransfer;
257 /** Pointer to the buffer. */
258 void *pvBuf;
259 } Failsafe;
260 } u;
261} PDMACTASKFILESEG;
262
263/**
264 * Per task data.
265 */
266typedef struct PDMASYNCCOMPLETIONTASKFILE
267{
268 /** Common data. */
269 PDMASYNCCOMPLETIONTASK Core;
270 /** Flag whether this is a flush request. */
271 bool fFlush;
272 /** Type dependent data. */
273 union
274 {
275 /** AIO request for the flush. */
276 RTFILEAIOREQ AioReq;
277 /** Data for a data transfer */
278 struct
279 {
280 /** Number of segments which still needs to be processed before the task
281 * completes. */
282 unsigned cSegments;
283 /** Head of the request segments list for read and write requests. */
284 PPDMACTASKFILESEG pSegmentsHead;
285 } DataTransfer;
286 } u;
287} PDMASYNCCOMPLETIONTASKFILE;
288/** Pointer to the endpoint class data. */
289typedef PDMASYNCCOMPLETIONTASKFILE *PPDMASYNCCOMPLETIONTASKFILE;
290
291/**
292 * Frees a task segment
293 *
294 * @returns nothing.
295 * @param pEndpoint Pointer to the endpoint the segment was for.
296 * @param pSeg The segment to free.
297 */
298static void pdmacFileSegmentFree(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint,
299 PPDMACTASKFILESEG pSeg)
300{
301 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClass = (PPDMASYNCCOMPLETIONEPCLASSFILE)pEndpoint->Core.pEpClass;
302
303 LogFlowFunc((": pEndpoint=%p pSeg=%p\n", pEndpoint, pSeg));
304
305 /* Try the per endpoint cache first. */
306 if (pEndpoint->cSegmentsCached < pEpClass->cSegmentsCacheMax)
307 {
308 /* Add it to the list. */
309 pSeg->pPrev = NULL;
310 pEndpoint->pSegmentsFreeTail->pNext = pSeg;
311 pEndpoint->pSegmentsFreeTail = pSeg;
312 ASMAtomicIncU32(&pEndpoint->cSegmentsCached);
313 }
314 else if (false)
315 {
316 /* Bigger class cache */
317 }
318 else
319 {
320 Log(("Freeing segment %p because all caches are full\n", pSeg));
321 MMR3HeapFree(pSeg);
322 }
323}
324
325/**
326 * Allocates a task segment
327 *
328 * @returns Pointer to the new task segment or NULL
329 * @param pEndpoint Pointer to the endpoint
330 */
331static PPDMACTASKFILESEG pdmacFileSegmentAlloc(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint)
332{
333 PPDMACTASKFILESEG pSeg = NULL;
334
335 /* Try the small per endpoint cache first. */
336 if (pEndpoint->pSegmentsFreeHead == pEndpoint->pSegmentsFreeTail)
337 {
338 /* Try the bigger endpoint class cache. */
339 PPDMASYNCCOMPLETIONEPCLASSFILE pEndpointClass = (PPDMASYNCCOMPLETIONEPCLASSFILE)pEndpoint->Core.pEpClass;
340
341#if 0
342 /* We start with the assigned slot id to distribute the load when allocating new tasks. */
343 unsigned iSlot = pEndpoint->iSlotStart;
344 do
345 {
346 pTask = (PPDMASYNCCOMPLETIONTASK)ASMAtomicXchgPtr((void * volatile *)&pEndpointClass->apTaskCache[iSlot], NULL);
347 if (pTask)
348 break;
349
350 iSlot = (iSlot + 1) % RT_ELEMENTS(pEndpointClass->apTaskCache);
351 } while (iSlot != pEndpoint->iSlotStart);
352#endif
353 if (!pSeg)
354 {
355 /*
356 * Allocate completely new.
357 * If this fails we return NULL.
358 */
359 int rc = MMR3HeapAllocZEx(pEndpointClass->Core.pVM, MM_TAG_PDM_ASYNC_COMPLETION,
360 sizeof(PDMACTASKFILESEG),
361 (void **)&pSeg);
362 if (RT_FAILURE(rc))
363 pSeg = NULL;
364
365 LogFlow(("Allocated segment %p\n", pSeg));
366 }
367#if 0
368 else
369 {
370 /* Remove the first element and put the rest into the slot again. */
371 PPDMASYNCCOMPLETIONTASK pTaskHeadNew = pTask->pNext;
372
373 pTaskHeadNew->pPrev = NULL;
374
375 /* Put back into the list adding any new tasks. */
376 while (true)
377 {
378 bool fChanged = ASMAtomicCmpXchgPtr((void * volatile *)&pEndpointClass->apTaskCache[iSlot], pTaskHeadNew, NULL);
379
380 if (fChanged)
381 break;
382
383 PPDMASYNCCOMPLETIONTASK pTaskHead = (PPDMASYNCCOMPLETIONTASK)ASMAtomicXchgPtr((void * volatile *)&pEndpointClass->apTaskCache[iSlot], NULL);
384
385 /* The new task could be taken inbetween */
386 if (pTaskHead)
387 {
388 /* Go to the end of the probably much shorter new list. */
389 PPDMASYNCCOMPLETIONTASK pTaskTail = pTaskHead;
390 while (pTaskTail->pNext)
391 pTaskTail = pTaskTail->pNext;
392
393 /* Concatenate */
394 pTaskTail->pNext = pTaskHeadNew;
395
396 pTaskHeadNew = pTaskHead;
397 }
398 /* Another round trying to change the list. */
399 }
400 /* We got a task from the global cache so decrement the counter */
401 ASMAtomicDecU32(&pEndpointClass->cTasksCached);
402 }
403#endif
404 }
405 else
406 {
407 /* Grab a free task from the head. */
408 AssertMsg(pEndpoint->cSegmentsCached > 0, ("No segments cached but list contains more than one element\n"));
409
410 pSeg = pEndpoint->pSegmentsFreeHead;
411 pEndpoint->pSegmentsFreeHead = pSeg->pNext;
412 ASMAtomicDecU32(&pEndpoint->cSegmentsCached);
413 }
414
415 pSeg->pNext = NULL;
416 pSeg->pPrev = NULL;
417
418 return pSeg;
419}
420
421static PPDMASYNCCOMPLETIONTASK pdmacFileEpGetNewTasks(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint)
422{
423 PPDMASYNCCOMPLETIONTASK pTasks = NULL;
424
425 /*
426 * Get pending tasks.
427 */
428 pTasks = (PPDMASYNCCOMPLETIONTASK)ASMAtomicXchgPtr((void * volatile *)&pEndpoint->pTasksNewHead, NULL);
429
430 /* Reverse the list to process in FIFO order. */
431 if (pTasks)
432 {
433 PPDMASYNCCOMPLETIONTASK pTask = pTasks;
434
435 pTasks = NULL;
436
437 while (pTask)
438 {
439 PPDMASYNCCOMPLETIONTASK pCur = pTask;
440 pTask = pTask->pNext;
441 pCur->pNext = pTasks;
442 pTasks = pCur;
443 }
444 }
445
446 return pTasks;
447}
448
449static int pdmacFileAioMgrFailsafeProcessEndpoint(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint)
450{
451 int rc = VINF_SUCCESS;
452 PPDMASYNCCOMPLETIONTASK pTasks = pdmacFileEpGetNewTasks(pEndpoint);
453
454 while (pTasks)
455 {
456 PPDMASYNCCOMPLETIONTASKFILE pTaskFile = (PPDMASYNCCOMPLETIONTASKFILE)pTasks;
457
458 if (pTasks->pNext)
459 AssertMsg(pTasks->uTaskId < pTasks->pNext->uTaskId,
460 ("The task IDs are not ordered Curr=%u Next=%u\n", pTasks->uTaskId, pTasks->pNext->uTaskId));
461
462 if (pTaskFile->fFlush)
463 {
464 rc = RTFileFlush(pEndpoint->File);
465 }
466 else
467 {
468 PPDMACTASKFILESEG pSeg = pTaskFile->u.DataTransfer.pSegmentsHead;
469
470 while(pSeg)
471 {
472 if (pSeg->u.Failsafe.fRead)
473 {
474 rc = RTFileReadAt(pEndpoint->File, pSeg->u.Failsafe.off,
475 pSeg->u.Failsafe.pvBuf,
476 pSeg->u.Failsafe.cbTransfer,
477 NULL);
478 }
479 else
480 {
481 rc = RTFileWriteAt(pEndpoint->File, pSeg->u.Failsafe.off,
482 pSeg->u.Failsafe.pvBuf,
483 pSeg->u.Failsafe.cbTransfer,
484 NULL);
485 }
486
487 /* Free the segment. */
488 PPDMACTASKFILESEG pCur = pSeg;
489 pSeg = pSeg->pNext;
490
491 pdmacFileSegmentFree(pEndpoint, pCur);
492 }
493 }
494
495 AssertRC(rc);
496 pTasks = pTasks->pNext;
497
498 /* Notify task owner */
499 pdmR3AsyncCompletionCompleteTask(&pTaskFile->Core);
500 }
501
502 return rc;
503}
504
505/**
506 * A fallback method in case something goes wrong with the normal
507 * I/O manager.
508 */
509static int pdmacFileAioMgrFailsafe(RTTHREAD ThreadSelf, void *pvUser)
510{
511 int rc = VINF_SUCCESS;
512 PPDMACEPFILEMGR pAioMgr = (PPDMACEPFILEMGR)pvUser;
513
514 while (!pAioMgr->fShutdown)
515 {
516 if (!ASMAtomicReadBool(&pAioMgr->fWokenUp))
517 {
518 ASMAtomicWriteBool(&pAioMgr->fWaitingEventSem, true);
519 rc = RTSemEventWait(pAioMgr->EventSem, RT_INDEFINITE_WAIT);
520 ASMAtomicWriteBool(&pAioMgr->fWaitingEventSem, false);
521 AssertRC(rc);
522 }
523 ASMAtomicXchgBool(&pAioMgr->fWokenUp, false);
524
525 /* Process endpoint events first. */
526 PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint = pAioMgr->pEndpointsHead;
527 while (pEndpoint)
528 {
529 rc = pdmacFileAioMgrFailsafeProcessEndpoint(pEndpoint);
530 AssertRC(rc);
531 pEndpoint = pEndpoint->AioMgr.pEndpointNext;
532 }
533
534 /* Now check for an external blocking event. */
535 if (pAioMgr->fBlockingEventPending)
536 {
537 switch (pAioMgr->enmBlockingEvent)
538 {
539 case PDMACEPFILEAIOMGRBLOCKINGEVENT_ADD_ENDPOINT:
540 {
541 PPDMASYNCCOMPLETIONENDPOINTFILE pEndpointNew = pAioMgr->BlockingEventData.AddEndpoint.pEndpoint;
542 AssertMsg(VALID_PTR(pEndpointNew), ("Adding endpoint event without a endpoint to add\n"));
543
544 pEndpointNew->AioMgr.pEndpointNext = pAioMgr->pEndpointsHead;
545 pEndpointNew->AioMgr.pEndpointPrev = NULL;
546 if (pAioMgr->pEndpointsHead)
547 pAioMgr->pEndpointsHead->AioMgr.pEndpointPrev = pEndpointNew;
548 pAioMgr->pEndpointsHead = pEndpointNew;
549 break;
550 }
551 case PDMACEPFILEAIOMGRBLOCKINGEVENT_REMOVE_ENDPOINT:
552 {
553 PPDMASYNCCOMPLETIONENDPOINTFILE pEndpointRemove = pAioMgr->BlockingEventData.RemoveEndpoint.pEndpoint;
554 AssertMsg(VALID_PTR(pEndpointRemove), ("Removing endpoint event without a endpoint to remove\n"));
555
556 PPDMASYNCCOMPLETIONENDPOINTFILE pPrev = pEndpointRemove->AioMgr.pEndpointPrev;
557 PPDMASYNCCOMPLETIONENDPOINTFILE pNext = pEndpointRemove->AioMgr.pEndpointNext;
558
559 if (pPrev)
560 pPrev->AioMgr.pEndpointNext = pNext;
561 else
562 pAioMgr->pEndpointsHead = pNext;
563
564 if (pNext)
565 pNext->AioMgr.pEndpointPrev = pPrev;
566 break;
567 }
568 case PDMACEPFILEAIOMGRBLOCKINGEVENT_CLOSE_ENDPOINT:
569 {
570 PPDMASYNCCOMPLETIONENDPOINTFILE pEndpointClose = pAioMgr->BlockingEventData.CloseEndpoint.pEndpoint;
571 AssertMsg(VALID_PTR(pEndpointClose), ("Close endpoint event without a endpoint to Close\n"));
572
573 /* Make sure all tasks finished. */
574 rc = pdmacFileAioMgrFailsafeProcessEndpoint(pEndpointClose);
575 AssertRC(rc);
576 }
577 case PDMACEPFILEAIOMGRBLOCKINGEVENT_SHUTDOWN:
578 break;
579 case PDMACEPFILEAIOMGRBLOCKINGEVENT_SUSPEND:
580 break;
581 default:
582 AssertMsgFailed(("Invalid event type %d\n", pAioMgr->enmBlockingEvent));
583 }
584
585 /* Release the waiting thread. */
586 rc = RTSemEventSignal(pAioMgr->EventSemBlock);
587 AssertRC(rc);
588 }
589 }
590
591 return rc;
592}
593
594static int pdmacFileAioMgrNormal(RTTHREAD ThreadSelf, void *pvUser)
595{
596 AssertMsgFailed(("Implement\n"));
597 return VERR_NOT_IMPLEMENTED;
598}
599
600static void pdmacFileAioMgrWakeup(PPDMACEPFILEMGR pAioMgr)
601{
602 bool fWokenUp = ASMAtomicXchgBool(&pAioMgr->fWokenUp, true);
603
604 if (!fWokenUp)
605 {
606 int rc = VINF_SUCCESS;
607 bool fWaitingEventSem = ASMAtomicReadBool(&pAioMgr->fWaitingEventSem);
608 bool fWaitingForIo = ASMAtomicReadBool(&pAioMgr->fWaitingForIo);
609
610 if (fWaitingEventSem)
611 rc = RTSemEventSignal(pAioMgr->EventSem);
612#if 0 /** @todo When RTFileAio* is used */
613 else if (fWaitingForIo)
614 rc = RTThreadPoke(pAioMgr->Thread);
615#endif
616
617 AssertRC(rc);
618 }
619}
620
621static int pdmacFileAioMgrWaitForBlockingEvent(PPDMACEPFILEMGR pAioMgr, PDMACEPFILEAIOMGRBLOCKINGEVENT enmEvent)
622{
623 int rc = VINF_SUCCESS;
624
625 pAioMgr->enmBlockingEvent = enmEvent;
626 pAioMgr->fBlockingEventPending = true;
627
628 /* Wakeup the async I/O manager */
629 pdmacFileAioMgrWakeup(pAioMgr);
630
631 /* Wait for completion. */
632 rc = RTSemEventWait(pAioMgr->EventSemBlock, RT_INDEFINITE_WAIT);
633 AssertRC(rc);
634
635 pAioMgr->fBlockingEventPending = false;
636
637 return rc;
638}
639
640static int pdmacFileAioMgrAddEndpoint(PPDMACEPFILEMGR pAioMgr, PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint)
641{
642 int rc;
643
644 rc = RTCritSectEnter(&pAioMgr->CritSectBlockingEvent);
645 AssertRCReturn(rc, rc);
646
647 pAioMgr->BlockingEventData.AddEndpoint.pEndpoint = pEndpoint;
648 rc = pdmacFileAioMgrWaitForBlockingEvent(pAioMgr, PDMACEPFILEAIOMGRBLOCKINGEVENT_ADD_ENDPOINT);
649
650 RTCritSectLeave(&pAioMgr->CritSectBlockingEvent);
651
652 return rc;
653}
654
655static int pdmacFileAioMgrRemoveEndpoint(PPDMACEPFILEMGR pAioMgr, PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint)
656{
657 int rc;
658
659 rc = RTCritSectEnter(&pAioMgr->CritSectBlockingEvent);
660 AssertRCReturn(rc, rc);
661
662 pAioMgr->BlockingEventData.RemoveEndpoint.pEndpoint = pEndpoint;
663 rc = pdmacFileAioMgrWaitForBlockingEvent(pAioMgr, PDMACEPFILEAIOMGRBLOCKINGEVENT_REMOVE_ENDPOINT);
664
665 RTCritSectLeave(&pAioMgr->CritSectBlockingEvent);
666
667 return rc;
668}
669
670static int pdmacFileAioMgrCloseEndpoint(PPDMACEPFILEMGR pAioMgr, PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint)
671{
672 int rc;
673
674 rc = RTCritSectEnter(&pAioMgr->CritSectBlockingEvent);
675 AssertRCReturn(rc, rc);
676
677 pAioMgr->BlockingEventData.CloseEndpoint.pEndpoint = pEndpoint;
678 rc = pdmacFileAioMgrWaitForBlockingEvent(pAioMgr, PDMACEPFILEAIOMGRBLOCKINGEVENT_CLOSE_ENDPOINT);
679
680 RTCritSectLeave(&pAioMgr->CritSectBlockingEvent);
681
682 return rc;
683}
684
685static int pdmacFileAioMgrShutdown(PPDMACEPFILEMGR pAioMgr)
686{
687 int rc;
688
689 ASMAtomicXchgBool(&pAioMgr->fShutdown, true);
690
691 rc = RTCritSectEnter(&pAioMgr->CritSectBlockingEvent);
692 AssertRCReturn(rc, rc);
693
694 rc = pdmacFileAioMgrWaitForBlockingEvent(pAioMgr, PDMACEPFILEAIOMGRBLOCKINGEVENT_SHUTDOWN);
695
696 RTCritSectLeave(&pAioMgr->CritSectBlockingEvent);
697
698 return rc;
699}
700
701static int pdmacFileEpAddTask(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint, PPDMASYNCCOMPLETIONTASKFILE pTask)
702{
703 PPDMASYNCCOMPLETIONTASK pNext;
704 do
705 {
706 pNext = pEndpoint->pTasksNewHead;
707 pTask->Core.pNext = pNext;
708 } while (!ASMAtomicCmpXchgPtr((void * volatile *)&pEndpoint->pTasksNewHead, (void *)pTask, (void *)pNext));
709
710 pdmacFileAioMgrWakeup(pEndpoint->pAioMgr);
711
712 return VINF_SUCCESS;
713}
714
715static int pdmacFileEpTaskInitiate(PPDMASYNCCOMPLETIONTASK pTask,
716 PPDMASYNCCOMPLETIONENDPOINT pEndpoint, RTFOFF off,
717 PCPDMDATASEG paSegments, size_t cSegments,
718 size_t cbTransfer, bool fRead)
719{
720 int rc = VINF_SUCCESS;
721 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
722 PPDMASYNCCOMPLETIONTASKFILE pTaskFile = (PPDMASYNCCOMPLETIONTASKFILE)pTask;
723 PPDMACEPFILEMGR pAioMgr = pEpFile->pAioMgr;
724
725 Assert(pAioMgr->fFailsafe); /** @todo change */
726
727 pTaskFile->u.DataTransfer.cSegments = cSegments;
728 pTaskFile->u.DataTransfer.pSegmentsHead = NULL;
729
730 PPDMACTASKFILESEG pSeg = pdmacFileSegmentAlloc(pEpFile);
731
732 pTaskFile->u.DataTransfer.pSegmentsHead = pSeg;
733
734 for (unsigned i = 0; i < cSegments; i++)
735 {
736 pSeg->u.Failsafe.fRead = fRead;
737 pSeg->u.Failsafe.off = off;
738 pSeg->u.Failsafe.cbTransfer = paSegments[i].cbSeg;
739 pSeg->u.Failsafe.pvBuf = paSegments[i].pvSeg;
740
741 off += paSegments[i].cbSeg;
742 cbTransfer -= paSegments[i].cbSeg;
743
744 if (i < (cSegments-1))
745 {
746 /* Allocate new segment. */
747 PPDMACTASKFILESEG pSegNext = pdmacFileSegmentAlloc(pEpFile);
748 AssertPtr(pSeg);
749 pSeg->pNext = pSegNext;
750 pSeg = pSegNext;
751 }
752 }
753
754 AssertMsg(!cbTransfer, ("Incomplete task cbTransfer=%u\n", cbTransfer));
755
756 /* Send it off */
757 pdmacFileEpAddTask(pEpFile, pTaskFile);
758
759 return VINF_SUCCESS;
760}
761
762/**
763 * Creates a new async I/O manager.
764 *
765 * @returns VBox status code.
766 * @param pEpClass Pointer to the endpoint class data.
767 * @param ppAioMgr Where to store the pointer to the new async I/O manager on success.
768 */
769static int pdmacFileAioMgrCreate(PPDMASYNCCOMPLETIONEPCLASSFILE pEpClass, PPPDMACEPFILEMGR ppAioMgr)
770{
771 int rc = VINF_SUCCESS;
772 PPDMACEPFILEMGR pAioMgrNew;
773
774 LogFlowFunc((": Entered\n"));
775
776 rc = MMR3HeapAllocZEx(pEpClass->Core.pVM, MM_TAG_PDM_ASYNC_COMPLETION, sizeof(PDMACEPFILEMGR), (void **)&pAioMgrNew);
777 if (RT_SUCCESS(rc))
778 {
779 pAioMgrNew->fFailsafe = pEpClass->fFailsafe;
780
781 rc = RTSemEventCreate(&pAioMgrNew->EventSem);
782 if (RT_SUCCESS(rc))
783 {
784 rc = RTSemEventCreate(&pAioMgrNew->EventSemBlock);
785 if (RT_SUCCESS(rc))
786 {
787 rc = RTCritSectInit(&pAioMgrNew->CritSectBlockingEvent);
788 if (RT_SUCCESS(rc))
789 {
790 rc = RTThreadCreateF(&pAioMgrNew->Thread,
791 pAioMgrNew->fFailsafe
792 ? pdmacFileAioMgrFailsafe
793 : pdmacFileAioMgrNormal,
794 pAioMgrNew,
795 0,
796 RTTHREADTYPE_IO,
797 0,
798 "AioMgr%d-%s", pEpClass->cAioMgrs,
799 pEpClass->fFailsafe
800 ? "F"
801 : "N");
802 if (RT_SUCCESS(rc))
803 {
804 /* Link it into the list. */
805 RTCritSectEnter(&pEpClass->CritSect);
806 pAioMgrNew->pNext = pEpClass->pAioMgrHead;
807 if (pEpClass->pAioMgrHead)
808 pEpClass->pAioMgrHead->pPrev = pAioMgrNew;
809 pEpClass->pAioMgrHead = pAioMgrNew;
810 pEpClass->cAioMgrs++;
811 RTCritSectLeave(&pEpClass->CritSect);
812
813 *ppAioMgr = pAioMgrNew;
814
815 Log(("PDMAC: Successfully created new file AIO Mgr {%s}\n", RTThreadGetName(pAioMgrNew->Thread)));
816 return VINF_SUCCESS;
817 }
818 RTCritSectDelete(&pAioMgrNew->CritSectBlockingEvent);
819 }
820 RTSemEventDestroy(pAioMgrNew->EventSem);
821 }
822 RTSemEventDestroy(pAioMgrNew->EventSemBlock);
823 }
824 MMR3HeapFree(pAioMgrNew);
825 }
826
827 LogFlowFunc((": Leave rc=%Rrc\n", rc));
828
829 return rc;
830}
831
832/**
833 * Destroys a async I/O manager.
834 *
835 * @returns nothing.
836 * @param pAioMgr The async I/O manager to destroy.
837 */
838static void pdmacFileAioMgrDestroy(PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile, PPDMACEPFILEMGR pAioMgr)
839{
840 /* A normal manager may have still endpoints attached and has to return them. */
841 Assert(pAioMgr->fFailsafe);
842 int rc = pdmacFileAioMgrShutdown(pAioMgr);
843 AssertRC(rc);
844
845 /* Unlink from the list. */
846 rc = RTCritSectEnter(&pEpClassFile->CritSect);
847 AssertRC(rc);
848
849 PPDMACEPFILEMGR pPrev = pAioMgr->pPrev;
850 PPDMACEPFILEMGR pNext = pAioMgr->pNext;
851
852 if (pPrev)
853 pPrev->pNext = pNext;
854 else
855 pEpClassFile->pAioMgrHead = pNext;
856
857 if (pNext)
858 pNext->pPrev = pPrev;
859
860 pEpClassFile->cAioMgrs--;
861
862 rc = RTCritSectLeave(&pEpClassFile->CritSect);
863 AssertRC(rc);
864
865 /* Free the ressources. */
866 RTCritSectDelete(&pAioMgr->CritSectBlockingEvent);
867 RTSemEventDestroy(pAioMgr->EventSem);
868 MMR3HeapFree(pAioMgr);
869}
870
871static int pdmacFileInitialize(PPDMASYNCCOMPLETIONEPCLASS pClassGlobals, PCFGMNODE pCfgNode)
872{
873 int rc = VINF_SUCCESS;
874 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pClassGlobals;
875
876 /** @todo: Change when the RTFileAio* API is used */
877 pEpClassFile->fFailsafe = true;
878
879 /* Init critical section. */
880 rc = RTCritSectInit(&pEpClassFile->CritSect);
881 return rc;
882}
883
884static void pdmacFileTerminate(PPDMASYNCCOMPLETIONEPCLASS pClassGlobals)
885{
886 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pClassGlobals;
887
888 /* All endpoints should be closed at this point. */
889 AssertMsg(!pEpClassFile->Core.pEndpointsHead, ("There are still endpoints left\n"));
890
891 /* Destroy all left async I/O managers. */
892 while (pEpClassFile->pAioMgrHead)
893 pdmacFileAioMgrDestroy(pEpClassFile, pEpClassFile->pAioMgrHead);
894
895 RTCritSectDelete(&pEpClassFile->CritSect);
896}
897
898static int pdmacFileEpInitialize(PPDMASYNCCOMPLETIONENDPOINT pEndpoint,
899 const char *pszUri, uint32_t fFlags)
900{
901 int rc = VINF_SUCCESS;
902 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
903 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pEndpoint->pEpClass;
904
905 unsigned fFileFlags = fFlags & PDMACEP_FILE_FLAGS_READ_ONLY
906 ? RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE
907 : RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE;
908
909 if (!pEpClassFile->fFailsafe)
910 fFlags |= RTFILE_O_ASYNC_IO;
911
912 rc = RTFileOpen(&pEpFile->File, pszUri, fFileFlags);
913 if (RT_SUCCESS(rc))
914 {
915 /* Initialize the cache */
916 rc = MMR3HeapAllocZEx(pEpClassFile->Core.pVM, MM_TAG_PDM_ASYNC_COMPLETION,
917 sizeof(PDMACTASKFILESEG),
918 (void **)&pEpFile->pSegmentsFreeHead);
919 if (RT_SUCCESS(rc))
920 {
921 /** @todo Check caching flag. */
922 PPDMACEPFILEMGR pAioMgr = NULL;
923
924 pEpFile->pSegmentsFreeTail = pEpFile->pSegmentsFreeHead;
925 pEpFile->cSegmentsCached = 0;
926
927 if (pEpClassFile->fFailsafe)
928 {
929 /* Safe mode. Every file has its own async I/O manager. */
930 rc = pdmacFileAioMgrCreate(pEpClassFile, &pAioMgr);
931 AssertRC(rc);
932 }
933 else
934 {
935 /* Check for an idling one or create new if not found */
936 AssertMsgFailed(("Implement\n"));
937 }
938
939 /* Assign the endpoint to the thread. */
940 pEpFile->pAioMgr = pAioMgr;
941 rc = pdmacFileAioMgrAddEndpoint(pAioMgr, pEpFile);
942 }
943
944 if (RT_FAILURE(rc))
945 RTFileClose(pEpFile->File);
946 }
947
948 return rc;
949}
950
951static int pdmacFileEpClose(PPDMASYNCCOMPLETIONENDPOINT pEndpoint)
952{
953 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
954 PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pEndpoint->pEpClass;
955
956 /* Make sure that all tasks finished for this endpoint. */
957 int rc = pdmacFileAioMgrCloseEndpoint(pEpFile->pAioMgr, pEpFile);
958 AssertRC(rc);
959
960 /* Remove the endpoint from the thread. */
961 rc = pdmacFileAioMgrRemoveEndpoint(pEpFile->pAioMgr, pEpFile);
962 AssertRC(rc);
963
964 /*
965 * If the async I/O manager is in failsafe mode this is the only endpoint
966 * he processes and thus can be destroyed now.
967 */
968 if (pEpFile->pAioMgr->fFailsafe)
969 pdmacFileAioMgrDestroy(pEpClassFile, pEpFile->pAioMgr);
970
971 /* Free cached segments. */
972 PPDMACTASKFILESEG pSeg = pEpFile->pSegmentsFreeHead;
973
974 while (pSeg)
975 {
976 PPDMACTASKFILESEG pSegFree = pSeg;
977 pSeg = pSeg->pNext;
978 MMR3HeapFree(pSegFree);
979 }
980
981 /* Free the cached data. */
982 Assert(!pEpFile->fCaching);
983
984 return VINF_SUCCESS;
985}
986
987static int pdmacFileEpRead(PPDMASYNCCOMPLETIONTASK pTask,
988 PPDMASYNCCOMPLETIONENDPOINT pEndpoint, RTFOFF off,
989 PCPDMDATASEG paSegments, size_t cSegments,
990 size_t cbRead)
991{
992 return pdmacFileEpTaskInitiate(pTask, pEndpoint, off, paSegments, cSegments, cbRead, true);
993}
994
995static int pdmacFileEpWrite(PPDMASYNCCOMPLETIONTASK pTask,
996 PPDMASYNCCOMPLETIONENDPOINT pEndpoint, RTFOFF off,
997 PCPDMDATASEG paSegments, size_t cSegments,
998 size_t cbWrite)
999{
1000 return pdmacFileEpTaskInitiate(pTask, pEndpoint, off, paSegments, cSegments, cbWrite, false);
1001}
1002
1003static int pdmacFileEpFlush(PPDMASYNCCOMPLETIONTASK pTask,
1004 PPDMASYNCCOMPLETIONENDPOINT pEndpoint)
1005{
1006 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1007 PPDMASYNCCOMPLETIONTASKFILE pTaskFile = (PPDMASYNCCOMPLETIONTASKFILE)pTask;
1008
1009 pTaskFile->fFlush = true;
1010 pdmacFileEpAddTask(pEpFile, pTaskFile);
1011
1012 return VINF_SUCCESS;
1013}
1014
1015static int pdmacFileEpGetSize(PPDMASYNCCOMPLETIONENDPOINT pEndpoint, uint64_t *pcbSize)
1016{
1017 PPDMASYNCCOMPLETIONENDPOINTFILE pEpFile = (PPDMASYNCCOMPLETIONENDPOINTFILE)pEndpoint;
1018
1019 return RTFileGetSize(pEpFile->File, pcbSize);
1020}
1021
1022const PDMASYNCCOMPLETIONEPCLASSOPS g_PDMAsyncCompletionEndpointClassFile =
1023{
1024 /* u32Version */
1025 PDMAC_EPCLASS_OPS_VERSION,
1026 /* pcszName */
1027 "File",
1028 /* enmClassType */
1029 PDMASYNCCOMPLETIONEPCLASSTYPE_FILE,
1030 /* cbEndpointClassGlobal */
1031 sizeof(PDMASYNCCOMPLETIONEPCLASSFILE),
1032 /* cbEndpoint */
1033 sizeof(PDMASYNCCOMPLETIONENDPOINTFILE),
1034 /* cbTask */
1035 sizeof(PDMASYNCCOMPLETIONTASKFILE),
1036 /* pfnInitialize */
1037 pdmacFileInitialize,
1038 /* pfnTerminate */
1039 pdmacFileTerminate,
1040 /* pfnEpInitialize. */
1041 pdmacFileEpInitialize,
1042 /* pfnEpClose */
1043 pdmacFileEpClose,
1044 /* pfnEpRead */
1045 pdmacFileEpRead,
1046 /* pfnEpWrite */
1047 pdmacFileEpWrite,
1048 /* pfnEpFlush */
1049 pdmacFileEpFlush,
1050 /* pfnEpGetSize */
1051 pdmacFileEpGetSize,
1052 /* u32VersionEnd */
1053 PDMAC_EPCLASS_OPS_VERSION
1054};
1055
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