diff -r 5af6c74cd793 -r af6ec97d9189 userlibandfileserver/fileserver/sfat32/sl_dir_cache.cpp --- a/userlibandfileserver/fileserver/sfat32/sl_dir_cache.cpp Wed Jun 23 11:59:44 2010 +0100 +++ b/userlibandfileserver/fileserver/sfat32/sl_dir_cache.cpp Wed Jun 23 12:52:28 2010 +0100 @@ -27,9 +27,9 @@ 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) +TDynamicDirCachePage* TDynamicDirCachePage::CreateCachePage(CDynamicDirCache* aOwnerCache, TInt64 aStartMedPos, TUint8* aStartRamAddr) { - return new(ELeave) TDynamicDirCachePage(aOwnerCache, aStartMedPos, aStartRamAddr); + return new TDynamicDirCachePage(aOwnerCache, aStartMedPos, aStartRamAddr); } /** @@ -113,6 +113,7 @@ iMinSizeInPages(aMinPageNum), iMaxSizeInPages(aMaxPageNum), iDrive(aDrive), + iLastVisitedPage(NULL), iLockedQ(_FOFF(TDynamicDirCachePage, iLink)), iUnlockedQ(_FOFF(TDynamicDirCachePage, iLink)), iLockedQCount(0), @@ -159,15 +160,18 @@ // allocate as many permanently locked pages as there are threads - plus one // otherwise DoMakePageMRU() won't work properly with only one thread //-- At present moment the size of TDrive thread pool is 1 (1 drive thread in a pool) - const TUint KThreadCount = 1; - iPermanentlyAllocatedPageCount = KThreadCount + 1; + const TUint KThreadCount = 1; + iPermanentlyAllocatedPageCount = KThreadCount; if (iPermanentlyAllocatedPageCount > iMinSizeInPages) iMinSizeInPages = iPermanentlyAllocatedPageCount; for (TUint n=0; niType == 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. The data will be _Appended_ the the descriptor aDes. The caller is responsible for maintaining this descriptor. @@ -245,13 +213,19 @@ aDes.Append(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); + // if page is from unlocked queue, try to keep it locked until we move to a + // different page from the unlocked queue + // this is to avoid excessive locking and unlocking operations that is + // highly likely to happen when DoFindL() linearly scan through the directory + if (pPage->PageType() == TDynamicDirCachePage::EUnlocked + && iLastVisitedPage != pPage) + { + // Note: iLastVisitedPage may have been moved from unlocked queue to locked queue + if(iLastVisitedPage && iLastVisitedPage->PageType() == TDynamicDirCachePage::EUnlocked) + { + User::LeaveIfError(UnlockPage(iLastVisitedPage)); + } + iLastVisitedPage = pPage; } } else // page locking failed @@ -621,23 +595,32 @@ case EDumpCache: { RFs fs; - fs.Connect(); + r = fs.Connect(); + if(r != KErrNone) + break; + const TUint32 debugRegister = DebugRegister(); fs.SetDebugRegister(debugRegister|KFSYS); Dump(); fs.SetDebugRegister(debugRegister); fs.Close(); + r = KErrNone; break; } case ECacheInfo: { RFs fs; - fs.Connect(); + r = fs.Connect(); + if(r != KErrNone) + break; + const TUint32 debugRegister = DebugRegister(); fs.SetDebugRegister(debugRegister|KFSYS); - Info(); + TDirCacheInfo* aInfo = static_cast(aParam2); + Info(aInfo); fs.SetDebugRegister(debugRegister); fs.Close(); + r = KErrNone; break; } @@ -680,60 +663,60 @@ { // __PRINT1(_L("MakePageMRU (%lx)"), aPos); // __PRINT4(_L("Current Cache State: iLockedQCount=%d, iUnlockedQCount=%d, iLookupTbl=%d, iMaxSizeInPages=%d"), iLockedQCount, iUnlockedQCount, iLookupTable.Count(), iMaxSizeInPages); - // check there are at least two locked pages - ASSERT(iLockedQCount > 1); + // check there are at least one locked pages + ASSERT(iLockedQCount > 0); // 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) + if (iLockedQCount > 1 && iLockedQ.First()->StartPos() == pageStartMedPos) { return; } } TDynamicDirCachePage* pPage = FindPageByPos(aPos); - if (pPage) - { - ASSERT(pPage->IsValid()); + 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 (pPage->PageType() == TDynamicDirCachePage::EUnlocked) + { + ASSERT(!pPage->IsLocked() || (pPage->IsLocked() && pPage == iLastVisitedPage)); + 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; - } - } + // 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 + // by now, the page is either locked or active page ASSERT(pPage && pPage->IsValid() && pPage->IsLocked()); - TBool allocateNewPage = pPage == iLockedQ.Last() && !CacheIsFull(); + TBool makeNewPageMRU = pPage == iLockedQ.Last(); switch (pPage->PageType()) @@ -762,24 +745,34 @@ ASSERT(0); } - if (allocateNewPage) - { - TDynamicDirCachePage* nPage = NULL; - TRAPD(err, nPage = AllocateAndLockNewPageL(0)); - if (err == KErrNone) - { + if (!makeNewPageMRU) + return; + + // when cache is full and a new MRU page is about to be added, we will need to evict the LRU page + // accordingly + if (CacheIsFull()) + { + TUint32& queueCnt = iMaxSizeInPages - iMinSizeInPages > 0 ? iUnlockedQCount : iLockedQCount; + queueCnt++; + CheckThresholds(); + queueCnt--; + } - // about to add a page to end of locked queue, so lie about iLockedQCount - iLockedQCount++; - CheckThresholds(); - iLockedQCount--; + // attempt to grow the cache by appending a clean, new page at the end of the locked page queue. + // This can fail when out of memory; the LRU mechanism then makes sure the oldest page will be re-used. + TDynamicDirCachePage* nPage = AllocateAndLockNewPage(); + if (!nPage) + return; - iLockedQ.AddLast(*nPage); - nPage->SetPageType(TDynamicDirCachePage::ELocked); - ++iLockedQCount; - LookupTblAdd(nPage); - } - } + // about to add the new active page, force the locked queue to evict the existing last page to make room + // for the new active page + iLockedQCount++; + CheckThresholds(); + iLockedQCount--; + + iLockedQ.AddLast(*nPage); + nPage->SetPageType(TDynamicDirCachePage::ELocked); + ++iLockedQCount; } /** @@ -855,7 +848,7 @@ */ void CDynamicDirCache::CheckThresholds() { - while (iLockedQCount + 1 > iMinSizeInPages) + while (iLockedQCount > iMinSizeInPages) { TDynamicDirCachePage* movePage = iLockedQ.Last(); UnlockPage(movePage); @@ -895,58 +888,67 @@ 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); +TDynamicDirCachePage* CDynamicDirCache::AllocateAndLockNewPage(/*TInt64 aStartMedPos*/) + { + __PRINT(_L("CDynamicDirCache::AllocateAndLockNewPage()")); + + TUint8* startRamAddr = iCacheMemoryClient->AllocateAndLockSegments(PageSizeInSegs()); + + if (!startRamAddr) + return NULL; + + TDynamicDirCachePage* pPage = TDynamicDirCachePage::CreateCachePage(this, 0, startRamAddr); - 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; - } + // Failure would mean the cache chunk was able to grow but we've run out of heap. + // This seems extremely unlikely, but decommit the now-unmanageable cache segment just in case. + if (!pPage) + { + iCacheMemoryClient->DecommitSegments(startRamAddr, PageSizeInSegs()); + return NULL; + } - return NULL; + pPage->SetLocked(ETrue); + pPage->SetValid(EFalse); + return pPage; } -#ifdef _DEBUG +#if defined(_DEBUG) || defined(_DEBUG_RELEASE) /** 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); +void CDynamicDirCache::Info(TDirCacheInfo* aInfo) const + { + __PRINT(_L("======== CDynamicDirCache::Info =========")); + const TUint32 SegmentSizeInBytesLog2 = CCacheMemoryManagerFactory::CacheMemoryManager()->SegmentSizeInBytesLog2(); + const TUint32 pageMemSizeLog2 = iPageSizeLog2 > SegmentSizeInBytesLog2 ? iPageSizeLog2 : SegmentSizeInBytesLog2; + // page size + __PRINT1(_L("=== Segment size: [%d Bytes]"), 1 << SegmentSizeInBytesLog2); + __PRINT1(_L("=== Page data size: [%d Bytes]"), iPageSizeInBytes); + __PRINT1(_L("=== Page mem size: [%d Bytes]"), 1 << pageMemSizeLog2); - // data size: - __PRINT1(_L("=== Min data size: [%d Bytes]"), iMinSizeInPages << iPageSizeLog2); - __PRINT1(_L("=== Max data size: [%d Bytes]"), iMaxSizeInPages << iPageSizeLog2); + // cache size in pages + __PRINT1(_L("=== Min cache size in pages: [%d]"), iMinSizeInPages); + __PRINT1(_L("=== Max cache size in pages: [%d]"), iMaxSizeInPages); - // 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); - } + // locked page num + __PRINT1(_L("=== Number of pages locked: [%d]"), iLockedQCount); + // unlocked page num + __PRINT1(_L("=== Number of pages unlocked: [%d]"), iUnlockedQCount); + __PRINT(_L("=========================================\n")); + + ASSERT(aInfo); + aInfo->iMemorySegmentSize = 1 << SegmentSizeInBytesLog2; + aInfo->iPageSizeInMemory = PageSizeInSegs() << SegmentSizeInBytesLog2; + aInfo->iPageSizeInData = iPageSizeInBytes; + aInfo->iMinCacheSizeInPages = iMinSizeInPages; + aInfo->iMaxCacheSizeInPages = iMaxSizeInPages; + aInfo->iMinCacheSizeInMemory = iMinSizeInPages * aInfo->iPageSizeInMemory; + aInfo->iMaxCacheSizeInMemory = iMaxSizeInPages * aInfo->iPageSizeInMemory; + aInfo->iLockedPageNumber = iLockedQCount; + aInfo->iUnlockedPageNumber = iUnlockedQCount; + } /** Dump cache content, only enabled in debug mode. @@ -996,7 +998,7 @@ } __PRINT(_L("===========================================\n")); } -#endif //_DEBUG +#endif //#if defined(_DEBUG) || defined(_DEBUG_RELEASE) /** Lock an unlocked page, or do nothing if the page is already locked. @@ -1028,14 +1030,17 @@ { ASSERT(aPage != NULL); __PRINT1(_L("CDynamicDirCache::UnlockPage(%lx)"), aPage->StartPos()); - TInt r = iCacheMemoryClient->UnlockSegments(aPage->StartPtr(), PageSizeInSegs()); - if (r == KErrNone) - { - aPage->SetLocked(EFalse); + if (aPage) + { + TInt r = iCacheMemoryClient->UnlockSegments(aPage->StartPtr(), PageSizeInSegs()); + if (r == KErrNone) + { + aPage->SetLocked(EFalse); + } + return r; } - return r; - } - + return KErrArgument; + } /** Decommit a locked or unlocked page. @return TInt KErrNone if decommition was successful, otherwise system-wide error code.