userlibandfileserver/fileserver/sfile/sf_file_cache.cpp
changeset 176 af6ec97d9189
parent 134 95847726fe57
child 254 1560c419b176
equal deleted inserted replaced
175:5af6c74cd793 176:af6ec97d9189
    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 
  1575 	{
  1602 	{
  1576 	iLock.Wait();
  1603 	iLock.Wait();
  1577 
  1604 
  1578 	CFsClientMessageRequest* newRequest = NULL;
  1605 	CFsClientMessageRequest* newRequest = NULL;
  1579 
  1606 
  1580 	TInt r = DoFlushDirty(newRequest, aOldRequest, ETrue);
  1607 	TInt r = DoFlushDirty(newRequest, aOldRequest, EFlushAll);
  1581 
  1608 
  1582 	iLock.Signal();
  1609 	iLock.Signal();
  1583 
  1610 
  1584 	if (newRequest)
  1611 	if (newRequest)
  1585 	    {
  1612 	    {
  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 				{