PDMAsyncCompletionFileCache.cpp revision 22ec733a5e041fcdfe02fce2eafc9faf8b0077dd
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * PDM Async I/O - Transport data asynchronous in R3 using EMT.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * File data cache.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Copyright (C) 2006-2008 Sun Microsystems, Inc.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * This file is part of VirtualBox Open Source Edition (OSE), as
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * available from http://www.virtualbox.org. This file is free software;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * you can redistribute it and/or modify it under the terms of the GNU
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * General Public License (GPL) as published by the Free Software
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Foundation, in version 2 as it comes in the "COPYING" file of the
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Clara, CA 95054 USA or visit http://www.sun.com if you need
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * additional information or have any questions.
17a2b317610f531d565bf4e940433aab2d9e6985Bill Taylor/** @page pg_pdm_async_completion_cache PDM Async Completion Cache - The file I/O cache
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * This component implements an I/O cache for file endpoints based on the 2Q cache algorithm.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor/*******************************************************************************
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor* Header Files *
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor*******************************************************************************/
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * A I/O memory context.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /** Pointer to the scatter/gather list. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /** Number of segments. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /** Current segment we are in. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /** Pointer to the current buffer. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /** Number of bytes left in the current buffer. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor# define PDMACFILECACHE_IS_CRITSECT_OWNER(Cache) \
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(RTCritSectIsOwner(&Cache->CritSect), \
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor ("Thread does not own critical section\n"));\
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor# define PDMACFILECACHE_EP_IS_SEMRW_WRITE_OWNER(pEpCache) \
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(RTSemRWIsWriteOwner(pEpCache->SemRWEntries), \
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor ("Thread is not exclusive owner of the per endpoint RW semaphore\n")); \
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor# define PDMACFILECACHE_EP_IS_SEMRW_READ_OWNER(pEpCache) \
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(RTSemRWIsReadOwner(pEpCache->SemRWEntries), \
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor ("Thread is not read owner of the per endpoint RW semaphore\n")); \
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor# define PDMACFILECACHE_IS_CRITSECT_OWNER(Cache) do { } while(0)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor# define PDMACFILECACHE_EP_IS_SEMRW_WRITE_OWNER(pEpCache) do { } while(0)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor# define PDMACFILECACHE_EP_IS_SEMRW_READ_OWNER(pEpCache) do { } while(0)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor/*******************************************************************************
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor* Internal Functions *
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor*******************************************************************************/
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylorstatic void pdmacFileCacheTaskCompleted(PPDMACTASKFILE pTask, void *pvUser, int rc);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Decrement the reference counter of the given cache entry.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @returns nothing.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pEntry The entry to release.
9e39c5ba00a55fa05777cc94b148296af305e135Bill TaylorDECLINLINE(void) pdmacFileEpCacheEntryRelease(PPDMACFILECACHEENTRY pEntry)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(pEntry->cRefs > 0, ("Trying to release a not referenced entry\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Increment the reference counter of the given cache entry.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @returns nothing.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pEntry The entry to reference.
9e39c5ba00a55fa05777cc94b148296af305e135Bill TaylorDECLINLINE(void) pdmacFileEpCacheEntryRef(PPDMACFILECACHEENTRY pEntry)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Initialize a I/O memory context.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @returns nothing
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pIoMemCtx Pointer to a unitialized I/O memory context.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param paDataSeg Pointer to the S/G list.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param cSegments Number of segments in the S/G list.
9e39c5ba00a55fa05777cc94b148296af305e135Bill TaylorDECLINLINE(void) pdmIoMemCtxInit(PPDMIOMEMCTX pIoMemCtx, PCPDMDATASEG paDataSeg, size_t cSegments)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg((cSegments > 0) && paDataSeg, ("Trying to initialize a I/O memory context without a S/G list\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pIoMemCtx->pbBuf = (uint8_t *)paDataSeg[0].pvSeg;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Return a buffer from the I/O memory context.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @returns Pointer to the buffer
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pIoMemCtx Pointer to the I/O memory context.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pcbData Pointer to the amount of byte requested.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * If the current buffer doesn't have enough bytes left
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * the amount is returned in the variable.
9e39c5ba00a55fa05777cc94b148296af305e135Bill TaylorDECLINLINE(uint8_t *) pdmIoMemCtxGetBuffer(PPDMIOMEMCTX pIoMemCtx, size_t *pcbData)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor size_t cbData = RT_MIN(*pcbData, pIoMemCtx->cbBufLeft);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* Advance to the next segment if required. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if (RT_UNLIKELY(pIoMemCtx->iSegIdx == pIoMemCtx->cSegments))
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pIoMemCtx->pbBuf = (uint8_t *)pIoMemCtx->paDataSeg[pIoMemCtx->iSegIdx].pvSeg;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pIoMemCtx->cbBufLeft = pIoMemCtx->paDataSeg[pIoMemCtx->iSegIdx].cbSeg;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylorstatic void pdmacFileCacheValidate(PPDMACFILECACHEGLOBAL pCache)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* Amount of cached data should never exceed the maximum amount. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor ("Current amount of cached data exceeds maximum\n"));
de710d24d2fae4468e64da999e1d952a247f142cJosef 'Jeff' Sipek /* The amount of cached data in the LRU and FRU list should match cbCached */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(pCache->LruRecentlyUsedIn.cbCached + pCache->LruFrequentlyUsed.cbCached == pCache->cbCached,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor ("Amount of cached data doesn't match\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(pCache->LruRecentlyUsedOut.cbCached <= pCache->cbRecentlyUsedOutMax,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor ("Paged out list exceeds maximum\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill TaylorDECLINLINE(void) pdmacFileCacheLockEnter(PPDMACFILECACHEGLOBAL pCache)
9e39c5ba00a55fa05777cc94b148296af305e135Bill TaylorDECLINLINE(void) pdmacFileCacheLockLeave(PPDMACFILECACHEGLOBAL pCache)
9e39c5ba00a55fa05777cc94b148296af305e135Bill TaylorDECLINLINE(void) pdmacFileCacheSub(PPDMACFILECACHEGLOBAL pCache, uint32_t cbAmount)
9e39c5ba00a55fa05777cc94b148296af305e135Bill TaylorDECLINLINE(void) pdmacFileCacheAdd(PPDMACFILECACHEGLOBAL pCache, uint32_t cbAmount)
9e39c5ba00a55fa05777cc94b148296af305e135Bill TaylorDECLINLINE(void) pdmacFileCacheListAdd(PPDMACFILELRULIST pList, uint32_t cbAmount)
9e39c5ba00a55fa05777cc94b148296af305e135Bill TaylorDECLINLINE(void) pdmacFileCacheListSub(PPDMACFILELRULIST pList, uint32_t cbAmount)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Checks consistency of a LRU list.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @returns nothing
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pList The LRU list to check.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pNotInList Element which is not allowed to occur in the list.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylorstatic void pdmacFileCacheCheckList(PPDMACFILELRULIST pList, PPDMACFILECACHEENTRY pNotInList)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* Check that there are no double entries and no cycles in the list. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor ("Entry %#p is at least two times in list %#p or there is a cycle in the list\n",
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(pCurr != pNotInList, ("Not allowed entry %#p is in list\n", pCurr));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(pCurr == pList->pTail, ("End of list reached but last element is not list tail\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Unlinks a cache entry from the LRU list it is assigned to.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @returns nothing.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pEntry The entry to unlink.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylorstatic void pdmacFileCacheEntryRemoveFromList(PPDMACFILECACHEENTRY pEntry)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor LogFlowFunc((": Deleting entry %#p from list %#p\n", pEntry, pList));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(pEntry != pPrev, ("Entry links to itself as previous element\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(pEntry != pNext, ("Entry links to itself as next element\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Adds a cache entry to the given LRU list unlinking it from the currently
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * assigned list if needed.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @returns nothing.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pList List to the add entry to.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pEntry Entry to add.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylorstatic void pdmacFileCacheEntryAddToList(PPDMACFILELRULIST pList, PPDMACFILECACHEENTRY pEntry)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor LogFlowFunc((": Adding entry %#p to list %#p\n", pEntry, pList));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* Remove from old list if needed */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Destroys a LRU list freeing all entries.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @returns nothing
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pList Pointer to the LRU list to destroy.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @note The caller must own the critical section of the cache.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylorstatic void pdmacFileCacheDestroyList(PPDMACFILELRULIST pList)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(!(pEntry->fFlags & (PDMACFILECACHE_ENTRY_IO_IN_PROGRESS | PDMACFILECACHE_ENTRY_IS_DIRTY)),
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor ("Entry is dirty and/or still in progress fFlags=%#x\n", pEntry->fFlags));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Tries to remove the given amount of bytes from a given list in the cache
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * moving the entries to one of the given ghosts lists
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @returns Amount of data which could be freed.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pCache Pointer to the global cache data.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param cbData The amount of the data to free.
17a2b317610f531d565bf4e940433aab2d9e6985Bill Taylor * @param pListSrc The source list to evict data from.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pGhostListSrc The ghost list removed entries should be moved to
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * NULL if the entry should be freed.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param fReuseBuffer Flag whether a buffer should be reused if it has the same size
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param ppbBuf Where to store the address of the buffer if an entry with the
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * same size was found and fReuseBuffer is true.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @note This function may return fewer bytes than requested because entries
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * may be marked as non evictable if they are used for I/O at the
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylorstatic size_t pdmacFileCacheEvictPagesFrom(PPDMACFILECACHEGLOBAL pCache, size_t cbData,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACFILELRULIST pListSrc, PPDMACFILELRULIST pGhostListDst,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(cbData > 0, ("Evicting 0 bytes not possible\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor || (pGhostListDst == &pCache->LruRecentlyUsedOut),
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor ("Destination list must be NULL or the recently used but paged out list\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* Start deleting from the tail. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* We can't evict pages which are currently in progress or dirty but not in progress */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if ( !(pCurr->fFlags & PDMACFILECACHE_NOT_EVICTABLE)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* Ok eviction candidate. Grab the endpoint semaphore and check again
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * because somebody else might have raced us. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACFILEENDPOINTCACHE pEndpointCache = &pCurr->pEndpoint->DataCache;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTSemRWRequestWrite(pEndpointCache->SemRWEntries, RT_INDEFINITE_WAIT);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if (!(pCurr->fFlags & PDMACFILECACHE_NOT_EVICTABLE)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor LogFlow(("Evicting entry %#p (%u bytes)\n", pCurr, pCurr->cbData));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTSemRWReleaseWrite(pEndpointCache->SemRWEntries);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACFILECACHEENTRY pGhostEntFree = pGhostListDst->pTail;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* We have to remove the last entries from the paged out list. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor while ( ((pGhostListDst->cbCached + pCurr->cbData) > pCache->cbRecentlyUsedOutMax)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACFILEENDPOINTCACHE pEndpointCacheFree = &pFree->pEndpoint->DataCache;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTSemRWRequestWrite(pEndpointCacheFree->SemRWEntries, RT_INDEFINITE_WAIT);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor STAM_PROFILE_ADV_START(&pCache->StatTreeRemove, Cache);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTAvlrFileOffsetRemove(pEndpointCacheFree->pTree, pFree->Core.Key);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor STAM_PROFILE_ADV_STOP(&pCache->StatTreeRemove, Cache);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTSemRWReleaseWrite(pEndpointCacheFree->SemRWEntries);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if (pGhostListDst->cbCached + pCurr->cbData > pCache->cbRecentlyUsedOutMax)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* Couldn't remove enough entries. Delete */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor STAM_PROFILE_ADV_START(&pCache->StatTreeRemove, Cache);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTAvlrFileOffsetRemove(pCurr->pEndpoint->DataCache.pTree, pCurr->Core.Key);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor STAM_PROFILE_ADV_STOP(&pCache->StatTreeRemove, Cache);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pdmacFileCacheEntryAddToList(pGhostListDst, pCurr);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* Delete the entry from the AVL tree it is assigned to. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor STAM_PROFILE_ADV_START(&pCache->StatTreeRemove, Cache);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTAvlrFileOffsetRemove(pCurr->pEndpoint->DataCache.pTree, pCurr->Core.Key);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor STAM_PROFILE_ADV_STOP(&pCache->StatTreeRemove, Cache);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTSemRWReleaseWrite(pEndpointCache->SemRWEntries);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor LogFlow(("Entry %#p (%u bytes) is still in progress and can't be evicted\n", pCurr, pCurr->cbData));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylorstatic bool pdmacFileCacheReclaim(PPDMACFILECACHEGLOBAL pCache, size_t cbData, bool fReuseBuffer, uint8_t **ppbBuffer)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if ((pCache->cbCached + cbData) < pCache->cbMax)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor return true;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor else if ((pCache->LruRecentlyUsedIn.cbCached + cbData) > pCache->cbRecentlyUsedInMax)
17a2b317610f531d565bf4e940433aab2d9e6985Bill Taylor /* Try to evict as many bytes as possible from A1in */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor cbRemoved = pdmacFileCacheEvictPagesFrom(pCache, cbData, &pCache->LruRecentlyUsedIn,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor &pCache->LruRecentlyUsedOut, fReuseBuffer, ppbBuffer);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * If it was not possible to remove enough entries
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * try the frequently accessed cache.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor Assert(!fReuseBuffer || !*ppbBuffer); /* It is not possible that we got a buffer with the correct size but we didn't freed enough data. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * If we removed something we can't pass the reuse buffer flag anymore because
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * we don't need to evict that much data
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor cbRemoved += pdmacFileCacheEvictPagesFrom(pCache, cbData, &pCache->LruFrequentlyUsed,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor cbRemoved += pdmacFileCacheEvictPagesFrom(pCache, cbData - cbRemoved, &pCache->LruFrequentlyUsed,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* We have to remove entries from frequently access list. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor cbRemoved = pdmacFileCacheEvictPagesFrom(pCache, cbData, &pCache->LruFrequentlyUsed,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor LogFlowFunc((": removed %u bytes, requested %u\n", cbRemoved, cbData));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Initiates a read I/O task for the given entry.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @returns nothing.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pEntry The entry to fetch the data to.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylorstatic void pdmacFileCacheReadFromEndpoint(PPDMACFILECACHEENTRY pEntry)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor LogFlowFunc((": Reading data into cache entry %#p\n", pEntry));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* Make sure no one evicts the entry while it is accessed. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pEntry->fFlags |= PDMACFILECACHE_ENTRY_IO_IN_PROGRESS;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACTASKFILE pIoTask = pdmacFileTaskAlloc(pEntry->pEndpoint);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(pEntry->pbData, ("Entry is in ghost state\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pIoTask->enmTransferType = PDMACTASKFILETRANSFER_READ;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pIoTask->pfnCompleted = pdmacFileCacheTaskCompleted;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* Send it off to the I/O manager. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Initiates a write I/O task for the given entry.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @returns nothing.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pEntry The entry to read the data from.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylorstatic void pdmacFileCacheWriteToEndpoint(PPDMACFILECACHEENTRY pEntry)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor LogFlowFunc((": Writing data from cache entry %#p\n", pEntry));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* Make sure no one evicts the entry while it is accessed. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pEntry->fFlags |= PDMACFILECACHE_ENTRY_IO_IN_PROGRESS;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACTASKFILE pIoTask = pdmacFileTaskAlloc(pEntry->pEndpoint);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(pEntry->pbData, ("Entry is in ghost state\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pIoTask->enmTransferType = PDMACTASKFILETRANSFER_WRITE;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pIoTask->pfnCompleted = pdmacFileCacheTaskCompleted;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor ASMAtomicIncU32(&pEntry->pEndpoint->DataCache.cWritesOutstanding);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* Send it off to the I/O manager. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Commit a single dirty entry to the endpoint
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @returns nothing
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pEntry The entry to commit.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylorstatic void pdmacFileCacheEntryCommit(PPDMACFILEENDPOINTCACHE pEndpointCache, PPDMACFILECACHEENTRY pEntry)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg( (pEntry->fFlags & PDMACFILECACHE_ENTRY_IS_DIRTY)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor && !(pEntry->fFlags & PDMACFILECACHE_ENTRY_IO_IN_PROGRESS),
de710d24d2fae4468e64da999e1d952a247f142cJosef 'Jeff' Sipek ("Invalid flags set for entry %#p\n", pEntry));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Commit all dirty entries for a single endpoint.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @returns nothing.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pEndpointCache The endpoint cache to commit.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylorstatic void pdmacFileCacheEndpointCommit(PPDMACFILEENDPOINTCACHE pEndpointCache)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTSemRWRequestWrite(pEndpointCache->SemRWEntries, RT_INDEFINITE_WAIT);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* The list is moved to a new header to reduce locking overhead. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTSpinlockAcquire(pEndpointCache->LockList, &Tmp);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTListMove(&ListDirtyNotCommitted, &pEndpointCache->ListDirtyNotCommitted);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTSpinlockRelease(pEndpointCache->LockList, &Tmp);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACFILECACHEENTRY pEntry = RTListNodeGetFirst(&ListDirtyNotCommitted,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor while (!RTListNodeIsLast(&ListDirtyNotCommitted, &pEntry->NodeNotCommitted))
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACFILECACHEENTRY pNext = RTListNodeGetNext(&pEntry->NodeNotCommitted, PDMACFILECACHEENTRY,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pdmacFileCacheEntryCommit(pEndpointCache, pEntry);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* Commit the last endpoint */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor Assert(RTListNodeIsLast(&ListDirtyNotCommitted, &pEntry->NodeNotCommitted));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pdmacFileCacheEntryCommit(pEndpointCache, pEntry);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(RTListIsEmpty(&ListDirtyNotCommitted),
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor ("Committed all entries but list is not empty\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTSemRWReleaseWrite(pEndpointCache->SemRWEntries);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(pEndpointCache->pCache->cbDirty >= cbCommitted,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor ("Number of committed bytes exceeds number of dirty bytes\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor ASMAtomicSubU32(&pEndpointCache->pCache->cbDirty, cbCommitted);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Commit all dirty entries in the cache.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @returns nothing.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pCache The global cache instance.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylorstatic void pdmacFileCacheCommitDirtyEntries(PPDMACFILECACHEGLOBAL pCache)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor bool fCommitInProgress = ASMAtomicXchgBool(&pCache->fCommitInProgress, true);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACFILEENDPOINTCACHE pEndpointCache = RTListNodeGetFirst(&pCache->ListEndpoints,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor while (!RTListNodeIsLast(&pCache->ListEndpoints, &pEndpointCache->NodeCacheEndpoint))
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pEndpointCache = RTListNodeGetNext(&pEndpointCache->NodeCacheEndpoint, PDMACFILEENDPOINTCACHE,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* Commit the last endpoint */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor Assert(RTListNodeIsLast(&pCache->ListEndpoints, &pEndpointCache->NodeCacheEndpoint));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor ASMAtomicWriteBool(&pCache->fCommitInProgress, false);
c7facc54c4abed9e554ff80225311e6b7048d3c9Bill Taylor * Adds the given entry as a dirty to the cache.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @returns Flag whether the amount of dirty bytes in the cache exceeds the threshold
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pEndpointCache The endpoint cache the entry belongs to.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pEntry The entry to add.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylorstatic bool pdmacFileCacheAddDirtyEntry(PPDMACFILEENDPOINTCACHE pEndpointCache, PPDMACFILECACHEENTRY pEntry)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACFILECACHEGLOBAL pCache = pEndpointCache->pCache;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* If the commit timer is disabled we commit right away. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pEntry->fFlags |= PDMACFILECACHE_ENTRY_IS_DIRTY;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pdmacFileCacheEntryCommit(pEndpointCache, pEntry);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor else if (!(pEntry->fFlags & PDMACFILECACHE_ENTRY_IS_DIRTY))
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pEntry->fFlags |= PDMACFILECACHE_ENTRY_IS_DIRTY;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTSpinlockAcquire(pEndpointCache->LockList, &Tmp);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTListAppend(&pEndpointCache->ListDirtyNotCommitted, &pEntry->NodeNotCommitted);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTSpinlockRelease(pEndpointCache->LockList, &Tmp);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor uint32_t cbDirty = ASMAtomicAddU32(&pCache->cbDirty, pEntry->cbData);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor fDirtyBytesExceeded = (cbDirty >= pCache->cbCommitDirtyThreshold);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Completes a task segment freeing all ressources and completes the task handle
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * if everything was transfered.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @returns Next task segment handle.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pEndpointCache The endpoint cache.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pTaskSeg Task segment to complete.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param rc Status code to set.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylorstatic PPDMACFILETASKSEG pdmacFileCacheTaskComplete(PPDMACFILEENDPOINTCACHE pEndpointCache, PPDMACFILETASKSEG pTaskSeg, int rc)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMASYNCCOMPLETIONTASKFILE pTaskFile = pTaskSeg->pTask;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor ASMAtomicCmpXchgS32(&pTaskFile->rc, rc, VINF_SUCCESS);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor uint32_t uOld = ASMAtomicSubS32(&pTaskFile->cbTransferLeft, pTaskSeg->cbTransfer);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(uOld >= pTaskSeg->cbTransfer, ("New value would overflow\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor && !ASMAtomicXchgBool(&pTaskFile->fCompleted, true))
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pdmR3AsyncCompletionCompleteTask(&pTaskFile->Core, pTaskFile->rc, true);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Completion callback for I/O tasks.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @returns nothing.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pTask The completed task.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pvUser Opaque user data.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param rc Status code of the completed request.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylorstatic void pdmacFileCacheTaskCompleted(PPDMACTASKFILE pTask, void *pvUser, int rc)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACFILECACHEENTRY pEntry = (PPDMACFILECACHEENTRY)pvUser;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint = pEntry->pEndpoint;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACFILEENDPOINTCACHE pEndpointCache = &pEndpoint->DataCache;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* Reference the entry now as we are clearing the I/O in progres flag
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * which protects the entry till now. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTSemRWRequestWrite(pEndpoint->DataCache.SemRWEntries, RT_INDEFINITE_WAIT);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pEntry->fFlags &= ~PDMACFILECACHE_ENTRY_IO_IN_PROGRESS;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* Process waiting segment list. The data in entry might have changed inbetween. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor bool fDirty = false;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg((pCurr && pEntry->pWaitingTail) || (!pCurr && !pEntry->pWaitingTail),
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor ("The list tail was not updated correctly\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if (pTask->enmTransferType == PDMACTASKFILETRANSFER_WRITE)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(pEndpointCache->cWritesOutstanding > 0, ("Completed write request but outstanding task count is 0\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor ASMAtomicDecU32(&pEndpointCache->cWritesOutstanding);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * An error here is difficult to handle as the original request completed already.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * The error is logged for now and the VM is paused.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * If the user continues the entry is written again in the hope
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * the user fixed the problem and the next write succeeds.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /** @todo r=aeichner: This solution doesn't work
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * The user will get the message but the VM will hang afterwards
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * VMR3Suspend() returns when the VM is suspended but suspending
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * the VM will reopen the images readonly in DrvVD. They are closed first
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * which will close the endpoints. This will block EMT while the
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * I/O manager processes the close request but the IO manager is stuck
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * in the VMR3Suspend call and can't process the request.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Another problem is that closing the VM means flushing the cache
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * but the entry failed and will probably fail again.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * No idea so far how to solve this problem... but the user gets informed
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor LogRel(("I/O cache: Error while writing entry at offset %RTfoff (%u bytes) to file \"%s\"\n",
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pEntry->Core.Key, pEntry->cbData, pEndpoint->Core.pszUri));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor rc = VMSetRuntimeError(pEndpoint->Core.pEpClass->pVM, 0, "CACHE_IOERR",
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor N_("The I/O cache encountered an error while updating data in file \"%s\" (rc=%Rrc). Make sure there is enough free space on the disk and that the disk is working properly. Operation can be resumed afterwards."), pEndpoint->Core.pszUri, rc);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor rc = VMR3Suspend(pEndpoint->Core.pEpClass->pVM);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pEntry->fFlags &= ~PDMACFILECACHE_ENTRY_IS_DIRTY;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(pCurr->fWrite, ("Completed write entries should never have read tasks attached\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor memcpy(pEntry->pbData + pCurr->uBufOffset, pCurr->pvBuf, pCurr->cbTransfer);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pCurr = pdmacFileCacheTaskComplete(pEndpointCache, pCurr, VINF_SUCCESS);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(pTask->enmTransferType == PDMACTASKFILETRANSFER_READ, ("Invalid transfer type\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(!(pEntry->fFlags & PDMACFILECACHE_ENTRY_IS_DIRTY),
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor ("Invalid flags set\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor memcpy(pEntry->pbData + pCurr->uBufOffset, pCurr->pvBuf, pCurr->cbTransfer);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor memcpy(pCurr->pvBuf, pEntry->pbData + pCurr->uBufOffset, pCurr->cbTransfer);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pCurr = pdmacFileCacheTaskComplete(pEndpointCache, pCurr, rc);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor bool fCommit = false;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor fCommit = pdmacFileCacheAddDirtyEntry(pEndpointCache, pEntry);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* Complete a pending flush if all writes have completed */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if (!ASMAtomicReadU32(&pEndpointCache->cWritesOutstanding))
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMASYNCCOMPLETIONTASKFILE pTaskFlush = (PPDMASYNCCOMPLETIONTASKFILE)ASMAtomicXchgPtr((void * volatile *)&pEndpointCache->pTaskFlush, NULL);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pdmR3AsyncCompletionCompleteTask(&pTaskFlush->Core, VINF_SUCCESS, true);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTSemRWReleaseWrite(pEndpoint->DataCache.SemRWEntries);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* Dereference so that it isn't protected anymore except we issued anyother write for it. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Commit timer callback.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylorstatic void pdmacFileCacheCommitTimerCallback(PVM pVM, PTMTIMER pTimer, void *pvUser)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMASYNCCOMPLETIONEPCLASSFILE pClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pvUser;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACFILECACHEGLOBAL pCache = &pClassFile->Cache;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor LogFlowFunc(("Commit interval expired, commiting dirty entries\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor TMTimerSetMillies(pTimer, pCache->u32CommitTimeoutMs);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor LogFlowFunc(("Entries committed, going to sleep\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Initializies the I/O cache.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * returns VBox status code.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pClassFile The global class data for file endpoints.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pCfgNode CFGM node to query configuration data from.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylorint pdmacFileCacheInit(PPDMASYNCCOMPLETIONEPCLASSFILE pClassFile, PCFGMNODE pCfgNode)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACFILECACHEGLOBAL pCache = &pClassFile->Cache;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor rc = CFGMR3QueryU32Def(pCfgNode, "CacheSize", &pCache->cbMax, 5 * _1M);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor LogFlowFunc((": Maximum number of bytes cached %u\n", pCache->cbMax));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* Initialize members */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pCache->cbRecentlyUsedInMax = (pCache->cbMax / 100) * 25; /* 25% of the buffer size */
17a2b317610f531d565bf4e940433aab2d9e6985Bill Taylor pCache->cbRecentlyUsedOutMax = (pCache->cbMax / 100) * 50; /* 50% of the buffer size */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor LogFlowFunc((": cbRecentlyUsedInMax=%u cbRecentlyUsedOutMax=%u\n", pCache->cbRecentlyUsedInMax, pCache->cbRecentlyUsedOutMax));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /** @todo r=aeichner: Experiment to find optimal default values */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor rc = CFGMR3QueryU32Def(pCfgNode, "CacheCommitIntervalMs", &pCache->u32CommitTimeoutMs, 10000 /* 10sec */);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor rc = CFGMR3QueryU32(pCfgNode, "CacheCommitThreshold", &pCache->cbCommitDirtyThreshold);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* Start committing after 50% of the cache are dirty */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pCache->cbCommitDirtyThreshold = pCache->cbMax / 2;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor STAMR3Register(pClassFile->Core.pVM, &pCache->cbMax,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor "Maximum cache size");
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor STAMR3Register(pClassFile->Core.pVM, &pCache->cbCached,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor "Currently used cache");
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor STAMR3Register(pClassFile->Core.pVM, &pCache->LruRecentlyUsedIn.cbCached,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor "Number of bytes cached in MRU list");
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor STAMR3Register(pClassFile->Core.pVM, &pCache->LruRecentlyUsedOut.cbCached,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor "Number of bytes cached in FRU list");
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor STAMR3Register(pClassFile->Core.pVM, &pCache->LruFrequentlyUsed.cbCached,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor "Number of bytes cached in FRU ghost list");
return VINF_SUCCESS;
return rc;
int pdmacFileEpCacheInit(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint, PPDMASYNCCOMPLETIONEPCLASSFILE pClassFile)
#ifdef VBOX_WITH_STATISTICS
return rc;
while (ASMAtomicReadU32(&pEntry->fFlags) & (PDMACFILECACHE_ENTRY_IO_IN_PROGRESS | PDMACFILECACHE_ENTRY_IS_DIRTY))
AssertMsg(!(pEntry->fFlags & (PDMACFILECACHE_ENTRY_IO_IN_PROGRESS | PDMACFILECACHE_ENTRY_IS_DIRTY)),
if (fUpdateCache)
return VINF_SUCCESS;
#ifdef VBOX_WITH_STATISTICS
PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pEndpoint->Core.pEpClass;
static PPDMACFILECACHEENTRY pdmacFileEpCacheGetCacheEntryByOffset(PPDMACFILEENDPOINTCACHE pEndpointCache, RTFOFF off)
if (pEntry)
return pEntry;
static void pdmacFileEpCacheGetCacheBestFitEntryByOffset(PPDMACFILEENDPOINTCACHE pEndpointCache, RTFOFF off,
if (ppEntryAbove)
*ppEntryAbove = (PPDMACFILECACHEENTRY)RTAvlrFileOffsetGetBestFit(pEndpointCache->pTree, off, true /*fAbove*/);
if (*ppEntryAbove)
if (ppEntryBelow)
*ppEntryBelow = (PPDMACFILECACHEENTRY)RTAvlrFileOffsetGetBestFit(pEndpointCache->pTree, off, false /*fAbove*/);
if (*ppEntryBelow)
static void pdmacFileEpCacheInsertEntry(PPDMACFILEENDPOINTCACHE pEndpointCache, PPDMACFILECACHEENTRY pEntry)
return NULL;
if (pbBuffer)
return NULL;
return pEntryNew;
DECLINLINE(void) pdmacFileEpCacheEntryAddWaitingSegment(PPDMACFILECACHEENTRY pEntry, PPDMACFILETASKSEG pSeg)
DECLINLINE(bool) pdmacFileEpCacheEntryFlagIsSetClearAcquireLock(PPDMACFILEENDPOINTCACHE pEndpointCache,
if (fPassed)
if (!fPassed)
return fPassed;
while (cbData)
while (cbData)
bool fWrite)
while (cbData)
while (cbData)
unsigned uAlignment,
off,
off,
if ( pEntryBelow
if ( pEntryAbove
if (pEntryAbove)
if (pEntryBelow)
if (pEntryAbove)
return cbInEntry;
unsigned uAlignment,
if (fEnough)
pbBuffer);
return pEntryNew;
int pdmacFileEpCacheRead(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint, PPDMASYNCCOMPLETIONTASKFILE pTask,
while (cbRead)
if (pEntry)
if (!cbRead)
&IoMemCtx,
pdmacFileCacheEntryRemoveFromList(pEntry); /* Remove it before we remove data, otherwise it may get freed when evicting data. */
if (fEnough)
if (pbBuffer)
&IoMemCtx,
#ifdef VBOX_WITH_IO_READ_CACHE
&cbToRead);
if (pEntryNew)
if (!cbRead)
&IoMemCtx,
LogFlow(("Couldn't evict %u bytes from the cache. Remaining request will be passed through\n", cbToRead));
if (pEntryAbove)
return rc;
int pdmacFileEpCacheWrite(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint, PPDMASYNCCOMPLETIONTASKFILE pTask,
while (cbWrite)
if (pEntry)
if (!cbWrite)
&IoMemCtx,
&IoMemCtx,
if (fCommit)
pdmacFileCacheEntryRemoveFromList(pEntry); /* Remove it before we remove data, otherwise it may get freed when evicting data. */
if (fEnough)
if (pbBuffer)
&IoMemCtx,
&cbToWrite);
if (pEntryNew)
if (fCommit)
&IoMemCtx,
LogFlow(("Couldn't evict %u bytes from the cache. Remaining request will be passed through\n", cbToWrite));
return rc;
int pdmacFileEpCacheFlush(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint, PPDMASYNCCOMPLETIONTASKFILE pTask)
return rc;