// Copyright (c) 1996-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\sfat32\sl_fatcache32.cpp
//
//
#include "sl_std.h"
#include "sl_fatcache32.h"
/**
@file
Various FAT32 caches implementation
*/
//#################################################################################################################################
//# CFat32LruCache implementation
//#################################################################################################################################
//-----------------------------------------------------------------------------
CFat32LruCache::CFat32LruCache()
:CFatPagedCacheBase(), iPageList(_FOFF(CFat32LruCachePage, iLink))
{
}
//-----------------------------------------------------------------------------
/**
FAT32 LRU cache factory function.
@param aOwner pointer to the owning FAT mount
@param aMaxMemSize maximal size of the memory the cache can use, bytes
@param aRdGranularityLog2 Log2(read granularity)
@param aWrGranularityLog2 Log2(write granularity)
@return pointer to the constructed object.
*/
CFat32LruCache* CFat32LruCache::NewL(CFatMountCB* aOwner, TUint32 aMaxMemSize, TUint32 aRdGranularityLog2, TUint32 aWrGranularityLog2)
{
__PRINT(_L("#-CFat32LruCache::NewL()"));
CFat32LruCache* pSelf = NULL;
pSelf = new (ELeave) CFat32LruCache;
CleanupStack::PushL(pSelf);
pSelf->InitialiseL(aOwner, aMaxMemSize, aRdGranularityLog2, aWrGranularityLog2);
CleanupStack::Pop();
return pSelf;
}
//-----------------------------------------------------------------------------
/**
@return pointer to the CFatBitCache interface.
*/
CFatBitCache* CFat32LruCache::BitCacheInterface()
{
return iBitCache;
}
//-----------------------------------------------------------------------------
/**
FAT32 LRU cache initialisation.
@param aOwner pointer to the owning FAT mount
@param aMaxMemSize maximal size of the memory the cache can use, bytes
@param aRdGranularityLog2 Log2(read granularity)
@param aWrGranularityLog2 Log2(write granularity)
@return pointer to the constructed object.
*/
void CFat32LruCache::InitialiseL(CFatMountCB* aOwner, TUint32 aMaxMemSize, TUint32 aRdGranularityLog2, TUint32 aWrGranularityLog2)
{
const TUint32 KReadGranularity = Pow2(aRdGranularityLog2);
const TUint32 KWriteGranularity = Pow2(aWrGranularityLog2);
__PRINT3(_L("#-CFat32LruCache::InitialiseL MaxMem:%u, RdGr:%d, WrGr:%d"),aMaxMemSize, KReadGranularity, KWriteGranularity);
(void)KReadGranularity;
(void)KWriteGranularity;
const TBool bParamsValid = (aRdGranularityLog2 >= aWrGranularityLog2) && (aWrGranularityLog2 >= KDefSectorSzLog2) && (aMaxMemSize > KReadGranularity);
__ASSERT_ALWAYS(bParamsValid, Fault(EFatCache_BadGranularity));
CFatPagedCacheBase::InitialiseL(aOwner);
ASSERT(FatType() == EFat32);
//-- according to the FAT32 specs, FAT32 min size is 65526 entries or 262104 bytes.
//-- It's possible to incorrectly format a small volume to FAT32, it shall be accessible read-only.
if(aMaxMemSize > FatSize())
{//-- strange situation, memory allocated for LRU cache is enough to cache whole FAT32
__PRINT(_L("#-CFat32LruCache::InitialiseL warning: LRU cache becomes fixed! (too much memory allowed)"));
aMaxMemSize = FatSize();
}
//-- LRU cache page size is (2^aRdGranularityLog2) bytes and consists of 2^(aRdGranularityLog2-aWrGranularity) sectors.
iPageSizeLog2 = aRdGranularityLog2;
iSectorSizeLog2 = aWrGranularityLog2; //-- Log2(number of sectors in cache page)
iMaxPages = aMaxMemSize / PageSize(); //-- maximal number of cache pages we can allocate
iNumPagesAllocated = 0;
__ASSERT_ALWAYS((iMaxPages > 1 && SectorsInPage() < KMaxSectorsInPage), Fault(EFatCache_BadGranularity));
//-- obtain maximal number of entries in the table
if(aOwner->UsableClusters() < 1)
{
ASSERT(0);
User::Leave(KErrCorrupt);
}
iMaxFatEntries = aOwner->UsableClusters()+KFatFirstSearchCluster; //-- FAT[0] & FAT[1] are not in use
//-- create FAT bit supercache if it is enabled in config
ASSERT(!iBitCache);
if(aOwner->FatConfig().FAT32_UseBitSupercache())
{
iBitCache = CFatBitCache::New(*this);
}
else
{
__PRINT(_L("#++ !! Fat Bit Supercache is disabled in config !!"));
}
}
//-----------------------------------------------------------------------------
/**
Close the cache and deallocate its memory.
@param aDiscardDirtyData if ETrue, will ignore dirty data. If EFalse, will panic on atempt to close dirty cache.
*/
void CFat32LruCache::Close(TBool aDiscardDirtyData)
{
__PRINT1(_L("#-CFat32LruCache::Close(%d)"), aDiscardDirtyData);
//-- delete FAT bit supercache if present
delete iBitCache;
iBitCache=NULL;
//-- delete existing cache pages
TPageIterator itr(iPageList);
for(;;)
{
CFat32LruCachePage* pPage = itr++;
if(!pPage)
break;
pPage->iLink.Deque(); //-- remove page from the list
if(pPage->IsDirty())
{//-- trying to destroy the cache that has dirty pages
__PRINT1(_L("#-CFat32LruCache::Close() The page is dirty! Start idx:%d"), pPage->StartFatIndex());
if(!aDiscardDirtyData)
{
Fault(EFatCache_DiscardingDirtyData);
}
//-- ignore this fact if requested.
}
delete pPage;
--iNumPagesAllocated;
}
SetDirty(EFalse);
ASSERT(!iNumPagesAllocated);
}
//-----------------------------------------------------------------------------
/**
Tries to read FAT entry from the cache. If the entry at aFatIndex is not cached, does nothing and returns EFalse.
If finds the cache page that contains entry at index "aFatIndex", reads it and returns ETrue.
@param aFatIndex FAT entry index within FAT table
@param aFatEntry on success it will contain FAT entry value
@return ETrue if the entry has been read
EFalse if index aFatIndex isn't cached
*/
TBool CFat32LruCache::ReadCachedEntryL(TUint32 aFatIndex, TFat32Entry& aResult)
{
//-- iterate through LRU list looking if the entry is cached.
TPageIterator itr(iPageList);
for(;;)
{
CFat32LruCachePage* pPage = itr++;
if(!pPage)
break;
if(pPage->ReadCachedEntryL(aFatIndex, aResult))
{//-- found entry in some cache page. Make this page LRU
if(!iPageList.IsFirst(pPage))
{
pPage->iLink.Deque();
iPageList.AddFirst(*pPage);
}
return ETrue;
}
}
return EFalse; //-- the entry is not cached
}
//-----------------------------------------------------------------------------
/**
Tries to write FAT entry to the cache. If the entry at aFatIndex is not cached, does nothing and returns EFalse.
If finds the cache page that contains entry at index "aFatIndex", overwrites it and returns ETrue
@param aFatIndex FAT entry index within FAT table
@param aFatEntry new FAT entry value
@return ETrue if the entry has been overwritten
EFalse if index aFatIndex isn't cached
*/
TBool CFat32LruCache::WriteCachedEntryL(TUint32 aFatIndex, TFat32Entry aFatEntry)
{
//-- iterate through LRU list looking if the entry is cached.
TPageIterator itr(iPageList);
for(;;)
{
CFat32LruCachePage* pPage = itr++;
if(!pPage)
break;
if(pPage->WriteCachedEntryL(aFatIndex, aFatEntry))
{//-- the entry was cached and modified now. Make this page LRU
if(!iPageList.IsFirst(pPage))
{
pPage->iLink.Deque();
iPageList.AddFirst(*pPage);
}
return ETrue;
}
}
return EFalse; //-- the entry is not cached
}
//-----------------------------------------------------------------------------
/**
Get a spare page. This function can either allocate a page if memory limit isn't reached yet
or find the least recently used (in the end of the LRU list) and evict it.
@return pointer to the cache page to use, it will be insertet to the beginning of the LRU list
*/
CFat32LruCachePage* CFat32LruCache::DoGetSpareCachePageL()
{
CFat32LruCachePage* pPage=NULL;
if(iNumPagesAllocated < iMaxPages)
{//-- we still can allocate a page
pPage = CFat32LruCachePage::NewL(*this);
++iNumPagesAllocated;
iPageList.AddFirst(*pPage); //-- insert the page into the beginning of LRU list
return pPage;
}
//-- all pages are already allocated, evict the last recently used and remove it from the list
pPage = iPageList.Last(); //-- least recently used page, last in the list
pPage->iLink.Deque(); //-- remove it from the LRU list
iPageList.AddFirst(*pPage); //-- insert the page into the beginning of LRU list
//__PRINT1(_L("#-CFat32LruCache::DoGetSpareCachePageL() page @FAT idx:%d evicted"), pPage->StartFatIndex());
//-- flush the page, writing its data to all copies of FAT, to FAT1, then to FAT2 etc.
ASSERT(NumFATs() >0);
if(pPage->IsDirty())
{
//-- write page data to all copies of FAT
for(iCurrentFatNo=0; iCurrentFatNo < NumFATs(); ++iCurrentFatNo)
{
const TBool keepDirty = iCurrentFatNo < (NumFATs()-1);
pPage->FlushL(keepDirty);
}
iCurrentFatNo = KInvalidFatNo;
}
return pPage;
}
//-----------------------------------------------------------------------------
/**
Read FAT entry from the cache.
@param aIndex FAT entry index to read
@return FAT entry value at the index "aIndex"
*/
TUint32 CFat32LruCache::ReadEntryL(TUint32 aIndex)
{
// __PRINT1(_L("#-CFat32LruCache::ReadEntryL() FAT idx:%d"), aIndex);
ASSERT(aIndex >= KFatFirstSearchCluster && aIndex < (FatSize() >> KFat32EntrySzLog2));
//-- firstly try to locate required entry in cache
TFat32Entry entry;
if(ReadCachedEntryL(aIndex, entry))
return entry; //-- the requested entry found in cache
//-- No luck, get a spare cache page (it will be inserted to the head of the LRU list)
CFat32LruCachePage* pPage = DoGetSpareCachePageL();
ASSERT(pPage);
entry = pPage->ReadFromMediaL(aIndex); //-- read whole FAT page from the media
return entry;
}
//-----------------------------------------------------------------------------
/**
Write FAT entry to the cache.
Appropriate FAT cache sector will be marked as "dirty" and will be eventually flushed to the media.
@param aIndex FAT entry index
@param aEntry FAT entry value
*/
void CFat32LruCache::WriteEntryL(TUint32 aIndex, TUint32 aEntry)
{
//__PRINT2(_L("#-CFat32LruCache::WriteEntryL() FAT idx:%d, val:%d"), aIndex, aEntry);
ASSERT(aIndex >= KFatFirstSearchCluster && aIndex < (FatSize() >> KFat32EntrySzLog2));
SetDirty(ETrue);
//-- 1. try to locate entry in the cache and overwrite it there if it is cached
if(WriteCachedEntryL(aIndex, aEntry))
return; //-- the entry in cache altered
//-- 2. the entry isn't cached; find a spare cache page (it will be inserted to the head of the LRU list)
CFat32LruCachePage* pPage = DoGetSpareCachePageL();
ASSERT(pPage);
pPage->ReadFromMediaL(aIndex); //-- read whole FAT page from the media
//-- 3. overwrite entry in cache
TBool bRes = pPage->WriteCachedEntryL(aIndex, aEntry);
ASSERT(bRes);
(void)bRes;
}
//-----------------------------------------------------------------------------
/**
A debug method that asserts that the cache is really clean
*/
void CFat32LruCache::AssertCacheReallyClean()
{
#ifdef _DEBUG
TPageIterator itr(iPageList);
for(;;)
{//-- iterate through LRU list flushing pages into the current copy of FAT
CFat32LruCachePage* pPage = itr++;
if(!pPage)
break;
if(pPage->IsDirty())
{
__PRINT(_L("#-CFat32LruCache::AssertCacheReallyClean()"));
ASSERT(0);
}
}
#endif
}
//-----------------------------------------------------------------------------
/**
Flushes all dirty data to the media.
*/
void CFat32LruCache::FlushL()
{
if(!IsDirty())
{
AssertCacheReallyClean();
return;
}
//-- flush dirty data to all copies of FAT
//-- all dirty pages will be written firstly to FAT1, then all of them will be written to FAT2 etc.
for(iCurrentFatNo=0; iCurrentFatNo < NumFATs(); ++iCurrentFatNo)
{
TPageIterator itr(iPageList);
for(;;)
{//-- iterate through LRU list flushing pages into the current copy of FAT
CFat32LruCachePage* pPage = itr++;
if(!pPage)
break;
//-- we need to keep page dirty until it is flushed to the last copy of FAT table
const TBool keepDirty = iCurrentFatNo < (NumFATs() - 1);
pPage->FlushL(keepDirty);
}
}
iCurrentFatNo = KInvalidFatNo;
SetDirty(EFalse);
}
//-----------------------------------------------------------------------------
/**
Invalidate whole cache. All pages will be marked as invalid and will be re-read from the media on first access to them.
@return always KErrNone
*/
TInt CFat32LruCache::Invalidate()
{
__PRINT(_L("#-CFat32LruCache::Invalidate()"));
const TBool bIgnoreDirtyData = CheckInvalidatingDirtyCache();
//-- iterate through LRU list marking every page as invalid
TPageIterator itr(iPageList);
for(;;)
{
CFat32LruCachePage* pPage = itr++;
if(!pPage)
break;
pPage->Invalidate(bIgnoreDirtyData);
}
SetDirty(EFalse);
return KErrNone;
}
//-----------------------------------------------------------------------------
/**
Invalidate FAT cache pages that contain FAT32 entries from aStartIndex to (aStartIndex+aNumEntries)
These pages will be marked as invalid and will be re-read from the media on first access to them.
@param aStartIndex FAT start index of the region being invalidated
@param aNumEntries number of entries to invalidate
@return always KErrNone
*/
TInt CFat32LruCache::InvalidateRegion(TUint32 aStartIndex, TUint32 aNumEntries)
{
__PRINT2(_L("#-CFat32LruCache::InvalidateRegion() startIndex:%d, entries:%d"),aStartIndex, aNumEntries);
ASSERT(aStartIndex >= KFatFirstSearchCluster && aStartIndex < (FatSize() >> KFat32EntrySzLog2));
if(!aNumEntries)
{
ASSERT(0);
return KErrNone;
}
const TBool bIgnoreDirtyData = CheckInvalidatingDirtyCache();
const TUint KEntriesInPage = Pow2(PageSizeLog2() - KFat32EntrySzLog2);
const TUint KLastIndex = aStartIndex+aNumEntries;
TBool bCacheIsStillDirty = EFalse; //-- ETrue if the cache is still dirty after invalidating its region
for(TUint currIndex = aStartIndex; currIndex < KLastIndex; currIndex+=KEntriesInPage)
{
TPageIterator itr(iPageList);
for(;;)
{//-- iterate through all pages, invalidating required
CFat32LruCachePage* pPage = itr++;
if(!pPage)
break;
if(pPage->IsEntryCached(currIndex))
{
pPage->Invalidate(bIgnoreDirtyData);
}
else if(pPage->IsDirty()) //-- invalid page can't be ditry.
{
bCacheIsStillDirty = ETrue; //-- we have at least 1 dirty page
}
}
}
SetDirty(bCacheIsStillDirty);
return KErrNone;
}
//-----------------------------------------------------------------------------
/**
Look for free FAT entry in the FAT sector that corresponds to the aFatEntryIndex.
Search is performed in both directions, the right one has more priority (FAT cluster chain needs to grow right).
See FindFreeEntryInCacheSector()
*/
TBool CFat32LruCache::FindFreeEntryInCacheSectorL(TUint32& aFatEntryIndex)
{
if(ReadEntryL(aFatEntryIndex) == KSpareCluster)
return ETrue;
//-- look for free FAT entries in the FAT cache sector corresponting to the aStartIndex.
//-- use the same approach as in CFatTable::FindClosestFreeClusterL()
const TUint32 coeff = SectorSizeLog2()-KFat32EntrySzLog2;
const TUint32 numEntriesInSector = Pow2(coeff); //-- number of FAT32 entries in FAT cache sector
TUint32 MinIdx = (aFatEntryIndex >> coeff) << coeff;
TUint32 MaxIdx = MinIdx+numEntriesInSector-1;
if(MinIdx == 0)
{//-- correct values if this is the first FAT sector; FAT[0] & FAT[1] are reserved
MinIdx += KFatFirstSearchCluster;
}
//-- actual number of usable FAT entries can be less than deducted from number of FAT sectors.
MaxIdx = Min(MaxIdx, iMaxFatEntries-1);
//-- look in both directions starting from the aFatEntryIndex
//-- but in one FAT cache page sector only
TBool canGoRight = ETrue;
TBool canGoLeft = ETrue;
TUint32 rightIdx=aFatEntryIndex;
TUint32 leftIdx=aFatEntryIndex;
for(TUint i=0; i<numEntriesInSector; ++i)
{
if(canGoRight)
{
if(rightIdx < MaxIdx)
++rightIdx;
else
canGoRight = EFalse;
}
if(canGoLeft)
{
if(leftIdx > MinIdx)
--leftIdx;
else
canGoLeft = EFalse;
}
if(!canGoRight && !canGoLeft)
return EFalse; //-- no free entries in this sector
if(canGoRight && ReadEntryL(rightIdx) == KSpareCluster)
{
aFatEntryIndex = rightIdx;
return ETrue;
}
if (canGoLeft && ReadEntryL(leftIdx) == KSpareCluster)
{
aFatEntryIndex = leftIdx;
return ETrue;
}
}//for(TUint i=0; i<numEntriesInSector; ++i)
return EFalse;
}
//#################################################################################################################################
// CFat32LruCachePage implementation
//#################################################################################################################################
CFat32LruCachePage::CFat32LruCachePage(CFatPagedCacheBase& aCache)
:CFatCachePageBase(aCache)
{
ASSERT(IsPowerOf2(EntriesInPage()));
}
/**
Factory function.
@param aCache reference to the owning cache.
@return pointer to the constructed object or NULL on error
*/
CFat32LruCachePage* CFat32LruCachePage::NewL(CFatPagedCacheBase& aCache)
{
CFat32LruCachePage* pSelf = NULL;
pSelf = new (ELeave) CFat32LruCachePage(aCache);
CleanupStack::PushL(pSelf);
pSelf->iData.CreateMaxL(pSelf->PageSize()); //-- allocate memory for the page
CleanupStack::Pop();
return pSelf;
}
//-----------------------------------------------------------------------------
/**
Get a pointer to the FAT32 entry in the page buffer.
The page 's data shall be valid and the entry shall belong to this page.
@param aFatIndex absolute FAT index (from the FAT start) of the entry
@return pointer to the FAT32 entry in the page buffer.
*/
TFat32Entry* CFat32LruCachePage::GetEntryPtr(TUint32 aFatIndex) const
{
ASSERT(IsValid() && IsEntryCached(aFatIndex));
const TUint KEntryIndexInPage = aFatIndex & (EntriesInPage()-1); //-- number of entries in page is always a power of 2
TFat32Entry* pEntry = ((TFat32Entry*)iData.Ptr()) + KEntryIndexInPage;
return pEntry;
}
//-----------------------------------------------------------------------------
/**
Read FAT32 entry from the cache.
1. If the entry at aFatIndex doesn't belong to this page, returns EFalse
2. If page's data are valid and the entry is cached just extracts data from the page buffer.
3. If page's data are invalid but the entry's index belongs to this page, firstly reads data from the media and goto 2
@param aFatIndex entry's absolute FAT index (from the FAT start)
@param aResult on sucess there will be FAT32 entry value
@return ETrue if the entry at aFatIndex belongs to this page (cached) and in this case aResult will contain this entry.
EFalse if the entry isn't cached.
*/
TBool CFat32LruCachePage::ReadCachedEntryL(TUint32 aFatIndex, TUint32& aResult)
{
if(!IsEntryCached(aFatIndex))
return EFalse; //-- the page doesn't contain required index
if(IsValid())
{//-- read entry directly from page buffer, the cached data are valid
aResult = (*GetEntryPtr(aFatIndex)) & KFat32EntryMask;
}
else
{//-- aFatIndex belongs to this page, but the page is invalid and needs to be read from the media
__PRINT1(_L("#-CFat32LruCachePage::ReadCachedEntry(%d) The page is invalid, reading from the media"), aFatIndex);
aResult = ReadFromMediaL(aFatIndex);
}
return ETrue;
}
//-----------------------------------------------------------------------------
/**
Read the FAT32 cache page from the media and return required FAT32 entry.
@param aFatIndex entry's absolute FAT index (from the FAT start)
@return entry value at aFatIndex.
*/
TUint32 CFat32LruCachePage::ReadFromMediaL(TUint32 aFatIndex)
{
//__PRINT1(_L("#-CFat32LruCachePage::ReadFromMediaL() FAT idx:%d"), aFatIndex);
const TUint KFat32EntriesInPageLog2 = iCache.PageSizeLog2()-KFat32EntrySzLog2; //-- number of FAT32 entries in page is always a power of 2
//-- find out index in FAT this page starts from
iStartIndexInFAT = (aFatIndex >> KFat32EntriesInPageLog2) << KFat32EntriesInPageLog2;
SetState(EInvalid); //-- mark the page as invalid just in case if the read fails.
//-- read page from the media
const TUint32 pageStartPos = iCache.FatStartPos() + (iStartIndexInFAT << KFat32EntrySzLog2);
TInt nRes = iCache.ReadFatData(pageStartPos, iCache.PageSize(), iData);
if(nRes != KErrNone)
{
__PRINT1(_L("#-CFat32LruCachePage::ReadFromMediaL() failed! code:%d"), nRes);
User::Leave(nRes);
}
SetClean(); //-- mark this page as clean
const TFat32Entry entry = (*GetEntryPtr(aFatIndex)) & KFat32EntryMask;
return entry;
}
//-----------------------------------------------------------------------------
/**
Writes FAT cache page sector to the media (to all copies of the FAT)
@param aSector page sector number
*/
void CFat32LruCachePage::DoWriteSectorL(TUint32 aSector)
{
//__PRINT1(_L("#-CFat32LruCachePage::DoWriteContiguousSectorsL() startSec:%d"),aSector);
ASSERT(aSector < iCache.SectorsInPage());
const TUint CacheSecSzLog2=iCache.SectorSizeLog2();
TInt offset = 0;
if(iStartIndexInFAT == 0 && aSector == 0)
{//-- this is the very beginning of FAT32. We must skip FAT[0] & FAT[1] entries and do not write them to media.
offset = KFatFirstSearchCluster << KFat32EntrySzLog2;
}
const TUint8* pData = iData.Ptr()+offset+(aSector << CacheSecSzLog2);
TUint32 dataLen = (1 << CacheSecSzLog2) - offset;
const TUint32 mediaPosStart = iCache.FatStartPos() + (iStartIndexInFAT << KFat32EntrySzLog2) + (aSector << CacheSecSzLog2) + offset;
const TUint32 mediaPosEnd = mediaPosStart + dataLen;
//-- check if we are going to write beyond FAT. It can happen if the write granularity is bigger that the sector size.
const TUint32 posFatEnd = iCache.FatStartPos() + iCache.FatSize();
if(mediaPosEnd > posFatEnd)
{//-- correct the leength of the data to write.
dataLen -= (mediaPosEnd-posFatEnd);
}
TPtrC8 ptrData(pData, dataLen); //-- source data descriptor
TInt nRes = iCache.WriteFatData(mediaPosStart, ptrData);
if(nRes != KErrNone)
{
__PRINT1(_L("#-CFat32LruCachePage::DoWriteSectorsL() failed! code:%d"), nRes);
User::Leave(nRes);
}
//-- if we have FAT bit supercache and it is in consistent state, check if the entry in this cache differs from the data in dirty FAT cache sector.
CFatBitCache *pFatBitCache = iCache.BitCacheInterface();
if(pFatBitCache && pFatBitCache->UsableState())
{
//-- absolute FAT cache sector number corresponding aSector number in _this_ cache page
const TUint32 absSectorNum = (iStartIndexInFAT >> (CacheSecSzLog2-KFat32EntrySzLog2)) + aSector;
if(pFatBitCache->FatSectorHasFreeEntry(absSectorNum))
{ //-- it means that the corresponding FAT cache sector may or may not contain free FAT entry.
//-- in this case we need to repopulate corresponding bit cache entry.
const TUint32 numEntries = dataLen >> KFat32EntrySzLog2; //-- amount of FAT entries in this sector
const TFat32Entry* pFat32Entry = (const TFat32Entry* )pData;
TBool bHasFreeFatEntry = EFalse;
for(TUint i=0; i<numEntries; ++i)
{//-- look for free entries in this particular FAT cache sector.
if(pFat32Entry[i] == KSpareCluster)
{
bHasFreeFatEntry = ETrue;
break;
}
}
if(!bHasFreeFatEntry)
{ //-- FAT bit cache indicates that FAT sector absSectorNum has free entries, but it doesn't.
//-- this is because we can only set "has free entry" flag in CAtaFatTable::WriteL().
//-- correct FAT bit cache entry
pFatBitCache->SetFreeEntryInFatSector(absSectorNum, EFalse);
//__PRINT2(_L("#++ :DoWriteSectorL() Fixed FAT bit cache BitVec[%d]=%d"), absSectorNum, pFatBitCache->FatSectorHasFreeEntry(absSectorNum));
}
}
else //if(pBitCache->FatSectorHasFreeEntry(absSectorNum))
{//-- don't need to do anything. The corresponding FAT cache sector never contained free FAT entry and
//-- free FAT entry has never been written there in CAtaFatTable::WriteL().
}
}//if(pFatBitCache && pFatBitCache->UsableState())
}
//-----------------------------------------------------------------------------
/**
Write FAT32 entry at aFatIndex to the cache. Note that the data are not written to the media, only to the cache page.
Corresponding page sector is marked as dirty and will be flushed on FlushL() call later.
1. If the entry at aFatIndex doesn't belong to this page, returns EFalse
2. If page's data are valid and the entry is cached, copies data to the page buffer and marks sector as dirty.
3. If page's data are invalid but the entry's index belongs to this page, firstly reads data from the media and goto 2
@param aFatIndex entry's absolute FAT index (from the FAT start)
@param aFatEntry FAT32 entry value
@return ETrue if the entry at aFatIndex belongs to this page (cached) and in this case aResult will contain this entry.
EFalse if the entry isn't cached.
*/
TBool CFat32LruCachePage::WriteCachedEntryL(TUint32 aFatIndex, TUint32 aFatEntry)
{
if(!IsEntryCached(aFatIndex))
return EFalse; //-- the page doesn't contain required index
if(!IsValid())
{//-- we are trying to write data to the page that has invalid data. //-- read the data from the media first.
ReadFromMediaL(aFatIndex);
}
//-- for FAT32 only low 28 bits are used, 4 high are reserved; preserve them
TFat32Entry* pEntry = GetEntryPtr(aFatIndex);
const TFat32Entry orgEntry = *pEntry;
*pEntry = (orgEntry & ~KFat32EntryMask) | (aFatEntry & KFat32EntryMask);
//-- mark corresponding sector of the cache page as dirty
const TUint entryIndexInPage = aFatIndex & (EntriesInPage()-1); //-- number of entries in page is always a power of 2
const TUint dirtySectorNum = entryIndexInPage >> (iCache.SectorSizeLog2() - KFat32EntrySzLog2);
ASSERT(dirtySectorNum < iCache.SectorsInPage());
iDirtySectors.SetBit(dirtySectorNum);
SetState(EDirty); //-- mark page as dirty.
return ETrue;
}
//#################################################################################################################################
// CFatBitCache implementation
//#################################################################################################################################
//-- define this macro for extra debugging facilities for the CFatBitCache
//-- probably needs to be removed completely as soon as everything settles
//#define FAT_BIT_CACHE_DEBUG
//-----------------------------------------------------------------------------
CFatBitCache::CFatBitCache(CFat32LruCache& aOnwerFatCache)
:iOwnerFatCache(aOnwerFatCache)
{
SetState(EInvalid);
DBG_STATEMENT(iPopulatingThreadId=0);
}
CFatBitCache::~CFatBitCache()
{
Close();
}
//-----------------------------------------------------------------------------
/**
FAT bit supercache factory method
@return pointer to the created object or NULL if it coud not create or initialise it.
*/
CFatBitCache* CFatBitCache::New(CFat32LruCache& aOnwerFatCache)
{
__PRINT(_L("#++ CFatBitCache::New()"));
CFatBitCache* pSelf = NULL;
pSelf = new CFatBitCache(aOnwerFatCache);
if(!pSelf)
return NULL; //-- failed to create object
TInt nRes = pSelf->Initialise();
if(nRes != KErrNone)
{//-- failed to initialise the object
delete pSelf;
pSelf = NULL;
}
return pSelf;
}
//-----------------------------------------------------------------------------
/**
Initialisation.
Note that this cache suports FAT32 only.
@return KErrNone on success; otherwise standard error code.
*/
TInt CFatBitCache::Initialise()
{
__PRINT(_L("#++ CFatBitCache::Initialise()"));
Close();
//-- only FAT32 supported
if(iOwnerFatCache.FatType() != EFat32)
{
ASSERT(0);
Fault(EFatCache_BadFatType);
}
//-- create the bit vector. each bit position there represents one FAT cache sector (in FAT cache page terms, see FAT page structure)
const TUint fatSize = iOwnerFatCache.FatSize(); //-- FAT size in bytes
const TUint fatCacheSecSize = Pow2(iOwnerFatCache.SectorSizeLog2()); //-- FAT cache sector size
const TUint maxFatUsableCacheSectors = (fatSize + (fatCacheSecSize-1)) >> iOwnerFatCache.SectorSizeLog2(); //-- maximal number of usable fat cache sectors in whole FAT table
//-- create a bit vector
__PRINT1(_L("#++ CFatBitCache::Initialise() FAT supercache bits:%u"), maxFatUsableCacheSectors);
TInt nRes = iBitCache.Create(maxFatUsableCacheSectors);
if(nRes != KErrNone)
{
__PRINT1(_L("#++ Failed to create a bit vector! code:%d"), nRes);
return nRes;
}
//-- calculate the coefficient to be used to convet FAT index to FAT cache sector number (bit vector index).
iFatIdxToSecCoeff = iOwnerFatCache.SectorSizeLog2()-KFat32EntrySzLog2;
SetState(ENotPopulated);
return KErrNone;
}
//-----------------------------------------------------------------------------
/**
Closes the cache and deallocates bit vector memory.
*/
void CFatBitCache::Close()
{
__PRINT(_L("#++ CFatBitCache::Close()"));
//-- this method must not be called during populating (optionally by another thread)
ASSERT(State() != EPopulating);
ASSERT(iPopulatingThreadId == 0);
iBitCache.Close();
SetState(EInvalid);
}
//-----------------------------------------------------------------------------
/**
Tell the cache that we are starting to populate it.
N.B. Start, Finish and populating methods shall be called from the same thread.
Only one thread can be populating the bit vector;
@return ETrue on success. Efalse means that the cache is in the invalid state for some reason.
*/
TBool CFatBitCache::StartPopulating()
{
__PRINT2(_L("#++ CFatBitCache::StartPopulating(), State:%d, ThreadId:%d"), State(), (TUint)RThread().Id());
if(State() != ENotPopulated)
{//-- wrong state
ASSERT(0);
return EFalse;
}
ASSERT(iPopulatingThreadId == 0);
iBitCache.Fill(0);
SetState(EPopulating);
//-- store the the ID of the thread that starts populating the cache; it'll be checked later during populating.
DBG_STATEMENT(iPopulatingThreadId = RThread().Id());
return ETrue;
}
//-----------------------------------------------------------------------------
/**
Tell the cache that we have finished to populate it.
@return ETrue on success. EFalse means that the cache is in the invalid state for some reason.
*/
TBool CFatBitCache::FinishPopulating(TBool aSuccess)
{
__PRINT2(_L("#++ CFatBitCache::PopulatingFinished(), ThreadId:%d, success:%d"), (TUint)RThread().Id(), aSuccess);
if(State() != EPopulating)
{//-- wrong state
ASSERT(0);
return EFalse;
}
ASSERT(iPopulatingThreadId == RThread().Id()); //-- check that this method is called from the same thread that started populating
DBG_STATEMENT(iPopulatingThreadId = 0);
if(aSuccess)
SetState(EPopulated); //-- the cache is usable; populated OK
else
SetState(EInvalid); //-- the cache isn't populated properly, make it not usable
return ETrue;
}
//-----------------------------------------------------------------------------
/**
Tell FAT bit cache that there is a free entry at FAT aFatIndex.
Only this method can be used to populate the bit array (in EPopulating state).
Other methods can't access bit array in EPopulating state i.e. it is safe to populate the cache
from the thread other than FS drive thread (e.g within background FAT scan)
@param aFatIndex free FAT32 entry index
@return ETrue on success. EFalse means that the cache is in the invalid state for some reason.
*/
TBool CFatBitCache::SetFreeFatEntry(TUint32 aFatIndex)
{
//__PRINT3(_L("#++ ReportFreeFatEntry: idx:%d, state:%s, tid:%d"), aFatIndex, State(), (TUint)RThread().Id());
if(State() != EPopulating && State() != EPopulated)
{//-- wrong state, this can happen if someone forcedly invalidated this cache during populating
return EFalse;
}
#if defined _DEBUG && defined FAT_BIT_CACHE_DEBUG
//-- This leads to serious performance degradation, so be careful with it.
if(State() == EPopulating)
{//-- check that this method is called from the same thread that started populating
if(iPopulatingThreadId != RThread().Id())
{
__PRINT3(_L("#++ !! ReportFreeFatEntry: Access from different thread!! idx:%d, state:%d, tid:%d"), aFatIndex, State(), (TUint)RThread().Id());
}
//ASSERT(iPopulatingThreadId == RThread().Id());
}
#endif
//-- set bit to '1' which indicates that the FAT cache sector corresponding to the aFatIndex has at least one free FAT entry
const TUint32 bitNumber = FatIndexToCacheSectorNumber(aFatIndex); //-- index in the bit array corresponding FAT cache sector
#if defined _DEBUG && defined FAT_BIT_CACHE_DEBUG
//-- This leads to serious performance degradation, so be careful with it.
TBool b = iBitCache[bitNumber];
if(!b && State()==EPopulated)
{//-- someone is reporting a free entry in the given cache sector.
__PRINT1(_L("#++ CFatBitCache::ReportFreeFatEntry BitVec[%d]=1"), bitNumber);
}
#endif
iBitCache.SetBit(bitNumber);
return ETrue;
}
//-----------------------------------------------------------------------------
/**
Forcedly mark a part of the FAT bit super cache as containing free clusters (or not).
@param aStartFatIndex start FAT index of the range
@param aEndFatIndex end FAT index of the range
@param aAsFree if ETrue, the range will be marked as containing free clusters
*/
void CFatBitCache::MarkFatRange(TUint32 aStartFatIndex, TUint32 aEndFatIndex, TBool aAsFree)
{
__PRINT3(_L("#++ CFatBitCache::MarkFatRange(%d, %d, %d)"), aStartFatIndex, aEndFatIndex, aAsFree);
ASSERT(State() == EPopulating || State() == EPopulated);
const TUint32 bitNumberStart = FatIndexToCacheSectorNumber(aStartFatIndex);
const TUint32 bitNumberEnd = FatIndexToCacheSectorNumber(aEndFatIndex);
iBitCache.Fill(bitNumberStart, bitNumberEnd, aAsFree);
}
//-----------------------------------------------------------------------------
/**
Try to locate closest to the aFatIndex free FAT entry in the FAT32 LRU cache.
This is done by several steps:
1. Try to find FAT cache sector containing free FAT entry (by using FAT sectors bitmap)
2. locate free FAT entry within this sector.
@param aFatIndex in: absolute FAT entry index that will be used to start search from (we need to find the closest free entry to it)
out: may contain FAT index of the located free entry.
@return one of the completion codes:
KErrNone free entry found and its index is in aFatIndex
KErrNotFound FAT sector closest to the aFatIndex entry doesn't contain free FAT entries; the conflict is resolved, need to call this method again
KErrEof couldn't find any free sectors in FAT; need to fall back to the old search method
KErrCorrupt if the state of the cache is inconsistent
*/
TInt CFatBitCache::FindClosestFreeFatEntry(TUint32& aFatIndex)
{
const TUint32 startFatCacheSec = FatIndexToCacheSectorNumber(aFatIndex);
//__PRINT2(_L("#++ CFatBitCache::FindClosestFreeFatEntry() start idx:%d, start cache sec:%d"), aFatIndex, startFatCacheSec);
ASSERT(aFatIndex >= KFatFirstSearchCluster);
if(!UsableState())
{
ASSERT(0);
return KErrCorrupt;
}
TUint32 fatSeekCacheSec = startFatCacheSec; //-- FAT cache sector number that has free FAT entry, used for search .
TUint32 fatSeekIndex = aFatIndex; //-- FAT index to start search with
//-- 1. look if FAT sector that corresponds to the aStartFatIndex already has free entries.
//-- 2. if not, try to locate closest FAT cache sector that has by searching a bit vector
if(FatSectorHasFreeEntry(fatSeekCacheSec))
{
}
else
{//-- look in iBitCache for '1' entries nearest to the fatCacheSec, right side priority
if(!iBitCache.Find(fatSeekCacheSec, 1, RBitVector::ENearestR))
{//-- strange situation, there are no '1' bits in whole vector, search failed
__PRINT(_L("#++ CFatBitCache::FindClosestFreeFatEntry() bit vector search failed!"));
return KErrEof;
}
//-- bit cache found FAT sector(fatSeekCacheSec) that may have free FAT entries
//-- calculate FAT entry start index in this sector
fatSeekIndex = Max(KFatFirstSearchCluster, CacheSectorNumberToFatIndex(fatSeekCacheSec));
}
//-- here we have absolute FAT cache sector number, which may contain at least one free FAT entty
ASSERT(FatSectorHasFreeEntry(fatSeekCacheSec));
//-- ask FAT cache to find the exact index of free FAT entry in this particular FAT cache sector
TInt nRes;
TBool bFreeEntryFound=EFalse;
TRAP(nRes, bFreeEntryFound = iOwnerFatCache.FindFreeEntryInCacheSectorL(fatSeekIndex));
if(nRes != KErrNone)
{//-- it's possible on media read error
return KErrCorrupt;
}
if(bFreeEntryFound)
{//-- found free entry at aNewFreeEntryIndex
aFatIndex = fatSeekIndex;
return KErrNone;
}
//-- bit cache mismatch; its entry ('1') indicates that cache sector number fatCacheSec has free FAT entries,
//-- while in reality it doesnt. We need to fix the bit cache.
//__PRINT1(_L("#++ CFatBitCache::FindClosestFreeFatEntry fixing cache conflict; BitVec[%d]=0"), fatSeekCacheSec);
SetFreeEntryInFatSector(fatSeekCacheSec, EFalse);
return KErrNotFound;
}
//-----------------------------------------------------------------------------
/**
Print out the contents of the object. This is a debug only method
*/
void CFatBitCache::Dump() const
{
#if defined _DEBUG && defined FAT_BIT_CACHE_DEBUG
const TUint32 vecSz = iBitCache.Size();
__PRINT2(_L("#++ CFatBitCache::Dump(): state:%d, entries:%d"), State(), vecSz);
TBuf<120> printBuf;
const TUint KPrintEntries = 32;
TUint i;
printBuf.Append(_L(" "));
for(i=0; i<KPrintEntries; ++i)
{
printBuf.AppendFormat(_L("%02d "),i);
}
__PRINT(printBuf);
for(i=0; i<vecSz;)
{
printBuf.Format(_L("%03d: "), i);
for(TInt j=0; j<KPrintEntries; ++j)
{
if(i >= vecSz)
break;
printBuf.AppendFormat(_L("% d "), (iBitCache[i]!=0));
++i;
}
__PRINT(printBuf);
}
#endif
}