PDMAsyncCompletionFileCache.cpp revision 22ec733a5e041fcdfe02fce2eafc9faf8b0077dd
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor/* $Id$ */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor/** @file
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * PDM Async I/O - Transport data asynchronous in R3 using EMT.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * File data cache.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor/*
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Copyright (C) 2006-2008 Sun Microsystems, Inc.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor *
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 *
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.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
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
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor/*******************************************************************************
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor* Header Files *
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor*******************************************************************************/
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor#define LOG_GROUP LOG_GROUP_PDM_ASYNC_COMPLETION
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor#include <iprt/types.h>
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor#include <iprt/mem.h>
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor#include <iprt/path.h>
de710d24d2fae4468e64da999e1d952a247f142cJosef 'Jeff' Sipek#include <VBox/log.h>
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor#include <VBox/stam.h>
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor#include "PDMAsyncCompletionFileInternal.h"
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor/**
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * A I/O memory context.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylortypedef struct PDMIOMEMCTX
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor{
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /** Pointer to the scatter/gather list. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PCPDMDATASEG paDataSeg;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /** Number of segments. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor size_t cSegments;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /** Current segment we are in. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor unsigned iSegIdx;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /** Pointer to the current buffer. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor uint8_t *pbBuf;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /** Number of bytes left in the current buffer. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor size_t cbBufLeft;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor} PDMIOMEMCTX, *PPDMIOMEMCTX;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor#ifdef VBOX_STRICT
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor# define PDMACFILECACHE_IS_CRITSECT_OWNER(Cache) \
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor do \
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor { \
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(RTCritSectIsOwner(&Cache->CritSect), \
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor ("Thread does not own critical section\n"));\
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor } while(0)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor# define PDMACFILECACHE_EP_IS_SEMRW_WRITE_OWNER(pEpCache) \
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor do \
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor { \
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(RTSemRWIsWriteOwner(pEpCache->SemRWEntries), \
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor ("Thread is not exclusive owner of the per endpoint RW semaphore\n")); \
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor } while(0)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor# define PDMACFILECACHE_EP_IS_SEMRW_READ_OWNER(pEpCache) \
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor do \
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor { \
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(RTSemRWIsReadOwner(pEpCache->SemRWEntries), \
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor ("Thread is not read owner of the per endpoint RW semaphore\n")); \
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor } while(0)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor#else
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#endif
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor/*******************************************************************************
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor* Internal Functions *
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor*******************************************************************************/
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylorstatic void pdmacFileCacheTaskCompleted(PPDMACTASKFILE pTask, void *pvUser, int rc);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor/**
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Decrement the reference counter of the given cache entry.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor *
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @returns nothing.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pEntry The entry to release.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor */
9e39c5ba00a55fa05777cc94b148296af305e135Bill TaylorDECLINLINE(void) pdmacFileEpCacheEntryRelease(PPDMACFILECACHEENTRY pEntry)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor{
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(pEntry->cRefs > 0, ("Trying to release a not referenced entry\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor ASMAtomicDecU32(&pEntry->cRefs);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor}
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor/**
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Increment the reference counter of the given cache entry.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor *
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @returns nothing.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pEntry The entry to reference.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor */
9e39c5ba00a55fa05777cc94b148296af305e135Bill TaylorDECLINLINE(void) pdmacFileEpCacheEntryRef(PPDMACFILECACHEENTRY pEntry)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor{
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor ASMAtomicIncU32(&pEntry->cRefs);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor}
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor/**
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Initialize a I/O memory context.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor *
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 Taylor */
9e39c5ba00a55fa05777cc94b148296af305e135Bill TaylorDECLINLINE(void) pdmIoMemCtxInit(PPDMIOMEMCTX pIoMemCtx, PCPDMDATASEG paDataSeg, size_t cSegments)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor{
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg((cSegments > 0) && paDataSeg, ("Trying to initialize a I/O memory context without a S/G list\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pIoMemCtx->paDataSeg = paDataSeg;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pIoMemCtx->cSegments = cSegments;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pIoMemCtx->iSegIdx = 0;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pIoMemCtx->pbBuf = (uint8_t *)paDataSeg[0].pvSeg;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pIoMemCtx->cbBufLeft = paDataSeg[0].cbSeg;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor}
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor/**
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Return a buffer from the I/O memory context.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor *
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 Taylor */
9e39c5ba00a55fa05777cc94b148296af305e135Bill TaylorDECLINLINE(uint8_t *) pdmIoMemCtxGetBuffer(PPDMIOMEMCTX pIoMemCtx, size_t *pcbData)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor{
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor size_t cbData = RT_MIN(*pcbData, pIoMemCtx->cbBufLeft);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor uint8_t *pbBuf = pIoMemCtx->pbBuf;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pIoMemCtx->cbBufLeft -= cbData;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* Advance to the next segment if required. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if (!pIoMemCtx->cbBufLeft)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pIoMemCtx->iSegIdx++;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if (RT_UNLIKELY(pIoMemCtx->iSegIdx == pIoMemCtx->cSegments))
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pIoMemCtx->cbBufLeft = 0;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pIoMemCtx->pbBuf = NULL;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor else
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pIoMemCtx->pbBuf = (uint8_t *)pIoMemCtx->paDataSeg[pIoMemCtx->iSegIdx].pvSeg;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pIoMemCtx->cbBufLeft = pIoMemCtx->paDataSeg[pIoMemCtx->iSegIdx].cbSeg;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor *pcbData = cbData;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor else
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pIoMemCtx->pbBuf += cbData;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor return pbBuf;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor}
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor#ifdef DEBUG
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylorstatic void pdmacFileCacheValidate(PPDMACFILECACHEGLOBAL pCache)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor{
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* Amount of cached data should never exceed the maximum amount. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(pCache->cbCached <= pCache->cbMax,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor ("Current amount of cached data exceeds maximum\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
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
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(pCache->LruRecentlyUsedOut.cbCached <= pCache->cbRecentlyUsedOutMax,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor ("Paged out list exceeds maximum\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor}
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor#endif
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill TaylorDECLINLINE(void) pdmacFileCacheLockEnter(PPDMACFILECACHEGLOBAL pCache)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor{
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTCritSectEnter(&pCache->CritSect);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor#ifdef DEBUG
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pdmacFileCacheValidate(pCache);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor#endif
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor}
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill TaylorDECLINLINE(void) pdmacFileCacheLockLeave(PPDMACFILECACHEGLOBAL pCache)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor{
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor#ifdef DEBUG
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pdmacFileCacheValidate(pCache);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor#endif
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTCritSectLeave(&pCache->CritSect);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor}
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill TaylorDECLINLINE(void) pdmacFileCacheSub(PPDMACFILECACHEGLOBAL pCache, uint32_t cbAmount)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor{
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PDMACFILECACHE_IS_CRITSECT_OWNER(pCache);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pCache->cbCached -= cbAmount;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor}
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill TaylorDECLINLINE(void) pdmacFileCacheAdd(PPDMACFILECACHEGLOBAL pCache, uint32_t cbAmount)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor{
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PDMACFILECACHE_IS_CRITSECT_OWNER(pCache);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pCache->cbCached += cbAmount;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor}
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill TaylorDECLINLINE(void) pdmacFileCacheListAdd(PPDMACFILELRULIST pList, uint32_t cbAmount)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor{
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pList->cbCached += cbAmount;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor}
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill TaylorDECLINLINE(void) pdmacFileCacheListSub(PPDMACFILELRULIST pList, uint32_t cbAmount)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor{
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pList->cbCached -= cbAmount;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor}
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor#ifdef PDMACFILECACHE_WITH_LRULIST_CHECKS
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor/**
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Checks consistency of a LRU list.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor *
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 Taylor */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylorstatic void pdmacFileCacheCheckList(PPDMACFILELRULIST pList, PPDMACFILECACHEENTRY pNotInList)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor{
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACFILECACHEENTRY pCurr = pList->pHead;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* Check that there are no double entries and no cycles in the list. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor while (pCurr)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACFILECACHEENTRY pNext = pCurr->pNext;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor while (pNext)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(pCurr != pNext,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor ("Entry %#p is at least two times in list %#p or there is a cycle in the list\n",
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pCurr, pList));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pNext = pNext->pNext;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(pCurr != pNotInList, ("Not allowed entry %#p is in list\n", pCurr));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if (!pCurr->pNext)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(pCurr == pList->pTail, ("End of list reached but last element is not list tail\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pCurr = pCurr->pNext;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor}
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor#endif
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor/**
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Unlinks a cache entry from the LRU list it is assigned to.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor *
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @returns nothing.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pEntry The entry to unlink.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylorstatic void pdmacFileCacheEntryRemoveFromList(PPDMACFILECACHEENTRY pEntry)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor{
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACFILELRULIST pList = pEntry->pList;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACFILECACHEENTRY pPrev, pNext;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor LogFlowFunc((": Deleting entry %#p from list %#p\n", pEntry, pList));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertPtr(pList);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor#ifdef PDMACFILECACHE_WITH_LRULIST_CHECKS
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pdmacFileCacheCheckList(pList, NULL);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor#endif
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pPrev = pEntry->pPrev;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pNext = pEntry->pNext;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
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
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if (pPrev)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pPrev->pNext = pNext;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor else
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pList->pHead = pNext;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if (pNext)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pNext->pPrev = NULL;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if (pNext)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pNext->pPrev = pPrev;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor else
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pList->pTail = pPrev;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if (pPrev)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pPrev->pNext = NULL;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pEntry->pList = NULL;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pEntry->pPrev = NULL;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pEntry->pNext = NULL;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pdmacFileCacheListSub(pList, pEntry->cbData);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor#ifdef PDMACFILECACHE_WITH_LRULIST_CHECKS
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pdmacFileCacheCheckList(pList, pEntry);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor#endif
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor}
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor/**
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Adds a cache entry to the given LRU list unlinking it from the currently
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * assigned list if needed.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor *
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @returns nothing.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pList List to the add entry to.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pEntry Entry to add.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylorstatic void pdmacFileCacheEntryAddToList(PPDMACFILELRULIST pList, PPDMACFILECACHEENTRY pEntry)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor{
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor LogFlowFunc((": Adding entry %#p to list %#p\n", pEntry, pList));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor#ifdef PDMACFILECACHE_WITH_LRULIST_CHECKS
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pdmacFileCacheCheckList(pList, NULL);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor#endif
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* Remove from old list if needed */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if (pEntry->pList)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pdmacFileCacheEntryRemoveFromList(pEntry);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pEntry->pNext = pList->pHead;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if (pList->pHead)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pList->pHead->pPrev = pEntry;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor else
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor Assert(!pList->pTail);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pList->pTail = pEntry;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pEntry->pPrev = NULL;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pList->pHead = pEntry;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pdmacFileCacheListAdd(pList, pEntry->cbData);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pEntry->pList = pList;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor#ifdef PDMACFILECACHE_WITH_LRULIST_CHECKS
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pdmacFileCacheCheckList(pList, NULL);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor#endif
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor}
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor/**
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Destroys a LRU list freeing all entries.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor *
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @returns nothing
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pList Pointer to the LRU list to destroy.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor *
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @note The caller must own the critical section of the cache.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylorstatic void pdmacFileCacheDestroyList(PPDMACFILELRULIST pList)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor{
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor while (pList->pHead)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACFILECACHEENTRY pEntry = pList->pHead;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pList->pHead = pEntry->pNext;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
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
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTMemPageFree(pEntry->pbData);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTMemFree(pEntry);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor}
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor/**
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 *
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 *
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 Taylor * moment.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylorstatic size_t pdmacFileCacheEvictPagesFrom(PPDMACFILECACHEGLOBAL pCache, size_t cbData,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACFILELRULIST pListSrc, PPDMACFILELRULIST pGhostListDst,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor bool fReuseBuffer, uint8_t **ppbBuffer)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor{
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor size_t cbEvicted = 0;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PDMACFILECACHE_IS_CRITSECT_OWNER(pCache);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(cbData > 0, ("Evicting 0 bytes not possible\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg( !pGhostListDst
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor || (pGhostListDst == &pCache->LruRecentlyUsedOut),
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor ("Destination list must be NULL or the recently used but paged out list\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if (fReuseBuffer)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertPtr(ppbBuffer);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor *ppbBuffer = NULL;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* Start deleting from the tail. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACFILECACHEENTRY pEntry = pListSrc->pTail;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor while ((cbEvicted < cbData) && pEntry)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACFILECACHEENTRY pCurr = pEntry;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pEntry = pEntry->pPrev;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
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 && (ASMAtomicReadU32(&pCurr->cRefs) == 0))
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
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
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if (!(pCurr->fFlags & PDMACFILECACHE_NOT_EVICTABLE)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor && (ASMAtomicReadU32(&pCurr->cRefs) == 0))
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor LogFlow(("Evicting entry %#p (%u bytes)\n", pCurr, pCurr->cbData));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if (fReuseBuffer && (pCurr->cbData == cbData))
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor STAM_COUNTER_INC(&pCache->StatBuffersReused);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor *ppbBuffer = pCurr->pbData;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor else if (pCurr->pbData)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTMemPageFree(pCurr->pbData);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pCurr->pbData = NULL;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor cbEvicted += pCurr->cbData;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pdmacFileCacheEntryRemoveFromList(pCurr);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pdmacFileCacheSub(pCache, pCurr->cbData);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if (pGhostListDst)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTSemRWReleaseWrite(pEndpointCache->SemRWEntries);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACFILECACHEENTRY pGhostEntFree = pGhostListDst->pTail;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
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 && pGhostEntFree)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACFILECACHEENTRY pFree = pGhostEntFree;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACFILEENDPOINTCACHE pEndpointCacheFree = &pFree->pEndpoint->DataCache;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pGhostEntFree = pGhostEntFree->pPrev;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTSemRWRequestWrite(pEndpointCacheFree->SemRWEntries, RT_INDEFINITE_WAIT);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if (ASMAtomicReadU32(&pFree->cRefs) == 0)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pdmacFileCacheEntryRemoveFromList(pFree);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
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
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTMemFree(pFree);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTSemRWReleaseWrite(pEndpointCacheFree->SemRWEntries);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if (pGhostListDst->cbCached + pCurr->cbData > pCache->cbRecentlyUsedOutMax)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
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
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTMemFree(pCurr);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor else
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pdmacFileCacheEntryAddToList(pGhostListDst, pCurr);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor else
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
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
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTSemRWReleaseWrite(pEndpointCache->SemRWEntries);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTMemFree(pCurr);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor else
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor LogFlow(("Entry %#p (%u bytes) is still in progress and can't be evicted\n", pCurr, pCurr->cbData));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor return cbEvicted;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor}
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylorstatic bool pdmacFileCacheReclaim(PPDMACFILECACHEGLOBAL pCache, size_t cbData, bool fReuseBuffer, uint8_t **ppbBuffer)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor{
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor size_t cbRemoved = 0;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if ((pCache->cbCached + cbData) < pCache->cbMax)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor return true;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor else if ((pCache->LruRecentlyUsedIn.cbCached + cbData) > pCache->cbRecentlyUsedInMax)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
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
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /*
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * If it was not possible to remove enough entries
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * try the frequently accessed cache.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if (cbRemoved < cbData)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
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
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /*
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 */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if (!cbRemoved)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor cbRemoved += pdmacFileCacheEvictPagesFrom(pCache, cbData, &pCache->LruFrequentlyUsed,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor NULL, fReuseBuffer, ppbBuffer);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor else
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor cbRemoved += pdmacFileCacheEvictPagesFrom(pCache, cbData - cbRemoved, &pCache->LruFrequentlyUsed,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor NULL, false, NULL);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor else
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* We have to remove entries from frequently access list. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor cbRemoved = pdmacFileCacheEvictPagesFrom(pCache, cbData, &pCache->LruFrequentlyUsed,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor NULL, fReuseBuffer, ppbBuffer);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor LogFlowFunc((": removed %u bytes, requested %u\n", cbRemoved, cbData));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor return (cbRemoved >= cbData);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor}
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor/**
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Initiates a read I/O task for the given entry.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor *
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @returns nothing.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pEntry The entry to fetch the data to.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylorstatic void pdmacFileCacheReadFromEndpoint(PPDMACFILECACHEENTRY pEntry)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor{
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor LogFlowFunc((": Reading data into cache entry %#p\n", pEntry));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* Make sure no one evicts the entry while it is accessed. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pEntry->fFlags |= PDMACFILECACHE_ENTRY_IO_IN_PROGRESS;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACTASKFILE pIoTask = pdmacFileTaskAlloc(pEntry->pEndpoint);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertPtr(pIoTask);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(pEntry->pbData, ("Entry is in ghost state\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pIoTask->pEndpoint = pEntry->pEndpoint;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pIoTask->enmTransferType = PDMACTASKFILETRANSFER_READ;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pIoTask->Off = pEntry->Core.Key;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pIoTask->DataSeg.cbSeg = pEntry->cbData;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pIoTask->DataSeg.pvSeg = pEntry->pbData;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pIoTask->pvUser = pEntry;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pIoTask->pfnCompleted = pdmacFileCacheTaskCompleted;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* Send it off to the I/O manager. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pdmacFileEpAddTask(pEntry->pEndpoint, pIoTask);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor}
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor/**
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Initiates a write I/O task for the given entry.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor *
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @returns nothing.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pEntry The entry to read the data from.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylorstatic void pdmacFileCacheWriteToEndpoint(PPDMACFILECACHEENTRY pEntry)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor{
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor LogFlowFunc((": Writing data from cache entry %#p\n", pEntry));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* Make sure no one evicts the entry while it is accessed. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pEntry->fFlags |= PDMACFILECACHE_ENTRY_IO_IN_PROGRESS;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACTASKFILE pIoTask = pdmacFileTaskAlloc(pEntry->pEndpoint);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertPtr(pIoTask);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(pEntry->pbData, ("Entry is in ghost state\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pIoTask->pEndpoint = pEntry->pEndpoint;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pIoTask->enmTransferType = PDMACTASKFILETRANSFER_WRITE;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pIoTask->Off = pEntry->Core.Key;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pIoTask->DataSeg.cbSeg = pEntry->cbData;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pIoTask->DataSeg.pvSeg = pEntry->pbData;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pIoTask->pvUser = pEntry;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pIoTask->pfnCompleted = pdmacFileCacheTaskCompleted;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor ASMAtomicIncU32(&pEntry->pEndpoint->DataCache.cWritesOutstanding);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* Send it off to the I/O manager. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pdmacFileEpAddTask(pEntry->pEndpoint, pIoTask);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor}
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor/**
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Commit a single dirty entry to the endpoint
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor *
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @returns nothing
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pEntry The entry to commit.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylorstatic void pdmacFileCacheEntryCommit(PPDMACFILEENDPOINTCACHE pEndpointCache, PPDMACFILECACHEENTRY pEntry)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor{
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor NOREF(pEndpointCache);
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
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pdmacFileCacheWriteToEndpoint(pEntry);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor}
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor/**
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Commit all dirty entries for a single endpoint.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor *
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @returns nothing.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pEndpointCache The endpoint cache to commit.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylorstatic void pdmacFileCacheEndpointCommit(PPDMACFILEENDPOINTCACHE pEndpointCache)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor{
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor uint32_t cbCommitted = 0;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTSemRWRequestWrite(pEndpointCache->SemRWEntries, RT_INDEFINITE_WAIT);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* The list is moved to a new header to reduce locking overhead. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTLISTNODE ListDirtyNotCommitted;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTSPINLOCKTMP Tmp;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTListInit(&ListDirtyNotCommitted);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTSpinlockAcquire(pEndpointCache->LockList, &Tmp);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTListMove(&ListDirtyNotCommitted, &pEndpointCache->ListDirtyNotCommitted);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTSpinlockRelease(pEndpointCache->LockList, &Tmp);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if (!RTListIsEmpty(&ListDirtyNotCommitted))
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACFILECACHEENTRY pEntry = RTListNodeGetFirst(&ListDirtyNotCommitted,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PDMACFILECACHEENTRY,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor NodeNotCommitted);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor while (!RTListNodeIsLast(&ListDirtyNotCommitted, &pEntry->NodeNotCommitted))
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACFILECACHEENTRY pNext = RTListNodeGetNext(&pEntry->NodeNotCommitted, PDMACFILECACHEENTRY,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor NodeNotCommitted);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pdmacFileCacheEntryCommit(pEndpointCache, pEntry);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor cbCommitted += pEntry->cbData;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTListNodeRemove(&pEntry->NodeNotCommitted);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pEntry = pNext;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* Commit the last endpoint */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor Assert(RTListNodeIsLast(&ListDirtyNotCommitted, &pEntry->NodeNotCommitted));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pdmacFileCacheEntryCommit(pEndpointCache, pEntry);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTListNodeRemove(&pEntry->NodeNotCommitted);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(RTListIsEmpty(&ListDirtyNotCommitted),
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor ("Committed all entries but list is not empty\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
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}
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor/**
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Commit all dirty entries in the cache.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor *
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @returns nothing.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * @param pCache The global cache instance.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylorstatic void pdmacFileCacheCommitDirtyEntries(PPDMACFILECACHEGLOBAL pCache)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor{
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor bool fCommitInProgress = ASMAtomicXchgBool(&pCache->fCommitInProgress, true);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if (!fCommitInProgress)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pdmacFileCacheLockEnter(pCache);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor Assert(!RTListIsEmpty(&pCache->ListEndpoints));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACFILEENDPOINTCACHE pEndpointCache = RTListNodeGetFirst(&pCache->ListEndpoints,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PDMACFILEENDPOINTCACHE,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor NodeCacheEndpoint);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertPtr(pEndpointCache);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor while (!RTListNodeIsLast(&pCache->ListEndpoints, &pEndpointCache->NodeCacheEndpoint))
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pdmacFileCacheEndpointCommit(pEndpointCache);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pEndpointCache = RTListNodeGetNext(&pEndpointCache->NodeCacheEndpoint, PDMACFILEENDPOINTCACHE,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor NodeCacheEndpoint);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* Commit the last endpoint */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor Assert(RTListNodeIsLast(&pCache->ListEndpoints, &pEndpointCache->NodeCacheEndpoint));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pdmacFileCacheEndpointCommit(pEndpointCache);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pdmacFileCacheLockLeave(pCache);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor ASMAtomicWriteBool(&pCache->fCommitInProgress, false);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor}
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor/**
c7facc54c4abed9e554ff80225311e6b7048d3c9Bill Taylor * Adds the given entry as a dirty to the cache.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor *
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 Taylor */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylorstatic bool pdmacFileCacheAddDirtyEntry(PPDMACFILEENDPOINTCACHE pEndpointCache, PPDMACFILECACHEENTRY pEntry)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor{
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor bool fDirtyBytesExceeded = false;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACFILECACHEGLOBAL pCache = pEndpointCache->pCache;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* If the commit timer is disabled we commit right away. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if (pCache->u32CommitTimeoutMs == 0)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pEntry->fFlags |= PDMACFILECACHE_ENTRY_IS_DIRTY;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pdmacFileCacheEntryCommit(pEndpointCache, pEntry);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor else if (!(pEntry->fFlags & PDMACFILECACHE_ENTRY_IS_DIRTY))
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pEntry->fFlags |= PDMACFILECACHE_ENTRY_IS_DIRTY;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTSPINLOCKTMP Tmp;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTSpinlockAcquire(pEndpointCache->LockList, &Tmp);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTListAppend(&pEndpointCache->ListDirtyNotCommitted, &pEntry->NodeNotCommitted);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTSpinlockRelease(pEndpointCache->LockList, &Tmp);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor uint32_t cbDirty = ASMAtomicAddU32(&pCache->cbDirty, pEntry->cbData);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor fDirtyBytesExceeded = (cbDirty >= pCache->cbCommitDirtyThreshold);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor return fDirtyBytesExceeded;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor}
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor/**
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Completes a task segment freeing all ressources and completes the task handle
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * if everything was transfered.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor *
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 Taylor */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylorstatic PPDMACFILETASKSEG pdmacFileCacheTaskComplete(PPDMACFILEENDPOINTCACHE pEndpointCache, PPDMACFILETASKSEG pTaskSeg, int rc)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor{
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACFILETASKSEG pNext = pTaskSeg->pNext;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMASYNCCOMPLETIONTASKFILE pTaskFile = pTaskSeg->pTask;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if (RT_FAILURE(rc))
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor ASMAtomicCmpXchgS32(&pTaskFile->rc, rc, VINF_SUCCESS);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor uint32_t uOld = ASMAtomicSubS32(&pTaskFile->cbTransferLeft, pTaskSeg->cbTransfer);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(uOld >= pTaskSeg->cbTransfer, ("New value would overflow\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if (!(uOld - pTaskSeg->cbTransfer)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor && !ASMAtomicXchgBool(&pTaskFile->fCompleted, true))
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pdmR3AsyncCompletionCompleteTask(&pTaskFile->Core, pTaskFile->rc, true);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTMemFree(pTaskSeg);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor return pNext;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor}
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor/**
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Completion callback for I/O tasks.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor *
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 Taylor */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylorstatic void pdmacFileCacheTaskCompleted(PPDMACTASKFILE pTask, void *pvUser, int rc)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor{
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACFILECACHEENTRY pEntry = (PPDMACFILECACHEENTRY)pvUser;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACFILECACHEGLOBAL pCache = pEntry->pCache;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint = pEntry->pEndpoint;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACFILEENDPOINTCACHE pEndpointCache = &pEndpoint->DataCache;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
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 pdmacFileEpCacheEntryRef(pEntry);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTSemRWRequestWrite(pEndpoint->DataCache.SemRWEntries, RT_INDEFINITE_WAIT);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pEntry->fFlags &= ~PDMACFILECACHE_ENTRY_IO_IN_PROGRESS;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* Process waiting segment list. The data in entry might have changed inbetween. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor bool fDirty = false;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACFILETASKSEG pCurr = pEntry->pWaitingHead;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg((pCurr && pEntry->pWaitingTail) || (!pCurr && !pEntry->pWaitingTail),
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor ("The list tail was not updated correctly\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pEntry->pWaitingTail = NULL;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pEntry->pWaitingHead = NULL;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if (pTask->enmTransferType == PDMACTASKFILETRANSFER_WRITE)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(pEndpointCache->cWritesOutstanding > 0, ("Completed write request but outstanding task count is 0\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor ASMAtomicDecU32(&pEndpointCache->cWritesOutstanding);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /*
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 */
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 * at least.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if (RT_FAILURE(rc))
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
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
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 AssertRC(rc);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor rc = VMR3Suspend(pEndpoint->Core.pEpClass->pVM);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor else
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pEntry->fFlags &= ~PDMACFILECACHE_ENTRY_IS_DIRTY;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor while (pCurr)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertMsg(pCurr->fWrite, ("Completed write entries should never have read tasks attached\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor memcpy(pEntry->pbData + pCurr->uBufOffset, pCurr->pvBuf, pCurr->cbTransfer);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor fDirty = true;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pCurr = pdmacFileCacheTaskComplete(pEndpointCache, pCurr, VINF_SUCCESS);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor else
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
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
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor while (pCurr)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if (pCurr->fWrite)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor memcpy(pEntry->pbData + pCurr->uBufOffset, pCurr->pvBuf, pCurr->cbTransfer);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor fDirty = true;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor else
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor memcpy(pCurr->pvBuf, pEntry->pbData + pCurr->uBufOffset, pCurr->cbTransfer);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pCurr = pdmacFileCacheTaskComplete(pEndpointCache, pCurr, rc);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor bool fCommit = false;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if (fDirty)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor fCommit = pdmacFileCacheAddDirtyEntry(pEndpointCache, pEntry);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* Complete a pending flush if all writes have completed */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if (!ASMAtomicReadU32(&pEndpointCache->cWritesOutstanding))
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMASYNCCOMPLETIONTASKFILE pTaskFlush = (PPDMASYNCCOMPLETIONTASKFILE)ASMAtomicXchgPtr((void * volatile *)&pEndpointCache->pTaskFlush, NULL);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if (pTaskFlush)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pdmR3AsyncCompletionCompleteTask(&pTaskFlush->Core, VINF_SUCCESS, true);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTSemRWReleaseWrite(pEndpoint->DataCache.SemRWEntries);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* Dereference so that it isn't protected anymore except we issued anyother write for it. */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pdmacFileEpCacheEntryRelease(pEntry);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if (fCommit)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pdmacFileCacheCommitDirtyEntries(pCache);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor}
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor/**
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Commit timer callback.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylorstatic void pdmacFileCacheCommitTimerCallback(PVM pVM, PTMTIMER pTimer, void *pvUser)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor{
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMASYNCCOMPLETIONEPCLASSFILE pClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pvUser;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACFILECACHEGLOBAL pCache = &pClassFile->Cache;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor LogFlowFunc(("Commit interval expired, commiting dirty entries\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if (ASMAtomicReadU32(&pCache->cbDirty) > 0)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pdmacFileCacheCommitDirtyEntries(pCache);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor TMTimerSetMillies(pTimer, pCache->u32CommitTimeoutMs);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor LogFlowFunc(("Entries committed, going to sleep\n"));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor}
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor/**
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor * Initializies the I/O cache.
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor *
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 Taylor */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylorint pdmacFileCacheInit(PPDMASYNCCOMPLETIONEPCLASSFILE pClassFile, PCFGMNODE pCfgNode)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor{
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor int rc = VINF_SUCCESS;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor PPDMACFILECACHEGLOBAL pCache = &pClassFile->Cache;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor rc = CFGMR3QueryU32Def(pCfgNode, "CacheSize", &pCache->cbMax, 5 * _1M);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertLogRelRCReturn(rc, rc);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor RTListInit(&pCache->ListEndpoints);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pCache->cRefs = 0;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pCache->cbCached = 0;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pCache->fCommitInProgress = 0;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor LogFlowFunc((": Maximum number of bytes cached %u\n", pCache->cbMax));
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* Initialize members */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pCache->LruRecentlyUsedIn.pHead = NULL;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pCache->LruRecentlyUsedIn.pTail = NULL;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pCache->LruRecentlyUsedIn.cbCached = 0;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pCache->LruRecentlyUsedOut.pHead = NULL;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pCache->LruRecentlyUsedOut.pTail = NULL;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pCache->LruRecentlyUsedOut.cbCached = 0;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pCache->LruFrequentlyUsed.pHead = NULL;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pCache->LruFrequentlyUsed.pTail = NULL;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pCache->LruFrequentlyUsed.cbCached = 0;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
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
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /** @todo r=aeichner: Experiment to find optimal default values */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor rc = CFGMR3QueryU32Def(pCfgNode, "CacheCommitIntervalMs", &pCache->u32CommitTimeoutMs, 10000 /* 10sec */);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor AssertLogRelRCReturn(rc, rc);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor rc = CFGMR3QueryU32(pCfgNode, "CacheCommitThreshold", &pCache->cbCommitDirtyThreshold);
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor if ( rc == VERR_CFGM_VALUE_NOT_FOUND
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor || rc == VERR_CFGM_NO_PARENT)
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor {
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor /* Start committing after 50% of the cache are dirty */
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor pCache->cbCommitDirtyThreshold = pCache->cbMax / 2;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor }
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor else
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor return rc;
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor STAMR3Register(pClassFile->Core.pVM, &pCache->cbMax,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor STAMTYPE_U32, STAMVISIBILITY_ALWAYS,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor "/PDM/AsyncCompletion/File/cbMax",
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor STAMUNIT_BYTES,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor "Maximum cache size");
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor STAMR3Register(pClassFile->Core.pVM, &pCache->cbCached,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor STAMTYPE_U32, STAMVISIBILITY_ALWAYS,
de710d24d2fae4468e64da999e1d952a247f142cJosef 'Jeff' Sipek "/PDM/AsyncCompletion/File/cbCached",
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor STAMUNIT_BYTES,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor "Currently used cache");
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor STAMR3Register(pClassFile->Core.pVM, &pCache->LruRecentlyUsedIn.cbCached,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor STAMTYPE_U32, STAMVISIBILITY_ALWAYS,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor "/PDM/AsyncCompletion/File/cbCachedMruIn",
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor STAMUNIT_BYTES,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor "Number of bytes cached in MRU list");
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor STAMR3Register(pClassFile->Core.pVM, &pCache->LruRecentlyUsedOut.cbCached,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor STAMTYPE_U32, STAMVISIBILITY_ALWAYS,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor "/PDM/AsyncCompletion/File/cbCachedMruOut",
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor STAMUNIT_BYTES,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor "Number of bytes cached in FRU list");
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor STAMR3Register(pClassFile->Core.pVM, &pCache->LruFrequentlyUsed.cbCached,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor STAMTYPE_U32, STAMVISIBILITY_ALWAYS,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor "/PDM/AsyncCompletion/File/cbCachedFru",
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor STAMUNIT_BYTES,
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor "Number of bytes cached in FRU ghost list");
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor
9e39c5ba00a55fa05777cc94b148296af305e135Bill Taylor#ifdef VBOX_WITH_STATISTICS
STAMR3Register(pClassFile->Core.pVM, &pCache->cHits,
STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS,
"/PDM/AsyncCompletion/File/CacheHits",
STAMUNIT_COUNT, "Number of hits in the cache");
STAMR3Register(pClassFile->Core.pVM, &pCache->cPartialHits,
STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS,
"/PDM/AsyncCompletion/File/CachePartialHits",
STAMUNIT_COUNT, "Number of partial hits in the cache");
STAMR3Register(pClassFile->Core.pVM, &pCache->cMisses,
STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS,
"/PDM/AsyncCompletion/File/CacheMisses",
STAMUNIT_COUNT, "Number of misses when accessing the cache");
STAMR3Register(pClassFile->Core.pVM, &pCache->StatRead,
STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS,
"/PDM/AsyncCompletion/File/CacheRead",
STAMUNIT_BYTES, "Number of bytes read from the cache");
STAMR3Register(pClassFile->Core.pVM, &pCache->StatWritten,
STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS,
"/PDM/AsyncCompletion/File/CacheWritten",
STAMUNIT_BYTES, "Number of bytes written to the cache");
STAMR3Register(pClassFile->Core.pVM, &pCache->StatTreeGet,
STAMTYPE_PROFILE_ADV, STAMVISIBILITY_ALWAYS,
"/PDM/AsyncCompletion/File/CacheTreeGet",
STAMUNIT_TICKS_PER_CALL, "Time taken to access an entry in the tree");
STAMR3Register(pClassFile->Core.pVM, &pCache->StatTreeInsert,
STAMTYPE_PROFILE_ADV, STAMVISIBILITY_ALWAYS,
"/PDM/AsyncCompletion/File/CacheTreeInsert",
STAMUNIT_TICKS_PER_CALL, "Time taken to insert an entry in the tree");
STAMR3Register(pClassFile->Core.pVM, &pCache->StatTreeRemove,
STAMTYPE_PROFILE_ADV, STAMVISIBILITY_ALWAYS,
"/PDM/AsyncCompletion/File/CacheTreeRemove",
STAMUNIT_TICKS_PER_CALL, "Time taken to remove an entry an the tree");
STAMR3Register(pClassFile->Core.pVM, &pCache->StatBuffersReused,
STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS,
"/PDM/AsyncCompletion/File/CacheBuffersReused",
STAMUNIT_COUNT, "Number of times a buffer could be reused");
#endif
/* Initialize the critical section */
rc = RTCritSectInit(&pCache->CritSect);
if (RT_SUCCESS(rc))
{
/* Create the commit timer */
if (pCache->u32CommitTimeoutMs > 0)
rc = TMR3TimerCreateInternal(pClassFile->Core.pVM, TMCLOCK_REAL,
pdmacFileCacheCommitTimerCallback,
pClassFile,
"Cache-Commit",
&pClassFile->Cache.pTimerCommit);
if (RT_SUCCESS(rc))
{
LogRel(("AIOMgr: Cache successfully initialised. Cache size is %u bytes\n", pCache->cbMax));
LogRel(("AIOMgr: Cache commit interval is %u ms\n", pCache->u32CommitTimeoutMs));
LogRel(("AIOMgr: Cache commit threshold is %u bytes\n", pCache->cbCommitDirtyThreshold));
return VINF_SUCCESS;
}
RTCritSectDelete(&pCache->CritSect);
}
return rc;
}
/**
* Destroysthe cache freeing all data.
*
* returns nothing.
* @param pClassFile The global class data for file endpoints.
*/
void pdmacFileCacheDestroy(PPDMASYNCCOMPLETIONEPCLASSFILE pClassFile)
{
PPDMACFILECACHEGLOBAL pCache = &pClassFile->Cache;
/* Make sure no one else uses the cache now */
pdmacFileCacheLockEnter(pCache);
/* Cleanup deleting all cache entries waiting for in progress entries to finish. */
pdmacFileCacheDestroyList(&pCache->LruRecentlyUsedIn);
pdmacFileCacheDestroyList(&pCache->LruRecentlyUsedOut);
pdmacFileCacheDestroyList(&pCache->LruFrequentlyUsed);
pdmacFileCacheLockLeave(pCache);
RTCritSectDelete(&pCache->CritSect);
}
/**
* Initializes per endpoint cache data
* like the AVL tree used to access cached entries.
*
* @returns VBox status code.
* @param pEndpoint The endpoint to init the cache for,
* @param pClassFile The global class data for file endpoints.
*/
int pdmacFileEpCacheInit(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint, PPDMASYNCCOMPLETIONEPCLASSFILE pClassFile)
{
PPDMACFILEENDPOINTCACHE pEndpointCache = &pEndpoint->DataCache;
pEndpointCache->pCache = &pClassFile->Cache;
RTListInit(&pEndpointCache->ListDirtyNotCommitted);
int rc = RTSpinlockCreate(&pEndpointCache->LockList);
if (RT_SUCCESS(rc))
{
rc = RTSemRWCreate(&pEndpointCache->SemRWEntries);
if (RT_SUCCESS(rc))
{
pEndpointCache->pTree = (PAVLRFOFFTREE)RTMemAllocZ(sizeof(AVLRFOFFTREE));
if (pEndpointCache->pTree)
{
pClassFile->Cache.cRefs++;
RTListAppend(&pClassFile->Cache.ListEndpoints, &pEndpointCache->NodeCacheEndpoint);
/* Arm the timer if this is the first endpoint. */
if ( pClassFile->Cache.cRefs == 1
&& pClassFile->Cache.u32CommitTimeoutMs > 0)
rc = TMTimerSetMillies(pClassFile->Cache.pTimerCommit, pClassFile->Cache.u32CommitTimeoutMs);
}
else
rc = VERR_NO_MEMORY;
if (RT_FAILURE(rc))
RTSemRWDestroy(pEndpointCache->SemRWEntries);
}
if (RT_FAILURE(rc))
RTSpinlockDestroy(pEndpointCache->LockList);
}
#ifdef VBOX_WITH_STATISTICS
if (RT_SUCCESS(rc))
{
STAMR3RegisterF(pClassFile->Core.pVM, &pEndpointCache->StatWriteDeferred,
STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS,
STAMUNIT_COUNT, "Number of deferred writes",
"/PDM/AsyncCompletion/File/%s/Cache/DeferredWrites", RTPathFilename(pEndpoint->Core.pszUri));
}
#endif
LogFlowFunc(("Leave rc=%Rrc\n", rc));
return rc;
}
/**
* Callback for the AVL destroy routine. Frees a cache entry for this endpoint.
*
* @returns IPRT status code.
* @param pNode The node to destroy.
* @param pvUser Opaque user data.
*/
static int pdmacFileEpCacheEntryDestroy(PAVLRFOFFNODECORE pNode, void *pvUser)
{
PPDMACFILECACHEENTRY pEntry = (PPDMACFILECACHEENTRY)pNode;
PPDMACFILECACHEGLOBAL pCache = (PPDMACFILECACHEGLOBAL)pvUser;
PPDMACFILEENDPOINTCACHE pEndpointCache = &pEntry->pEndpoint->DataCache;
while (ASMAtomicReadU32(&pEntry->fFlags) & (PDMACFILECACHE_ENTRY_IO_IN_PROGRESS | PDMACFILECACHE_ENTRY_IS_DIRTY))
{
RTSemRWReleaseWrite(pEndpointCache->SemRWEntries);
RTThreadSleep(250);
RTSemRWRequestWrite(pEndpointCache->SemRWEntries, RT_INDEFINITE_WAIT);
}
AssertMsg(!(pEntry->fFlags & (PDMACFILECACHE_ENTRY_IO_IN_PROGRESS | PDMACFILECACHE_ENTRY_IS_DIRTY)),
("Entry is dirty and/or still in progress fFlags=%#x\n", pEntry->fFlags));
bool fUpdateCache = pEntry->pList == &pCache->LruFrequentlyUsed
|| pEntry->pList == &pCache->LruRecentlyUsedIn;
pdmacFileCacheEntryRemoveFromList(pEntry);
if (fUpdateCache)
pdmacFileCacheSub(pCache, pEntry->cbData);
RTMemPageFree(pEntry->pbData);
RTMemFree(pEntry);
return VINF_SUCCESS;
}
/**
* Destroys all cache ressources used by the given endpoint.
*
* @returns nothing.
* @param pEndpoint The endpoint to the destroy.
*/
void pdmacFileEpCacheDestroy(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint)
{
PPDMACFILEENDPOINTCACHE pEndpointCache = &pEndpoint->DataCache;
PPDMACFILECACHEGLOBAL pCache = pEndpointCache->pCache;
/* Make sure nobody is accessing the cache while we delete the tree. */
pdmacFileCacheLockEnter(pCache);
RTSemRWRequestWrite(pEndpointCache->SemRWEntries, RT_INDEFINITE_WAIT);
RTAvlrFileOffsetDestroy(pEndpointCache->pTree, pdmacFileEpCacheEntryDestroy, pCache);
RTSemRWReleaseWrite(pEndpointCache->SemRWEntries);
RTSpinlockDestroy(pEndpointCache->LockList);
pCache->cRefs--;
RTListNodeRemove(&pEndpointCache->NodeCacheEndpoint);
if ( !pCache->cRefs
&& pCache->u32CommitTimeoutMs > 0)
TMTimerStop(pCache->pTimerCommit);
pdmacFileCacheLockLeave(pCache);
RTSemRWDestroy(pEndpointCache->SemRWEntries);
#ifdef VBOX_WITH_STATISTICS
PPDMASYNCCOMPLETIONEPCLASSFILE pEpClassFile = (PPDMASYNCCOMPLETIONEPCLASSFILE)pEndpoint->Core.pEpClass;
STAMR3Deregister(pEpClassFile->Core.pVM, &pEndpointCache->StatWriteDeferred);
#endif
}
static PPDMACFILECACHEENTRY pdmacFileEpCacheGetCacheEntryByOffset(PPDMACFILEENDPOINTCACHE pEndpointCache, RTFOFF off)
{
PPDMACFILECACHEGLOBAL pCache = pEndpointCache->pCache;
PPDMACFILECACHEENTRY pEntry = NULL;
STAM_PROFILE_ADV_START(&pCache->StatTreeGet, Cache);
RTSemRWRequestRead(pEndpointCache->SemRWEntries, RT_INDEFINITE_WAIT);
pEntry = (PPDMACFILECACHEENTRY)RTAvlrFileOffsetRangeGet(pEndpointCache->pTree, off);
if (pEntry)
pdmacFileEpCacheEntryRef(pEntry);
RTSemRWReleaseRead(pEndpointCache->SemRWEntries);
STAM_PROFILE_ADV_STOP(&pCache->StatTreeGet, Cache);
return pEntry;
}
/**
* Return the best fit cache entries for the given offset.
*
* @returns nothing.
* @param pEndpointCache The endpoint cache.
* @param off The offset.
* @param pEntryAbove Where to store the pointer to the best fit entry above the
* the given offset. NULL if not required.
* @param pEntryBelow Where to store the pointer to the best fit entry below the
* the given offset. NULL if not required.
*/
static void pdmacFileEpCacheGetCacheBestFitEntryByOffset(PPDMACFILEENDPOINTCACHE pEndpointCache, RTFOFF off,
PPDMACFILECACHEENTRY *ppEntryAbove,
PPDMACFILECACHEENTRY *ppEntryBelow)
{
PPDMACFILECACHEGLOBAL pCache = pEndpointCache->pCache;
STAM_PROFILE_ADV_START(&pCache->StatTreeGet, Cache);
RTSemRWRequestRead(pEndpointCache->SemRWEntries, RT_INDEFINITE_WAIT);
if (ppEntryAbove)
{
*ppEntryAbove = (PPDMACFILECACHEENTRY)RTAvlrFileOffsetGetBestFit(pEndpointCache->pTree, off, true /*fAbove*/);
if (*ppEntryAbove)
pdmacFileEpCacheEntryRef(*ppEntryAbove);
}
if (ppEntryBelow)
{
*ppEntryBelow = (PPDMACFILECACHEENTRY)RTAvlrFileOffsetGetBestFit(pEndpointCache->pTree, off, false /*fAbove*/);
if (*ppEntryBelow)
pdmacFileEpCacheEntryRef(*ppEntryBelow);
}
RTSemRWReleaseRead(pEndpointCache->SemRWEntries);
STAM_PROFILE_ADV_STOP(&pCache->StatTreeGet, Cache);
}
static void pdmacFileEpCacheInsertEntry(PPDMACFILEENDPOINTCACHE pEndpointCache, PPDMACFILECACHEENTRY pEntry)
{
PPDMACFILECACHEGLOBAL pCache = pEndpointCache->pCache;
STAM_PROFILE_ADV_START(&pCache->StatTreeInsert, Cache);
RTSemRWRequestWrite(pEndpointCache->SemRWEntries, RT_INDEFINITE_WAIT);
bool fInserted = RTAvlrFileOffsetInsert(pEndpointCache->pTree, &pEntry->Core);
AssertMsg(fInserted, ("Node was not inserted into tree\n"));
STAM_PROFILE_ADV_STOP(&pCache->StatTreeInsert, Cache);
RTSemRWReleaseWrite(pEndpointCache->SemRWEntries);
}
/**
* Allocates and initializes a new entry for the cache.
* The entry has a reference count of 1.
*
* @returns Pointer to the new cache entry or NULL if out of memory.
* @param pCache The cache the entry belongs to.
* @param pEndoint The endpoint the entry holds data for.
* @param off Start offset.
* @param cbData Size of the cache entry.
* @param pbBuffer Pointer to the buffer to use.
* NULL if a new buffer should be allocated.
* The buffer needs to have the same size of the entry.
*/
static PPDMACFILECACHEENTRY pdmacFileCacheEntryAlloc(PPDMACFILECACHEGLOBAL pCache,
PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint,
RTFOFF off, size_t cbData, uint8_t *pbBuffer)
{
PPDMACFILECACHEENTRY pEntryNew = (PPDMACFILECACHEENTRY)RTMemAllocZ(sizeof(PDMACFILECACHEENTRY));
if (RT_UNLIKELY(!pEntryNew))
return NULL;
pEntryNew->Core.Key = off;
pEntryNew->Core.KeyLast = off + cbData - 1;
pEntryNew->pEndpoint = pEndpoint;
pEntryNew->pCache = pCache;
pEntryNew->fFlags = 0;
pEntryNew->cRefs = 1; /* We are using it now. */
pEntryNew->pList = NULL;
pEntryNew->cbData = cbData;
pEntryNew->pWaitingHead = NULL;
pEntryNew->pWaitingTail = NULL;
if (pbBuffer)
pEntryNew->pbData = pbBuffer;
else
pEntryNew->pbData = (uint8_t *)RTMemPageAlloc(cbData);
if (RT_UNLIKELY(!pEntryNew->pbData))
{
RTMemFree(pEntryNew);
return NULL;
}
return pEntryNew;
}
/**
* Adds a segment to the waiting list for a cache entry
* which is currently in progress.
*
* @returns nothing.
* @param pEntry The cache entry to add the segment to.
* @param pSeg The segment to add.
*/
DECLINLINE(void) pdmacFileEpCacheEntryAddWaitingSegment(PPDMACFILECACHEENTRY pEntry, PPDMACFILETASKSEG pSeg)
{
pSeg->pNext = NULL;
if (pEntry->pWaitingHead)
{
AssertPtr(pEntry->pWaitingTail);
pEntry->pWaitingTail->pNext = pSeg;
pEntry->pWaitingTail = pSeg;
}
else
{
Assert(!pEntry->pWaitingTail);
pEntry->pWaitingHead = pSeg;
pEntry->pWaitingTail = pSeg;
}
}
/**
* Checks that a set of flags is set/clear acquiring the R/W semaphore
* in exclusive mode.
*
* @returns true if the flag in fSet is set and the one in fClear is clear.
* false othwerise.
* The R/W semaphore is only held if true is returned.
*
* @param pEndpointCache The endpoint cache instance data.
* @param pEntry The entry to check the flags for.
* @param fSet The flag which is tested to be set.
* @param fClear The flag which is tested to be clear.
*/
DECLINLINE(bool) pdmacFileEpCacheEntryFlagIsSetClearAcquireLock(PPDMACFILEENDPOINTCACHE pEndpointCache,
PPDMACFILECACHEENTRY pEntry,
uint32_t fSet, uint32_t fClear)
{
uint32_t fFlags = ASMAtomicReadU32(&pEntry->fFlags);
bool fPassed = ((fFlags & fSet) && !(fFlags & fClear));
if (fPassed)
{
/* Acquire the lock and check again becuase the completion callback might have raced us. */
RTSemRWRequestWrite(pEndpointCache->SemRWEntries, RT_INDEFINITE_WAIT);
fFlags = ASMAtomicReadU32(&pEntry->fFlags);
fPassed = ((fFlags & fSet) && !(fFlags & fClear));
/* Drop the lock if we didn't passed the test. */
if (!fPassed)
RTSemRWReleaseWrite(pEndpointCache->SemRWEntries);
}
return fPassed;
}
/**
* Copies data to a buffer described by a I/O memory context.
*
* @returns nothing.
* @param pIoMemCtx The I/O memory context to copy the data into.
* @param pbData Pointer to the data data to copy.
* @param cbData Amount of data to copy.
*/
static void pdmacFileEpCacheCopyToIoMemCtx(PPDMIOMEMCTX pIoMemCtx,
uint8_t *pbData,
size_t cbData)
{
while (cbData)
{
size_t cbCopy = cbData;
uint8_t *pbBuf = pdmIoMemCtxGetBuffer(pIoMemCtx, &cbCopy);
AssertPtr(pbBuf);
memcpy(pbBuf, pbData, cbCopy);
cbData -= cbCopy;
pbData += cbCopy;
}
}
/**
* Copies data from a buffer described by a I/O memory context.
*
* @returns nothing.
* @param pIoMemCtx The I/O memory context to copy the data from.
* @param pbData Pointer to the destination buffer.
* @param cbData Amount of data to copy.
*/
static void pdmacFileEpCacheCopyFromIoMemCtx(PPDMIOMEMCTX pIoMemCtx,
uint8_t *pbData,
size_t cbData)
{
while (cbData)
{
size_t cbCopy = cbData;
uint8_t *pbBuf = pdmIoMemCtxGetBuffer(pIoMemCtx, &cbCopy);
AssertPtr(pbBuf);
memcpy(pbData, pbBuf, cbCopy);
cbData -= cbCopy;
pbData += cbCopy;
}
}
/**
* Add a buffer described by the I/O memory context
* to the entry waiting for completion.
*
* @returns nothing.
* @param pEntry The entry to add the buffer to.
* @param pTask Task associated with the buffer.
* @param pIoMemCtx The memory context to use.
* @param OffDiff Offset from the start of the buffer
* in the entry.
* @param cbData Amount of data to wait for onthis entry.
* @param fWrite Flag whether the task waits because it wants to write
* to the cache entry.
*/
static void pdmacFileEpCacheEntryWaitersAdd(PPDMACFILECACHEENTRY pEntry,
PPDMASYNCCOMPLETIONTASKFILE pTask,
PPDMIOMEMCTX pIoMemCtx,
RTFOFF OffDiff,
size_t cbData,
bool fWrite)
{
while (cbData)
{
PPDMACFILETASKSEG pSeg = (PPDMACFILETASKSEG)RTMemAllocZ(sizeof(PDMACFILETASKSEG));
size_t cbSeg = cbData;
uint8_t *pbBuf = pdmIoMemCtxGetBuffer(pIoMemCtx, &cbSeg);
pSeg->pTask = pTask;
pSeg->uBufOffset = OffDiff;
pSeg->cbTransfer = cbSeg;
pSeg->pvBuf = pbBuf;
pSeg->fWrite = fWrite;
pdmacFileEpCacheEntryAddWaitingSegment(pEntry, pSeg);
cbData -= cbSeg;
OffDiff += cbSeg;
}
}
/**
* Passthrough a part of a request directly to the I/O manager
* handling the endpoint.
*
* @returns nothing.
* @param pEndpoint The endpoint.
* @param pTask The task.
* @param pIoMemCtx The I/O memory context to use.
* @param offStart Offset to start transfer from.
* @param cbData Amount of data to transfer.
* @param enmTransferType The transfer type (read/write)
*/
static void pdmacFileEpCacheRequestPassthrough(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint,
PPDMASYNCCOMPLETIONTASKFILE pTask,
PPDMIOMEMCTX pIoMemCtx,
RTFOFF offStart, size_t cbData,
PDMACTASKFILETRANSFER enmTransferType)
{
while (cbData)
{
size_t cbSeg = cbData;
uint8_t *pbBuf = pdmIoMemCtxGetBuffer(pIoMemCtx, &cbSeg);
PPDMACTASKFILE pIoTask = pdmacFileTaskAlloc(pEndpoint);
AssertPtr(pIoTask);
pIoTask->pEndpoint = pEndpoint;
pIoTask->enmTransferType = enmTransferType;
pIoTask->Off = offStart;
pIoTask->DataSeg.cbSeg = cbSeg;
pIoTask->DataSeg.pvSeg = pbBuf;
pIoTask->pvUser = pTask;
pIoTask->pfnCompleted = pdmacFileEpTaskCompleted;
offStart += cbSeg;
cbData -= cbSeg;
/* Send it off to the I/O manager. */
pdmacFileEpAddTask(pEndpoint, pIoTask);
}
}
/**
* Calculate aligned offset and size for a new cache entry
* which do not intersect with an already existing entry and the
* file end.
*
* @returns The number of bytes the entry can hold of the requested amount
* of byte.
* @param pEndpoint The endpoint.
* @param pEndpointCache The endpoint cache.
* @param off The start offset.
* @param cb The number of bytes the entry needs to hold at least.
* @param uAlignment Alignment of the boundary sizes.
* @param poffAligned Where to store the aligned offset.
* @param pcbAligned Where to store the aligned size of the entry.
*/
static size_t pdmacFileEpCacheEntryBoundariesCalc(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint,
PPDMACFILEENDPOINTCACHE pEndpointCache,
RTFOFF off, size_t cb,
unsigned uAlignment,
RTFOFF *poffAligned, size_t *pcbAligned)
{
size_t cbAligned;
size_t cbInEntry = 0;
RTFOFF offAligned;
PPDMACFILECACHEENTRY pEntryAbove = NULL;
PPDMACFILECACHEENTRY pEntryBelow = NULL;
/* Get the best fit entries around the offset */
pdmacFileEpCacheGetCacheBestFitEntryByOffset(pEndpointCache, off,
&pEntryAbove, &pEntryBelow);
/* Log the info */
LogFlow(("%sest fit entry below off=%RTfoff (BestFit=%RTfoff BestFitEnd=%RTfoff BestFitSize=%u)\n",
pEntryBelow ? "B" : "No b",
off,
pEntryBelow ? pEntryBelow->Core.Key : 0,
pEntryBelow ? pEntryBelow->Core.KeyLast : 0,
pEntryBelow ? pEntryBelow->cbData : 0));
LogFlow(("%sest fit entry above off=%RTfoff (BestFit=%RTfoff BestFitEnd=%RTfoff BestFitSize=%u)\n",
pEntryAbove ? "B" : "No b",
off,
pEntryAbove ? pEntryAbove->Core.Key : 0,
pEntryAbove ? pEntryAbove->Core.KeyLast : 0,
pEntryAbove ? pEntryAbove->cbData : 0));
/* Align the offset first. */
offAligned = off & ~(RTFOFF)(512-1);
if ( pEntryBelow
&& offAligned <= pEntryBelow->Core.KeyLast)
offAligned = pEntryBelow->Core.KeyLast;
if ( pEntryAbove
&& off + (RTFOFF)cb > pEntryAbove->Core.Key)
{
cbInEntry = pEntryAbove->Core.Key - off;
cbAligned = pEntryAbove->Core.Key - offAligned;
}
else
{
/*
* Align the size to a 4KB boundary.
* Memory size is aligned to a page boundary
* and memory is wasted if the size is rather small.
* (For example reads with a size of 512 bytes).
*/
cbInEntry = cb;
cbAligned = RT_ALIGN_Z(cb + (off - offAligned), uAlignment);
/*
* Clip to file size if the original request doesn't
* exceed the file (not an appending write)
*/
uint64_t cbReq = off + (RTFOFF)cb;
if (cbReq >= pEndpoint->cbFile)
cbAligned = cbReq - offAligned;
else
cbAligned = RT_MIN(pEndpoint->cbFile - offAligned, cbAligned);
if (pEntryAbove)
{
Assert(pEntryAbove->Core.Key >= off);
cbAligned = RT_MIN(cbAligned, (uint64_t)pEntryAbove->Core.Key - offAligned);
}
}
/* A few sanity checks */
AssertMsg(!pEntryBelow || pEntryBelow->Core.KeyLast < offAligned,
("Aligned start offset intersects with another cache entry\n"));
AssertMsg(!pEntryAbove || (offAligned + (RTFOFF)cbAligned) <= pEntryAbove->Core.Key,
("Aligned size intersects with another cache entry\n"));
Assert(cbInEntry <= cbAligned);
AssertMsg( ( offAligned + (RTFOFF)cbAligned <= (RTFOFF)pEndpoint->cbFile
&& off + (RTFOFF)cb <= (RTFOFF)pEndpoint->cbFile)
|| (offAligned + (RTFOFF)cbAligned <= off + (RTFOFF)cb),
("Unwanted file size increase\n"));
if (pEntryBelow)
pdmacFileEpCacheEntryRelease(pEntryBelow);
if (pEntryAbove)
pdmacFileEpCacheEntryRelease(pEntryAbove);
LogFlow(("offAligned=%RTfoff cbAligned=%u\n", offAligned, cbAligned));
*poffAligned = offAligned;
*pcbAligned = cbAligned;
return cbInEntry;
}
/**
* Create a new cache entry evicting data from the cache if required.
*
* @returns Pointer to the new cache entry or NULL
* if not enough bytes could be evicted from the cache.
* @param pEndpoint The endpoint.
* @param pEndpointCache The endpoint cache.
* @param off The offset.
* @param cb Number of bytes the cache entry should have.
* @param uAlignment Alignment the size of the entry should have.
* @param pcbData Where to store the number of bytes the new
* entry can hold. May be lower than actually requested
* due to another entry intersecting the access range.
*/
static PPDMACFILECACHEENTRY pdmacFileEpCacheEntryCreate(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint,
PPDMACFILEENDPOINTCACHE pEndpointCache,
RTFOFF off, size_t cb,
unsigned uAlignment,
size_t *pcbData)
{
RTFOFF offStart = 0;
size_t cbEntry = 0;
PPDMACFILECACHEENTRY pEntryNew = NULL;
PPDMACFILECACHEGLOBAL pCache = pEndpointCache->pCache;
uint8_t *pbBuffer = NULL;
*pcbData = pdmacFileEpCacheEntryBoundariesCalc(pEndpoint,
pEndpointCache,
off, cb,
uAlignment,
&offStart, &cbEntry);
pdmacFileCacheLockEnter(pCache);
bool fEnough = pdmacFileCacheReclaim(pCache, cbEntry, true, &pbBuffer);
if (fEnough)
{
LogFlow(("Evicted enough bytes (%u requested). Creating new cache entry\n", cbEntry));
pEntryNew = pdmacFileCacheEntryAlloc(pCache, pEndpoint,
offStart, cbEntry,
pbBuffer);
if (RT_LIKELY(pEntryNew))
{
pdmacFileCacheEntryAddToList(&pCache->LruRecentlyUsedIn, pEntryNew);
pdmacFileCacheAdd(pCache, cbEntry);
pdmacFileCacheLockLeave(pCache);
pdmacFileEpCacheInsertEntry(pEndpointCache, pEntryNew);
AssertMsg( (off >= pEntryNew->Core.Key)
&& (off + (RTFOFF)*pcbData <= pEntryNew->Core.KeyLast + 1),
("Overflow in calculation off=%RTfoff OffsetAligned=%RTfoff\n",
off, pEntryNew->Core.Key));
}
else
pdmacFileCacheLockLeave(pCache);
}
else
pdmacFileCacheLockLeave(pCache);
return pEntryNew;
}
/**
* Reads the specified data from the endpoint using the cache if possible.
*
* @returns VBox status code.
* @param pEndpoint The endpoint to read from.
* @param pTask The task structure used as identifier for this request.
* @param off The offset to start reading from.
* @param paSegments Pointer to the array holding the destination buffers.
* @param cSegments Number of segments in the array.
* @param cbRead Number of bytes to read.
*/
int pdmacFileEpCacheRead(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint, PPDMASYNCCOMPLETIONTASKFILE pTask,
RTFOFF off, PCPDMDATASEG paSegments, size_t cSegments,
size_t cbRead)
{
int rc = VINF_SUCCESS;
PPDMACFILEENDPOINTCACHE pEndpointCache = &pEndpoint->DataCache;
PPDMACFILECACHEGLOBAL pCache = pEndpointCache->pCache;
PPDMACFILECACHEENTRY pEntry;
LogFlowFunc((": pEndpoint=%#p{%s} pTask=%#p off=%RTfoff paSegments=%#p cSegments=%u cbRead=%u\n",
pEndpoint, pEndpoint->Core.pszUri, pTask, off, paSegments, cSegments, cbRead));
pTask->cbTransferLeft = cbRead;
/* Set to completed to make sure that the task is valid while we access it. */
ASMAtomicWriteBool(&pTask->fCompleted, true);
/* Init the I/O memory context */
PDMIOMEMCTX IoMemCtx;
pdmIoMemCtxInit(&IoMemCtx, paSegments, cSegments);
while (cbRead)
{
size_t cbToRead;
pEntry = pdmacFileEpCacheGetCacheEntryByOffset(pEndpointCache, off);
/*
* 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)
{
RTFOFF OffDiff = off - pEntry->Core.Key;
AssertMsg(off >= pEntry->Core.Key,
("Overflow in calculation off=%RTfoff OffsetAligned=%RTfoff\n",
off, pEntry->Core.Key));
AssertPtr(pEntry->pList);
cbToRead = RT_MIN(pEntry->cbData - OffDiff, cbRead);
AssertMsg(off + (RTFOFF)cbToRead <= pEntry->Core.Key + pEntry->Core.KeyLast + 1,
("Buffer of cache entry exceeded off=%RTfoff cbToRead=%d\n",
off, cbToRead));
cbRead -= cbToRead;
if (!cbRead)
STAM_COUNTER_INC(&pCache->cHits);
else
STAM_COUNTER_INC(&pCache->cPartialHits);
STAM_COUNTER_ADD(&pCache->StatRead, cbToRead);
/* Ghost lists contain no data. */
if ( (pEntry->pList == &pCache->LruRecentlyUsedIn)
|| (pEntry->pList == &pCache->LruFrequentlyUsed))
{
if (pdmacFileEpCacheEntryFlagIsSetClearAcquireLock(pEndpointCache, pEntry,
PDMACFILECACHE_ENTRY_IO_IN_PROGRESS,
PDMACFILECACHE_ENTRY_IS_DIRTY))
{
/* Entry didn't completed yet. Append to the list */
pdmacFileEpCacheEntryWaitersAdd(pEntry, pTask,
&IoMemCtx,
OffDiff, cbToRead,
false /* fWrite */);
RTSemRWReleaseWrite(pEndpointCache->SemRWEntries);
}
else
{
/* Read as much as we can from the entry. */
pdmacFileEpCacheCopyToIoMemCtx(&IoMemCtx, pEntry->pbData + OffDiff, cbToRead);
ASMAtomicSubS32(&pTask->cbTransferLeft, cbToRead);
}
/* Move this entry to the top position */
if (pEntry->pList == &pCache->LruFrequentlyUsed)
{
pdmacFileCacheLockEnter(pCache);
pdmacFileCacheEntryAddToList(&pCache->LruFrequentlyUsed, pEntry);
pdmacFileCacheLockLeave(pCache);
}
/* Release the entry */
pdmacFileEpCacheEntryRelease(pEntry);
}
else
{
uint8_t *pbBuffer = NULL;
LogFlow(("Fetching data for ghost entry %#p from file\n", pEntry));
pdmacFileCacheLockEnter(pCache);
pdmacFileCacheEntryRemoveFromList(pEntry); /* Remove it before we remove data, otherwise it may get freed when evicting data. */
bool fEnough = pdmacFileCacheReclaim(pCache, pEntry->cbData, true, &pbBuffer);
/* Move the entry to Am and fetch it to the cache. */
if (fEnough)
{
pdmacFileCacheEntryAddToList(&pCache->LruFrequentlyUsed, pEntry);
pdmacFileCacheAdd(pCache, pEntry->cbData);
pdmacFileCacheLockLeave(pCache);
if (pbBuffer)
pEntry->pbData = pbBuffer;
else
pEntry->pbData = (uint8_t *)RTMemPageAlloc(pEntry->cbData);
AssertPtr(pEntry->pbData);
pdmacFileEpCacheEntryWaitersAdd(pEntry, pTask,
&IoMemCtx,
OffDiff, cbToRead,
false /* fWrite */);
pdmacFileCacheReadFromEndpoint(pEntry);
/* Release the entry */
pdmacFileEpCacheEntryRelease(pEntry);
}
else
{
RTSemRWRequestWrite(pEndpointCache->SemRWEntries, RT_INDEFINITE_WAIT);
STAM_PROFILE_ADV_START(&pCache->StatTreeRemove, Cache);
RTAvlrFileOffsetRemove(pEndpointCache->pTree, pEntry->Core.Key);
STAM_PROFILE_ADV_STOP(&pCache->StatTreeRemove, Cache);
RTSemRWReleaseWrite(pEndpointCache->SemRWEntries);
pdmacFileCacheLockLeave(pCache);
RTMemFree(pEntry);
pdmacFileEpCacheRequestPassthrough(pEndpoint, pTask,
&IoMemCtx, off, cbToRead,
PDMACTASKFILETRANSFER_READ);
}
}
}
else
{
#ifdef VBOX_WITH_IO_READ_CACHE
/* No entry found for this offset. Create a new entry and fetch the data to the cache. */
PPDMACFILECACHEENTRY pEntryNew = pdmacFileEpCacheEntryCreate(pEndpoint,
pEndpointCache,
off, cbRead,
PAGE_SIZE,
&cbToRead);
cbRead -= cbToRead;
if (pEntryNew)
{
if (!cbRead)
STAM_COUNTER_INC(&pCache->cMisses);
else
STAM_COUNTER_INC(&pCache->cPartialHits);
pdmacFileEpCacheEntryWaitersAdd(pEntryNew, pTask,
&IoMemCtx,
off - pEntryNew->Core.Key,
cbToRead,
false /* fWrite */);
pdmacFileCacheReadFromEndpoint(pEntryNew);
pdmacFileEpCacheEntryRelease(pEntryNew); /* it is protected by the I/O in progress flag now. */
}
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. Remaining request will be passed through\n", cbToRead));
pdmacFileEpCacheRequestPassthrough(pEndpoint, pTask,
&IoMemCtx, off, cbToRead,
PDMACTASKFILETRANSFER_READ);
}
#else
/* Clip read size if neccessary. */
PPDMACFILECACHEENTRY pEntryAbove;
pdmacFileEpCacheGetCacheBestFitEntryByOffset(pEndpointCache, off,
&pEntryAbove, NULL);
if (pEntryAbove)
{
if (off + (RTFOFF)cbRead > pEntryAbove->Core.Key)
cbToRead = pEntryAbove->Core.Key - off;
else
cbToRead = cbRead;
pdmacFileEpCacheEntryRelease(pEntryAbove);
}
else
cbToRead = cbRead;
cbRead -= cbToRead;
pdmacFileEpCacheRequestPassthrough(pEndpoint, pTask,
&IoMemCtx, off, cbToRead,
PDMACTASKFILETRANSFER_READ);
#endif
}
off += cbToRead;
}
ASMAtomicWriteBool(&pTask->fCompleted, false);
if (ASMAtomicReadS32(&pTask->cbTransferLeft) == 0
&& !ASMAtomicXchgBool(&pTask->fCompleted, true))
pdmR3AsyncCompletionCompleteTask(&pTask->Core, VINF_SUCCESS, false);
else
rc = VINF_AIO_TASK_PENDING;
LogFlowFunc((": Leave rc=%Rrc\n", rc));
return rc;
}
/**
* Writes the given data to the endpoint using the cache if possible.
*
* @returns VBox status code.
* @param pEndpoint The endpoint to write to.
* @param pTask The task structure used as identifier for this request.
* @param off The offset to start writing to
* @param paSegments Pointer to the array holding the source buffers.
* @param cSegments Number of segments in the array.
* @param cbWrite Number of bytes to write.
*/
int pdmacFileEpCacheWrite(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint, PPDMASYNCCOMPLETIONTASKFILE pTask,
RTFOFF off, PCPDMDATASEG paSegments, size_t cSegments,
size_t cbWrite)
{
int rc = VINF_SUCCESS;
PPDMACFILEENDPOINTCACHE pEndpointCache = &pEndpoint->DataCache;
PPDMACFILECACHEGLOBAL pCache = pEndpointCache->pCache;
PPDMACFILECACHEENTRY pEntry;
LogFlowFunc((": pEndpoint=%#p{%s} pTask=%#p off=%RTfoff paSegments=%#p cSegments=%u cbWrite=%u\n",
pEndpoint, pEndpoint->Core.pszUri, pTask, off, paSegments, cSegments, cbWrite));
pTask->cbTransferLeft = cbWrite;
/* Set to completed to make sure that the task is valid while we access it. */
ASMAtomicWriteBool(&pTask->fCompleted, true);
/* Init the I/O memory context */
PDMIOMEMCTX IoMemCtx;
pdmIoMemCtxInit(&IoMemCtx, paSegments, cSegments);
while (cbWrite)
{
size_t cbToWrite;
pEntry = pdmacFileEpCacheGetCacheEntryByOffset(pEndpointCache, off);
if (pEntry)
{
/* Write the data into the entry and mark it as dirty */
AssertPtr(pEntry->pList);
RTFOFF OffDiff = off - pEntry->Core.Key;
AssertMsg(off >= pEntry->Core.Key,
("Overflow in calculation off=%RTfoff OffsetAligned=%RTfoff\n",
off, pEntry->Core.Key));
cbToWrite = RT_MIN(pEntry->cbData - OffDiff, cbWrite);
cbWrite -= cbToWrite;
if (!cbWrite)
STAM_COUNTER_INC(&pCache->cHits);
else
STAM_COUNTER_INC(&pCache->cPartialHits);
STAM_COUNTER_ADD(&pCache->StatWritten, cbToWrite);
/* Ghost lists contain no data. */
if ( (pEntry->pList == &pCache->LruRecentlyUsedIn)
|| (pEntry->pList == &pCache->LruFrequentlyUsed))
{
/* Check if the entry is dirty. */
if(pdmacFileEpCacheEntryFlagIsSetClearAcquireLock(pEndpointCache, pEntry,
PDMACFILECACHE_ENTRY_IS_DIRTY,
0))
{
/* If it is dirty but not in progrss just update the data. */
if (!(pEntry->fFlags & PDMACFILECACHE_ENTRY_IO_IN_PROGRESS))
{
pdmacFileEpCacheCopyFromIoMemCtx(&IoMemCtx,
pEntry->pbData + OffDiff,
cbToWrite);
ASMAtomicSubS32(&pTask->cbTransferLeft, cbToWrite);
}
else
{
/* The data isn't written to the file yet */
pdmacFileEpCacheEntryWaitersAdd(pEntry, pTask,
&IoMemCtx,
OffDiff, cbToWrite,
true /* fWrite */);
STAM_COUNTER_INC(&pEndpointCache->StatWriteDeferred);
}
RTSemRWReleaseWrite(pEndpointCache->SemRWEntries);
}
else /* Dirty bit not set */
{
/*
* Check if a read is in progress for this entry.
* We have to defer processing in that case.
*/
if(pdmacFileEpCacheEntryFlagIsSetClearAcquireLock(pEndpointCache, pEntry,
PDMACFILECACHE_ENTRY_IO_IN_PROGRESS,
0))
{
pdmacFileEpCacheEntryWaitersAdd(pEntry, pTask,
&IoMemCtx,
OffDiff, cbToWrite,
true /* fWrite */);
STAM_COUNTER_INC(&pEndpointCache->StatWriteDeferred);
RTSemRWReleaseWrite(pEndpointCache->SemRWEntries);
}
else /* I/O in progress flag not set */
{
/* Write as much as we can into the entry and update the file. */
pdmacFileEpCacheCopyFromIoMemCtx(&IoMemCtx,
pEntry->pbData + OffDiff,
cbToWrite);
ASMAtomicSubS32(&pTask->cbTransferLeft, cbToWrite);
bool fCommit = pdmacFileCacheAddDirtyEntry(pEndpointCache, pEntry);
if (fCommit)
pdmacFileCacheCommitDirtyEntries(pCache);
}
} /* Dirty bit not set */
/* Move this entry to the top position */
if (pEntry->pList == &pCache->LruFrequentlyUsed)
{
pdmacFileCacheLockEnter(pCache);
pdmacFileCacheEntryAddToList(&pCache->LruFrequentlyUsed, pEntry);
pdmacFileCacheLockLeave(pCache);
}
pdmacFileEpCacheEntryRelease(pEntry);
}
else /* Entry is on the ghost list */
{
uint8_t *pbBuffer = NULL;
pdmacFileCacheLockEnter(pCache);
pdmacFileCacheEntryRemoveFromList(pEntry); /* Remove it before we remove data, otherwise it may get freed when evicting data. */
bool fEnough = pdmacFileCacheReclaim(pCache, pEntry->cbData, true, &pbBuffer);
if (fEnough)
{
/* Move the entry to Am and fetch it to the cache. */
pdmacFileCacheEntryAddToList(&pCache->LruFrequentlyUsed, pEntry);
pdmacFileCacheAdd(pCache, pEntry->cbData);
pdmacFileCacheLockLeave(pCache);
if (pbBuffer)
pEntry->pbData = pbBuffer;
else
pEntry->pbData = (uint8_t *)RTMemPageAlloc(pEntry->cbData);
AssertPtr(pEntry->pbData);
pdmacFileEpCacheEntryWaitersAdd(pEntry, pTask,
&IoMemCtx,
OffDiff, cbToWrite,
true /* fWrite */);
STAM_COUNTER_INC(&pEndpointCache->StatWriteDeferred);
pdmacFileCacheReadFromEndpoint(pEntry);
/* Release the reference. If it is still needed the I/O in progress flag should protect it now. */
pdmacFileEpCacheEntryRelease(pEntry);
}
else
{
RTSemRWRequestWrite(pEndpointCache->SemRWEntries, RT_INDEFINITE_WAIT);
STAM_PROFILE_ADV_START(&pCache->StatTreeRemove, Cache);
RTAvlrFileOffsetRemove(pEndpointCache->pTree, pEntry->Core.Key);
STAM_PROFILE_ADV_STOP(&pCache->StatTreeRemove, Cache);
RTSemRWReleaseWrite(pEndpointCache->SemRWEntries);
pdmacFileCacheLockLeave(pCache);
RTMemFree(pEntry);
pdmacFileEpCacheRequestPassthrough(pEndpoint, pTask,
&IoMemCtx, off, cbToWrite,
PDMACTASKFILETRANSFER_WRITE);
}
}
}
else /* No entry found */
{
/*
* No entry found. Try to create a new cache entry to store the data in and if that fails
* write directly to the file.
*/
PPDMACFILECACHEENTRY pEntryNew = pdmacFileEpCacheEntryCreate(pEndpoint,
pEndpointCache,
off, cbWrite,
512,
&cbToWrite);
cbWrite -= cbToWrite;
if (pEntryNew)
{
RTFOFF offDiff = off - pEntryNew->Core.Key;
STAM_COUNTER_INC(&pCache->cHits);
/*
* Check if it is possible to just write the data without waiting
* for it to get fetched first.
*/
if (!offDiff && pEntryNew->cbData == cbToWrite)
{
pdmacFileEpCacheCopyFromIoMemCtx(&IoMemCtx,
pEntryNew->pbData,
cbToWrite);
ASMAtomicSubS32(&pTask->cbTransferLeft, cbToWrite);
bool fCommit = pdmacFileCacheAddDirtyEntry(pEndpointCache, pEntryNew);
if (fCommit)
pdmacFileCacheCommitDirtyEntries(pCache);
STAM_COUNTER_ADD(&pCache->StatWritten, cbToWrite);
}
else
{
/* Defer the write and fetch the data from the endpoint. */
pdmacFileEpCacheEntryWaitersAdd(pEntryNew, pTask,
&IoMemCtx,
offDiff, cbToWrite,
true /* fWrite */);
STAM_COUNTER_INC(&pEndpointCache->StatWriteDeferred);
pdmacFileCacheReadFromEndpoint(pEntryNew);
}
pdmacFileEpCacheEntryRelease(pEntryNew);
}
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. Remaining request will be passed through\n", cbToWrite));
STAM_COUNTER_INC(&pCache->cMisses);
pdmacFileEpCacheRequestPassthrough(pEndpoint, pTask,
&IoMemCtx, off, cbToWrite,
PDMACTASKFILETRANSFER_WRITE);
}
}
off += cbToWrite;
}
ASMAtomicWriteBool(&pTask->fCompleted, false);
if (ASMAtomicReadS32(&pTask->cbTransferLeft) == 0
&& !ASMAtomicXchgBool(&pTask->fCompleted, true))
pdmR3AsyncCompletionCompleteTask(&pTask->Core, VINF_SUCCESS, false);
else
rc = VINF_AIO_TASK_PENDING;
LogFlowFunc((": Leave rc=%Rrc\n", rc));
return rc;
}
int pdmacFileEpCacheFlush(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint, PPDMASYNCCOMPLETIONTASKFILE pTask)
{
int rc = VINF_SUCCESS;
LogFlowFunc((": pEndpoint=%#p{%s} pTask=%#p\n",
pEndpoint, pEndpoint->Core.pszUri, pTask));
if (ASMAtomicReadPtr((void * volatile *)&pEndpoint->DataCache.pTaskFlush))
rc = VERR_RESOURCE_BUSY;
else
{
/* Check for dirty entries in the cache. */
pdmacFileCacheEndpointCommit(&pEndpoint->DataCache);
if (ASMAtomicReadU32(&pEndpoint->DataCache.cWritesOutstanding) > 0)
{
ASMAtomicWritePtr((void * volatile *)&pEndpoint->DataCache.pTaskFlush, pTask);
rc = VINF_AIO_TASK_PENDING;
}
else
pdmR3AsyncCompletionCompleteTask(&pTask->Core, VINF_SUCCESS, false);
}
LogFlowFunc((": Leave rc=%Rrc\n", rc));
return rc;
}