userlibandfileserver/fileserver/sfile/sf_file_cache.cpp
branchGCC_SURGE
changeset 221 39b39e1a406e
parent 201 43365a9b78a3
child 266 0008ccd16016
equal deleted inserted replaced
219:0ff03867bdb6 221:39b39e1a406e
    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;
  1566 	{
  1602 	{
  1567 	iLock.Wait();
  1603 	iLock.Wait();
  1568 
  1604 
  1569 	CFsClientMessageRequest* newRequest = NULL;
  1605 	CFsClientMessageRequest* newRequest = NULL;
  1570 
  1606 
  1571 	TInt r = DoFlushDirty(newRequest, aOldRequest, ETrue);
  1607 	TInt r = DoFlushDirty(newRequest, aOldRequest, EFlushAll);
  1572 
  1608 
  1573 	iLock.Signal();
  1609 	iLock.Signal();
  1574 
  1610 
  1575 	if (newRequest)
  1611 	if (newRequest)
  1576 	    {
  1612 	    {
  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);