PDMAsyncCompletionFileNormal.cpp revision 0493c79a1867f7760af6bc330c8c42a09da852ab
/* $Id$ */
/** @file
* PDM Async I/O - Transport data asynchronous in R3 using EMT.
* Async File I/O manager.
*/
/*
* 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.
*/
#include "PDMAsyncCompletionFileInternal.h"
/** The update period for the I/O load statistics in ms. */
#define PDMACEPFILEMGR_LOAD_UPDATE_PERIOD 1000
/** Maximum number of requests a manager will handle. */
{
int rc = VINF_SUCCESS;
if (rc == VERR_OUT_OF_RANGE)
if (RT_SUCCESS(rc))
{
/* Initialize request handle array. */
pAioMgr->iFreeEntryNext = 0;
pAioMgr->iFreeReqNext = 0;
if (pAioMgr->pahReqsFree)
{
return VINF_SUCCESS;
}
else
{
rc = VERR_NO_MEMORY;
}
}
return rc;
}
{
{
}
}
/**
* Sorts the endpoint list with insertion sort.
*/
{
while (pEpCurr)
{
/* Remember the next element to sort because the list might change. */
/* Unlink the current element from the list. */
if (pPrev)
else
if (pNext)
/* Go back until we reached the place to insert the current endpoint into. */
/* Link the endpoint into the list. */
if (pEpPrev)
else
if (pEpPrev)
else
}
#ifdef DEBUG
/* Validate sorting alogrithm */
unsigned cEndpoints = 0;
AssertMsg(!pEpCurr->AioMgr.pEndpointPrev, ("First element in the list points to previous element\n"));
while (pEpCurr)
{
cEndpoints++;
}
#endif
}
/**
* Removes an endpoint from the currently assigned manager.
*
* @returns TRUE if there are still requests pending on the current manager for this endpoint.
* FALSE otherwise.
* @param pEndpointRemove The endpoint to remove.
*/
{
pAioMgr->cEndpoints--;
if (pPrev)
else
if (pNext)
/* Make sure that there is no request pending on this manager for the endpoint. */
{
/* Reopen the file so that the new endpoint can reassociate with the file */
return false;
}
return true;
}
/**
* Creates a new I/O manager and spreads the I/O load of the endpoints
* between the given I/O manager and the new one.
*
* @returns nothing.
* @param pAioMgr The I/O manager with high I/O load.
*/
{
int rc = VINF_SUCCESS;
/* Splitting can't be done with only one open endpoint. */
{
&pAioMgrNew);
if (RT_SUCCESS(rc))
{
/* We will sort the list by request count per second. */
/* Now move some endpoints to the new manager. */
unsigned cReqsOther = 0;
while (pCurr)
{
if (cReqsHere <= cReqsOther)
{
/*
* The other manager has more requests to handle now.
* We will keep the current endpoint.
*/
}
else
{
/* Move to other endpoint. */
Log(("Moving endpoint %#p{%s} with %u reqs/s to other manager\n", pCurr->Core.pszUri, pCurr->AioMgr.cReqsPerSec));
if (fReqsPending)
{
}
else
{
}
}
}
}
else
{
/* Don't process further but leave a log entry about reduced performance. */
}
}
}
/**
* Error handler which will create the failsafe managers and destroy the failed I/O manager.
*
* @returns VBox status code
* @param pAioMgr The I/O manager the error ocurred on.
* @param rc The error code.
*/
{
LogRel(("AIOMgr: I/O manager %#p encountered a critical error (rc=%Rrc) during operation. Falling back to failsafe mode. Expect reduced performance\n",
LogRel(("AIOMgr: Please contact the product vendor\n"));
PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pAioMgr->pEndpointsHead->Core.pEpClass;
AssertMsgFailed(("Implement\n"));
return VINF_SUCCESS;
}
/**
* Put a list of tasks in the pending request list of an endpoint.
*/
DECLINLINE(void) pdmacFileAioMgrEpAddTaskList(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint, PPDMACTASKFILE pTaskHead)
{
/* Add the rest of the tasks to the pending list */
{
}
else
{
}
/* Update the tail. */
}
/**
* Put one task in the pending request list of an endpoint.
*/
DECLINLINE(void) pdmacFileAioMgrEpAddTask(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint, PPDMACTASKFILE pTask)
{
/* Add the rest of the tasks to the pending list */
{
}
else
{
}
}
/**
* Wrapper around RTFIleAioCtxSubmit() which is also doing error handling.
*/
{
int rc;
if (RT_FAILURE(rc))
{
{
/*
* We run out of resources.
* Need to check which requests got queued
* and put the rest on the pending list again.
*/
{
pEpClass->fOutOfResourcesWarningPrinted = true;
LogRel(("AIOMgr: The operating system doesn't have enough resources "
"to handle the I/O load of the VM. Expect reduced I/O performance\n"));
}
{
if (rcReq != VERR_FILE_AIO_IN_PROGRESS)
{
("Request returned unexpected return code: rc=%Rrc\n", rcReq));
/* Put the entry on the free array */
}
}
}
else
}
return rc;
}
{
unsigned cRequests = 0;
int rc = VINF_SUCCESS;
PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pEndpoint->Core.pEpClass;
("Trying to process request lists of a non active endpoint!\n"));
/* Go through the list and queue the requests until we get a flush request */
{
("Endpoints do not match\n"));
switch (pCurr->enmTransferType)
{
{
/* If there is no data transfer request this flush request finished immediately. */
{
}
else
{
/* Do not process the task list further until the flush finished. */
}
break;
}
{
/* Get a request handle. */
{
}
else
{
}
/* Check if the alignment requirements are met.
* Offset, transfer size and buffer address
* need to be on a 512 boundary. */
("Read exceeds file size offStart=%RTfoff cbToTransfer=%d cbFile=%llu\n",
{
/* Create bounce buffer. */
pCurr->fBounceBuffer = true;
/** @todo: I think we need something like a RTMemAllocAligned method here.
* Current assumption is that the maximum alignment is 4096byte
* (GPT disk on Windows)
* so we can use RTMemPageAlloc here.
*/
{
{
/* We have to fill the buffer first before we can update the data. */
}
else
}
}
else
pCurr->fBounceBuffer = false;
("AIO: Alignment restrictions not met! pvBuf=%p uBitmaskAlignment=%p\n", pvBuf, pEpClassFile->uBitmaskAlignment));
{
/* Grow the file if needed. */
{
}
}
else
cMaxRequests--;
cRequests++;
{
cRequests = 0;
if (RT_FAILURE(rc))
{
break;
}
}
break;
}
default:
}
}
if (cRequests)
{
("Unexpected return code rc=%Rrc\n", rc));
}
if (pTaskHead)
{
/* Add the rest of the tasks to the pending list */
{
/*
* The I/O manager has no room left for more requests
* but there are still requests to process.
* Create a new I/O manager and let it handle some endpoints.
*/
}
}
/* Insufficient resources are not fatal. */
rc = VINF_SUCCESS;
return rc;
}
/**
* Adds all pending requests for the given endpoint
* until a flush request is encountered or there is no
* request anymore.
*
* @returns VBox status code.
* @param pAioMgr The async I/O manager for the endpoint
* @param pEndpoint The endpoint to get the requests from.
*/
{
int rc = VINF_SUCCESS;
("Trying to process request lists of a non active endpoint!\n"));
/* Check the pending list first */
{
/*
* Clear the list as the processing routine will insert them into the list
* again if it gets a flush request.
*/
}
{
/* Now the request queue. */
if (pTasksHead)
{
}
}
return rc;
}
{
int rc = VINF_SUCCESS;
bool fNotifyWaiter = false;
switch (pAioMgr->enmBlockingEvent)
{
{
PPDMASYNCCOMPLETIONENDPOINTFILE pEndpointNew = (PPDMASYNCCOMPLETIONENDPOINTFILE)ASMAtomicReadPtr((void * volatile *)&pAioMgr->BlockingEventData.AddEndpoint.pEndpoint);
if (pAioMgr->pEndpointsHead)
/* Assign the completion point to this file. */
fNotifyWaiter = true;
pAioMgr->cEndpoints++;
break;
}
{
PPDMASYNCCOMPLETIONENDPOINTFILE pEndpointRemove = (PPDMASYNCCOMPLETIONENDPOINTFILE)ASMAtomicReadPtr((void * volatile *)&pAioMgr->BlockingEventData.RemoveEndpoint.pEndpoint);
break;
}
{
PPDMASYNCCOMPLETIONENDPOINTFILE pEndpointClose = (PPDMASYNCCOMPLETIONENDPOINTFILE)ASMAtomicReadPtr((void * volatile *)&pAioMgr->BlockingEventData.CloseEndpoint.pEndpoint);
/* Make sure all tasks finished. Process the queues a last time first. */
break;
}
{
if (!pAioMgr->cRequestsActive)
fNotifyWaiter = true;
break;
}
{
break;
}
{
fNotifyWaiter = true;
break;
}
default:
}
if (fNotifyWaiter)
{
/* Release the waiting thread. */
LogFlow(("Signalling waiter\n"));
}
return rc;
}
/** Helper macro for checking for error codes. */
if (RT_FAILURE(rc)) \
{\
return rc2;\
}
/**
* The normal I/O manager using the RTFileAio* API
*
* @returns VBox status code.
* @param ThreadSelf Handle of the thread.
* @param pvUser Opaque user data.
*/
{
int rc = VINF_SUCCESS;
{
LogFlow(("Got woken up\n"));
/* Check for an external blocking event first. */
if (pAioMgr->fBlockingEventPending)
{
}
{
/* Check the assigned endpoints for new tasks if there isn't a flush request active at the moment. */
while (pEndpoint)
{
{
}
}
while (pAioMgr->cRequestsActive)
{
uint32_t cReqsCompleted = 0;
else
for (uint32_t i = 0; i < cReqsCompleted; i++)
{
size_t cbTransfered = 0;
|| (pTask->fBounceBuffer)),
("Task didn't completed successfully (rc=%Rrc) or was incomplete (cbTransfered=%u)\n", rc, cbTransfered));
{
/* Write it now. */
/* Grow the file if needed. */
{
}
}
else
{
if (pTask->fBounceBuffer)
{
}
/* Put the entry on the free array */
/* Call completion callback */
/*
* If there is no request left on the endpoint but a flush request is set
* it completed now and we notify the owner.
* Furthermore we look for new requests and continue.
*/
{
/* Call completion callback */
AssertMsg(pTask->pEndpoint == pEndpoint, ("Endpoint of the flush request does not match assigned one\n"));
}
}
{
{
/* Check if there are events on the endpoint. */
}
}
{
/* Reopen the file so that the new endpoint can reassociate with the file */
{
}
else
{
/* Release the waiting thread. */
LogFlow(("Signalling waiter\n"));
}
}
}
/* Check for an external blocking event before we go to sleep again. */
if (pAioMgr->fBlockingEventPending)
{
}
/* Update load statistics. */
if (uMillisCurr > uMillisEnd)
{
/* Calculate timespan. */
while (pEndpointCurr)
{
pEndpointCurr->AioMgr.cReqsPerSec = pEndpointCurr->AioMgr.cReqsProcessed / (uMillisCurr + PDMACEPFILEMGR_LOAD_UPDATE_PERIOD);
}
/* Set new update interval */
}
}
}
}
return rc;
}