os/kernelhwsrv/userlibandfileserver/fileserver/sfile/sf_file_cache.cpp
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
     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