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