111 :iPageSizeLog2(aPageSizeInBytesLog2), |
111 :iPageSizeLog2(aPageSizeInBytesLog2), |
112 iWrGranularityLog2(aWrGranularityLog2), |
112 iWrGranularityLog2(aWrGranularityLog2), |
113 iMinSizeInPages(aMinPageNum), |
113 iMinSizeInPages(aMinPageNum), |
114 iMaxSizeInPages(aMaxPageNum), |
114 iMaxSizeInPages(aMaxPageNum), |
115 iDrive(aDrive), |
115 iDrive(aDrive), |
|
116 iLastVisitedPage(NULL), |
116 iLockedQ(_FOFF(TDynamicDirCachePage, iLink)), |
117 iLockedQ(_FOFF(TDynamicDirCachePage, iLink)), |
117 iUnlockedQ(_FOFF(TDynamicDirCachePage, iLink)), |
118 iUnlockedQ(_FOFF(TDynamicDirCachePage, iLink)), |
118 iLockedQCount(0), |
119 iLockedQCount(0), |
119 iUnlockedQCount(0), |
120 iUnlockedQCount(0), |
120 iHashFunction(HashFunction), |
121 iHashFunction(HashFunction), |
156 } |
157 } |
157 |
158 |
158 |
159 |
159 // allocate as many permanently locked pages as there are threads - plus one |
160 // allocate as many permanently locked pages as there are threads - plus one |
160 // otherwise DoMakePageMRU() won't work properly with only one thread |
161 // otherwise DoMakePageMRU() won't work properly with only one thread |
161 //-- At present moment the size of TDrive thread pool is 1 (1 drive thread in a pool) |
162 //-- At present moment the size of TDrive thread pool is 1 (1 drive thread in a pool) |
162 const TUint KThreadCount = 1; |
163 const TUint KThreadCount = 1; |
163 iPermanentlyAllocatedPageCount = KThreadCount + 1; |
164 iPermanentlyAllocatedPageCount = KThreadCount; |
164 |
165 |
165 if (iPermanentlyAllocatedPageCount > iMinSizeInPages) |
166 if (iPermanentlyAllocatedPageCount > iMinSizeInPages) |
166 iMinSizeInPages = iPermanentlyAllocatedPageCount; |
167 iMinSizeInPages = iPermanentlyAllocatedPageCount; |
167 |
168 |
168 for (TUint n=0; n<iPermanentlyAllocatedPageCount; n++) |
169 for (TUint n=0; n<iPermanentlyAllocatedPageCount; n++) |
169 { |
170 { |
170 TDynamicDirCachePage* pPage = AllocateAndLockNewPage(0); |
171 TDynamicDirCachePage* pPage = AllocateAndLockNewPage(); |
171 ASSERT(pPage); |
172 ASSERT(pPage); |
172 if (!pPage) |
173 if (!pPage) |
173 User::Leave(KErrNoMemory); |
174 User::Leave(KErrNoMemory); |
174 AddFirstOntoQueue(pPage, TDynamicDirCachePage::ELocked); |
175 AddFirstOntoQueue(pPage, TDynamicDirCachePage::ELocked); |
175 LookupTblAdd(pPage); |
176 LookupTblAdd(pPage); |
176 } |
177 } |
177 |
178 |
178 } |
179 } |
189 CleanupStack::Pop(); |
190 CleanupStack::Pop(); |
190 return pSelf; |
191 return pSelf; |
191 } |
192 } |
192 |
193 |
193 /** |
194 /** |
194 Insert an unlocked page into the last position of the locked queue, may squeeze the original last page into |
|
195 the unlocked queue. |
|
196 This function is used on last visited but 'unlocked' pages to avoid excessive lock/unlock calls to cache memory |
|
197 manager as contiguous entry reading/writing often happens on the same page. |
|
198 @param aPage the page to be inserted. |
|
199 @pre the page type of aPage should only be TDynamicDirCachePage::EUnknown |
|
200 */ |
|
201 void CDynamicDirCache::MakePageLastLocked(TDynamicDirCachePage* aPage) |
|
202 { |
|
203 // this function should not be called on active pages |
|
204 ASSERT(aPage->iType == TDynamicDirCachePage::EUnknown); |
|
205 |
|
206 if (iLockedQ.IsEmpty()) |
|
207 { |
|
208 // if locked queue is empty, add it onto the locked queue directly |
|
209 AddFirstOntoQueue(aPage, TDynamicDirCachePage::ELocked); |
|
210 } |
|
211 else |
|
212 { |
|
213 // otherwise, we squeeze for the last position on locked queue |
|
214 while (iLockedQCount + 1 >= iMinSizeInPages) |
|
215 { |
|
216 TDynamicDirCachePage* last = iLockedQ.Last(); |
|
217 DeQueue(last); |
|
218 UnlockPage(last); |
|
219 AddFirstOntoQueue(last, TDynamicDirCachePage::EUnlocked); |
|
220 } |
|
221 |
|
222 // iLockedQCount + 1 < iMinSizeInPages |
|
223 iLockedQ.AddLast(*aPage); |
|
224 aPage->SetPageType(TDynamicDirCachePage::ELocked); |
|
225 iLockedQCount++; |
|
226 } |
|
227 } |
|
228 |
|
229 /** |
|
230 Read data from a single page. If the page is not found or not valid anymore, read media onto iActive page first. |
195 Read data from a single page. If the page is not found or not valid anymore, read media onto iActive page first. |
231 The data will be _Appended_ the the descriptor aDes. The caller is responsible for maintaining this descriptor. |
196 The data will be _Appended_ the the descriptor aDes. The caller is responsible for maintaining this descriptor. |
232 |
197 |
233 @param aPos the starting position of the media address to be read. |
198 @param aPos the starting position of the media address to be read. |
234 @param aLength the length of the content to be read. |
199 @param aLength the length of the content to be read. |
246 { |
211 { |
247 // read data and append them to the descriptor |
212 // read data and append them to the descriptor |
248 aDes.Append(pPage->PtrInPage(aPos), aLength); |
213 aDes.Append(pPage->PtrInPage(aPos), aLength); |
249 |
214 |
250 |
215 |
251 // if page is from unlocked queue, insert it onto the last page of the locked |
216 // if page is from unlocked queue, try to keep it locked until we move to a |
252 // queue. this is to avoid excessive locking and unlocking operations that is |
217 // different page from the unlocked queue |
253 // highly likely to happen for following reads. |
218 // this is to avoid excessive locking and unlocking operations that is |
254 if (pPage->PageType() == TDynamicDirCachePage::EUnlocked) |
219 // highly likely to happen when DoFindL() linearly scan through the directory |
255 { |
220 if (pPage->PageType() == TDynamicDirCachePage::EUnlocked |
256 DeQueue(pPage); |
221 && iLastVisitedPage != pPage) |
257 MakePageLastLocked(pPage); |
222 { |
|
223 // Note: iLastVisitedPage may have been moved from unlocked queue to locked queue |
|
224 if(iLastVisitedPage && iLastVisitedPage->PageType() == TDynamicDirCachePage::EUnlocked) |
|
225 { |
|
226 User::LeaveIfError(UnlockPage(iLastVisitedPage)); |
|
227 } |
|
228 iLastVisitedPage = pPage; |
258 } |
229 } |
259 } |
230 } |
260 else // page locking failed |
231 else // page locking failed |
261 { |
232 { |
262 ASSERT(pPage->PageType() == TDynamicDirCachePage::EUnlocked); |
233 ASSERT(pPage->PageType() == TDynamicDirCachePage::EUnlocked); |
681 |
661 |
682 void CDynamicDirCache::DoMakePageMRU(TInt64 aPos) |
662 void CDynamicDirCache::DoMakePageMRU(TInt64 aPos) |
683 { |
663 { |
684 // __PRINT1(_L("MakePageMRU (%lx)"), aPos); |
664 // __PRINT1(_L("MakePageMRU (%lx)"), aPos); |
685 // __PRINT4(_L("Current Cache State: iLockedQCount=%d, iUnlockedQCount=%d, iLookupTbl=%d, iMaxSizeInPages=%d"), iLockedQCount, iUnlockedQCount, iLookupTable.Count(), iMaxSizeInPages); |
665 // __PRINT4(_L("Current Cache State: iLockedQCount=%d, iUnlockedQCount=%d, iLookupTbl=%d, iMaxSizeInPages=%d"), iLockedQCount, iUnlockedQCount, iLookupTable.Count(), iMaxSizeInPages); |
686 // check there are at least two locked pages |
666 // check there are at least one locked pages |
687 ASSERT(iLockedQCount > 1); |
667 ASSERT(iLockedQCount > 0); |
688 |
668 |
689 // check the MRU page first, if it is already the MRU page, we can return immediately |
669 // check the MRU page first, if it is already the MRU page, we can return immediately |
690 TInt64 pageStartMedPos = CalcPageStartPos(aPos); |
670 TInt64 pageStartMedPos = CalcPageStartPos(aPos); |
691 if (!iLockedQ.IsEmpty()) |
671 if (!iLockedQ.IsEmpty()) |
692 { |
672 { |
693 if (iLockedQ.First()->StartPos() == pageStartMedPos) |
673 if (iLockedQCount > 1 && iLockedQ.First()->StartPos() == pageStartMedPos) |
694 { |
674 { |
695 return; |
675 return; |
696 } |
676 } |
697 } |
677 } |
698 |
678 |
762 } |
743 } |
763 default: |
744 default: |
764 ASSERT(0); |
745 ASSERT(0); |
765 } |
746 } |
766 |
747 |
767 if (CacheIsFull() || !growCache) |
748 if (!makeNewPageMRU) |
768 return; |
749 return; |
769 |
750 |
770 // attempt to grow the cache by appending a clean, new page at the end of the LRU list. |
751 // when cache is full and a new MRU page is about to be added, we will need to evict the LRU page |
771 // This can fail when out of memory; the LRU mechanism then makes sure the oldest page will be re-used. |
752 // accordingly |
772 TDynamicDirCachePage* nPage = AllocateAndLockNewPage(0); |
753 if (CacheIsFull()) |
773 if (!nPage) |
754 { |
774 return; |
755 TUint32& queueCnt = iMaxSizeInPages - iMinSizeInPages > 0 ? iUnlockedQCount : iLockedQCount; |
775 |
756 queueCnt++; |
776 // about to add a page to end of locked queue, so lie about iLockedQCount |
757 CheckThresholds(); |
|
758 queueCnt--; |
|
759 } |
|
760 |
|
761 // attempt to grow the cache by appending a clean, new page at the end of the locked page queue. |
|
762 // This can fail when out of memory; the LRU mechanism then makes sure the oldest page will be re-used. |
|
763 TDynamicDirCachePage* nPage = AllocateAndLockNewPage(); |
|
764 if (!nPage) |
|
765 return; |
|
766 |
|
767 // about to add the new active page, force the locked queue to evict the existing last page to make room |
|
768 // for the new active page |
777 iLockedQCount++; |
769 iLockedQCount++; |
778 CheckThresholds(); |
770 CheckThresholds(); |
779 iLockedQCount--; |
771 iLockedQCount--; |
780 |
772 |
781 iLockedQ.AddLast(*nPage); |
773 iLockedQ.AddLast(*nPage); |
782 nPage->SetPageType(TDynamicDirCachePage::ELocked); |
774 nPage->SetPageType(TDynamicDirCachePage::ELocked); |
783 ++iLockedQCount; |
775 ++iLockedQCount; |
784 LookupTblAdd(nPage); |
|
785 } |
776 } |
786 |
777 |
787 /** |
778 /** |
788 Implementation of pure virtual function. |
779 Implementation of pure virtual function. |
789 @see MDiskSpecialAccessor::MakePageMRU() |
780 @see MDiskSpecialAccessor::MakePageMRU() |
895 |
886 |
896 /** |
887 /** |
897 Try to create a new page and lock the page content when it is created. This function should only be called |
888 Try to create a new page and lock the page content when it is created. This function should only be called |
898 when creating iActive page or making a page MRU (which might result in page evictions). |
889 when creating iActive page or making a page MRU (which might result in page evictions). |
899 @return the pointer of the newly created page, or NULL if allocation failed. |
890 @return the pointer of the newly created page, or NULL if allocation failed. |
900 @param aStartMedPos the starting media address of the page to be created. |
891 */ |
901 @pre aStartMedPos should not already be existing in the cache. |
892 TDynamicDirCachePage* CDynamicDirCache::AllocateAndLockNewPage(/*TInt64 aStartMedPos*/) |
902 */ |
893 { |
903 TDynamicDirCachePage* CDynamicDirCache::AllocateAndLockNewPage(TInt64 aStartMedPos) |
894 __PRINT(_L("CDynamicDirCache::AllocateAndLockNewPage()")); |
904 { |
895 |
905 __PRINT1(_L("CDynamicDirCache::AllocateAndLockNewPage(aStartMedPos=%lx)"), aStartMedPos); |
896 TUint8* startRamAddr = iCacheMemoryClient->AllocateAndLockSegments(PageSizeInSegs()); |
906 |
897 |
907 TUint8* startRamAddr = iCacheMemoryClient->AllocateAndLockSegments(PageSizeInSegs()); |
898 if (!startRamAddr) |
908 |
899 return NULL; |
909 if (!startRamAddr) |
900 |
910 return NULL; |
901 TDynamicDirCachePage* pPage = TDynamicDirCachePage::CreateCachePage(this, 0, startRamAddr); |
911 |
902 |
912 TDynamicDirCachePage* pPage = TDynamicDirCachePage::CreateCachePage(this, aStartMedPos, startRamAddr); |
903 // Failure would mean the cache chunk was able to grow but we've run out of heap. |
913 |
904 // This seems extremely unlikely, but decommit the now-unmanageable cache segment just in case. |
914 // Failure would mean the cache chunk was able to grow but we've run out of heap. |
905 if (!pPage) |
915 // This seems extremely unlikely, but decommit the now-unmanageable cache segment just in case. |
906 { |
916 if (!pPage) |
907 iCacheMemoryClient->DecommitSegments(startRamAddr, PageSizeInSegs()); |
917 { |
908 return NULL; |
918 iCacheMemoryClient->DecommitSegments(startRamAddr, PageSizeInSegs()); |
909 } |
919 return NULL; |
|
920 } |
|
921 |
910 |
922 pPage->SetLocked(ETrue); |
911 pPage->SetLocked(ETrue); |
923 pPage->SetValid(EFalse); |
912 pPage->SetValid(EFalse); |
924 return pPage; |
913 return pPage; |
925 } |
914 } |
926 |
915 |
927 #ifdef _DEBUG |
916 #if defined(_DEBUG) || defined(_DEBUG_RELEASE) |
928 /** |
917 /** |
929 Dump cache information, only enabled in debug mode. |
918 Dump cache information, only enabled in debug mode. |
930 @see CDynamicDirCache::Control() |
919 @see CDynamicDirCache::Control() |
931 */ |
920 */ |
932 void CDynamicDirCache::Info() const |
921 void CDynamicDirCache::Info(TDirCacheInfo* aInfo) const |
933 { |
922 { |
934 __PRINT(_L("======== CDynamicDirCache::Info =========")); |
923 __PRINT(_L("======== CDynamicDirCache::Info =========")); |
935 const TUint32 SegmentSizeInBytesLog2 = CCacheMemoryManagerFactory::CacheMemoryManager()->SegmentSizeInBytesLog2(); |
924 const TUint32 SegmentSizeInBytesLog2 = CCacheMemoryManagerFactory::CacheMemoryManager()->SegmentSizeInBytesLog2(); |
936 // page size |
925 const TUint32 pageMemSizeLog2 = iPageSizeLog2 > SegmentSizeInBytesLog2 ? iPageSizeLog2 : SegmentSizeInBytesLog2; |
937 __PRINT1(_L("=== Pages size: [%d Bytes]"), iPageSizeInBytes); |
926 // page size |
938 __PRINT1(_L("=== Segment size: [%d Bytes]"), 1 << SegmentSizeInBytesLog2); |
927 __PRINT1(_L("=== Segment size: [%d Bytes]"), 1 << SegmentSizeInBytesLog2); |
939 |
928 __PRINT1(_L("=== Page data size: [%d Bytes]"), iPageSizeInBytes); |
940 // data size: |
929 __PRINT1(_L("=== Page mem size: [%d Bytes]"), 1 << pageMemSizeLog2); |
941 __PRINT1(_L("=== Min data size: [%d Bytes]"), iMinSizeInPages << iPageSizeLog2); |
930 |
942 __PRINT1(_L("=== Max data size: [%d Bytes]"), iMaxSizeInPages << iPageSizeLog2); |
931 // cache size in pages |
943 |
932 __PRINT1(_L("=== Min cache size in pages: [%d]"), iMinSizeInPages); |
944 // memory size: |
933 __PRINT1(_L("=== Max cache size in pages: [%d]"), iMaxSizeInPages); |
945 const TUint32 pageMemSizeLog2 = iPageSizeLog2 > SegmentSizeInBytesLog2 ? iPageSizeLog2 : SegmentSizeInBytesLog2; |
934 |
946 __PRINT1(_L("=== Min memory size: [%d Bytes]"), iMinSizeInPages << pageMemSizeLog2); |
935 // locked page num |
947 __PRINT1(_L("=== Max memory size: [%d Bytes]"), iMaxSizeInPages << pageMemSizeLog2); |
936 __PRINT1(_L("=== Number of pages locked: [%d]"), iLockedQCount); |
948 |
937 // unlocked page num |
949 // reserved pages |
938 __PRINT1(_L("=== Number of pages unlocked: [%d]"), iUnlockedQCount); |
950 __PRINT1(_L("=== Number of pages reserved: [%d]"), iMinSizeInPages); |
939 __PRINT(_L("=========================================\n")); |
951 __PRINT1(_L("=== Reserved memory: [%d Bytes]"), (iMinSizeInPages * PageSizeInSegs()) << SegmentSizeInBytesLog2); |
940 |
952 // locked page num |
941 ASSERT(aInfo); |
953 __PRINT1(_L("=== Number of pages locked: [%d]"), iLockedQCount); |
942 aInfo->iMemorySegmentSize = 1 << SegmentSizeInBytesLog2; |
954 __PRINT1(_L("=== Locked memory: [%d Bytes]"), (iLockedQCount * PageSizeInSegs()) << SegmentSizeInBytesLog2); |
943 aInfo->iPageSizeInMemory = PageSizeInSegs() << SegmentSizeInBytesLog2; |
955 // unlocked page num |
944 aInfo->iPageSizeInData = iPageSizeInBytes; |
956 __PRINT1(_L("=== Number of pages unlocked: [%d]"), iUnlockedQCount); |
945 aInfo->iMinCacheSizeInPages = iMinSizeInPages; |
957 __PRINT1(_L("=== Unlocked memory: [%d Bytes]"), (iUnlockedQCount * PageSizeInSegs()) << SegmentSizeInBytesLog2); |
946 aInfo->iMaxCacheSizeInPages = iMaxSizeInPages; |
958 } |
947 aInfo->iMinCacheSizeInMemory = iMinSizeInPages * aInfo->iPageSizeInMemory; |
|
948 aInfo->iMaxCacheSizeInMemory = iMaxSizeInPages * aInfo->iPageSizeInMemory; |
|
949 aInfo->iLockedPageNumber = iLockedQCount; |
|
950 aInfo->iUnlockedPageNumber = iUnlockedQCount; |
|
951 } |
959 |
952 |
960 /** |
953 /** |
961 Dump cache content, only enabled in debug mode. |
954 Dump cache content, only enabled in debug mode. |
962 @see CDynamicDirCache::Control() |
955 @see CDynamicDirCache::Control() |
963 */ |
956 */ |