30 #include "sf_file_cache.h" |
30 #include "sf_file_cache.h" |
31 #include "sf_cache_man.h" |
31 #include "sf_cache_man.h" |
32 #include "sf_cache_client.h" |
32 #include "sf_cache_client.h" |
33 #include "sf_file_cache_defs.h" |
33 #include "sf_file_cache_defs.h" |
34 |
34 |
|
35 #ifdef OST_TRACE_COMPILER_IN_USE |
|
36 #include "sf_file_cacheTraces.h" |
|
37 #endif |
|
38 |
35 |
39 |
36 // disables flushing of stale cachelines before each write |
40 // disables flushing of stale cachelines before each write |
37 #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 |
38 |
45 |
39 enum TFileCacheFault |
46 enum TFileCacheFault |
40 { |
47 { |
41 ECacheSettingsInitFailed, |
48 ECacheSettingsInitFailed, |
42 ECacheSettingsNotFound, |
49 ECacheSettingsNotFound, |
61 EReOpeningUnNamedFile, |
68 EReOpeningUnNamedFile, |
62 EStartingDirtyAWrongStage, |
69 EStartingDirtyAWrongStage, |
63 EUnexpectedReNewLFailure, |
70 EUnexpectedReNewLFailure, |
64 EDirtyDataOwnerNull, |
71 EDirtyDataOwnerNull, |
65 EFlushingWithSessionNull, |
72 EFlushingWithSessionNull, |
|
73 EFlushingWithAllocatedRequest, |
66 }; |
74 }; |
67 |
75 |
68 |
76 |
69 LOCAL_C void Fault(TFileCacheFault aFault) |
77 LOCAL_C void Fault(TFileCacheFault aFault) |
70 // |
78 // |
73 { |
81 { |
74 User::Panic(_L("FSFILECACHE"), aFault); |
82 User::Panic(_L("FSFILECACHE"), aFault); |
75 } |
83 } |
76 |
84 |
77 const TInt KMinSequentialReadsBeforeReadAhead = 3; |
85 const TInt KMinSequentialReadsBeforeReadAhead = 3; |
|
86 |
|
87 #ifdef DOUBLE_BUFFERED_WRITING |
|
88 const TInt KMinSequentialAppendsBeforeFlush = 3; |
|
89 #endif |
78 |
90 |
79 //************************************ |
91 //************************************ |
80 // CFileCache |
92 // CFileCache |
81 //************************************ |
93 //************************************ |
82 |
94 |
983 |
995 |
984 // 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 |
985 // with any dirty data already in the file cache |
997 // with any dirty data already in the file cache |
986 if (aMode & EFileWriteBuffered) |
998 if (aMode & EFileWriteBuffered) |
987 { |
999 { |
988 TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, ETrue); |
1000 TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFlushAll); |
989 if (r == CFsRequest::EReqActionBusy || r != CFsRequest::EReqActionComplete) |
1001 if (r == CFsRequest::EReqActionBusy || r != CFsRequest::EReqActionComplete) |
990 return r; |
1002 return r; |
991 } |
1003 } |
992 |
1004 |
993 retCode = CFsRequest::EReqActionContinue; // read uncached |
1005 retCode = CFsRequest::EReqActionContinue; // read uncached |
1157 |
1169 |
1158 if (currentPos > iSize64) |
1170 if (currentPos > iSize64) |
1159 currentPos = iSize64; |
1171 currentPos = iSize64; |
1160 iInitialSize = iSize64; |
1172 iInitialSize = iSize64; |
1161 |
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 |
1162 // if EFileWriteDirectIO OR |
1186 // if EFileWriteDirectIO OR |
1163 // (caching writes and requested write len > size of the cache), |
1187 // (caching writes and requested write len > size of the cache), |
1164 // flush the write cache |
1188 // flush the write cache |
1165 if ((aMode & EFileWriteBuffered) == 0 || |
1189 if ((aMode & EFileWriteBuffered) == 0 || |
1166 (cachingWrites && totalLen > iCacheSize)) |
1190 (cachingWrites && totalLen > iCacheSize)) |
1167 { |
1191 { |
1168 TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, ETrue); |
1192 TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFlushAll); |
1169 if (r == CFsRequest::EReqActionBusy || r != CFsRequest::EReqActionComplete) |
1193 if (r == CFsRequest::EReqActionBusy || r != CFsRequest::EReqActionComplete) |
1170 return r; |
1194 return r; |
1171 } |
1195 } |
1172 // if EFileWriteDirectIO then overwrite any non-dirty data and |
1196 // if EFileWriteDirectIO then overwrite any non-dirty data and |
1173 // write direct to disk.... |
1197 // write direct to disk.... |
1235 // 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) |
1236 // or all dirty data on this drive (if there is any). |
1260 // or all dirty data on this drive (if there is any). |
1237 // If there's nothing to flush then the dirty (locked) data |
1261 // 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 |
1262 // must belong to another drive, so there's not much we can do |
1239 // but write through |
1263 // but write through |
1240 TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFalse); |
1264 TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFlushSingle); |
1241 if (r == CFsRequest::EReqActionBusy) |
1265 if (r == CFsRequest::EReqActionBusy) |
1242 return CFsRequest::EReqActionBusy; |
1266 return CFsRequest::EReqActionBusy; |
1243 if (r != KErrInUse) |
1267 if (r != KErrInUse) |
1244 { |
1268 { |
1245 // 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 |
1260 { |
1284 { |
1261 // if no segment available, flush a single dirty cacheline |
1285 // if no segment available, flush a single dirty cacheline |
1262 // or wait if already flushing |
1286 // or wait if already flushing |
1263 if (iFlushBusy) |
1287 if (iFlushBusy) |
1264 return CFsRequest::EReqActionBusy; |
1288 return CFsRequest::EReqActionBusy; |
1265 TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFalse); |
1289 TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFlushSingle); |
1266 if (r != CFsRequest::EReqActionBusy && r != CFsRequest::EReqActionComplete) |
1290 if (r != CFsRequest::EReqActionBusy && r != CFsRequest::EReqActionComplete) |
1267 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); |
1268 } |
1299 } |
1269 #endif |
1300 #endif |
1270 // 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 |
1271 if (cachingWrites && addr == NULL && lastError == KErrNone) |
1302 if (cachingWrites && addr == NULL && lastError == KErrNone) |
1272 { |
1303 { |
1493 // 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 |
1494 lastError = KErrNone; |
1525 lastError = KErrNone; |
1495 |
1526 |
1496 if (cachingWrites) |
1527 if (cachingWrites) |
1497 { |
1528 { |
1498 TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, ETrue); |
1529 TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFlushAll); |
1499 #ifdef _DEBUG |
1530 #ifdef _DEBUG |
1500 if (r == CFsRequest::EReqActionBusy) |
1531 if (r == CFsRequest::EReqActionBusy) |
1501 CCacheManagerFactory::CacheManager()->Stats().iWriteThroughWithDirtyDataCount++; |
1532 CCacheManagerFactory::CacheManager()->Stats().iWriteThroughWithDirtyDataCount++; |
1502 #endif |
1533 #endif |
1503 // Need to reset currentOperation.iReadWriteArgs.iTotalLength here to make sure |
1534 // Need to reset currentOperation.iReadWriteArgs.iTotalLength here to make sure |
1504 // TFsFileWrite::PostInitialise() doesn't think there's no data left to process |
1535 // TFsFileWrite::PostInitialise() doesn't think there's no data left to process |
1505 aMsgRequest.ReStart(); |
1536 aMsgRequest.ReStart(); |
|
1537 |
|
1538 //Need to preserve the current state otherwise if we are over the ram threshold |
|
1539 //the request can end up in a livelock trying to repeatedly flush. |
|
1540 currentOperation->iState = EStWriteThrough; |
|
1541 |
1506 if (r == CFsRequest::EReqActionBusy || r != CFsRequest::EReqActionComplete) |
1542 if (r == CFsRequest::EReqActionBusy || r != CFsRequest::EReqActionComplete) |
1507 return r; |
1543 return r; |
1508 } |
1544 } |
1509 |
1545 |
1510 retCode = CFsRequest::EReqActionContinue; |
1546 retCode = CFsRequest::EReqActionContinue; |
1604 } |
1640 } |
1605 |
1641 |
1606 /** |
1642 /** |
1607 CFileCache::DoFlushDirty() |
1643 CFileCache::DoFlushDirty() |
1608 |
1644 |
1609 @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 |
1610 |
1646 |
1611 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 |
1612 CFsRequest::EReqActionComplete if nothing to flush / flush completed |
1648 CFsRequest::EReqActionComplete if nothing to flush / flush completed |
1613 KErrNoMemory if failed to allocate a request |
1649 KErrNoMemory if failed to allocate a request |
1614 or one of the system wide error codes |
1650 or one of the system wide error codes |
1615 |
1651 |
1616 */ |
1652 */ |
1617 TInt CFileCache::DoFlushDirty(CFsClientMessageRequest*& aNewRequest, CFsRequest* aOldRequest, TBool aFlushAll) |
1653 TInt CFileCache::DoFlushDirty(CFsClientMessageRequest*& aNewRequest, CFsRequest* aOldRequest, TFlushMode aFlushMode) |
1618 { |
1654 { |
1619 TInt64 pos; |
1655 TInt64 pos; |
1620 TUint8* addr; |
1656 TUint8* addr; |
|
1657 |
|
1658 __ASSERT_ALWAYS(aNewRequest == NULL, Fault(EFlushingWithAllocatedRequest)); |
1621 |
1659 |
1622 if (iFlushBusy) |
1660 if (iFlushBusy) |
1623 return CFsRequest::EReqActionBusy; |
1661 return CFsRequest::EReqActionBusy; |
1624 |
1662 |
1625 TInt segmentCount = iCacheClient->FindFirstDirtySegment(pos, addr); |
1663 TInt segmentCount = iCacheClient->FindFirstDirtySegment(pos, addr); |
1670 aNewRequest->Free(); |
1708 aNewRequest->Free(); |
1671 aNewRequest = NULL; |
1709 aNewRequest = NULL; |
1672 return r; |
1710 return r; |
1673 } |
1711 } |
1674 |
1712 |
1675 // set the number of segments to flush - either all dirty data or the equivalent of one full cacheline |
1713 // set the flush mode |
1676 aNewRequest->CurrentOperation().iScratchValue0 = |
1714 aNewRequest->CurrentOperation().iScratchValue0 = (TAny*) aFlushMode; |
1677 (TAny*) (aFlushAll?KMaxTInt:iCacheClient->CacheLineSizeInSegments()); |
|
1678 |
|
1679 |
1715 |
1680 // issue flush request |
1716 // issue flush request |
1681 r = FlushDirtySm(*aNewRequest); |
1717 r = FlushDirtySm(*aNewRequest); |
1682 |
1718 |
1683 // We should only get three possible return codes from FlushDirtySm() : |
1719 // We should only get three possible return codes from FlushDirtySm() : |
1690 // requeue the caller's request (aOldRequest) if there is one |
1726 // requeue the caller's request (aOldRequest) if there is one |
1691 return CFsRequest::EReqActionBusy; |
1727 return CFsRequest::EReqActionBusy; |
1692 } |
1728 } |
1693 else // CFsRequest::EReqActionComplete |
1729 else // CFsRequest::EReqActionComplete |
1694 { |
1730 { |
|
1731 // Return any allocation failures etc to client |
|
1732 TInt lastError = aNewRequest->LastError(); |
|
1733 if (lastError != KErrNone) |
|
1734 r = lastError; |
|
1735 |
1695 aNewRequest->PopOperation(); |
1736 aNewRequest->PopOperation(); |
1696 aNewRequest->Free(); |
1737 aNewRequest->Free(); |
1697 aNewRequest = NULL; |
1738 aNewRequest = NULL; |
1698 return r; |
1739 return r; |
1699 } |
1740 } |
1767 currentOperation->iState = EStWriteToDisk; |
1808 currentOperation->iState = EStWriteToDisk; |
1768 |
1809 |
1769 TUint8* addr; |
1810 TUint8* addr; |
1770 TInt segmentCount = iCacheClient->FindFirstDirtySegment(pos, addr); |
1811 TInt segmentCount = iCacheClient->FindFirstDirtySegment(pos, addr); |
1771 |
1812 |
1772 // keep track of how many segments we've written |
|
1773 currentOperation->iScratchValue0 = (TAny*) ((TInt) currentOperation->iScratchValue0 - segmentCount); |
|
1774 |
|
1775 // 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 |
1776 // 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 |
1777 // segments as clean, otherwise they will never get freed ! |
1815 // segments as clean, otherwise they will never get freed ! |
1778 if (segmentCount == 0 || (lastError != KErrNone && lastError != KErrCancel)) |
1816 if (segmentCount == 0 || (lastError != KErrNone && lastError != KErrCancel)) |
1779 { |
1817 { |
1796 { |
1834 { |
1797 currentOperation->iState = EStMarkAsClean; |
1835 currentOperation->iState = EStMarkAsClean; |
1798 break; |
1836 break; |
1799 } |
1837 } |
1800 |
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 |
1801 __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); |
1802 |
1862 |
1803 TInt filledSegmentCount; |
1863 TInt filledSegmentCount; |
1804 TInt lockError; |
1864 TInt lockError; |
1805 addr = iCacheClient->FindAndLockSegments(pos, segmentCount, filledSegmentCount, lockError, ETrue); |
1865 addr = iCacheClient->FindAndLockSegments(pos, segmentCount, filledSegmentCount, lockError, ETrue); |
1889 // Mark segment as clean |
1949 // Mark segment as clean |
1890 iCacheClient->MarkSegmentsAsClean(pos); |
1950 iCacheClient->MarkSegmentsAsClean(pos); |
1891 } |
1951 } |
1892 |
1952 |
1893 |
1953 |
1894 if (TInt(currentOperation->iScratchValue0) > 0) |
1954 switch((TFlushMode) (TInt) currentOperation->iScratchValue0) |
1895 currentOperation->iState = EStWriteToDisk; |
1955 { |
1896 else |
1956 case EFlushSingle: |
1897 currentOperation->iState = EStEnd; |
1957 currentOperation->iState = EStEnd; |
1898 |
1958 break; |
1899 |
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 } |
1900 } |
1972 } |
1901 break; |
1973 break; |
1902 |
1974 |
1903 case EStEnd: |
1975 case EStEnd: |
1904 { |
1976 { |
1989 |
2061 |
1990 // Without this code, retry will indiscriminately write over whatever disk happens to be present. |
2062 // Without this code, retry will indiscriminately write over whatever disk happens to be present. |
1991 // However if the write error is to the bootsector remounting will always fail because the boot |
2063 // However if the write error is to the bootsector remounting will always fail because the boot |
1992 // sector will have changed and hence the disk is useless. |
2064 // sector will have changed and hence the disk is useless. |
1993 // |
2065 // |
1994 TRACE1(UTF::EBorder, UTraceModuleFileSys::ECMountCBReMount, EF32TraceUidFileSys, DriveNumber()); |
2066 OstTrace1(TRACE_FILESYSTEM, FSYS_ECMOUNTCBREMOUNT2, "drive %d", DriveNumber()); |
1995 |
2067 |
1996 TInt remountSuccess = iDrive->ReMount(*iMount); |
2068 TInt remountSuccess = iDrive->ReMount(*iMount); |
1997 |
2069 |
1998 TRACE1(UTF::EBorder, UTraceModuleFileSys::ECMountCBReMountRet, EF32TraceUidFileSys, remountSuccess); |
2070 OstTrace1(TRACE_FILESYSTEM, FSYS_ECMOUNTCBREMOUNT2RET, "success %d", remountSuccess); |
1999 if (!remountSuccess) |
2071 if (!remountSuccess) |
2000 continue; |
2072 continue; |
2001 |
2073 |
2002 |
2074 |
2003 iMount->Drive().SetChanged(EFalse); |
2075 iMount->Drive().SetChanged(EFalse); |