2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/cc/view.c
5 * PURPOSE: Cache manager
7 * PROGRAMMERS: David Welch (welch@mcmail.com)
10 /* NOTES **********************************************************************
12 * This is not the NT implementation of a file cache nor anything much like
15 * The general procedure for a filesystem to implement a read or write
16 * dispatch routine is as follows
18 * (1) If caching for the FCB hasn't been initiated then so do by calling
19 * CcInitializeFileCache.
21 * (2) For each 4k region which is being read or written obtain a cache page
22 * by calling CcRequestCachePage.
24 * (3) If either the page is being read or not completely written, and it is
25 * not up to date then read its data from the underlying medium. If the read
26 * fails then call CcReleaseCachePage with VALID as FALSE and return a error.
28 * (4) Copy the data into or out of the page as necessary.
30 * (5) Release the cache page
32 /* INCLUDES ******************************************************************/
38 #if defined (ALLOC_PRAGMA)
39 #pragma alloc_text(INIT, CcInitView)
42 /* GLOBALS *******************************************************************/
45 * If CACHE_BITMAP is defined, the cache manager uses one large memory region
46 * within the kernel address space and allocate/deallocate space from this block
47 * over a bitmap. If CACHE_BITMAP is used, the size of the mdl mapping region
48 * must be reduced (ntoskrnl\mm\mdl.c, MI_MDLMAPPING_REGION_SIZE).
50 //#define CACHE_BITMAP
52 static LIST_ENTRY DirtySegmentListHead
;
53 static LIST_ENTRY CacheSegmentListHead
;
54 static LIST_ENTRY CacheSegmentLRUListHead
;
55 static LIST_ENTRY ClosedListHead
;
56 ULONG DirtyPageCount
=0;
58 KGUARDED_MUTEX ViewLock
;
61 #define CI_CACHESEG_MAPPING_REGION_SIZE (128*1024*1024)
63 static PVOID CiCacheSegMappingRegionBase
= NULL
;
64 static RTL_BITMAP CiCacheSegMappingRegionAllocMap
;
65 static ULONG CiCacheSegMappingRegionHint
;
66 static KSPIN_LOCK CiCacheSegMappingRegionLock
;
69 NPAGED_LOOKASIDE_LIST iBcbLookasideList
;
70 static NPAGED_LOOKASIDE_LIST BcbLookasideList
;
71 static NPAGED_LOOKASIDE_LIST CacheSegLookasideList
;
74 static void CcRosCacheSegmentIncRefCount_ ( PCACHE_SEGMENT cs
, const char* file
, int line
)
79 DbgPrint("(%s:%i) CacheSegment %p ++RefCount=%d, Dirty %d, PageOut %d\n",
80 file
, line
, cs
, cs
->ReferenceCount
, cs
->Dirty
, cs
->PageOut
);
83 static void CcRosCacheSegmentDecRefCount_ ( PCACHE_SEGMENT cs
, const char* file
, int line
)
88 DbgPrint("(%s:%i) CacheSegment %p --RefCount=%d, Dirty %d, PageOut %d\n",
89 file
, line
, cs
, cs
->ReferenceCount
, cs
->Dirty
, cs
->PageOut
);
92 #define CcRosCacheSegmentIncRefCount(cs) CcRosCacheSegmentIncRefCount_(cs,__FILE__,__LINE__)
93 #define CcRosCacheSegmentDecRefCount(cs) CcRosCacheSegmentDecRefCount_(cs,__FILE__,__LINE__)
95 #define CcRosCacheSegmentIncRefCount(cs) (++((cs)->ReferenceCount))
96 #define CcRosCacheSegmentDecRefCount(cs) (--((cs)->ReferenceCount))
100 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg
);
103 /* FUNCTIONS *****************************************************************/
113 PLIST_ENTRY current_entry
;
114 PCACHE_SEGMENT current
;
123 DPRINT1("Enabling Tracing for CacheMap 0x%p:\n", Bcb
);
125 KeAcquireGuardedMutex(&ViewLock
);
126 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldirql
);
128 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
129 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
131 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, BcbSegmentListEntry
);
132 current_entry
= current_entry
->Flink
;
134 DPRINT1(" CacheSegment 0x%p enabled, RefCount %d, Dirty %d, PageOut %d\n",
135 current
, current
->ReferenceCount
, current
->Dirty
, current
->PageOut
);
137 KeReleaseSpinLock(&Bcb
->BcbLock
, oldirql
);
138 KeReleaseGuardedMutex(&ViewLock
);
142 DPRINT1("Disabling Tracing for CacheMap 0x%p:\n", Bcb
);
153 CcRosFlushCacheSegment(PCACHE_SEGMENT CacheSegment
)
158 Status
= WriteCacheSegment(CacheSegment
);
159 if (NT_SUCCESS(Status
))
161 KeAcquireGuardedMutex(&ViewLock
);
162 KeAcquireSpinLock(&CacheSegment
->Bcb
->BcbLock
, &oldIrql
);
164 CacheSegment
->Dirty
= FALSE
;
165 RemoveEntryList(&CacheSegment
->DirtySegmentListEntry
);
166 DirtyPageCount
-= CacheSegment
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
167 CcRosCacheSegmentDecRefCount ( CacheSegment
);
169 KeReleaseSpinLock(&CacheSegment
->Bcb
->BcbLock
, oldIrql
);
170 KeReleaseGuardedMutex(&ViewLock
);
178 CcRosFlushDirtyPages(ULONG Target
, PULONG Count
, BOOLEAN Wait
)
180 PLIST_ENTRY current_entry
;
181 PCACHE_SEGMENT current
;
182 ULONG PagesPerSegment
;
185 LARGE_INTEGER ZeroTimeout
;
187 DPRINT("CcRosFlushDirtyPages(Target %d)\n", Target
);
190 ZeroTimeout
.QuadPart
= 0;
192 KeEnterCriticalRegion();
193 KeAcquireGuardedMutex(&ViewLock
);
195 current_entry
= DirtySegmentListHead
.Flink
;
196 if (current_entry
== &DirtySegmentListHead
)
198 DPRINT("No Dirty pages\n");
201 while (current_entry
!= &DirtySegmentListHead
&& Target
> 0)
203 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
204 DirtySegmentListEntry
);
205 current_entry
= current_entry
->Flink
;
207 CcRosCacheSegmentIncRefCount(current
);
209 Locked
= current
->Bcb
->Callbacks
->AcquireForLazyWrite(
210 current
->Bcb
->LazyWriteContext
, Wait
);
213 CcRosCacheSegmentDecRefCount(current
);
217 Status
= KeWaitForSingleObject(¤t
->Mutex
,
221 Wait
? NULL
: &ZeroTimeout
);
222 if (Status
!= STATUS_SUCCESS
)
224 current
->Bcb
->Callbacks
->ReleaseFromLazyWrite(
225 current
->Bcb
->LazyWriteContext
);
226 CcRosCacheSegmentDecRefCount(current
);
230 ASSERT(current
->Dirty
);
232 /* One reference is added above */
233 if (current
->ReferenceCount
> 2)
235 KeReleaseMutex(¤t
->Mutex
, 0);
236 current
->Bcb
->Callbacks
->ReleaseFromLazyWrite(
237 current
->Bcb
->LazyWriteContext
);
238 CcRosCacheSegmentDecRefCount(current
);
242 PagesPerSegment
= current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
244 KeReleaseGuardedMutex(&ViewLock
);
246 Status
= CcRosFlushCacheSegment(current
);
248 KeReleaseMutex(¤t
->Mutex
, 0);
249 current
->Bcb
->Callbacks
->ReleaseFromLazyWrite(
250 current
->Bcb
->LazyWriteContext
);
252 KeAcquireGuardedMutex(&ViewLock
);
253 CcRosCacheSegmentDecRefCount(current
);
255 if (!NT_SUCCESS(Status
) && (Status
!= STATUS_END_OF_FILE
))
257 DPRINT1("CC: Failed to flush cache segment.\n");
261 (*Count
) += PagesPerSegment
;
262 Target
-= PagesPerSegment
;
265 current_entry
= DirtySegmentListHead
.Flink
;
268 KeReleaseGuardedMutex(&ViewLock
);
269 KeLeaveCriticalRegion();
271 DPRINT("CcRosFlushDirtyPages() finished\n");
272 return(STATUS_SUCCESS
);
276 CcRosTrimCache(ULONG Target
, ULONG Priority
, PULONG NrFreed
)
278 * FUNCTION: Try to free some memory from the file cache.
280 * Target - The number of pages to be freed.
281 * Priority - The priority of free (currently unused).
282 * NrFreed - Points to a variable where the number of pages
283 * actually freed is returned.
286 PLIST_ENTRY current_entry
;
287 PCACHE_SEGMENT current
;
288 ULONG PagesPerSegment
;
294 BOOLEAN FlushedPages
= FALSE
;
296 DPRINT("CcRosTrimCache(Target %d)\n", Target
);
298 InitializeListHead(&FreeList
);
303 KeAcquireGuardedMutex(&ViewLock
);
305 current_entry
= CacheSegmentLRUListHead
.Flink
;
306 while (current_entry
!= &CacheSegmentLRUListHead
)
308 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
309 CacheSegmentLRUListEntry
);
310 current_entry
= current_entry
->Flink
;
312 KeAcquireSpinLock(¤t
->Bcb
->BcbLock
, &oldIrql
);
314 /* Reference the cache segment */
315 CcRosCacheSegmentIncRefCount(current
);
317 /* Check if it's mapped and not dirty */
318 if (current
->MappedCount
> 0 && !current
->Dirty
)
320 /* We have to break these locks because Cc sucks */
321 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
322 KeReleaseGuardedMutex(&ViewLock
);
324 /* Page out the segment */
325 for (i
= 0; i
< current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
; i
++)
327 Page
= (PFN_NUMBER
)(MmGetPhysicalAddress((PUCHAR
)current
->BaseAddress
+ (i
* PAGE_SIZE
)).QuadPart
>> PAGE_SHIFT
);
329 MmPageOutPhysicalAddress(Page
);
332 /* Reacquire the locks */
333 KeAcquireGuardedMutex(&ViewLock
);
334 KeAcquireSpinLock(¤t
->Bcb
->BcbLock
, &oldIrql
);
337 /* Dereference the cache segment */
338 CcRosCacheSegmentDecRefCount(current
);
340 /* Check if we can free this entry now */
341 if (current
->ReferenceCount
== 0)
343 ASSERT(!current
->Dirty
);
344 ASSERT(!current
->MappedCount
);
346 RemoveEntryList(¤t
->BcbSegmentListEntry
);
347 RemoveEntryList(¤t
->CacheSegmentListEntry
);
348 RemoveEntryList(¤t
->CacheSegmentLRUListEntry
);
349 InsertHeadList(&FreeList
, ¤t
->BcbSegmentListEntry
);
351 /* Calculate how many pages we freed for Mm */
352 PagesPerSegment
= current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
353 PagesFreed
= min(PagesPerSegment
, Target
);
354 Target
-= PagesFreed
;
355 (*NrFreed
) += PagesFreed
;
358 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
361 KeReleaseGuardedMutex(&ViewLock
);
363 /* Try flushing pages if we haven't met our target */
364 if (Target
> 0 && !FlushedPages
)
366 /* Flush dirty pages to disk */
367 CcRosFlushDirtyPages(Target
, &PagesFreed
, FALSE
);
370 /* We can only swap as many pages as we flushed */
371 if (PagesFreed
< Target
) Target
= PagesFreed
;
373 /* Check if we flushed anything */
376 /* Try again after flushing dirty pages */
377 DPRINT("Flushed %d dirty cache pages to disk\n", PagesFreed
);
382 while (!IsListEmpty(&FreeList
))
384 current_entry
= RemoveHeadList(&FreeList
);
385 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
386 BcbSegmentListEntry
);
387 CcRosInternalFreeCacheSegment(current
);
390 DPRINT("Evicted %d cache pages\n", (*NrFreed
));
392 return(STATUS_SUCCESS
);
397 CcRosReleaseCacheSegment(PBCB Bcb
,
398 PCACHE_SEGMENT CacheSeg
,
408 DPRINT("CcReleaseCacheSegment(Bcb 0x%p, CacheSeg 0x%p, Valid %d)\n",
409 Bcb
, CacheSeg
, Valid
);
411 KeAcquireGuardedMutex(&ViewLock
);
412 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
414 CacheSeg
->Valid
= Valid
;
416 WasDirty
= CacheSeg
->Dirty
;
417 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| Dirty
;
419 if (!WasDirty
&& CacheSeg
->Dirty
)
421 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
422 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
427 CacheSeg
->MappedCount
++;
429 CcRosCacheSegmentDecRefCount(CacheSeg
);
430 if (Mapped
&& CacheSeg
->MappedCount
== 1)
432 CcRosCacheSegmentIncRefCount(CacheSeg
);
434 if (!WasDirty
&& CacheSeg
->Dirty
)
436 CcRosCacheSegmentIncRefCount(CacheSeg
);
439 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
440 KeReleaseGuardedMutex(&ViewLock
);
441 KeReleaseMutex(&CacheSeg
->Mutex
, 0);
443 return(STATUS_SUCCESS
);
446 /* Returns with Cache Segment Lock Held! */
449 CcRosLookupCacheSegment(PBCB Bcb
, ULONG FileOffset
)
451 PLIST_ENTRY current_entry
;
452 PCACHE_SEGMENT current
;
457 DPRINT("CcRosLookupCacheSegment(Bcb -x%p, FileOffset %d)\n", Bcb
, FileOffset
);
459 KeAcquireGuardedMutex(&ViewLock
);
460 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
462 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
463 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
465 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
466 BcbSegmentListEntry
);
467 if (current
->FileOffset
<= FileOffset
&&
468 (current
->FileOffset
+ Bcb
->CacheSegmentSize
) > FileOffset
)
470 CcRosCacheSegmentIncRefCount(current
);
471 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
472 KeReleaseGuardedMutex(&ViewLock
);
473 KeWaitForSingleObject(¤t
->Mutex
,
480 current_entry
= current_entry
->Flink
;
483 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
484 KeReleaseGuardedMutex(&ViewLock
);
491 CcRosMarkDirtyCacheSegment(PBCB Bcb
, ULONG FileOffset
)
493 PCACHE_SEGMENT CacheSeg
;
498 DPRINT("CcRosMarkDirtyCacheSegment(Bcb 0x%p, FileOffset %d)\n", Bcb
, FileOffset
);
500 CacheSeg
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
501 if (CacheSeg
== NULL
)
503 KeBugCheck(CACHE_MANAGER
);
506 KeAcquireGuardedMutex(&ViewLock
);
507 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
509 if (!CacheSeg
->Dirty
)
511 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
512 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
516 CcRosCacheSegmentDecRefCount(CacheSeg
);
519 /* Move to the tail of the LRU list */
520 RemoveEntryList(&CacheSeg
->CacheSegmentLRUListEntry
);
521 InsertTailList(&CacheSegmentLRUListHead
, &CacheSeg
->CacheSegmentLRUListEntry
);
523 CacheSeg
->Dirty
= TRUE
;
525 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
526 KeReleaseGuardedMutex(&ViewLock
);
527 KeReleaseMutex(&CacheSeg
->Mutex
, 0);
529 return(STATUS_SUCCESS
);
534 CcRosUnmapCacheSegment(PBCB Bcb
, ULONG FileOffset
, BOOLEAN NowDirty
)
536 PCACHE_SEGMENT CacheSeg
;
542 DPRINT("CcRosUnmapCacheSegment(Bcb 0x%p, FileOffset %d, NowDirty %d)\n",
543 Bcb
, FileOffset
, NowDirty
);
545 CacheSeg
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
546 if (CacheSeg
== NULL
)
548 return(STATUS_UNSUCCESSFUL
);
551 KeAcquireGuardedMutex(&ViewLock
);
552 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
554 WasDirty
= CacheSeg
->Dirty
;
555 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| NowDirty
;
557 CacheSeg
->MappedCount
--;
559 if (!WasDirty
&& NowDirty
)
561 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
562 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
565 CcRosCacheSegmentDecRefCount(CacheSeg
);
566 if (!WasDirty
&& NowDirty
)
568 CcRosCacheSegmentIncRefCount(CacheSeg
);
570 if (CacheSeg
->MappedCount
== 0)
572 CcRosCacheSegmentDecRefCount(CacheSeg
);
575 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
576 KeReleaseGuardedMutex(&ViewLock
);
577 KeReleaseMutex(&CacheSeg
->Mutex
, 0);
579 return(STATUS_SUCCESS
);
584 CcRosCreateCacheSegment(PBCB Bcb
,
586 PCACHE_SEGMENT
* CacheSeg
)
588 PCACHE_SEGMENT current
;
589 PCACHE_SEGMENT previous
;
590 PLIST_ENTRY current_entry
;
594 ULONG StartingOffset
;
596 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
600 DPRINT("CcRosCreateCacheSegment()\n");
602 BoundaryAddressMultiple
.QuadPart
= 0;
603 if (FileOffset
>= Bcb
->FileSize
.u
.LowPart
)
606 return STATUS_INVALID_PARAMETER
;
609 current
= ExAllocateFromNPagedLookasideList(&CacheSegLookasideList
);
610 current
->Valid
= FALSE
;
611 current
->Dirty
= FALSE
;
612 current
->PageOut
= FALSE
;
613 current
->FileOffset
= ROUND_DOWN(FileOffset
, Bcb
->CacheSegmentSize
);
618 DPRINT1("CacheMap 0x%p: new Cache Segment: 0x%p\n", Bcb
, current
);
621 current
->MappedCount
= 0;
622 current
->DirtySegmentListEntry
.Flink
= NULL
;
623 current
->DirtySegmentListEntry
.Blink
= NULL
;
624 current
->ReferenceCount
= 1;
625 KeInitializeMutex(¤t
->Mutex
, 0);
626 KeWaitForSingleObject(¤t
->Mutex
,
631 KeAcquireGuardedMutex(&ViewLock
);
634 /* There is window between the call to CcRosLookupCacheSegment
635 * and CcRosCreateCacheSegment. We must check if a segment on
636 * the fileoffset exist. If there exist a segment, we release
637 * our new created segment and return the existing one.
639 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
640 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
642 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
644 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
645 BcbSegmentListEntry
);
646 if (current
->FileOffset
<= FileOffset
&&
647 (current
->FileOffset
+ Bcb
->CacheSegmentSize
) > FileOffset
)
649 CcRosCacheSegmentIncRefCount(current
);
650 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
654 DPRINT1("CacheMap 0x%p: deleting newly created Cache Segment 0x%p ( found existing one 0x%p )\n",
660 KeReleaseMutex(&(*CacheSeg
)->Mutex
, 0);
661 KeReleaseGuardedMutex(&ViewLock
);
662 ExFreeToNPagedLookasideList(&CacheSegLookasideList
, *CacheSeg
);
664 KeWaitForSingleObject(¤t
->Mutex
,
669 return STATUS_SUCCESS
;
671 if (current
->FileOffset
< FileOffset
)
673 if (previous
== NULL
)
679 if (previous
->FileOffset
< current
->FileOffset
)
685 current_entry
= current_entry
->Flink
;
687 /* There was no existing segment. */
691 InsertHeadList(&previous
->BcbSegmentListEntry
, ¤t
->BcbSegmentListEntry
);
695 InsertHeadList(&Bcb
->BcbSegmentListHead
, ¤t
->BcbSegmentListEntry
);
697 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
698 InsertTailList(&CacheSegmentListHead
, ¤t
->CacheSegmentListEntry
);
699 InsertTailList(&CacheSegmentLRUListHead
, ¤t
->CacheSegmentLRUListEntry
);
700 KeReleaseGuardedMutex(&ViewLock
);
702 KeAcquireSpinLock(&CiCacheSegMappingRegionLock
, &oldIrql
);
704 StartingOffset
= RtlFindClearBitsAndSet(&CiCacheSegMappingRegionAllocMap
, Bcb
->CacheSegmentSize
/ PAGE_SIZE
, CiCacheSegMappingRegionHint
);
706 if (StartingOffset
== 0xffffffff)
708 DPRINT1("Out of CacheSeg mapping space\n");
709 KeBugCheck(CACHE_MANAGER
);
712 current
->BaseAddress
= CiCacheSegMappingRegionBase
+ StartingOffset
* PAGE_SIZE
;
714 if (CiCacheSegMappingRegionHint
== StartingOffset
)
716 CiCacheSegMappingRegionHint
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
719 KeReleaseSpinLock(&CiCacheSegMappingRegionLock
, oldIrql
);
721 MmLockAddressSpace(MmGetKernelAddressSpace());
722 current
->BaseAddress
= NULL
;
723 Status
= MmCreateMemoryArea(MmGetKernelAddressSpace(),
724 0, // nothing checks for cache_segment mareas, so set to 0
725 ¤t
->BaseAddress
,
726 Bcb
->CacheSegmentSize
,
728 (PMEMORY_AREA
*)¤t
->MemoryArea
,
731 BoundaryAddressMultiple
);
732 MmUnlockAddressSpace(MmGetKernelAddressSpace());
733 if (!NT_SUCCESS(Status
))
735 KeBugCheck(CACHE_MANAGER
);
739 /* Create a virtual mapping for this memory area */
740 MI_SET_USAGE(MI_USAGE_CACHE
);
744 if ((Bcb
->FileObject
) && (Bcb
->FileObject
->FileName
.Buffer
))
746 pos
= wcsrchr(Bcb
->FileObject
->FileName
.Buffer
, '\\');
747 len
= wcslen(pos
) * sizeof(WCHAR
);
748 if (pos
) snprintf(MI_PFN_CURRENT_PROCESS_NAME
, min(16, len
), "%S", pos
);
752 MmMapMemoryArea(current
->BaseAddress
, Bcb
->CacheSegmentSize
,
753 MC_CACHE
, PAGE_READWRITE
);
755 return(STATUS_SUCCESS
);
760 CcRosGetCacheSegmentChain(PBCB Bcb
,
763 PCACHE_SEGMENT
* CacheSeg
)
765 PCACHE_SEGMENT current
;
767 PCACHE_SEGMENT
* CacheSegList
;
768 PCACHE_SEGMENT Previous
= NULL
;
772 DPRINT("CcRosGetCacheSegmentChain()\n");
774 Length
= ROUND_UP(Length
, Bcb
->CacheSegmentSize
);
776 CacheSegList
= _alloca(sizeof(PCACHE_SEGMENT
) *
777 (Length
/ Bcb
->CacheSegmentSize
));
780 * Look for a cache segment already mapping the same data.
782 for (i
= 0; i
< (Length
/ Bcb
->CacheSegmentSize
); i
++)
784 ULONG CurrentOffset
= FileOffset
+ (i
* Bcb
->CacheSegmentSize
);
785 current
= CcRosLookupCacheSegment(Bcb
, CurrentOffset
);
788 KeAcquireGuardedMutex(&ViewLock
);
790 /* Move to tail of LRU list */
791 RemoveEntryList(¤t
->CacheSegmentLRUListEntry
);
792 InsertTailList(&CacheSegmentLRUListHead
, ¤t
->CacheSegmentLRUListEntry
);
794 KeReleaseGuardedMutex(&ViewLock
);
796 CacheSegList
[i
] = current
;
800 CcRosCreateCacheSegment(Bcb
, CurrentOffset
, ¤t
);
801 CacheSegList
[i
] = current
;
805 for (i
= 0; i
< (Length
/ Bcb
->CacheSegmentSize
); i
++)
809 *CacheSeg
= CacheSegList
[i
];
810 Previous
= CacheSegList
[i
];
814 Previous
->NextInChain
= CacheSegList
[i
];
815 Previous
= CacheSegList
[i
];
819 Previous
->NextInChain
= NULL
;
821 return(STATUS_SUCCESS
);
826 CcRosGetCacheSegment(PBCB Bcb
,
831 PCACHE_SEGMENT
* CacheSeg
)
833 PCACHE_SEGMENT current
;
838 DPRINT("CcRosGetCacheSegment()\n");
841 * Look for a cache segment already mapping the same data.
843 current
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
847 * Otherwise create a new segment.
849 Status
= CcRosCreateCacheSegment(Bcb
, FileOffset
, ¤t
);
850 if (!NT_SUCCESS(Status
))
856 KeAcquireGuardedMutex(&ViewLock
);
858 /* Move to the tail of the LRU list */
859 RemoveEntryList(¤t
->CacheSegmentLRUListEntry
);
860 InsertTailList(&CacheSegmentLRUListHead
, ¤t
->CacheSegmentLRUListEntry
);
862 KeReleaseGuardedMutex(&ViewLock
);
865 * Return information about the segment to the caller.
867 *UptoDate
= current
->Valid
;
868 *BaseAddress
= current
->BaseAddress
;
869 DPRINT("*BaseAddress %p\n", *BaseAddress
);
871 *BaseOffset
= current
->FileOffset
;
872 return(STATUS_SUCCESS
);
876 CcRosRequestCacheSegment(PBCB Bcb
,
880 PCACHE_SEGMENT
* CacheSeg
)
882 * FUNCTION: Request a page mapping for a BCB
889 if ((FileOffset
% Bcb
->CacheSegmentSize
) != 0)
891 DPRINT1("Bad fileoffset %x should be multiple of %x",
892 FileOffset
, Bcb
->CacheSegmentSize
);
893 KeBugCheck(CACHE_MANAGER
);
896 return(CcRosGetCacheSegment(Bcb
,
906 CcFreeCachePage(PVOID Context
, MEMORY_AREA
* MemoryArea
, PVOID Address
,
907 PFN_NUMBER Page
, SWAPENTRY SwapEntry
, BOOLEAN Dirty
)
909 ASSERT(SwapEntry
== 0);
912 ASSERT(MmGetReferenceCountPage(Page
) == 1);
913 MmReleasePageMemoryConsumer(MC_CACHE
, Page
);
918 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg
)
920 * FUNCTION: Releases a cache segment associated with a BCB
930 DPRINT("Freeing cache segment 0x%p\n", CacheSeg
);
932 if ( CacheSeg
->Bcb
->Trace
)
934 DPRINT1("CacheMap 0x%p: deleting Cache Segment: 0x%p\n", CacheSeg
->Bcb
, CacheSeg
);
938 RegionSize
= CacheSeg
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
940 /* Unmap all the pages. */
941 for (i
= 0; i
< RegionSize
; i
++)
943 MmDeleteVirtualMapping(NULL
,
944 CacheSeg
->BaseAddress
+ (i
* PAGE_SIZE
),
948 MmReleasePageMemoryConsumer(MC_CACHE
, Page
);
951 KeAcquireSpinLock(&CiCacheSegMappingRegionLock
, &oldIrql
);
952 /* Deallocate all the pages used. */
953 Base
= (ULONG
)(CacheSeg
->BaseAddress
- CiCacheSegMappingRegionBase
) / PAGE_SIZE
;
955 RtlClearBits(&CiCacheSegMappingRegionAllocMap
, Base
, RegionSize
);
957 CiCacheSegMappingRegionHint
= min (CiCacheSegMappingRegionHint
, Base
);
959 KeReleaseSpinLock(&CiCacheSegMappingRegionLock
, oldIrql
);
961 MmLockAddressSpace(MmGetKernelAddressSpace());
962 MmFreeMemoryArea(MmGetKernelAddressSpace(),
963 CacheSeg
->MemoryArea
,
966 MmUnlockAddressSpace(MmGetKernelAddressSpace());
968 ExFreeToNPagedLookasideList(&CacheSegLookasideList
, CacheSeg
);
969 return(STATUS_SUCCESS
);
974 CcRosFreeCacheSegment(PBCB Bcb
, PCACHE_SEGMENT CacheSeg
)
981 DPRINT("CcRosFreeCacheSegment(Bcb 0x%p, CacheSeg 0x%p)\n",
984 KeAcquireGuardedMutex(&ViewLock
);
985 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
986 RemoveEntryList(&CacheSeg
->BcbSegmentListEntry
);
987 RemoveEntryList(&CacheSeg
->CacheSegmentListEntry
);
988 RemoveEntryList(&CacheSeg
->CacheSegmentLRUListEntry
);
991 RemoveEntryList(&CacheSeg
->DirtySegmentListEntry
);
992 DirtyPageCount
-= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
995 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
996 KeReleaseGuardedMutex(&ViewLock
);
998 Status
= CcRosInternalFreeCacheSegment(CacheSeg
);
1006 CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointers
,
1007 IN PLARGE_INTEGER FileOffset OPTIONAL
,
1009 OUT PIO_STATUS_BLOCK IoStatus
)
1012 LARGE_INTEGER Offset
;
1013 PCACHE_SEGMENT current
;
1017 DPRINT("CcFlushCache(SectionObjectPointers 0x%p, FileOffset 0x%p, Length %d, IoStatus 0x%p)\n",
1018 SectionObjectPointers
, FileOffset
, Length
, IoStatus
);
1020 if (SectionObjectPointers
&& SectionObjectPointers
->SharedCacheMap
)
1022 Bcb
= (PBCB
)SectionObjectPointers
->SharedCacheMap
;
1026 Offset
= *FileOffset
;
1030 Offset
.QuadPart
= (LONGLONG
)0;
1031 Length
= Bcb
->FileSize
.u
.LowPart
;
1036 IoStatus
->Status
= STATUS_SUCCESS
;
1037 IoStatus
->Information
= 0;
1042 current
= CcRosLookupCacheSegment (Bcb
, Offset
.u
.LowPart
);
1043 if (current
!= NULL
)
1047 Status
= CcRosFlushCacheSegment(current
);
1048 if (!NT_SUCCESS(Status
) && IoStatus
!= NULL
)
1050 IoStatus
->Status
= Status
;
1053 KeReleaseMutex(¤t
->Mutex
, 0);
1055 KeAcquireGuardedMutex(&ViewLock
);
1056 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
1057 CcRosCacheSegmentDecRefCount(current
);
1058 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
1059 KeReleaseGuardedMutex(&ViewLock
);
1062 Offset
.QuadPart
+= Bcb
->CacheSegmentSize
;
1063 if (Length
> Bcb
->CacheSegmentSize
)
1065 Length
-= Bcb
->CacheSegmentSize
;
1077 IoStatus
->Status
= STATUS_INVALID_PARAMETER
;
1084 CcRosDeleteFileCache(PFILE_OBJECT FileObject
, PBCB Bcb
)
1086 * FUNCTION: Releases the BCB associated with a file object
1089 PLIST_ENTRY current_entry
;
1090 PCACHE_SEGMENT current
;
1091 LIST_ENTRY FreeList
;
1097 KeReleaseGuardedMutex(&ViewLock
);
1099 CcFlushCache(FileObject
->SectionObjectPointer
, NULL
, 0, NULL
);
1101 KeAcquireGuardedMutex(&ViewLock
);
1103 if (Bcb
->RefCount
== 0)
1105 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
1107 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1108 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1111 FileObject
->SectionObjectPointer
->SharedCacheMap
= NULL
;
1114 * Release all cache segments.
1116 InitializeListHead(&FreeList
);
1117 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
1118 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
1119 while (!IsListEmpty(&Bcb
->BcbSegmentListHead
))
1121 current_entry
= RemoveTailList(&Bcb
->BcbSegmentListHead
);
1122 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, BcbSegmentListEntry
);
1123 RemoveEntryList(¤t
->CacheSegmentListEntry
);
1124 RemoveEntryList(¤t
->CacheSegmentLRUListEntry
);
1127 RemoveEntryList(¤t
->DirtySegmentListEntry
);
1128 DirtyPageCount
-= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
1129 DPRINT1("Freeing dirty segment\n");
1131 InsertHeadList(&FreeList
, ¤t
->BcbSegmentListEntry
);
1136 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
1138 KeReleaseGuardedMutex(&ViewLock
);
1139 ObDereferenceObject (Bcb
->FileObject
);
1141 while (!IsListEmpty(&FreeList
))
1143 current_entry
= RemoveTailList(&FreeList
);
1144 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, BcbSegmentListEntry
);
1145 CcRosInternalFreeCacheSegment(current
);
1147 ExFreeToNPagedLookasideList(&BcbLookasideList
, Bcb
);
1148 KeAcquireGuardedMutex(&ViewLock
);
1150 return(STATUS_SUCCESS
);
1155 CcRosReferenceCache(PFILE_OBJECT FileObject
)
1158 KeAcquireGuardedMutex(&ViewLock
);
1159 Bcb
= (PBCB
)FileObject
->SectionObjectPointer
->SharedCacheMap
;
1161 if (Bcb
->RefCount
== 0)
1163 ASSERT(Bcb
->BcbRemoveListEntry
.Flink
!= NULL
);
1164 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1165 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1170 ASSERT(Bcb
->BcbRemoveListEntry
.Flink
== NULL
);
1173 KeReleaseGuardedMutex(&ViewLock
);
1178 CcRosSetRemoveOnClose(PSECTION_OBJECT_POINTERS SectionObjectPointer
)
1181 DPRINT("CcRosSetRemoveOnClose()\n");
1182 KeAcquireGuardedMutex(&ViewLock
);
1183 Bcb
= (PBCB
)SectionObjectPointer
->SharedCacheMap
;
1186 Bcb
->RemoveOnClose
= TRUE
;
1187 if (Bcb
->RefCount
== 0)
1189 CcRosDeleteFileCache(Bcb
->FileObject
, Bcb
);
1192 KeReleaseGuardedMutex(&ViewLock
);
1198 CcRosDereferenceCache(PFILE_OBJECT FileObject
)
1201 KeAcquireGuardedMutex(&ViewLock
);
1202 Bcb
= (PBCB
)FileObject
->SectionObjectPointer
->SharedCacheMap
;
1204 if (Bcb
->RefCount
> 0)
1207 if (Bcb
->RefCount
== 0)
1209 MmFreeSectionSegments(Bcb
->FileObject
);
1210 CcRosDeleteFileCache(FileObject
, Bcb
);
1213 KeReleaseGuardedMutex(&ViewLock
);
1217 CcRosReleaseFileCache(PFILE_OBJECT FileObject
)
1219 * FUNCTION: Called by the file system when a handle to a file object
1225 KeAcquireGuardedMutex(&ViewLock
);
1227 if (FileObject
->SectionObjectPointer
->SharedCacheMap
!= NULL
)
1229 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1230 if (FileObject
->PrivateCacheMap
!= NULL
)
1232 FileObject
->PrivateCacheMap
= NULL
;
1233 if (Bcb
->RefCount
> 0)
1236 if (Bcb
->RefCount
== 0)
1238 MmFreeSectionSegments(Bcb
->FileObject
);
1239 CcRosDeleteFileCache(FileObject
, Bcb
);
1244 KeReleaseGuardedMutex(&ViewLock
);
1245 return(STATUS_SUCCESS
);
1250 CcTryToInitializeFileCache(PFILE_OBJECT FileObject
)
1255 KeAcquireGuardedMutex(&ViewLock
);
1257 ASSERT(FileObject
->SectionObjectPointer
);
1258 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1261 Status
= STATUS_UNSUCCESSFUL
;
1265 if (FileObject
->PrivateCacheMap
== NULL
)
1267 FileObject
->PrivateCacheMap
= Bcb
;
1270 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
1272 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1273 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1275 Status
= STATUS_SUCCESS
;
1277 KeReleaseGuardedMutex(&ViewLock
);
1284 CcRosInitializeFileCache(PFILE_OBJECT FileObject
,
1285 ULONG CacheSegmentSize
,
1286 PCACHE_MANAGER_CALLBACKS CallBacks
,
1287 PVOID LazyWriterContext
)
1289 * FUNCTION: Initializes a BCB for a file object
1294 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1295 DPRINT("CcRosInitializeFileCache(FileObject 0x%p, Bcb 0x%p, CacheSegmentSize %d)\n",
1296 FileObject
, Bcb
, CacheSegmentSize
);
1298 KeAcquireGuardedMutex(&ViewLock
);
1301 Bcb
= ExAllocateFromNPagedLookasideList(&BcbLookasideList
);
1304 KeReleaseGuardedMutex(&ViewLock
);
1305 return(STATUS_UNSUCCESSFUL
);
1307 memset(Bcb
, 0, sizeof(BCB
));
1308 ObReferenceObjectByPointer(FileObject
,
1312 Bcb
->FileObject
= FileObject
;
1313 Bcb
->CacheSegmentSize
= CacheSegmentSize
;
1314 Bcb
->Callbacks
= CallBacks
;
1315 Bcb
->LazyWriteContext
= LazyWriterContext
;
1316 if (FileObject
->FsContext
)
1318 Bcb
->AllocationSize
=
1319 ((PFSRTL_COMMON_FCB_HEADER
)FileObject
->FsContext
)->AllocationSize
;
1321 ((PFSRTL_COMMON_FCB_HEADER
)FileObject
->FsContext
)->FileSize
;
1323 KeInitializeSpinLock(&Bcb
->BcbLock
);
1324 InitializeListHead(&Bcb
->BcbSegmentListHead
);
1325 FileObject
->SectionObjectPointer
->SharedCacheMap
= Bcb
;
1327 if (FileObject
->PrivateCacheMap
== NULL
)
1329 FileObject
->PrivateCacheMap
= Bcb
;
1332 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
1334 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1335 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1337 KeReleaseGuardedMutex(&ViewLock
);
1339 return(STATUS_SUCCESS
);
1346 CcGetFileObjectFromSectionPtrs(IN PSECTION_OBJECT_POINTERS SectionObjectPointers
)
1349 if (SectionObjectPointers
&& SectionObjectPointers
->SharedCacheMap
)
1351 Bcb
= (PBCB
)SectionObjectPointers
->SharedCacheMap
;
1353 return Bcb
->FileObject
;
1366 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
1369 DPRINT("CcInitView()\n");
1371 BoundaryAddressMultiple
.QuadPart
= 0;
1372 CiCacheSegMappingRegionHint
= 0;
1373 CiCacheSegMappingRegionBase
= NULL
;
1375 MmLockAddressSpace(MmGetKernelAddressSpace());
1377 Status
= MmCreateMemoryArea(MmGetKernelAddressSpace(),
1378 MEMORY_AREA_CACHE_SEGMENT
,
1379 &CiCacheSegMappingRegionBase
,
1380 CI_CACHESEG_MAPPING_REGION_SIZE
,
1385 BoundaryAddressMultiple
);
1386 MmUnlockAddressSpace(MmGetKernelAddressSpace());
1387 if (!NT_SUCCESS(Status
))
1389 KeBugCheck(CACHE_MANAGER
);
1392 Buffer
= ExAllocatePool(NonPagedPool
, CI_CACHESEG_MAPPING_REGION_SIZE
/ (PAGE_SIZE
* 8));
1395 KeBugCheck(CACHE_MANAGER
);
1398 RtlInitializeBitMap(&CiCacheSegMappingRegionAllocMap
, Buffer
, CI_CACHESEG_MAPPING_REGION_SIZE
/ PAGE_SIZE
);
1399 RtlClearAllBits(&CiCacheSegMappingRegionAllocMap
);
1401 KeInitializeSpinLock(&CiCacheSegMappingRegionLock
);
1403 InitializeListHead(&CacheSegmentListHead
);
1404 InitializeListHead(&DirtySegmentListHead
);
1405 InitializeListHead(&CacheSegmentLRUListHead
);
1406 InitializeListHead(&ClosedListHead
);
1407 KeInitializeGuardedMutex(&ViewLock
);
1408 ExInitializeNPagedLookasideList (&iBcbLookasideList
,
1412 sizeof(INTERNAL_BCB
),
1415 ExInitializeNPagedLookasideList (&BcbLookasideList
,
1422 ExInitializeNPagedLookasideList (&CacheSegLookasideList
,
1426 sizeof(CACHE_SEGMENT
),
1430 MmInitializeMemoryConsumer(MC_CACHE
, CcRosTrimCache
);
1432 CcInitCacheZeroPage();