userlibandfileserver/fileserver/sfile/sf_file_cache.cpp
branchGCC_SURGE
changeset 221 39b39e1a406e
parent 201 43365a9b78a3
child 266 0008ccd16016
--- a/userlibandfileserver/fileserver/sfile/sf_file_cache.cpp	Wed Jul 21 14:46:58 2010 +0100
+++ b/userlibandfileserver/fileserver/sfile/sf_file_cache.cpp	Thu Jul 22 16:46:39 2010 +0100
@@ -32,10 +32,17 @@
 #include "sf_cache_client.h"
 #include "sf_file_cache_defs.h"
 
+#ifdef OST_TRACE_COMPILER_IN_USE
+#include "sf_file_cacheTraces.h"
+#endif
+
 
 // disables flushing of stale cachelines before each write
 #define LAZY_WRITE
 
+// call SetSizeL() to pre-allocate FAT clusters before flushing dirty data
+#define SETSIZE_BEFORE_WRITE	
+
 enum TFileCacheFault
 	{
 	ECacheSettingsInitFailed,
@@ -63,6 +70,7 @@
 	EUnexpectedReNewLFailure,
 	EDirtyDataOwnerNull,
 	EFlushingWithSessionNull,
+	EFlushingWithAllocatedRequest,
 	};
 
 
@@ -76,6 +84,10 @@
 
 const TInt KMinSequentialReadsBeforeReadAhead = 3;
 
+#ifdef DOUBLE_BUFFERED_WRITING
+const TInt KMinSequentialAppendsBeforeFlush = 3;
+#endif
+
 //************************************
 // CFileCache
 //************************************
@@ -985,7 +997,7 @@
 				// with any dirty data already in the file cache
 				if (aMode & EFileWriteBuffered)
 					{
-					TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, ETrue);
+					TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFlushAll);
 					if (r == CFsRequest::EReqActionBusy || r != CFsRequest::EReqActionComplete)
 						return r;
 					}
@@ -1159,13 +1171,25 @@
 					currentPos = iSize64;
 				iInitialSize = iSize64;
 
+
+#ifdef DOUBLE_BUFFERED_WRITING
+				// count the number of sequential write-appends
+				if (currentOperation->iClientRequest)
+					{
+					if (currentPos == iSize64)
+						iSequentialAppends++;
+					else
+						iSequentialAppends = 0;
+					}
+#endif // DOUBLE_BUFFERED_WRITING
+
 				// if EFileWriteDirectIO OR 
 				// (caching writes and requested write len > size of the cache), 
 				// flush the write cache
 				if ((aMode & EFileWriteBuffered) == 0 || 
 					(cachingWrites && totalLen > iCacheSize))
 					{
-					TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, ETrue);
+					TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFlushAll);
 					if (r == CFsRequest::EReqActionBusy || r != CFsRequest::EReqActionComplete)
 						return r;
 					}
@@ -1237,7 +1261,7 @@
 					// If there's nothing to flush then the dirty (locked) data
 					// must belong to another drive, so there's not much we can do
 					// but write through 
-					TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFalse);
+					TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFlushSingle);
 					if (r == CFsRequest::EReqActionBusy)
 						return CFsRequest::EReqActionBusy;
 					if (r != KErrInUse)
@@ -1262,11 +1286,18 @@
 					// or wait if already flushing
 					if (iFlushBusy)
 						return CFsRequest::EReqActionBusy;
-					TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFalse);
+					TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFlushSingle);
 					if (r != CFsRequest::EReqActionBusy && r != CFsRequest::EReqActionComplete)
 						lastError = r;
 					}
 #endif
+#ifdef DOUBLE_BUFFERED_WRITING
+				if (cachingWrites && lastError == KErrNone && 
+					iSequentialAppends >= KMinSequentialAppendsBeforeFlush && iCacheClient->LockedSegmentsHalfUsed())
+					{
+					DoFlushDirty(aNewRequest, &aMsgRequest, EFlushHalf);
+					}
+#endif
 				// if no cacheline found & write caching is enabled, allocate a new cacheline
 				if (cachingWrites && addr == NULL && lastError == KErrNone)
 					{
@@ -1495,7 +1526,7 @@
 
 				if (cachingWrites)
 					{
-					TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, ETrue);
+					TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFlushAll);
 #ifdef _DEBUG
 					if (r == CFsRequest::EReqActionBusy)
 						CCacheManagerFactory::CacheManager()->Stats().iWriteThroughWithDirtyDataCount++;
@@ -1503,6 +1534,11 @@
 					// Need to reset currentOperation.iReadWriteArgs.iTotalLength here to make sure 
 					// TFsFileWrite::PostInitialise() doesn't think there's no data left to process
 					aMsgRequest.ReStart();
+					
+					//Need to preserve the current state otherwise if we are over the ram threshold 
+					//the request can end up in a livelock trying to repeatedly flush.
+					currentOperation->iState = EStWriteThrough;
+					
 					if (r == CFsRequest::EReqActionBusy || r != CFsRequest::EReqActionComplete)
 						return r;
 					}
@@ -1568,7 +1604,7 @@
 
 	CFsClientMessageRequest* newRequest = NULL;
 
-	TInt r = DoFlushDirty(newRequest, aOldRequest, ETrue);
+	TInt r = DoFlushDirty(newRequest, aOldRequest, EFlushAll);
 
 	iLock.Signal();
 
@@ -1606,7 +1642,7 @@
 /**
 CFileCache::DoFlushDirty()
 
-@param aFlushAll. If EFalse then only a single cacheline will be flushed
+@param aFlushMode. The flush mode which indicates how many cache-lines to flush. @See TFlushMode
 
 returns	CFsRequest::EReqActionBusy if a flush is in progress OR cacheline already in use
 		CFsRequest::EReqActionComplete if nothing to flush / flush completed 
@@ -1614,11 +1650,13 @@
 		or one of the system wide error codes
 
  */
-TInt CFileCache::DoFlushDirty(CFsClientMessageRequest*& aNewRequest, CFsRequest* aOldRequest, TBool aFlushAll)
+TInt CFileCache::DoFlushDirty(CFsClientMessageRequest*& aNewRequest, CFsRequest* aOldRequest, TFlushMode aFlushMode)
 	{
 	TInt64  pos;
 	TUint8* addr;
 
+	__ASSERT_ALWAYS(aNewRequest == NULL, Fault(EFlushingWithAllocatedRequest));
+
 	if (iFlushBusy)
 		return CFsRequest::EReqActionBusy;
 
@@ -1672,10 +1710,8 @@
 		return r;
 		}
 
-	// set the number of segments to flush - either all dirty data or the equivalent of one full cacheline
-	aNewRequest->CurrentOperation().iScratchValue0 = 
-		(TAny*) (aFlushAll?KMaxTInt:iCacheClient->CacheLineSizeInSegments());
-
+	// set the flush mode
+	aNewRequest->CurrentOperation().iScratchValue0 = (TAny*) aFlushMode;
 
 	// issue flush request
 	r = FlushDirtySm(*aNewRequest);
@@ -1692,6 +1728,11 @@
 		}
 	else	// CFsRequest::EReqActionComplete
 		{
+		// Return any allocation failures etc to client
+		TInt lastError = aNewRequest->LastError();
+		if (lastError != KErrNone)
+			r = lastError;
+
 		aNewRequest->PopOperation();
 		aNewRequest->Free();
 		aNewRequest = NULL;
@@ -1769,9 +1810,6 @@
 				TUint8* addr;
 				TInt segmentCount = iCacheClient->FindFirstDirtySegment(pos, addr);
 				
-				// keep track of how many segments we've written
-				currentOperation->iScratchValue0 = (TAny*) ((TInt) currentOperation->iScratchValue0 - segmentCount);
-
 				// if no more dirty segments of if a genuine error then finish
 				// NB if the request has been cancelled then we still need to proceed and mark all the
 				// segments as clean, otherwise they will never get freed !
@@ -1798,6 +1836,28 @@
 					break;
 					}
 
+#ifdef SETSIZE_BEFORE_WRITE	
+				TInt64 physicalFileSize = iFileCB->Size64();
+				if ((pos + (TInt64) len) > physicalFileSize)
+					{
+					// Need to switch to drive thread before calling CFileCB::SetSizeL()
+					if (!IsDriveThread())
+						{
+						aMsgRequest.SetState(CFsRequest::EReqStatePostInitialise);
+						return CFsRequest::EReqActionBusy;
+						}
+					__CACHE_PRINT2(_L("CACHEFILE: Increasing uncached size from %ld to %ld"), iFileCB->Size64(), iSize64);
+					TInt r;
+
+					r = CheckDiskSpace(iSize64 - physicalFileSize, &aMsgRequest);
+			        if(r == KErrNone)
+						TRAP(r, iFileCB->SetSizeL(iSize64))
+					if (r == KErrNone)
+						{
+ 						iFileCB->SetSize64(iSize64, ETrue);	// NB drive might be locked
+						}
+					}
+#endif
 				__CACHE_PRINT3(_L("CACHEFILE: FlushDirty first dirty pos %d, addr %08X count %d"), I64LOW(pos), addr, segmentCount);
 
 				TInt filledSegmentCount;
@@ -1891,12 +1951,24 @@
 					}
 
 
-				if (TInt(currentOperation->iScratchValue0) > 0)
-					currentOperation->iState = EStWriteToDisk;
-				else
-					currentOperation->iState = EStEnd;
-
-
+				switch((TFlushMode) (TInt) currentOperation->iScratchValue0)
+					{
+					case EFlushSingle:
+						currentOperation->iState = EStEnd;
+						break;
+					case EFlushHalf:
+						if (iCacheClient->LockedSegmentsHalfUsed())
+							currentOperation->iState = EStWriteToDisk;
+						else
+							currentOperation->iState = EStEnd;
+						break;
+					case EFlushAll:
+						currentOperation->iState = EStWriteToDisk;
+						break;
+					default:
+						ASSERT(0);
+						break;
+					}
 				}
 				break;
 
@@ -1991,11 +2063,11 @@
 			// However if the write error is to the bootsector remounting will always fail because the boot
 			// sector will have changed and hence the disk is useless.
 			// 
-			TRACE1(UTF::EBorder, UTraceModuleFileSys::ECMountCBReMount, EF32TraceUidFileSys, DriveNumber());
+			OstTrace1(TRACE_FILESYSTEM, FSYS_ECMOUNTCBREMOUNT2, "drive %d", DriveNumber());
 
 			TInt remountSuccess = iDrive->ReMount(*iMount);
 
-			TRACE1(UTF::EBorder, UTraceModuleFileSys::ECMountCBReMountRet, EF32TraceUidFileSys, remountSuccess);
+			OstTrace1(TRACE_FILESYSTEM, FSYS_ECMOUNTCBREMOUNT2RET, "success %d", remountSuccess);
 			if (!remountSuccess)
 				continue;