userlibandfileserver/fileserver/sfile/sf_file_cache.cpp
changeset 0 a41df078684a
child 26 c734af59ce98
equal deleted inserted replaced
-1:000000000000 0:a41df078684a
       
     1 // Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of the License "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 // f32\sfile\sf_file_cache.cpp
       
    15 // 
       
    16 //
       
    17 
       
    18 /**
       
    19  @file
       
    20  @internalTechnology
       
    21 */
       
    22 
       
    23 #include <e32std.h>
       
    24 #include <e32std_private.h>
       
    25 #include "sf_std.h"
       
    26 #include <e32uid.h>
       
    27 #include <e32wins.h>
       
    28 #include <f32file.h>
       
    29 #include <hal.h>
       
    30 #include "sf_file_cache.h"
       
    31 #include "sf_cache_man.h"
       
    32 #include "sf_cache_client.h"
       
    33 #include "sf_file_cache_defs.h"
       
    34 
       
    35 
       
    36 // disables flushing of stale cachelines before each write
       
    37 #define LAZY_WRITE
       
    38 
       
    39 enum TFileCacheFault
       
    40 	{
       
    41 	ECacheSettingsInitFailed,
       
    42 	ECacheSettingsNotFound,
       
    43 	ECacheSettingGetFailed,
       
    44 	ECacheCodeFault,
       
    45 	ECacheBadOperationIndex,
       
    46 	ENoFileCache,
       
    47 	EFileAlreadyClosed,
       
    48 	EClosingDirtyFile,
       
    49 	ECompletingWriteWithDataRemaining,
       
    50 	EPosBeyondSize,
       
    51 	EMsgAlreadyCompleted,
       
    52 	EBadRetCode,
       
    53 	EInternalError,
       
    54 	ERequestUncancelled,
       
    55 	ELockAlreadyOpen,
       
    56 	EBadSegmentCount,
       
    57 	EReNewingOpenCache,
       
    58 	EClosingUnNamedFile,
       
    59 	EFileNameAlreadyOwned,
       
    60 	EClosedFileHasNoName,
       
    61 	EReOpeningUnNamedFile,
       
    62 	EStartingDirtyAWrongStage,
       
    63 	EUnexpectedReNewLFailure,
       
    64 	EDirtyDataOwnerNull,
       
    65 	EFlushingWithSessionNull,
       
    66 	};
       
    67 
       
    68 
       
    69 LOCAL_C void Fault(TFileCacheFault aFault)
       
    70 //
       
    71 // Report a fault in the file cache
       
    72 //
       
    73 	{
       
    74 	User::Panic(_L("FSFILECACHE"), aFault);
       
    75 	}
       
    76 
       
    77 const TInt KMinSequentialReadsBeforeReadAhead = 3;
       
    78 
       
    79 //************************************
       
    80 // CFileCache
       
    81 //************************************
       
    82 
       
    83 inline TBool ReadCachingEnabled(CFileShare& aShare)
       
    84 	{
       
    85 	TUint mode = aShare.iMode;
       
    86 	
       
    87 	TInt fileCacheFlags = TFileCacheSettings::Flags(aShare.File().Drive().DriveNumber());
       
    88 
       
    89 	if (((mode & EFileReadBuffered) && (fileCacheFlags & (EFileCacheReadEnabled | EFileCacheReadOn))) ||
       
    90 		(((mode & EFileReadDirectIO) == 0) && (fileCacheFlags & EFileCacheReadOn)))
       
    91 		{
       
    92 		return ETrue;
       
    93 		}
       
    94 	else
       
    95 		{
       
    96 		return EFalse;
       
    97 		}
       
    98 	}
       
    99 
       
   100 inline TBool WriteCachingEnabled(CFileShare& aShare)
       
   101 	{
       
   102 	TUint mode = aShare.iMode;
       
   103 	
       
   104 	TInt fileCacheFlags = TFileCacheSettings::Flags(aShare.File().Drive().DriveNumber());
       
   105 
       
   106 	if ((mode & EFileWrite) == 0)
       
   107 		{
       
   108 		return EFalse;
       
   109 		}
       
   110 	else if (((mode & EFileWriteBuffered) && (fileCacheFlags & (EFileCacheWriteEnabled | EFileCacheWriteOn))) ||
       
   111 		(((mode & EFileWriteDirectIO) == 0) && (fileCacheFlags & EFileCacheWriteOn)))
       
   112 		{
       
   113 		return ETrue;
       
   114 		}
       
   115 	else
       
   116 		{
       
   117 		return EFalse;
       
   118 		}
       
   119 	}
       
   120 
       
   121 void CFileCache::SetFileCacheFlags(CFileShare& aShare)
       
   122 	{
       
   123 	TInt fileCacheFlags = TFileCacheSettings::Flags(iDriveNum);
       
   124 
       
   125 	TUint& mode = aShare.iMode;
       
   126 
       
   127 	// enable/disable read ahead
       
   128 	if (((mode & EFileReadAheadOn) && (fileCacheFlags & (EFileCacheReadAheadEnabled | EFileCacheReadAheadOn))) ||
       
   129 		 (((mode & EFileReadAheadOff) == 0) && (fileCacheFlags & EFileCacheReadAheadOn)))
       
   130 		{
       
   131 		__CACHE_PRINT(_L("CACHEFILE: EFileCacheReadAhead ENABLED"));
       
   132 		mode|= EFileReadAheadOn;
       
   133 		}
       
   134 	else
       
   135 		{
       
   136 		__CACHE_PRINT(_L("CACHEFILE: EFileCacheReadAhead disabled"));
       
   137 		mode&= ~EFileReadAheadOn;
       
   138 		}
       
   139 
       
   140 	// enable/disable read caching 
       
   141 	if (ReadCachingEnabled(aShare))
       
   142 		{
       
   143 		__CACHE_PRINT(_L("CACHEFILE: EFileCacheRead ENABLED"));
       
   144 		mode|= EFileReadBuffered;
       
   145 		}
       
   146 	else
       
   147 		{
       
   148 		__CACHE_PRINT(_L("CACHEFILE: EFileCacheRead disabled"));
       
   149 		// if read caching is off, turn off read-ahead too
       
   150 		mode&= ~(EFileReadBuffered | EFileReadAheadOn);
       
   151 		}
       
   152 
       
   153 	// enable/disable write caching 
       
   154 	if (WriteCachingEnabled(aShare))
       
   155 		{
       
   156 		__CACHE_PRINT(_L("CACHEFILE: EFileCacheWrite ENABLED"));
       
   157 		mode|= EFileWriteBuffered;
       
   158 		}
       
   159 	else
       
   160 		{
       
   161 		__CACHE_PRINT(_L("CACHEFILE: EFileCacheWrite disabled"));
       
   162 		mode&= ~EFileWriteBuffered;
       
   163 		}
       
   164 
       
   165 	}
       
   166 
       
   167 void CFileCache::ConstructL(CFileShare& aShare)
       
   168 	{
       
   169 	iDrive = &aShare.File().Drive();
       
   170 	iDriveNum = iDrive->DriveNumber();
       
   171 	iMount=&iDrive->CurrentMount();
       
   172 
       
   173 	DoInitL(iDriveNum);	
       
   174 	
       
   175 
       
   176 	CCacheManager* manager = CCacheManagerFactory::CacheManager();
       
   177 
       
   178 	iCacheClient = manager->CreateClientL();
       
   179 	manager->RegisterClient(*iCacheClient);
       
   180 	
       
   181 
       
   182 	TInt segmentSize = SegmentSize();
       
   183 	TInt segmentSizeMask = I64LOW(SegmentSizeMask());
       
   184 	// Get file cache size
       
   185 	iCacheSize			= TFileCacheSettings::CacheSize(iDriveNum);
       
   186 	// must be at least one segment
       
   187 	iCacheSize			= Max(iCacheSize, segmentSize);
       
   188 	// round up to nearest whole segment
       
   189 	iCacheSize			= (iCacheSize + segmentSize - 1) & segmentSizeMask;
       
   190 
       
   191 	// Get max read-ahead length
       
   192 	iMaxReadAheadLen	= TFileCacheSettings::MaxReadAheadLen(iDriveNum);
       
   193 	// must be at least one segment
       
   194 	iMaxReadAheadLen	= Max(iMaxReadAheadLen, segmentSize);
       
   195 	// round up to nearest whole segment
       
   196 	iMaxReadAheadLen	= (iMaxReadAheadLen + segmentSize - 1) & segmentSizeMask;
       
   197 	// read-ahead should not be greater than the cache size minus one segment
       
   198 	iMaxReadAheadLen	= Min(iMaxReadAheadLen, iCacheSize - segmentSize);
       
   199 	// ... or greater than one cacheline (128K should be enough !)
       
   200 	iMaxReadAheadLen	= Min(iMaxReadAheadLen, iCacheClient->CacheLineSize());
       
   201 
       
   202 	iFileCacheReadAsync = TFileCacheSettings::FileCacheReadAsync(iDriveNum);
       
   203 
       
   204 	iClosedFileKeepAliveTime	= TFileCacheSettings::ClosedFileKeepAliveTime(iDriveNum);
       
   205 	iDirtyDataFlushTime			= TFileCacheSettings::DirtyDataFlushTime(iDriveNum);
       
   206 
       
   207 	// Calculate max number of segments to cache
       
   208 	TInt maxSegmentsToCache = iCacheSize >> SegmentSizeLog2();
       
   209 
       
   210 	__CACHE_PRINT1(_L("CACHEFILE: maxSegmentsToCache %d"), maxSegmentsToCache);
       
   211 
       
   212 	iCacheClient->SetMaxSegments(maxSegmentsToCache);
       
   213 
       
   214 	CFileCache* fileCache = ReNewL(aShare);
       
   215 	__ASSERT_ALWAYS(fileCache != NULL, Fault(EUnexpectedReNewLFailure));
       
   216 	}
       
   217 
       
   218 CFileCache* CFileCache::NewL(CFileShare& aShare)
       
   219 	{
       
   220 	__CACHE_PRINT(_L("CACHEFILE: CFileCache::NewL()"));
       
   221 
       
   222 	CFileCB* file = &aShare.File();
       
   223 	if ((CCacheManagerFactory::CacheManager() == NULL) ||
       
   224 		(!ReadCachingEnabled(aShare) && !WriteCachingEnabled(aShare)) ||
       
   225 		(FsThreadManager::IsDriveSync(file->Drive().DriveNumber(),EFalse)))
       
   226 		return NULL;
       
   227 
       
   228 	CFileCache* fileCache = new(ELeave) CFileCache();
       
   229 	CleanupClosePushL(*fileCache);
       
   230 	fileCache->ConstructL(aShare);
       
   231 	CleanupStack::Pop(1, fileCache);
       
   232 	return fileCache;
       
   233 	}
       
   234 
       
   235 CFileCache* CFileCache::ReNewL(CFileShare& aShare)
       
   236 	{
       
   237 	__CACHE_PRINT(_L("CACHEFILE: CFileCache::ReNewL()"));
       
   238 
       
   239 	// check not already open i.e. attached to a CFileCB
       
   240 	__ASSERT_DEBUG(iFileCB == NULL, Fault(EReNewingOpenCache));
       
   241 
       
   242 	// make sure the drive thread exists (the mount may have been mounted
       
   243 	// synchronously since the drive was last open)
       
   244 	const TInt r = FsThreadManager::GetDriveThread(iDriveNum, &iDriveThread);
       
   245 	if ((r!= KErrNone) || !iDriveThread)
       
   246 		return NULL;
       
   247 
       
   248 	// if re-opening in DirectIo mode, destroy the file cache 
       
   249 	if (!ReadCachingEnabled(aShare) && !WriteCachingEnabled(aShare))
       
   250 		{
       
   251 		Close();
       
   252 		return NULL;
       
   253 		}
       
   254 
       
   255 	SetFileCacheFlags(aShare);
       
   256 
       
   257 	// assign ownership of this object to aFileCB before any leaves occur
       
   258 	iFileCB = &aShare.File();
       
   259 	iFileCB->iBody->iFileCache = this;
       
   260 	
       
   261 	__ASSERT_DEBUG(iLock.Handle() == KNullHandle, Fault(ELockAlreadyOpen));
       
   262 	User::LeaveIfError(iLock.CreateLocal());
       
   263 
       
   264 	// delete the file name to save heap space (it's only needed 
       
   265 	// while the file is on the closed queue so that it can be matched)
       
   266 	delete iFileNameF;
       
   267 	iFileNameF = NULL;
       
   268 
       
   269 	return this;
       
   270 	}
       
   271 
       
   272 void CFileCache::Init(CFileShare& aShare)
       
   273 	{
       
   274 	SetFileCacheFlags(aShare);
       
   275 	}
       
   276 
       
   277 CFileCache::CFileCache() : 
       
   278 	iClosedTimer(ClosedTimerEvent, this),
       
   279 	iDirtyTimer(DirtyTimerEvent, this)
       
   280 	{
       
   281 	}
       
   282 
       
   283 CFileCache::~CFileCache()
       
   284 	{
       
   285 	iLock.Close();
       
   286 
       
   287 	// stop the owning CFileCB from pointing to an about-to-be deleted object
       
   288 	if (iFileCB && iFileCB->iBody)
       
   289 		iFileCB->iBody->iFileCache = NULL;
       
   290 
       
   291 	CCacheManagerFactory::CacheManager()->DeregisterClient(*iCacheClient);
       
   292 
       
   293 	delete iCacheClient;
       
   294 
       
   295 	delete iFileNameF;
       
   296 	}
       
   297 
       
   298 
       
   299 TInt CFileCache::ClosedTimerEvent(TAny* aFileCache)
       
   300 	{
       
   301 	TClosedFileUtils::Remove((CFileCache*) aFileCache);
       
   302 	return KErrNone;
       
   303 	}
       
   304 
       
   305 TInt CFileCache::DirtyTimerEvent(TAny* aFileCache)
       
   306 	{
       
   307 	// Cannot report errors here
       
   308 	// coverity [unchecked_value]
       
   309 	(void)((CFileCache*) aFileCache)->FlushDirty();
       
   310 
       
   311 	return KErrNone;
       
   312 	}
       
   313 
       
   314 
       
   315 
       
   316 void CFileCache::Close()
       
   317 	{
       
   318 	__CACHE_PRINT1(_L("CFileCache::Close() 0x%x"),this);
       
   319 
       
   320 	TInt r = KErrNone;
       
   321 
       
   322 #ifdef _DEBUG
       
   323 	if (iCacheClient)	// NB Object may not have been fully constructed
       
   324 		{
       
   325 		TInt64  pos;
       
   326 		TUint8* addr;
       
   327 		__ASSERT_DEBUG(((iCacheClient->FindFirstDirtySegment(pos, addr)) == 0), Fault(EClosingDirtyFile));
       
   328 		__ASSERT_DEBUG(!iDirtyDataOwner, Fault(EClosingDirtyFile));
       
   329 		}
       
   330 #endif
       
   331 		
       
   332 		
       
   333 	__CACHE_PRINT2(_L("CFileCache::Close() iFileCB %08X IsClosed %d"), 
       
   334 		iFileCB, TClosedFileUtils::IsClosed(this));
       
   335 	// if not already closed move to closed file queue
       
   336 	if (iFileCB != NULL && 
       
   337 		!iFileCB->DeleteOnClose() &&
       
   338 		!TClosedFileUtils::IsClosed(this) && 
       
   339 		IsDriveThread())
       
   340 		{
       
   341 		// add to ClosedFiles container
       
   342 		__CACHE_PRINT1(_L("CLOSEDFILES: Adding %S\n"), &iFileCB->FileNameF() );
       
   343 		TRAP(r, TClosedFileUtils::AddL(this, ETrue));
       
   344 
       
   345 		// Acquire ownership of the CFileCB's file name
       
   346 		__ASSERT_DEBUG(iFileCB->iFileNameF, Fault(EClosingUnNamedFile));
       
   347 		__ASSERT_DEBUG(iFileNameF == NULL, Fault(EFileNameAlreadyOwned));
       
   348 		iFileNameF = iFileCB->iFileNameF;
       
   349 		iNameHash = iFileCB->iNameHash;
       
   350 		iFileCB->iFileNameF = NULL;
       
   351 
       
   352 		// remove pointer to owning CFileCB as this is called from CFileCB's destructor
       
   353 		iFileCB = NULL;
       
   354 
       
   355 		// Successfully moved file !
       
   356 		if (r == KErrNone)
       
   357 			{
       
   358 			// close the RFastLock object here to prevent OOM kernel tests from failing
       
   359 			iLock.Close();
       
   360 			return;
       
   361 			}
       
   362 		}
       
   363 
       
   364 	iClosedTimer.Stop();
       
   365 
       
   366 	// if already closed, close for good. N.B. CFsObject::DoClose() will 
       
   367 	// cause it to be removed from the ClosedFiles container
       
   368 	CFsDispatchObject::Close();
       
   369 	}
       
   370 
       
   371 
       
   372 CMountCB& CFileCache::Mount() const
       
   373 	{
       
   374 	return *iMount;
       
   375 	}
       
   376 
       
   377 CFileCB* CFileCache::FileCB()
       
   378 	{
       
   379 	return iFileCB;
       
   380 	}
       
   381 
       
   382 
       
   383 
       
   384 TInt CFileCache::SegmentSize() const
       
   385 	{
       
   386 	return iCacheClient->SegmentSize();
       
   387 	}
       
   388 
       
   389 TInt CFileCache::SegmentSizeLog2() const
       
   390 	{
       
   391 	return iCacheClient->SegmentSizeLog2();
       
   392 	}
       
   393 
       
   394 TInt64 CFileCache::SegmentSizeMask() const
       
   395 	{
       
   396 	return iCacheClient->SegmentSizeMask();
       
   397 	}
       
   398 
       
   399 /**
       
   400 Sets the cached file size  
       
   401 
       
   402 @param aSize The size of the file.
       
   403 */  
       
   404 void CFileCache::SetSize64(TInt64 aSize)
       
   405 	{
       
   406 	__e32_atomic_store_ord64(&iSize64, aSize);
       
   407 	}
       
   408 
       
   409 TDrive& CFileCache::Drive() const
       
   410 	{
       
   411 	return *iDrive;
       
   412 	}
       
   413 
       
   414 TUint32 CFileCache::NameHash() const
       
   415 	{
       
   416 	return(iNameHash);
       
   417 	}
       
   418 
       
   419 HBufC& CFileCache::FileNameF() const
       
   420 	{
       
   421 	__ASSERT_DEBUG(iFileNameF, Fault(EClosedFileHasNoName));
       
   422 	return(*iFileNameF);
       
   423 	}
       
   424 
       
   425 
       
   426 
       
   427 
       
   428 TBool CFileCache::IsDriveThread()
       
   429 	{
       
   430 	return FsThreadManager::IsDriveThread(iDriveNum, EFalse);
       
   431 	}
       
   432 
       
   433 
       
   434 void CFileCache::ResetReadAhead()
       
   435 	{
       
   436 //	iSequentialReads = 0;
       
   437 	iReadAheadLen = 0;
       
   438 	iReadAheadPos = 0;
       
   439 	}
       
   440 
       
   441 /**
       
   442 ReadAhead() - 
       
   443 dispatches a new message to fill the read cache if 
       
   444 (ReadAheadPos - ShareReadPos) <= (ReadAheadLen)
       
   445 
       
   446 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
       
   447 		^				^
       
   448 		|----- ReadAheadLen ----------------->
       
   449 		|				| 
       
   450   ShareReadPos		ReadAheadPos	
       
   451 
       
   452 Every successful read-ahead doubles ReadAheadLen until it reaches the maximum 
       
   453 (KDefaultFileCacheMaxReadAheadLen). 
       
   454 */
       
   455 void CFileCache::ReadAhead(CFsMessageRequest& aMsgRequest, TUint aMode)
       
   456 	{
       
   457 	if (!(aMode & EFileReadAheadOn) ||
       
   458 		(iSequentialReads < KMinSequentialReadsBeforeReadAhead) ||
       
   459 		iMaxReadAheadLen == 0)
       
   460 		return;
       
   461 
       
   462 
       
   463 	TInt segmentSize = SegmentSize();
       
   464 	TInt64 segmentSizeMask = SegmentSizeMask();
       
   465 	
       
   466 	// if the read-ahead pos has been reset to zero, then the read-ahead 
       
   467 	// position and length must be re-calculated
       
   468 
       
   469 	TBool resetting = (iReadAheadPos == 0)?(TBool)ETrue:(TBool)EFalse;
       
   470 	if (resetting)
       
   471 		{
       
   472 		iReadAheadPos = (iLastReadPos + segmentSize - 1) & segmentSizeMask;	
       
   473 
       
   474 		// ensure read ahead len at least as big as last read
       
   475 		iReadAheadLen = Max(iReadAheadLen, iLastReadLen);	
       
   476 			
       
   477 		// round up to a segment size
       
   478 		iReadAheadLen = (iReadAheadLen + segmentSize - 1) & (TInt) segmentSizeMask;
       
   479 
       
   480 		// ensure read ahead len at least as big as 1 segments 
       
   481 		iReadAheadLen = Max(iReadAheadLen, segmentSize);	
       
   482 			
       
   483 		// ensure read ahead len not greater than the maximum read ahead len
       
   484 		iReadAheadLen = Min(iReadAheadLen, iMaxReadAheadLen);
       
   485 
       
   486 		iReadAheadRequest = NULL;
       
   487 		}
       
   488 
       
   489 	TInt bytesBuffered = (TInt) (iReadAheadPos - iLastReadPos);
       
   490 	TInt bytesNotBuffered = iMaxReadAheadLen - bytesBuffered;
       
   491 
       
   492 
       
   493 	// if read-ahead buffer len > current read-ahead len OR
       
   494 	// read-ahead buffer is more than half full, do nothing
       
   495 	if ((iReadAheadRequest) ||
       
   496 		(bytesBuffered > iReadAheadLen) || 
       
   497 		(bytesBuffered > (iMaxReadAheadLen>>1)) ||
       
   498 		(iReadAheadPos >= iSize64))
       
   499 		{
       
   500 		return;
       
   501 		}
       
   502 
       
   503 	// double the read-ahead length - unless this is the first
       
   504 	if (!resetting)
       
   505 		iReadAheadLen<<= 1;
       
   506 
       
   507 	// ensure read ahead len not greater than the free space available in buffer
       
   508 	iReadAheadLen = Min(iReadAheadLen, bytesNotBuffered);
       
   509 
       
   510 	// round up to a segment size
       
   511 	iReadAheadLen = (iReadAheadLen + segmentSize - 1) & (TInt) segmentSizeMask;
       
   512 
       
   513 #if defined (_DEBUG_READ_AHEAD)
       
   514 	TInt64 oldReadAheadPos = iReadAheadPos;
       
   515 #endif
       
   516 
       
   517 	DoReadAhead(aMsgRequest, aMode);
       
   518 
       
   519 
       
   520 //	RDebug::Print(_L("Buffered: old %d new %d"), bytesBuffered, (iReadAheadPos == 0)?bytesBuffered:iReadAheadPos - iLastReadPos);
       
   521 
       
   522 #if defined (_DEBUG_READ_AHEAD)
       
   523 RDebug::Print(_L("Buffered: old %d new %d iLastReadPos %d ReadAheadPos old %d new %d iReadAheadLen %d"), 
       
   524 	bytesBuffered, 
       
   525 	(TInt) ((iReadAheadPos == 0)?bytesBuffered:iReadAheadPos - iLastReadPos),
       
   526 	I64LOW(iLastReadPos),
       
   527 	I64LOW(oldReadAheadPos),
       
   528 	I64LOW(iReadAheadPos),
       
   529 	iReadAheadLen);
       
   530 #endif	// (_DEBUG_READ_AHEAD)
       
   531 
       
   532 	
       
   533 	return;
       
   534 	}
       
   535 
       
   536 
       
   537 void CFileCache::DoReadAhead(CFsMessageRequest& aMsgRequest, TUint aMode)
       
   538 	{
       
   539 
       
   540 
       
   541 	if (iCacheClient->FindSegment(iReadAheadPos) != NULL)
       
   542 		{
       
   543 		// if read ahead pos is already cached, then synchronous reads have caught up,
       
   544 		// so reset read ahead pos.
       
   545 #if defined (_DEBUG_READ_AHEAD)
       
   546 		RDebug::Print(_L("ReadAhead: pos %d already cached"), I64LOW(iReadAheadPos));
       
   547 #endif
       
   548 		iReadAheadPos = 0;
       
   549 		return;
       
   550 		}
       
   551 
       
   552 
       
   553 	CFsClientMessageRequest* newRequest = NULL;
       
   554 	TInt r = AllocateRequest(newRequest, EFalse, aMsgRequest.Session());
       
   555 	if (r != KErrNone)
       
   556 		return;
       
   557 
       
   558 	r = newRequest->PushOperation(
       
   559 		iReadAheadPos, 
       
   560 		iReadAheadLen,
       
   561 		(TUint8*) NULL, 
       
   562 		0,						// aOffset 
       
   563 		NULL);					// aCallback
       
   564 	if (r != KErrNone)
       
   565 		{
       
   566 		newRequest->Free();
       
   567 		newRequest = NULL;
       
   568 		return;
       
   569 		}
       
   570 
       
   571 	__CACHE_PRINT2(_L("TFsFileRead: ReadAhead pos %ld len %d"), newRequest->CurrentOperation().iReadWriteArgs.iPos, newRequest->CurrentOperation().iReadWriteArgs.iTotalLength);
       
   572 
       
   573 //	RDebug::Print(_L("ReadH:\tpos %d\tlen %d"), I64LOW(newRequest->CurrentOperation().iReadWriteArgs.iPos), newRequest->CurrentOperation().iReadWriteArgs.iTotalLength);
       
   574 
       
   575 	r = ReadBuffered(*newRequest, aMode);
       
   576 	if (r != CFsRequest::EReqActionContinue)
       
   577 		{
       
   578 		// if read ahead pos is already cached, then synchronous reads have caught up,
       
   579 		// so reset read ahead pos.
       
   580 		if (r == CFsRequest::EReqActionComplete)
       
   581 			{
       
   582 #if defined (_DEBUG_READ_AHEAD)
       
   583 			RDebug::Print(_L("ReadAhead pos %d ALREADY DONE !!!"), I64LOW(iReadAheadPos));
       
   584 #endif
       
   585 			iReadAheadPos = 0;
       
   586 			}
       
   587 
       
   588 		newRequest->PopOperation();
       
   589 		newRequest->Free();
       
   590 		newRequest = NULL;
       
   591 		return;
       
   592 		}
       
   593 
       
   594 	iReadAheadPos = iReadAheadPos + iReadAheadLen;
       
   595 	iReadAheadRequest = newRequest;
       
   596 
       
   597 #if defined (_DEBUG_READ_AHEAD)
       
   598 	RDebug::Print(_L("Dispatching ReadAhead with %s priority"), iFileCacheReadAsync?_S16("HIGH"):_S16("LOW"));
       
   599 #endif
       
   600 	// If if media driver reads are synchronous (i.e. not interrupt driven) dispatch read-ahead 
       
   601 	// with low priority  so that it doesn't prevent client thread from running
       
   602 	newRequest->Dispatch(
       
   603 		EFalse,					// don't init
       
   604 		iFileCacheReadAsync?(TBool)EFalse:(TBool)ETrue, 
       
   605 		EFalse);				// dispatch to back
       
   606 	}
       
   607 
       
   608 TInt CFileCache::CompleteRead(CFsRequest* aRequest)
       
   609 	{
       
   610 	CFsMessageRequest& msgRequest = *(CFsMessageRequest*) aRequest;
       
   611 
       
   612 	__ASSERT_DEBUG(msgRequest.CurrentOperationPtr() != NULL, Fault(ECacheBadOperationIndex));
       
   613 
       
   614 	CFileShare* share;
       
   615 	CFileCB* file;
       
   616 	GetFileFromScratch(aRequest, share, file);
       
   617 	CFileCache* fileCache = file->FileCache();
       
   618 
       
   619 	__ASSERT_DEBUG(fileCache != NULL, Fault(ENoFileCache));
       
   620 #ifdef _DEBUG
       
   621 	TInt cancelling = (msgRequest.LastError() == KErrCancel)?(TBool)ETrue:(TBool)EFalse;
       
   622 #endif
       
   623 
       
   624 	TUint mode = share?share->iMode : EFileReadBuffered;
       
   625 	TInt r = fileCache->ReadBuffered(msgRequest, mode);
       
   626 
       
   627 	// if this request has been cancelled we mustn't dispatch it again - 
       
   628 	// we still need to call state machine however so that any locked segments can be unlocked
       
   629 
       
   630 
       
   631 	TInt lastError = msgRequest.LastError();
       
   632 
       
   633 #ifdef _DEBUG
       
   634 	__ASSERT_DEBUG(!cancelling || msgRequest.LastError() == KErrCancel, Fault(ERequestUncancelled));
       
   635 #endif
       
   636 
       
   637 	if (lastError == KErrCancel)
       
   638 		r = CFsRequest::EReqActionComplete;
       
   639 
       
   640 	return r;
       
   641 	}
       
   642 
       
   643 /**
       
   644 ReadBuffered - attempts to read from cache. 
       
   645 Called from TFsFileRead::Initialise() and CFileCache::CompleteRead()
       
   646 
       
   647 @return CFsRequest::EReqActionComplete if request entirely satisfied
       
   648 		CFsRequest::EReqActionContinue if request not yet complete. 
       
   649 			Results in transition from :
       
   650 				TFsFileRead::PostInitialise() to TFsFileRead::DoRequestL() or
       
   651 				CFileCache::CompleteRead() to TFsFileRead::DoRequestL()
       
   652 		CFsRequest::EReqActionBusy if filecache is "busy"
       
   653 */
       
   654 TInt CFileCache::ReadBuffered(CFsMessageRequest& aMsgRequest, TUint aMode)
       
   655 	{
       
   656 	iLock.Wait();
       
   657 
       
   658 	CFsClientMessageRequest* newRequest = NULL;
       
   659 
       
   660 	__CACHE_PRINT2(_L("CFileCache::ReadBuffered()  pos %d, len %d"), I64LOW(aMsgRequest.CurrentOperation().iReadWriteArgs.iPos), aMsgRequest.CurrentOperation().iReadWriteArgs.iTotalLength);
       
   661 	TInt r = DoReadBuffered(aMsgRequest, aMode, newRequest);
       
   662 	
       
   663 	iLock.Signal();
       
   664 
       
   665 	if (newRequest)
       
   666 		newRequest->Dispatch();
       
   667 
       
   668 	return r;
       
   669 	}
       
   670 
       
   671 
       
   672 void CFileCache::UpdateSharePosition(CFsMessageRequest& aMsgRequest, TMsgOperation& aCurrentOperation)
       
   673 	{
       
   674 	// update the file share's position if this request came from client
       
   675 	if (aCurrentOperation.iClientRequest)
       
   676 		{
       
   677 		CFileShare* share = (CFileShare*)aMsgRequest.ScratchValue();
       
   678 		if (aMsgRequest.LastError() == KErrNone)
       
   679 			{
       
   680 			__e32_atomic_store_ord64(&share->iPos, aCurrentOperation.iReadWriteArgs.iPos);
       
   681 			}
       
   682 		}
       
   683 	}
       
   684 
       
   685 TInt CFileCache::DoReadBuffered(CFsMessageRequest& aMsgRequest, TUint aMode, CFsClientMessageRequest*& aNewRequest)
       
   686 	{
       
   687 	enum states
       
   688 		{
       
   689 		EStBegin=0,
       
   690 		EStReadFromCache,
       
   691 		EStReadFromDiskComplete,
       
   692 		EStCopyToClient,
       
   693 		EStReStart,
       
   694 		EStEnd
       
   695 		};
       
   696 
       
   697 
       
   698 	TInt retCode = CFsRequest::EReqActionComplete;
       
   699 	TInt& lastError = aMsgRequest.LastError();
       
   700 	TBool driveThread = IsDriveThread();
       
   701 
       
   702 	// temp storage for transition between EStReadFromCache / EStReadFromDiskComplete and EStCopyToClient
       
   703 	TInt readLen = 0;
       
   704 	TUint8* addr = NULL;
       
   705 	TInt64 segmentStartPos = 0;
       
   706 
       
   707 	TInt segmentSize = SegmentSize();
       
   708 	TInt segmentSizeLog2 = SegmentSizeLog2();
       
   709 	TInt64 segmentSizeMask = SegmentSizeMask();
       
   710 
       
   711 	for(;;)
       
   712 		{
       
   713 		TMsgOperation* currentOperation = &aMsgRequest.CurrentOperation();
       
   714 		TInt64& currentPos = currentOperation->iReadWriteArgs.iPos;
       
   715 		TInt& totalLen = currentOperation->iReadWriteArgs.iTotalLength;
       
   716 		TInt& currentOffset = currentOperation->iReadWriteArgs.iOffset;
       
   717 		TBool readAhead = (currentOperation->iReadWriteArgs.iData == NULL)?(TBool)ETrue:(TBool)EFalse;
       
   718 
       
   719 		switch(currentOperation->iState)
       
   720 			{
       
   721 			case EStBegin:
       
   722 
       
   723 
       
   724 				// if EFileReadDirectIO, flush write cache and read direct from  disk
       
   725 				if (!(aMode & EFileReadBuffered))
       
   726 					{
       
   727 					iLock.Signal();
       
   728 					TInt r = FlushDirty(&aMsgRequest);
       
   729 					iLock.Wait();
       
   730 					if (r == CFsRequest::EReqActionBusy)
       
   731 						return CFsRequest::EReqActionBusy;
       
   732 					return CFsRequest::EReqActionContinue;	// read uncached
       
   733 					}
       
   734 
       
   735 				if (currentPos > iSize64)
       
   736 					currentPos = iSize64;
       
   737 
       
   738 				currentOperation->iState = EStReadFromCache;
       
   739 
       
   740 				// count the number of sequential reads for read-ahead
       
   741 				if (currentOperation->iClientRequest)
       
   742 					{
       
   743 					if (currentPos == iLastReadPos)
       
   744 						{
       
   745 						iSequentialReads++;
       
   746 						}
       
   747 					else
       
   748 						{
       
   749 						iSequentialReads = 0;
       
   750 						ResetReadAhead();
       
   751 						}
       
   752 					iLastReadPos = currentPos + totalLen;
       
   753 					iLastReadLen = totalLen;
       
   754 					}
       
   755 
       
   756 				
       
   757 				// fall into...
       
   758 
       
   759 			case EStReadFromCache:
       
   760 				{
       
   761 				// reading past end of file ?
       
   762 				if (currentPos + totalLen > iSize64)
       
   763 					totalLen = (TInt) (iSize64 - currentPos);
       
   764 				
       
   765 
       
   766 				if (totalLen == 0 || lastError != KErrNone)
       
   767 					{
       
   768 					currentOperation->iState = EStEnd;
       
   769 					break;
       
   770 					}
       
   771 
       
   772 				segmentStartPos = currentPos & segmentSizeMask;
       
   773 
       
   774 			
       
   775 				TInt64 endPos = currentPos + totalLen;
       
   776 				TUint maxLenToRead = (TUint)Min((TInt64)(iCacheClient->CacheLineSize()), (endPos - segmentStartPos));
       
   777 				TInt maxSegments = (maxLenToRead + segmentSize - 1) >> segmentSizeLog2;
       
   778 
       
   779 
       
   780 				TInt segmentCount = maxSegments;
       
   781 				TInt filledSegmentCount;
       
   782 				TInt lockError;
       
   783 				addr = iCacheClient->FindAndLockSegments(segmentStartPos, segmentCount, filledSegmentCount, lockError, EFalse);
       
   784 
       
   785 
       
   786 #if defined (_DEBUG_READ_AHEAD)
       
   787 				if (addr && readAhead)
       
   788 					RDebug::Print(_L("READAHEAD CACHELINE ALREADY EXISTS POS %d"), I64LOW(segmentStartPos));
       
   789 #endif
       
   790 
       
   791 				// if cacheline contains filled and empty segments, deal with these seperately
       
   792 				// to simplify the code.....
       
   793 				if (filledSegmentCount > 0 && segmentCount > filledSegmentCount)
       
   794 					{
       
   795 					segmentCount = Min(segmentCount, filledSegmentCount);
       
   796 					}
       
   797 
       
   798 				if (lockError == KErrInUse)
       
   799 					{
       
   800 					// cacheline in use (by other thread):
       
   801 					// if this is a read-ahead, abandon it
       
   802 					// otherwise re-post the request
       
   803 					if (readAhead)
       
   804 						{
       
   805 						totalLen = 0;
       
   806 						retCode = CFsRequest::EReqActionComplete;
       
   807 						currentOperation->iState = EStEnd;
       
   808 						}
       
   809 					else
       
   810 						{
       
   811 #if defined (_DEBUG_READ_AHEAD)
       
   812 						RDebug::Print(_L("READC CACHELINE BUSY POS %d"), I64LOW(segmentStartPos));
       
   813 #endif
       
   814 
       
   815 						return CFsRequest::EReqActionBusy;
       
   816 						}
       
   817 					break;
       
   818 					}
       
   819 
       
   820 				// if not found, try to allocate as many contiguous segments 
       
   821 				// as possible so that the number of reads issued to the media 
       
   822 				// driver is kept to a minumum
       
   823 
       
   824 				if (addr == NULL)	// not found ?
       
   825 					{
       
   826 					// if read len > size of the cache, don't read excess 
       
   827 					// through the cache as this is wasteful
       
   828 					if (totalLen > iCacheSize)
       
   829 						{
       
   830 						TInt len = totalLen - iCacheSize;
       
   831 						TInt r = aMsgRequest.PushOperation(
       
   832 							currentPos, len, 
       
   833 							(TDesC8*) currentOperation->iReadWriteArgs.iData, currentOffset,
       
   834 							CompleteRead, 
       
   835 							EStReadFromCache);
       
   836 						if (r != KErrNone)
       
   837 							{
       
   838 							currentOperation->iState = EStReStart;
       
   839 							break;
       
   840 							}
       
   841 
       
   842 						currentOffset+= len;
       
   843 						currentPos+= len;
       
   844 						totalLen-= len;
       
   845 						return CFsRequest::EReqActionContinue;
       
   846 						}
       
   847 
       
   848 					// if position not cached postpone Initialise() to drive thread
       
   849 					// as there may be a read ahead already in the queue
       
   850 
       
   851 					if (!driveThread && !readAhead)
       
   852 						{
       
   853 #if defined (_DEBUG_READ_AHEAD)
       
   854 						RDebug::Print(_L("*** POSTING READ TO DRIVE THREAD POS %d ***"), I64LOW(segmentStartPos));
       
   855 #endif
       
   856 						return CFsRequest::EReqActionBusy;
       
   857 						}
       
   858 
       
   859 					segmentCount = maxSegments;
       
   860 					addr = iCacheClient->AllocateAndLockSegments(segmentStartPos, segmentCount, EFalse, !readAhead);
       
   861 					if (addr == NULL)
       
   862 						{
       
   863 						__CACHE_PRINT(_L("AllocateSegment failed"));
       
   864 						currentOperation->iState = EStReStart;
       
   865 						break;
       
   866 						}
       
   867 					}
       
   868 
       
   869 				
       
   870 				readLen = segmentCount << segmentSizeLog2;
       
   871 				__ASSERT_DEBUG(iSize64 > segmentStartPos, Fault(EPosBeyondSize));
       
   872 				readLen = (TInt)Min((TInt64)readLen, (iSize64 - segmentStartPos));
       
   873 
       
   874 				if (iCacheClient->SegmentEmpty(segmentStartPos))
       
   875 					{
       
   876 					// store readLen & addr in scratch area
       
   877 					currentOperation->iScratchValue0 = addr;						
       
   878 					currentOperation->iScratchValue1 = (TAny*) readLen;						
       
   879 
       
   880 					// read into cache segment(s)
       
   881 					__CACHE_PRINT2(_L("CACHEFILE: read pos %ld, readLen %d"), segmentStartPos, readLen);
       
   882 					TInt r = aMsgRequest.PushOperation(
       
   883 						segmentStartPos, readLen, addr, 0,
       
   884 						CompleteRead, 
       
   885 						EStReadFromDiskComplete);
       
   886 					if (r != KErrNone)
       
   887 						{
       
   888 						iCacheClient->UnlockSegments(segmentStartPos);
       
   889 						currentOperation->iState = EStReStart;
       
   890 						break;
       
   891 						}
       
   892 
       
   893 					return CFsRequest::EReqActionContinue;
       
   894 					}
       
   895 				else
       
   896 					{
       
   897 					currentOperation->iState = EStCopyToClient;
       
   898 					}
       
   899 				}
       
   900 				// fall into ...
       
   901 			
       
   902 			case EStCopyToClient:
       
   903 				{
       
   904 				TInt segmentsLocked = (readLen + segmentSize - 1) >> segmentSizeLog2;
       
   905 
       
   906 				if (lastError == KErrNone)
       
   907 					{
       
   908 					// copy to user buffer
       
   909 					TInt offset = iCacheClient->CacheOffset(currentPos);	// offset into segment
       
   910 					TInt len = Min(readLen - offset, totalLen);
       
   911 					
       
   912 					// if addr is NULL then this is a read ahead request
       
   913 					if (currentOperation->iReadWriteArgs.iData != NULL)
       
   914 						{
       
   915 						if (currentOperation->iClientRequest)
       
   916 							{
       
   917 							__ASSERT_DEBUG (aMsgRequest.Message().Handle() != NULL, Fault(EMsgAlreadyCompleted));
       
   918 							TPtrC8 ptr(addr+offset, len);
       
   919 							lastError = aMsgRequest.Write(0, ptr, currentOffset);
       
   920 							}
       
   921 						else
       
   922 							{
       
   923 							memcpy(((TUint8*) currentOperation->iReadWriteArgs.iData) + currentOffset, addr+offset, len);
       
   924 							}
       
   925 						}
       
   926 					else
       
   927 						{
       
   928 						iReadAheadRequest = NULL;
       
   929 						}
       
   930 
       
   931 					currentOffset+= len;
       
   932 					currentPos+= len;
       
   933 					totalLen-= len;
       
   934 					}
       
   935 
       
   936 				if (lastError == KErrNone)
       
   937 					iCacheClient->MarkSegmentsAsFilled(segmentStartPos, segmentsLocked);
       
   938 				iCacheClient->UnlockSegments(segmentStartPos);
       
   939 
       
   940 				if (lastError != KErrNone)
       
   941 					{
       
   942 					retCode = CFsRequest::EReqActionComplete;
       
   943 					currentOperation->iState = EStEnd;
       
   944 					break;
       
   945 					}
       
   946 
       
   947 				if (totalLen > 0 && lastError == KErrNone)
       
   948 					{
       
   949 					currentOperation->iState = EStReadFromCache;
       
   950 					break;
       
   951 					}
       
   952 				currentOperation->iState = EStEnd;
       
   953 				}
       
   954 			// fall into ...
       
   955 			
       
   956 
       
   957 			case EStEnd:
       
   958 				// update the file share's position if this request came from client
       
   959 				UpdateSharePosition(aMsgRequest, *currentOperation);
       
   960 				return retCode;
       
   961 
       
   962 			case EStReadFromDiskComplete:
       
   963 				{
       
   964 				// restore readLen etc from scratch area
       
   965 				segmentStartPos = currentPos & segmentSizeMask;
       
   966 				addr = (TUint8*) currentOperation->iScratchValue0;
       
   967 				readLen = (TInt) currentOperation->iScratchValue1;
       
   968 				
       
   969 				aMsgRequest.CurrentOperation().iState = EStCopyToClient;
       
   970 				}
       
   971 				break;
       
   972 
       
   973 			case EStReStart:
       
   974 
       
   975 				// ignore the failure if this is a read-ahead
       
   976 				if (readAhead)
       
   977 					{
       
   978 					totalLen = 0;
       
   979 					retCode = CFsRequest::EReqActionComplete;
       
   980 					currentOperation->iState = EStEnd;
       
   981 					break;
       
   982 					}
       
   983 
       
   984 				// We need to flush all dirty data to disk in case this read overlaps
       
   985 				// with any dirty data already in the file cache
       
   986 				if (aMode & EFileWriteBuffered)
       
   987 					{
       
   988 					TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, ETrue);
       
   989 					if (r == CFsRequest::EReqActionBusy || r != CFsRequest::EReqActionComplete)
       
   990 						return r;
       
   991 					}
       
   992 
       
   993 				retCode = CFsRequest::EReqActionContinue;	// read uncached
       
   994 				currentOperation->iState = EStEnd;
       
   995 
       
   996 				/*
       
   997 				We're now going to by-pass the file cache.
       
   998 				If we've already written something to the client's buffer then, in the flexible
       
   999 				memory model, the KDesWrittenShift bit will be set and so the descriptor length will 
       
  1000 				updated in RMessageK::CallbackFunc() when the client thread runs. This (shorter) 
       
  1001 				length will overwrite the descriptor length written by the local media subsystem.
       
  1002 				To get round this problem, we set the descriptor length artificially by writing a 
       
  1003 				zero-length descriptor at the end of the client's buffer.
       
  1004 				*/
       
  1005 				if (currentOffset > 0 && currentOperation->iClientRequest)
       
  1006 					{
       
  1007 					__ASSERT_DEBUG (aMsgRequest.Message().Handle() != NULL, Fault(EMsgAlreadyCompleted));
       
  1008 					TPtrC8 ptr(NULL, 0);
       
  1009 					TInt r = aMsgRequest.Write(0, ptr, currentOffset+totalLen);
       
  1010 					__CACHE_PRINT3(_L("CFileCache::DoReadBuffered() failed at pos %lx offset %x totalLen %x\n"), currentPos, currentOffset, totalLen);
       
  1011 					__CACHE_PRINT2(_L("CFileCache::DoReadBuffered() writing zero bytes at offset %x r %d\n"), currentOffset + totalLen, r);
       
  1012 					if (r != KErrNone)
       
  1013 						retCode = r;
       
  1014 					}
       
  1015 
       
  1016 				aMsgRequest.ReStart();
       
  1017 				UpdateSharePosition(aMsgRequest, *currentOperation);
       
  1018 				return retCode;
       
  1019 
       
  1020 
       
  1021 			};
       
  1022 		}	// for (;;)
       
  1023 
       
  1024 	}
       
  1025 
       
  1026 
       
  1027 
       
  1028 /**
       
  1029 WriteBuffered - attempts to write to cache. 
       
  1030 Called from TFsFileRead::Initialise and TFsFileRead::DoRequestL
       
  1031 
       
  1032 @return CFsRequest::EReqActionComplete if request entirely satisfied
       
  1033 		CFsRequest::EReqActionContinue if request not yet complete
       
  1034 		CFsRequest::EReqActionBusy if filecache is busy
       
  1035 */
       
  1036 TInt CFileCache::WriteBuffered(CFsMessageRequest& aMsgRequest, TUint aMode)
       
  1037 	{
       
  1038 	iLock.Wait();
       
  1039 
       
  1040 
       
  1041 	CFsClientMessageRequest* newRequest = NULL;
       
  1042 
       
  1043 	__CACHE_PRINT2(_L("CFileCache::WriteBuffered()  pos %d, len %d"), I64LOW(aMsgRequest.CurrentOperation().iReadWriteArgs.iPos), aMsgRequest.CurrentOperation().iReadWriteArgs.iTotalLength);
       
  1044 	TInt r = DoWriteBuffered(aMsgRequest, newRequest, aMode);
       
  1045 	
       
  1046 	iLock.Signal();
       
  1047 
       
  1048 	if (newRequest)
       
  1049 		newRequest->Dispatch();
       
  1050 
       
  1051 	// completion ?
       
  1052 	if (r == CFsRequest::EReqActionComplete)
       
  1053 		{
       
  1054 		TMsgOperation& currentOperation = aMsgRequest.CurrentOperation();
       
  1055 		
       
  1056 		__ASSERT_DEBUG(aMsgRequest.CurrentOperationPtr() != NULL, Fault(ECacheBadOperationIndex));
       
  1057 
       
  1058 		TFsFileWrite::CommonEnd(&aMsgRequest, r, iInitialSize, iSize64, currentOperation.iReadWriteArgs.iPos, EFalse);
       
  1059 		}
       
  1060 	
       
  1061 	return r;
       
  1062 	}
       
  1063 
       
  1064 
       
  1065 
       
  1066 
       
  1067 TInt CFileCache::CompleteWrite(CFsRequest* aRequest)
       
  1068 	{
       
  1069 	CFsMessageRequest& msgRequest = *(CFsMessageRequest*) aRequest;
       
  1070 
       
  1071 	CFileShare* share = (CFileShare*)aRequest->ScratchValue();
       
  1072 	CFileCB& file = share->File();
       
  1073 	CFileCache* fileCache = file.FileCache(); //&file;
       
  1074 	__ASSERT_DEBUG(fileCache != NULL, Fault(ENoFileCache));
       
  1075 
       
  1076 #ifdef _DEBUG
       
  1077 	TInt cancelling = (msgRequest.LastError() == KErrCancel)?(TBool)ETrue:(TBool)EFalse;
       
  1078 #endif
       
  1079 	
       
  1080 
       
  1081 	TInt r = fileCache->WriteBuffered(msgRequest, share->iMode);
       
  1082 
       
  1083 #ifdef _DEBUG
       
  1084 	__ASSERT_DEBUG(!cancelling || msgRequest.LastError() == KErrCancel, Fault(ERequestUncancelled));
       
  1085 #endif
       
  1086 
       
  1087 	// if this request has been cancelled we mustn't dispatch it again - 
       
  1088 	// we still need to call state machine however so that any locked segments can be unlocked
       
  1089 	TInt lastError = msgRequest.LastError();
       
  1090 	if (lastError == KErrCancel)
       
  1091 		r = CFsRequest::EReqActionComplete;
       
  1092 
       
  1093 	return r;
       
  1094 	}
       
  1095 
       
  1096 
       
  1097 
       
  1098 TInt CFileCache::DoWriteBuffered(CFsMessageRequest& aMsgRequest, CFsClientMessageRequest*& aNewRequest, TUint aMode)
       
  1099 
       
  1100 	{
       
  1101 	enum states
       
  1102 		{
       
  1103 		EStBegin=0,
       
  1104 		EStWriteToCache,
       
  1105 		EStReadFromDisk,
       
  1106 		EStReadFromDiskComplete,
       
  1107 		EStWriteToCacheComplete,
       
  1108 		EStCopyFromClient,
       
  1109 		EStReStart,
       
  1110 		EStEnd,
       
  1111 		EStWriteThrough
       
  1112 		};
       
  1113 
       
  1114 	enum flags
       
  1115 		{
       
  1116 		EReadFirstSegment	= 0x01,
       
  1117 		EReadLastSegment	= 0x02
       
  1118 		};
       
  1119 
       
  1120 	TBool cachingWrites = (aMode & EFileWriteBuffered)?(TBool)ETrue:(TBool)EFalse;
       
  1121 	TInt& lastError = aMsgRequest.LastError();
       
  1122 	TBool driveThread = IsDriveThread();
       
  1123 	TInt segmentSize = SegmentSize();
       
  1124 	TInt segmentSizeLog2 = SegmentSizeLog2();
       
  1125 	TInt64 segmentSizeMask = SegmentSizeMask();
       
  1126 
       
  1127 	// temp storage for transition between EStWriteToCache / EStWriteToCacheComplete and EStCopyFromClient
       
  1128 	TInt segmentCount = 0;
       
  1129 	TUint8* addr = NULL;
       
  1130 	TInt64 firstSegmentStartPos = 0;
       
  1131 	TInt64 readPos = 0;
       
  1132 	TUint8* readAddr = NULL;
       
  1133 	TInt readSegmentCount = 0;
       
  1134 
       
  1135 	TInt readFlags = 0;
       
  1136 	
       
  1137 	for(;;)
       
  1138 		{
       
  1139 		TMsgOperation* currentOperation = &aMsgRequest.CurrentOperation();
       
  1140 		TInt retCode = CFsRequest::EReqActionComplete;
       
  1141 
       
  1142 		TInt64& currentPos = currentOperation->iReadWriteArgs.iPos;
       
  1143 		TInt& totalLen = currentOperation->iReadWriteArgs.iTotalLength;
       
  1144 		TInt& currentOffset = currentOperation->iReadWriteArgs.iOffset;
       
  1145 		switch(currentOperation->iState)
       
  1146 			{
       
  1147 			case EStBegin:
       
  1148 				{				
       
  1149 				// Report background flush errors back to client
       
  1150 				CFileShare* share = (CFileShare*) aMsgRequest.ScratchValue(); 
       
  1151 				if (share->iFlushError != KErrNone)
       
  1152 					{
       
  1153 					TInt r = share->iFlushError;
       
  1154 					share->iFlushError = KErrNone;
       
  1155 					return r;
       
  1156 					}
       
  1157 
       
  1158 				if (currentPos > iSize64)
       
  1159 					currentPos = iSize64;
       
  1160 				iInitialSize = iSize64;
       
  1161 
       
  1162 				// if EFileWriteDirectIO OR 
       
  1163 				// (caching writes and requested write len > size of the cache), 
       
  1164 				// flush the write cache
       
  1165 				if ((aMode & EFileWriteBuffered) == 0 || 
       
  1166 					(cachingWrites && totalLen > iCacheSize))
       
  1167 					{
       
  1168 					TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, ETrue);
       
  1169 					if (r == CFsRequest::EReqActionBusy || r != CFsRequest::EReqActionComplete)
       
  1170 						return r;
       
  1171 					}
       
  1172 				// if EFileWriteDirectIO then overwrite any non-dirty data and 
       
  1173 				// write direct to disk....
       
  1174 
       
  1175 				// if caching writes and requested write len > size of the cache, 
       
  1176 				// don't write excess  through the cache as this is wasteful
       
  1177 				if (cachingWrites && totalLen > iCacheSize)
       
  1178 					{
       
  1179 					// Destroy ALL cached data (dirty and non-dirty) to ensure 
       
  1180 					// cache is consistent with data written to disk
       
  1181 					iCacheClient->Purge(ETrue);
       
  1182 
       
  1183 					TInt len = totalLen - iCacheSize;
       
  1184 					TInt r = aMsgRequest.PushOperation(
       
  1185 						currentPos, len, 
       
  1186 						(TDesC8*) currentOperation->iReadWriteArgs.iData, currentOffset,
       
  1187 						CompleteWrite, 
       
  1188 						EStWriteToCache);
       
  1189 					if (r != KErrNone)
       
  1190 						{
       
  1191 						currentOperation->iState = EStReStart;
       
  1192 						break;
       
  1193 						}
       
  1194 
       
  1195 					currentOffset+= len;
       
  1196 					currentPos+= len;
       
  1197 					totalLen-= len;
       
  1198 					return CFsRequest::EReqActionContinue;
       
  1199 					}
       
  1200 
       
  1201 				currentOperation->iState = EStWriteToCache;
       
  1202 				}		
       
  1203 				break;
       
  1204 
       
  1205 			case EStWriteToCache:
       
  1206 				__ASSERT_DEBUG(aMsgRequest.CurrentOperationPtr() != NULL, Fault(ECacheBadOperationIndex));
       
  1207 				{
       
  1208 				// NB we must carry on if we get an error to ensure cache is consistent with disk
       
  1209 				if (totalLen == 0 || lastError == KErrCancel)
       
  1210 					{
       
  1211 					currentOperation->iState = EStWriteToCacheComplete;
       
  1212 					break;
       
  1213 					}
       
  1214 
       
  1215 				firstSegmentStartPos = currentPos & segmentSizeMask;
       
  1216 				TInt64 endPos = currentPos + totalLen;
       
  1217 
       
  1218 				// Find the maximum number of contiguous segments we need to lock
       
  1219 				// in this cacheline - when we unlock these will be marked as filled
       
  1220 				const TInt64 dataRange = Min((TInt64)iCacheSize, (endPos + segmentSize - 1 - firstSegmentStartPos));
       
  1221 				TInt maxSegmentCount =  (TInt)(dataRange >> segmentSizeLog2);
       
  1222 				
       
  1223 				segmentCount = maxSegmentCount;
       
  1224 
       
  1225 				TInt lockError;
       
  1226 				TInt filledSegmentCount;
       
  1227 				addr = iCacheClient->FindAndLockSegments(firstSegmentStartPos, segmentCount, filledSegmentCount, lockError, cachingWrites);
       
  1228 				
       
  1229 				if (lockError == KErrInUse)
       
  1230 					return CFsRequest::EReqActionBusy;
       
  1231 
       
  1232 #ifdef LAZY_WRITE
       
  1233 				if (cachingWrites && addr == NULL && lastError == KErrNone && iCacheClient->TooManyLockedSegments())
       
  1234 					{
       
  1235 					// Flush a single dirty cacheline (if there is one for this file)
       
  1236 					// or all dirty data  on this drive (if there is any).
       
  1237 					// 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
       
  1239 					// but write through 
       
  1240 					TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFalse);
       
  1241 					if (r == CFsRequest::EReqActionBusy)
       
  1242 						return CFsRequest::EReqActionBusy;
       
  1243 					if (r != KErrInUse)
       
  1244 						{
       
  1245 						// Need to switch to drive thread to flush all data on this drive
       
  1246 						if (!driveThread)
       
  1247 							return CFsRequest::EReqActionBusy;
       
  1248 						iLock.Signal();
       
  1249 						TInt r = iDrive->FlushCachedFileInfo();
       
  1250 						iLock.Wait();
       
  1251 						if (r == CFsRequest::EReqActionBusy)
       
  1252 							return CFsRequest::EReqActionBusy;
       
  1253 
       
  1254 						lastError = KErrNoMemory;	// write through
       
  1255 						}
       
  1256 					}
       
  1257 #else
       
  1258 				// flush cache before allocating a new cacheline
       
  1259 				if (cachingWrites && addr == NULL && lastError == KErrNone)
       
  1260 					{
       
  1261 					// if no segment available, flush a single dirty cacheline 
       
  1262 					// or wait if already flushing
       
  1263 					if (iFlushBusy)
       
  1264 						return CFsRequest::EReqActionBusy;
       
  1265 					TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFalse);
       
  1266 					if (r != CFsRequest::EReqActionBusy && r != CFsRequest::EReqActionComplete)
       
  1267 						lastError = r;
       
  1268 					}
       
  1269 #endif
       
  1270 				// if no cacheline found & write caching is enabled, allocate a new cacheline
       
  1271 				if (cachingWrites && addr == NULL && lastError == KErrNone)
       
  1272 					{
       
  1273 					// try to allocate up to write cache size - this may not 
       
  1274 					// be possible if a segment in the range is already cached
       
  1275 					segmentCount = maxSegmentCount;
       
  1276 					addr = iCacheClient->AllocateAndLockSegments(currentPos, segmentCount, cachingWrites, ETrue);
       
  1277 					
       
  1278 					// continue if alloc failed
       
  1279 					if (addr == NULL)
       
  1280 						lastError = KErrNoMemory;
       
  1281 				
       
  1282 					}
       
  1283 
       
  1284 				if (addr == NULL)
       
  1285 					{
       
  1286 					if (cachingWrites && lastError == KErrNone)
       
  1287 						lastError = KErrNoMemory;
       
  1288 					segmentCount = 1;
       
  1289 					currentOperation->iState = EStCopyFromClient;
       
  1290 					break;
       
  1291 					}
       
  1292 
       
  1293 				// if the first or last segment are empty then we'll have to read-before-write
       
  1294 				TInt64 lastSegmentStartPos = firstSegmentStartPos + ((segmentCount-1) << segmentSizeLog2);
       
  1295 				
       
  1296 				TInt startPosSegOffset = iCacheClient->CacheOffset(currentPos);
       
  1297 				TInt endPosSegOffset = iCacheClient->CacheOffset(endPos);
       
  1298 
       
  1299 				// partial first segment ?
       
  1300 				if (startPosSegOffset != 0 && 
       
  1301 					firstSegmentStartPos < iFileCB->Size64() &&
       
  1302 					iCacheClient->SegmentEmpty(firstSegmentStartPos))
       
  1303 					{
       
  1304 					readFlags|= EReadFirstSegment;
       
  1305 					}
       
  1306 		
       
  1307 				// partial last segment ? 
       
  1308 				// NB this may be the first segment too !
       
  1309 				if (endPosSegOffset != 0 && 
       
  1310 					endPos < lastSegmentStartPos + segmentSize &&
       
  1311 					endPos < iFileCB->Size64() &&
       
  1312 					iCacheClient->SegmentEmpty(lastSegmentStartPos))
       
  1313 					{
       
  1314 					readFlags|= EReadLastSegment;
       
  1315 					}
       
  1316 
       
  1317 				// read-before-write required ?
       
  1318 				if (readFlags & EReadFirstSegment)
       
  1319 					{
       
  1320 					readFlags&= ~EReadFirstSegment;
       
  1321 					readPos = firstSegmentStartPos;
       
  1322 					readAddr = addr;
       
  1323 					readSegmentCount = 1;
       
  1324 					// if the last segment is empty and it's the same as the first or contiguous,
       
  1325 					// then read that too 
       
  1326 					if ((readFlags & EReadLastSegment) && (segmentCount <= 2))
       
  1327 						{
       
  1328 						readSegmentCount = segmentCount;
       
  1329 						readFlags&= ~EReadLastSegment;
       
  1330 						}
       
  1331 					currentOperation->iState = EStReadFromDisk;
       
  1332 					}
       
  1333 				else if (readFlags & EReadLastSegment)
       
  1334 					{
       
  1335 					readFlags&= ~EReadLastSegment;
       
  1336 					readPos = lastSegmentStartPos;
       
  1337 					readAddr = addr + ((segmentCount-1) << segmentSizeLog2);
       
  1338 					readSegmentCount = 1;
       
  1339 					currentOperation->iState = EStReadFromDisk;
       
  1340 					}
       
  1341 				else
       
  1342 					{
       
  1343 					currentOperation->iState = EStCopyFromClient;
       
  1344 					}
       
  1345 				}
       
  1346 				break;
       
  1347 
       
  1348 			case EStReadFromDisk:
       
  1349 				{
       
  1350 				// Save address & segmentCount in scratch area
       
  1351 				currentOperation->iScratchValue0 = addr;	
       
  1352 				__ASSERT_DEBUG(segmentCount < 0xFFFF, Fault(EBadSegmentCount));
       
  1353 				currentOperation->iScratchValue1 = (TAny*) ((readFlags << 16) | segmentCount);
       
  1354 
       
  1355 				TInt maxReadLen = readSegmentCount << segmentSizeLog2;
       
  1356 #ifdef __VC32__
       
  1357 #pragma warning( disable : 4244 )  
       
  1358 //Disable Compiler Warning (levels 3 and 4) C4244: 'conversion' conversion from 'type1' to 'type2', possible loss of data
       
  1359 // maxReadLen is of 31 bits. Hence Min result is reduced to 31 bits
       
  1360 #endif
       
  1361 				TInt readLen = (TInt)Min((TInt64)maxReadLen, (iSize64 - readPos));
       
  1362 #ifdef __VC32__
       
  1363 #pragma warning( default : 4244 )
       
  1364 #endif
       
  1365 				__ASSERT_DEBUG (aMsgRequest.Message().Handle() != NULL, Fault(EMsgAlreadyCompleted));					
       
  1366 				TInt r = aMsgRequest.PushOperation(
       
  1367 					readPos, readLen, readAddr, 0, 
       
  1368 					CompleteWrite,
       
  1369 					EStReadFromDiskComplete,
       
  1370 					EFsFileRead);
       
  1371 				if (r != KErrNone)
       
  1372 					{
       
  1373 					lastError = KErrNoMemory;
       
  1374 					currentOperation->iState = EStEnd;
       
  1375 					iCacheClient->UnlockSegments(firstSegmentStartPos);
       
  1376 					break;
       
  1377 					}
       
  1378 
       
  1379 				// requeue this request
       
  1380 				return CFsRequest::EReqActionContinue;
       
  1381 				}
       
  1382 
       
  1383 			case EStReadFromDiskComplete:
       
  1384 				{
       
  1385 				__ASSERT_DEBUG(aMsgRequest.CurrentOperationPtr() != NULL, Fault(ECacheBadOperationIndex));
       
  1386 				// restore addr & segmentCount etc from scratch area
       
  1387 				firstSegmentStartPos = currentPos & segmentSizeMask;
       
  1388 				addr = (TUint8*) currentOperation->iScratchValue0;
       
  1389 				segmentCount = ((TInt) currentOperation->iScratchValue1) & 0xFFFF;
       
  1390 				readFlags = ((TInt) currentOperation->iScratchValue1) >> 16;
       
  1391 
       
  1392 				if (readFlags & EReadLastSegment)
       
  1393 					{
       
  1394 					TInt64 lastSegmentStartPos = firstSegmentStartPos + ((segmentCount-1) << segmentSizeLog2);
       
  1395 					readFlags&= ~EReadLastSegment;
       
  1396 					readPos = lastSegmentStartPos;
       
  1397 					readAddr = addr + ((segmentCount-1) << segmentSizeLog2);
       
  1398 					readSegmentCount = 1;
       
  1399 					currentOperation->iState = EStReadFromDisk;
       
  1400 					break;
       
  1401 					}
       
  1402 				
       
  1403 				aMsgRequest.CurrentOperation().iState = EStCopyFromClient;
       
  1404 				}
       
  1405 				break;
       
  1406 
       
  1407 			case EStCopyFromClient:
       
  1408 				{
       
  1409 				TInt writeLen = segmentCount << segmentSizeLog2;
       
  1410 				TInt offset = iCacheClient->CacheOffset(currentPos);	// offset into segment
       
  1411 				writeLen = Min(writeLen - offset, totalLen);
       
  1412 
       
  1413 				if (addr != NULL)
       
  1414 					{
       
  1415 					__ASSERT_DEBUG (aMsgRequest.Message().Handle() != NULL, Fault(EMsgAlreadyCompleted));
       
  1416 					// copy from user buffer to cache buffer
       
  1417 					TInt writeError = KErrNone;
       
  1418 					if (currentOperation->iClientRequest)
       
  1419 						{
       
  1420 						TPtr8 ptr(addr+offset, writeLen, writeLen);
       
  1421 						writeError = aMsgRequest.Read(0, ptr, currentOffset);
       
  1422 						}
       
  1423 					else
       
  1424 						{
       
  1425 						memcpy(addr+offset, ((TUint8*) currentOperation->iReadWriteArgs.iData) + currentOffset, writeLen);
       
  1426 						}
       
  1427 
       
  1428 					// "writing" past end of file ?
       
  1429 					if (currentPos + writeLen > iSize64 && cachingWrites && lastError == KErrNone && writeError == KErrNone)
       
  1430 						{
       
  1431 						__CACHE_PRINT2(_L("CACHEFILE: extending size old %ld new %ld"), iSize64, currentPos + totalLen);
       
  1432 						iSize64 = currentPos + writeLen;
       
  1433 						}
       
  1434 
       
  1435 					TInt anyError = (writeError != KErrNone)?writeError:lastError;
       
  1436 
       
  1437 					if (anyError == KErrNone)
       
  1438 						iCacheClient->MarkSegmentsAsFilled(firstSegmentStartPos, segmentCount);
       
  1439 
       
  1440 					if (cachingWrites && anyError == KErrNone)
       
  1441 						{
       
  1442 						iCacheClient->MarkSegmentsAsDirty(firstSegmentStartPos, segmentCount);
       
  1443 						// start dirty data timer
       
  1444 						FileDirty(aMsgRequest);
       
  1445 						}
       
  1446 
       
  1447 					// unlock if we're not buffering writes (segments won't be unlocked if dirty)
       
  1448 					iCacheClient->UnlockSegments(firstSegmentStartPos);
       
  1449 					}
       
  1450 
       
  1451 				currentOffset+= writeLen;
       
  1452 				currentPos+= writeLen;
       
  1453 				totalLen-= writeLen;
       
  1454 
       
  1455 				currentOperation->iState = EStWriteToCache;
       
  1456 				break;
       
  1457 				}
       
  1458 
       
  1459 			case EStWriteToCacheComplete:
       
  1460 				{
       
  1461 				__ASSERT_DEBUG(aMsgRequest.CurrentOperationPtr() != NULL, Fault(ECacheBadOperationIndex));
       
  1462 
       
  1463 				if (lastError == KErrCancel)
       
  1464 					{
       
  1465 					currentOperation->iState = EStEnd;
       
  1466 					}
       
  1467 				else if ((!cachingWrites) || (lastError != KErrNone))
       
  1468 					{
       
  1469 					// allow TFsFileWrite::DoRequestL() to proceed normally using original pos & len
       
  1470 					__ASSERT_DEBUG(aMsgRequest.CurrentOperationPtr() != NULL, Fault(ECacheBadOperationIndex));
       
  1471 	
       
  1472 					currentOperation->iState = EStWriteThrough;
       
  1473 					}
       
  1474 				else
       
  1475 					{
       
  1476 					__CACHE_PRINT2(_L("CACHEFILE: write buffered pos %ld, len %d"), 
       
  1477 						aMsgRequest.CurrentOperation().iReadWriteArgs.iPos, aMsgRequest.CurrentOperation().iReadWriteArgs.iOffset);
       
  1478 					__ASSERT_DEBUG(totalLen == 0, Fault(ECompletingWriteWithDataRemaining));
       
  1479 
       
  1480 					currentOperation->iState = EStEnd;
       
  1481 					}
       
  1482 				break;
       
  1483 				}
       
  1484 
       
  1485 			case EStWriteThrough:
       
  1486 
       
  1487 				if (lastError == KErrCancel)
       
  1488 					{
       
  1489 					currentOperation->iState = EStEnd;
       
  1490 					break;
       
  1491 					}
       
  1492 
       
  1493 				// we're going to issue an uncached write so clear any error
       
  1494 				lastError = KErrNone;
       
  1495 
       
  1496 				if (cachingWrites)
       
  1497 					{
       
  1498 					TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, ETrue);
       
  1499 #ifdef _DEBUG
       
  1500 					if (r == CFsRequest::EReqActionBusy)
       
  1501 						CCacheManagerFactory::CacheManager()->Stats().iWriteThroughWithDirtyDataCount++;
       
  1502 #endif
       
  1503 					// Need to reset currentOperation.iReadWriteArgs.iTotalLength here to make sure 
       
  1504 					// TFsFileWrite::PostInitialise() doesn't think there's no data left to process
       
  1505 					aMsgRequest.ReStart();
       
  1506 					if (r == CFsRequest::EReqActionBusy || r != CFsRequest::EReqActionComplete)
       
  1507 						return r;
       
  1508 					}
       
  1509 
       
  1510 				retCode = CFsRequest::EReqActionContinue;
       
  1511 
       
  1512 				// fall into...EStRestart
       
  1513 				currentOperation->iState = EStReStart;
       
  1514 
       
  1515 			case EStReStart:
       
  1516 
       
  1517 				__ASSERT_DEBUG(retCode == CFsRequest::EReqActionBusy || retCode == CFsRequest::EReqActionContinue, Fault(EBadRetCode));
       
  1518 
       
  1519 				aMsgRequest.ReStart();
       
  1520 				UpdateSharePosition(aMsgRequest, *currentOperation);
       
  1521 				if (currentOperation->iClientRequest)
       
  1522 					currentOperation->iReadWriteArgs.iPos = currentOperation->iClientPosition; // NB maybe KCurrentPosition64
       
  1523 				return retCode;
       
  1524 
       
  1525 			case EStEnd:
       
  1526 				return retCode;
       
  1527 			}
       
  1528 		}
       
  1529 	}
       
  1530 
       
  1531 
       
  1532 
       
  1533 TInt CFileCache::AllocateRequest(CFsClientMessageRequest*& aNewRequest, TBool aWrite, CSessionFs* aSession)
       
  1534 	{
       
  1535 
       
  1536 	RLocalMessage msgNew;
       
  1537 	const TOperation& oP = OperationArray[aWrite?EFsFileWriteDirty:EFsFileRead];
       
  1538 	TInt r = RequestAllocator::GetMessageRequest(oP, msgNew, aNewRequest);
       
  1539 	if (r != KErrNone)
       
  1540 		return r;
       
  1541 
       
  1542 	aNewRequest->Set(msgNew, oP, aSession);
       
  1543 	aNewRequest->SetDrive(iDrive);
       
  1544 	
       
  1545 	// read-aheads and write-dirty requests should not be posted to plugins
       
  1546 	// If there are data-modifying plugins, then these should sit above the file cache
       
  1547 	aNewRequest->iCurrentPlugin = NULL;
       
  1548 	aNewRequest->EnablePostIntercept(EFalse);
       
  1549 
       
  1550 	// Scratch value is a CFileCB pointer NOT a CFileShare for requests
       
  1551 	// allocated by the file cache
       
  1552 	aNewRequest->SetScratchValue64( MAKE_TINT64(EFalse, (TUint) iFileCB) );
       
  1553 
       
  1554 	// don't call Initialise(), don't call PostInitialise()
       
  1555 	aNewRequest->SetState(CFsRequest::EReqStateDoRequest);
       
  1556 	
       
  1557 	// don't call Message().Complete()
       
  1558 	aNewRequest->SetCompleted(EFalse);		
       
  1559 
       
  1560 	__ASSERT_DEBUG(aNewRequest->CurrentOperationPtr() == NULL, Fault(EBadOperationIndex));
       
  1561 
       
  1562 	return KErrNone;
       
  1563 	}
       
  1564 
       
  1565 TInt CFileCache::FlushDirty(CFsRequest* aOldRequest)
       
  1566 	{
       
  1567 	iLock.Wait();
       
  1568 
       
  1569 	CFsClientMessageRequest* newRequest = NULL;
       
  1570 
       
  1571 	TInt r = DoFlushDirty(newRequest, aOldRequest, ETrue);
       
  1572 
       
  1573 	iLock.Signal();
       
  1574 
       
  1575 	if (newRequest)
       
  1576 	    {
       
  1577 		//To be used in notification framework.
       
  1578 	    //newRequest->iUID = aOldRequest->Message().Identity();
       
  1579 		newRequest->Dispatch();
       
  1580 	    }
       
  1581 
       
  1582 	return r;
       
  1583 	}
       
  1584 
       
  1585 
       
  1586 void CFileCache::Purge(TBool aPurgeDirty)
       
  1587 	{
       
  1588 	iLock.Wait();
       
  1589 
       
  1590 	iCacheClient->Purge(aPurgeDirty);
       
  1591 
       
  1592 	iLock.Signal();
       
  1593 	}
       
  1594 
       
  1595 void CFileCache::PropagateFlushErrorToAllFileShares()
       
  1596 	{
       
  1597 	FileShares->Lock();
       
  1598 	TInt count = FileShares->Count();
       
  1599 	while(count--)
       
  1600 		{
       
  1601 		CFileShare* share = (CFileShare*)(*FileShares)[count];
       
  1602 		if (&share->File() == iFileCB)
       
  1603 			{
       
  1604 			share->iFlushError = iFlushError;
       
  1605 			}
       
  1606 		}
       
  1607 	FileShares->Unlock();
       
  1608 	}
       
  1609 
       
  1610 /**
       
  1611 CFileCache::DoFlushDirty()
       
  1612 
       
  1613 @param aFlushAll. If EFalse then only a single cacheline will be flushed
       
  1614 
       
  1615 returns	CFsRequest::EReqActionBusy if a flush is in progress OR cacheline already in use
       
  1616 		CFsRequest::EReqActionComplete if nothing to flush / flush completed 
       
  1617 		KErrNoMemory if failed to allocate a request
       
  1618 		or one of the system wide error codes
       
  1619 
       
  1620  */
       
  1621 TInt CFileCache::DoFlushDirty(CFsClientMessageRequest*& aNewRequest, CFsRequest* aOldRequest, TBool aFlushAll)
       
  1622 	{
       
  1623 	TInt64  pos;
       
  1624 	TUint8* addr;
       
  1625 
       
  1626 	if (iFlushBusy)
       
  1627 		return CFsRequest::EReqActionBusy;
       
  1628 
       
  1629 	TInt segmentCount = iCacheClient->FindFirstDirtySegment(pos, addr);
       
  1630 
       
  1631 	if (segmentCount == 0)
       
  1632 		{
       
  1633 		TInt flushError = iFlushError;
       
  1634 		iFlushError = KErrNone;
       
  1635 
       
  1636 		// If this flush didn't originate from a client request return last error
       
  1637 		if (!aOldRequest || !aOldRequest->Session())
       
  1638 			return (flushError == KErrNone)? CFsRequest::EReqActionComplete : flushError;
       
  1639 
       
  1640 		// Return the last error from CFileShare::iFlushError 
       
  1641 		// and then clear CFileShare::iFlushError 
       
  1642 
       
  1643 		CFileShare* share = (CFileShare*) SessionObjectFromHandle(
       
  1644 			aOldRequest->Message().Int3(),
       
  1645 			FileShares->UniqueID(),
       
  1646 			aOldRequest->Session());
       
  1647 
       
  1648 		TInt r = KErrNone;
       
  1649 		if (share)
       
  1650 			{
       
  1651 			r = share->iFlushError;
       
  1652 			share->iFlushError = KErrNone;
       
  1653 			}
       
  1654 		if (r == KErrNone)
       
  1655 			return CFsRequest::EReqActionComplete;
       
  1656 
       
  1657 		return r;
       
  1658 		}
       
  1659 
       
  1660 
       
  1661 	// NB aOldRequest->Session may be NULL - e.g for FileShareCloseOp
       
  1662 	CSessionFs* session = aOldRequest && aOldRequest->Session() ? aOldRequest->Session() : iDirtyDataOwner;
       
  1663 
       
  1664 	__ASSERT_ALWAYS(session, Fault(EFlushingWithSessionNull));
       
  1665 
       
  1666 	TInt r = AllocateRequest(aNewRequest, ETrue, session);
       
  1667 	if (r != KErrNone)
       
  1668 		return r;
       
  1669 	
       
  1670 	r = aNewRequest->PushOperation(0, 0, (TUint8*) NULL, 0);
       
  1671 
       
  1672 	if (r != KErrNone)
       
  1673 		{
       
  1674 		aNewRequest->Free();
       
  1675 		aNewRequest = NULL;
       
  1676 		return r;
       
  1677 		}
       
  1678 
       
  1679 	// set the number of segments to flush - either all dirty data or the equivalent of one full cacheline
       
  1680 	aNewRequest->CurrentOperation().iScratchValue0 = 
       
  1681 		(TAny*) (aFlushAll?KMaxTInt:iCacheClient->CacheLineSizeInSegments());
       
  1682 
       
  1683 
       
  1684 	// issue flush request
       
  1685 	r = FlushDirtySm(*aNewRequest);
       
  1686 
       
  1687 	// We should only get three possible return codes from FlushDirtySm() :
       
  1688 	// CFsRequest::EReqActionContinue	- a write request (aNewRequest) needs to be dispatched
       
  1689 	// CFsRequest::EReqActionComplete	- completed already - this can happen if dirty data was beyond file end
       
  1690 	// CFsRequest::EReqActionBusy		- first dirty cacheline is already in use.
       
  1691 	__ASSERT_DEBUG(	r == CFsRequest::EReqActionContinue || r == CFsRequest::EReqActionComplete || r == CFsRequest::EReqActionBusy, Fault(EBadRetCode));
       
  1692 	if (r == CFsRequest::EReqActionContinue || r == CFsRequest::EReqActionBusy)
       
  1693 		{
       
  1694 		// requeue the caller's request (aOldRequest) if there is one
       
  1695 		return CFsRequest::EReqActionBusy;
       
  1696 		}
       
  1697 	else	// CFsRequest::EReqActionComplete
       
  1698 		{
       
  1699 		aNewRequest->PopOperation();
       
  1700 		aNewRequest->Free();
       
  1701 		aNewRequest = NULL;
       
  1702 		return r;
       
  1703 		}
       
  1704 	
       
  1705 	}
       
  1706 
       
  1707 
       
  1708 TInt TFsFileWriteDirty::PostInitialise(CFsRequest* aRequest)
       
  1709 	{
       
  1710 	return CFileCache::CompleteFlushDirty(aRequest);
       
  1711 	}
       
  1712 
       
  1713 TInt CFileCache::CompleteFlushDirty(CFsRequest* aRequest)
       
  1714 	{
       
  1715 	CFsMessageRequest& msgRequest = *(CFsMessageRequest*) aRequest;
       
  1716 
       
  1717 	CFileShare* share;
       
  1718 	CFileCB* file;
       
  1719 	GetFileFromScratch(aRequest, share, file);
       
  1720 	CFileCache* fileCache = file->FileCache();
       
  1721 
       
  1722 	__ASSERT_DEBUG(fileCache != NULL, Fault(ENoFileCache));
       
  1723 
       
  1724 #ifdef _DEBUG
       
  1725 	TInt cancelling = (msgRequest.LastError() == KErrCancel)?(TBool)ETrue:(TBool)EFalse;
       
  1726 #endif
       
  1727 
       
  1728 	fileCache->iLock.Wait();
       
  1729 	TInt r = fileCache->FlushDirtySm(msgRequest);
       
  1730 	fileCache->iLock.Signal();
       
  1731 
       
  1732 #ifdef _DEBUG
       
  1733 	__ASSERT_DEBUG(!cancelling || msgRequest.LastError() == KErrCancel, Fault(ERequestUncancelled));
       
  1734 #endif
       
  1735 
       
  1736 	// if this request has been cancelled we mustn't dispatch it again - 
       
  1737 	// we still need to call state machine however so that any locked segments can be unlocked
       
  1738 	if (msgRequest.LastError() == KErrCancel)
       
  1739 		r = CFsRequest::EReqActionComplete;
       
  1740 
       
  1741 	return r;
       
  1742 	}
       
  1743 
       
  1744 TInt CFileCache::FlushDirtySm(CFsMessageRequest& aMsgRequest)
       
  1745 	{
       
  1746 	enum states
       
  1747 		{
       
  1748 		EStBegin=0,
       
  1749 		EStWriteToDisk,
       
  1750 		EStWriteToDiskComplete,
       
  1751 		EStMarkAsClean,
       
  1752 		EStEnd
       
  1753 		};
       
  1754 
       
  1755 	TMsgOperation* currentOperation = &aMsgRequest.CurrentOperation();
       
  1756 	TInt& lastError = aMsgRequest.LastError();
       
  1757 	TInt64 pos = 0;
       
  1758 
       
  1759 	for(;;)
       
  1760 		{
       
  1761 
       
  1762 		switch(currentOperation->iState)
       
  1763 			{
       
  1764 			case EStBegin:
       
  1765 				iFlushBusy = ETrue;
       
  1766 
       
  1767 				// fall into...
       
  1768 
       
  1769 			case EStWriteToDisk:
       
  1770 				{
       
  1771 				currentOperation->iState = EStWriteToDisk;
       
  1772 
       
  1773 				TUint8* addr;
       
  1774 				TInt segmentCount = iCacheClient->FindFirstDirtySegment(pos, addr);
       
  1775 				
       
  1776 				// keep track of how many segments we've written
       
  1777 				currentOperation->iScratchValue0 = (TAny*) ((TInt) currentOperation->iScratchValue0 - segmentCount);
       
  1778 
       
  1779 				// if no more dirty segments of if a genuine error then finish
       
  1780 				// NB if the request has been cancelled then we still need to proceed and mark all the
       
  1781 				// segments as clean, otherwise they will never get freed !
       
  1782 				if (segmentCount == 0 || (lastError != KErrNone && lastError != KErrCancel))
       
  1783 					{
       
  1784 					currentOperation->iState = EStEnd;
       
  1785 					break;
       
  1786 					}
       
  1787 
       
  1788 				TInt  len = segmentCount << SegmentSizeLog2();
       
  1789 
       
  1790 				if (pos < iSize64)
       
  1791 					// Result of Min shall be of size TInt
       
  1792 					// Hence to suppress warning
       
  1793 					len = (TInt)(Min(iSize64 - pos, (TInt64)len));
       
  1794 				else
       
  1795 					len = 0;
       
  1796 
       
  1797 
       
  1798 				// if writing past end of file or this request has been cancelled, just mark as clean
       
  1799 				if (len == 0 || lastError == KErrCancel)
       
  1800 					{
       
  1801 					currentOperation->iState = EStMarkAsClean;
       
  1802 					break;
       
  1803 					}
       
  1804 
       
  1805 				__CACHE_PRINT3(_L("CACHEFILE: FlushDirty first dirty pos %d, addr %08X count %d"), I64LOW(pos), addr, segmentCount);
       
  1806 
       
  1807 				TInt filledSegmentCount;
       
  1808 				TInt lockError;
       
  1809 				addr = iCacheClient->FindAndLockSegments(pos, segmentCount, filledSegmentCount, lockError, ETrue);
       
  1810 
       
  1811 				// If cacheline is busy, we need to post request to back of drive queue
       
  1812 				// To dispatch to drive thread and intercept before DoRequestL() we must call iPostInitialise(), 
       
  1813 				// so set the state back to CFsRequest::EReqStatePostInitialise
       
  1814 				if (lockError == KErrInUse)
       
  1815 					{
       
  1816 					__CACHE_PRINT(_L("FlushDirtySm() - cacheline BUSY !"));
       
  1817 					aMsgRequest.SetState(CFsRequest::EReqStatePostInitialise);
       
  1818 					return CFsRequest::EReqActionBusy;
       
  1819 					}
       
  1820 				else if (lockError != KErrNone)
       
  1821 					{
       
  1822 					iFlushBusy = EFalse;
       
  1823 					lastError = lockError;
       
  1824 					return CFsRequest::EReqActionComplete;
       
  1825 					}
       
  1826 
       
  1827 				currentOperation->Set(pos, len, addr, 0, EStWriteToDiskComplete);
       
  1828 				if (pos < iSize64)
       
  1829 					{
       
  1830 					TInt r = aMsgRequest.PushOperation(
       
  1831 						pos, len, addr, 0,
       
  1832 						CompleteFlushDirty, 
       
  1833 						EStWriteToDiskComplete);
       
  1834 					if (r != KErrNone)
       
  1835 						{
       
  1836 						iCacheClient->UnlockSegments(pos);
       
  1837 						iFlushBusy = EFalse;
       
  1838 						lastError = r;
       
  1839 						return CFsRequest::EReqActionComplete;
       
  1840 						}
       
  1841 
       
  1842 					return CFsRequest::EReqActionContinue;	// continue on to TFsFileWrite::DoRequestL()()
       
  1843 					}
       
  1844 				}
       
  1845 
       
  1846 			case EStWriteToDiskComplete:
       
  1847 				{
       
  1848 #ifdef _DEBUG
       
  1849 				// simulate a media eject to test critical error server
       
  1850 				if (CCacheManagerFactory::CacheManager()->SimulateWriteFailureEnabled())
       
  1851 					{
       
  1852 					lastError = KErrNotReady;
       
  1853 					iDrive->Dismount();
       
  1854 					}
       
  1855 #endif
       
  1856 				pos = currentOperation->iReadWriteArgs.iPos;
       
  1857 				iCacheClient->UnlockSegments(pos);
       
  1858 				}
       
  1859 				// fall into...
       
  1860 
       
  1861 			case EStMarkAsClean:
       
  1862 				{
       
  1863 				// NB pos must be set by EStWriteToDiskComplete or EStWriteToDisk
       
  1864 
       
  1865 				if (lastError != KErrNone)
       
  1866 					{
       
  1867 					__CACHE_PRINT1(_L("CACHEFILE: WriteThrough FAILED %d"), lastError);
       
  1868 					
       
  1869 					lastError = HandleWriteDirtyError(lastError);
       
  1870 
       
  1871 					// retry ?
       
  1872 					if (lastError == KErrNone)
       
  1873 						{
       
  1874 						// clear error and try again
       
  1875 						currentOperation->iState = EStWriteToDisk;
       
  1876 						break;
       
  1877 						}
       
  1878 
       
  1879 					iFlushError = lastError;
       
  1880 					PropagateFlushErrorToAllFileShares();
       
  1881 
       
  1882 					__CACHE_PRINT2(_L("CACHEFILE: Resetting size from %ld to %ld"), iSize64, iFileCB->Size64());
       
  1883  					SetSize64(iFileCB->Size64());
       
  1884 					}
       
  1885 
       
  1886 				if (lastError != KErrNone)
       
  1887 					{
       
  1888 					// Destroy ALL cached data (dirty and non-dirty) !
       
  1889 					iCacheClient->Purge(ETrue);
       
  1890 					}
       
  1891 				else
       
  1892 					{
       
  1893 					// Mark segment as clean
       
  1894 					iCacheClient->MarkSegmentsAsClean(pos);
       
  1895 					}
       
  1896 
       
  1897 
       
  1898 				if (TInt(currentOperation->iScratchValue0) > 0)
       
  1899 					currentOperation->iState = EStWriteToDisk;
       
  1900 				else
       
  1901 					currentOperation->iState = EStEnd;
       
  1902 
       
  1903 
       
  1904 				}
       
  1905 				break;
       
  1906 
       
  1907 			case EStEnd:
       
  1908 				{
       
  1909 				iFlushBusy = EFalse;
       
  1910 
       
  1911 				TUint8* addr;
       
  1912 				MarkFileClean();
       
  1913 				// Re-start dirty data timer if there is still some dirty data
       
  1914 				if (iCacheClient->FindFirstDirtySegment(pos, addr) != 0)
       
  1915 					FileDirty(aMsgRequest);
       
  1916 
       
  1917 				return CFsRequest::EReqActionComplete;
       
  1918 				}
       
  1919 			}
       
  1920 		}
       
  1921 	}
       
  1922 
       
  1923 /**
       
  1924 Handle a dirty data write error
       
  1925 
       
  1926 Returns aError if dirty data should be thrown away or
       
  1927 		KErrNone if write should be retried 
       
  1928 */
       
  1929 TInt CFileCache::HandleWriteDirtyError(TInt aError)
       
  1930 	{
       
  1931 	__THRD_PRINT3(_L(">TRACE: CFileCache::HandleWriteDirtyError() aError %d mounted %d changed %d"), aError, iDrive->IsMounted(), iDrive->IsChanged());
       
  1932 
       
  1933 	// normally the disk change will have been detected by TDrive::CheckMount() but occasionally,
       
  1934 	// the change will occur while writing - in which case we need to mimick what TDrive::CheckMount does,
       
  1935 	// to make sure we are in a consisten state i.e. dismount the drive and set iCurrentMount to NULL
       
  1936 	if (iDrive->IsChanged())
       
  1937 		{
       
  1938 		iDrive->SetChanged(EFalse);
       
  1939 		if (iDrive->IsMounted())		//	Dismount the mount if it is still marked as mounted
       
  1940             iDrive->Dismount();
       
  1941 		}
       
  1942 
       
  1943 	// if error didn't occur because of a media eject, do nothing 
       
  1944 	if (aError == KErrNotReady && !iDrive->IsMounted())
       
  1945 		{
       
  1946 	
       
  1947 		TLocaleMessage line1;
       
  1948 		TLocaleMessage line2;
       
  1949 		line1=EFileServer_PutTheCardBackLine1;
       
  1950 		line2=EFileServer_PutTheCardBackLine2;
       
  1951 
       
  1952 		//-- create Notifier  
       
  1953 		CAsyncNotifier* notifier = CAsyncNotifier::New(); 
       
  1954 		if( !notifier )
       
  1955 			{
       
  1956 			return aError;
       
  1957 			}
       
  1958 
       
  1959 		notifier->SetMount(iMount);
       
  1960 
       
  1961 		
       
  1962 		// While this (drive) thread is calling the notifier server (& effectively suspended), 
       
  1963 		// the main thread may call TFsFileRead::PostInitialise() or TFsFileWrite::PostInitialise() 
       
  1964 		// which would cause dead-lock unless we release the lock here. We also need to release 
       
  1965 		// the lock before calling CDriveThread::CompleteReadWriteRequests().
       
  1966 		iLock.Signal();	
       
  1967 
       
  1968 		FOREVER
       
  1969 			{
       
  1970 			TInt buttonVal;
       
  1971 
       
  1972 			TInt ret = notifier->Notify(
       
  1973 				TLocaleMessageText(line1),
       
  1974 				TLocaleMessageText(line2),
       
  1975 				TLocaleMessageText(EFileServer_Button1),
       
  1976 				TLocaleMessageText(EFileServer_Button2),
       
  1977 				buttonVal);
       
  1978 			if (ret!=KErrNone)
       
  1979 				break; 
       
  1980 			if (buttonVal!=1)
       
  1981 				break; // Abort
       
  1982 
       
  1983 
       
  1984 			// Wait up to 3 seconds for a disk change - this is because there is often a substantial 
       
  1985 			// (one/two second) delay between inserting a card & getting a notification that the card is ready;
       
  1986 			// if we give up too soon and fire of the notifier again, it's not very user-friendly !
       
  1987 			const TTimeIntervalMicroSeconds32 KHalfSecond(500000);
       
  1988 			const TInt KRetries = 6;;
       
  1989 			for (TInt n=0; !iDrive->IsChanged() && n<KRetries; n++)
       
  1990 				User::After(KHalfSecond);
       
  1991 
       
  1992 
       
  1993 
       
  1994 			// Without this code, retry will indiscriminately write over whatever disk happens to be present.
       
  1995 			// However if the write error is to the bootsector remounting will always fail because the boot
       
  1996 			// sector will have changed and hence the disk is useless.
       
  1997 			// 
       
  1998 			TRACE1(UTF::EBorder, UTraceModuleFileSys::ECMountCBReMount, EF32TraceUidFileSys, DriveNumber());
       
  1999 
       
  2000 			TInt remountSuccess = iDrive->ReMount(*iMount);
       
  2001 
       
  2002 			TRACE1(UTF::EBorder, UTraceModuleFileSys::ECMountCBReMountRet, EF32TraceUidFileSys, remountSuccess);
       
  2003 			if (!remountSuccess)
       
  2004 				continue;
       
  2005 			
       
  2006 			
       
  2007 			iMount->Drive().SetChanged(EFalse);
       
  2008 
       
  2009 			aError = KErrNone; // Retry
       
  2010 			break;
       
  2011 			}
       
  2012 
       
  2013 		delete notifier;
       
  2014 
       
  2015 
       
  2016 		// Cancel hung state 
       
  2017 		FsThreadManager::SetDriveHung(DriveNumber(), EFalse);
       
  2018 
       
  2019 		
       
  2020 		// media had been removed and NOT replaced: so destroy ALL cached data 
       
  2021 		// (dirty and non-dirty) for all filecaches on this drive
       
  2022 		if (aError != KErrNone)
       
  2023 			{
       
  2024 			CDriveThread* pT=NULL;
       
  2025 			TInt r=FsThreadManager::GetDriveThread(DriveNumber(), &pT);
       
  2026 			if(r==KErrNone)
       
  2027 				{
       
  2028 				pT->CompleteReadWriteRequests();
       
  2029 				iDrive->PurgeDirty(*iMount);
       
  2030 				}
       
  2031 			}
       
  2032 
       
  2033 		iLock.Wait();
       
  2034 		}
       
  2035 
       
  2036 
       
  2037 	return aError;
       
  2038 	}
       
  2039 
       
  2040 
       
  2041 /**
       
  2042 Mark file as dirty
       
  2043 */
       
  2044 void CFileCache::FileDirty(CFsMessageRequest& aMsgRequest)
       
  2045 	{
       
  2046 	CSessionFs* session = aMsgRequest.Session();
       
  2047 	__ASSERT_ALWAYS(session, Fault(EDirtyDataOwnerNull));
       
  2048 
       
  2049 	// Remember the last session which caused the file to become dirty
       
  2050 	// Always record whether any session has reserved access so the CheckDiskSpace() behaves correctly
       
  2051 	if (iDirtyDataOwner == NULL || session->ReservedAccess(iDriveNum))
       
  2052 		iDirtyDataOwner = session;
       
  2053 
       
  2054 	// start a timer after which file will be flushed
       
  2055 	CDriveThread* driveThread=NULL;
       
  2056 	TInt r = FsThreadManager::GetDriveThread(iDriveNum, &driveThread);
       
  2057 	if(r == KErrNone && driveThread != NULL)
       
  2058 		iDirtyTimer.Start(driveThread, iDirtyDataFlushTime);
       
  2059 	}
       
  2060 
       
  2061 //----------------------------------------------------------------------------
       
  2062 /**
       
  2063     Mark the file as clean and stop dirty data timer
       
  2064 */
       
  2065 void CFileCache::MarkFileClean()
       
  2066 	{
       
  2067 	iDirtyDataOwner = NULL;
       
  2068 
       
  2069 	if (!iDriveThread)
       
  2070 		return;
       
  2071 
       
  2072 	iDirtyTimer.Stop();
       
  2073 	}
       
  2074 
       
  2075 
       
  2076 
       
  2077 //************************************
       
  2078 // TFileCacheSettings
       
  2079 //************************************
       
  2080 
       
  2081 RArray<TFileCacheSettings::TFileCacheConfig>* TFileCacheSettings::iFileCacheSettings = NULL;
       
  2082 
       
  2083 
       
  2084 
       
  2085 const TInt KDriveCacheSettingsArrayGranularity = 4;
       
  2086 
       
  2087 
       
  2088 TFileCacheSettings::TFileCacheConfig::TFileCacheConfig(TInt aDrive) : 
       
  2089 	iDrive(aDrive), 
       
  2090 	iFileCacheReadAsync(KDefaultFileCacheReadAsync),
       
  2091 	iFairSchedulingLen(KDefaultFairSchedulingLen << KByteToByteShift),
       
  2092 	iCacheSize(KDefaultFileCacheSize << KByteToByteShift),
       
  2093 	iMaxReadAheadLen(KDefaultFileCacheMaxReadAheadLen << KByteToByteShift),
       
  2094 	iClosedFileKeepAliveTime(KDefaultClosedFileKeepAliveTime  << KByteToByteShift),	// convert milliSecs -> microSecs (approximately)
       
  2095 	iDirtyDataFlushTime(KDefaultDirtyDataFlushTime  << KByteToByteShift)				// convert milliSecs -> microSecs (approximately)
       
  2096 	{
       
  2097 	iFlags = ConvertEnumToFlags(KDefaultFileCacheRead, KDefaultFileCacheReadAhead, KDefaultFileCacheWrite);
       
  2098 	}
       
  2099 
       
  2100 
       
  2101 TFileCacheFlags TFileCacheSettings::TFileCacheConfig::ConvertEnumToFlags(const TInt aFileCacheRead, const TInt aFileCacheReadAhead, const TInt aFileCacheWrite) 
       
  2102 	{
       
  2103 	TInt flags(0);
       
  2104 
       
  2105 	// read caching
       
  2106 	if (aFileCacheRead == EFileCacheFlagEnabled)
       
  2107 		flags|= EFileCacheReadEnabled;
       
  2108 	else if (aFileCacheRead == EFileCacheFlagOn)
       
  2109 		flags|= EFileCacheReadEnabled | EFileCacheReadOn;
       
  2110 
       
  2111 	// read ahead
       
  2112 	if (aFileCacheReadAhead == EFileCacheFlagEnabled)
       
  2113 		flags|= EFileCacheReadAheadEnabled;
       
  2114 	else if (aFileCacheReadAhead == EFileCacheFlagOn)
       
  2115 		flags|= EFileCacheReadAheadEnabled | EFileCacheReadAheadOn;
       
  2116 
       
  2117 	// write caching
       
  2118 	if (aFileCacheWrite == EFileCacheFlagEnabled)
       
  2119 		flags|= EFileCacheWriteEnabled;
       
  2120 	else if (aFileCacheWrite == EFileCacheFlagOn)
       
  2121 		flags|= EFileCacheWriteEnabled | EFileCacheWriteOn;
       
  2122 
       
  2123 	return TFileCacheFlags(flags);
       
  2124 	}
       
  2125 
       
  2126 
       
  2127 	
       
  2128 _LIT8(KLitSectionNameDrive,"Drive%C");
       
  2129 
       
  2130 static const TPtrC8 KCacheFlagEnumStrings[]=
       
  2131 	{
       
  2132 	_S8("OFF"),
       
  2133 	_S8("ENABLED"),
       
  2134 	_S8("ON"),
       
  2135 	_S8(""),	// terminator
       
  2136 	};
       
  2137 const TInt KMaxEnumLen = 7;
       
  2138 
       
  2139 void TFileCacheSettings::ReadEnum(const TDesC8& aSection, const TDesC8& aProperty, TInt32& aEnumVal, const TPtrC8* aEnumStrings)
       
  2140 	{
       
  2141 	TBuf8<KMaxEnumLen> buf;
       
  2142 	if (!F32Properties::GetString(aSection, aProperty, buf))
       
  2143 		return;
       
  2144 	TInt n;
       
  2145 	const TPtrC8* enumString;
       
  2146 	for (enumString=aEnumStrings, n=0; enumString->Length()!= 0; enumString++, n++)
       
  2147 		{
       
  2148 		if (buf.LeftTPtr(enumString->Length()).MatchF(*enumString) == 0)
       
  2149 			{
       
  2150 			aEnumVal = n;
       
  2151 			break;
       
  2152 			}
       
  2153 		}
       
  2154 	}
       
  2155 	
       
  2156 TInt TFileCacheSettings::ReadPropertiesFile(TInt aDriveNumber)
       
  2157 	{
       
  2158 	Init();
       
  2159 
       
  2160 
       
  2161 	if (!TGlobalFileCacheSettings::Enabled())
       
  2162 		return KErrNone;
       
  2163 
       
  2164 	TFileCacheConfig* driveCacheSettings;
       
  2165 	TInt r = GetFileCacheConfig(aDriveNumber, driveCacheSettings);
       
  2166 	if (r != KErrNone)
       
  2167 		return r;
       
  2168 
       
  2169 	// restore default settings in case they've been changed by SetFlags()
       
  2170 	driveCacheSettings->iFlags = TFileCacheConfig::ConvertEnumToFlags(KDefaultFileCacheRead, KDefaultFileCacheReadAhead, KDefaultFileCacheWrite);
       
  2171 
       
  2172 
       
  2173 	// Get file cache configuration settings for this drive
       
  2174 	// N.B. Size/length values are specified in Kilobytes, timer values in Milliseconds
       
  2175 	TBuf8<8> sectionName;
       
  2176 	sectionName.Format(KLitSectionNameDrive, 'A' + aDriveNumber);
       
  2177 
       
  2178 	TInt32 val;
       
  2179 	//  Read FileCacheSize
       
  2180 	if (F32Properties::GetInt(sectionName, _L8("FileCacheSize"),		val))
       
  2181 		driveCacheSettings->iCacheSize = val << KByteToByteShift;
       
  2182 
       
  2183 	// ensure read-ahead len is not greater than the cache size
       
  2184 	driveCacheSettings->iMaxReadAheadLen = 
       
  2185 		Min(driveCacheSettings->iMaxReadAheadLen, driveCacheSettings->iCacheSize);
       
  2186 
       
  2187 	// Read FileCacheReadAsync
       
  2188 	TBool bVal;
       
  2189 	if (F32Properties::GetBool(sectionName, _L8("FileCacheReadAsync"), bVal))
       
  2190 		driveCacheSettings->iFileCacheReadAsync = bVal;
       
  2191 
       
  2192 	// Read FairSchedulingLen
       
  2193 	if (F32Properties::GetInt(sectionName, _L8("FairSchedulingLen"),	val))
       
  2194 		driveCacheSettings->iFairSchedulingLen = val << KByteToByteShift;
       
  2195 
       
  2196 	// Read ClosedFileKeepAliveTime - convert miliSecs to microSecs (approximately)
       
  2197 	if (F32Properties::GetInt(sectionName, _L8("ClosedFileKeepAliveTime"),	val))
       
  2198 		driveCacheSettings->iClosedFileKeepAliveTime = val << KByteToByteShift;
       
  2199 
       
  2200 	// Read DirtyDataFlushTime - convert miliSecs to microSecs (approximately)
       
  2201 	if (F32Properties::GetInt(sectionName, _L8("DirtyDataFlushTime"),	val))
       
  2202 		driveCacheSettings->iDirtyDataFlushTime = val << KByteToByteShift;
       
  2203 
       
  2204 	// get read, read-ahead and write states
       
  2205 	TInt32 readVal = KDefaultFileCacheRead;
       
  2206 	TInt32 readAheadVal = KDefaultFileCacheReadAhead;
       
  2207 	TInt32 writeVal = KDefaultFileCacheWrite;
       
  2208 
       
  2209 	ReadEnum(sectionName, _L8("FileCacheRead"), readVal, &KCacheFlagEnumStrings[0]);
       
  2210 	ReadEnum(sectionName, _L8("FileCacheReadAhead"), readAheadVal, &KCacheFlagEnumStrings[0]);
       
  2211 	ReadEnum(sectionName, _L8("FileCacheWrite"), writeVal, &KCacheFlagEnumStrings[0]);
       
  2212 	driveCacheSettings->iFlags = TFileCacheConfig::ConvertEnumToFlags(readVal, readAheadVal, writeVal);
       
  2213 
       
  2214 	__CACHE_PRINT7(_L("ReadPropertiesFile() drive %C flags %08X CacheSize %d FileCacheReadAsync %d iFairSchedulingLen %d iClosedFileKeepAliveTime %d iDirtyDataFlushTime %d\n"),
       
  2215 		aDriveNumber + 'A', 
       
  2216 		driveCacheSettings->iFlags, 
       
  2217 		driveCacheSettings->iCacheSize, 
       
  2218 		driveCacheSettings->iFileCacheReadAsync,
       
  2219 		driveCacheSettings->iFairSchedulingLen,
       
  2220 		driveCacheSettings->iClosedFileKeepAliveTime,
       
  2221 		driveCacheSettings->iDirtyDataFlushTime);
       
  2222 	
       
  2223 	return KErrNone;
       
  2224 	}
       
  2225 
       
  2226 
       
  2227 TInt TFileCacheSettings::GetFileCacheConfig(TInt aDrive, TFileCacheConfig*& aConfig)
       
  2228 	{
       
  2229 	Init();
       
  2230 
       
  2231 	aConfig = NULL;
       
  2232 
       
  2233 	TFileCacheConfig driveCacheSettingsToMatch(aDrive);
       
  2234 	
       
  2235 	TInt index = iFileCacheSettings->FindInUnsignedKeyOrder(driveCacheSettingsToMatch);
       
  2236 	if (index == KErrNotFound)
       
  2237 		{
       
  2238 		// create a new entry. 
       
  2239 		TInt r = iFileCacheSettings->InsertInUnsignedKeyOrder(driveCacheSettingsToMatch);
       
  2240 		if (r != KErrNone)
       
  2241 			return r;
       
  2242 		index = iFileCacheSettings->FindInUnsignedKeyOrder(driveCacheSettingsToMatch);
       
  2243 		__ASSERT_ALWAYS(index != KErrNotFound, Fault(ECacheSettingsNotFound));
       
  2244 		}
       
  2245 
       
  2246 	aConfig = &(*iFileCacheSettings)[index];
       
  2247 	return KErrNone;
       
  2248 	}
       
  2249 
       
  2250 void TFileCacheSettings::SetFlags(TInt aDrive, TFileCacheFlags aFlags)
       
  2251 	{
       
  2252 	TFileCacheConfig* driveCacheSettings;
       
  2253 	TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
       
  2254 	__ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
       
  2255 
       
  2256 	driveCacheSettings->iFlags = aFlags;
       
  2257 	}
       
  2258 
       
  2259 TFileCacheFlags TFileCacheSettings::Flags(TInt aDrive)
       
  2260 	{
       
  2261 	TFileCacheConfig* driveCacheSettings;
       
  2262 	TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
       
  2263 	__ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
       
  2264 
       
  2265 	return driveCacheSettings->iFlags;
       
  2266 	}
       
  2267 
       
  2268 void TFileCacheSettings::Init()
       
  2269 	{
       
  2270 	if (iFileCacheSettings == NULL)
       
  2271 		{
       
  2272 		iFileCacheSettings = new RArray<TFileCacheConfig>(KDriveCacheSettingsArrayGranularity, _FOFF(TFileCacheConfig, iDrive));
       
  2273 		__ASSERT_ALWAYS(iFileCacheSettings != NULL, Fault(ECacheSettingsInitFailed));
       
  2274 		}
       
  2275 	}
       
  2276 
       
  2277 
       
  2278 TBool TFileCacheSettings::FileCacheReadAsync(TInt aDrive)
       
  2279 	{
       
  2280 	TFileCacheConfig* driveCacheSettings;
       
  2281 	TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
       
  2282 	__ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
       
  2283 
       
  2284 	return driveCacheSettings->iFileCacheReadAsync;
       
  2285 	}
       
  2286 
       
  2287 TInt TFileCacheSettings::FairSchedulingLen(TInt aDrive)
       
  2288 	{
       
  2289 	TFileCacheConfig* driveCacheSettings;
       
  2290 	TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
       
  2291 	__ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
       
  2292 
       
  2293 	return driveCacheSettings->iFairSchedulingLen;
       
  2294 	}
       
  2295 
       
  2296 TInt TFileCacheSettings::MaxReadAheadLen(TInt aDrive)
       
  2297 	{
       
  2298 	TFileCacheConfig* driveCacheSettings;
       
  2299 	TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
       
  2300 	__ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
       
  2301 
       
  2302 	return driveCacheSettings->iMaxReadAheadLen;
       
  2303 	}
       
  2304 
       
  2305 TInt TFileCacheSettings::CacheSize(TInt aDrive)
       
  2306 	{
       
  2307 	TFileCacheConfig* driveCacheSettings;
       
  2308 	TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
       
  2309 	__ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
       
  2310 
       
  2311 	return driveCacheSettings->iCacheSize;
       
  2312 	}
       
  2313 
       
  2314 TInt TFileCacheSettings::ClosedFileKeepAliveTime(TInt aDrive)
       
  2315 	{
       
  2316 	TFileCacheConfig* driveCacheSettings;
       
  2317 	TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
       
  2318 	__ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
       
  2319 
       
  2320 	return driveCacheSettings->iClosedFileKeepAliveTime;
       
  2321 	}
       
  2322 
       
  2323 
       
  2324 TInt TFileCacheSettings::DirtyDataFlushTime(TInt aDrive)
       
  2325 	{
       
  2326 	TFileCacheConfig* driveCacheSettings;
       
  2327 	TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
       
  2328 	__ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
       
  2329 
       
  2330 	return driveCacheSettings->iDirtyDataFlushTime;
       
  2331 	}
       
  2332 
       
  2333 //************************************
       
  2334 // TClosedFileUtils
       
  2335 //************************************
       
  2336 
       
  2337 CFsObjectCon* TClosedFileUtils::iClosedFiles = NULL;
       
  2338 
       
  2339 void TClosedFileUtils::InitL()
       
  2340 	{
       
  2341 	iClosedFiles = TheContainer->CreateL();
       
  2342 	if (iClosedFiles == NULL)
       
  2343 		User::LeaveIfError(KErrNoMemory);
       
  2344 
       
  2345 	}
       
  2346 
       
  2347 
       
  2348 TBool TClosedFileUtils::IsClosed(CFileCache* aFileCache)
       
  2349 	{
       
  2350 	return (aFileCache->Container() == iClosedFiles);
       
  2351 	}
       
  2352 
       
  2353 TInt TClosedFileUtils::Count()
       
  2354 	{
       
  2355 	return iClosedFiles->Count();
       
  2356 	}
       
  2357 
       
  2358 CFileCache* TClosedFileUtils::At(TInt aIndex)
       
  2359 	{
       
  2360 	return (CFileCache*) (*iClosedFiles)[aIndex];
       
  2361 	}
       
  2362 
       
  2363 
       
  2364 // Add a closed file to closed file container
       
  2365 void TClosedFileUtils::AddL(CFileCache* aFileCache, TBool aLock)
       
  2366 	{
       
  2367 	if (aFileCache->iDriveThread)
       
  2368 		{
       
  2369 		iClosedFiles->AddL(aFileCache, aLock);
       
  2370 
       
  2371 		// start a timer after which file will be removed from closed queue
       
  2372 		CDriveThread* driveThread=NULL;
       
  2373 		TInt r = FsThreadManager::GetDriveThread(aFileCache->iDriveNum, &driveThread);
       
  2374 		if(r == KErrNone && driveThread != NULL)
       
  2375 			aFileCache->iClosedTimer.Start(driveThread, aFileCache->iClosedFileKeepAliveTime);
       
  2376 		}
       
  2377 	}
       
  2378 
       
  2379 
       
  2380 
       
  2381 // Remove a closed file from closed file container so that it can be re-opened
       
  2382 void TClosedFileUtils::ReOpen(CFileCache* aFileCache, TBool aLock)
       
  2383 	{
       
  2384 	// get the drive thread in case it has changed since the file was last open
       
  2385 	const TInt r = FsThreadManager::GetDriveThread(aFileCache->iDriveNum, &aFileCache->iDriveThread);
       
  2386 	if ((r == KErrNone) && aFileCache->iDriveThread)
       
  2387 		aFileCache->iClosedTimer.Stop();
       
  2388 
       
  2389 	iClosedFiles->Remove(aFileCache, aLock);
       
  2390 	}
       
  2391 
       
  2392 // Remove all closed files from closed file container and close them for good
       
  2393 void TClosedFileUtils::Remove()
       
  2394 	{
       
  2395 	RemoveFiles(NULL, NULL);
       
  2396 	}
       
  2397 
       
  2398 // Remove all closed files belonging to a particular TDrive from closed file container and close them for good
       
  2399 void TClosedFileUtils::Remove(TInt aDrvNumber)
       
  2400 	{
       
  2401 	RemoveFiles(&TClosedFileUtils::TestDrive, (TAny*) aDrvNumber);
       
  2402 	}
       
  2403 
       
  2404 // Remove a closed file from closed file container and close it for good
       
  2405 void TClosedFileUtils::Remove(CFileCache* aFileCache)
       
  2406 	{
       
  2407 	RemoveFiles(&TClosedFileUtils::TestFile, (TAny*) aFileCache);
       
  2408 	}
       
  2409 
       
  2410 void TClosedFileUtils::RemoveFiles(TTestFunc aTestFunc, TAny* aTestVal)
       
  2411 	{
       
  2412 	Lock();
       
  2413 
       
  2414 	TInt count = TClosedFileUtils::Count();
       
  2415 	while(count--)
       
  2416 		{
       
  2417 		CFileCache& file = *(CFileCache*)(*iClosedFiles)[count];
       
  2418 		if ((aTestFunc == NULL) || ((*aTestFunc)(file, aTestVal)))
       
  2419 			{
       
  2420 			iClosedFiles->Remove(&file, EFalse);
       
  2421 			file.Close();
       
  2422 			}
       
  2423 		}
       
  2424 
       
  2425 	Unlock();
       
  2426 	}
       
  2427 
       
  2428 
       
  2429 TBool TClosedFileUtils::TestDrive(CFileCache& aFileCache, TAny* aVal)
       
  2430 	{
       
  2431 	TBool r = (aFileCache.iDriveNum == (TInt) aVal)?(TBool) ETrue: (TBool) EFalse;
       
  2432 	if (r)
       
  2433 		{
       
  2434 		__CACHE_PRINT1(_L("CLOSEDFILES: TestDrive() closing %S\n"), &aFileCache.FileNameF() );
       
  2435 		}
       
  2436 	return r;
       
  2437 	}
       
  2438 TBool TClosedFileUtils::TestFile(CFileCache& aFileCache, TAny* aVal)
       
  2439 	{
       
  2440 	TBool r = (&aFileCache == ((CFileCache*) aVal))?(TBool)ETrue:(TBool)EFalse;
       
  2441 	if (r)
       
  2442 		{
       
  2443 		__CACHE_PRINT1(_L("CLOSEDFILES: TestFile() closing %S\n"), &aFileCache.FileNameF() );
       
  2444 		}
       
  2445 	return r;
       
  2446 	}
       
  2447 
       
  2448 	
       
  2449 	
       
  2450 void TClosedFileUtils::Lock()
       
  2451 	{
       
  2452 	iClosedFiles->Lock();
       
  2453 	}
       
  2454 
       
  2455 void TClosedFileUtils::Unlock()
       
  2456 	{
       
  2457 	iClosedFiles->Unlock();
       
  2458 	}
       
  2459 	
       
  2460