37 #endif |
37 #endif |
38 |
38 |
39 |
39 |
40 // disables flushing of stale cachelines before each write |
40 // disables flushing of stale cachelines before each write |
41 #define LAZY_WRITE |
41 #define LAZY_WRITE |
|
42 |
|
43 // call SetSizeL() to pre-allocate FAT clusters before flushing dirty data |
|
44 #define SETSIZE_BEFORE_WRITE |
42 |
45 |
43 enum TFileCacheFault |
46 enum TFileCacheFault |
44 { |
47 { |
45 ECacheSettingsInitFailed, |
48 ECacheSettingsInitFailed, |
46 ECacheSettingsNotFound, |
49 ECacheSettingsNotFound, |
65 EReOpeningUnNamedFile, |
68 EReOpeningUnNamedFile, |
66 EStartingDirtyAWrongStage, |
69 EStartingDirtyAWrongStage, |
67 EUnexpectedReNewLFailure, |
70 EUnexpectedReNewLFailure, |
68 EDirtyDataOwnerNull, |
71 EDirtyDataOwnerNull, |
69 EFlushingWithSessionNull, |
72 EFlushingWithSessionNull, |
|
73 EFlushingWithAllocatedRequest, |
70 }; |
74 }; |
71 |
75 |
72 |
76 |
73 LOCAL_C void Fault(TFileCacheFault aFault) |
77 LOCAL_C void Fault(TFileCacheFault aFault) |
74 // |
78 // |
77 { |
81 { |
78 User::Panic(_L("FSFILECACHE"), aFault); |
82 User::Panic(_L("FSFILECACHE"), aFault); |
79 } |
83 } |
80 |
84 |
81 const TInt KMinSequentialReadsBeforeReadAhead = 3; |
85 const TInt KMinSequentialReadsBeforeReadAhead = 3; |
|
86 |
|
87 #ifdef DOUBLE_BUFFERED_WRITING |
|
88 const TInt KMinSequentialAppendsBeforeFlush = 3; |
|
89 #endif |
82 |
90 |
83 //************************************ |
91 //************************************ |
84 // CFileCache |
92 // CFileCache |
85 //************************************ |
93 //************************************ |
86 |
94 |
987 |
995 |
988 // We need to flush all dirty data to disk in case this read overlaps |
996 // We need to flush all dirty data to disk in case this read overlaps |
989 // with any dirty data already in the file cache |
997 // with any dirty data already in the file cache |
990 if (aMode & EFileWriteBuffered) |
998 if (aMode & EFileWriteBuffered) |
991 { |
999 { |
992 TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, ETrue); |
1000 TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFlushAll); |
993 if (r == CFsRequest::EReqActionBusy || r != CFsRequest::EReqActionComplete) |
1001 if (r == CFsRequest::EReqActionBusy || r != CFsRequest::EReqActionComplete) |
994 return r; |
1002 return r; |
995 } |
1003 } |
996 |
1004 |
997 retCode = CFsRequest::EReqActionContinue; // read uncached |
1005 retCode = CFsRequest::EReqActionContinue; // read uncached |
1161 |
1169 |
1162 if (currentPos > iSize64) |
1170 if (currentPos > iSize64) |
1163 currentPos = iSize64; |
1171 currentPos = iSize64; |
1164 iInitialSize = iSize64; |
1172 iInitialSize = iSize64; |
1165 |
1173 |
|
1174 |
|
1175 #ifdef DOUBLE_BUFFERED_WRITING |
|
1176 // count the number of sequential write-appends |
|
1177 if (currentOperation->iClientRequest) |
|
1178 { |
|
1179 if (currentPos == iSize64) |
|
1180 iSequentialAppends++; |
|
1181 else |
|
1182 iSequentialAppends = 0; |
|
1183 } |
|
1184 #endif // DOUBLE_BUFFERED_WRITING |
|
1185 |
1166 // if EFileWriteDirectIO OR |
1186 // if EFileWriteDirectIO OR |
1167 // (caching writes and requested write len > size of the cache), |
1187 // (caching writes and requested write len > size of the cache), |
1168 // flush the write cache |
1188 // flush the write cache |
1169 if ((aMode & EFileWriteBuffered) == 0 || |
1189 if ((aMode & EFileWriteBuffered) == 0 || |
1170 (cachingWrites && totalLen > iCacheSize)) |
1190 (cachingWrites && totalLen > iCacheSize)) |
1171 { |
1191 { |
1172 TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, ETrue); |
1192 TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFlushAll); |
1173 if (r == CFsRequest::EReqActionBusy || r != CFsRequest::EReqActionComplete) |
1193 if (r == CFsRequest::EReqActionBusy || r != CFsRequest::EReqActionComplete) |
1174 return r; |
1194 return r; |
1175 } |
1195 } |
1176 // if EFileWriteDirectIO then overwrite any non-dirty data and |
1196 // if EFileWriteDirectIO then overwrite any non-dirty data and |
1177 // write direct to disk.... |
1197 // write direct to disk.... |
1239 // Flush a single dirty cacheline (if there is one for this file) |
1259 // Flush a single dirty cacheline (if there is one for this file) |
1240 // or all dirty data on this drive (if there is any). |
1260 // or all dirty data on this drive (if there is any). |
1241 // If there's nothing to flush then the dirty (locked) data |
1261 // If there's nothing to flush then the dirty (locked) data |
1242 // must belong to another drive, so there's not much we can do |
1262 // must belong to another drive, so there's not much we can do |
1243 // but write through |
1263 // but write through |
1244 TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFalse); |
1264 TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFlushSingle); |
1245 if (r == CFsRequest::EReqActionBusy) |
1265 if (r == CFsRequest::EReqActionBusy) |
1246 return CFsRequest::EReqActionBusy; |
1266 return CFsRequest::EReqActionBusy; |
1247 if (r != KErrInUse) |
1267 if (r != KErrInUse) |
1248 { |
1268 { |
1249 // Need to switch to drive thread to flush all data on this drive |
1269 // Need to switch to drive thread to flush all data on this drive |
1264 { |
1284 { |
1265 // if no segment available, flush a single dirty cacheline |
1285 // if no segment available, flush a single dirty cacheline |
1266 // or wait if already flushing |
1286 // or wait if already flushing |
1267 if (iFlushBusy) |
1287 if (iFlushBusy) |
1268 return CFsRequest::EReqActionBusy; |
1288 return CFsRequest::EReqActionBusy; |
1269 TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFalse); |
1289 TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFlushSingle); |
1270 if (r != CFsRequest::EReqActionBusy && r != CFsRequest::EReqActionComplete) |
1290 if (r != CFsRequest::EReqActionBusy && r != CFsRequest::EReqActionComplete) |
1271 lastError = r; |
1291 lastError = r; |
|
1292 } |
|
1293 #endif |
|
1294 #ifdef DOUBLE_BUFFERED_WRITING |
|
1295 if (cachingWrites && lastError == KErrNone && |
|
1296 iSequentialAppends >= KMinSequentialAppendsBeforeFlush && iCacheClient->LockedSegmentsHalfUsed()) |
|
1297 { |
|
1298 DoFlushDirty(aNewRequest, &aMsgRequest, EFlushHalf); |
1272 } |
1299 } |
1273 #endif |
1300 #endif |
1274 // if no cacheline found & write caching is enabled, allocate a new cacheline |
1301 // if no cacheline found & write caching is enabled, allocate a new cacheline |
1275 if (cachingWrites && addr == NULL && lastError == KErrNone) |
1302 if (cachingWrites && addr == NULL && lastError == KErrNone) |
1276 { |
1303 { |
1497 // we're going to issue an uncached write so clear any error |
1524 // we're going to issue an uncached write so clear any error |
1498 lastError = KErrNone; |
1525 lastError = KErrNone; |
1499 |
1526 |
1500 if (cachingWrites) |
1527 if (cachingWrites) |
1501 { |
1528 { |
1502 TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, ETrue); |
1529 TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFlushAll); |
1503 #ifdef _DEBUG |
1530 #ifdef _DEBUG |
1504 if (r == CFsRequest::EReqActionBusy) |
1531 if (r == CFsRequest::EReqActionBusy) |
1505 CCacheManagerFactory::CacheManager()->Stats().iWriteThroughWithDirtyDataCount++; |
1532 CCacheManagerFactory::CacheManager()->Stats().iWriteThroughWithDirtyDataCount++; |
1506 #endif |
1533 #endif |
1507 // Need to reset currentOperation.iReadWriteArgs.iTotalLength here to make sure |
1534 // Need to reset currentOperation.iReadWriteArgs.iTotalLength here to make sure |
1613 } |
1640 } |
1614 |
1641 |
1615 /** |
1642 /** |
1616 CFileCache::DoFlushDirty() |
1643 CFileCache::DoFlushDirty() |
1617 |
1644 |
1618 @param aFlushAll. If EFalse then only a single cacheline will be flushed |
1645 @param aFlushMode. The flush mode which indicates how many cache-lines to flush. @See TFlushMode |
1619 |
1646 |
1620 returns CFsRequest::EReqActionBusy if a flush is in progress OR cacheline already in use |
1647 returns CFsRequest::EReqActionBusy if a flush is in progress OR cacheline already in use |
1621 CFsRequest::EReqActionComplete if nothing to flush / flush completed |
1648 CFsRequest::EReqActionComplete if nothing to flush / flush completed |
1622 KErrNoMemory if failed to allocate a request |
1649 KErrNoMemory if failed to allocate a request |
1623 or one of the system wide error codes |
1650 or one of the system wide error codes |
1624 |
1651 |
1625 */ |
1652 */ |
1626 TInt CFileCache::DoFlushDirty(CFsClientMessageRequest*& aNewRequest, CFsRequest* aOldRequest, TBool aFlushAll) |
1653 TInt CFileCache::DoFlushDirty(CFsClientMessageRequest*& aNewRequest, CFsRequest* aOldRequest, TFlushMode aFlushMode) |
1627 { |
1654 { |
1628 TInt64 pos; |
1655 TInt64 pos; |
1629 TUint8* addr; |
1656 TUint8* addr; |
|
1657 |
|
1658 __ASSERT_ALWAYS(aNewRequest == NULL, Fault(EFlushingWithAllocatedRequest)); |
1630 |
1659 |
1631 if (iFlushBusy) |
1660 if (iFlushBusy) |
1632 return CFsRequest::EReqActionBusy; |
1661 return CFsRequest::EReqActionBusy; |
1633 |
1662 |
1634 TInt segmentCount = iCacheClient->FindFirstDirtySegment(pos, addr); |
1663 TInt segmentCount = iCacheClient->FindFirstDirtySegment(pos, addr); |
1679 aNewRequest->Free(); |
1708 aNewRequest->Free(); |
1680 aNewRequest = NULL; |
1709 aNewRequest = NULL; |
1681 return r; |
1710 return r; |
1682 } |
1711 } |
1683 |
1712 |
1684 // set the number of segments to flush - either all dirty data or the equivalent of one full cacheline |
1713 // set the flush mode |
1685 aNewRequest->CurrentOperation().iScratchValue0 = |
1714 aNewRequest->CurrentOperation().iScratchValue0 = (TAny*) aFlushMode; |
1686 (TAny*) (aFlushAll?KMaxTInt:iCacheClient->CacheLineSizeInSegments()); |
|
1687 |
|
1688 |
1715 |
1689 // issue flush request |
1716 // issue flush request |
1690 r = FlushDirtySm(*aNewRequest); |
1717 r = FlushDirtySm(*aNewRequest); |
1691 |
1718 |
1692 // We should only get three possible return codes from FlushDirtySm() : |
1719 // We should only get three possible return codes from FlushDirtySm() : |
1699 // requeue the caller's request (aOldRequest) if there is one |
1726 // requeue the caller's request (aOldRequest) if there is one |
1700 return CFsRequest::EReqActionBusy; |
1727 return CFsRequest::EReqActionBusy; |
1701 } |
1728 } |
1702 else // CFsRequest::EReqActionComplete |
1729 else // CFsRequest::EReqActionComplete |
1703 { |
1730 { |
|
1731 // Return any allocation failures etc to client |
|
1732 TInt lastError = aNewRequest->LastError(); |
|
1733 if (lastError != KErrNone) |
|
1734 r = lastError; |
|
1735 |
1704 aNewRequest->PopOperation(); |
1736 aNewRequest->PopOperation(); |
1705 aNewRequest->Free(); |
1737 aNewRequest->Free(); |
1706 aNewRequest = NULL; |
1738 aNewRequest = NULL; |
1707 return r; |
1739 return r; |
1708 } |
1740 } |
1776 currentOperation->iState = EStWriteToDisk; |
1808 currentOperation->iState = EStWriteToDisk; |
1777 |
1809 |
1778 TUint8* addr; |
1810 TUint8* addr; |
1779 TInt segmentCount = iCacheClient->FindFirstDirtySegment(pos, addr); |
1811 TInt segmentCount = iCacheClient->FindFirstDirtySegment(pos, addr); |
1780 |
1812 |
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 |
1813 // 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 |
1814 // 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 ! |
1815 // segments as clean, otherwise they will never get freed ! |
1787 if (segmentCount == 0 || (lastError != KErrNone && lastError != KErrCancel)) |
1816 if (segmentCount == 0 || (lastError != KErrNone && lastError != KErrCancel)) |
1788 { |
1817 { |
1805 { |
1834 { |
1806 currentOperation->iState = EStMarkAsClean; |
1835 currentOperation->iState = EStMarkAsClean; |
1807 break; |
1836 break; |
1808 } |
1837 } |
1809 |
1838 |
|
1839 #ifdef SETSIZE_BEFORE_WRITE |
|
1840 TInt64 physicalFileSize = iFileCB->Size64(); |
|
1841 if ((pos + (TInt64) len) > physicalFileSize) |
|
1842 { |
|
1843 // Need to switch to drive thread before calling CFileCB::SetSizeL() |
|
1844 if (!IsDriveThread()) |
|
1845 { |
|
1846 aMsgRequest.SetState(CFsRequest::EReqStatePostInitialise); |
|
1847 return CFsRequest::EReqActionBusy; |
|
1848 } |
|
1849 __CACHE_PRINT2(_L("CACHEFILE: Increasing uncached size from %ld to %ld"), iFileCB->Size64(), iSize64); |
|
1850 TInt r; |
|
1851 |
|
1852 r = CheckDiskSpace(iSize64 - physicalFileSize, &aMsgRequest); |
|
1853 if(r == KErrNone) |
|
1854 TRAP(r, iFileCB->SetSizeL(iSize64)) |
|
1855 if (r == KErrNone) |
|
1856 { |
|
1857 iFileCB->SetSize64(iSize64, ETrue); // NB drive might be locked |
|
1858 } |
|
1859 } |
|
1860 #endif |
1810 __CACHE_PRINT3(_L("CACHEFILE: FlushDirty first dirty pos %d, addr %08X count %d"), I64LOW(pos), addr, segmentCount); |
1861 __CACHE_PRINT3(_L("CACHEFILE: FlushDirty first dirty pos %d, addr %08X count %d"), I64LOW(pos), addr, segmentCount); |
1811 |
1862 |
1812 TInt filledSegmentCount; |
1863 TInt filledSegmentCount; |
1813 TInt lockError; |
1864 TInt lockError; |
1814 addr = iCacheClient->FindAndLockSegments(pos, segmentCount, filledSegmentCount, lockError, ETrue); |
1865 addr = iCacheClient->FindAndLockSegments(pos, segmentCount, filledSegmentCount, lockError, ETrue); |
1898 // Mark segment as clean |
1949 // Mark segment as clean |
1899 iCacheClient->MarkSegmentsAsClean(pos); |
1950 iCacheClient->MarkSegmentsAsClean(pos); |
1900 } |
1951 } |
1901 |
1952 |
1902 |
1953 |
1903 if (TInt(currentOperation->iScratchValue0) > 0) |
1954 switch((TFlushMode) (TInt) currentOperation->iScratchValue0) |
1904 currentOperation->iState = EStWriteToDisk; |
1955 { |
1905 else |
1956 case EFlushSingle: |
1906 currentOperation->iState = EStEnd; |
1957 currentOperation->iState = EStEnd; |
1907 |
1958 break; |
1908 |
1959 case EFlushHalf: |
|
1960 if (iCacheClient->LockedSegmentsHalfUsed()) |
|
1961 currentOperation->iState = EStWriteToDisk; |
|
1962 else |
|
1963 currentOperation->iState = EStEnd; |
|
1964 break; |
|
1965 case EFlushAll: |
|
1966 currentOperation->iState = EStWriteToDisk; |
|
1967 break; |
|
1968 default: |
|
1969 ASSERT(0); |
|
1970 break; |
|
1971 } |
1909 } |
1972 } |
1910 break; |
1973 break; |
1911 |
1974 |
1912 case EStEnd: |
1975 case EStEnd: |
1913 { |
1976 { |