PDMAsyncCompletionFile.cpp revision 2640b304447a3353a161e04ea294b8b66e18dc5d
/* $Id$ */
/** @file
* PDM Async I/O - Transport data asynchronous in R3 using EMT.
*/
/*
* Copyright (C) 2006-2009 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 *
*******************************************************************************/
#define RT_STRICT
//#define DEBUG
#include "PDMInternal.h"
#include <iprt/critsect.h>
#include <iprt/semaphore.h>
#include "PDMAsyncCompletionFileInternal.h"
/**
* Frees a task.
*
* @returns nothing.
* @param pEndpoint Pointer to the endpoint the segment was for.
* @param pTask The task 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 (!pTask)
{
/*
* Allocate completely new.
* If this fails we return NULL.
*/
sizeof(PDMACTASKFILE),
(void **)&pTask);
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->cTasksCached > 0, ("No tasks cached but list contains more than one element\n"));
}
return pTask;
}
{
/*
* Get pending tasks.
*/
/* Reverse the list to process in FIFO order. */
if (pTasks)
{
while (pTask)
{
}
}
return pTasks;
}
{
if (!fWokenUp)
{
int rc = VINF_SUCCESS;
if (fWaitingEventSem)
}
}
static int pdmacFileAioMgrWaitForBlockingEvent(PPDMACEPFILEMGR pAioMgr, PDMACEPFILEAIOMGRBLOCKINGEVENT enmEvent)
{
int rc = VINF_SUCCESS;
/* Wakeup the async I/O manager */
/* Wait for completion. */
ASMAtomicWriteU32((volatile uint32_t *)&pAioMgr->enmBlockingEvent, PDMACEPFILEAIOMGRBLOCKINGEVENT_INVALID);
return rc;
}
{
int rc;
if (RT_SUCCESS(rc))
return rc;
}
static int pdmacFileAioMgrRemoveEndpoint(PPDMACEPFILEMGR pAioMgr, PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint)
{
int rc;
ASMAtomicWritePtr((void * volatile *)&pAioMgr->BlockingEventData.RemoveEndpoint.pEndpoint, pEndpoint);
return rc;
}
static int pdmacFileAioMgrCloseEndpoint(PPDMACEPFILEMGR pAioMgr, PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint)
{
int rc;
ASMAtomicWritePtr((void * volatile *)&pAioMgr->BlockingEventData.CloseEndpoint.pEndpoint, pEndpoint);
return rc;
}
{
int rc;
return rc;
}
{
do
{
} while (!ASMAtomicCmpXchgPtr((void * volatile *)&pEndpoint->pTasksNewHead, (void *)pTask, (void *)pNext));
return VINF_SUCCESS;
}
{
{
}
else
{
Assert((uint32_t)pTask->DataSeg.cbSeg == pTask->DataSeg.cbSeg && (int32_t)pTask->DataSeg.cbSeg >= 0);
}
}
{
int rc = VINF_SUCCESS;
|| (enmTransfer == PDMACTASKFILETRANSFER_WRITE));
for (unsigned i = 0; i < cSegments; i++)
{
/* Send it off to the I/O manager. */
}
pdmR3AsyncCompletionCompleteTask(pTask, false);
else
return rc;
}
/**
* 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.
* @param fFailsafe Flag to force a failsafe manager even if the global flag is not set.
*/
int pdmacFileAioMgrCreate(PPDMASYNCCOMPLETIONEPCLASSFILE pEpClass, PPPDMACEPFILEMGR ppAioMgr, bool fFailsafe)
{
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))
{
/* Init the rest of the manager. */
if (!pAioMgrNew->fFailsafe)
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)
{
/* Unlink from the list. */
if (pPrev)
else
if (pNext)
pEpClassFile->cAioMgrs--;
/* Free the ressources. */
}
{
int rc = VINF_SUCCESS;
#ifdef DEBUG
#endif
if (RT_FAILURE(rc))
{
LogRel(("AIO: Async I/O manager not supported (rc=%Rrc). Falling back to failsafe manager\n",
rc));
pEpClassFile->fFailsafe = true;
}
else
{
pEpClassFile->uBitmaskAlignment = AioLimits.cbBufferAlignment ? ~((RTR3UINTPTR)AioLimits.cbBufferAlignment - 1) : RTR3UINTPTR_MAX;
/* The user can force the failsafe manager. */
if (pEpClassFile->fFailsafe)
LogRel(("AIOMgr: Failsafe I/O was requested by user\n"));
}
/* Init critical section. */
if (RT_SUCCESS(rc))
{
/* Check if the host cache should be used too. */
#ifndef RT_OS_LINUX
#else
/*
* Host cache + async I/O is not supported on Linux. Check if the user enabled the cache,
* leave a warning and disable it always.
*/
bool fDummy;
if (RT_SUCCESS(rc))
LogRel(("AIOMgr: The host cache is not supported with async I/O on Linux\n"));
pEpClassFile->fHostCacheEnabled = false;
#endif
/* Check if the cache was disabled by the user. */
if (pEpClassFile->fCacheEnabled)
{
/* Init cache structure */
if (RT_FAILURE(rc))
{
pEpClassFile->fCacheEnabled = false;
LogRel(("AIOMgr: Failed to initialise the cache (rc=%Rrc), disabled caching\n"));
}
}
else
LogRel(("AIOMgr: Cache was globally disabled\n"));
}
return rc;
}
{
/* All endpoints should be closed at this point. */
/* Destroy all left async I/O managers. */
while (pEpClassFile->pAioMgrHead)
/* Destroy the cache. */
if (pEpClassFile->fCacheEnabled)
}
{
int rc = VINF_SUCCESS;
("PDMAsyncCompletion: Invalid flag specified\n"), VERR_INVALID_PARAMETER);
if (!pEpClassFile->fFailsafe)
{
/*
* We only disable the cache if the size of the file is a multiple of 512.
* Certain hosts like Windows, Linux and Solaris require that transfer sizes
* are aligned to the volume sector size.
* If not we just make sure that the data is written to disk with RTFILE_O_WRITE_THROUGH
* which will trash the host cache but ensures that the host cache will not
* contain dirty buffers.
*/
if (RT_SUCCESS(rc))
{
{
#if defined(RT_OS_LINUX)
AssertMsg(!pEpClassFile->fHostCacheEnabled, ("Host cache + async I/O is not supported on Linux\n"));
#else
if (!pEpClassFile->fHostCacheEnabled)
#endif
}
}
}
/* Open with final flags. */
{
LogRel(("pdmacFileEpInitialize: RTFileOpen %s / %08x failed with %Rrc\n",
/*
* Solaris doesn't support directio on ZFS so far. :-\
* Trying to enable it returns VERR_INVALID_FUNCTION
* (ENOTTY). Remove it and hope for the best.
* ZFS supports write throttling in case applications
* write more data than can be synced to the disk
* without blocking the whole application.
*
* On Linux we have the same problem with cifs.
* Have to disable async I/O here too because it requires O_DIRECT.
*/
#ifdef RT_OS_LINUX
fUseFailsafeManager = true;
#endif
/* Open again. */
if (RT_FAILURE(rc))
{
LogRel(("pdmacFileEpInitialize: RTFileOpen %s / %08x failed AGAIN(!) with %Rrc\n",
}
}
if (RT_SUCCESS(rc))
{
{
/* Could be a block device */
}
if (RT_SUCCESS(rc))
{
/* Initialize the segment cache */
sizeof(PDMACTASKFILE),
(void **)&pEpFile->pTasksFreeHead);
if (RT_SUCCESS(rc))
{
pEpFile->cTasksCached = 0;
if (fUseFailsafeManager)
{
/* Safe mode. Every file has its own async I/O manager. */
}
else
{
if ( (fFlags & PDMACEP_FILE_FLAGS_CACHING)
&& (pEpClassFile->fCacheEnabled))
{
if (RT_FAILURE(rc))
{
LogRel(("AIOMgr: Endpoint for \"%s\" was opened with caching but initializing cache failed. Disabled caching\n", pszUri));
}
}
/* Check for an idling not failsafe one or create new if not found */
if (!pAioMgr)
{
}
}
rc = VERR_NO_MEMORY;
else
{
/* Assign the endpoint to the thread. */
if (RT_FAILURE(rc))
{
}
}
}
}
if (RT_FAILURE(rc))
}
#ifdef VBOX_WITH_STATISTICS
if (RT_SUCCESS(rc))
{
STAMUNIT_TICKS_PER_CALL, "Time taken to read from the endpoint",
STAMUNIT_TICKS_PER_CALL, "Time taken to write to the endpoint",
}
#endif
return rc;
}
{
AssertMsgFailed(("The locked ranges tree should be empty at that point\n"));
return VINF_SUCCESS;
}
{
/* Make sure that all tasks finished for this endpoint. */
/*
* 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 tasks. */
while (pTask)
{
}
/* Free the cached data. */
/* Destroy the locked ranges tree now. */
#ifdef VBOX_WITH_STATISTICS
#endif
return VINF_SUCCESS;
}
{
int rc = VINF_SUCCESS;
else
return rc;
}
{
int rc = VINF_SUCCESS;
return VERR_NOT_SUPPORTED;
else
return rc;
}
{
int rc = VINF_SUCCESS;
return VERR_NOT_SUPPORTED;
pTaskFile->cbTransferLeft = 0;
else
{
}
return rc;
}
{
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 */
};