33 #include "sf_file_cache_defs.h" |
33 #include "sf_file_cache_defs.h" |
34 |
34 |
35 |
35 |
36 // disables flushing of stale cachelines before each write |
36 // disables flushing of stale cachelines before each write |
37 #define LAZY_WRITE |
37 #define LAZY_WRITE |
|
38 |
|
39 // call SetSizeL() to pre-allocate FAT clusters before flushing dirty data |
|
40 #define SETSIZE_BEFORE_WRITE |
38 |
41 |
39 enum TFileCacheFault |
42 enum TFileCacheFault |
40 { |
43 { |
41 ECacheSettingsInitFailed, |
44 ECacheSettingsInitFailed, |
42 ECacheSettingsNotFound, |
45 ECacheSettingsNotFound, |
61 EReOpeningUnNamedFile, |
64 EReOpeningUnNamedFile, |
62 EStartingDirtyAWrongStage, |
65 EStartingDirtyAWrongStage, |
63 EUnexpectedReNewLFailure, |
66 EUnexpectedReNewLFailure, |
64 EDirtyDataOwnerNull, |
67 EDirtyDataOwnerNull, |
65 EFlushingWithSessionNull, |
68 EFlushingWithSessionNull, |
|
69 EFlushingWithAllocatedRequest, |
66 }; |
70 }; |
67 |
71 |
68 |
72 |
69 LOCAL_C void Fault(TFileCacheFault aFault) |
73 LOCAL_C void Fault(TFileCacheFault aFault) |
70 // |
74 // |
983 |
987 |
984 // We need to flush all dirty data to disk in case this read overlaps |
988 // We need to flush all dirty data to disk in case this read overlaps |
985 // with any dirty data already in the file cache |
989 // with any dirty data already in the file cache |
986 if (aMode & EFileWriteBuffered) |
990 if (aMode & EFileWriteBuffered) |
987 { |
991 { |
988 TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, ETrue); |
992 TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFlushAll); |
989 if (r == CFsRequest::EReqActionBusy || r != CFsRequest::EReqActionComplete) |
993 if (r == CFsRequest::EReqActionBusy || r != CFsRequest::EReqActionComplete) |
990 return r; |
994 return r; |
991 } |
995 } |
992 |
996 |
993 retCode = CFsRequest::EReqActionContinue; // read uncached |
997 retCode = CFsRequest::EReqActionContinue; // read uncached |
1163 // (caching writes and requested write len > size of the cache), |
1167 // (caching writes and requested write len > size of the cache), |
1164 // flush the write cache |
1168 // flush the write cache |
1165 if ((aMode & EFileWriteBuffered) == 0 || |
1169 if ((aMode & EFileWriteBuffered) == 0 || |
1166 (cachingWrites && totalLen > iCacheSize)) |
1170 (cachingWrites && totalLen > iCacheSize)) |
1167 { |
1171 { |
1168 TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, ETrue); |
1172 TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFlushAll); |
1169 if (r == CFsRequest::EReqActionBusy || r != CFsRequest::EReqActionComplete) |
1173 if (r == CFsRequest::EReqActionBusy || r != CFsRequest::EReqActionComplete) |
1170 return r; |
1174 return r; |
1171 } |
1175 } |
1172 // if EFileWriteDirectIO then overwrite any non-dirty data and |
1176 // if EFileWriteDirectIO then overwrite any non-dirty data and |
1173 // write direct to disk.... |
1177 // write direct to disk.... |
1235 // Flush a single dirty cacheline (if there is one for this file) |
1239 // Flush a single dirty cacheline (if there is one for this file) |
1236 // or all dirty data on this drive (if there is any). |
1240 // or all dirty data on this drive (if there is any). |
1237 // If there's nothing to flush then the dirty (locked) data |
1241 // If there's nothing to flush then the dirty (locked) data |
1238 // must belong to another drive, so there's not much we can do |
1242 // must belong to another drive, so there's not much we can do |
1239 // but write through |
1243 // but write through |
1240 TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFalse); |
1244 TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFlushSingle); |
1241 if (r == CFsRequest::EReqActionBusy) |
1245 if (r == CFsRequest::EReqActionBusy) |
1242 return CFsRequest::EReqActionBusy; |
1246 return CFsRequest::EReqActionBusy; |
1243 if (r != KErrInUse) |
1247 if (r != KErrInUse) |
1244 { |
1248 { |
1245 // Need to switch to drive thread to flush all data on this drive |
1249 // Need to switch to drive thread to flush all data on this drive |
1260 { |
1264 { |
1261 // if no segment available, flush a single dirty cacheline |
1265 // if no segment available, flush a single dirty cacheline |
1262 // or wait if already flushing |
1266 // or wait if already flushing |
1263 if (iFlushBusy) |
1267 if (iFlushBusy) |
1264 return CFsRequest::EReqActionBusy; |
1268 return CFsRequest::EReqActionBusy; |
1265 TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFalse); |
1269 TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFlushSingle); |
1266 if (r != CFsRequest::EReqActionBusy && r != CFsRequest::EReqActionComplete) |
1270 if (r != CFsRequest::EReqActionBusy && r != CFsRequest::EReqActionComplete) |
1267 lastError = r; |
1271 lastError = r; |
1268 } |
1272 } |
1269 #endif |
1273 #endif |
1270 // if no cacheline found & write caching is enabled, allocate a new cacheline |
1274 // if no cacheline found & write caching is enabled, allocate a new cacheline |
1493 // we're going to issue an uncached write so clear any error |
1497 // we're going to issue an uncached write so clear any error |
1494 lastError = KErrNone; |
1498 lastError = KErrNone; |
1495 |
1499 |
1496 if (cachingWrites) |
1500 if (cachingWrites) |
1497 { |
1501 { |
1498 TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, ETrue); |
1502 TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFlushAll); |
1499 #ifdef _DEBUG |
1503 #ifdef _DEBUG |
1500 if (r == CFsRequest::EReqActionBusy) |
1504 if (r == CFsRequest::EReqActionBusy) |
1501 CCacheManagerFactory::CacheManager()->Stats().iWriteThroughWithDirtyDataCount++; |
1505 CCacheManagerFactory::CacheManager()->Stats().iWriteThroughWithDirtyDataCount++; |
1502 #endif |
1506 #endif |
1503 // Need to reset currentOperation.iReadWriteArgs.iTotalLength here to make sure |
1507 // Need to reset currentOperation.iReadWriteArgs.iTotalLength here to make sure |
1613 } |
1617 } |
1614 |
1618 |
1615 /** |
1619 /** |
1616 CFileCache::DoFlushDirty() |
1620 CFileCache::DoFlushDirty() |
1617 |
1621 |
1618 @param aFlushAll. If EFalse then only a single cacheline will be flushed |
1622 @param aFlushMode. The flush mode which indicates how many cache-lines to flush. @See TFlushMode |
1619 |
1623 |
1620 returns CFsRequest::EReqActionBusy if a flush is in progress OR cacheline already in use |
1624 returns CFsRequest::EReqActionBusy if a flush is in progress OR cacheline already in use |
1621 CFsRequest::EReqActionComplete if nothing to flush / flush completed |
1625 CFsRequest::EReqActionComplete if nothing to flush / flush completed |
1622 KErrNoMemory if failed to allocate a request |
1626 KErrNoMemory if failed to allocate a request |
1623 or one of the system wide error codes |
1627 or one of the system wide error codes |
1624 |
1628 |
1625 */ |
1629 */ |
1626 TInt CFileCache::DoFlushDirty(CFsClientMessageRequest*& aNewRequest, CFsRequest* aOldRequest, TBool aFlushAll) |
1630 TInt CFileCache::DoFlushDirty(CFsClientMessageRequest*& aNewRequest, CFsRequest* aOldRequest, TFlushMode aFlushMode) |
1627 { |
1631 { |
1628 TInt64 pos; |
1632 TInt64 pos; |
1629 TUint8* addr; |
1633 TUint8* addr; |
|
1634 |
|
1635 __ASSERT_ALWAYS(aNewRequest == NULL, Fault(EFlushingWithAllocatedRequest)); |
1630 |
1636 |
1631 if (iFlushBusy) |
1637 if (iFlushBusy) |
1632 return CFsRequest::EReqActionBusy; |
1638 return CFsRequest::EReqActionBusy; |
1633 |
1639 |
1634 TInt segmentCount = iCacheClient->FindFirstDirtySegment(pos, addr); |
1640 TInt segmentCount = iCacheClient->FindFirstDirtySegment(pos, addr); |
1679 aNewRequest->Free(); |
1685 aNewRequest->Free(); |
1680 aNewRequest = NULL; |
1686 aNewRequest = NULL; |
1681 return r; |
1687 return r; |
1682 } |
1688 } |
1683 |
1689 |
1684 // set the number of segments to flush - either all dirty data or the equivalent of one full cacheline |
1690 // set the flush mode |
1685 aNewRequest->CurrentOperation().iScratchValue0 = |
1691 aNewRequest->CurrentOperation().iScratchValue0 = (TAny*) aFlushMode; |
1686 (TAny*) (aFlushAll?KMaxTInt:iCacheClient->CacheLineSizeInSegments()); |
|
1687 |
|
1688 |
1692 |
1689 // issue flush request |
1693 // issue flush request |
1690 r = FlushDirtySm(*aNewRequest); |
1694 r = FlushDirtySm(*aNewRequest); |
1691 |
1695 |
1692 // We should only get three possible return codes from FlushDirtySm() : |
1696 // We should only get three possible return codes from FlushDirtySm() : |
1699 // requeue the caller's request (aOldRequest) if there is one |
1703 // requeue the caller's request (aOldRequest) if there is one |
1700 return CFsRequest::EReqActionBusy; |
1704 return CFsRequest::EReqActionBusy; |
1701 } |
1705 } |
1702 else // CFsRequest::EReqActionComplete |
1706 else // CFsRequest::EReqActionComplete |
1703 { |
1707 { |
|
1708 // Return any allocation failures etc to client |
|
1709 TInt lastError = aNewRequest->LastError(); |
|
1710 if (lastError != KErrNone) |
|
1711 r = lastError; |
|
1712 |
1704 aNewRequest->PopOperation(); |
1713 aNewRequest->PopOperation(); |
1705 aNewRequest->Free(); |
1714 aNewRequest->Free(); |
1706 aNewRequest = NULL; |
1715 aNewRequest = NULL; |
1707 return r; |
1716 return r; |
1708 } |
1717 } |
1776 currentOperation->iState = EStWriteToDisk; |
1785 currentOperation->iState = EStWriteToDisk; |
1777 |
1786 |
1778 TUint8* addr; |
1787 TUint8* addr; |
1779 TInt segmentCount = iCacheClient->FindFirstDirtySegment(pos, addr); |
1788 TInt segmentCount = iCacheClient->FindFirstDirtySegment(pos, addr); |
1780 |
1789 |
1781 // keep track of how many segments we've written |
|
1782 currentOperation->iScratchValue0 = (TAny*) ((TInt) currentOperation->iScratchValue0 - segmentCount); |
|
1783 |
|
1784 // if no more dirty segments of if a genuine error then finish |
1790 // if no more dirty segments of if a genuine error then finish |
1785 // NB if the request has been cancelled then we still need to proceed and mark all the |
1791 // NB if the request has been cancelled then we still need to proceed and mark all the |
1786 // segments as clean, otherwise they will never get freed ! |
1792 // segments as clean, otherwise they will never get freed ! |
1787 if (segmentCount == 0 || (lastError != KErrNone && lastError != KErrCancel)) |
1793 if (segmentCount == 0 || (lastError != KErrNone && lastError != KErrCancel)) |
1788 { |
1794 { |
1805 { |
1811 { |
1806 currentOperation->iState = EStMarkAsClean; |
1812 currentOperation->iState = EStMarkAsClean; |
1807 break; |
1813 break; |
1808 } |
1814 } |
1809 |
1815 |
|
1816 #ifdef SETSIZE_BEFORE_WRITE |
|
1817 TInt64 physicalFileSize = iFileCB->Size64(); |
|
1818 if ( (!iDrive->IsRugged()) && ((pos + (TInt64) len) > physicalFileSize)) |
|
1819 { |
|
1820 // Need to switch to drive thread before calling CFileCB::SetSizeL() |
|
1821 if (!IsDriveThread()) |
|
1822 { |
|
1823 aMsgRequest.SetState(CFsRequest::EReqStatePostInitialise); |
|
1824 return CFsRequest::EReqActionBusy; |
|
1825 } |
|
1826 __CACHE_PRINT2(_L("CACHEFILE: Increasing uncached size from %ld to %ld"), iFileCB->Size64(), iSize64); |
|
1827 TInt r; |
|
1828 |
|
1829 r = CheckDiskSpace(iSize64 - physicalFileSize, &aMsgRequest); |
|
1830 if(r == KErrNone) |
|
1831 TRAP(r, iFileCB->SetSizeL(iSize64)) |
|
1832 if (r == KErrNone) |
|
1833 { |
|
1834 iFileCB->SetSize64(iSize64, ETrue); // NB drive might be locked |
|
1835 } |
|
1836 } |
|
1837 #endif |
1810 __CACHE_PRINT3(_L("CACHEFILE: FlushDirty first dirty pos %d, addr %08X count %d"), I64LOW(pos), addr, segmentCount); |
1838 __CACHE_PRINT3(_L("CACHEFILE: FlushDirty first dirty pos %d, addr %08X count %d"), I64LOW(pos), addr, segmentCount); |
1811 |
1839 |
1812 TInt filledSegmentCount; |
1840 TInt filledSegmentCount; |
1813 TInt lockError; |
1841 TInt lockError; |
1814 addr = iCacheClient->FindAndLockSegments(pos, segmentCount, filledSegmentCount, lockError, ETrue); |
1842 addr = iCacheClient->FindAndLockSegments(pos, segmentCount, filledSegmentCount, lockError, ETrue); |