1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/kernelhwsrv/userlibandfileserver/fileserver/sfile/sf_file_cache.cpp Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,2460 @@
1.4 +// Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
1.5 +// All rights reserved.
1.6 +// This component and the accompanying materials are made available
1.7 +// under the terms of the License "Eclipse Public License v1.0"
1.8 +// which accompanies this distribution, and is available
1.9 +// at the URL "http://www.eclipse.org/legal/epl-v10.html".
1.10 +//
1.11 +// Initial Contributors:
1.12 +// Nokia Corporation - initial contribution.
1.13 +//
1.14 +// Contributors:
1.15 +//
1.16 +// Description:
1.17 +// f32\sfile\sf_file_cache.cpp
1.18 +//
1.19 +//
1.20 +
1.21 +/**
1.22 + @file
1.23 + @internalTechnology
1.24 +*/
1.25 +
1.26 +#include <e32std.h>
1.27 +#include <e32std_private.h>
1.28 +#include "sf_std.h"
1.29 +#include <e32uid.h>
1.30 +#include <e32wins.h>
1.31 +#include <f32file.h>
1.32 +#include <hal.h>
1.33 +#include "sf_file_cache.h"
1.34 +#include "sf_cache_man.h"
1.35 +#include "sf_cache_client.h"
1.36 +#include "sf_file_cache_defs.h"
1.37 +
1.38 +
1.39 +// disables flushing of stale cachelines before each write
1.40 +#define LAZY_WRITE
1.41 +
1.42 +enum TFileCacheFault
1.43 + {
1.44 + ECacheSettingsInitFailed,
1.45 + ECacheSettingsNotFound,
1.46 + ECacheSettingGetFailed,
1.47 + ECacheCodeFault,
1.48 + ECacheBadOperationIndex,
1.49 + ENoFileCache,
1.50 + EFileAlreadyClosed,
1.51 + EClosingDirtyFile,
1.52 + ECompletingWriteWithDataRemaining,
1.53 + EPosBeyondSize,
1.54 + EMsgAlreadyCompleted,
1.55 + EBadRetCode,
1.56 + EInternalError,
1.57 + ERequestUncancelled,
1.58 + ELockAlreadyOpen,
1.59 + EBadSegmentCount,
1.60 + EReNewingOpenCache,
1.61 + EClosingUnNamedFile,
1.62 + EFileNameAlreadyOwned,
1.63 + EClosedFileHasNoName,
1.64 + EReOpeningUnNamedFile,
1.65 + EStartingDirtyAWrongStage,
1.66 + EUnexpectedReNewLFailure,
1.67 + EDirtyDataOwnerNull,
1.68 + EFlushingWithSessionNull,
1.69 + };
1.70 +
1.71 +
1.72 +LOCAL_C void Fault(TFileCacheFault aFault)
1.73 +//
1.74 +// Report a fault in the file cache
1.75 +//
1.76 + {
1.77 + User::Panic(_L("FSFILECACHE"), aFault);
1.78 + }
1.79 +
1.80 +const TInt KMinSequentialReadsBeforeReadAhead = 3;
1.81 +
1.82 +//************************************
1.83 +// CFileCache
1.84 +//************************************
1.85 +
1.86 +inline TBool ReadCachingEnabled(CFileShare& aShare)
1.87 + {
1.88 + TUint mode = aShare.iMode;
1.89 +
1.90 + TInt fileCacheFlags = TFileCacheSettings::Flags(aShare.File().Drive().DriveNumber());
1.91 +
1.92 + if (((mode & EFileReadBuffered) && (fileCacheFlags & (EFileCacheReadEnabled | EFileCacheReadOn))) ||
1.93 + (((mode & EFileReadDirectIO) == 0) && (fileCacheFlags & EFileCacheReadOn)))
1.94 + {
1.95 + return ETrue;
1.96 + }
1.97 + else
1.98 + {
1.99 + return EFalse;
1.100 + }
1.101 + }
1.102 +
1.103 +inline TBool WriteCachingEnabled(CFileShare& aShare)
1.104 + {
1.105 + TUint mode = aShare.iMode;
1.106 +
1.107 + TInt fileCacheFlags = TFileCacheSettings::Flags(aShare.File().Drive().DriveNumber());
1.108 +
1.109 + if ((mode & EFileWrite) == 0)
1.110 + {
1.111 + return EFalse;
1.112 + }
1.113 + else if (((mode & EFileWriteBuffered) && (fileCacheFlags & (EFileCacheWriteEnabled | EFileCacheWriteOn))) ||
1.114 + (((mode & EFileWriteDirectIO) == 0) && (fileCacheFlags & EFileCacheWriteOn)))
1.115 + {
1.116 + return ETrue;
1.117 + }
1.118 + else
1.119 + {
1.120 + return EFalse;
1.121 + }
1.122 + }
1.123 +
1.124 +void CFileCache::SetFileCacheFlags(CFileShare& aShare)
1.125 + {
1.126 + TInt fileCacheFlags = TFileCacheSettings::Flags(iDriveNum);
1.127 +
1.128 + TUint& mode = aShare.iMode;
1.129 +
1.130 + // enable/disable read ahead
1.131 + if (((mode & EFileReadAheadOn) && (fileCacheFlags & (EFileCacheReadAheadEnabled | EFileCacheReadAheadOn))) ||
1.132 + (((mode & EFileReadAheadOff) == 0) && (fileCacheFlags & EFileCacheReadAheadOn)))
1.133 + {
1.134 + __CACHE_PRINT(_L("CACHEFILE: EFileCacheReadAhead ENABLED"));
1.135 + mode|= EFileReadAheadOn;
1.136 + }
1.137 + else
1.138 + {
1.139 + __CACHE_PRINT(_L("CACHEFILE: EFileCacheReadAhead disabled"));
1.140 + mode&= ~EFileReadAheadOn;
1.141 + }
1.142 +
1.143 + // enable/disable read caching
1.144 + if (ReadCachingEnabled(aShare))
1.145 + {
1.146 + __CACHE_PRINT(_L("CACHEFILE: EFileCacheRead ENABLED"));
1.147 + mode|= EFileReadBuffered;
1.148 + }
1.149 + else
1.150 + {
1.151 + __CACHE_PRINT(_L("CACHEFILE: EFileCacheRead disabled"));
1.152 + // if read caching is off, turn off read-ahead too
1.153 + mode&= ~(EFileReadBuffered | EFileReadAheadOn);
1.154 + }
1.155 +
1.156 + // enable/disable write caching
1.157 + if (WriteCachingEnabled(aShare))
1.158 + {
1.159 + __CACHE_PRINT(_L("CACHEFILE: EFileCacheWrite ENABLED"));
1.160 + mode|= EFileWriteBuffered;
1.161 + }
1.162 + else
1.163 + {
1.164 + __CACHE_PRINT(_L("CACHEFILE: EFileCacheWrite disabled"));
1.165 + mode&= ~EFileWriteBuffered;
1.166 + }
1.167 +
1.168 + }
1.169 +
1.170 +void CFileCache::ConstructL(CFileShare& aShare)
1.171 + {
1.172 + iDrive = &aShare.File().Drive();
1.173 + iDriveNum = iDrive->DriveNumber();
1.174 + iMount=&iDrive->CurrentMount();
1.175 +
1.176 + DoInitL(iDriveNum);
1.177 +
1.178 +
1.179 + CCacheManager* manager = CCacheManagerFactory::CacheManager();
1.180 +
1.181 + iCacheClient = manager->CreateClientL();
1.182 + manager->RegisterClient(*iCacheClient);
1.183 +
1.184 +
1.185 + TInt segmentSize = SegmentSize();
1.186 + TInt segmentSizeMask = I64LOW(SegmentSizeMask());
1.187 + // Get file cache size
1.188 + iCacheSize = TFileCacheSettings::CacheSize(iDriveNum);
1.189 + // must be at least one segment
1.190 + iCacheSize = Max(iCacheSize, segmentSize);
1.191 + // round up to nearest whole segment
1.192 + iCacheSize = (iCacheSize + segmentSize - 1) & segmentSizeMask;
1.193 +
1.194 + // Get max read-ahead length
1.195 + iMaxReadAheadLen = TFileCacheSettings::MaxReadAheadLen(iDriveNum);
1.196 + // must be at least one segment
1.197 + iMaxReadAheadLen = Max(iMaxReadAheadLen, segmentSize);
1.198 + // round up to nearest whole segment
1.199 + iMaxReadAheadLen = (iMaxReadAheadLen + segmentSize - 1) & segmentSizeMask;
1.200 + // read-ahead should not be greater than the cache size minus one segment
1.201 + iMaxReadAheadLen = Min(iMaxReadAheadLen, iCacheSize - segmentSize);
1.202 + // ... or greater than one cacheline (128K should be enough !)
1.203 + iMaxReadAheadLen = Min(iMaxReadAheadLen, iCacheClient->CacheLineSize());
1.204 +
1.205 + iFileCacheReadAsync = TFileCacheSettings::FileCacheReadAsync(iDriveNum);
1.206 +
1.207 + iClosedFileKeepAliveTime = TFileCacheSettings::ClosedFileKeepAliveTime(iDriveNum);
1.208 + iDirtyDataFlushTime = TFileCacheSettings::DirtyDataFlushTime(iDriveNum);
1.209 +
1.210 + // Calculate max number of segments to cache
1.211 + TInt maxSegmentsToCache = iCacheSize >> SegmentSizeLog2();
1.212 +
1.213 + __CACHE_PRINT1(_L("CACHEFILE: maxSegmentsToCache %d"), maxSegmentsToCache);
1.214 +
1.215 + iCacheClient->SetMaxSegments(maxSegmentsToCache);
1.216 +
1.217 + CFileCache* fileCache = ReNewL(aShare);
1.218 + __ASSERT_ALWAYS(fileCache != NULL, Fault(EUnexpectedReNewLFailure));
1.219 + }
1.220 +
1.221 +CFileCache* CFileCache::NewL(CFileShare& aShare)
1.222 + {
1.223 + __CACHE_PRINT(_L("CACHEFILE: CFileCache::NewL()"));
1.224 +
1.225 + CFileCB* file = &aShare.File();
1.226 + if ((CCacheManagerFactory::CacheManager() == NULL) ||
1.227 + (!ReadCachingEnabled(aShare) && !WriteCachingEnabled(aShare)) ||
1.228 + (FsThreadManager::IsDriveSync(file->Drive().DriveNumber(),EFalse)))
1.229 + return NULL;
1.230 +
1.231 + CFileCache* fileCache = new(ELeave) CFileCache();
1.232 + CleanupClosePushL(*fileCache);
1.233 + fileCache->ConstructL(aShare);
1.234 + CleanupStack::Pop(1, fileCache);
1.235 + return fileCache;
1.236 + }
1.237 +
1.238 +CFileCache* CFileCache::ReNewL(CFileShare& aShare)
1.239 + {
1.240 + __CACHE_PRINT(_L("CACHEFILE: CFileCache::ReNewL()"));
1.241 +
1.242 + // check not already open i.e. attached to a CFileCB
1.243 + __ASSERT_DEBUG(iFileCB == NULL, Fault(EReNewingOpenCache));
1.244 +
1.245 + // make sure the drive thread exists (the mount may have been mounted
1.246 + // synchronously since the drive was last open)
1.247 + const TInt r = FsThreadManager::GetDriveThread(iDriveNum, &iDriveThread);
1.248 + if ((r!= KErrNone) || !iDriveThread)
1.249 + return NULL;
1.250 +
1.251 + // if re-opening in DirectIo mode, destroy the file cache
1.252 + if (!ReadCachingEnabled(aShare) && !WriteCachingEnabled(aShare))
1.253 + {
1.254 + Close();
1.255 + return NULL;
1.256 + }
1.257 +
1.258 + SetFileCacheFlags(aShare);
1.259 +
1.260 + // assign ownership of this object to aFileCB before any leaves occur
1.261 + iFileCB = &aShare.File();
1.262 + iFileCB->iBody->iFileCache = this;
1.263 +
1.264 + __ASSERT_DEBUG(iLock.Handle() == KNullHandle, Fault(ELockAlreadyOpen));
1.265 + User::LeaveIfError(iLock.CreateLocal());
1.266 +
1.267 + // delete the file name to save heap space (it's only needed
1.268 + // while the file is on the closed queue so that it can be matched)
1.269 + delete iFileNameF;
1.270 + iFileNameF = NULL;
1.271 +
1.272 + return this;
1.273 + }
1.274 +
1.275 +void CFileCache::Init(CFileShare& aShare)
1.276 + {
1.277 + SetFileCacheFlags(aShare);
1.278 + }
1.279 +
1.280 +CFileCache::CFileCache() :
1.281 + iClosedTimer(ClosedTimerEvent, this),
1.282 + iDirtyTimer(DirtyTimerEvent, this)
1.283 + {
1.284 + }
1.285 +
1.286 +CFileCache::~CFileCache()
1.287 + {
1.288 + iLock.Close();
1.289 +
1.290 + // stop the owning CFileCB from pointing to an about-to-be deleted object
1.291 + if (iFileCB && iFileCB->iBody)
1.292 + iFileCB->iBody->iFileCache = NULL;
1.293 +
1.294 + CCacheManagerFactory::CacheManager()->DeregisterClient(*iCacheClient);
1.295 +
1.296 + delete iCacheClient;
1.297 +
1.298 + delete iFileNameF;
1.299 + }
1.300 +
1.301 +
1.302 +TInt CFileCache::ClosedTimerEvent(TAny* aFileCache)
1.303 + {
1.304 + TClosedFileUtils::Remove((CFileCache*) aFileCache);
1.305 + return KErrNone;
1.306 + }
1.307 +
1.308 +TInt CFileCache::DirtyTimerEvent(TAny* aFileCache)
1.309 + {
1.310 + // Cannot report errors here
1.311 + // coverity [unchecked_value]
1.312 + (void)((CFileCache*) aFileCache)->FlushDirty();
1.313 +
1.314 + return KErrNone;
1.315 + }
1.316 +
1.317 +
1.318 +
1.319 +void CFileCache::Close()
1.320 + {
1.321 + __CACHE_PRINT1(_L("CFileCache::Close() 0x%x"),this);
1.322 +
1.323 + TInt r = KErrNone;
1.324 +
1.325 +#ifdef _DEBUG
1.326 + if (iCacheClient) // NB Object may not have been fully constructed
1.327 + {
1.328 + TInt64 pos;
1.329 + TUint8* addr;
1.330 + __ASSERT_DEBUG(((iCacheClient->FindFirstDirtySegment(pos, addr)) == 0), Fault(EClosingDirtyFile));
1.331 + __ASSERT_DEBUG(!iDirtyDataOwner, Fault(EClosingDirtyFile));
1.332 + }
1.333 +#endif
1.334 +
1.335 +
1.336 + __CACHE_PRINT2(_L("CFileCache::Close() iFileCB %08X IsClosed %d"),
1.337 + iFileCB, TClosedFileUtils::IsClosed(this));
1.338 + // if not already closed move to closed file queue
1.339 + if (iFileCB != NULL &&
1.340 + !iFileCB->DeleteOnClose() &&
1.341 + !TClosedFileUtils::IsClosed(this) &&
1.342 + IsDriveThread())
1.343 + {
1.344 + // add to ClosedFiles container
1.345 + __CACHE_PRINT1(_L("CLOSEDFILES: Adding %S\n"), &iFileCB->FileNameF() );
1.346 + TRAP(r, TClosedFileUtils::AddL(this, ETrue));
1.347 +
1.348 + // Acquire ownership of the CFileCB's file name
1.349 + __ASSERT_DEBUG(iFileCB->iFileNameF, Fault(EClosingUnNamedFile));
1.350 + __ASSERT_DEBUG(iFileNameF == NULL, Fault(EFileNameAlreadyOwned));
1.351 + iFileNameF = iFileCB->iFileNameF;
1.352 + iNameHash = iFileCB->iNameHash;
1.353 + iFileCB->iFileNameF = NULL;
1.354 +
1.355 + // remove pointer to owning CFileCB as this is called from CFileCB's destructor
1.356 + iFileCB = NULL;
1.357 +
1.358 + // Successfully moved file !
1.359 + if (r == KErrNone)
1.360 + {
1.361 + // close the RFastLock object here to prevent OOM kernel tests from failing
1.362 + iLock.Close();
1.363 + return;
1.364 + }
1.365 + }
1.366 +
1.367 + iClosedTimer.Stop();
1.368 +
1.369 + // if already closed, close for good. N.B. CFsObject::DoClose() will
1.370 + // cause it to be removed from the ClosedFiles container
1.371 + CFsDispatchObject::Close();
1.372 + }
1.373 +
1.374 +
1.375 +CMountCB& CFileCache::Mount() const
1.376 + {
1.377 + return *iMount;
1.378 + }
1.379 +
1.380 +CFileCB* CFileCache::FileCB()
1.381 + {
1.382 + return iFileCB;
1.383 + }
1.384 +
1.385 +
1.386 +
1.387 +TInt CFileCache::SegmentSize() const
1.388 + {
1.389 + return iCacheClient->SegmentSize();
1.390 + }
1.391 +
1.392 +TInt CFileCache::SegmentSizeLog2() const
1.393 + {
1.394 + return iCacheClient->SegmentSizeLog2();
1.395 + }
1.396 +
1.397 +TInt64 CFileCache::SegmentSizeMask() const
1.398 + {
1.399 + return iCacheClient->SegmentSizeMask();
1.400 + }
1.401 +
1.402 +/**
1.403 +Sets the cached file size
1.404 +
1.405 +@param aSize The size of the file.
1.406 +*/
1.407 +void CFileCache::SetSize64(TInt64 aSize)
1.408 + {
1.409 + __e32_atomic_store_ord64(&iSize64, aSize);
1.410 + }
1.411 +
1.412 +TDrive& CFileCache::Drive() const
1.413 + {
1.414 + return *iDrive;
1.415 + }
1.416 +
1.417 +TUint32 CFileCache::NameHash() const
1.418 + {
1.419 + return(iNameHash);
1.420 + }
1.421 +
1.422 +HBufC& CFileCache::FileNameF() const
1.423 + {
1.424 + __ASSERT_DEBUG(iFileNameF, Fault(EClosedFileHasNoName));
1.425 + return(*iFileNameF);
1.426 + }
1.427 +
1.428 +
1.429 +
1.430 +
1.431 +TBool CFileCache::IsDriveThread()
1.432 + {
1.433 + return FsThreadManager::IsDriveThread(iDriveNum, EFalse);
1.434 + }
1.435 +
1.436 +
1.437 +void CFileCache::ResetReadAhead()
1.438 + {
1.439 +// iSequentialReads = 0;
1.440 + iReadAheadLen = 0;
1.441 + iReadAheadPos = 0;
1.442 + }
1.443 +
1.444 +/**
1.445 +ReadAhead() -
1.446 +dispatches a new message to fill the read cache if
1.447 +(ReadAheadPos - ShareReadPos) <= (ReadAheadLen)
1.448 +
1.449 +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1.450 + ^ ^
1.451 + |----- ReadAheadLen ----------------->
1.452 + | |
1.453 + ShareReadPos ReadAheadPos
1.454 +
1.455 +Every successful read-ahead doubles ReadAheadLen until it reaches the maximum
1.456 +(KDefaultFileCacheMaxReadAheadLen).
1.457 +*/
1.458 +void CFileCache::ReadAhead(CFsMessageRequest& aMsgRequest, TUint aMode)
1.459 + {
1.460 + if (!(aMode & EFileReadAheadOn) ||
1.461 + (iSequentialReads < KMinSequentialReadsBeforeReadAhead) ||
1.462 + iMaxReadAheadLen == 0)
1.463 + return;
1.464 +
1.465 +
1.466 + TInt segmentSize = SegmentSize();
1.467 + TInt64 segmentSizeMask = SegmentSizeMask();
1.468 +
1.469 + // if the read-ahead pos has been reset to zero, then the read-ahead
1.470 + // position and length must be re-calculated
1.471 +
1.472 + TBool resetting = (iReadAheadPos == 0)?(TBool)ETrue:(TBool)EFalse;
1.473 + if (resetting)
1.474 + {
1.475 + iReadAheadPos = (iLastReadPos + segmentSize - 1) & segmentSizeMask;
1.476 +
1.477 + // ensure read ahead len at least as big as last read
1.478 + iReadAheadLen = Max(iReadAheadLen, iLastReadLen);
1.479 +
1.480 + // round up to a segment size
1.481 + iReadAheadLen = (iReadAheadLen + segmentSize - 1) & (TInt) segmentSizeMask;
1.482 +
1.483 + // ensure read ahead len at least as big as 1 segments
1.484 + iReadAheadLen = Max(iReadAheadLen, segmentSize);
1.485 +
1.486 + // ensure read ahead len not greater than the maximum read ahead len
1.487 + iReadAheadLen = Min(iReadAheadLen, iMaxReadAheadLen);
1.488 +
1.489 + iReadAheadRequest = NULL;
1.490 + }
1.491 +
1.492 + TInt bytesBuffered = (TInt) (iReadAheadPos - iLastReadPos);
1.493 + TInt bytesNotBuffered = iMaxReadAheadLen - bytesBuffered;
1.494 +
1.495 +
1.496 + // if read-ahead buffer len > current read-ahead len OR
1.497 + // read-ahead buffer is more than half full, do nothing
1.498 + if ((iReadAheadRequest) ||
1.499 + (bytesBuffered > iReadAheadLen) ||
1.500 + (bytesBuffered > (iMaxReadAheadLen>>1)) ||
1.501 + (iReadAheadPos >= iSize64))
1.502 + {
1.503 + return;
1.504 + }
1.505 +
1.506 + // double the read-ahead length - unless this is the first
1.507 + if (!resetting)
1.508 + iReadAheadLen<<= 1;
1.509 +
1.510 + // ensure read ahead len not greater than the free space available in buffer
1.511 + iReadAheadLen = Min(iReadAheadLen, bytesNotBuffered);
1.512 +
1.513 + // round up to a segment size
1.514 + iReadAheadLen = (iReadAheadLen + segmentSize - 1) & (TInt) segmentSizeMask;
1.515 +
1.516 +#if defined (_DEBUG_READ_AHEAD)
1.517 + TInt64 oldReadAheadPos = iReadAheadPos;
1.518 +#endif
1.519 +
1.520 + DoReadAhead(aMsgRequest, aMode);
1.521 +
1.522 +
1.523 +// RDebug::Print(_L("Buffered: old %d new %d"), bytesBuffered, (iReadAheadPos == 0)?bytesBuffered:iReadAheadPos - iLastReadPos);
1.524 +
1.525 +#if defined (_DEBUG_READ_AHEAD)
1.526 +RDebug::Print(_L("Buffered: old %d new %d iLastReadPos %d ReadAheadPos old %d new %d iReadAheadLen %d"),
1.527 + bytesBuffered,
1.528 + (TInt) ((iReadAheadPos == 0)?bytesBuffered:iReadAheadPos - iLastReadPos),
1.529 + I64LOW(iLastReadPos),
1.530 + I64LOW(oldReadAheadPos),
1.531 + I64LOW(iReadAheadPos),
1.532 + iReadAheadLen);
1.533 +#endif // (_DEBUG_READ_AHEAD)
1.534 +
1.535 +
1.536 + return;
1.537 + }
1.538 +
1.539 +
1.540 +void CFileCache::DoReadAhead(CFsMessageRequest& aMsgRequest, TUint aMode)
1.541 + {
1.542 +
1.543 +
1.544 + if (iCacheClient->FindSegment(iReadAheadPos) != NULL)
1.545 + {
1.546 + // if read ahead pos is already cached, then synchronous reads have caught up,
1.547 + // so reset read ahead pos.
1.548 +#if defined (_DEBUG_READ_AHEAD)
1.549 + RDebug::Print(_L("ReadAhead: pos %d already cached"), I64LOW(iReadAheadPos));
1.550 +#endif
1.551 + iReadAheadPos = 0;
1.552 + return;
1.553 + }
1.554 +
1.555 +
1.556 + CFsClientMessageRequest* newRequest = NULL;
1.557 + TInt r = AllocateRequest(newRequest, EFalse, aMsgRequest.Session());
1.558 + if (r != KErrNone)
1.559 + return;
1.560 +
1.561 + r = newRequest->PushOperation(
1.562 + iReadAheadPos,
1.563 + iReadAheadLen,
1.564 + (TUint8*) NULL,
1.565 + 0, // aOffset
1.566 + NULL); // aCallback
1.567 + if (r != KErrNone)
1.568 + {
1.569 + newRequest->Free();
1.570 + newRequest = NULL;
1.571 + return;
1.572 + }
1.573 +
1.574 + __CACHE_PRINT2(_L("TFsFileRead: ReadAhead pos %ld len %d"), newRequest->CurrentOperation().iReadWriteArgs.iPos, newRequest->CurrentOperation().iReadWriteArgs.iTotalLength);
1.575 +
1.576 +// RDebug::Print(_L("ReadH:\tpos %d\tlen %d"), I64LOW(newRequest->CurrentOperation().iReadWriteArgs.iPos), newRequest->CurrentOperation().iReadWriteArgs.iTotalLength);
1.577 +
1.578 + r = ReadBuffered(*newRequest, aMode);
1.579 + if (r != CFsRequest::EReqActionContinue)
1.580 + {
1.581 + // if read ahead pos is already cached, then synchronous reads have caught up,
1.582 + // so reset read ahead pos.
1.583 + if (r == CFsRequest::EReqActionComplete)
1.584 + {
1.585 +#if defined (_DEBUG_READ_AHEAD)
1.586 + RDebug::Print(_L("ReadAhead pos %d ALREADY DONE !!!"), I64LOW(iReadAheadPos));
1.587 +#endif
1.588 + iReadAheadPos = 0;
1.589 + }
1.590 +
1.591 + newRequest->PopOperation();
1.592 + newRequest->Free();
1.593 + newRequest = NULL;
1.594 + return;
1.595 + }
1.596 +
1.597 + iReadAheadPos = iReadAheadPos + iReadAheadLen;
1.598 + iReadAheadRequest = newRequest;
1.599 +
1.600 +#if defined (_DEBUG_READ_AHEAD)
1.601 + RDebug::Print(_L("Dispatching ReadAhead with %s priority"), iFileCacheReadAsync?_S16("HIGH"):_S16("LOW"));
1.602 +#endif
1.603 + // If if media driver reads are synchronous (i.e. not interrupt driven) dispatch read-ahead
1.604 + // with low priority so that it doesn't prevent client thread from running
1.605 + newRequest->Dispatch(
1.606 + EFalse, // don't init
1.607 + iFileCacheReadAsync?(TBool)EFalse:(TBool)ETrue,
1.608 + EFalse); // dispatch to back
1.609 + }
1.610 +
1.611 +TInt CFileCache::CompleteRead(CFsRequest* aRequest)
1.612 + {
1.613 + CFsMessageRequest& msgRequest = *(CFsMessageRequest*) aRequest;
1.614 +
1.615 + __ASSERT_DEBUG(msgRequest.CurrentOperationPtr() != NULL, Fault(ECacheBadOperationIndex));
1.616 +
1.617 + CFileShare* share;
1.618 + CFileCB* file;
1.619 + GetFileFromScratch(aRequest, share, file);
1.620 + CFileCache* fileCache = file->FileCache();
1.621 +
1.622 + __ASSERT_DEBUG(fileCache != NULL, Fault(ENoFileCache));
1.623 +#ifdef _DEBUG
1.624 + TInt cancelling = (msgRequest.LastError() == KErrCancel)?(TBool)ETrue:(TBool)EFalse;
1.625 +#endif
1.626 +
1.627 + TUint mode = share?share->iMode : EFileReadBuffered;
1.628 + TInt r = fileCache->ReadBuffered(msgRequest, mode);
1.629 +
1.630 + // if this request has been cancelled we mustn't dispatch it again -
1.631 + // we still need to call state machine however so that any locked segments can be unlocked
1.632 +
1.633 +
1.634 + TInt lastError = msgRequest.LastError();
1.635 +
1.636 +#ifdef _DEBUG
1.637 + __ASSERT_DEBUG(!cancelling || msgRequest.LastError() == KErrCancel, Fault(ERequestUncancelled));
1.638 +#endif
1.639 +
1.640 + if (lastError == KErrCancel)
1.641 + r = CFsRequest::EReqActionComplete;
1.642 +
1.643 + return r;
1.644 + }
1.645 +
1.646 +/**
1.647 +ReadBuffered - attempts to read from cache.
1.648 +Called from TFsFileRead::Initialise() and CFileCache::CompleteRead()
1.649 +
1.650 +@return CFsRequest::EReqActionComplete if request entirely satisfied
1.651 + CFsRequest::EReqActionContinue if request not yet complete.
1.652 + Results in transition from :
1.653 + TFsFileRead::PostInitialise() to TFsFileRead::DoRequestL() or
1.654 + CFileCache::CompleteRead() to TFsFileRead::DoRequestL()
1.655 + CFsRequest::EReqActionBusy if filecache is "busy"
1.656 +*/
1.657 +TInt CFileCache::ReadBuffered(CFsMessageRequest& aMsgRequest, TUint aMode)
1.658 + {
1.659 + iLock.Wait();
1.660 +
1.661 + CFsClientMessageRequest* newRequest = NULL;
1.662 +
1.663 + __CACHE_PRINT2(_L("CFileCache::ReadBuffered() pos %d, len %d"), I64LOW(aMsgRequest.CurrentOperation().iReadWriteArgs.iPos), aMsgRequest.CurrentOperation().iReadWriteArgs.iTotalLength);
1.664 + TInt r = DoReadBuffered(aMsgRequest, aMode, newRequest);
1.665 +
1.666 + iLock.Signal();
1.667 +
1.668 + if (newRequest)
1.669 + newRequest->Dispatch();
1.670 +
1.671 + return r;
1.672 + }
1.673 +
1.674 +
1.675 +void CFileCache::UpdateSharePosition(CFsMessageRequest& aMsgRequest, TMsgOperation& aCurrentOperation)
1.676 + {
1.677 + // update the file share's position if this request came from client
1.678 + if (aCurrentOperation.iClientRequest)
1.679 + {
1.680 + CFileShare* share = (CFileShare*)aMsgRequest.ScratchValue();
1.681 + if (aMsgRequest.LastError() == KErrNone)
1.682 + {
1.683 + __e32_atomic_store_ord64(&share->iPos, aCurrentOperation.iReadWriteArgs.iPos);
1.684 + }
1.685 + }
1.686 + }
1.687 +
1.688 +TInt CFileCache::DoReadBuffered(CFsMessageRequest& aMsgRequest, TUint aMode, CFsClientMessageRequest*& aNewRequest)
1.689 + {
1.690 + enum states
1.691 + {
1.692 + EStBegin=0,
1.693 + EStReadFromCache,
1.694 + EStReadFromDiskComplete,
1.695 + EStCopyToClient,
1.696 + EStReStart,
1.697 + EStEnd
1.698 + };
1.699 +
1.700 +
1.701 + TInt retCode = CFsRequest::EReqActionComplete;
1.702 + TInt& lastError = aMsgRequest.LastError();
1.703 + TBool driveThread = IsDriveThread();
1.704 +
1.705 + // temp storage for transition between EStReadFromCache / EStReadFromDiskComplete and EStCopyToClient
1.706 + TInt readLen = 0;
1.707 + TUint8* addr = NULL;
1.708 + TInt64 segmentStartPos = 0;
1.709 +
1.710 + TInt segmentSize = SegmentSize();
1.711 + TInt segmentSizeLog2 = SegmentSizeLog2();
1.712 + TInt64 segmentSizeMask = SegmentSizeMask();
1.713 +
1.714 + for(;;)
1.715 + {
1.716 + TMsgOperation* currentOperation = &aMsgRequest.CurrentOperation();
1.717 + TInt64& currentPos = currentOperation->iReadWriteArgs.iPos;
1.718 + TInt& totalLen = currentOperation->iReadWriteArgs.iTotalLength;
1.719 + TInt& currentOffset = currentOperation->iReadWriteArgs.iOffset;
1.720 + TBool readAhead = (currentOperation->iReadWriteArgs.iData == NULL)?(TBool)ETrue:(TBool)EFalse;
1.721 +
1.722 + switch(currentOperation->iState)
1.723 + {
1.724 + case EStBegin:
1.725 +
1.726 +
1.727 + // if EFileReadDirectIO, flush write cache and read direct from disk
1.728 + if (!(aMode & EFileReadBuffered))
1.729 + {
1.730 + iLock.Signal();
1.731 + TInt r = FlushDirty(&aMsgRequest);
1.732 + iLock.Wait();
1.733 + if (r == CFsRequest::EReqActionBusy)
1.734 + return CFsRequest::EReqActionBusy;
1.735 + return CFsRequest::EReqActionContinue; // read uncached
1.736 + }
1.737 +
1.738 + if (currentPos > iSize64)
1.739 + currentPos = iSize64;
1.740 +
1.741 + currentOperation->iState = EStReadFromCache;
1.742 +
1.743 + // count the number of sequential reads for read-ahead
1.744 + if (currentOperation->iClientRequest)
1.745 + {
1.746 + if (currentPos == iLastReadPos)
1.747 + {
1.748 + iSequentialReads++;
1.749 + }
1.750 + else
1.751 + {
1.752 + iSequentialReads = 0;
1.753 + ResetReadAhead();
1.754 + }
1.755 + iLastReadPos = currentPos + totalLen;
1.756 + iLastReadLen = totalLen;
1.757 + }
1.758 +
1.759 +
1.760 + // fall into...
1.761 +
1.762 + case EStReadFromCache:
1.763 + {
1.764 + // reading past end of file ?
1.765 + if (currentPos + totalLen > iSize64)
1.766 + totalLen = (TInt) (iSize64 - currentPos);
1.767 +
1.768 +
1.769 + if (totalLen == 0 || lastError != KErrNone)
1.770 + {
1.771 + currentOperation->iState = EStEnd;
1.772 + break;
1.773 + }
1.774 +
1.775 + segmentStartPos = currentPos & segmentSizeMask;
1.776 +
1.777 +
1.778 + TInt64 endPos = currentPos + totalLen;
1.779 + TUint maxLenToRead = (TUint)Min((TInt64)(iCacheClient->CacheLineSize()), (endPos - segmentStartPos));
1.780 + TInt maxSegments = (maxLenToRead + segmentSize - 1) >> segmentSizeLog2;
1.781 +
1.782 +
1.783 + TInt segmentCount = maxSegments;
1.784 + TInt filledSegmentCount;
1.785 + TInt lockError;
1.786 + addr = iCacheClient->FindAndLockSegments(segmentStartPos, segmentCount, filledSegmentCount, lockError, EFalse);
1.787 +
1.788 +
1.789 +#if defined (_DEBUG_READ_AHEAD)
1.790 + if (addr && readAhead)
1.791 + RDebug::Print(_L("READAHEAD CACHELINE ALREADY EXISTS POS %d"), I64LOW(segmentStartPos));
1.792 +#endif
1.793 +
1.794 + // if cacheline contains filled and empty segments, deal with these seperately
1.795 + // to simplify the code.....
1.796 + if (filledSegmentCount > 0 && segmentCount > filledSegmentCount)
1.797 + {
1.798 + segmentCount = Min(segmentCount, filledSegmentCount);
1.799 + }
1.800 +
1.801 + if (lockError == KErrInUse)
1.802 + {
1.803 + // cacheline in use (by other thread):
1.804 + // if this is a read-ahead, abandon it
1.805 + // otherwise re-post the request
1.806 + if (readAhead)
1.807 + {
1.808 + totalLen = 0;
1.809 + retCode = CFsRequest::EReqActionComplete;
1.810 + currentOperation->iState = EStEnd;
1.811 + }
1.812 + else
1.813 + {
1.814 +#if defined (_DEBUG_READ_AHEAD)
1.815 + RDebug::Print(_L("READC CACHELINE BUSY POS %d"), I64LOW(segmentStartPos));
1.816 +#endif
1.817 +
1.818 + return CFsRequest::EReqActionBusy;
1.819 + }
1.820 + break;
1.821 + }
1.822 +
1.823 + // if not found, try to allocate as many contiguous segments
1.824 + // as possible so that the number of reads issued to the media
1.825 + // driver is kept to a minumum
1.826 +
1.827 + if (addr == NULL) // not found ?
1.828 + {
1.829 + // if read len > size of the cache, don't read excess
1.830 + // through the cache as this is wasteful
1.831 + if (totalLen > iCacheSize)
1.832 + {
1.833 + TInt len = totalLen - iCacheSize;
1.834 + TInt r = aMsgRequest.PushOperation(
1.835 + currentPos, len,
1.836 + (TDesC8*) currentOperation->iReadWriteArgs.iData, currentOffset,
1.837 + CompleteRead,
1.838 + EStReadFromCache);
1.839 + if (r != KErrNone)
1.840 + {
1.841 + currentOperation->iState = EStReStart;
1.842 + break;
1.843 + }
1.844 +
1.845 + currentOffset+= len;
1.846 + currentPos+= len;
1.847 + totalLen-= len;
1.848 + return CFsRequest::EReqActionContinue;
1.849 + }
1.850 +
1.851 + // if position not cached postpone Initialise() to drive thread
1.852 + // as there may be a read ahead already in the queue
1.853 +
1.854 + if (!driveThread && !readAhead)
1.855 + {
1.856 +#if defined (_DEBUG_READ_AHEAD)
1.857 + RDebug::Print(_L("*** POSTING READ TO DRIVE THREAD POS %d ***"), I64LOW(segmentStartPos));
1.858 +#endif
1.859 + return CFsRequest::EReqActionBusy;
1.860 + }
1.861 +
1.862 + segmentCount = maxSegments;
1.863 + addr = iCacheClient->AllocateAndLockSegments(segmentStartPos, segmentCount, EFalse, !readAhead);
1.864 + if (addr == NULL)
1.865 + {
1.866 + __CACHE_PRINT(_L("AllocateSegment failed"));
1.867 + currentOperation->iState = EStReStart;
1.868 + break;
1.869 + }
1.870 + }
1.871 +
1.872 +
1.873 + readLen = segmentCount << segmentSizeLog2;
1.874 + __ASSERT_DEBUG(iSize64 > segmentStartPos, Fault(EPosBeyondSize));
1.875 + readLen = (TInt)Min((TInt64)readLen, (iSize64 - segmentStartPos));
1.876 +
1.877 + if (iCacheClient->SegmentEmpty(segmentStartPos))
1.878 + {
1.879 + // store readLen & addr in scratch area
1.880 + currentOperation->iScratchValue0 = addr;
1.881 + currentOperation->iScratchValue1 = (TAny*) readLen;
1.882 +
1.883 + // read into cache segment(s)
1.884 + __CACHE_PRINT2(_L("CACHEFILE: read pos %ld, readLen %d"), segmentStartPos, readLen);
1.885 + TInt r = aMsgRequest.PushOperation(
1.886 + segmentStartPos, readLen, addr, 0,
1.887 + CompleteRead,
1.888 + EStReadFromDiskComplete);
1.889 + if (r != KErrNone)
1.890 + {
1.891 + iCacheClient->UnlockSegments(segmentStartPos);
1.892 + currentOperation->iState = EStReStart;
1.893 + break;
1.894 + }
1.895 +
1.896 + return CFsRequest::EReqActionContinue;
1.897 + }
1.898 + else
1.899 + {
1.900 + currentOperation->iState = EStCopyToClient;
1.901 + }
1.902 + }
1.903 + // fall into ...
1.904 +
1.905 + case EStCopyToClient:
1.906 + {
1.907 + TInt segmentsLocked = (readLen + segmentSize - 1) >> segmentSizeLog2;
1.908 +
1.909 + if (lastError == KErrNone)
1.910 + {
1.911 + // copy to user buffer
1.912 + TInt offset = iCacheClient->CacheOffset(currentPos); // offset into segment
1.913 + TInt len = Min(readLen - offset, totalLen);
1.914 +
1.915 + // if addr is NULL then this is a read ahead request
1.916 + if (currentOperation->iReadWriteArgs.iData != NULL)
1.917 + {
1.918 + if (currentOperation->iClientRequest)
1.919 + {
1.920 + __ASSERT_DEBUG (aMsgRequest.Message().Handle() != NULL, Fault(EMsgAlreadyCompleted));
1.921 + TPtrC8 ptr(addr+offset, len);
1.922 + lastError = aMsgRequest.Write(0, ptr, currentOffset);
1.923 + }
1.924 + else
1.925 + {
1.926 + memcpy(((TUint8*) currentOperation->iReadWriteArgs.iData) + currentOffset, addr+offset, len);
1.927 + }
1.928 + }
1.929 + else
1.930 + {
1.931 + iReadAheadRequest = NULL;
1.932 + }
1.933 +
1.934 + currentOffset+= len;
1.935 + currentPos+= len;
1.936 + totalLen-= len;
1.937 + }
1.938 +
1.939 + if (lastError == KErrNone)
1.940 + iCacheClient->MarkSegmentsAsFilled(segmentStartPos, segmentsLocked);
1.941 + iCacheClient->UnlockSegments(segmentStartPos);
1.942 +
1.943 + if (lastError != KErrNone)
1.944 + {
1.945 + retCode = CFsRequest::EReqActionComplete;
1.946 + currentOperation->iState = EStEnd;
1.947 + break;
1.948 + }
1.949 +
1.950 + if (totalLen > 0 && lastError == KErrNone)
1.951 + {
1.952 + currentOperation->iState = EStReadFromCache;
1.953 + break;
1.954 + }
1.955 + currentOperation->iState = EStEnd;
1.956 + }
1.957 + // fall into ...
1.958 +
1.959 +
1.960 + case EStEnd:
1.961 + // update the file share's position if this request came from client
1.962 + UpdateSharePosition(aMsgRequest, *currentOperation);
1.963 + return retCode;
1.964 +
1.965 + case EStReadFromDiskComplete:
1.966 + {
1.967 + // restore readLen etc from scratch area
1.968 + segmentStartPos = currentPos & segmentSizeMask;
1.969 + addr = (TUint8*) currentOperation->iScratchValue0;
1.970 + readLen = (TInt) currentOperation->iScratchValue1;
1.971 +
1.972 + aMsgRequest.CurrentOperation().iState = EStCopyToClient;
1.973 + }
1.974 + break;
1.975 +
1.976 + case EStReStart:
1.977 +
1.978 + // ignore the failure if this is a read-ahead
1.979 + if (readAhead)
1.980 + {
1.981 + totalLen = 0;
1.982 + retCode = CFsRequest::EReqActionComplete;
1.983 + currentOperation->iState = EStEnd;
1.984 + break;
1.985 + }
1.986 +
1.987 + // We need to flush all dirty data to disk in case this read overlaps
1.988 + // with any dirty data already in the file cache
1.989 + if (aMode & EFileWriteBuffered)
1.990 + {
1.991 + TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, ETrue);
1.992 + if (r == CFsRequest::EReqActionBusy || r != CFsRequest::EReqActionComplete)
1.993 + return r;
1.994 + }
1.995 +
1.996 + retCode = CFsRequest::EReqActionContinue; // read uncached
1.997 + currentOperation->iState = EStEnd;
1.998 +
1.999 + /*
1.1000 + We're now going to by-pass the file cache.
1.1001 + If we've already written something to the client's buffer then, in the flexible
1.1002 + memory model, the KDesWrittenShift bit will be set and so the descriptor length will
1.1003 + updated in RMessageK::CallbackFunc() when the client thread runs. This (shorter)
1.1004 + length will overwrite the descriptor length written by the local media subsystem.
1.1005 + To get round this problem, we set the descriptor length artificially by writing a
1.1006 + zero-length descriptor at the end of the client's buffer.
1.1007 + */
1.1008 + if (currentOffset > 0 && currentOperation->iClientRequest)
1.1009 + {
1.1010 + __ASSERT_DEBUG (aMsgRequest.Message().Handle() != NULL, Fault(EMsgAlreadyCompleted));
1.1011 + TPtrC8 ptr(NULL, 0);
1.1012 + TInt r = aMsgRequest.Write(0, ptr, currentOffset+totalLen);
1.1013 + __CACHE_PRINT3(_L("CFileCache::DoReadBuffered() failed at pos %lx offset %x totalLen %x\n"), currentPos, currentOffset, totalLen);
1.1014 + __CACHE_PRINT2(_L("CFileCache::DoReadBuffered() writing zero bytes at offset %x r %d\n"), currentOffset + totalLen, r);
1.1015 + if (r != KErrNone)
1.1016 + retCode = r;
1.1017 + }
1.1018 +
1.1019 + aMsgRequest.ReStart();
1.1020 + UpdateSharePosition(aMsgRequest, *currentOperation);
1.1021 + return retCode;
1.1022 +
1.1023 +
1.1024 + };
1.1025 + } // for (;;)
1.1026 +
1.1027 + }
1.1028 +
1.1029 +
1.1030 +
1.1031 +/**
1.1032 +WriteBuffered - attempts to write to cache.
1.1033 +Called from TFsFileRead::Initialise and TFsFileRead::DoRequestL
1.1034 +
1.1035 +@return CFsRequest::EReqActionComplete if request entirely satisfied
1.1036 + CFsRequest::EReqActionContinue if request not yet complete
1.1037 + CFsRequest::EReqActionBusy if filecache is busy
1.1038 +*/
1.1039 +TInt CFileCache::WriteBuffered(CFsMessageRequest& aMsgRequest, TUint aMode)
1.1040 + {
1.1041 + iLock.Wait();
1.1042 +
1.1043 +
1.1044 + CFsClientMessageRequest* newRequest = NULL;
1.1045 +
1.1046 + __CACHE_PRINT2(_L("CFileCache::WriteBuffered() pos %d, len %d"), I64LOW(aMsgRequest.CurrentOperation().iReadWriteArgs.iPos), aMsgRequest.CurrentOperation().iReadWriteArgs.iTotalLength);
1.1047 + TInt r = DoWriteBuffered(aMsgRequest, newRequest, aMode);
1.1048 +
1.1049 + iLock.Signal();
1.1050 +
1.1051 + if (newRequest)
1.1052 + newRequest->Dispatch();
1.1053 +
1.1054 + // completion ?
1.1055 + if (r == CFsRequest::EReqActionComplete)
1.1056 + {
1.1057 + TMsgOperation& currentOperation = aMsgRequest.CurrentOperation();
1.1058 +
1.1059 + __ASSERT_DEBUG(aMsgRequest.CurrentOperationPtr() != NULL, Fault(ECacheBadOperationIndex));
1.1060 +
1.1061 + TFsFileWrite::CommonEnd(&aMsgRequest, r, iInitialSize, iSize64, currentOperation.iReadWriteArgs.iPos, EFalse);
1.1062 + }
1.1063 +
1.1064 + return r;
1.1065 + }
1.1066 +
1.1067 +
1.1068 +
1.1069 +
1.1070 +TInt CFileCache::CompleteWrite(CFsRequest* aRequest)
1.1071 + {
1.1072 + CFsMessageRequest& msgRequest = *(CFsMessageRequest*) aRequest;
1.1073 +
1.1074 + CFileShare* share = (CFileShare*)aRequest->ScratchValue();
1.1075 + CFileCB& file = share->File();
1.1076 + CFileCache* fileCache = file.FileCache(); //&file;
1.1077 + __ASSERT_DEBUG(fileCache != NULL, Fault(ENoFileCache));
1.1078 +
1.1079 +#ifdef _DEBUG
1.1080 + TInt cancelling = (msgRequest.LastError() == KErrCancel)?(TBool)ETrue:(TBool)EFalse;
1.1081 +#endif
1.1082 +
1.1083 +
1.1084 + TInt r = fileCache->WriteBuffered(msgRequest, share->iMode);
1.1085 +
1.1086 +#ifdef _DEBUG
1.1087 + __ASSERT_DEBUG(!cancelling || msgRequest.LastError() == KErrCancel, Fault(ERequestUncancelled));
1.1088 +#endif
1.1089 +
1.1090 + // if this request has been cancelled we mustn't dispatch it again -
1.1091 + // we still need to call state machine however so that any locked segments can be unlocked
1.1092 + TInt lastError = msgRequest.LastError();
1.1093 + if (lastError == KErrCancel)
1.1094 + r = CFsRequest::EReqActionComplete;
1.1095 +
1.1096 + return r;
1.1097 + }
1.1098 +
1.1099 +
1.1100 +
1.1101 +TInt CFileCache::DoWriteBuffered(CFsMessageRequest& aMsgRequest, CFsClientMessageRequest*& aNewRequest, TUint aMode)
1.1102 +
1.1103 + {
1.1104 + enum states
1.1105 + {
1.1106 + EStBegin=0,
1.1107 + EStWriteToCache,
1.1108 + EStReadFromDisk,
1.1109 + EStReadFromDiskComplete,
1.1110 + EStWriteToCacheComplete,
1.1111 + EStCopyFromClient,
1.1112 + EStReStart,
1.1113 + EStEnd,
1.1114 + EStWriteThrough
1.1115 + };
1.1116 +
1.1117 + enum flags
1.1118 + {
1.1119 + EReadFirstSegment = 0x01,
1.1120 + EReadLastSegment = 0x02
1.1121 + };
1.1122 +
1.1123 + TBool cachingWrites = (aMode & EFileWriteBuffered)?(TBool)ETrue:(TBool)EFalse;
1.1124 + TInt& lastError = aMsgRequest.LastError();
1.1125 + TBool driveThread = IsDriveThread();
1.1126 + TInt segmentSize = SegmentSize();
1.1127 + TInt segmentSizeLog2 = SegmentSizeLog2();
1.1128 + TInt64 segmentSizeMask = SegmentSizeMask();
1.1129 +
1.1130 + // temp storage for transition between EStWriteToCache / EStWriteToCacheComplete and EStCopyFromClient
1.1131 + TInt segmentCount = 0;
1.1132 + TUint8* addr = NULL;
1.1133 + TInt64 firstSegmentStartPos = 0;
1.1134 + TInt64 readPos = 0;
1.1135 + TUint8* readAddr = NULL;
1.1136 + TInt readSegmentCount = 0;
1.1137 +
1.1138 + TInt readFlags = 0;
1.1139 +
1.1140 + for(;;)
1.1141 + {
1.1142 + TMsgOperation* currentOperation = &aMsgRequest.CurrentOperation();
1.1143 + TInt retCode = CFsRequest::EReqActionComplete;
1.1144 +
1.1145 + TInt64& currentPos = currentOperation->iReadWriteArgs.iPos;
1.1146 + TInt& totalLen = currentOperation->iReadWriteArgs.iTotalLength;
1.1147 + TInt& currentOffset = currentOperation->iReadWriteArgs.iOffset;
1.1148 + switch(currentOperation->iState)
1.1149 + {
1.1150 + case EStBegin:
1.1151 + {
1.1152 + // Report background flush errors back to client
1.1153 + CFileShare* share = (CFileShare*) aMsgRequest.ScratchValue();
1.1154 + if (share->iFlushError != KErrNone)
1.1155 + {
1.1156 + TInt r = share->iFlushError;
1.1157 + share->iFlushError = KErrNone;
1.1158 + return r;
1.1159 + }
1.1160 +
1.1161 + if (currentPos > iSize64)
1.1162 + currentPos = iSize64;
1.1163 + iInitialSize = iSize64;
1.1164 +
1.1165 + // if EFileWriteDirectIO OR
1.1166 + // (caching writes and requested write len > size of the cache),
1.1167 + // flush the write cache
1.1168 + if ((aMode & EFileWriteBuffered) == 0 ||
1.1169 + (cachingWrites && totalLen > iCacheSize))
1.1170 + {
1.1171 + TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, ETrue);
1.1172 + if (r == CFsRequest::EReqActionBusy || r != CFsRequest::EReqActionComplete)
1.1173 + return r;
1.1174 + }
1.1175 + // if EFileWriteDirectIO then overwrite any non-dirty data and
1.1176 + // write direct to disk....
1.1177 +
1.1178 + // if caching writes and requested write len > size of the cache,
1.1179 + // don't write excess through the cache as this is wasteful
1.1180 + if (cachingWrites && totalLen > iCacheSize)
1.1181 + {
1.1182 + // Destroy ALL cached data (dirty and non-dirty) to ensure
1.1183 + // cache is consistent with data written to disk
1.1184 + iCacheClient->Purge(ETrue);
1.1185 +
1.1186 + TInt len = totalLen - iCacheSize;
1.1187 + TInt r = aMsgRequest.PushOperation(
1.1188 + currentPos, len,
1.1189 + (TDesC8*) currentOperation->iReadWriteArgs.iData, currentOffset,
1.1190 + CompleteWrite,
1.1191 + EStWriteToCache);
1.1192 + if (r != KErrNone)
1.1193 + {
1.1194 + currentOperation->iState = EStReStart;
1.1195 + break;
1.1196 + }
1.1197 +
1.1198 + currentOffset+= len;
1.1199 + currentPos+= len;
1.1200 + totalLen-= len;
1.1201 + return CFsRequest::EReqActionContinue;
1.1202 + }
1.1203 +
1.1204 + currentOperation->iState = EStWriteToCache;
1.1205 + }
1.1206 + break;
1.1207 +
1.1208 + case EStWriteToCache:
1.1209 + __ASSERT_DEBUG(aMsgRequest.CurrentOperationPtr() != NULL, Fault(ECacheBadOperationIndex));
1.1210 + {
1.1211 + // NB we must carry on if we get an error to ensure cache is consistent with disk
1.1212 + if (totalLen == 0 || lastError == KErrCancel)
1.1213 + {
1.1214 + currentOperation->iState = EStWriteToCacheComplete;
1.1215 + break;
1.1216 + }
1.1217 +
1.1218 + firstSegmentStartPos = currentPos & segmentSizeMask;
1.1219 + TInt64 endPos = currentPos + totalLen;
1.1220 +
1.1221 + // Find the maximum number of contiguous segments we need to lock
1.1222 + // in this cacheline - when we unlock these will be marked as filled
1.1223 + const TInt64 dataRange = Min((TInt64)iCacheSize, (endPos + segmentSize - 1 - firstSegmentStartPos));
1.1224 + TInt maxSegmentCount = (TInt)(dataRange >> segmentSizeLog2);
1.1225 +
1.1226 + segmentCount = maxSegmentCount;
1.1227 +
1.1228 + TInt lockError;
1.1229 + TInt filledSegmentCount;
1.1230 + addr = iCacheClient->FindAndLockSegments(firstSegmentStartPos, segmentCount, filledSegmentCount, lockError, cachingWrites);
1.1231 +
1.1232 + if (lockError == KErrInUse)
1.1233 + return CFsRequest::EReqActionBusy;
1.1234 +
1.1235 +#ifdef LAZY_WRITE
1.1236 + if (cachingWrites && addr == NULL && lastError == KErrNone && iCacheClient->TooManyLockedSegments())
1.1237 + {
1.1238 + // Flush a single dirty cacheline (if there is one for this file)
1.1239 + // or all dirty data on this drive (if there is any).
1.1240 + // If there's nothing to flush then the dirty (locked) data
1.1241 + // must belong to another drive, so there's not much we can do
1.1242 + // but write through
1.1243 + TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFalse);
1.1244 + if (r == CFsRequest::EReqActionBusy)
1.1245 + return CFsRequest::EReqActionBusy;
1.1246 + if (r != KErrInUse)
1.1247 + {
1.1248 + // Need to switch to drive thread to flush all data on this drive
1.1249 + if (!driveThread)
1.1250 + return CFsRequest::EReqActionBusy;
1.1251 + iLock.Signal();
1.1252 + TInt r = iDrive->FlushCachedFileInfo();
1.1253 + iLock.Wait();
1.1254 + if (r == CFsRequest::EReqActionBusy)
1.1255 + return CFsRequest::EReqActionBusy;
1.1256 +
1.1257 + lastError = KErrNoMemory; // write through
1.1258 + }
1.1259 + }
1.1260 +#else
1.1261 + // flush cache before allocating a new cacheline
1.1262 + if (cachingWrites && addr == NULL && lastError == KErrNone)
1.1263 + {
1.1264 + // if no segment available, flush a single dirty cacheline
1.1265 + // or wait if already flushing
1.1266 + if (iFlushBusy)
1.1267 + return CFsRequest::EReqActionBusy;
1.1268 + TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFalse);
1.1269 + if (r != CFsRequest::EReqActionBusy && r != CFsRequest::EReqActionComplete)
1.1270 + lastError = r;
1.1271 + }
1.1272 +#endif
1.1273 + // if no cacheline found & write caching is enabled, allocate a new cacheline
1.1274 + if (cachingWrites && addr == NULL && lastError == KErrNone)
1.1275 + {
1.1276 + // try to allocate up to write cache size - this may not
1.1277 + // be possible if a segment in the range is already cached
1.1278 + segmentCount = maxSegmentCount;
1.1279 + addr = iCacheClient->AllocateAndLockSegments(currentPos, segmentCount, cachingWrites, ETrue);
1.1280 +
1.1281 + // continue if alloc failed
1.1282 + if (addr == NULL)
1.1283 + lastError = KErrNoMemory;
1.1284 +
1.1285 + }
1.1286 +
1.1287 + if (addr == NULL)
1.1288 + {
1.1289 + if (cachingWrites && lastError == KErrNone)
1.1290 + lastError = KErrNoMemory;
1.1291 + segmentCount = 1;
1.1292 + currentOperation->iState = EStCopyFromClient;
1.1293 + break;
1.1294 + }
1.1295 +
1.1296 + // if the first or last segment are empty then we'll have to read-before-write
1.1297 + TInt64 lastSegmentStartPos = firstSegmentStartPos + ((segmentCount-1) << segmentSizeLog2);
1.1298 +
1.1299 + TInt startPosSegOffset = iCacheClient->CacheOffset(currentPos);
1.1300 + TInt endPosSegOffset = iCacheClient->CacheOffset(endPos);
1.1301 +
1.1302 + // partial first segment ?
1.1303 + if (startPosSegOffset != 0 &&
1.1304 + firstSegmentStartPos < iFileCB->Size64() &&
1.1305 + iCacheClient->SegmentEmpty(firstSegmentStartPos))
1.1306 + {
1.1307 + readFlags|= EReadFirstSegment;
1.1308 + }
1.1309 +
1.1310 + // partial last segment ?
1.1311 + // NB this may be the first segment too !
1.1312 + if (endPosSegOffset != 0 &&
1.1313 + endPos < lastSegmentStartPos + segmentSize &&
1.1314 + endPos < iFileCB->Size64() &&
1.1315 + iCacheClient->SegmentEmpty(lastSegmentStartPos))
1.1316 + {
1.1317 + readFlags|= EReadLastSegment;
1.1318 + }
1.1319 +
1.1320 + // read-before-write required ?
1.1321 + if (readFlags & EReadFirstSegment)
1.1322 + {
1.1323 + readFlags&= ~EReadFirstSegment;
1.1324 + readPos = firstSegmentStartPos;
1.1325 + readAddr = addr;
1.1326 + readSegmentCount = 1;
1.1327 + // if the last segment is empty and it's the same as the first or contiguous,
1.1328 + // then read that too
1.1329 + if ((readFlags & EReadLastSegment) && (segmentCount <= 2))
1.1330 + {
1.1331 + readSegmentCount = segmentCount;
1.1332 + readFlags&= ~EReadLastSegment;
1.1333 + }
1.1334 + currentOperation->iState = EStReadFromDisk;
1.1335 + }
1.1336 + else if (readFlags & EReadLastSegment)
1.1337 + {
1.1338 + readFlags&= ~EReadLastSegment;
1.1339 + readPos = lastSegmentStartPos;
1.1340 + readAddr = addr + ((segmentCount-1) << segmentSizeLog2);
1.1341 + readSegmentCount = 1;
1.1342 + currentOperation->iState = EStReadFromDisk;
1.1343 + }
1.1344 + else
1.1345 + {
1.1346 + currentOperation->iState = EStCopyFromClient;
1.1347 + }
1.1348 + }
1.1349 + break;
1.1350 +
1.1351 + case EStReadFromDisk:
1.1352 + {
1.1353 + // Save address & segmentCount in scratch area
1.1354 + currentOperation->iScratchValue0 = addr;
1.1355 + __ASSERT_DEBUG(segmentCount < 0xFFFF, Fault(EBadSegmentCount));
1.1356 + currentOperation->iScratchValue1 = (TAny*) ((readFlags << 16) | segmentCount);
1.1357 +
1.1358 + TInt maxReadLen = readSegmentCount << segmentSizeLog2;
1.1359 +#ifdef __VC32__
1.1360 +#pragma warning( disable : 4244 )
1.1361 +//Disable Compiler Warning (levels 3 and 4) C4244: 'conversion' conversion from 'type1' to 'type2', possible loss of data
1.1362 +// maxReadLen is of 31 bits. Hence Min result is reduced to 31 bits
1.1363 +#endif
1.1364 + TInt readLen = (TInt)Min((TInt64)maxReadLen, (iSize64 - readPos));
1.1365 +#ifdef __VC32__
1.1366 +#pragma warning( default : 4244 )
1.1367 +#endif
1.1368 + __ASSERT_DEBUG (aMsgRequest.Message().Handle() != NULL, Fault(EMsgAlreadyCompleted));
1.1369 + TInt r = aMsgRequest.PushOperation(
1.1370 + readPos, readLen, readAddr, 0,
1.1371 + CompleteWrite,
1.1372 + EStReadFromDiskComplete,
1.1373 + EFsFileRead);
1.1374 + if (r != KErrNone)
1.1375 + {
1.1376 + lastError = KErrNoMemory;
1.1377 + currentOperation->iState = EStEnd;
1.1378 + iCacheClient->UnlockSegments(firstSegmentStartPos);
1.1379 + break;
1.1380 + }
1.1381 +
1.1382 + // requeue this request
1.1383 + return CFsRequest::EReqActionContinue;
1.1384 + }
1.1385 +
1.1386 + case EStReadFromDiskComplete:
1.1387 + {
1.1388 + __ASSERT_DEBUG(aMsgRequest.CurrentOperationPtr() != NULL, Fault(ECacheBadOperationIndex));
1.1389 + // restore addr & segmentCount etc from scratch area
1.1390 + firstSegmentStartPos = currentPos & segmentSizeMask;
1.1391 + addr = (TUint8*) currentOperation->iScratchValue0;
1.1392 + segmentCount = ((TInt) currentOperation->iScratchValue1) & 0xFFFF;
1.1393 + readFlags = ((TInt) currentOperation->iScratchValue1) >> 16;
1.1394 +
1.1395 + if (readFlags & EReadLastSegment)
1.1396 + {
1.1397 + TInt64 lastSegmentStartPos = firstSegmentStartPos + ((segmentCount-1) << segmentSizeLog2);
1.1398 + readFlags&= ~EReadLastSegment;
1.1399 + readPos = lastSegmentStartPos;
1.1400 + readAddr = addr + ((segmentCount-1) << segmentSizeLog2);
1.1401 + readSegmentCount = 1;
1.1402 + currentOperation->iState = EStReadFromDisk;
1.1403 + break;
1.1404 + }
1.1405 +
1.1406 + aMsgRequest.CurrentOperation().iState = EStCopyFromClient;
1.1407 + }
1.1408 + break;
1.1409 +
1.1410 + case EStCopyFromClient:
1.1411 + {
1.1412 + TInt writeLen = segmentCount << segmentSizeLog2;
1.1413 + TInt offset = iCacheClient->CacheOffset(currentPos); // offset into segment
1.1414 + writeLen = Min(writeLen - offset, totalLen);
1.1415 +
1.1416 + if (addr != NULL)
1.1417 + {
1.1418 + __ASSERT_DEBUG (aMsgRequest.Message().Handle() != NULL, Fault(EMsgAlreadyCompleted));
1.1419 + // copy from user buffer to cache buffer
1.1420 + TInt writeError = KErrNone;
1.1421 + if (currentOperation->iClientRequest)
1.1422 + {
1.1423 + TPtr8 ptr(addr+offset, writeLen, writeLen);
1.1424 + writeError = aMsgRequest.Read(0, ptr, currentOffset);
1.1425 + }
1.1426 + else
1.1427 + {
1.1428 + memcpy(addr+offset, ((TUint8*) currentOperation->iReadWriteArgs.iData) + currentOffset, writeLen);
1.1429 + }
1.1430 +
1.1431 + // "writing" past end of file ?
1.1432 + if (currentPos + writeLen > iSize64 && cachingWrites && lastError == KErrNone && writeError == KErrNone)
1.1433 + {
1.1434 + __CACHE_PRINT2(_L("CACHEFILE: extending size old %ld new %ld"), iSize64, currentPos + totalLen);
1.1435 + iSize64 = currentPos + writeLen;
1.1436 + }
1.1437 +
1.1438 + TInt anyError = (writeError != KErrNone)?writeError:lastError;
1.1439 +
1.1440 + if (anyError == KErrNone)
1.1441 + iCacheClient->MarkSegmentsAsFilled(firstSegmentStartPos, segmentCount);
1.1442 +
1.1443 + if (cachingWrites && anyError == KErrNone)
1.1444 + {
1.1445 + iCacheClient->MarkSegmentsAsDirty(firstSegmentStartPos, segmentCount);
1.1446 + // start dirty data timer
1.1447 + FileDirty(aMsgRequest);
1.1448 + }
1.1449 +
1.1450 + // unlock if we're not buffering writes (segments won't be unlocked if dirty)
1.1451 + iCacheClient->UnlockSegments(firstSegmentStartPos);
1.1452 + }
1.1453 +
1.1454 + currentOffset+= writeLen;
1.1455 + currentPos+= writeLen;
1.1456 + totalLen-= writeLen;
1.1457 +
1.1458 + currentOperation->iState = EStWriteToCache;
1.1459 + break;
1.1460 + }
1.1461 +
1.1462 + case EStWriteToCacheComplete:
1.1463 + {
1.1464 + __ASSERT_DEBUG(aMsgRequest.CurrentOperationPtr() != NULL, Fault(ECacheBadOperationIndex));
1.1465 +
1.1466 + if (lastError == KErrCancel)
1.1467 + {
1.1468 + currentOperation->iState = EStEnd;
1.1469 + }
1.1470 + else if ((!cachingWrites) || (lastError != KErrNone))
1.1471 + {
1.1472 + // allow TFsFileWrite::DoRequestL() to proceed normally using original pos & len
1.1473 + __ASSERT_DEBUG(aMsgRequest.CurrentOperationPtr() != NULL, Fault(ECacheBadOperationIndex));
1.1474 +
1.1475 + currentOperation->iState = EStWriteThrough;
1.1476 + }
1.1477 + else
1.1478 + {
1.1479 + __CACHE_PRINT2(_L("CACHEFILE: write buffered pos %ld, len %d"),
1.1480 + aMsgRequest.CurrentOperation().iReadWriteArgs.iPos, aMsgRequest.CurrentOperation().iReadWriteArgs.iOffset);
1.1481 + __ASSERT_DEBUG(totalLen == 0, Fault(ECompletingWriteWithDataRemaining));
1.1482 +
1.1483 + currentOperation->iState = EStEnd;
1.1484 + }
1.1485 + break;
1.1486 + }
1.1487 +
1.1488 + case EStWriteThrough:
1.1489 +
1.1490 + if (lastError == KErrCancel)
1.1491 + {
1.1492 + currentOperation->iState = EStEnd;
1.1493 + break;
1.1494 + }
1.1495 +
1.1496 + // we're going to issue an uncached write so clear any error
1.1497 + lastError = KErrNone;
1.1498 +
1.1499 + if (cachingWrites)
1.1500 + {
1.1501 + TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, ETrue);
1.1502 +#ifdef _DEBUG
1.1503 + if (r == CFsRequest::EReqActionBusy)
1.1504 + CCacheManagerFactory::CacheManager()->Stats().iWriteThroughWithDirtyDataCount++;
1.1505 +#endif
1.1506 + // Need to reset currentOperation.iReadWriteArgs.iTotalLength here to make sure
1.1507 + // TFsFileWrite::PostInitialise() doesn't think there's no data left to process
1.1508 + aMsgRequest.ReStart();
1.1509 + if (r == CFsRequest::EReqActionBusy || r != CFsRequest::EReqActionComplete)
1.1510 + return r;
1.1511 + }
1.1512 +
1.1513 + retCode = CFsRequest::EReqActionContinue;
1.1514 +
1.1515 + // fall into...EStRestart
1.1516 + currentOperation->iState = EStReStart;
1.1517 +
1.1518 + case EStReStart:
1.1519 +
1.1520 + __ASSERT_DEBUG(retCode == CFsRequest::EReqActionBusy || retCode == CFsRequest::EReqActionContinue, Fault(EBadRetCode));
1.1521 +
1.1522 + aMsgRequest.ReStart();
1.1523 + UpdateSharePosition(aMsgRequest, *currentOperation);
1.1524 + if (currentOperation->iClientRequest)
1.1525 + currentOperation->iReadWriteArgs.iPos = currentOperation->iClientPosition; // NB maybe KCurrentPosition64
1.1526 + return retCode;
1.1527 +
1.1528 + case EStEnd:
1.1529 + return retCode;
1.1530 + }
1.1531 + }
1.1532 + }
1.1533 +
1.1534 +
1.1535 +
1.1536 +TInt CFileCache::AllocateRequest(CFsClientMessageRequest*& aNewRequest, TBool aWrite, CSessionFs* aSession)
1.1537 + {
1.1538 +
1.1539 + RLocalMessage msgNew;
1.1540 + const TOperation& oP = OperationArray[aWrite?EFsFileWriteDirty:EFsFileRead];
1.1541 + TInt r = RequestAllocator::GetMessageRequest(oP, msgNew, aNewRequest);
1.1542 + if (r != KErrNone)
1.1543 + return r;
1.1544 +
1.1545 + aNewRequest->Set(msgNew, oP, aSession);
1.1546 + aNewRequest->SetDrive(iDrive);
1.1547 +
1.1548 + // read-aheads and write-dirty requests should not be posted to plugins
1.1549 + // If there are data-modifying plugins, then these should sit above the file cache
1.1550 + aNewRequest->iCurrentPlugin = NULL;
1.1551 + aNewRequest->EnablePostIntercept(EFalse);
1.1552 +
1.1553 + // Scratch value is a CFileCB pointer NOT a CFileShare for requests
1.1554 + // allocated by the file cache
1.1555 + aNewRequest->SetScratchValue64( MAKE_TINT64(EFalse, (TUint) iFileCB) );
1.1556 +
1.1557 + // don't call Initialise(), don't call PostInitialise()
1.1558 + aNewRequest->SetState(CFsRequest::EReqStateDoRequest);
1.1559 +
1.1560 + // don't call Message().Complete()
1.1561 + aNewRequest->SetCompleted(EFalse);
1.1562 +
1.1563 + __ASSERT_DEBUG(aNewRequest->CurrentOperationPtr() == NULL, Fault(EBadOperationIndex));
1.1564 +
1.1565 + return KErrNone;
1.1566 + }
1.1567 +
1.1568 +TInt CFileCache::FlushDirty(CFsRequest* aOldRequest)
1.1569 + {
1.1570 + iLock.Wait();
1.1571 +
1.1572 + CFsClientMessageRequest* newRequest = NULL;
1.1573 +
1.1574 + TInt r = DoFlushDirty(newRequest, aOldRequest, ETrue);
1.1575 +
1.1576 + iLock.Signal();
1.1577 +
1.1578 + if (newRequest)
1.1579 + {
1.1580 + //To be used in notification framework.
1.1581 + //newRequest->iUID = aOldRequest->Message().Identity();
1.1582 + newRequest->Dispatch();
1.1583 + }
1.1584 +
1.1585 + return r;
1.1586 + }
1.1587 +
1.1588 +
1.1589 +void CFileCache::Purge(TBool aPurgeDirty)
1.1590 + {
1.1591 + iLock.Wait();
1.1592 +
1.1593 + iCacheClient->Purge(aPurgeDirty);
1.1594 +
1.1595 + iLock.Signal();
1.1596 + }
1.1597 +
1.1598 +void CFileCache::PropagateFlushErrorToAllFileShares()
1.1599 + {
1.1600 + FileShares->Lock();
1.1601 + TInt count = FileShares->Count();
1.1602 + while(count--)
1.1603 + {
1.1604 + CFileShare* share = (CFileShare*)(*FileShares)[count];
1.1605 + if (&share->File() == iFileCB)
1.1606 + {
1.1607 + share->iFlushError = iFlushError;
1.1608 + }
1.1609 + }
1.1610 + FileShares->Unlock();
1.1611 + }
1.1612 +
1.1613 +/**
1.1614 +CFileCache::DoFlushDirty()
1.1615 +
1.1616 +@param aFlushAll. If EFalse then only a single cacheline will be flushed
1.1617 +
1.1618 +returns CFsRequest::EReqActionBusy if a flush is in progress OR cacheline already in use
1.1619 + CFsRequest::EReqActionComplete if nothing to flush / flush completed
1.1620 + KErrNoMemory if failed to allocate a request
1.1621 + or one of the system wide error codes
1.1622 +
1.1623 + */
1.1624 +TInt CFileCache::DoFlushDirty(CFsClientMessageRequest*& aNewRequest, CFsRequest* aOldRequest, TBool aFlushAll)
1.1625 + {
1.1626 + TInt64 pos;
1.1627 + TUint8* addr;
1.1628 +
1.1629 + if (iFlushBusy)
1.1630 + return CFsRequest::EReqActionBusy;
1.1631 +
1.1632 + TInt segmentCount = iCacheClient->FindFirstDirtySegment(pos, addr);
1.1633 +
1.1634 + if (segmentCount == 0)
1.1635 + {
1.1636 + TInt flushError = iFlushError;
1.1637 + iFlushError = KErrNone;
1.1638 +
1.1639 + // If this flush didn't originate from a client request return last error
1.1640 + if (!aOldRequest || !aOldRequest->Session())
1.1641 + return (flushError == KErrNone)? CFsRequest::EReqActionComplete : flushError;
1.1642 +
1.1643 + // Return the last error from CFileShare::iFlushError
1.1644 + // and then clear CFileShare::iFlushError
1.1645 +
1.1646 + CFileShare* share = (CFileShare*) SessionObjectFromHandle(
1.1647 + aOldRequest->Message().Int3(),
1.1648 + FileShares->UniqueID(),
1.1649 + aOldRequest->Session());
1.1650 +
1.1651 + TInt r = KErrNone;
1.1652 + if (share)
1.1653 + {
1.1654 + r = share->iFlushError;
1.1655 + share->iFlushError = KErrNone;
1.1656 + }
1.1657 + if (r == KErrNone)
1.1658 + return CFsRequest::EReqActionComplete;
1.1659 +
1.1660 + return r;
1.1661 + }
1.1662 +
1.1663 +
1.1664 + // NB aOldRequest->Session may be NULL - e.g for FileShareCloseOp
1.1665 + CSessionFs* session = aOldRequest && aOldRequest->Session() ? aOldRequest->Session() : iDirtyDataOwner;
1.1666 +
1.1667 + __ASSERT_ALWAYS(session, Fault(EFlushingWithSessionNull));
1.1668 +
1.1669 + TInt r = AllocateRequest(aNewRequest, ETrue, session);
1.1670 + if (r != KErrNone)
1.1671 + return r;
1.1672 +
1.1673 + r = aNewRequest->PushOperation(0, 0, (TUint8*) NULL, 0);
1.1674 +
1.1675 + if (r != KErrNone)
1.1676 + {
1.1677 + aNewRequest->Free();
1.1678 + aNewRequest = NULL;
1.1679 + return r;
1.1680 + }
1.1681 +
1.1682 + // set the number of segments to flush - either all dirty data or the equivalent of one full cacheline
1.1683 + aNewRequest->CurrentOperation().iScratchValue0 =
1.1684 + (TAny*) (aFlushAll?KMaxTInt:iCacheClient->CacheLineSizeInSegments());
1.1685 +
1.1686 +
1.1687 + // issue flush request
1.1688 + r = FlushDirtySm(*aNewRequest);
1.1689 +
1.1690 + // We should only get three possible return codes from FlushDirtySm() :
1.1691 + // CFsRequest::EReqActionContinue - a write request (aNewRequest) needs to be dispatched
1.1692 + // CFsRequest::EReqActionComplete - completed already - this can happen if dirty data was beyond file end
1.1693 + // CFsRequest::EReqActionBusy - first dirty cacheline is already in use.
1.1694 + __ASSERT_DEBUG( r == CFsRequest::EReqActionContinue || r == CFsRequest::EReqActionComplete || r == CFsRequest::EReqActionBusy, Fault(EBadRetCode));
1.1695 + if (r == CFsRequest::EReqActionContinue || r == CFsRequest::EReqActionBusy)
1.1696 + {
1.1697 + // requeue the caller's request (aOldRequest) if there is one
1.1698 + return CFsRequest::EReqActionBusy;
1.1699 + }
1.1700 + else // CFsRequest::EReqActionComplete
1.1701 + {
1.1702 + aNewRequest->PopOperation();
1.1703 + aNewRequest->Free();
1.1704 + aNewRequest = NULL;
1.1705 + return r;
1.1706 + }
1.1707 +
1.1708 + }
1.1709 +
1.1710 +
1.1711 +TInt TFsFileWriteDirty::PostInitialise(CFsRequest* aRequest)
1.1712 + {
1.1713 + return CFileCache::CompleteFlushDirty(aRequest);
1.1714 + }
1.1715 +
1.1716 +TInt CFileCache::CompleteFlushDirty(CFsRequest* aRequest)
1.1717 + {
1.1718 + CFsMessageRequest& msgRequest = *(CFsMessageRequest*) aRequest;
1.1719 +
1.1720 + CFileShare* share;
1.1721 + CFileCB* file;
1.1722 + GetFileFromScratch(aRequest, share, file);
1.1723 + CFileCache* fileCache = file->FileCache();
1.1724 +
1.1725 + __ASSERT_DEBUG(fileCache != NULL, Fault(ENoFileCache));
1.1726 +
1.1727 +#ifdef _DEBUG
1.1728 + TInt cancelling = (msgRequest.LastError() == KErrCancel)?(TBool)ETrue:(TBool)EFalse;
1.1729 +#endif
1.1730 +
1.1731 + fileCache->iLock.Wait();
1.1732 + TInt r = fileCache->FlushDirtySm(msgRequest);
1.1733 + fileCache->iLock.Signal();
1.1734 +
1.1735 +#ifdef _DEBUG
1.1736 + __ASSERT_DEBUG(!cancelling || msgRequest.LastError() == KErrCancel, Fault(ERequestUncancelled));
1.1737 +#endif
1.1738 +
1.1739 + // if this request has been cancelled we mustn't dispatch it again -
1.1740 + // we still need to call state machine however so that any locked segments can be unlocked
1.1741 + if (msgRequest.LastError() == KErrCancel)
1.1742 + r = CFsRequest::EReqActionComplete;
1.1743 +
1.1744 + return r;
1.1745 + }
1.1746 +
1.1747 +TInt CFileCache::FlushDirtySm(CFsMessageRequest& aMsgRequest)
1.1748 + {
1.1749 + enum states
1.1750 + {
1.1751 + EStBegin=0,
1.1752 + EStWriteToDisk,
1.1753 + EStWriteToDiskComplete,
1.1754 + EStMarkAsClean,
1.1755 + EStEnd
1.1756 + };
1.1757 +
1.1758 + TMsgOperation* currentOperation = &aMsgRequest.CurrentOperation();
1.1759 + TInt& lastError = aMsgRequest.LastError();
1.1760 + TInt64 pos = 0;
1.1761 +
1.1762 + for(;;)
1.1763 + {
1.1764 +
1.1765 + switch(currentOperation->iState)
1.1766 + {
1.1767 + case EStBegin:
1.1768 + iFlushBusy = ETrue;
1.1769 +
1.1770 + // fall into...
1.1771 +
1.1772 + case EStWriteToDisk:
1.1773 + {
1.1774 + currentOperation->iState = EStWriteToDisk;
1.1775 +
1.1776 + TUint8* addr;
1.1777 + TInt segmentCount = iCacheClient->FindFirstDirtySegment(pos, addr);
1.1778 +
1.1779 + // keep track of how many segments we've written
1.1780 + currentOperation->iScratchValue0 = (TAny*) ((TInt) currentOperation->iScratchValue0 - segmentCount);
1.1781 +
1.1782 + // if no more dirty segments of if a genuine error then finish
1.1783 + // NB if the request has been cancelled then we still need to proceed and mark all the
1.1784 + // segments as clean, otherwise they will never get freed !
1.1785 + if (segmentCount == 0 || (lastError != KErrNone && lastError != KErrCancel))
1.1786 + {
1.1787 + currentOperation->iState = EStEnd;
1.1788 + break;
1.1789 + }
1.1790 +
1.1791 + TInt len = segmentCount << SegmentSizeLog2();
1.1792 +
1.1793 + if (pos < iSize64)
1.1794 + // Result of Min shall be of size TInt
1.1795 + // Hence to suppress warning
1.1796 + len = (TInt)(Min(iSize64 - pos, (TInt64)len));
1.1797 + else
1.1798 + len = 0;
1.1799 +
1.1800 +
1.1801 + // if writing past end of file or this request has been cancelled, just mark as clean
1.1802 + if (len == 0 || lastError == KErrCancel)
1.1803 + {
1.1804 + currentOperation->iState = EStMarkAsClean;
1.1805 + break;
1.1806 + }
1.1807 +
1.1808 + __CACHE_PRINT3(_L("CACHEFILE: FlushDirty first dirty pos %d, addr %08X count %d"), I64LOW(pos), addr, segmentCount);
1.1809 +
1.1810 + TInt filledSegmentCount;
1.1811 + TInt lockError;
1.1812 + addr = iCacheClient->FindAndLockSegments(pos, segmentCount, filledSegmentCount, lockError, ETrue);
1.1813 +
1.1814 + // If cacheline is busy, we need to post request to back of drive queue
1.1815 + // To dispatch to drive thread and intercept before DoRequestL() we must call iPostInitialise(),
1.1816 + // so set the state back to CFsRequest::EReqStatePostInitialise
1.1817 + if (lockError == KErrInUse)
1.1818 + {
1.1819 + __CACHE_PRINT(_L("FlushDirtySm() - cacheline BUSY !"));
1.1820 + aMsgRequest.SetState(CFsRequest::EReqStatePostInitialise);
1.1821 + return CFsRequest::EReqActionBusy;
1.1822 + }
1.1823 + else if (lockError != KErrNone)
1.1824 + {
1.1825 + iFlushBusy = EFalse;
1.1826 + lastError = lockError;
1.1827 + return CFsRequest::EReqActionComplete;
1.1828 + }
1.1829 +
1.1830 + currentOperation->Set(pos, len, addr, 0, EStWriteToDiskComplete);
1.1831 + if (pos < iSize64)
1.1832 + {
1.1833 + TInt r = aMsgRequest.PushOperation(
1.1834 + pos, len, addr, 0,
1.1835 + CompleteFlushDirty,
1.1836 + EStWriteToDiskComplete);
1.1837 + if (r != KErrNone)
1.1838 + {
1.1839 + iCacheClient->UnlockSegments(pos);
1.1840 + iFlushBusy = EFalse;
1.1841 + lastError = r;
1.1842 + return CFsRequest::EReqActionComplete;
1.1843 + }
1.1844 +
1.1845 + return CFsRequest::EReqActionContinue; // continue on to TFsFileWrite::DoRequestL()()
1.1846 + }
1.1847 + }
1.1848 +
1.1849 + case EStWriteToDiskComplete:
1.1850 + {
1.1851 +#ifdef _DEBUG
1.1852 + // simulate a media eject to test critical error server
1.1853 + if (CCacheManagerFactory::CacheManager()->SimulateWriteFailureEnabled())
1.1854 + {
1.1855 + lastError = KErrNotReady;
1.1856 + iDrive->Dismount();
1.1857 + }
1.1858 +#endif
1.1859 + pos = currentOperation->iReadWriteArgs.iPos;
1.1860 + iCacheClient->UnlockSegments(pos);
1.1861 + }
1.1862 + // fall into...
1.1863 +
1.1864 + case EStMarkAsClean:
1.1865 + {
1.1866 + // NB pos must be set by EStWriteToDiskComplete or EStWriteToDisk
1.1867 +
1.1868 + if (lastError != KErrNone)
1.1869 + {
1.1870 + __CACHE_PRINT1(_L("CACHEFILE: WriteThrough FAILED %d"), lastError);
1.1871 +
1.1872 + lastError = HandleWriteDirtyError(lastError);
1.1873 +
1.1874 + // retry ?
1.1875 + if (lastError == KErrNone)
1.1876 + {
1.1877 + // clear error and try again
1.1878 + currentOperation->iState = EStWriteToDisk;
1.1879 + break;
1.1880 + }
1.1881 +
1.1882 + iFlushError = lastError;
1.1883 + PropagateFlushErrorToAllFileShares();
1.1884 +
1.1885 + __CACHE_PRINT2(_L("CACHEFILE: Resetting size from %ld to %ld"), iSize64, iFileCB->Size64());
1.1886 + SetSize64(iFileCB->Size64());
1.1887 + }
1.1888 +
1.1889 + if (lastError != KErrNone)
1.1890 + {
1.1891 + // Destroy ALL cached data (dirty and non-dirty) !
1.1892 + iCacheClient->Purge(ETrue);
1.1893 + }
1.1894 + else
1.1895 + {
1.1896 + // Mark segment as clean
1.1897 + iCacheClient->MarkSegmentsAsClean(pos);
1.1898 + }
1.1899 +
1.1900 +
1.1901 + if (TInt(currentOperation->iScratchValue0) > 0)
1.1902 + currentOperation->iState = EStWriteToDisk;
1.1903 + else
1.1904 + currentOperation->iState = EStEnd;
1.1905 +
1.1906 +
1.1907 + }
1.1908 + break;
1.1909 +
1.1910 + case EStEnd:
1.1911 + {
1.1912 + iFlushBusy = EFalse;
1.1913 +
1.1914 + TUint8* addr;
1.1915 + MarkFileClean();
1.1916 + // Re-start dirty data timer if there is still some dirty data
1.1917 + if (iCacheClient->FindFirstDirtySegment(pos, addr) != 0)
1.1918 + FileDirty(aMsgRequest);
1.1919 +
1.1920 + return CFsRequest::EReqActionComplete;
1.1921 + }
1.1922 + }
1.1923 + }
1.1924 + }
1.1925 +
1.1926 +/**
1.1927 +Handle a dirty data write error
1.1928 +
1.1929 +Returns aError if dirty data should be thrown away or
1.1930 + KErrNone if write should be retried
1.1931 +*/
1.1932 +TInt CFileCache::HandleWriteDirtyError(TInt aError)
1.1933 + {
1.1934 + __THRD_PRINT3(_L(">TRACE: CFileCache::HandleWriteDirtyError() aError %d mounted %d changed %d"), aError, iDrive->IsMounted(), iDrive->IsChanged());
1.1935 +
1.1936 + // normally the disk change will have been detected by TDrive::CheckMount() but occasionally,
1.1937 + // the change will occur while writing - in which case we need to mimick what TDrive::CheckMount does,
1.1938 + // to make sure we are in a consisten state i.e. dismount the drive and set iCurrentMount to NULL
1.1939 + if (iDrive->IsChanged())
1.1940 + {
1.1941 + iDrive->SetChanged(EFalse);
1.1942 + if (iDrive->IsMounted()) // Dismount the mount if it is still marked as mounted
1.1943 + iDrive->Dismount();
1.1944 + }
1.1945 +
1.1946 + // if error didn't occur because of a media eject, do nothing
1.1947 + if (aError == KErrNotReady && !iDrive->IsMounted())
1.1948 + {
1.1949 +
1.1950 + TLocaleMessage line1;
1.1951 + TLocaleMessage line2;
1.1952 + line1=EFileServer_PutTheCardBackLine1;
1.1953 + line2=EFileServer_PutTheCardBackLine2;
1.1954 +
1.1955 + //-- create Notifier
1.1956 + CAsyncNotifier* notifier = CAsyncNotifier::New();
1.1957 + if( !notifier )
1.1958 + {
1.1959 + return aError;
1.1960 + }
1.1961 +
1.1962 + notifier->SetMount(iMount);
1.1963 +
1.1964 +
1.1965 + // While this (drive) thread is calling the notifier server (& effectively suspended),
1.1966 + // the main thread may call TFsFileRead::PostInitialise() or TFsFileWrite::PostInitialise()
1.1967 + // which would cause dead-lock unless we release the lock here. We also need to release
1.1968 + // the lock before calling CDriveThread::CompleteReadWriteRequests().
1.1969 + iLock.Signal();
1.1970 +
1.1971 + FOREVER
1.1972 + {
1.1973 + TInt buttonVal;
1.1974 +
1.1975 + TInt ret = notifier->Notify(
1.1976 + TLocaleMessageText(line1),
1.1977 + TLocaleMessageText(line2),
1.1978 + TLocaleMessageText(EFileServer_Button1),
1.1979 + TLocaleMessageText(EFileServer_Button2),
1.1980 + buttonVal);
1.1981 + if (ret!=KErrNone)
1.1982 + break;
1.1983 + if (buttonVal!=1)
1.1984 + break; // Abort
1.1985 +
1.1986 +
1.1987 + // Wait up to 3 seconds for a disk change - this is because there is often a substantial
1.1988 + // (one/two second) delay between inserting a card & getting a notification that the card is ready;
1.1989 + // if we give up too soon and fire of the notifier again, it's not very user-friendly !
1.1990 + const TTimeIntervalMicroSeconds32 KHalfSecond(500000);
1.1991 + const TInt KRetries = 6;;
1.1992 + for (TInt n=0; !iDrive->IsChanged() && n<KRetries; n++)
1.1993 + User::After(KHalfSecond);
1.1994 +
1.1995 +
1.1996 +
1.1997 + // Without this code, retry will indiscriminately write over whatever disk happens to be present.
1.1998 + // However if the write error is to the bootsector remounting will always fail because the boot
1.1999 + // sector will have changed and hence the disk is useless.
1.2000 + //
1.2001 + TRACE1(UTF::EBorder, UTraceModuleFileSys::ECMountCBReMount, EF32TraceUidFileSys, DriveNumber());
1.2002 +
1.2003 + TInt remountSuccess = iDrive->ReMount(*iMount);
1.2004 +
1.2005 + TRACE1(UTF::EBorder, UTraceModuleFileSys::ECMountCBReMountRet, EF32TraceUidFileSys, remountSuccess);
1.2006 + if (!remountSuccess)
1.2007 + continue;
1.2008 +
1.2009 +
1.2010 + iMount->Drive().SetChanged(EFalse);
1.2011 +
1.2012 + aError = KErrNone; // Retry
1.2013 + break;
1.2014 + }
1.2015 +
1.2016 + delete notifier;
1.2017 +
1.2018 +
1.2019 + // Cancel hung state
1.2020 + FsThreadManager::SetDriveHung(DriveNumber(), EFalse);
1.2021 +
1.2022 +
1.2023 + // media had been removed and NOT replaced: so destroy ALL cached data
1.2024 + // (dirty and non-dirty) for all filecaches on this drive
1.2025 + if (aError != KErrNone)
1.2026 + {
1.2027 + CDriveThread* pT=NULL;
1.2028 + TInt r=FsThreadManager::GetDriveThread(DriveNumber(), &pT);
1.2029 + if(r==KErrNone)
1.2030 + {
1.2031 + pT->CompleteReadWriteRequests();
1.2032 + iDrive->PurgeDirty(*iMount);
1.2033 + }
1.2034 + }
1.2035 +
1.2036 + iLock.Wait();
1.2037 + }
1.2038 +
1.2039 +
1.2040 + return aError;
1.2041 + }
1.2042 +
1.2043 +
1.2044 +/**
1.2045 +Mark file as dirty
1.2046 +*/
1.2047 +void CFileCache::FileDirty(CFsMessageRequest& aMsgRequest)
1.2048 + {
1.2049 + CSessionFs* session = aMsgRequest.Session();
1.2050 + __ASSERT_ALWAYS(session, Fault(EDirtyDataOwnerNull));
1.2051 +
1.2052 + // Remember the last session which caused the file to become dirty
1.2053 + // Always record whether any session has reserved access so the CheckDiskSpace() behaves correctly
1.2054 + if (iDirtyDataOwner == NULL || session->ReservedAccess(iDriveNum))
1.2055 + iDirtyDataOwner = session;
1.2056 +
1.2057 + // start a timer after which file will be flushed
1.2058 + CDriveThread* driveThread=NULL;
1.2059 + TInt r = FsThreadManager::GetDriveThread(iDriveNum, &driveThread);
1.2060 + if(r == KErrNone && driveThread != NULL)
1.2061 + iDirtyTimer.Start(driveThread, iDirtyDataFlushTime);
1.2062 + }
1.2063 +
1.2064 +//----------------------------------------------------------------------------
1.2065 +/**
1.2066 + Mark the file as clean and stop dirty data timer
1.2067 +*/
1.2068 +void CFileCache::MarkFileClean()
1.2069 + {
1.2070 + iDirtyDataOwner = NULL;
1.2071 +
1.2072 + if (!iDriveThread)
1.2073 + return;
1.2074 +
1.2075 + iDirtyTimer.Stop();
1.2076 + }
1.2077 +
1.2078 +
1.2079 +
1.2080 +//************************************
1.2081 +// TFileCacheSettings
1.2082 +//************************************
1.2083 +
1.2084 +RArray<TFileCacheSettings::TFileCacheConfig>* TFileCacheSettings::iFileCacheSettings = NULL;
1.2085 +
1.2086 +
1.2087 +
1.2088 +const TInt KDriveCacheSettingsArrayGranularity = 4;
1.2089 +
1.2090 +
1.2091 +TFileCacheSettings::TFileCacheConfig::TFileCacheConfig(TInt aDrive) :
1.2092 + iDrive(aDrive),
1.2093 + iFileCacheReadAsync(KDefaultFileCacheReadAsync),
1.2094 + iFairSchedulingLen(KDefaultFairSchedulingLen << KByteToByteShift),
1.2095 + iCacheSize(KDefaultFileCacheSize << KByteToByteShift),
1.2096 + iMaxReadAheadLen(KDefaultFileCacheMaxReadAheadLen << KByteToByteShift),
1.2097 + iClosedFileKeepAliveTime(KDefaultClosedFileKeepAliveTime << KByteToByteShift), // convert milliSecs -> microSecs (approximately)
1.2098 + iDirtyDataFlushTime(KDefaultDirtyDataFlushTime << KByteToByteShift) // convert milliSecs -> microSecs (approximately)
1.2099 + {
1.2100 + iFlags = ConvertEnumToFlags(KDefaultFileCacheRead, KDefaultFileCacheReadAhead, KDefaultFileCacheWrite);
1.2101 + }
1.2102 +
1.2103 +
1.2104 +TFileCacheFlags TFileCacheSettings::TFileCacheConfig::ConvertEnumToFlags(const TInt aFileCacheRead, const TInt aFileCacheReadAhead, const TInt aFileCacheWrite)
1.2105 + {
1.2106 + TInt flags(0);
1.2107 +
1.2108 + // read caching
1.2109 + if (aFileCacheRead == EFileCacheFlagEnabled)
1.2110 + flags|= EFileCacheReadEnabled;
1.2111 + else if (aFileCacheRead == EFileCacheFlagOn)
1.2112 + flags|= EFileCacheReadEnabled | EFileCacheReadOn;
1.2113 +
1.2114 + // read ahead
1.2115 + if (aFileCacheReadAhead == EFileCacheFlagEnabled)
1.2116 + flags|= EFileCacheReadAheadEnabled;
1.2117 + else if (aFileCacheReadAhead == EFileCacheFlagOn)
1.2118 + flags|= EFileCacheReadAheadEnabled | EFileCacheReadAheadOn;
1.2119 +
1.2120 + // write caching
1.2121 + if (aFileCacheWrite == EFileCacheFlagEnabled)
1.2122 + flags|= EFileCacheWriteEnabled;
1.2123 + else if (aFileCacheWrite == EFileCacheFlagOn)
1.2124 + flags|= EFileCacheWriteEnabled | EFileCacheWriteOn;
1.2125 +
1.2126 + return TFileCacheFlags(flags);
1.2127 + }
1.2128 +
1.2129 +
1.2130 +
1.2131 +_LIT8(KLitSectionNameDrive,"Drive%C");
1.2132 +
1.2133 +static const TPtrC8 KCacheFlagEnumStrings[]=
1.2134 + {
1.2135 + _S8("OFF"),
1.2136 + _S8("ENABLED"),
1.2137 + _S8("ON"),
1.2138 + _S8(""), // terminator
1.2139 + };
1.2140 +const TInt KMaxEnumLen = 7;
1.2141 +
1.2142 +void TFileCacheSettings::ReadEnum(const TDesC8& aSection, const TDesC8& aProperty, TInt32& aEnumVal, const TPtrC8* aEnumStrings)
1.2143 + {
1.2144 + TBuf8<KMaxEnumLen> buf;
1.2145 + if (!F32Properties::GetString(aSection, aProperty, buf))
1.2146 + return;
1.2147 + TInt n;
1.2148 + const TPtrC8* enumString;
1.2149 + for (enumString=aEnumStrings, n=0; enumString->Length()!= 0; enumString++, n++)
1.2150 + {
1.2151 + if (buf.LeftTPtr(enumString->Length()).MatchF(*enumString) == 0)
1.2152 + {
1.2153 + aEnumVal = n;
1.2154 + break;
1.2155 + }
1.2156 + }
1.2157 + }
1.2158 +
1.2159 +TInt TFileCacheSettings::ReadPropertiesFile(TInt aDriveNumber)
1.2160 + {
1.2161 + Init();
1.2162 +
1.2163 +
1.2164 + if (!TGlobalFileCacheSettings::Enabled())
1.2165 + return KErrNone;
1.2166 +
1.2167 + TFileCacheConfig* driveCacheSettings;
1.2168 + TInt r = GetFileCacheConfig(aDriveNumber, driveCacheSettings);
1.2169 + if (r != KErrNone)
1.2170 + return r;
1.2171 +
1.2172 + // restore default settings in case they've been changed by SetFlags()
1.2173 + driveCacheSettings->iFlags = TFileCacheConfig::ConvertEnumToFlags(KDefaultFileCacheRead, KDefaultFileCacheReadAhead, KDefaultFileCacheWrite);
1.2174 +
1.2175 +
1.2176 + // Get file cache configuration settings for this drive
1.2177 + // N.B. Size/length values are specified in Kilobytes, timer values in Milliseconds
1.2178 + TBuf8<8> sectionName;
1.2179 + sectionName.Format(KLitSectionNameDrive, 'A' + aDriveNumber);
1.2180 +
1.2181 + TInt32 val;
1.2182 + // Read FileCacheSize
1.2183 + if (F32Properties::GetInt(sectionName, _L8("FileCacheSize"), val))
1.2184 + driveCacheSettings->iCacheSize = val << KByteToByteShift;
1.2185 +
1.2186 + // ensure read-ahead len is not greater than the cache size
1.2187 + driveCacheSettings->iMaxReadAheadLen =
1.2188 + Min(driveCacheSettings->iMaxReadAheadLen, driveCacheSettings->iCacheSize);
1.2189 +
1.2190 + // Read FileCacheReadAsync
1.2191 + TBool bVal;
1.2192 + if (F32Properties::GetBool(sectionName, _L8("FileCacheReadAsync"), bVal))
1.2193 + driveCacheSettings->iFileCacheReadAsync = bVal;
1.2194 +
1.2195 + // Read FairSchedulingLen
1.2196 + if (F32Properties::GetInt(sectionName, _L8("FairSchedulingLen"), val))
1.2197 + driveCacheSettings->iFairSchedulingLen = val << KByteToByteShift;
1.2198 +
1.2199 + // Read ClosedFileKeepAliveTime - convert miliSecs to microSecs (approximately)
1.2200 + if (F32Properties::GetInt(sectionName, _L8("ClosedFileKeepAliveTime"), val))
1.2201 + driveCacheSettings->iClosedFileKeepAliveTime = val << KByteToByteShift;
1.2202 +
1.2203 + // Read DirtyDataFlushTime - convert miliSecs to microSecs (approximately)
1.2204 + if (F32Properties::GetInt(sectionName, _L8("DirtyDataFlushTime"), val))
1.2205 + driveCacheSettings->iDirtyDataFlushTime = val << KByteToByteShift;
1.2206 +
1.2207 + // get read, read-ahead and write states
1.2208 + TInt32 readVal = KDefaultFileCacheRead;
1.2209 + TInt32 readAheadVal = KDefaultFileCacheReadAhead;
1.2210 + TInt32 writeVal = KDefaultFileCacheWrite;
1.2211 +
1.2212 + ReadEnum(sectionName, _L8("FileCacheRead"), readVal, &KCacheFlagEnumStrings[0]);
1.2213 + ReadEnum(sectionName, _L8("FileCacheReadAhead"), readAheadVal, &KCacheFlagEnumStrings[0]);
1.2214 + ReadEnum(sectionName, _L8("FileCacheWrite"), writeVal, &KCacheFlagEnumStrings[0]);
1.2215 + driveCacheSettings->iFlags = TFileCacheConfig::ConvertEnumToFlags(readVal, readAheadVal, writeVal);
1.2216 +
1.2217 + __CACHE_PRINT7(_L("ReadPropertiesFile() drive %C flags %08X CacheSize %d FileCacheReadAsync %d iFairSchedulingLen %d iClosedFileKeepAliveTime %d iDirtyDataFlushTime %d\n"),
1.2218 + aDriveNumber + 'A',
1.2219 + driveCacheSettings->iFlags,
1.2220 + driveCacheSettings->iCacheSize,
1.2221 + driveCacheSettings->iFileCacheReadAsync,
1.2222 + driveCacheSettings->iFairSchedulingLen,
1.2223 + driveCacheSettings->iClosedFileKeepAliveTime,
1.2224 + driveCacheSettings->iDirtyDataFlushTime);
1.2225 +
1.2226 + return KErrNone;
1.2227 + }
1.2228 +
1.2229 +
1.2230 +TInt TFileCacheSettings::GetFileCacheConfig(TInt aDrive, TFileCacheConfig*& aConfig)
1.2231 + {
1.2232 + Init();
1.2233 +
1.2234 + aConfig = NULL;
1.2235 +
1.2236 + TFileCacheConfig driveCacheSettingsToMatch(aDrive);
1.2237 +
1.2238 + TInt index = iFileCacheSettings->FindInUnsignedKeyOrder(driveCacheSettingsToMatch);
1.2239 + if (index == KErrNotFound)
1.2240 + {
1.2241 + // create a new entry.
1.2242 + TInt r = iFileCacheSettings->InsertInUnsignedKeyOrder(driveCacheSettingsToMatch);
1.2243 + if (r != KErrNone)
1.2244 + return r;
1.2245 + index = iFileCacheSettings->FindInUnsignedKeyOrder(driveCacheSettingsToMatch);
1.2246 + __ASSERT_ALWAYS(index != KErrNotFound, Fault(ECacheSettingsNotFound));
1.2247 + }
1.2248 +
1.2249 + aConfig = &(*iFileCacheSettings)[index];
1.2250 + return KErrNone;
1.2251 + }
1.2252 +
1.2253 +void TFileCacheSettings::SetFlags(TInt aDrive, TFileCacheFlags aFlags)
1.2254 + {
1.2255 + TFileCacheConfig* driveCacheSettings;
1.2256 + TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
1.2257 + __ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
1.2258 +
1.2259 + driveCacheSettings->iFlags = aFlags;
1.2260 + }
1.2261 +
1.2262 +TFileCacheFlags TFileCacheSettings::Flags(TInt aDrive)
1.2263 + {
1.2264 + TFileCacheConfig* driveCacheSettings;
1.2265 + TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
1.2266 + __ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
1.2267 +
1.2268 + return driveCacheSettings->iFlags;
1.2269 + }
1.2270 +
1.2271 +void TFileCacheSettings::Init()
1.2272 + {
1.2273 + if (iFileCacheSettings == NULL)
1.2274 + {
1.2275 + iFileCacheSettings = new RArray<TFileCacheConfig>(KDriveCacheSettingsArrayGranularity, _FOFF(TFileCacheConfig, iDrive));
1.2276 + __ASSERT_ALWAYS(iFileCacheSettings != NULL, Fault(ECacheSettingsInitFailed));
1.2277 + }
1.2278 + }
1.2279 +
1.2280 +
1.2281 +TBool TFileCacheSettings::FileCacheReadAsync(TInt aDrive)
1.2282 + {
1.2283 + TFileCacheConfig* driveCacheSettings;
1.2284 + TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
1.2285 + __ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
1.2286 +
1.2287 + return driveCacheSettings->iFileCacheReadAsync;
1.2288 + }
1.2289 +
1.2290 +TInt TFileCacheSettings::FairSchedulingLen(TInt aDrive)
1.2291 + {
1.2292 + TFileCacheConfig* driveCacheSettings;
1.2293 + TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
1.2294 + __ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
1.2295 +
1.2296 + return driveCacheSettings->iFairSchedulingLen;
1.2297 + }
1.2298 +
1.2299 +TInt TFileCacheSettings::MaxReadAheadLen(TInt aDrive)
1.2300 + {
1.2301 + TFileCacheConfig* driveCacheSettings;
1.2302 + TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
1.2303 + __ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
1.2304 +
1.2305 + return driveCacheSettings->iMaxReadAheadLen;
1.2306 + }
1.2307 +
1.2308 +TInt TFileCacheSettings::CacheSize(TInt aDrive)
1.2309 + {
1.2310 + TFileCacheConfig* driveCacheSettings;
1.2311 + TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
1.2312 + __ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
1.2313 +
1.2314 + return driveCacheSettings->iCacheSize;
1.2315 + }
1.2316 +
1.2317 +TInt TFileCacheSettings::ClosedFileKeepAliveTime(TInt aDrive)
1.2318 + {
1.2319 + TFileCacheConfig* driveCacheSettings;
1.2320 + TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
1.2321 + __ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
1.2322 +
1.2323 + return driveCacheSettings->iClosedFileKeepAliveTime;
1.2324 + }
1.2325 +
1.2326 +
1.2327 +TInt TFileCacheSettings::DirtyDataFlushTime(TInt aDrive)
1.2328 + {
1.2329 + TFileCacheConfig* driveCacheSettings;
1.2330 + TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
1.2331 + __ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
1.2332 +
1.2333 + return driveCacheSettings->iDirtyDataFlushTime;
1.2334 + }
1.2335 +
1.2336 +//************************************
1.2337 +// TClosedFileUtils
1.2338 +//************************************
1.2339 +
1.2340 +CFsObjectCon* TClosedFileUtils::iClosedFiles = NULL;
1.2341 +
1.2342 +void TClosedFileUtils::InitL()
1.2343 + {
1.2344 + iClosedFiles = TheContainer->CreateL();
1.2345 + if (iClosedFiles == NULL)
1.2346 + User::LeaveIfError(KErrNoMemory);
1.2347 +
1.2348 + }
1.2349 +
1.2350 +
1.2351 +TBool TClosedFileUtils::IsClosed(CFileCache* aFileCache)
1.2352 + {
1.2353 + return (aFileCache->Container() == iClosedFiles);
1.2354 + }
1.2355 +
1.2356 +TInt TClosedFileUtils::Count()
1.2357 + {
1.2358 + return iClosedFiles->Count();
1.2359 + }
1.2360 +
1.2361 +CFileCache* TClosedFileUtils::At(TInt aIndex)
1.2362 + {
1.2363 + return (CFileCache*) (*iClosedFiles)[aIndex];
1.2364 + }
1.2365 +
1.2366 +
1.2367 +// Add a closed file to closed file container
1.2368 +void TClosedFileUtils::AddL(CFileCache* aFileCache, TBool aLock)
1.2369 + {
1.2370 + if (aFileCache->iDriveThread)
1.2371 + {
1.2372 + iClosedFiles->AddL(aFileCache, aLock);
1.2373 +
1.2374 + // start a timer after which file will be removed from closed queue
1.2375 + CDriveThread* driveThread=NULL;
1.2376 + TInt r = FsThreadManager::GetDriveThread(aFileCache->iDriveNum, &driveThread);
1.2377 + if(r == KErrNone && driveThread != NULL)
1.2378 + aFileCache->iClosedTimer.Start(driveThread, aFileCache->iClosedFileKeepAliveTime);
1.2379 + }
1.2380 + }
1.2381 +
1.2382 +
1.2383 +
1.2384 +// Remove a closed file from closed file container so that it can be re-opened
1.2385 +void TClosedFileUtils::ReOpen(CFileCache* aFileCache, TBool aLock)
1.2386 + {
1.2387 + // get the drive thread in case it has changed since the file was last open
1.2388 + const TInt r = FsThreadManager::GetDriveThread(aFileCache->iDriveNum, &aFileCache->iDriveThread);
1.2389 + if ((r == KErrNone) && aFileCache->iDriveThread)
1.2390 + aFileCache->iClosedTimer.Stop();
1.2391 +
1.2392 + iClosedFiles->Remove(aFileCache, aLock);
1.2393 + }
1.2394 +
1.2395 +// Remove all closed files from closed file container and close them for good
1.2396 +void TClosedFileUtils::Remove()
1.2397 + {
1.2398 + RemoveFiles(NULL, NULL);
1.2399 + }
1.2400 +
1.2401 +// Remove all closed files belonging to a particular TDrive from closed file container and close them for good
1.2402 +void TClosedFileUtils::Remove(TInt aDrvNumber)
1.2403 + {
1.2404 + RemoveFiles(&TClosedFileUtils::TestDrive, (TAny*) aDrvNumber);
1.2405 + }
1.2406 +
1.2407 +// Remove a closed file from closed file container and close it for good
1.2408 +void TClosedFileUtils::Remove(CFileCache* aFileCache)
1.2409 + {
1.2410 + RemoveFiles(&TClosedFileUtils::TestFile, (TAny*) aFileCache);
1.2411 + }
1.2412 +
1.2413 +void TClosedFileUtils::RemoveFiles(TTestFunc aTestFunc, TAny* aTestVal)
1.2414 + {
1.2415 + Lock();
1.2416 +
1.2417 + TInt count = TClosedFileUtils::Count();
1.2418 + while(count--)
1.2419 + {
1.2420 + CFileCache& file = *(CFileCache*)(*iClosedFiles)[count];
1.2421 + if ((aTestFunc == NULL) || ((*aTestFunc)(file, aTestVal)))
1.2422 + {
1.2423 + iClosedFiles->Remove(&file, EFalse);
1.2424 + file.Close();
1.2425 + }
1.2426 + }
1.2427 +
1.2428 + Unlock();
1.2429 + }
1.2430 +
1.2431 +
1.2432 +TBool TClosedFileUtils::TestDrive(CFileCache& aFileCache, TAny* aVal)
1.2433 + {
1.2434 + TBool r = (aFileCache.iDriveNum == (TInt) aVal)?(TBool) ETrue: (TBool) EFalse;
1.2435 + if (r)
1.2436 + {
1.2437 + __CACHE_PRINT1(_L("CLOSEDFILES: TestDrive() closing %S\n"), &aFileCache.FileNameF() );
1.2438 + }
1.2439 + return r;
1.2440 + }
1.2441 +TBool TClosedFileUtils::TestFile(CFileCache& aFileCache, TAny* aVal)
1.2442 + {
1.2443 + TBool r = (&aFileCache == ((CFileCache*) aVal))?(TBool)ETrue:(TBool)EFalse;
1.2444 + if (r)
1.2445 + {
1.2446 + __CACHE_PRINT1(_L("CLOSEDFILES: TestFile() closing %S\n"), &aFileCache.FileNameF() );
1.2447 + }
1.2448 + return r;
1.2449 + }
1.2450 +
1.2451 +
1.2452 +
1.2453 +void TClosedFileUtils::Lock()
1.2454 + {
1.2455 + iClosedFiles->Lock();
1.2456 + }
1.2457 +
1.2458 +void TClosedFileUtils::Unlock()
1.2459 + {
1.2460 + iClosedFiles->Unlock();
1.2461 + }
1.2462 +
1.2463 +