PDMAsyncCompletionFileCache.cpp revision d116d2fdc8bc55e97a36032d13c0532de69d6aca
/* $Id$ */
/** @file
* PDM Async I/O - Transport data asynchronous in R3 using EMT.
* File data cache.
*/
/*
* 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"
{
/* Check that there are no double entries and no cycles in the list. */
while (pCurr)
{
while (pNext)
{
("Entry %#p is at least two times in list %#p or there is a cycle in the list\n",
}
}
#endif
}
{
if (pPrev)
else
{
if (pNext)
}
if (pNext)
else
{
if (pPrev)
}
}
{
/* Remove from old list if needed */
else
{
}
}
/**
* Destroys a LRU list freeing all entries.
*
* @returns nothing
* @param pList Pointer to the LRU list to destroy.
*
* @note The caller must own the critical section of the cache.
*/
{
{
AssertMsg(!(pEntry->fFlags & (PDMACFILECACHE_ENTRY_IO_IN_PROGRESS | PDMACFILECACHE_ENTRY_IS_DIRTY)),
}
}
{
("Destination list must be NULL or one of the ghost lists\n"));
/* Start deleting from the tail. */
{
/* We can't evict pages which are currently in progress */
{
{
}
if (pGhostListDst)
{
}
else
{
/* Delete the entry from the AVL tree it is assigned to. */
}
}
else
LogFlow(("Entry %#p (%u bytes) is still in progress and can't be evicted\n", pCurr, pCurr->cbData));
}
return cbEvicted;
}
static size_t pdmacFileCacheReplace(PPDMACFILECACHEGLOBAL pCache, size_t cbData, PPDMACFILELRULIST pEntryList)
{
{
/* We need to remove entry size pages from T1 and move the entries to B1 */
}
else
{
/* We need to remove entry size pages from T2 and move the entries to B2 */
}
}
{
{
/* Delete desired pages from the cache. */
{
NULL);
}
else
{
NULL);
}
}
else
{
{
NULL);
}
}
return cbRemoved;
}
{
int32_t uUpdateVal = 0;
/* Update parameters */
{
uUpdateVal = 1;
else
}
{
uUpdateVal = 1;
else
}
else
AssertMsgFailed(("Invalid list type\n"));
}
{
/* Make sure no one evicts the entry while it is accessed. */
/* Send it off to the I/O manager. */
}
{
/* Make sure no one evicts the entry while it is accessed. */
/* Send it off to the I/O manager. */
}
{
{
/* Process waiting segment list. The data in entry might have changed inbetween. */
while (pCurr)
{
}
}
else
{
/* Process waiting segment list. */
while (pCurr)
{
{
}
else
}
}
}
{
int rc = VINF_SUCCESS;
/* Initialize members */
"/PDM/AsyncCompletion/File/cbMax",
"Maximum cache size");
"Currently used cache");
"Number of bytes cached in Mru list");
"Number of bytes cached in Fru list");
"Number of bytes cached in Mru ghost list");
STAMUNIT_BYTES, "Number of bytes cached in Fru ghost list");
#ifdef VBOX_WITH_STATISTICS
STAMUNIT_COUNT, "Number of hits in the cache");
STAMUNIT_COUNT, "Number of partial hits in the cache");
STAMUNIT_COUNT, "Number of misses when accessing the cache");
STAMUNIT_BYTES, "Number of bytes read from the cache");
STAMUNIT_BYTES, "Number of bytes written to the cache");
STAMUNIT_TICKS_PER_CALL, "Time taken to access an entry in the tree");
STAMUNIT_TICKS_PER_CALL, "Time taken to insert an entry in the tree");
STAMUNIT_TICKS_PER_CALL, "Time taken to remove an entry an the tree");
#endif
/* Initialize the critical section */
return rc;
}
{
/* Make sure no one else uses the cache now */
/* Cleanup deleting all cache entries waiting for in progress entries to finish. */
}
int pdmacFileEpCacheInit(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint, PPDMASYNCCOMPLETIONEPCLASSFILE pClassFile)
{
return VINF_SUCCESS;
}
{
AssertMsg(!(pEntry->fFlags & (PDMACFILECACHE_ENTRY_IO_IN_PROGRESS | PDMACFILECACHE_ENTRY_IS_DIRTY)),
return VINF_SUCCESS;
}
{
/* Make sure nobody is accessing the cache while we delete the tree. */
}
/**
* Advances the current segment buffer by the number of bytes transfered
* or gets the next segment.
*/
#define ADVANCE_SEGMENT_BUFFER(BytesTransfered) \
do \
{ \
cbSegLeft -= BytesTransfered; \
if (!cbSegLeft) \
{ \
iSegCurr++; \
} \
else \
pbSegBuf += BytesTransfered; \
} \
while (0);
int pdmacFileEpCacheRead(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint, PPDMASYNCCOMPLETIONTASKFILE pTask,
{
int rc = VINF_SUCCESS;
LogFlowFunc((": pEndpoint=%#p{%s} pTask=%#p off=%RTfoff paSegments=%#p cSegments=%u cbRead=%u\n",
/* Set to completed to make sure that the task is valid while we access it. */
int iSegCurr = 0;
while (cbRead)
{
/*
* If there is no entry we try to create a new one eviciting unused pages
* if the cache is full. If this is not possible we will pass the request through
* and skip the caching (all entries may be still in progress so they can't
* be evicted)
* If we have an entry it can be in one of the LRU lists where the entry
* contains data (recently used or frequently used LRU) so we can just read
* the data we need and put the entry at the head of the frequently used LRU list.
* In case the entry is in one of the ghost lists it doesn't contain any data.
* We have to fetch it again evicting pages from either T1 or T2 to make room.
*/
if (pEntry)
{
("Overflow in calculation off=%RTfoff OffsetAligned=%RTfoff\n",
if (!cbRead)
else
/* Ghost lists contain no data. */
{
{
/* Entry didn't completed yet. Append to the list */
while (cbToRead)
{
}
}
else
{
/* Read as much as we can from the entry. */
while (cbToRead)
{
}
}
/* Move this entry to the top position */
}
else
{
/* Move the entry to T2 and fetch it to the cache. */
while (cbToRead)
{
("Overflow in calculation off=%RTfoff OffsetAligned=%RTfoff\n",
}
}
}
else
{
/* No entry found for this offset. Get best fit entry and fetch the data to the cache. */
PPDMACFILECACHEENTRY pEntryBestFit = (PPDMACFILECACHEENTRY)RTAvlrFileOffsetGetBestFit(pEndpointCache->pTree, off, true);
LogFlow(("%sbest fit entry for off=%RTfoff (BestFit=%RTfoff BestFitEnd=%RTfoff BestFitSize=%u)\n",
off,
else
if (!cbRead)
else
{
uint32_t uBufOffset = 0;
while (cbToRead)
{
}
}
else
{
/*
* There is not enough free space in the cache.
* Pass the request directly to the I/O manager.
*/
LogFlow(("Couldn't evict %u bytes from the cache (%u actually removed). Remaining request will be passed through\n", cbToRead, cbRemoved));
while (cbToRead)
{
/* Send it off to the I/O manager. */
}
}
}
}
return rc;
}
int pdmacFileEpCacheWrite(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint, PPDMASYNCCOMPLETIONTASKFILE pTask,
{
int rc = VINF_SUCCESS;
LogFlowFunc((": pEndpoint=%#p{%s} pTask=%#p off=%RTfoff paSegments=%#p cSegments=%u cbWrite=%u\n",
/* Set to completed to make sure that the task is valid while we access it. */
int iSegCurr = 0;
while (cbWrite)
{
if (pEntry)
{
/* Write the data into the entry and mark it as dirty */
("Overflow in calculation off=%RTfoff OffsetAligned=%RTfoff\n",
if (!cbWrite)
else
/* Ghost lists contain no data. */
{
{
("Entry is dirty but not in progress\n"));
/* The data isn't written to the file yet */
while (cbToWrite)
{
}
}
else
{
("Entry is not dirty but in progress\n"));
/* Write as much as we can into the entry and update the file. */
while (cbToWrite)
{
}
}
/* Move this entry to the top position */
}
else
{
/* Move the entry to T2 and fetch it to the cache. */
while (cbToWrite)
{
("Overflow in calculation off=%RTfoff OffsetAligned=%RTfoff\n",
}
}
}
else
{
/*
* No entry found. Write directly into file.
*/
PPDMACFILECACHEENTRY pEntryBestFit = (PPDMACFILECACHEENTRY)RTAvlrFileOffsetGetBestFit(pEndpointCache->pTree, off, true);
LogFlow(("%sbest fit entry for off=%RTfoff (BestFit=%RTfoff BestFitEnd=%RTfoff BestFitSize=%u)\n",
off,
else
while (cbToWrite)
{
/* Send it off to the I/O manager. */
}
}
}
return VINF_SUCCESS;
}