--- 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;