PDMAsyncCompletionFile.cpp revision 5341459ca931b65de60b5af2a4cba6836b6b45ca
/* $Id$ */
/** @file
* PDM Async I/O - Transport data asynchronous in R3 using EMT.
*/
/*
* Copyright (C) 2006-2008 Sun Microsystems, Inc.
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include "PDMInternal.h"
#include <iprt/critsect.h>
#include <iprt/semaphore.h>
#include "PDMAsyncCompletionInternal.h"
/** @todo: Revise the caching of tasks. We have currently four caches:
* Per endpoint task cache
* Per class cache
* Per endpoint task segment cache
* Per class task segment cache
*
* We could use the RT heap for this probably or extend MMR3Heap (uses RTMemAlloc
* instead of managing larger blocks) to have this global for the whole VM.
*/
/**
* A few forward declerations.
*/
typedef struct PDMASYNCCOMPLETIONENDPOINTFILE *PPDMASYNCCOMPLETIONENDPOINTFILE;
/** Pointer to a request segment. */
typedef struct PDMACTASKFILESEG *PPDMACTASKFILESEG;
/**
* Blocking event types.
*/
typedef enum PDMACEPFILEAIOMGRBLOCKINGEVENT
{
/** Invalid tye */
/** An endpoint is added to the manager. */
/** An endpoint is removed from the manager. */
/** An endpoint is about to be closed. */
/** The manager is requested to terminate */
/** The manager is requested to suspend */
/** 32bit hack */
PDMACEPFILEAIOMGRBLOCKINGEVENT_32BIT_HACK = 0x7fffffff
/**
* State of a async I/O manager.
*/
typedef struct PDMACEPFILEMGR
{
/** Next Aio manager in the list. */
/** Previous Aio manager in the list. */
/** Event semaphore the manager sleeps on when waiting for new requests. */
/** Flag whether the thread waits in the event semaphore. */
volatile bool fWaitingEventSem;
/** Flag whether this manager uses the failsafe method. */
bool fFailsafe;
/** Flag whether the thread is waiting for I/O to complete. */
volatile bool fWaitingForIo;
/** Thread data */
/** Flag whether the I/O manager is requested to terminate */
volatile bool fShutdown;
/** Flag whether the I/O manager was woken up. */
volatile bool fWokenUp;
/** List of endpoints assigned to this manager. */
/** Critical section protecting the blocking event handling. */
/** Event sempahore for blocking external events.
* The caller waits on it until the async I/O manager
* finished processing the event. */
/** Flag whether a blocking event is pending and needs
* processing by the I/O manager. */
bool fBlockingEventPending;
/** Blocking event type */
/** Event type data */
union
{
/** Add endpoint event. */
struct
{
/** The endpoint to be added */
} AddEndpoint;
/** Remove endpoint event. */
struct
{
/** The endpoint to be added */
/** Close endpoint event. */
struct
{
/** The endpoint to be closed */
/** Pointer to a async I/O manager state. */
typedef PDMACEPFILEMGR *PPDMACEPFILEMGR;
/** Pointer to a async I/O manager state pointer. */
typedef PPDMACEPFILEMGR *PPPDMACEPFILEMGR;
/**
* Global data for the file endpoint class.
*/
typedef struct PDMASYNCCOMPLETIONEPCLASSFILE
{
/** Common data. */
/** Flag whether we use the failsafe method. */
bool fFailsafe;
/** Critical section protecting the list of async I/O managers. */
/** Pointer to the head of the async I/O managers. */
/** Number of async I/O managers currently running. */
unsigned cAioMgrs;
/** Maximum number of segments to cache per endpoint */
unsigned cSegmentsCacheMax;
/** Pointer to the endpoint class data. */
typedef enum PDMACEPFILEBLOCKINGEVENT
{
/** The invalid event type */
/** A task is about to be canceled */
/** Usual 32bit hack */
PDMACEPFILEBLOCKINGEVENT_32BIT_HACK = 0x7fffffff
/**
* Data for the file endpoint.
*/
typedef struct PDMASYNCCOMPLETIONENDPOINTFILE
{
/** Common data. */
/** async I/O manager this endpoint is assigned to. */
/** File handle. */
/** Flag whether caching is enabled for this file. */
bool fCaching;
/** List of new tasks. */
/** Head of the small cache for allocated task segments for exclusive
* use by this endpoint. */
/** Tail of the small cache for allocated task segments for exclusive
* use by this endpoint. */
/** Number of elements in the cache. */
volatile uint32_t cSegmentsCached;
/** Event sempahore for blocking external events.
* The caller waits on it until the async I/O manager
* finished processing the event. */
/** Flag whether a blocking event is pending and needs
* processing by the I/O manager. */
bool fBlockingEventPending;
/** Blocking event type */
/** Additional data needed for the event types. */
union
{
/** Cancelation event. */
struct
{
/** The task to cancel. */
} Cancel;
/** Data for exclusive use by the assigned async I/O manager. */
struct
{
/** Pointer to the next endpoint assigned to the manager. */
/** Pointer to the previous endpoint assigned to the manager. */
} AioMgr;
/** Pointer to the endpoint class data. */
/**
* Segment data of a request.
*/
typedef struct PDMACTASKFILESEG
{
/** Pointer to the next segment in the list. */
/** Pointer to the previous segment in the list. */
/** Data for the filesafe and normal manager. */
union
{
/** AIO request */
/** Data for the failsafe manager. */
struct
{
/** Flag whether this is a re request. False for write */
bool fRead;
/** Offset to start from */
/** Size of the transfer */
/** Pointer to the buffer. */
void *pvBuf;
} Failsafe;
} u;
/**
* Per task data.
*/
typedef struct PDMASYNCCOMPLETIONTASKFILE
{
/** Common data. */
/** Flag whether this is a flush request. */
bool fFlush;
/** Type dependent data. */
union
{
/** AIO request for the flush. */
/** Data for a data transfer */
struct
{
/** Number of segments which still needs to be processed before the task
* completes. */
unsigned cSegments;
/** Head of the request segments list for read and write requests. */
} DataTransfer;
} u;
/** Pointer to the endpoint class data. */
/**
* Frees a task segment
*
* @returns nothing.
* @param pEndpoint Pointer to the endpoint the segment was for.
* @param pSeg The segment to free.
*/
{
/* Try the per endpoint cache first. */
{
/* Add it to the list. */
}
else if (false)
{
/* Bigger class cache */
}
else
{
}
}
/**
* Allocates a task segment
*
* @returns Pointer to the new task segment or NULL
* @param pEndpoint Pointer to the endpoint
*/
{
/* Try the small per endpoint cache first. */
{
/* Try the bigger endpoint class cache. */
PPDMASYNCCOMPLETIONEPCLASSFILE pEndpointClass = (PPDMASYNCCOMPLETIONEPCLASSFILE)pEndpoint->Core.pEpClass;
#if 0
/* We start with the assigned slot id to distribute the load when allocating new tasks. */
do
{
pTask = (PPDMASYNCCOMPLETIONTASK)ASMAtomicXchgPtr((void * volatile *)&pEndpointClass->apTaskCache[iSlot], NULL);
if (pTask)
break;
#endif
if (!pSeg)
{
/*
* Allocate completely new.
* If this fails we return NULL.
*/
sizeof(PDMACTASKFILESEG),
(void **)&pSeg);
if (RT_FAILURE(rc))
}
#if 0
else
{
/* Remove the first element and put the rest into the slot again. */
/* Put back into the list adding any new tasks. */
while (true)
{
bool fChanged = ASMAtomicCmpXchgPtr((void * volatile *)&pEndpointClass->apTaskCache[iSlot], pTaskHeadNew, NULL);
if (fChanged)
break;
PPDMASYNCCOMPLETIONTASK pTaskHead = (PPDMASYNCCOMPLETIONTASK)ASMAtomicXchgPtr((void * volatile *)&pEndpointClass->apTaskCache[iSlot], NULL);
/* The new task could be taken inbetween */
if (pTaskHead)
{
/* Go to the end of the probably much shorter new list. */
/* Concatenate */
}
/* Another round trying to change the list. */
}
/* We got a task from the global cache so decrement the counter */
}
#endif
}
else
{
/* Grab a free task from the head. */
AssertMsg(pEndpoint->cSegmentsCached > 0, ("No segments cached but list contains more than one element\n"));
}
return pSeg;
}
{
/*
* Get pending tasks.
*/
pTasks = (PPDMASYNCCOMPLETIONTASK)ASMAtomicXchgPtr((void * volatile *)&pEndpoint->pTasksNewHead, NULL);
/* Reverse the list to process in FIFO order. */
if (pTasks)
{
while (pTask)
{
}
}
return pTasks;
}
{
int rc = VINF_SUCCESS;
while (pTasks)
{
{
}
else
{
while(pSeg)
{
{
NULL);
}
else
{
NULL);
}
/* Free the segment. */
}
}
/* Notify task owner */
}
return rc;
}
/**
* A fallback method in case something goes wrong with the normal
* I/O manager.
*/
{
int rc = VINF_SUCCESS;
{
{
}
/* Process endpoint events first. */
while (pEndpoint)
{
}
/* Now check for an external blocking event. */
if (pAioMgr->fBlockingEventPending)
{
switch (pAioMgr->enmBlockingEvent)
{
{
if (pAioMgr->pEndpointsHead)
break;
}
{
PPDMASYNCCOMPLETIONENDPOINTFILE pEndpointRemove = pAioMgr->BlockingEventData.RemoveEndpoint.pEndpoint;
if (pPrev)
else
if (pNext)
break;
}
{
PPDMASYNCCOMPLETIONENDPOINTFILE pEndpointClose = pAioMgr->BlockingEventData.CloseEndpoint.pEndpoint;
/* Make sure all tasks finished. */
}
break;
break;
default:
}
/* Release the waiting thread. */
}
}
return rc;
}
{
AssertMsgFailed(("Implement\n"));
return VERR_NOT_IMPLEMENTED;
}
{
if (!fWokenUp)
{
int rc = VINF_SUCCESS;
if (fWaitingEventSem)
else if (fWaitingForIo)
}
}
static int pdmacFileAioMgrWaitForBlockingEvent(PPDMACEPFILEMGR pAioMgr, PDMACEPFILEAIOMGRBLOCKINGEVENT enmEvent)
{
int rc = VINF_SUCCESS;
pAioMgr->fBlockingEventPending = true;
/* Wakeup the async I/O manager */
/* Wait for completion. */
pAioMgr->fBlockingEventPending = false;
return rc;
}
static int pdmacFileAioMgrAddEndpoint(PPDMACEPFILEMGR pAioMgr, PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint)
{
int rc;
return rc;
}
static int pdmacFileAioMgrRemoveEndpoint(PPDMACEPFILEMGR pAioMgr, PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint)
{
int rc;
return rc;
}
static int pdmacFileAioMgrCloseEndpoint(PPDMACEPFILEMGR pAioMgr, PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint)
{
int rc;
return rc;
}
{
int rc;
return rc;
}
static int pdmacFileEpAddTask(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint, PPDMASYNCCOMPLETIONTASKFILE pTask)
{
do
{
} while (!ASMAtomicCmpXchgPtr((void * volatile *)&pEndpoint->pTasksNewHead, (void *)pTask, (void *)pNext));
return VINF_SUCCESS;
}
{
int rc = VINF_SUCCESS;
for (unsigned i = 0; i < cSegments; i++)
{
if (i < (cSegments-1))
{
/* Allocate new segment. */
}
}
/* Send it off */
return VINF_SUCCESS;
}
/**
* Creates a new async I/O manager.
*
* @returns VBox status code.
* @param pEpClass Pointer to the endpoint class data.
* @param ppAioMgr Where to store the pointer to the new async I/O manager on success.
*/
static int pdmacFileAioMgrCreate(PPDMASYNCCOMPLETIONEPCLASSFILE pEpClass, PPPDMACEPFILEMGR ppAioMgr)
{
int rc = VINF_SUCCESS;
LogFlowFunc((": Entered\n"));
rc = MMR3HeapAllocZEx(pEpClass->Core.pVM, MM_TAG_PDM_ASYNC_COMPLETION, sizeof(PDMACEPFILEMGR), (void **)&pAioMgrNew);
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
0,
0,
? "F"
: "N");
if (RT_SUCCESS(rc))
{
/* Link it into the list. */
if (pEpClass->pAioMgrHead)
*ppAioMgr = pAioMgrNew;
return VINF_SUCCESS;
}
}
}
}
}
return rc;
}
/**
* Destroys a async I/O manager.
*
* @returns nothing.
* @param pAioMgr The async I/O manager to destroy.
*/
static void pdmacFileAioMgrDestroy(PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile, PPDMACEPFILEMGR pAioMgr)
{
/* A normal manager may have still endpoints attached and has to return them. */
/* Unlink from the list. */
if (pPrev)
else
if (pNext)
pEpClassFile->cAioMgrs--;
/* Free the ressources. */
}
{
int rc = VINF_SUCCESS;
/** @todo: Change when the RTFileAio* API is used */
pEpClassFile->fFailsafe = true;
/* Init critical section. */
return rc;
}
{
/* All endpoints should be closed at this point. */
/* Destroy all left async I/O managers. */
while (pEpClassFile->pAioMgrHead)
}
{
int rc = VINF_SUCCESS;
if (!pEpClassFile->fFailsafe)
if (RT_SUCCESS(rc))
{
/* Initialize the cache */
sizeof(PDMACTASKFILESEG),
(void **)&pEpFile->pSegmentsFreeHead);
if (RT_SUCCESS(rc))
{
/** @todo Check caching flag. */
pEpFile->cSegmentsCached = 0;
if (pEpClassFile->fFailsafe)
{
/* Safe mode. Every file has its own async I/O manager. */
}
else
{
/* Check for an idling one or create new if not found */
AssertMsgFailed(("Implement\n"));
}
/* Assign the endpoint to the thread. */
}
if (RT_FAILURE(rc))
}
return rc;
}
{
/* Make sure that all tasks finished for this endpoint. */
/* Remove the endpoint from the thread. */
/*
* If the async I/O manager is in failsafe mode this is the only endpoint
* he processes and thus can be destroyed now.
*/
/* Free cached segments. */
while (pSeg)
{
}
/* Free the cached data. */
return VINF_SUCCESS;
}
{
}
{
}
{
return VINF_SUCCESS;
}
{
}
{
/* u32Version */
/* pcszName */
"File",
/* enmClassType */
/* cbEndpointClassGlobal */
sizeof(PDMASYNCCOMPLETIONEPCLASSFILE),
/* cbEndpoint */
sizeof(PDMASYNCCOMPLETIONENDPOINTFILE),
/* cbTask */
sizeof(PDMASYNCCOMPLETIONTASKFILE),
/* pfnInitialize */
/* pfnTerminate */
/* pfnEpInitialize. */
/* pfnEpClose */
/* pfnEpRead */
/* pfnEpWrite */
/* pfnEpFlush */
/* pfnEpGetSize */
/* u32VersionEnd */
};