userlibandfileserver/fileserver/sfile/sf_file_cache.cpp
branchRCL_3
changeset 41 0ffb4e86fcc9
parent 26 c734af59ce98
child 43 c1f20ce4abcf
equal deleted inserted replaced
39:2bb754abd467 41:0ffb4e86fcc9
    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 
  1571 	{
  1575 	{
  1572 	iLock.Wait();
  1576 	iLock.Wait();
  1573 
  1577 
  1574 	CFsClientMessageRequest* newRequest = NULL;
  1578 	CFsClientMessageRequest* newRequest = NULL;
  1575 
  1579 
  1576 	TInt r = DoFlushDirty(newRequest, aOldRequest, ETrue);
  1580 	TInt r = DoFlushDirty(newRequest, aOldRequest, EFlushAll);
  1577 
  1581 
  1578 	iLock.Signal();
  1582 	iLock.Signal();
  1579 
  1583 
  1580 	if (newRequest)
  1584 	if (newRequest)
  1581 	    {
  1585 	    {
  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);
  1898 					// Mark segment as clean
  1926 					// Mark segment as clean
  1899 					iCacheClient->MarkSegmentsAsClean(pos);
  1927 					iCacheClient->MarkSegmentsAsClean(pos);
  1900 					}
  1928 					}
  1901 
  1929 
  1902 
  1930 
  1903 				if (TInt(currentOperation->iScratchValue0) > 0)
  1931 				switch((TFlushMode) (TInt) currentOperation->iScratchValue0)
  1904 					currentOperation->iState = EStWriteToDisk;
  1932 					{
  1905 				else
  1933 					case EFlushSingle:
  1906 					currentOperation->iState = EStEnd;
  1934 						currentOperation->iState = EStEnd;
  1907 
  1935 						break;
  1908 
  1936 					case EFlushAll:
       
  1937 						currentOperation->iState = EStWriteToDisk;
       
  1938 						break;
       
  1939 					default:
       
  1940 						ASSERT(0);
       
  1941 						break;
       
  1942 					}
  1909 				}
  1943 				}
  1910 				break;
  1944 				break;
  1911 
  1945 
  1912 			case EStEnd:
  1946 			case EStEnd:
  1913 				{
  1947 				{