--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/userlibandfileserver/fileserver/sfile/sf_cache_man.cpp Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,1198 @@
+// Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the License "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+// f32\sfile\sf_cache_man.cpp
+//
+//
+
+/**
+ @file
+ @internalTechnology
+*/
+
+
+#include <e32std.h>
+#include <e32std_private.h>
+#include "sf_std.h"
+#include <e32uid.h>
+#include <e32wins.h>
+#include <f32file.h>
+#include <hal.h>
+#include "sf_cache_man.h"
+#include "sf_cache_client.h"
+#include "sf_file_cache.h"
+#include "sf_file_cache_defs.h"
+
+//#undef __SIMULATE_LOCK_FAILURES__
+//#define __RAMDOM_LOCK_FAILURES__
+//#define __PSEUDO_RANDOM_FAILURES__
+//#define __LOCK_ENTIRE_CACHELINE__
+
+#ifdef __RAMDOM_LOCK_FAILURES__
+#include <e32math.h>
+#endif
+
+#define CACHE_NUM_TO_ADDR(n) (iBase + (n << iCacheLineSizeLog2))
+#define ADDR_TO_CACHELINE_ADDR(a) ((((a - iBase) >> iCacheLineSizeLog2) << iCacheLineSizeLog2) + iBase)
+#define ADDR_TO_INDEX(a) ((a - iBase) >> iCacheLineSizeLog2)
+
+#if defined(_DEBUG)
+ #define __CACHELINE_INVARIANT(aCacheLine) \
+ __ASSERT_DEBUG( \
+ aCacheLine.iDirtySegments <= aCacheLine.iFilledSegments && \
+ aCacheLine.iFilledSegments <= aCacheLine.iAllocatedSegments, \
+ Fault(EInvalidCacheLine));
+
+#else
+ #define __CACHELINE_INVARIANT(aCacheLine)
+#endif
+
+const TInt KSegmentSize = 4096;
+const TInt KSegmentSizeLog2 = 12;
+const TInt64 KSegmentSizeMask = MAKE_TINT64(0x7FFFFFFF,0xFFFFF000);
+
+const TInt KCacheLineSizeLog2 = 17; // 128K
+const TInt KCacheLineSize = (1 << KCacheLineSizeLog2 );
+const TInt KCacheLineSizeInSegments = (KCacheLineSize >> KSegmentSizeLog2);
+
+CCacheManager* CCacheManagerFactory::iCacheManager = NULL;
+
+
+LOCAL_C void Fault(TCacheManagerFault aFault)
+//
+// Report a fault in the cache manager
+//
+ {
+ User::Panic(_L("FSCACHEMAN"), aFault);
+ }
+
+
+/*
+Indicates if a number passed in is a power of two
+
+@param aNum number to be tested
+@return Flag to indicate the result of the test
+*/
+LOCAL_C TBool IsPowerOfTwo(TInt aNum)
+ {
+ return (aNum != 0 && (aNum & -aNum) == aNum);
+ }
+
+
+//********************
+// CCacheManagerFactory
+//********************
+void CCacheManagerFactory::CreateL()
+ {
+ __ASSERT_ALWAYS(iCacheManager == NULL, Fault(ECacheAlreadyCreated));
+
+ if (TGlobalFileCacheSettings::Enabled())
+ {
+ iCacheManager = CCacheManager::NewCacheL(TGlobalFileCacheSettings::CacheSize());
+ }
+ else
+ {
+ Destroy();
+ }
+ }
+
+CCacheManager* CCacheManagerFactory::CacheManager()
+ {
+ return iCacheManager;
+ }
+
+TInt CCacheManagerFactory::Destroy()
+ {
+ delete iCacheManager;
+ iCacheManager = NULL;
+ return KErrNone;
+ }
+
+//********************
+// CCacheManager
+//********************
+CCacheManager* CCacheManager::NewCacheL(TInt aCacheSize)
+ {
+ CCacheManager* cacheManager = new (ELeave) CCacheManager(aCacheSize);
+
+ CleanupStack::PushL(cacheManager);
+ cacheManager->ConstructL();
+ CleanupStack::Pop(1, cacheManager);
+
+ return cacheManager;
+ }
+
+CCacheManager::~CCacheManager()
+ {
+ CacheLock();
+
+ iFreeQueue.Close();
+ iUsedQueue.Close();
+
+ delete [] iCacheLines;
+
+ CacheUnlock();
+ iLock.Close();
+
+ iChunk.Close();
+ }
+
+
+CCacheManager::CCacheManager(TUint aCacheSize)
+ {
+ // Get the page size
+ TInt pageSize;
+ TInt r = HAL::Get(HAL::EMemoryPageSize, pageSize);
+ if (r != KErrNone)
+ pageSize = KSegmentSize;
+ __ASSERT_ALWAYS(IsPowerOfTwo(pageSize), Fault(EIllegalPageSize));
+
+ // For the time being we only a support page size of 4K and we assume
+ // that page size = segment size, since the extra overhead of supporting
+ // variable page sizes now is non-trivial.
+ //
+ // If a processor with a different page size needs to be supported
+ // in the future, then we need to either rework this code to be able to
+ // devide up pages into smaller segments or analyze the impact of having
+ // a different page size on performance.
+ __ASSERT_ALWAYS(pageSize == KSegmentSize, Fault(EIllegalPageSize));
+
+ iCacheLineSize = KCacheLineSize;
+ iCacheLineSizeLog2 = KCacheLineSizeLog2;
+
+ iCacheSize = aCacheSize;
+
+ iSegmentsPerCacheLine = 1 << (iCacheLineSizeLog2 - KSegmentSizeLog2);
+
+ iMaxLockedSegments = TGlobalFileCacheSettings::MaxLockedSize() >> KSegmentSizeLog2;
+
+#ifdef __SIMULATE_LOCK_FAILURES__
+ iSimulateLockFailureMode = ETrue;
+#endif
+ }
+
+void CCacheManager::ConstructL()
+ {
+
+ // calculate the low-memory threshold below which we fail any attempt to allocate memory
+ TMemoryInfoV1Buf meminfo;
+ TInt r = UserHal::MemoryInfo(meminfo);
+ __ASSERT_ALWAYS(r==KErrNone,Fault(EMemoryInfoFailed));
+
+ iLowMemoryThreshold = (meminfo().iTotalRamInBytes * TGlobalFileCacheSettings::LowMemoryThreshold()) / 100;
+ __CACHE_PRINT4(_L("CACHEMAN: totalRAM %d freeRAM %d KDefaultLowMemoryThresholdPercent %d iLowMemoryThreshold %d"),
+ meminfo().iTotalRamInBytes, meminfo().iFreeRamInBytes, KDefaultLowMemoryThreshold, iLowMemoryThreshold);
+ __CACHE_PRINT1(_L("CACHEMAN: iCacheSize %d"), iCacheSize);
+ TChunkCreateInfo createInfo;
+ createInfo.SetCache(iCacheSize);
+ createInfo.SetOwner(EOwnerProcess);
+ r = iChunk.Create(createInfo);
+ User::LeaveIfError(r);
+
+
+ TInt mm = UserSvr::HalFunction(EHalGroupKernel, EKernelHalMemModelInfo, 0, 0) & EMemModelTypeMask;
+ if (mm < EMemModelTypeFlexible)
+ {
+ // On memory models before flexible, File System has to register chunks that will be DMA-ed.
+ // On flexible memory model, local media is using new physical pinning Kernel interface that
+ // doesn't require registration of the chunk.
+ UserSvr::RegisterTrustedChunk(iChunk.Handle());
+ }
+
+
+ __ASSERT_ALWAYS(iCacheSize > KSegmentSize, Fault(EIllegalCacheSize));
+
+ r = iLock.CreateLocal();
+ User::LeaveIfError(r);
+
+ iNumOfCacheLines = iCacheSize >> iCacheLineSizeLog2;
+ iBase = Base();
+
+ iCacheLines = new (ELeave) TCacheLine[iNumOfCacheLines];
+
+ for (TInt n=0; n<iNumOfCacheLines; n++)
+ {
+ // coverity[var_decl]
+ TCacheLine cacheLine;
+ cacheLine.iAddr = CACHE_NUM_TO_ADDR(n);
+ cacheLine.iAllocatedSegments = 0;
+ cacheLine.iFilledSegments = 0;
+ cacheLine.iDirtySegments = 0;
+ cacheLine.iLockCount = 0;
+ cacheLine.iLockedSegmentStart = 0;
+ cacheLine.iLockedSegmentCount = 0;
+ cacheLine.iClient = NULL;
+ // coverity[uninit_use]
+ iCacheLines[n] = cacheLine;
+
+ r = iFreeQueue.Append(&iCacheLines[n]);
+ User::LeaveIfError(r);
+ }
+
+ }
+
+CCacheClient* CCacheManager::CreateClientL()
+ {
+ CCacheClient* client = CCacheClient::NewL(*this);
+ return client;
+ }
+
+void CCacheManager::RegisterClient(CCacheClient& /*aClient*/)
+ {
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ iStats.iFileCount++;
+#endif
+ }
+
+void CCacheManager::DeregisterClient(CCacheClient& /*aClient*/)
+ {
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ iStats.iFileCount--;
+#endif
+ }
+
+
+void CCacheManager::FreeMemoryChanged(TBool aMemoryLow)
+ {
+ iMemoryLow = aMemoryLow;
+ }
+
+TBool CCacheManager::TooManyLockedSegments()
+ {
+ return (iLockedSegmentCount >= iMaxLockedSegments)?(TBool)ETrue:(TBool)EFalse;
+ }
+
+TInt CCacheManager::SegmentSize()
+ {
+ return KSegmentSize;
+ }
+
+TInt CCacheManager::SegmentSizeLog2()
+ {
+ return KSegmentSizeLog2;
+ }
+
+TInt64 CCacheManager::SegmentSizeMask()
+ {
+ return KSegmentSizeMask;
+ }
+
+TInt CCacheManager::CacheLineSize()
+ {
+ return KCacheLineSize;
+ }
+
+TInt CCacheManager::CacheLineSizeInSegments()
+ {
+ return KCacheLineSizeInSegments;
+ }
+
+TInt CCacheManager::CacheLineSizeLog2()
+ {
+ return KCacheLineSizeLog2;
+ }
+
+
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+TFileCacheStats& CCacheManager::Stats()
+ {
+ iStats.iFreeCount = iFreeQueue.Count();
+ iStats.iUsedCount = iUsedQueue.Count();
+ iStats.iLockedSegmentCount = iLockedSegmentCount;
+ iStats.iFilesOnClosedQueue = TClosedFileUtils::Count();
+ __ASSERT_DEBUG(iStats.iFreeCount >= 0, Fault(EInvalidStats));
+ __ASSERT_DEBUG(iStats.iUsedCount >= 0, Fault(EInvalidStats));
+ __ASSERT_DEBUG(iStats.iLockedSegmentCount >= 0, Fault(EInvalidStats));
+ __ASSERT_DEBUG(iStats.iFilesOnClosedQueue >= 0, Fault(EInvalidStats));
+ return iStats;
+ }
+#endif // #if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+
+
+
+void CCacheManager::CacheLock()
+ {
+ iLock.Wait();
+
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ iManagerLocked = ETrue;
+#endif
+ }
+
+void CCacheManager::CacheUnlock()
+ {
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ iManagerLocked = EFalse;
+#endif
+ iLock.Signal();
+ }
+
+
+TInt CCacheManager::AllocateAndLockCacheLine(CCacheClient* aClient, TInt64 aPos, const TCacheLine*& aCacheLine, TInt aSegmentCount)
+ {
+ __ASSERT_DEBUG(aSegmentCount <= iSegmentsPerCacheLine, Fault(EInvalidAllocCount));
+
+ // check for low system memory
+ TMemoryInfoV1Buf meminfo;
+ TInt r = UserHal::MemoryInfo(meminfo);
+ __ASSERT_ALWAYS(r==KErrNone,Fault(EMemoryInfoFailed));
+ if (iMemoryLow || (meminfo().iFreeRamInBytes < iLowMemoryThreshold))
+ {
+ __CACHE_PRINT(_L("CACHEMAN: free RAM below threshold !!!"));
+ return KErrNoMemory;
+ }
+
+ CacheLock();
+
+ __CACHE_PRINT1(_L("CACHEMAN: Allocate %d segments, Lock %d"), aSegmentCount);
+ __CACHE_PRINT2(_L("CACHEMAN: iFreeQueue %d iUsedQueue %d"), iFreeQueue.Count(), iUsedQueue.Count());
+
+#ifdef __SIMULATE_LOCK_FAILURES__
+ if (SimulatedFailure(iAllocFailureCount))
+ {
+ __CACHE_PRINT(_L("CACHEMAN: simulating allocation failure"));
+ CacheUnlock();
+ return KErrNoMemory;
+ }
+#endif
+
+ // any cachelines free ?
+ TInt freeCacheLines = iFreeQueue.Count();
+ TCacheLine* cacheLine = NULL;
+ if (freeCacheLines == 0 && !StealCacheLine(aClient))
+ {
+ CacheUnlock();
+ return KErrNotFound;
+ }
+
+ cacheLine = iFreeQueue[0];
+
+ __CACHELINE_INVARIANT((*cacheLine));
+ __ASSERT_DEBUG( cacheLine->iAllocatedSegments == 0, Fault(EInvalidAllocCount));
+ __ASSERT_DEBUG( cacheLine->iFilledSegments == 0, Fault(EInvalidFillCount));
+ __ASSERT_DEBUG( cacheLine->iDirtySegments == 0, Fault(EInvalidFillCount));
+ __ASSERT_DEBUG( cacheLine->iLockCount == 0, Fault(EInvalidLockCount));
+ __ASSERT_DEBUG( cacheLine->iLockedSegmentStart == 0, Fault(EInvalidLockedPageStart));
+ __ASSERT_DEBUG( cacheLine->iLockedSegmentCount == 0, Fault(EInvalidLockedPageCount));
+ __ASSERT_DEBUG( cacheLine->iClient == NULL, Fault(EInvalidClient));
+
+ TUint8* addr = cacheLine->iAddr;
+
+ // Commit it
+ r = Commit(addr, aSegmentCount);
+ if (r != KErrNone)
+ {
+ CacheUnlock();
+ return r;
+ }
+ cacheLine->iAllocatedSegments = (TUint8) (aSegmentCount);
+ cacheLine->iLockedSegmentStart = 0;
+ cacheLine->iLockedSegmentCount = (TUint8) aSegmentCount;
+
+ // Add to used queue
+ r = iUsedQueue.InsertInAddressOrder(cacheLine);
+ if (r != KErrNone)
+ {
+ Decommit(addr, aSegmentCount);
+ CacheUnlock();
+ return r;
+ }
+
+ cacheLine->iClient = aClient;
+ cacheLine->iPos = aPos;
+
+ // Remove from free queue
+ iFreeQueue.Remove(0);
+
+ // RChunk will lock segments initially unless explicitly unlocked
+
+ cacheLine->iLockCount++;
+
+ __CACHE_PRINT2(_L("CACHEMAN: LockCount for %08X is %d"), cacheLine->iAddr, cacheLine->iLockCount);
+
+ CacheUnlock();
+
+ aCacheLine = cacheLine;
+ return r;
+
+ }
+
+/**
+CCacheManager::ExtendCacheLine()
+
+Attempts to extend the length of an existing cacheline by committing extra segments
+The cacheline must be owned and locked already.
+
+@param aCacheLine A reference to a locked, owned cacheline
+@param aSegmentCount The new length of the cacheline (including the existing segments)
+ This must be greater then the existing length.
+
+@return KErrNone if successful
+ or other system wide error code.
+*/
+TInt CCacheManager::ExtendCacheLine(CCacheClient* aClient, const TCacheLine& aCacheLine, TInt aSegmentCount)
+ {
+ CacheLock();
+
+ __ASSERT_DEBUG(aSegmentCount > 0, Fault(EInvalidSegmentCount));
+
+ TCacheLine& cacheLine = const_cast<TCacheLine&>(aCacheLine);
+ __CACHELINE_INVARIANT((cacheLine));
+ __ASSERT_DEBUG(aSegmentCount <= iSegmentsPerCacheLine, Fault(EInvalidSegmentCount));
+ __ASSERT_DEBUG(aSegmentCount > cacheLine.iAllocatedSegments, Fault(EInvalidSegmentCount));
+ __ASSERT_DEBUG(cacheLine.iLockCount > 0, Fault(EInvalidLockCount)); // must be locked already
+ __ASSERT_ALWAYS(cacheLine.iClient == aClient, Fault(EExtendingUnownedCacheline));
+
+ // ensure all pages in cachline are locked
+ __ASSERT_DEBUG(cacheLine.iLockedSegmentStart == 0, Fault(EInvalidLockRange));
+ __ASSERT_DEBUG(cacheLine.iLockedSegmentCount == cacheLine.iAllocatedSegments, Fault(EInvalidLockRange));
+
+ __CACHE_PRINT2(_L("CACHEMAN: ExtendCacheLine(%08X, %d)"), cacheLine.iAddr, aSegmentCount);
+
+ // Commit the new segments
+ TUint8* addrNewSegment = cacheLine.iAddr + (cacheLine.iAllocatedSegments << KSegmentSizeLog2);
+ TInt segmentCountNew = aSegmentCount - cacheLine.iAllocatedSegments;
+
+ TInt r = Commit(addrNewSegment, segmentCountNew);
+ if (r == KErrNone)
+ {
+ cacheLine.iAllocatedSegments = cacheLine.iLockedSegmentCount = (TUint8) aSegmentCount;
+ }
+
+
+ CacheUnlock();
+ return r;
+ }
+
+/**
+CCacheManager::ReUseCacheLine()
+
+Attempts to lock and then extend or shrink an already owned cacheline, ready for re-use.
+If successful the cacheline is returned locked and all segments are marked as empty.
+The cacheline must initially be unlocked.
+
+@param aCacheLine A reference to an unlocked cacheline
+@param aSegmentCount The new length of the cacheline;
+ this may be greater or less than the existing length.
+
+@return KErrNone if successful
+ or other system wide error code.
+*/
+TInt CCacheManager::ReAllocateAndLockCacheLine(CCacheClient* aClient, TInt64 aPos, const TCacheLine& aCacheLine, TInt aSegmentCount)
+ {
+ TCacheLine& cacheLine = const_cast<TCacheLine&>(aCacheLine);
+ __CACHELINE_INVARIANT((cacheLine));
+
+ __ASSERT_DEBUG(aSegmentCount > 0, Fault(EInvalidSegmentCount));
+ __ASSERT_DEBUG(aSegmentCount <= iSegmentsPerCacheLine, Fault(EInvalidSegmentCount));
+
+ __CACHE_PRINT2(_L("CACHEMAN: ReUseCacheLine(%08X, %d)"), cacheLine.iAddr, aSegmentCount);
+
+ TInt r;
+ // old cacheline same size or bigger ?
+ if (cacheLine.iAllocatedSegments >= aSegmentCount)
+ {
+ r = LockCacheLine(aClient, aCacheLine, 0, aSegmentCount);
+ if (r!= KErrNone)
+ return r;
+ if (cacheLine.iAllocatedSegments > aSegmentCount)
+ {
+ cacheLine.iFilledSegments = (TUint8) aSegmentCount;
+ RemoveEmptySegments(aClient, aCacheLine);
+ }
+ }
+ // old cacheline smaller
+ else
+ {
+ r = LockCacheLine(aClient, aCacheLine, 0, cacheLine.iAllocatedSegments);
+ if (r != KErrNone)
+ return r;
+ r = ExtendCacheLine(aClient, aCacheLine, aSegmentCount);
+ if (r != KErrNone)
+ {
+ UnlockCacheLine(aClient, aCacheLine);
+ return r;
+ }
+ }
+ cacheLine.iFilledSegments = 0;
+ cacheLine.iPos = aPos;
+
+ return KErrNone;
+ }
+
+
+/**
+CCacheManager::LockCacheLine()
+
+@return KErrNone on success
+*/
+TInt CCacheManager::LockCacheLine(
+ CCacheClient* aClient,
+ const TCacheLine& aCacheLine,
+ TInt aLockedPageStart,
+ TInt aLockedPageCount)
+ {
+ CacheLock();
+
+
+ TCacheLine& cacheLine = const_cast<TCacheLine&>(aCacheLine);
+ __CACHELINE_INVARIANT((cacheLine));
+
+ // has the cacheline been stolen ?
+ if (cacheLine.iClient != aClient)
+ {
+ __CACHE_PRINT(_L("CCacheManager::LockCacheLine(), Cacheline Stolen !\n"));
+ CacheUnlock();
+ return KErrNotFound;
+ }
+
+ // validate lock range
+ __ASSERT_DEBUG(aLockedPageStart >= 0, Fault(EInvalidLockRange));
+ __ASSERT_DEBUG(aLockedPageStart + aLockedPageCount <= cacheLine.iAllocatedSegments, Fault(EInvalidLockRange));
+ __ASSERT_DEBUG(aLockedPageCount <= iSegmentsPerCacheLine, Fault(EInvalidLockRange));
+ // if already locked, don't allow lock range to grow down or up (to simplify code)
+ __ASSERT_DEBUG(cacheLine.iLockCount == 0 ||
+ aLockedPageStart >= cacheLine.iLockedSegmentStart,
+ Fault(EInvalidLockRange));
+ __ASSERT_DEBUG(cacheLine.iLockCount == 0 ||
+ aLockedPageStart + aLockedPageCount <= cacheLine.iLockedSegmentStart + cacheLine.iLockedSegmentCount,
+ Fault(EInvalidLockRange));
+
+ __CACHE_PRINT1(_L("CACHEMAN: LockCacheLine(%08X, %d)"), cacheLine.iAddr);
+
+ if (InUse(aCacheLine))
+ {
+ __CACHE_PRINT(_L("CCacheManager::LockCacheLine(), Cacheline in use !\n"));
+ CacheUnlock();
+ return KErrInUse;
+ }
+
+ TInt lockErr = KErrNone;
+
+ // increment the lock count
+ // if not already locked, lock requested segments
+ if (cacheLine.iLockCount++ == 0)
+ {
+#ifdef __LOCK_ENTIRE_CACHELINE__
+ lockErr = Lock(cacheLine.iAddr, cacheLine.iAllocatedSegments);
+#else
+ __ASSERT_DEBUG(cacheLine.iDirtySegments == 0, Fault(ELockingAndAlreadyDirty));
+ lockErr = Lock( cacheLine.iAddr + (aLockedPageStart<<KSegmentSizeLog2), aLockedPageCount);
+#endif
+
+ if (lockErr == KErrNone)
+ {
+ cacheLine.iLockedSegmentStart = (TUint8) aLockedPageStart;
+ cacheLine.iLockedSegmentCount = (TUint8) aLockedPageCount;
+ }
+ else // if (lockErr != KErrNone)
+ {
+ __CACHE_PRINT2(_L("CACHEMAN: LockCacheLine(%08X) failure %d"), cacheLine.iAddr, lockErr);
+ cacheLine.iLockCount--;
+ // NB a lock failure implies segment is decomitted
+ FreeCacheLine(cacheLine);
+ }
+ }
+ // if already locked (because cacheline is dirty) ensure lock range
+ // isn't growing (this isn't allowed to keep the code simpler)
+ else
+ {
+ __ASSERT_DEBUG(cacheLine.iLockedSegmentStart == 0, Fault(EInvalidLockRange));
+ __ASSERT_DEBUG(cacheLine.iLockedSegmentCount >= aLockedPageStart + aLockedPageCount, Fault(EInvalidLockRange));
+ }
+
+ CacheUnlock();
+ return lockErr;
+ }
+
+/**
+UnlockCacheLine()
+*/
+TBool CCacheManager::UnlockCacheLine(CCacheClient* aClient, const TCacheLine& aCacheLine)
+ {
+ CacheLock();
+
+ TCacheLine& cacheLine = const_cast<TCacheLine&>(aCacheLine);
+ __CACHELINE_INVARIANT((cacheLine));
+
+ __ASSERT_DEBUG(cacheLine.iLockCount > 0, Fault(EInvalidLockCount));
+ __ASSERT_ALWAYS(cacheLine.iClient == aClient, Fault(EUnlockingUnownedCacheline));
+
+
+ __CACHE_PRINT2(_L("CACHEMAN: UnlockCacheLine(%08X, %d)"), cacheLine.iAddr, cacheLine.iAllocatedSegments);
+
+
+ // decrement the lock count
+ TBool success = ETrue;
+
+ if (cacheLine.iLockCount > 1)
+ {
+ cacheLine.iLockCount--;
+ }
+ else
+ {
+ if (cacheLine.iDirtySegments == 0)
+ {
+ cacheLine.iLockCount--;
+#ifdef __LOCK_ENTIRE_CACHELINE__
+ Unlock(cacheLine.iAddr, cacheLine.iAllocatedSegments);
+#else
+ Unlock(
+ cacheLine.iAddr + (cacheLine.iLockedSegmentStart<<KSegmentSizeLog2),
+ cacheLine.iLockedSegmentCount);
+#endif
+
+ cacheLine.iLockedSegmentStart = cacheLine.iLockedSegmentCount = 0;
+ }
+ else
+ {
+ __CACHE_PRINT(_L("CACHEMAN: UnlockCacheLine - not unlocking segment dirty"));
+ success = EFalse;
+ }
+ }
+
+ CacheUnlock();
+
+ return success;
+ }
+
+
+
+TBool CCacheManager::StealCacheLine(CCacheClient* aClient)
+ {
+ __ASSERT_DEBUG(iManagerLocked, Fault(EManagerNotLocked));
+
+ TInt usedQueueSize = iUsedQueue.Count();
+
+ #define INC_INDEX(x) (x++, x = (x >= usedQueueSize ? 0 : x))
+
+ __CACHE_PRINT2(_L("CACHEMAN: StealCacheLine, iNextCacheLineToSteal %d used count %d"), iNextCacheLineToSteal, iUsedQueue.Count());
+
+ TInt iInitIndex = iNextCacheLineToSteal;
+
+ // Loop through all used cachelines, starting at the last stolen
+ // cacheline index + 1 and ending when we find a suitable cacheline
+ // to steal or we have looped back to the start
+ for (INC_INDEX(iNextCacheLineToSteal);
+ iNextCacheLineToSteal != iInitIndex;
+ INC_INDEX(iNextCacheLineToSteal))
+ {
+ TCacheLine& cacheLine = *iUsedQueue[iNextCacheLineToSteal];
+ if (cacheLine.iLockCount == 0 && cacheLine.iClient != aClient)
+ {
+ __CACHE_PRINT3(_L("CACHEMAN: StealCacheLine, stealing %d from %08X to %08X"),
+ iNextCacheLineToSteal, cacheLine.iClient, aClient);
+ FreeCacheLine(cacheLine);
+ return ETrue;
+ }
+ }
+ return EFalse;
+ }
+
+
+TBool CCacheManager::FreeCacheLine(CCacheClient* aClient, const TCacheLine& aCacheLine)
+ {
+ CacheLock();
+
+ TCacheLine& cacheLine = const_cast<TCacheLine&>(aCacheLine);
+ __CACHELINE_INVARIANT((cacheLine));
+
+ // Has the cacheline been stolen ? (Assume success if it has)
+ if (cacheLine.iClient != aClient)
+ {
+ __CACHE_PRINT(_L("CCacheManager::FreeCacheLine(), Cacheline Stolen !!!!\n"));
+ CacheUnlock();
+ return ETrue;
+ }
+
+ // Is the cacheline locked ?
+ if (cacheLine.iLockCount > 0)
+ {
+ __CACHE_PRINT(_L("CCacheManager::FreeCacheLine(), attempt to free locked cacheline\n"));
+ CacheUnlock();
+ return EFalse;
+ }
+
+ FreeCacheLine(cacheLine);
+
+ CacheUnlock();
+ return ETrue;
+ }
+
+TInt CCacheManager::LockCount(CCacheClient* aClient, const TCacheLine& aCacheLine)
+ {
+ TCacheLine& cacheLine = const_cast<TCacheLine&>(aCacheLine);
+ __CACHELINE_INVARIANT((cacheLine));
+
+ // cacheline stolen ?
+ if (cacheLine.iClient != aClient)
+ return 0;
+
+ return cacheLine.iLockCount;
+ }
+
+TInt CCacheManager::FillCount(CCacheClient* aClient, const TCacheLine& aCacheLine)
+ {
+ TCacheLine& cacheLine = const_cast<TCacheLine&>(aCacheLine);
+ __CACHELINE_INVARIANT((cacheLine));
+
+ // cacheline stolen ?
+ if (cacheLine.iClient != aClient)
+ return 0;
+
+ return cacheLine.iFilledSegments;
+ }
+
+TInt CCacheManager::DirtyCount(CCacheClient* aClient, const TCacheLine& aCacheLine)
+ {
+ TCacheLine& cacheLine = const_cast<TCacheLine&>(aCacheLine);
+ __CACHELINE_INVARIANT((cacheLine));
+
+ // cacheline stolen ?
+ if (cacheLine.iClient != aClient)
+ return 0;
+
+ return cacheLine.iDirtySegments;
+ }
+
+TBool CCacheManager::InUse(const TCacheLine& aCacheLine)
+ {
+ TCacheLine& cacheLine = const_cast<TCacheLine&>(aCacheLine);
+ __CACHELINE_INVARIANT((cacheLine));
+
+ // busy if lock count >= 1 and there are no dirty segments
+ // or if lock count >= 2 and there are dirty segments
+ return (cacheLine.iLockCount -
+ (cacheLine.iDirtySegments?1:0)) > 0 ? (TBool)ETrue : (TBool)EFalse;
+ }
+
+void CCacheManager::SetFilled(CCacheClient* aClient, const TCacheLine& aCacheLine, TInt aSegmentCount)
+ {
+ TCacheLine& cacheLine = const_cast<TCacheLine&>(aCacheLine);
+ __CACHELINE_INVARIANT((cacheLine));
+ __ASSERT_DEBUG(aSegmentCount <= iSegmentsPerCacheLine, Fault(EInvalidSegmentCount));
+ __ASSERT_DEBUG(aSegmentCount <= cacheLine.iAllocatedSegments , Fault(EInvalidDirtyCount));
+ __ASSERT_DEBUG(cacheLine.iLockCount > 0, Fault(ESetFilledNotLocked));
+ __ASSERT_ALWAYS(cacheLine.iClient == aClient, Fault(EExtendingUnownedCacheline));
+
+
+ cacheLine.iFilledSegments = Max(cacheLine.iFilledSegments, (TUint8) aSegmentCount);
+
+ __ASSERT_DEBUG(cacheLine.iFilledSegments >= cacheLine.iDirtySegments , Fault(EInvalidDirtyCount));
+ }
+
+void CCacheManager::SetDirty(CCacheClient* aClient, const TCacheLine& aCacheLine, TInt aSegmentCount)
+ {
+ TCacheLine& cacheLine = const_cast<TCacheLine&>(aCacheLine);
+ __CACHELINE_INVARIANT((cacheLine));
+ __ASSERT_DEBUG(aSegmentCount <= iSegmentsPerCacheLine, Fault(EInvalidSegmentCount));
+ __ASSERT_DEBUG(aSegmentCount <= cacheLine.iAllocatedSegments , Fault(EInvalidDirtyCount));
+ __ASSERT_DEBUG(cacheLine.iLockCount > 0, Fault(ESetDirtyNotLocked));
+ __ASSERT_ALWAYS(cacheLine.iClient == aClient, Fault(EExtendingUnownedCacheline));
+
+ // ensure that lock range is valid - we insist on dirty
+ // cachelines having all dirty pages locked up to the the last dirty page
+ __ASSERT_DEBUG(cacheLine.iLockedSegmentStart == 0, Fault(ESetDirtyInvalidLockRange));
+ __ASSERT_DEBUG(cacheLine.iLockedSegmentCount >= aSegmentCount, Fault(ESetDirtyInvalidLockRange));
+
+ cacheLine.iFilledSegments = Max(cacheLine.iFilledSegments, (TUint8) aSegmentCount);
+ cacheLine.iDirtySegments = Max(cacheLine.iDirtySegments, (TUint8) aSegmentCount);
+
+ __ASSERT_DEBUG(cacheLine.iFilledSegments >= cacheLine.iDirtySegments , Fault(EInvalidDirtyCount));
+ }
+
+void CCacheManager::ClearDirty(CCacheClient* aClient, const TCacheLine& aCacheLine)
+ {
+ TCacheLine& cacheLine = const_cast<TCacheLine&>(aCacheLine);
+ __CACHELINE_INVARIANT((cacheLine));
+ __ASSERT_DEBUG(cacheLine.iLockCount > 0, Fault(EClearDirtyNotLocked));
+ __ASSERT_ALWAYS(cacheLine.iClient == aClient, Fault(EExtendingUnownedCacheline));
+
+ TInt dirtySegments = cacheLine.iDirtySegments;
+ cacheLine.iDirtySegments = 0;
+ SetFilled(aClient, cacheLine, dirtySegments);
+ UnlockCacheLine(aClient, cacheLine);
+ }
+
+
+void CCacheManager::RemoveEmptySegments(CCacheClient* aClient, const TCacheLine& aCacheLine)
+ {
+ CacheLock();
+
+ TCacheLine& cacheLine = const_cast<TCacheLine&>(aCacheLine);
+ __CACHELINE_INVARIANT((cacheLine));
+
+ // has the cacheline been stolen ?
+ if (cacheLine.iClient != aClient)
+ {
+ __CACHE_PRINT(_L("CCacheManager::RemoveEmptySegments((), Cacheline Stolen ! !!!\n"));
+ CacheUnlock();
+ return;
+ }
+
+ __ASSERT_DEBUG(cacheLine.iLockCount > 0, Fault(ERemovingEmptyUnlocked));
+
+ // Unlock any locked segments past the last filled segment
+ TInt filledSegmentCount = cacheLine.iFilledSegments;
+ TInt firstSegmentToUnlock;
+ TInt segmentsToUnlock;
+#ifdef __LOCK_ENTIRE_CACHELINE__
+ firstSegmentToUnlock = filledSegmentCount;
+ segmentsToUnlock = cacheLine.iAllocatedSegments - filledSegmentCount;
+#else
+ TInt firstLockedSegment = cacheLine.iLockedSegmentStart;
+ if (firstLockedSegment <= filledSegmentCount)
+ {
+ firstSegmentToUnlock = filledSegmentCount;
+ segmentsToUnlock = firstLockedSegment + cacheLine.iLockedSegmentCount - filledSegmentCount;
+ }
+ else
+ {
+ firstSegmentToUnlock = firstLockedSegment;
+ segmentsToUnlock = cacheLine.iLockedSegmentCount;
+ }
+#endif
+ TUint8* addrFirstSegmentToUnlock =
+ cacheLine.iAddr + (firstSegmentToUnlock << KSegmentSizeLog2);
+ if (segmentsToUnlock > 0)
+ {
+ Unlock(addrFirstSegmentToUnlock, segmentsToUnlock);
+ cacheLine.iLockedSegmentCount =
+ (TUint8) (cacheLine.iLockedSegmentCount - segmentsToUnlock);
+ }
+
+ // Decommit any segments past the last filled segment
+ Decommit(
+ cacheLine.iAddr + (filledSegmentCount << KSegmentSizeLog2),
+ cacheLine.iAllocatedSegments - filledSegmentCount);
+ cacheLine.iAllocatedSegments = (TUint8) filledSegmentCount;
+
+ CacheUnlock();
+ }
+
+
+void CCacheManager::FreeCacheLine(TCacheLine& aCacheLine)
+ {
+ __ASSERT_DEBUG(iManagerLocked, Fault(EManagerNotLocked));
+ aCacheLine.iClient = NULL;
+ __ASSERT_ALWAYS( (aCacheLine.iLockCount == 0), Fault(EFreeingLockedCacheLine));
+ __ASSERT_ALWAYS( (aCacheLine.iDirtySegments == 0), Fault(EFreeingDirtyCacheLine));
+
+ Decommit(aCacheLine.iAddr, aCacheLine.iAllocatedSegments);
+ aCacheLine.iAllocatedSegments = 0;
+ aCacheLine.iFilledSegments = 0;
+ aCacheLine.iLockedSegmentStart = 0;
+ aCacheLine.iLockedSegmentCount = 0;
+
+ // Remove from used queue
+ TInt index = iUsedQueue.FindInAddressOrder(&aCacheLine);
+ __ASSERT_ALWAYS(index != KErrNotFound, Fault(ESegmentNotFound));
+ iUsedQueue.Remove(index);
+
+ // put back on free queue
+// TInt r = iFreeQueue.Append(&aCacheLine);
+ TInt r = iFreeQueue.InsertInAddressOrder(&aCacheLine);
+ __ASSERT_ALWAYS(r == KErrNone, Fault(EAppendToFreeQueueFailed));
+
+ __CACHE_PRINT2(_L("CACHEMAN: FreeCacheLine, iFreeQueue %d iUsedQueue %d"), iFreeQueue.Count(), iUsedQueue.Count());
+ }
+
+
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+void CCacheManager::DumpCacheLine(TCacheLine& aCacheLine)
+ {
+ RDebug::Print(_L("CACHEMAN: Cacheline client %08X addr %08X pos %d alloc %d filled %d dirty %d lock %d \tData: %02X %02X %02X %02X %02X %02X %02X %02X "),
+ aCacheLine.iClient,
+ aCacheLine.iAddr,
+ I64LOW(aCacheLine.iPos),
+
+ aCacheLine.iAllocatedSegments,
+ aCacheLine.iFilledSegments,
+ aCacheLine.iDirtySegments,
+ aCacheLine.iLockCount,
+
+ aCacheLine.iAddr[0],
+ aCacheLine.iAddr[1],
+ aCacheLine.iAddr[2],
+ aCacheLine.iAddr[3],
+ aCacheLine.iAddr[4],
+ aCacheLine.iAddr[5],
+ aCacheLine.iAddr[6],
+ aCacheLine.iAddr[7]
+ );
+ }
+
+void CCacheManager::DumpCache()
+ {
+ CacheLock();
+
+ RPointerArray<CCacheClient> clients;
+
+ RDebug::Print(_L("**** CACHEMAN: CacheLines ****\n"));
+ TInt usedQueueSize = iUsedQueue.Count();
+ TInt n;
+ for (n=0; n<usedQueueSize; n++)
+ {
+ TCacheLine& cacheLine = *iUsedQueue[n];
+ DumpCacheLine(cacheLine);
+
+ clients.InsertInAddressOrder(cacheLine.iClient);
+ }
+
+ TInt clientCount = clients.Count();
+
+ for (n=0; n<clientCount; n++)
+ {
+ RDebug::Print(_L("**** CACHEMAN: CacheClient #%d ****\n"), n);
+ clients[n]->DumpCache();
+ }
+
+ clients.Close();
+
+ CacheUnlock();
+ }
+#endif
+
+
+
+
+TUint8* CCacheManager::Base()
+ {
+ return iChunk.Base();
+ }
+
+
+TInt CCacheManager::Lock(TUint8* const aAddr, TInt aSegmentCount)
+ {
+ TInt r = iChunk.Lock(aAddr-iBase, aSegmentCount << KSegmentSizeLog2);
+//RDebug::Print(_L("RChunk::Lock(%08X, %d) %d"), aAddr-iBase, aSegmentCount << KSegmentSizeLog2, r);
+ __ASSERT_DEBUG(r == KErrNone || r == KErrNoMemory || r == KErrNotFound, Fault(EUnexpectedLockFailure));
+
+ __CACHE_PRINT3(_L("CACHEMAN: LOCK %08X %d %d"), aAddr, aSegmentCount, r);
+
+#ifdef __SIMULATE_LOCK_FAILURES__
+ if (SimulatedFailure(iLockFailureCount))
+ {
+ __CACHE_PRINT(_L("CACHEMAN: simulating lock failure"));
+ r = KErrNotFound;
+ }
+#endif
+
+ if (r == KErrNone)
+ {
+ iLockedSegmentCount+= aSegmentCount;
+ }
+ else
+ {
+ __CACHE_PRINT(_L("CACHEMAN: LOCK FAILED"));
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ iStats.iLockFailureCount++;
+#endif
+ }
+
+ return r;
+ }
+
+TInt CCacheManager::Unlock(TUint8* const aAddr, TInt aSegmentCount)
+ {
+ TInt r = iChunk.Unlock(aAddr-iBase, aSegmentCount << KSegmentSizeLog2);
+//RDebug::Print(_L("RChunk::Unlock(%08X, %d) %d"), aAddr-iBase, aSegmentCount << KSegmentSizeLog2, r);
+
+ __CACHE_PRINT3(_L("CACHEMAN: UNLOCK %08X %d %d"), aAddr, aSegmentCount, r);
+ if (r == KErrNone)
+ {
+ iLockedSegmentCount-= aSegmentCount;
+ }
+ else
+ {
+ __CACHE_PRINT(_L("CACHEMAN: UNLOCK FAILED"));
+ }
+
+ return r;
+ }
+
+TInt CCacheManager::Commit(TUint8* const aAddr, TInt aSegmentCount)
+ {
+#ifdef __SIMULATE_LOCK_FAILURES__
+ if (SimulatedFailure(iCommitFailureCount))
+ {
+ __CACHE_PRINT(_L("CACHEMAN: simulating commit failure "));
+ return KErrNoMemory;
+ }
+#endif
+
+ TInt r = iChunk.Commit(aAddr-iBase, aSegmentCount << KSegmentSizeLog2);
+//RDebug::Print(_L("RChunk::Commit(%08X, %d) %d, "), aAddr-iBase, aSegmentCount << KSegmentSizeLog2, r);
+
+ __CACHE_PRINT3(_L("CACHEMAN: COMMIT: %08X %d %d, "), aAddr, aSegmentCount, r);
+ if (r == KErrNone)
+ {
+ iLockedSegmentCount+= aSegmentCount;
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ iStats.iAllocatedSegmentCount+= aSegmentCount;
+#endif
+ }
+ else
+ {
+ __CACHE_PRINT(_L("CACHEMAN: COMMIT FAILED"));
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ iStats.iCommitFailureCount++;
+#endif
+ }
+
+
+ __ASSERT_DEBUG(r == KErrNone || r == KErrNoMemory, Fault(EUnexpectedCommitFailure));
+
+ return r;
+ }
+
+TInt CCacheManager::Decommit(TUint8* const aAddr, TInt aSegmentCount)
+ {
+ TInt r = iChunk.Decommit(aAddr-iBase, aSegmentCount << KSegmentSizeLog2);
+//RDebug::Print(_L("RChunk::Decommit(%08X, %d), %d"), aAddr-iBase, aSegmentCount << KSegmentSizeLog2, r);
+
+ __CACHE_PRINT3(_L("CACHEMAN: DECOMMIT: %08X %d %d, "), aAddr, aSegmentCount, r);
+ if (r == KErrNone)
+ {
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+ iStats.iAllocatedSegmentCount-= aSegmentCount;
+#endif
+ }
+ else
+ {
+ __CACHE_PRINT(_L("CACHEMAN: DECOMMIT FAILED"));
+ }
+
+ return r;
+ }
+
+#if defined(_DEBUG) || defined(_DEBUG_RELEASE)
+
+void CCacheManager::SimulateLockFailureMode(TBool aEnable)
+ {
+ iSimulateLockFailureMode = aEnable;
+__CACHE_PRINT1(_L("CACHEMAN: SimulateLockFailureMode: %d, "), iSimulateLockFailureMode);
+ }
+
+void CCacheManager::AllocateMaxSegments(TBool aEnable)
+ {
+ iAllocateMaxSegments = aEnable;
+__CACHE_PRINT1(_L("CACHEMAN: iAllocateMaxSegments: %d, "), iAllocateMaxSegments);
+ }
+
+TBool CCacheManager::AllocateMaxSegments()
+ {
+ return iAllocateMaxSegments;
+ }
+
+void CCacheManager::SimulateWriteFailure()
+ {
+ iSimulateWriteFailure = ETrue;
+ }
+
+TBool CCacheManager::SimulateWriteFailureEnabled()
+ {
+ TBool b = iSimulateWriteFailure;
+ iSimulateWriteFailure = EFalse;
+ return b;
+ }
+
+TBool CCacheManager::SimulatedFailure(TInt& aFailureCount)
+ {
+#ifdef __SIMULATE_LOCK_FAILURES__
+ if (iSimulateLockFailureMode)
+ {
+#if defined (__RAMDOM_LOCK_FAILURES__)
+ static TInt FailCount = 15;
+ if (++aFailureCount >= FailCount)
+#elif defined (__PSEUDO_RANDOM_FAILURES__)
+ const TInt FailCounts[] = {15,2,0,21,1,12,24};
+ const TInt FailCountSize = sizeof(FailCounts) / sizeof(TInt);
+ static TInt FailCountIndex = 0;
+ if (++aFailureCount >= FailCounts[FailCountIndex])
+#else
+ const TInt KFailCount = 15;
+ if (++aFailureCount >= KFailCount)
+#endif
+ {
+ aFailureCount = 0;
+#if defined (__RAMDOM_LOCK_FAILURES__)
+ FailCount = Math::Random() & 0x1F;
+ __CACHE_PRINT1(_L("FailCount %d"), FailCount);
+#endif
+
+#if defined (__PSEUDO_RANDOM_FAILURES__)
+ FailCountIndex = (FailCountIndex +1) % FailCountSize ;
+ __CACHE_PRINT1(_L("FailCount %d"), FailCounts[FailCountIndex]);
+#endif
+
+ return ETrue;
+ }
+ }
+#endif
+ return EFalse;
+ }
+#endif // defined(_DEBUG) || defined(_DEBUG_RELEASE)
+
+//************************************
+// TGlobalFileCacheSettings
+//************************************
+
+//TGlobalCacheFlags TGlobalFileCacheSettings::iFlags = TGlobalCacheFlags(ECacheEnabled);
+TInt32 TGlobalFileCacheSettings::iCacheSize = KDefaultGlobalCacheSize << KByteToByteShift;
+TInt32 TGlobalFileCacheSettings::iMaxLockedSize = KDefaultGlobalCacheMaxLockedSize << KByteToByteShift;
+TInt32 TGlobalFileCacheSettings::iLowMemoryThreshold = KDefaultLowMemoryThreshold;
+TBool TGlobalFileCacheSettings::iEnabled = KDefaultGlobalCacheEnabled;
+
+_LIT8(KLitSectionNameFileCache,"FileCache");
+
+void TGlobalFileCacheSettings::ReadPropertiesFile()
+ {
+ F32Properties::GetBool(KLitSectionNameFileCache, _L8("GlobalCacheEnabled"), iEnabled);
+
+ // Get size of cache in kilobytes
+ TInt32 cacheSizeInKBytes;
+ if (F32Properties::GetInt(KLitSectionNameFileCache, _L8("GlobalCacheSize"), cacheSizeInKBytes))
+ iCacheSize = cacheSizeInKBytes << KByteToByteShift;
+
+ // Get maximum amount of locked data i.e data unavailable for paging
+ TInt32 maxLockedSize;
+ if (F32Properties::GetInt(KLitSectionNameFileCache, _L8("GlobalCacheMaxLockedSize"), maxLockedSize))
+ iMaxLockedSize = maxLockedSize << KByteToByteShift;
+
+ // Get low memory threshold
+ TInt32 lowMemoryThreshold;
+ if (F32Properties::GetInt(KLitSectionNameFileCache, _L8("LowMemoryThreshold"), lowMemoryThreshold))
+ iLowMemoryThreshold = lowMemoryThreshold;
+
+ }
+
+
+TBool TGlobalFileCacheSettings::Enabled()
+ {
+ return iEnabled;
+ }
+
+TInt TGlobalFileCacheSettings::CacheSize()
+ {
+ return iCacheSize;
+ }
+TInt TGlobalFileCacheSettings::MaxLockedSize()
+ {
+ return iMaxLockedSize;
+ }
+
+TInt TGlobalFileCacheSettings::LowMemoryThreshold()
+ {
+ return iLowMemoryThreshold;
+ }