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 Locked
= current
->Bcb
->Callbacks
->AcquireForLazyWrite(
208 current
->Bcb
->LazyWriteContext
, Wait
);
214 Status
= KeWaitForSingleObject(¤t
->Mutex
,
218 Wait
? NULL
: &ZeroTimeout
);
219 if (Status
!= STATUS_SUCCESS
)
221 current
->Bcb
->Callbacks
->ReleaseFromLazyWrite(
222 current
->Bcb
->LazyWriteContext
);
226 ASSERT(current
->Dirty
);
227 if (current
->ReferenceCount
> 1)
229 KeReleaseMutex(¤t
->Mutex
, 0);
230 current
->Bcb
->Callbacks
->ReleaseFromLazyWrite(
231 current
->Bcb
->LazyWriteContext
);
235 PagesPerSegment
= current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
237 KeReleaseGuardedMutex(&ViewLock
);
239 Status
= CcRosFlushCacheSegment(current
);
241 KeReleaseMutex(¤t
->Mutex
, 0);
242 current
->Bcb
->Callbacks
->ReleaseFromLazyWrite(
243 current
->Bcb
->LazyWriteContext
);
245 if (!NT_SUCCESS(Status
) && (Status
!= STATUS_END_OF_FILE
))
247 DPRINT1("CC: Failed to flush cache segment.\n");
251 (*Count
) += PagesPerSegment
;
252 Target
-= PagesPerSegment
;
255 KeAcquireGuardedMutex(&ViewLock
);
256 current_entry
= DirtySegmentListHead
.Flink
;
259 KeReleaseGuardedMutex(&ViewLock
);
260 KeLeaveCriticalRegion();
262 DPRINT("CcRosFlushDirtyPages() finished\n");
263 return(STATUS_SUCCESS
);
267 CcRosTrimCache(ULONG Target
, ULONG Priority
, PULONG NrFreed
)
269 * FUNCTION: Try to free some memory from the file cache.
271 * Target - The number of pages to be freed.
272 * Priority - The priority of free (currently unused).
273 * NrFreed - Points to a variable where the number of pages
274 * actually freed is returned.
277 PLIST_ENTRY current_entry
;
278 PCACHE_SEGMENT current
;
279 ULONG PagesPerSegment
;
285 BOOLEAN FlushedPages
= FALSE
;
287 DPRINT("CcRosTrimCache(Target %d)\n", Target
);
289 InitializeListHead(&FreeList
);
294 KeAcquireGuardedMutex(&ViewLock
);
296 current_entry
= CacheSegmentLRUListHead
.Flink
;
297 while (current_entry
!= &CacheSegmentLRUListHead
)
299 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
300 CacheSegmentLRUListEntry
);
301 current_entry
= current_entry
->Flink
;
303 KeAcquireSpinLock(¤t
->Bcb
->BcbLock
, &oldIrql
);
305 /* Reference the cache segment */
306 CcRosCacheSegmentIncRefCount(current
);
308 /* Check if it's mapped and not dirty */
309 if (current
->MappedCount
> 0 && !current
->Dirty
)
311 /* We have to break these locks because Cc sucks */
312 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
313 KeReleaseGuardedMutex(&ViewLock
);
315 /* Page out the segment */
316 for (i
= 0; i
< current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
; i
++)
318 Page
= (PFN_NUMBER
)(MmGetPhysicalAddress((PUCHAR
)current
->BaseAddress
+ (i
* PAGE_SIZE
)).QuadPart
>> PAGE_SHIFT
);
320 MmPageOutPhysicalAddress(Page
);
323 /* Reacquire the locks */
324 KeAcquireGuardedMutex(&ViewLock
);
325 KeAcquireSpinLock(¤t
->Bcb
->BcbLock
, &oldIrql
);
328 /* Dereference the cache segment */
329 CcRosCacheSegmentDecRefCount(current
);
331 /* Check if we can free this entry now */
332 if (current
->ReferenceCount
== 0)
334 ASSERT(!current
->Dirty
);
335 ASSERT(!current
->MappedCount
);
337 RemoveEntryList(¤t
->BcbSegmentListEntry
);
338 RemoveEntryList(¤t
->CacheSegmentListEntry
);
339 RemoveEntryList(¤t
->CacheSegmentLRUListEntry
);
340 InsertHeadList(&FreeList
, ¤t
->BcbSegmentListEntry
);
342 /* Calculate how many pages we freed for Mm */
343 PagesPerSegment
= current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
344 PagesFreed
= min(PagesPerSegment
, Target
);
345 Target
-= PagesFreed
;
346 (*NrFreed
) += PagesFreed
;
349 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
352 KeReleaseGuardedMutex(&ViewLock
);
354 /* Try flushing pages if we haven't met our target */
355 if (Target
> 0 && !FlushedPages
)
357 /* Flush dirty pages to disk */
358 CcRosFlushDirtyPages(Target
, &PagesFreed
, FALSE
);
361 /* We can only swap as many pages as we flushed */
362 if (PagesFreed
< Target
) Target
= PagesFreed
;
364 /* Check if we flushed anything */
367 /* Try again after flushing dirty pages */
368 DPRINT("Flushed %d dirty cache pages to disk\n", PagesFreed
);
373 while (!IsListEmpty(&FreeList
))
375 current_entry
= RemoveHeadList(&FreeList
);
376 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
377 BcbSegmentListEntry
);
378 CcRosInternalFreeCacheSegment(current
);
381 DPRINT("Evicted %d cache pages\n", (*NrFreed
));
383 return(STATUS_SUCCESS
);
388 CcRosReleaseCacheSegment(PBCB Bcb
,
389 PCACHE_SEGMENT CacheSeg
,
394 BOOLEAN WasDirty
= CacheSeg
->Dirty
;
399 DPRINT("CcReleaseCacheSegment(Bcb 0x%p, CacheSeg 0x%p, Valid %d)\n",
400 Bcb
, CacheSeg
, Valid
);
402 CacheSeg
->Valid
= Valid
;
403 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| Dirty
;
405 KeAcquireGuardedMutex(&ViewLock
);
406 if (!WasDirty
&& CacheSeg
->Dirty
)
408 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
409 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
414 CacheSeg
->MappedCount
++;
416 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
417 CcRosCacheSegmentDecRefCount(CacheSeg
);
418 if (Mapped
&& CacheSeg
->MappedCount
== 1)
420 CcRosCacheSegmentIncRefCount(CacheSeg
);
422 if (!WasDirty
&& CacheSeg
->Dirty
)
424 CcRosCacheSegmentIncRefCount(CacheSeg
);
426 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
427 KeReleaseGuardedMutex(&ViewLock
);
428 KeReleaseMutex(&CacheSeg
->Mutex
, 0);
430 return(STATUS_SUCCESS
);
433 /* Returns with Cache Segment Lock Held! */
436 CcRosLookupCacheSegment(PBCB Bcb
, ULONG FileOffset
)
438 PLIST_ENTRY current_entry
;
439 PCACHE_SEGMENT current
;
444 DPRINT("CcRosLookupCacheSegment(Bcb -x%p, FileOffset %d)\n", Bcb
, FileOffset
);
446 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
447 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
448 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
450 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
451 BcbSegmentListEntry
);
452 if (current
->FileOffset
<= FileOffset
&&
453 (current
->FileOffset
+ Bcb
->CacheSegmentSize
) > FileOffset
)
455 CcRosCacheSegmentIncRefCount(current
);
456 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
457 KeWaitForSingleObject(¤t
->Mutex
,
464 current_entry
= current_entry
->Flink
;
466 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
472 CcRosMarkDirtyCacheSegment(PBCB Bcb
, ULONG FileOffset
)
474 PCACHE_SEGMENT CacheSeg
;
479 DPRINT("CcRosMarkDirtyCacheSegment(Bcb 0x%p, FileOffset %d)\n", Bcb
, FileOffset
);
481 CacheSeg
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
482 if (CacheSeg
== NULL
)
484 KeBugCheck(CACHE_MANAGER
);
486 if (!CacheSeg
->Dirty
)
488 KeAcquireGuardedMutex(&ViewLock
);
489 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
490 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
491 KeReleaseGuardedMutex(&ViewLock
);
495 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
496 CcRosCacheSegmentDecRefCount(CacheSeg
);
497 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
500 KeAcquireGuardedMutex(&ViewLock
);
502 /* Move to the tail of the LRU list */
503 RemoveEntryList(&CacheSeg
->CacheSegmentLRUListEntry
);
504 InsertTailList(&CacheSegmentLRUListHead
, &CacheSeg
->CacheSegmentLRUListEntry
);
506 KeReleaseGuardedMutex(&ViewLock
);
508 CacheSeg
->Dirty
= TRUE
;
509 KeReleaseMutex(&CacheSeg
->Mutex
, 0);
511 return(STATUS_SUCCESS
);
516 CcRosUnmapCacheSegment(PBCB Bcb
, ULONG FileOffset
, BOOLEAN NowDirty
)
518 PCACHE_SEGMENT CacheSeg
;
524 DPRINT("CcRosUnmapCacheSegment(Bcb 0x%p, FileOffset %d, NowDirty %d)\n",
525 Bcb
, FileOffset
, NowDirty
);
527 CacheSeg
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
528 if (CacheSeg
== NULL
)
530 return(STATUS_UNSUCCESSFUL
);
533 WasDirty
= CacheSeg
->Dirty
;
534 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| NowDirty
;
536 CacheSeg
->MappedCount
--;
538 if (!WasDirty
&& NowDirty
)
540 KeAcquireGuardedMutex(&ViewLock
);
541 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
542 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
543 KeReleaseGuardedMutex(&ViewLock
);
546 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
547 CcRosCacheSegmentDecRefCount(CacheSeg
);
548 if (!WasDirty
&& NowDirty
)
550 CcRosCacheSegmentIncRefCount(CacheSeg
);
552 if (CacheSeg
->MappedCount
== 0)
554 CcRosCacheSegmentDecRefCount(CacheSeg
);
556 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
558 KeReleaseMutex(&CacheSeg
->Mutex
, 0);
560 return(STATUS_SUCCESS
);
565 CcRosCreateCacheSegment(PBCB Bcb
,
567 PCACHE_SEGMENT
* CacheSeg
)
569 PCACHE_SEGMENT current
;
570 PCACHE_SEGMENT previous
;
571 PLIST_ENTRY current_entry
;
575 ULONG StartingOffset
;
577 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
581 DPRINT("CcRosCreateCacheSegment()\n");
583 BoundaryAddressMultiple
.QuadPart
= 0;
584 if (FileOffset
>= Bcb
->FileSize
.u
.LowPart
)
587 return STATUS_INVALID_PARAMETER
;
590 current
= ExAllocateFromNPagedLookasideList(&CacheSegLookasideList
);
591 current
->Valid
= FALSE
;
592 current
->Dirty
= FALSE
;
593 current
->PageOut
= FALSE
;
594 current
->FileOffset
= ROUND_DOWN(FileOffset
, Bcb
->CacheSegmentSize
);
599 DPRINT1("CacheMap 0x%p: new Cache Segment: 0x%p\n", Bcb
, current
);
602 current
->MappedCount
= 0;
603 current
->DirtySegmentListEntry
.Flink
= NULL
;
604 current
->DirtySegmentListEntry
.Blink
= NULL
;
605 current
->ReferenceCount
= 1;
606 KeInitializeMutex(¤t
->Mutex
, 0);
607 KeWaitForSingleObject(¤t
->Mutex
,
612 KeAcquireGuardedMutex(&ViewLock
);
615 /* There is window between the call to CcRosLookupCacheSegment
616 * and CcRosCreateCacheSegment. We must check if a segment on
617 * the fileoffset exist. If there exist a segment, we release
618 * our new created segment and return the existing one.
620 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
621 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
623 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
625 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
626 BcbSegmentListEntry
);
627 if (current
->FileOffset
<= FileOffset
&&
628 (current
->FileOffset
+ Bcb
->CacheSegmentSize
) > FileOffset
)
630 CcRosCacheSegmentIncRefCount(current
);
631 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
635 DPRINT1("CacheMap 0x%p: deleting newly created Cache Segment 0x%p ( found existing one 0x%p )\n",
641 KeReleaseMutex(&(*CacheSeg
)->Mutex
, 0);
642 KeReleaseGuardedMutex(&ViewLock
);
643 ExFreeToNPagedLookasideList(&CacheSegLookasideList
, *CacheSeg
);
645 KeWaitForSingleObject(¤t
->Mutex
,
650 return STATUS_SUCCESS
;
652 if (current
->FileOffset
< FileOffset
)
654 if (previous
== NULL
)
660 if (previous
->FileOffset
< current
->FileOffset
)
666 current_entry
= current_entry
->Flink
;
668 /* There was no existing segment. */
672 InsertHeadList(&previous
->BcbSegmentListEntry
, ¤t
->BcbSegmentListEntry
);
676 InsertHeadList(&Bcb
->BcbSegmentListHead
, ¤t
->BcbSegmentListEntry
);
678 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
679 InsertTailList(&CacheSegmentListHead
, ¤t
->CacheSegmentListEntry
);
680 InsertTailList(&CacheSegmentLRUListHead
, ¤t
->CacheSegmentLRUListEntry
);
681 KeReleaseGuardedMutex(&ViewLock
);
683 KeAcquireSpinLock(&CiCacheSegMappingRegionLock
, &oldIrql
);
685 StartingOffset
= RtlFindClearBitsAndSet(&CiCacheSegMappingRegionAllocMap
, Bcb
->CacheSegmentSize
/ PAGE_SIZE
, CiCacheSegMappingRegionHint
);
687 if (StartingOffset
== 0xffffffff)
689 DPRINT1("Out of CacheSeg mapping space\n");
690 KeBugCheck(CACHE_MANAGER
);
693 current
->BaseAddress
= CiCacheSegMappingRegionBase
+ StartingOffset
* PAGE_SIZE
;
695 if (CiCacheSegMappingRegionHint
== StartingOffset
)
697 CiCacheSegMappingRegionHint
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
700 KeReleaseSpinLock(&CiCacheSegMappingRegionLock
, oldIrql
);
702 MmLockAddressSpace(MmGetKernelAddressSpace());
703 current
->BaseAddress
= NULL
;
704 Status
= MmCreateMemoryArea(MmGetKernelAddressSpace(),
705 0, // nothing checks for cache_segment mareas, so set to 0
706 ¤t
->BaseAddress
,
707 Bcb
->CacheSegmentSize
,
709 (PMEMORY_AREA
*)¤t
->MemoryArea
,
712 BoundaryAddressMultiple
);
713 MmUnlockAddressSpace(MmGetKernelAddressSpace());
714 if (!NT_SUCCESS(Status
))
716 KeBugCheck(CACHE_MANAGER
);
720 /* Create a virtual mapping for this memory area */
721 MI_SET_USAGE(MI_USAGE_CACHE
);
725 if ((Bcb
->FileObject
) && (Bcb
->FileObject
->FileName
.Buffer
))
727 pos
= wcsrchr(Bcb
->FileObject
->FileName
.Buffer
, '\\');
728 len
= wcslen(pos
) * sizeof(WCHAR
);
729 if (pos
) snprintf(MI_PFN_CURRENT_PROCESS_NAME
, min(16, len
), "%S", pos
);
733 MmMapMemoryArea(current
->BaseAddress
, Bcb
->CacheSegmentSize
,
734 MC_CACHE
, PAGE_READWRITE
);
736 return(STATUS_SUCCESS
);
741 CcRosGetCacheSegmentChain(PBCB Bcb
,
744 PCACHE_SEGMENT
* CacheSeg
)
746 PCACHE_SEGMENT current
;
748 PCACHE_SEGMENT
* CacheSegList
;
749 PCACHE_SEGMENT Previous
= NULL
;
753 DPRINT("CcRosGetCacheSegmentChain()\n");
755 Length
= ROUND_UP(Length
, Bcb
->CacheSegmentSize
);
757 CacheSegList
= _alloca(sizeof(PCACHE_SEGMENT
) *
758 (Length
/ Bcb
->CacheSegmentSize
));
761 * Look for a cache segment already mapping the same data.
763 for (i
= 0; i
< (Length
/ Bcb
->CacheSegmentSize
); i
++)
765 ULONG CurrentOffset
= FileOffset
+ (i
* Bcb
->CacheSegmentSize
);
766 current
= CcRosLookupCacheSegment(Bcb
, CurrentOffset
);
769 KeAcquireGuardedMutex(&ViewLock
);
771 /* Move to tail of LRU list */
772 RemoveEntryList(¤t
->CacheSegmentLRUListEntry
);
773 InsertTailList(&CacheSegmentLRUListHead
, ¤t
->CacheSegmentLRUListEntry
);
775 KeReleaseGuardedMutex(&ViewLock
);
777 CacheSegList
[i
] = current
;
781 CcRosCreateCacheSegment(Bcb
, CurrentOffset
, ¤t
);
782 CacheSegList
[i
] = current
;
786 for (i
= 0; i
< (Length
/ Bcb
->CacheSegmentSize
); i
++)
790 *CacheSeg
= CacheSegList
[i
];
791 Previous
= CacheSegList
[i
];
795 Previous
->NextInChain
= CacheSegList
[i
];
796 Previous
= CacheSegList
[i
];
800 Previous
->NextInChain
= NULL
;
802 return(STATUS_SUCCESS
);
807 CcRosGetCacheSegment(PBCB Bcb
,
812 PCACHE_SEGMENT
* CacheSeg
)
814 PCACHE_SEGMENT current
;
819 DPRINT("CcRosGetCacheSegment()\n");
822 * Look for a cache segment already mapping the same data.
824 current
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
828 * Otherwise create a new segment.
830 Status
= CcRosCreateCacheSegment(Bcb
, FileOffset
, ¤t
);
831 if (!NT_SUCCESS(Status
))
837 KeAcquireGuardedMutex(&ViewLock
);
839 /* Move to the tail of the LRU list */
840 RemoveEntryList(¤t
->CacheSegmentLRUListEntry
);
841 InsertTailList(&CacheSegmentLRUListHead
, ¤t
->CacheSegmentLRUListEntry
);
843 KeReleaseGuardedMutex(&ViewLock
);
846 * Return information about the segment to the caller.
848 *UptoDate
= current
->Valid
;
849 *BaseAddress
= current
->BaseAddress
;
850 DPRINT("*BaseAddress 0x%.8X\n", *BaseAddress
);
852 *BaseOffset
= current
->FileOffset
;
853 return(STATUS_SUCCESS
);
857 CcRosRequestCacheSegment(PBCB Bcb
,
861 PCACHE_SEGMENT
* CacheSeg
)
863 * FUNCTION: Request a page mapping for a BCB
870 if ((FileOffset
% Bcb
->CacheSegmentSize
) != 0)
872 DPRINT1("Bad fileoffset %x should be multiple of %x",
873 FileOffset
, Bcb
->CacheSegmentSize
);
874 KeBugCheck(CACHE_MANAGER
);
877 return(CcRosGetCacheSegment(Bcb
,
887 CcFreeCachePage(PVOID Context
, MEMORY_AREA
* MemoryArea
, PVOID Address
,
888 PFN_NUMBER Page
, SWAPENTRY SwapEntry
, BOOLEAN Dirty
)
890 ASSERT(SwapEntry
== 0);
893 ASSERT(MmGetReferenceCountPage(Page
) == 1);
894 MmReleasePageMemoryConsumer(MC_CACHE
, Page
);
899 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg
)
901 * FUNCTION: Releases a cache segment associated with a BCB
911 DPRINT("Freeing cache segment 0x%p\n", CacheSeg
);
913 if ( CacheSeg
->Bcb
->Trace
)
915 DPRINT1("CacheMap 0x%p: deleting Cache Segment: 0x%p\n", CacheSeg
->Bcb
, CacheSeg
);
919 RegionSize
= CacheSeg
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
921 /* Unmap all the pages. */
922 for (i
= 0; i
< RegionSize
; i
++)
924 MmDeleteVirtualMapping(NULL
,
925 CacheSeg
->BaseAddress
+ (i
* PAGE_SIZE
),
929 MmReleasePageMemoryConsumer(MC_CACHE
, Page
);
932 KeAcquireSpinLock(&CiCacheSegMappingRegionLock
, &oldIrql
);
933 /* Deallocate all the pages used. */
934 Base
= (ULONG
)(CacheSeg
->BaseAddress
- CiCacheSegMappingRegionBase
) / PAGE_SIZE
;
936 RtlClearBits(&CiCacheSegMappingRegionAllocMap
, Base
, RegionSize
);
938 CiCacheSegMappingRegionHint
= min (CiCacheSegMappingRegionHint
, Base
);
940 KeReleaseSpinLock(&CiCacheSegMappingRegionLock
, oldIrql
);
942 MmLockAddressSpace(MmGetKernelAddressSpace());
943 MmFreeMemoryArea(MmGetKernelAddressSpace(),
944 CacheSeg
->MemoryArea
,
947 MmUnlockAddressSpace(MmGetKernelAddressSpace());
949 ExFreeToNPagedLookasideList(&CacheSegLookasideList
, CacheSeg
);
950 return(STATUS_SUCCESS
);
955 CcRosFreeCacheSegment(PBCB Bcb
, PCACHE_SEGMENT CacheSeg
)
962 DPRINT("CcRosFreeCacheSegment(Bcb 0x%p, CacheSeg 0x%p)\n",
965 KeAcquireGuardedMutex(&ViewLock
);
966 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
967 RemoveEntryList(&CacheSeg
->BcbSegmentListEntry
);
968 RemoveEntryList(&CacheSeg
->CacheSegmentListEntry
);
969 RemoveEntryList(&CacheSeg
->CacheSegmentLRUListEntry
);
972 RemoveEntryList(&CacheSeg
->DirtySegmentListEntry
);
973 DirtyPageCount
-= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
976 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
977 KeReleaseGuardedMutex(&ViewLock
);
979 Status
= CcRosInternalFreeCacheSegment(CacheSeg
);
987 CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointers
,
988 IN PLARGE_INTEGER FileOffset OPTIONAL
,
990 OUT PIO_STATUS_BLOCK IoStatus
)
993 LARGE_INTEGER Offset
;
994 PCACHE_SEGMENT current
;
998 DPRINT("CcFlushCache(SectionObjectPointers 0x%p, FileOffset 0x%p, Length %d, IoStatus 0x%p)\n",
999 SectionObjectPointers
, FileOffset
, Length
, IoStatus
);
1001 if (SectionObjectPointers
&& SectionObjectPointers
->SharedCacheMap
)
1003 Bcb
= (PBCB
)SectionObjectPointers
->SharedCacheMap
;
1007 Offset
= *FileOffset
;
1011 Offset
.QuadPart
= (LONGLONG
)0;
1012 Length
= Bcb
->FileSize
.u
.LowPart
;
1017 IoStatus
->Status
= STATUS_SUCCESS
;
1018 IoStatus
->Information
= 0;
1023 current
= CcRosLookupCacheSegment (Bcb
, Offset
.u
.LowPart
);
1024 if (current
!= NULL
)
1028 Status
= CcRosFlushCacheSegment(current
);
1029 if (!NT_SUCCESS(Status
) && IoStatus
!= NULL
)
1031 IoStatus
->Status
= Status
;
1034 KeReleaseMutex(¤t
->Mutex
, 0);
1035 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
1036 CcRosCacheSegmentDecRefCount(current
);
1037 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
1040 Offset
.QuadPart
+= Bcb
->CacheSegmentSize
;
1041 if (Length
> Bcb
->CacheSegmentSize
)
1043 Length
-= Bcb
->CacheSegmentSize
;
1055 IoStatus
->Status
= STATUS_INVALID_PARAMETER
;
1062 CcRosDeleteFileCache(PFILE_OBJECT FileObject
, PBCB Bcb
)
1064 * FUNCTION: Releases the BCB associated with a file object
1067 PLIST_ENTRY current_entry
;
1068 PCACHE_SEGMENT current
;
1069 LIST_ENTRY FreeList
;
1075 KeReleaseGuardedMutex(&ViewLock
);
1077 CcFlushCache(FileObject
->SectionObjectPointer
, NULL
, 0, NULL
);
1079 KeAcquireGuardedMutex(&ViewLock
);
1081 if (Bcb
->RefCount
== 0)
1083 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
1085 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1086 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1089 FileObject
->SectionObjectPointer
->SharedCacheMap
= NULL
;
1092 * Release all cache segments.
1094 InitializeListHead(&FreeList
);
1095 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
1096 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
1097 while (!IsListEmpty(&Bcb
->BcbSegmentListHead
))
1099 current_entry
= RemoveTailList(&Bcb
->BcbSegmentListHead
);
1100 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, BcbSegmentListEntry
);
1101 RemoveEntryList(¤t
->CacheSegmentListEntry
);
1102 RemoveEntryList(¤t
->CacheSegmentLRUListEntry
);
1105 RemoveEntryList(¤t
->DirtySegmentListEntry
);
1106 DirtyPageCount
-= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
1107 DPRINT1("Freeing dirty segment\n");
1109 InsertHeadList(&FreeList
, ¤t
->BcbSegmentListEntry
);
1114 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
1116 KeReleaseGuardedMutex(&ViewLock
);
1117 ObDereferenceObject (Bcb
->FileObject
);
1119 while (!IsListEmpty(&FreeList
))
1121 current_entry
= RemoveTailList(&FreeList
);
1122 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, BcbSegmentListEntry
);
1123 CcRosInternalFreeCacheSegment(current
);
1125 ExFreeToNPagedLookasideList(&BcbLookasideList
, Bcb
);
1126 KeAcquireGuardedMutex(&ViewLock
);
1128 return(STATUS_SUCCESS
);
1133 CcRosReferenceCache(PFILE_OBJECT FileObject
)
1136 KeAcquireGuardedMutex(&ViewLock
);
1137 Bcb
= (PBCB
)FileObject
->SectionObjectPointer
->SharedCacheMap
;
1139 if (Bcb
->RefCount
== 0)
1141 ASSERT(Bcb
->BcbRemoveListEntry
.Flink
!= NULL
);
1142 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1143 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1148 ASSERT(Bcb
->BcbRemoveListEntry
.Flink
== NULL
);
1151 KeReleaseGuardedMutex(&ViewLock
);
1156 CcRosSetRemoveOnClose(PSECTION_OBJECT_POINTERS SectionObjectPointer
)
1159 DPRINT("CcRosSetRemoveOnClose()\n");
1160 KeAcquireGuardedMutex(&ViewLock
);
1161 Bcb
= (PBCB
)SectionObjectPointer
->SharedCacheMap
;
1164 Bcb
->RemoveOnClose
= TRUE
;
1165 if (Bcb
->RefCount
== 0)
1167 CcRosDeleteFileCache(Bcb
->FileObject
, Bcb
);
1170 KeReleaseGuardedMutex(&ViewLock
);
1176 CcRosDereferenceCache(PFILE_OBJECT FileObject
)
1179 KeAcquireGuardedMutex(&ViewLock
);
1180 Bcb
= (PBCB
)FileObject
->SectionObjectPointer
->SharedCacheMap
;
1182 if (Bcb
->RefCount
> 0)
1185 if (Bcb
->RefCount
== 0)
1187 MmFreeSectionSegments(Bcb
->FileObject
);
1188 CcRosDeleteFileCache(FileObject
, Bcb
);
1191 KeReleaseGuardedMutex(&ViewLock
);
1195 CcRosReleaseFileCache(PFILE_OBJECT FileObject
)
1197 * FUNCTION: Called by the file system when a handle to a file object
1203 KeAcquireGuardedMutex(&ViewLock
);
1205 if (FileObject
->SectionObjectPointer
->SharedCacheMap
!= NULL
)
1207 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1208 if (FileObject
->PrivateCacheMap
!= NULL
)
1210 FileObject
->PrivateCacheMap
= NULL
;
1211 if (Bcb
->RefCount
> 0)
1214 if (Bcb
->RefCount
== 0)
1216 MmFreeSectionSegments(Bcb
->FileObject
);
1217 CcRosDeleteFileCache(FileObject
, Bcb
);
1222 KeReleaseGuardedMutex(&ViewLock
);
1223 return(STATUS_SUCCESS
);
1228 CcTryToInitializeFileCache(PFILE_OBJECT FileObject
)
1233 KeAcquireGuardedMutex(&ViewLock
);
1235 ASSERT(FileObject
->SectionObjectPointer
);
1236 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1239 Status
= STATUS_UNSUCCESSFUL
;
1243 if (FileObject
->PrivateCacheMap
== NULL
)
1245 FileObject
->PrivateCacheMap
= Bcb
;
1248 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
1250 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1251 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1253 Status
= STATUS_SUCCESS
;
1255 KeReleaseGuardedMutex(&ViewLock
);
1262 CcRosInitializeFileCache(PFILE_OBJECT FileObject
,
1263 ULONG CacheSegmentSize
,
1264 PCACHE_MANAGER_CALLBACKS CallBacks
,
1265 PVOID LazyWriterContext
)
1267 * FUNCTION: Initializes a BCB for a file object
1272 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1273 DPRINT("CcRosInitializeFileCache(FileObject 0x%p, Bcb 0x%p, CacheSegmentSize %d)\n",
1274 FileObject
, Bcb
, CacheSegmentSize
);
1276 KeAcquireGuardedMutex(&ViewLock
);
1279 Bcb
= ExAllocateFromNPagedLookasideList(&BcbLookasideList
);
1282 KeReleaseGuardedMutex(&ViewLock
);
1283 return(STATUS_UNSUCCESSFUL
);
1285 memset(Bcb
, 0, sizeof(BCB
));
1286 ObReferenceObjectByPointer(FileObject
,
1290 Bcb
->FileObject
= FileObject
;
1291 Bcb
->CacheSegmentSize
= CacheSegmentSize
;
1292 Bcb
->Callbacks
= CallBacks
;
1293 Bcb
->LazyWriteContext
= LazyWriterContext
;
1294 if (FileObject
->FsContext
)
1296 Bcb
->AllocationSize
=
1297 ((PFSRTL_COMMON_FCB_HEADER
)FileObject
->FsContext
)->AllocationSize
;
1299 ((PFSRTL_COMMON_FCB_HEADER
)FileObject
->FsContext
)->FileSize
;
1301 KeInitializeSpinLock(&Bcb
->BcbLock
);
1302 InitializeListHead(&Bcb
->BcbSegmentListHead
);
1303 FileObject
->SectionObjectPointer
->SharedCacheMap
= Bcb
;
1305 if (FileObject
->PrivateCacheMap
== NULL
)
1307 FileObject
->PrivateCacheMap
= Bcb
;
1310 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
1312 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1313 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1315 KeReleaseGuardedMutex(&ViewLock
);
1317 return(STATUS_SUCCESS
);
1324 CcGetFileObjectFromSectionPtrs(IN PSECTION_OBJECT_POINTERS SectionObjectPointers
)
1327 if (SectionObjectPointers
&& SectionObjectPointers
->SharedCacheMap
)
1329 Bcb
= (PBCB
)SectionObjectPointers
->SharedCacheMap
;
1331 return Bcb
->FileObject
;
1344 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
1347 DPRINT("CcInitView()\n");
1349 BoundaryAddressMultiple
.QuadPart
= 0;
1350 CiCacheSegMappingRegionHint
= 0;
1351 CiCacheSegMappingRegionBase
= NULL
;
1353 MmLockAddressSpace(MmGetKernelAddressSpace());
1355 Status
= MmCreateMemoryArea(MmGetKernelAddressSpace(),
1356 MEMORY_AREA_CACHE_SEGMENT
,
1357 &CiCacheSegMappingRegionBase
,
1358 CI_CACHESEG_MAPPING_REGION_SIZE
,
1363 BoundaryAddressMultiple
);
1364 MmUnlockAddressSpace(MmGetKernelAddressSpace());
1365 if (!NT_SUCCESS(Status
))
1367 KeBugCheck(CACHE_MANAGER
);
1370 Buffer
= ExAllocatePool(NonPagedPool
, CI_CACHESEG_MAPPING_REGION_SIZE
/ (PAGE_SIZE
* 8));
1373 KeBugCheck(CACHE_MANAGER
);
1376 RtlInitializeBitMap(&CiCacheSegMappingRegionAllocMap
, Buffer
, CI_CACHESEG_MAPPING_REGION_SIZE
/ PAGE_SIZE
);
1377 RtlClearAllBits(&CiCacheSegMappingRegionAllocMap
);
1379 KeInitializeSpinLock(&CiCacheSegMappingRegionLock
);
1381 InitializeListHead(&CacheSegmentListHead
);
1382 InitializeListHead(&DirtySegmentListHead
);
1383 InitializeListHead(&CacheSegmentLRUListHead
);
1384 InitializeListHead(&ClosedListHead
);
1385 KeInitializeGuardedMutex(&ViewLock
);
1386 ExInitializeNPagedLookasideList (&iBcbLookasideList
,
1390 sizeof(INTERNAL_BCB
),
1393 ExInitializeNPagedLookasideList (&BcbLookasideList
,
1400 ExInitializeNPagedLookasideList (&CacheSegLookasideList
,
1404 sizeof(CACHE_SEGMENT
),
1408 MmInitializeMemoryConsumer(MC_CACHE
, CcRosTrimCache
);
1410 CcInitCacheZeroPage();