diff -r 000000000000 -r 96e5fb8b040d userlibandfileserver/fileserver/sfat32/sl_dir_cache.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/userlibandfileserver/fileserver/sfat32/sl_dir_cache.cpp Thu Dec 17 09:24:54 2009 +0200 @@ -0,0 +1,1227 @@ +// Copyright (c) 2008-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\sfat\sl_dir_cache.cpp +// +// + +#include "sl_std.h" +#include "sl_dir_cache.h" + +//====================================================================== +TDynamicDirCachePage::~TDynamicDirCachePage() + { + } + +/** +The static cache page creation function. +Cache page objects are not supposed to be created on the stack, so this factory function is required. +*/ +TDynamicDirCachePage* TDynamicDirCachePage::NewL(CDynamicDirCache* aOwnerCache, TInt64 aStartMedPos, TUint8* aStartRamAddr) + { + return new(ELeave) TDynamicDirCachePage(aOwnerCache, aStartMedPos, aStartRamAddr); + } + +/** +Cache page constructor. +@param aOwnerCache pointer of the cache that owns this page +@param aStartMedPos the start address on the media that this page caches +@param aStartRamAddr the start address in the ram that this page content lives +*/ +TDynamicDirCachePage::TDynamicDirCachePage(CDynamicDirCache* aOwnerCache, TInt64 aStartMedPos, TUint8* aStartRamAddr) +:iStartMedPos(aStartMedPos), +iStartRamAddr(aStartRamAddr), +iOwnerCache(aOwnerCache), +iValid(EFalse), +iLocked(EFalse) + { +// __PRINT3(_L("TDynamicDirCachePage::TDynamicDirCachePage(aStartMedPos=%lx, aStartRamAddr=0x%X, aPageSize=%u)"), aStartMedPos, aStartRamAddr, PageSizeInBytes()); + iType = EUnknown; + } + +/////////////////////////////// class CDynamicDirCache::TLookupEntry /////////////////////////// +/** +Required by RHashSet to identify individual hash set entries. +@see RHashSet +*/ +TBool IdentityFunction(const TLookupEntry& aEntry1, const TLookupEntry& aEntry2) + { + // only check starting med pos for hash searching + return aEntry1.iPos == aEntry2.iPos; + } +/** +Required by RHashSet to generate hash value. +@see RHashSet +*/ +TUint32 HashFunction(const TLookupEntry& aEntry) + { + return (DefaultHash::Integer(I64HIGH(aEntry.iPos)) + DefaultHash::Integer(I64LOW(aEntry.iPos))); + } + +/////////////////////////////// class CDynamicDirCache /////////////////////////// +CDynamicDirCache::~CDynamicDirCache() + { +// __PRINT(_L("CDynamicDirCache::~CDynamicDirCache()")); + + // we should never decommit locked pages + while (!iLockedQ.IsEmpty()) + { + TDynamicDirCachePage* page = iLockedQ.Last(); + DeQueue(page); // remove from queue + LookupTblRemove(page->StartPos()); // remove from lookuptable + delete page; + } + ASSERT(iLockedQCount == 0); + + while (!iUnlockedQ.IsEmpty()) + { + TDynamicDirCachePage* page = iUnlockedQ.Last(); + DeQueue(page); // remove from queue + LookupTblRemove(page->StartPos()); // remove from lookuptable + DecommitPage(page); // inform cache client to decommit page memory + delete page; + } + ASSERT(iUnlockedQCount == 0); + + delete iActivePage; + + ASSERT(iLookupTable.Count() == 0); + iLookupTable.Close(); + if (iCacheMemoryClient) + iCacheMemoryClient->Reset(); + } + +/** +Constructor of CDynamicDirCache. +@param aDrive local drive interface to read/write media +@param aMinPageNum the minimum page number for the cache, includes iActive page and locked pages. +@param aMaxPageNum the maximum page number for the cache, includes iActive page, locked pages and unlocked pages. +@param aPageSizeInBytesLog2 the log2 value of page size in bytes, assumes page size is always a power of two +*/ +CDynamicDirCache::CDynamicDirCache(TFatDriveInterface& aDrive, TUint32 aMinPageNum, TUint32 aMaxPageNum, TUint32 aPageSizeInBytesLog2) +:iPageSizeLog2(aPageSizeInBytesLog2), +iMinSizeInPages(aMinPageNum), +iMaxSizeInPages(aMaxPageNum), +iDrive(aDrive), +iLockedQ(_FOFF(TDynamicDirCachePage, iLink)), +iUnlockedQ(_FOFF(TDynamicDirCachePage, iLink)), +iLockedQCount(0), +iUnlockedQCount(0), +iHashFunction(HashFunction), +iIdentityFunction(IdentityFunction), +iLookupTable(iHashFunction, iIdentityFunction) + { + iPageSizeInBytes = 1 << aPageSizeInBytesLog2; + iCacheDisabled = EFalse; + iMinCacheSizeInBytes = aMinPageNum << aPageSizeInBytesLog2; + iMaxCacheSizeInBytes = aMaxPageNum << aPageSizeInBytesLog2; + ASSERT(iPageSizeInBytes && iPageSizeInBytes <= iMinCacheSizeInBytes && iMinCacheSizeInBytes <= iMaxCacheSizeInBytes); + // initial value, will be reset from outside + iCacheBasePos = 0; + } + +/** +Second phase constructor of CDynamicDirCache. +@param aClientName the identification of cache memeory client this cache connects +*/ +void CDynamicDirCache::ConstructL(const TDesC& aClientName) + { +// __PRINT3(_L("CDynamicDirCache::ConstructL(Min=%u, Max=%u, page=%u)"), iMinCacheSizeInBytes, iMaxCacheSizeInBytes, iPageSizeInBytes); + CCacheMemoryManager* manager = CCacheMemoryManagerFactory::CacheMemoryManager(); + if (manager) + { + // client will register itself onto cache memory manager when created + // note this operation may leave under OOM condition + iCacheMemoryClient = manager->ConnectClientL(aClientName, iMinSizeInPages * PageSizeInSegs(), iMaxSizeInPages * PageSizeInSegs()); + } + else + { + User::Leave(KErrNotSupported); + } + + ASSERT(iCacheMemoryClient); + if (!iCacheMemoryClient) + { + User::Leave(KErrNoMemory); + } + + // reserve active page + iActivePage = AllocateAndLockNewPageL(0); + ASSERT(iActivePage); + if (!iActivePage) + { + User::Leave(KErrNoMemory); + } + iActivePage->SetPageType(TDynamicDirCachePage::EActivePage); + } + +/** +Static factory function of CDynamicDirCache +*/ +CDynamicDirCache* CDynamicDirCache::NewL(TFatDriveInterface& aDrive, TUint32 aMinPageNum, TUint32 aMaxPageNum, TUint32 aPageSizeLog2, const TDesC& aClientName) + { +// __PRINT3(_L("CDynamicDirCache::NewL(MinPageNum=%u, MaxPageNum=%u, page=%u)"), aMinPageNum, aMaxPageNum, 1<ConstructL(aClientName); + CleanupStack::Pop(); + return pSelf; + } + +/** +Insert an unlocked page into the last position of the locked queue, may squeeze the original last page into +the unlocked queue. +This function is used on last visited but 'unlocked' pages to avoid excessive lock/unlock calls to cache memory +manager as contiguous entry reading/writing often happens on the same page. +@param aPage the page to be inserted. +@pre the page type of aPage should only be TDynamicDirCachePage::EUnknown +*/ +void CDynamicDirCache::MakePageLastLocked(TDynamicDirCachePage* aPage) + { + // this function should not be called on active pages + ASSERT(aPage->iType == TDynamicDirCachePage::EUnknown); + + if (iLockedQ.IsEmpty()) + { + // if locked queue is empty, add it onto the locked queue directly + AddFirstOntoQueue(aPage, TDynamicDirCachePage::ELocked); + } + else + { + // otherwise, we squeeze for the last position on locked queue + while (iLockedQCount + 1 >= iMinSizeInPages) + { + TDynamicDirCachePage* last = iLockedQ.Last(); + DeQueue(last); + UnlockPage(last); + AddFirstOntoQueue(last, TDynamicDirCachePage::EUnlocked); + } + + // iLockedQCount + 1 < iMinSizeInPages + iLockedQ.AddLast(*aPage); + aPage->SetPageType(TDynamicDirCachePage::ELocked); + iLockedQCount++; + } + } + +/** +Read data from a single page. If the page is not found or not valid anymore, read media onto iActive page +first. +@param aPos the starting position of the media address to be read. +@param aLength the length of the content to be read. +@param aDes the descriptor to contain the content. +@pre aLength should be no more than page size. +*/ +void CDynamicDirCache::ReadDataFromSinglePageL(TInt64 aPos, TInt aLength, TDes8& aDes) + { + //-- the data section is in the cache page entirely, take data directly from the cache + TDynamicDirCachePage* pPage = FindPageByPos(aPos); + if (pPage) + { + // lock page before reading, + if (LockPage(pPage) != NULL) + { + // read data + aDes.Copy(pPage->PtrInPage(aPos), aLength); + + // if page is from unlocked queue, insert it onto the last page of the locked + // queue. this is to avoid excessive locking and unlocking operations that is + // highly likely to happen for following reads. + if (pPage->PageType() == TDynamicDirCachePage::EUnlocked) + { + DeQueue(pPage); + MakePageLastLocked(pPage); + } + } + else // page locking failed + { + ASSERT(pPage->PageType() == TDynamicDirCachePage::EUnlocked); + DeQueue(pPage); + LookupTblRemove(pPage->StartPos()); + DecommitPage(pPage); + delete pPage; + pPage = NULL; + } + } + + if (!pPage) + { + // if page not found or page data not valid anymore, use active page to read data in + pPage = UpdateActivePageL(aPos); + // read data + aDes.Copy(pPage->PtrInPage(aPos), aLength); + } + + } + +//==================================================================== +/** +Implementation of pure virtual function. +@see MWTCacheInterface::ReadL() +*/ +void CDynamicDirCache::ReadL(TInt64 aPos, TInt aLength, TDes8& aDes) + { +#ifdef _DEBUG + if(iCacheDisabled) + { + // cache is disabled for debug purposes + __PRINT(_L("CDynamicDirCache disabled")); + User::LeaveIfError(iDrive.ReadNonCritical(aPos, aLength, aDes)); + return; + } +#endif //_DEBUG + + aDes.Zero(); + const TUint32 PageSz = iPageSizeInBytes;//-- cache page size + + TInt64 pageStartMedPos = CalcPageStartPos(aPos); + const TUint32 bytesToPageEnd = (TUint32)(pageStartMedPos + PageSz - aPos); //-- number of bytes from aPos to the end of the page + +// __PRINT5(_L("CDynamicDirCache::ReadL: aPos=%lx, aLength=%x, page:%lx, pageSz:%x, bytesToPageEnd=%x"), aPos, aLength, pageStartMedPos, PageSz, bytesToPageEnd); + // if all data needed is on a single page + if((TUint32)aLength <= bytesToPageEnd) + { + ReadDataFromSinglePageL(aPos, aLength, aDes); + } + // or data to be read cross cache page boundary or probably we have more than 1 page to read + else + { + __PRINT(_L("CDynamicDirCache::ReadL() CROSS PAGE!")); + TUint32 dataLen(aLength); //-- current data length + TInt64 currMediaPos(aPos); //-- current media position + + //-- 1. read data that are already in the current page + ReadDataFromSinglePageL(currMediaPos, bytesToPageEnd, aDes); + dataLen -= bytesToPageEnd; + currMediaPos += bytesToPageEnd; + + TPtr8 dataNext = aDes.MidTPtr(aDes.Length()); + + //-- 2. read whole pages of data + while (dataLen >= PageSz) + { + //-- find out if currMediaPos is in cache. If not, find a spare page and read data there + ReadDataFromSinglePageL(currMediaPos, PageSz, dataNext); + currMediaPos += PageSz; + dataLen -= PageSz; + dataNext = dataNext.MidTPtr(dataNext.Length()); + } + + //-- 3. read the rest of the data + if(dataLen > 0) + { + ReadDataFromSinglePageL(currMediaPos, dataLen, dataNext); + } + } //else((TUint32)aLength <= bytesToPageEnd) + } + +/** +Write data through a single page. If the page is not found or not valid anymore, read media onto iActive page +first, then write data through iActive page. +@param aPos the starting position of the media address to be write. +@param aData the starting address that the writing content lives in the ram. +@param aDataLen the length of the content to be written. +@pre aDataLen should be no more than page size. +*/ +void CDynamicDirCache::WriteDataOntoSinglePageL(TInt64 aPos, const TUint8* aData, TUint32 aDataLen) + { + ASSERT(aDataLen <= iPageSizeInBytes); + //-- the data section is in the cache page entirely, take data directly from the cache + TDynamicDirCachePage* pPage = FindPageByPos(aPos); + if (pPage) + { + // lock page before writing, + if (LockPage(pPage) != NULL) + { + //-- update cache + Mem::Copy(pPage->PtrInPage(aPos), aData, aDataLen); + } + else + { + ASSERT(pPage->PageType() == TDynamicDirCachePage::EUnlocked); + DeQueue(pPage); + LookupTblRemove(pPage->StartPos()); + DecommitPage(pPage); + delete pPage; + pPage = NULL; + } + } + + // if page not found or page data not valid anymore, use active page to read data in + if (!pPage) + { + pPage = UpdateActivePageL(aPos); + //-- update cache + Mem::Copy(pPage->PtrInPage(aPos), aData, aDataLen); + } + + // make sure the page is unlocked after use + if (pPage->PageType() == TDynamicDirCachePage::EUnlocked) + { + UnlockPage(pPage); + } + + // always make writting events MRU + MakePageMRU(aPos); + return; + } + +/** +Implementation of pure virtual function. +@see MWTCacheInterface::WriteL() +*/ +void CDynamicDirCache::WriteL(TInt64 aPos,const TDesC8& aDes) + { +#ifdef _DEBUG + if(iCacheDisabled) + { + // cache is disabled for debug purposes + __PRINT(_L("CDynamicDirCache disabled")); + User::LeaveIfError(iDrive.WriteCritical(aPos,aDes)); + return; + } +#endif //_DEBUG + + TUint32 dataLen = aDes.Size(); + const TUint8* pData = aDes.Ptr(); + const TUint32 PageSz = iPageSizeInBytes; //-- cache page size + + TInt64 pageStartMedPos = CalcPageStartPos(aPos); + TUint32 bytesToPageEnd = (TUint32)(pageStartMedPos + PageSz - aPos); + +// __PRINT5(_L("CDynamicDirCache::WriteL: aPos=%lx, aLength=%x, page:%lx, pageSz:%x, bytesToPageEnd=%x"), aPos, dataLen, pageStartMedPos, PageSz, bytesToPageEnd); + + if(dataLen <= bytesToPageEnd) + { + WriteDataOntoSinglePageL(aPos, pData, dataLen); + } + else + { + __PRINT(_L("CDynamicDirCache::WriteL() CROSS PAGE!")); + + //-- Data to be written cross cache page boundary or probably we have more than 1 page to write + TInt64 currMediaPos(aPos); + + //-- 1. update the current page + WriteDataOntoSinglePageL(currMediaPos, pData, bytesToPageEnd); + + pData += bytesToPageEnd; + currMediaPos += bytesToPageEnd; + dataLen -= bytesToPageEnd; + + //-- 2. write whole pages of data to the cache + while (dataLen >= PageSz) + { + WriteDataOntoSinglePageL(currMediaPos, pData, PageSz); + + pData += PageSz; + currMediaPos += PageSz; + dataLen -= PageSz; + } + + //-- 3. write the rest of the data + if(dataLen > 0) + { + WriteDataOntoSinglePageL(currMediaPos, pData, dataLen); + } + }// else(dataLen <= bytesToPageEnd) + + + //-- write data to the media + const TInt nErr = iDrive.WriteCritical(aPos,aDes); + if(nErr != KErrNone) + {//-- some serious problem occured during writing, invalidate cache. + InvalidateCache(); + User::Leave(nErr); + } + } + +/** +Implementation of pure virtual function. +@see MWTCacheInterface::InvalidateCache() +*/ +void CDynamicDirCache::InvalidateCache(void) + { + __PRINT2(_L("CDynamicDirCache::InvalidateCache(locked=%d, unlocked=%d)"), iLockedQCount, iUnlockedQCount); + // we should never decommit locked pages as they needs to be reserved anyway + // the overhead of unnecessary page committing operations + while(!iLockedQ.IsEmpty()) + { + TDynamicDirCachePage* page = iLockedQ.Last(); + DeQueue(page); // remove from queue + LookupTblRemove(page->StartPos()); // remove from lookuptable + delete page; + } + ASSERT(iLockedQCount == 0); + + // however we should decommit unlocked pages here + while (!iUnlockedQ.IsEmpty()) + { + TDynamicDirCachePage* page = iUnlockedQ.Last(); + DeQueue(page); // remove from queue + LookupTblRemove(page->StartPos()); // remove from lookuptable + DecommitPage(page); // inform cache client to decommit page memory + delete page; + } + ASSERT(iUnlockedQCount == 0); + + ASSERT(iLookupTable.Count() == 0); + iLookupTable.Close(); + + ASSERT(iCacheMemoryClient); + + // initialize cache state. + // Note that once the client is reset, all pages lose connection with the client + // including the active page. So we will need to reset and re-allocate active page + // properly. + if (iCacheMemoryClient) + iCacheMemoryClient->Reset(); + + // reset and re-allocate active page + ResetPagePos(iActivePage); // reset start media position (0), invalidate page content + TUint8* startRamAddr = iCacheMemoryClient->AllocateAndLockSegments(PageSizeInSegs()); + // this should always succeed as the client has just been reset and there are always reserved pages + ASSERT(startRamAddr); + iActivePage->SetStartPtr(startRamAddr); // set RAM address + } + + +/** this method isn't implemented*/ +void CDynamicDirCache::InvalidateCachePage(TUint64 /*aPos*/) + { + ASSERT(0); + } + + +/** +Implementation of pure virtual function. +@see MWTCacheInterface::PosCached() +*/ +TUint32 CDynamicDirCache::PosCached(const TInt64& aPos, TInt64& aCachedPosStart) + { + const TInt64 pageStartMedPos = CalcPageStartPos(aPos); + + // only search the page in lookup table + // NOTE: we don't count the active page into acount here, + // this is to avoid pulling next pages recursively + TDynamicDirCachePage* pPage = LookupTblFind(pageStartMedPos); + + // then check if page is still valid if page is on Unlocked Page Queue + if (pPage && pPage->PageType() == TDynamicDirCachePage::EUnlocked) + { + if (LockPage(pPage) != NULL) + { +// __PRINT1(_L("CDynamicDirCache::PosCached: page(0x%lx) found on Unlocked Queue!"), aPos); + // have to unlock it before returning, otherwise there will be memory leak + UnlockPage(pPage); + aCachedPosStart = pPage->StartPos(); + return pPage->PageSizeInBytes(); + } + else // if the unlocked page is not valid anymore, remove it + { + DeQueue(pPage); + LookupTblRemove(pPage->StartPos()); + DecommitPage(pPage); + delete pPage; + pPage = NULL; + } + } + // otherwise if page is already locked or valid active page + else if (pPage) + { + __PRINT1(_L("CDynamicDirCache::PosCached: page(0x%lx) on Locked Queue!"), aPos); + aCachedPosStart = pPage->StartPos(); + return pPage->PageSizeInBytes(); + } + + // page is not found or not valid anymore + return 0; + } + +/** +Implementation of pure virtual function. +@see MWTCacheInterface::CacheSizeInBytes() +*/ +TUint32 CDynamicDirCache::CacheSizeInBytes() const + { + return iMaxCacheSizeInBytes; + } + +/** +Implementation of pure virtual function. +@see MWTCacheInterface::Control() +*/ +TInt CDynamicDirCache::Control(TUint32 aFunction, TUint32 aParam1, TAny* aParam2) + { + TInt r = KErrNotSupported; +#ifdef _DEBUG + (void)aParam2; + switch(aFunction) + { + // disable / enable cache, for debug + // if aParam1 != 0 cache will be disabled, enabled otherwise + case EDisableCache: + iCacheDisabled = aParam1 ? 1 : 0; + r = KErrNone; + break; + + // dump cache, for debug + case EDumpCache: + { + RFs fs; + fs.Connect(); + const TUint32 debugRegister = DebugRegister(); + fs.SetDebugRegister(debugRegister|KFSYS); + Dump(); + fs.SetDebugRegister(debugRegister); + fs.Close(); + break; + } + case ECacheInfo: + { + RFs fs; + fs.Connect(); + const TUint32 debugRegister = DebugRegister(); + fs.SetDebugRegister(debugRegister|KFSYS); + Info(); + fs.SetDebugRegister(debugRegister); + fs.Close(); + break; + } + + default: + __PRINT1(_L("CDynamicDirCache::Control() invalid function: %d"), aFunction); + ASSERT(0); + break; + } + +#else + (void)aFunction; //-- supress warnings + (void)aParam1; + (void)aParam2; + User::Invariant(); //-- don't call this method in release build +#endif //_DEBUG + + return r; + } + +/** +Implementation of pure virtual function. +@see MWTCacheInterface::SetCacheBasePos() +*/ +void CDynamicDirCache::SetCacheBasePos(TInt64 aBasePos) + { + iCacheBasePos = aBasePos; + } + +/** +Implementation of pure virtual function. +@see MWTCacheInterface::SetCacheBasePos() +*/ +TUint32 CDynamicDirCache::PageSizeInBytesLog2() const + { + return iPageSizeLog2; + } + +/** +Implementation of pure virtual function. +@see MWTCacheInterface::MakePageMRU() +*/ +void CDynamicDirCache::MakePageMRU(TInt64 aPos) + { + __PRINT1(_L("MakePageMRU (%lx)"), aPos); +// __PRINT4(_L("Current Cache State: iLockedQCount=%d, iUnlockedQCount=%d, iLookupTbl=%d, iMaxSizeInPages=%d"), iLockedQCount, iUnlockedQCount, iLookupTable.Count(), iMaxSizeInPages); + // check the MRU page first, if it is already the MRU page, we can return immediately + TInt64 pageStartMedPos = CalcPageStartPos(aPos); + if (!iLockedQ.IsEmpty()) + { + if (iLockedQ.First()->StartPos() == pageStartMedPos) + { + return; + } + } + + TDynamicDirCachePage* pPage = FindPageByPos(aPos); + if (pPage) + { + ASSERT(pPage->IsValid()); + // lock page before make it MRU + if (pPage->PageType() == TDynamicDirCachePage::EUnlocked) + { + ASSERT(!pPage->IsLocked()); + if (LockPage(pPage) == NULL) + { + DeQueue(pPage); + LookupTblRemove(pPage->StartPos()); + DecommitPage(pPage); + delete pPage; + pPage = NULL; + } + } + else + { + // error checking: page should either be locked or active + ASSERT(LockPage(pPage) != NULL); + } + } + + // if page not found or page data not valid anymore, use active page to read data + if (!pPage) + { + TRAPD(err, pPage = UpdateActivePageL(aPos)); + if (err != KErrNone) + { + // problem occurred reading active page, return immediately. + return; + } + } + + // by now, the page is either locked or active page + ASSERT(pPage && pPage->IsValid() && pPage->IsLocked()); + + switch (pPage->PageType()) + { + // if the page is the active page, we will need to find a new active page for replacement + case TDynamicDirCachePage::EActivePage: + { + TDynamicDirCachePage* newAP = NULL; + // if there is more cache room available, try to create a new page first + if (!CacheIsFull()) + { + // allocate and lock a new page + TRAPD(err, newAP = AllocateAndLockNewPageL(0)); + // if any error ocurrs, return immediately + if (err != KErrNone) + { + // unlock the page that was originally unlocked before leave + if (pPage->PageType() == TDynamicDirCachePage::EUnlocked) + { + UnlockPage(pPage); + } + return; + } + + if (newAP) + { + // replace the active page with the new page + newAP->SetPageType(TDynamicDirCachePage::EActivePage); + iActivePage = newAP; + } + } + + // if cache has grown to its max size, or new page allocation failed + if (!newAP) + { + // try to lock the LRU page on the unlocked page queque first + if (!iUnlockedQ.IsEmpty()) + { + newAP = iUnlockedQ.Last(); + ASSERT(newAP->IsValid()); + if (LockPage(newAP) != NULL) + { + // deque, reset pos, set new type + DeQueue(newAP); + LookupTblRemove(newAP->StartPos()); + ResetPagePos(newAP); + newAP->SetPageType(TDynamicDirCachePage::EActivePage); + // replace active page + iActivePage = newAP; + } + // if falied locking the LRU page from unclocked queque, + // delete it + else + { + DeQueue(newAP); + LookupTblRemove(newAP->StartPos()); + DecommitPage(newAP); + delete newAP; + newAP = NULL; + } + } + } + + // if still have not found new active page + // grab the LRU page from Locked Page Queue for active page + if (!newAP) + { + ASSERT(!iLockedQ.IsEmpty()); + newAP = iLockedQ.Last(); + // deque, reset pos, set new type + DeQueue(newAP); + LookupTblRemove(newAP->StartPos()); + ResetPagePos(newAP); + newAP->SetPageType(TDynamicDirCachePage::EActivePage); + // replace active page + iActivePage = newAP; + } + + // we should always be able to find a locked page for active page + ASSERT(newAP != NULL); + + // make original page (i.e. former active page) MRU + // add onto locked queue + AddFirstOntoQueue(pPage, TDynamicDirCachePage::ELocked); + // add onto lookuptbl, as active page is not on lookup tbl originally + LookupTblAdd(pPage); + // check cache limit + CheckThresholds(); + return; + } + case TDynamicDirCachePage::EUnlocked: + { + // if page was originally on Unlocked Page Queque, remove it from Unlocked Page Queue, add it + // to the Locked Page Queue and make it MRU + DeQueue(pPage); + AddFirstOntoQueue(pPage, TDynamicDirCachePage::ELocked); + // check cache limit + CheckThresholds(); + return; + } + case TDynamicDirCachePage::ELocked: + { + // otherwise the page was on Locked Page Queue, make it MRU + // no need to check cache limit + if (pPage != iLockedQ.First()) + { + DeQueue(pPage); + AddFirstOntoQueue(pPage, TDynamicDirCachePage::ELocked); + return; + } + break; + } + default: + ASSERT(0); + } + } + +//==================================================================== +/** +Internal query function, to check if aPos is cached or not. iActive page is included in searching. +*/ +TDynamicDirCachePage* CDynamicDirCache::FindPageByPos(TInt64 aPos) + { + __PRINT1(_L("CDynamicDirCache::FindPageByPos(aPos=%lx)"), aPos); + // align the page position + TInt64 pageStartMedPos = CalcPageStartPos(aPos); + + if ((iActivePage->StartPos() == pageStartMedPos)) + { + ASSERT(iActivePage->IsValid()); + return iActivePage; + } + + // search in lookup table + return LookupTblFind(pageStartMedPos); + } + +/** +read a page length data into iActive page and return iActive page if read is successful. +*/ +TDynamicDirCachePage* CDynamicDirCache::UpdateActivePageL(TInt64 aPos) + { + // align the page position + TInt64 pageStartMedPos = CalcPageStartPos(aPos); + + if (iActivePage->StartPos() == pageStartMedPos && iActivePage->IsValid()) + { + return iActivePage; + } + + __PRINT2(_L("CDynamicDirCache::UpdateActivePageL(aPos=%lx, active=%lx)"), aPos, iActivePage->StartPos()); + + // set start med pos value, no other effects, only available to active page + iActivePage->SetPos(pageStartMedPos); + + // read data, make active page valid + TUint8* data = iActivePage->PtrInPage(iActivePage->iStartMedPos); + TPtr8 dataPtr(data, iPageSizeInBytes); + const TInt nErr = iDrive.ReadNonCritical(iActivePage->iStartMedPos, iPageSizeInBytes, dataPtr); + if(nErr !=KErrNone) + { + // some serious problem occured during reading, invalidate cache. + InvalidateCache(); + User::Leave(nErr); + } + iActivePage->SetValid(ETrue); + + return iActivePage; + } + +/** +Check if the number of (locked pages + iActive page) and unlocked pages have exceeded minimum allowed page +number and maximum allowed page number respectively. +*/ +void CDynamicDirCache::CheckThresholds() + { + while (iLockedQCount + 1 > iMinSizeInPages) + { + TDynamicDirCachePage* movePage = iLockedQ.Last(); + UnlockPage(movePage); + DeQueue(movePage); + TInt err = LookupTblRemove(movePage->StartPos()); + ASSERT(err == KErrNone); + + // if it is a valid page, add onto unlocked queue + if (movePage->StartPos() != 0) + { + ASSERT(movePage->IsValid()); + AddFirstOntoQueue(movePage, TDynamicDirCachePage::EUnlocked); + err = LookupTblAdd(movePage); + ASSERT(err == KErrNone); + } + else // reserved page, delete + { + DecommitPage(movePage); + delete movePage; + } + } + + // if unlocked queue exceeds limit, delete LRU page + // note: all pages on unlocked queue should be valid + while (iUnlockedQCount > iMaxSizeInPages - iMinSizeInPages) + { + TDynamicDirCachePage* removePage = iUnlockedQ.Last(); + ASSERT(removePage->StartPos() != 0 && removePage->IsValid()); + DeQueue(removePage); + LookupTblRemove(removePage->StartPos()); + DecommitPage(removePage); + delete removePage; + } + } + +/** +Try to create a new page and lock the page content when it is created. This function should only be called +when creating iActive page or making a page MRU (which might result in page evictions). +@return the pointer of the newly created page, or NULL if allocation failed. +@param aStartMedPos the starting media address of the page to be created. +@pre aStartMedPos should not already be existing in the cache. +*/ +TDynamicDirCachePage* CDynamicDirCache::AllocateAndLockNewPageL(TInt64 aStartMedPos) + { + __PRINT1(_L("CDynamicDirCache::AllocateAndLockNewPageL(aStartMedPos=%lx)"), aStartMedPos); + + TUint8* startRamAddr = iCacheMemoryClient->AllocateAndLockSegments(PageSizeInSegs()); + if (startRamAddr) + { + // create new page and return + TDynamicDirCachePage* pPage = TDynamicDirCachePage::NewL(this, aStartMedPos, startRamAddr); + pPage->SetLocked(ETrue); + pPage->SetValid(EFalse); + return pPage; + } + + return NULL; + } + +#ifdef _DEBUG +/** +Dump cache information, only enabled in debug mode. +@see CDynamicDirCache::Control() +*/ +void CDynamicDirCache::Info() const + { + __PRINT(_L("======== CDynamicDirCache::Info =========")); + const TUint32 SegmentSizeInBytesLog2 = CCacheMemoryManagerFactory::CacheMemoryManager()->SegmentSizeInBytesLog2(); + // page size + __PRINT1(_L("=== Pages size: [%d Bytes]"), iPageSizeInBytes); + __PRINT1(_L("=== Segment size: [%d Bytes]"), 1 << SegmentSizeInBytesLog2); + + // data size: + __PRINT1(_L("=== Min data size: [%d Bytes]"), iMinSizeInPages << iPageSizeLog2); + __PRINT1(_L("=== Max data size: [%d Bytes]"), iMaxSizeInPages << iPageSizeLog2); + + // memory size: + const TUint32 pageMemSizeLog2 = iPageSizeLog2 > SegmentSizeInBytesLog2 ? iPageSizeLog2 : SegmentSizeInBytesLog2; + __PRINT1(_L("=== Min memory size: [%d Bytes]"), iMinSizeInPages << pageMemSizeLog2); + __PRINT1(_L("=== Max memory size: [%d Bytes]"), iMaxSizeInPages << pageMemSizeLog2); + + // reserved pages + __PRINT1(_L("=== Number of pages reserved: [%d]"), iMinSizeInPages); + __PRINT1(_L("=== Reserved memory: [%d Bytes]"), (iMinSizeInPages * PageSizeInSegs()) << SegmentSizeInBytesLog2); + // locked page num + __PRINT1(_L("=== Number of pages locked: [%d]"), iLockedQCount); + __PRINT1(_L("=== Locked memory: [%d Bytes]"), (iLockedQCount * PageSizeInSegs()) << SegmentSizeInBytesLog2); + // unlocked page num + __PRINT1(_L("=== Number of pages unlocked: [%d]"), iUnlockedQCount); + __PRINT1(_L("=== Unlocked memory: [%d Bytes]"), (iUnlockedQCount * PageSizeInSegs()) << SegmentSizeInBytesLog2); + } + +/** +Dump cache content, only enabled in debug mode. +@see CDynamicDirCache::Control() +*/ +void CDynamicDirCache::Dump() + { + __PRINT(_L("======== CDynamicDirCache::Dump =========")); + if (!iLockedQ.IsEmpty()) + { + TDblQueIter q(iLockedQ); + q.SetToFirst(); + TInt i = 0; + while((TDynamicDirCachePage*)q) + { + TDynamicDirCachePage* pP = q++; + __PRINT3(_L("=== CDynamicDirCache::iLockedQ\t[%4d](pos=%lx, size=%d)"), i++, pP->StartPos(), pP->PageSizeInBytes()); + } + } + if (!iUnlockedQ.IsEmpty()) + { + TDblQueIter q(iUnlockedQ); + q.SetToFirst(); + TInt i = 0; + while((TDynamicDirCachePage*)q) + { + TDynamicDirCachePage* pP = q++; + __PRINT3(_L("=== CDynamicDirCache::iUnlockedQ\t[%4d](pos=%lx, size=%u)"), i++, pP->StartPos(), pP->PageSizeInBytes()); + } + } + __PRINT2(_L("=== CDynamicDirCache::iActivePage\t[*](pos=%lx, size=%u)"), iActivePage->StartPos(), iActivePage->PageSizeInBytes()); + + if (iLookupTable.Count()) + { + TInt i = 0; + THashSetIter iter(iLookupTable); + TLookupEntry* pEntry; + pEntry = (TLookupEntry*) iter.Next(); + while(pEntry) + { + TDynamicDirCachePage* pP = pEntry->iPage; + __PRINT3(_L("=== CDynamicDirCache::iLookupTable\t[%4d](pos=%lx, size=%u)"), i++, pP->StartPos(), pP->PageSizeInBytes()); + pEntry = (TLookupEntry*) iter.Next(); + }; + } + __PRINT(_L("===========================================\n")); + } +#endif //_DEBUG + +/** +Lock an unlocked page, or do nothing if the page is already locked. +@return TUint8* pointer of the page to be locked, if locking is successful, otherwise return NULL. +@param aPage the pointer of the page to be locked. +*/ +TUint8* CDynamicDirCache::LockPage(TDynamicDirCachePage* aPage) + { + ASSERT(aPage != NULL); + if (aPage->IsLocked()) + return aPage->StartPtr(); + + TInt r = iCacheMemoryClient->LockSegments(aPage->StartPtr(), PageSizeInSegs()); + if (r == KErrNone) + { + aPage->SetLocked(ETrue); + return aPage->StartPtr(); + } + + return NULL; + } + +/** +Unlock a locked page. +@return TInt KErrNone if unlocking was successful, otherwise system-wide error code. +@param aPage the pointer of the page to be unlocked. +*/ +TInt CDynamicDirCache::UnlockPage(TDynamicDirCachePage* aPage) + { + ASSERT(aPage != NULL); + __PRINT1(_L("CDynamicDirCache::UnlockPage(%lx)"), aPage->StartPos()); + TInt r = iCacheMemoryClient->UnlockSegments(aPage->StartPtr(), PageSizeInSegs()); + if (r == KErrNone) + { + aPage->SetLocked(EFalse); + } + return r; + } + +/** +Decommit a locked or unlocked page. +@return TInt KErrNone if decommition was successful, otherwise system-wide error code. +@param aPage the pointer of the page to be decommitted. +*/ +TInt CDynamicDirCache::DecommitPage(TDynamicDirCachePage* aPage) + { + ASSERT(aPage != NULL); + __PRINT1(_L("CDynamicDirCache::DecommitPage(%lx)"), aPage->StartPos()); + if (aPage) + { + TInt r = iCacheMemoryClient->DecommitSegments(aPage->StartPtr(), PageSizeInSegs()); + if (r == KErrNone) + { + aPage->SetLocked(EFalse); + aPage->SetValid(EFalse); + } + return r; + } + return KErrArgument; + } + +/////////////////////////// aluxiliary functions ////////////////////////////////// +/** +Calculate the page size in segments. Segment size is the size of the kernel memory unit that cache memory manager manages. +We are making assumption here about the page size: page size should always be either less than segment size +or multiple times of segment size +@return TUint32 the page size in segments. +*/ +TUint32 CDynamicDirCache::PageSizeInSegs() const + { + // initialize cache memory manager as all file systems have mounted by now + ASSERT(CCacheMemoryManagerFactory::CacheMemoryManager()); + const TUint32 SegmentSizeInBytesLog2 = CCacheMemoryManagerFactory::CacheMemoryManager()->SegmentSizeInBytesLog2(); + + // Page size should be non-zero + ASSERT(iPageSizeInBytes); + + TUint32 segs = iPageSizeInBytes >> SegmentSizeInBytesLog2; + return segs > 0 ? segs : 1; + } + +/** +Deque the page from locked queue or unlocked queue. All pages are managed through these two queues, expect iActive +page. +@param aPage the pointer of the page to be dequeued +@return TInt KErrArgument if aPage is invalid, otherwise KErrNone. +*/ +TInt CDynamicDirCache::DeQueue(TDynamicDirCachePage* aPage) + { + ASSERT(aPage); + if (!aPage) + return KErrArgument; + + if (aPage->iType == TDynamicDirCachePage::ELocked) + { + aPage->Deque(); + aPage->SetPageType(TDynamicDirCachePage::EUnknown); + --iLockedQCount; + } + else if (aPage->iType == TDynamicDirCachePage::EUnlocked) + { + aPage->Deque(); + aPage->SetPageType(TDynamicDirCachePage::EUnknown); + --iUnlockedQCount; + } + else + { + ASSERT(0); + return KErrArgument; + } + return KErrNone; + } + +/** +Insert a page to the first position of locked queue or unlocked queue. +@param aPage the pointer of the page to be inserted. +@param aType the type of the queue to be inserted. +@return TInt KErrArgument if aPage is invalid, otherwise KErrNone. +*/ +TInt CDynamicDirCache::AddFirstOntoQueue(TDynamicDirCachePage* aPage, TDynamicDirCachePage::TPageType aType) + { + ASSERT(aPage); + if (!aPage) + return KErrArgument; + + // page must be dequed first or it is active page + if (aPage->iType != TDynamicDirCachePage::EActivePage && aPage->iType != TDynamicDirCachePage::EUnknown) + { + ASSERT(0); + return KErrArgument; + } + + if (aType == TDynamicDirCachePage::ELocked) + { + iLockedQ.AddFirst(*aPage); + aPage->SetPageType(TDynamicDirCachePage::ELocked); + ++iLockedQCount; + } + else if (aType == TDynamicDirCachePage::EUnlocked) + { + iUnlockedQ.AddFirst(*aPage); + aPage->SetPageType(TDynamicDirCachePage::EUnlocked); + ++iUnlockedQCount; + } + else + { + ASSERT(0); + return KErrArgument; + } + + return KErrNone; + } + +/** +Remove a page from the lookup table, indexed by the starting media address of the page content. +@param aPagePos the starting media position of the page to be removed. +*/ +TInt CDynamicDirCache::LookupTblRemove(TInt64 aPagePos) + { + if (aPagePos == 0) + { + return KErrNone; + } + + TInt r = iLookupTable.Remove(TLookupEntry(aPagePos, 0, NULL)); + return r; + } + +/** +Insert a page to the lookup table, indexed by the starting media address of the page content. +@param aPagePos the starting media position of the page to be inserted. +*/ +TInt CDynamicDirCache::LookupTblAdd(TDynamicDirCachePage* aPage) + { + ASSERT(aPage); + if (!aPage) + return KErrArgument; + + if (aPage->StartPos() == 0) + { + return KErrNone; + } + + TInt r = iLookupTable.Insert(TLookupEntry(aPage->StartPos(), iPageSizeInBytes, aPage)); + return r; + } + +/** +Reset the media address of the page to 0, also invalidate the page. +@param aPage the pointer of the page to be reset. +*/ +TInt CDynamicDirCache::ResetPagePos(TDynamicDirCachePage* aPage) + { + ASSERT(aPage); + if (!aPage) + return KErrArgument; + + aPage->ResetPos(); + return KErrNone; + } + +/** +Search the lookup table to find the page start with a specific media address. +@param aPos the starting media address to be searched. +*/ +TDynamicDirCachePage* CDynamicDirCache::LookupTblFind(TInt64 aPos) + { + if (aPos == 0) + { + ASSERT(0); + return NULL; + } + + TLookupEntry* entry = iLookupTable.Find(TLookupEntry(aPos, 0, NULL)); + if(entry) + { + ASSERT(entry->iPage->IsValid()); + return entry->iPage; + } + + return NULL; + }