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
;
75 /* void * alloca(size_t size); */
76 #elif defined(_MSC_VER)
77 void* _alloca(size_t size
);
79 #error Unknown compiler for alloca intrinsic stack allocation "function"
82 #if defined(DBG) || defined(KDBG)
83 static void CcRosCacheSegmentIncRefCount_ ( 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 static void CcRosCacheSegmentDecRefCount_ ( PCACHE_SEGMENT cs
, const char* file
, int line
)
97 DbgPrint("(%s:%i) CacheSegment %p --RefCount=%d, Dirty %d, PageOut %d\n",
98 file
, line
, cs
, cs
->ReferenceCount
, cs
->Dirty
, cs
->PageOut
);
101 #define CcRosCacheSegmentIncRefCount(cs) CcRosCacheSegmentIncRefCount_(cs,__FILE__,__LINE__)
102 #define CcRosCacheSegmentDecRefCount(cs) CcRosCacheSegmentDecRefCount_(cs,__FILE__,__LINE__)
104 #define CcRosCacheSegmentIncRefCount(cs) (++((cs)->ReferenceCount))
105 #define CcRosCacheSegmentDecRefCount(cs) (--((cs)->ReferenceCount))
109 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg
);
112 /* FUNCTIONS *****************************************************************/
120 #if defined(DBG) || defined(KDBG)
122 PLIST_ENTRY current_entry
;
123 PCACHE_SEGMENT current
;
132 DPRINT1("Enabling Tracing for CacheMap 0x%p:\n", Bcb
);
134 KeAcquireGuardedMutex(&ViewLock
);
135 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldirql
);
137 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
138 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
140 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, BcbSegmentListEntry
);
141 current_entry
= current_entry
->Flink
;
143 DPRINT1(" CacheSegment 0x%p enabled, RefCount %d, Dirty %d, PageOut %d\n",
144 current
, current
->ReferenceCount
, current
->Dirty
, current
->PageOut
);
146 KeReleaseSpinLock(&Bcb
->BcbLock
, oldirql
);
147 KeReleaseGuardedMutex(&ViewLock
);
151 DPRINT1("Disabling Tracing for CacheMap 0x%p:\n", Bcb
);
162 CcRosFlushCacheSegment(PCACHE_SEGMENT CacheSegment
)
167 Status
= WriteCacheSegment(CacheSegment
);
168 if (NT_SUCCESS(Status
))
170 KeAcquireGuardedMutex(&ViewLock
);
171 KeAcquireSpinLock(&CacheSegment
->Bcb
->BcbLock
, &oldIrql
);
173 CacheSegment
->Dirty
= FALSE
;
174 RemoveEntryList(&CacheSegment
->DirtySegmentListEntry
);
175 DirtyPageCount
-= CacheSegment
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
176 CcRosCacheSegmentDecRefCount ( CacheSegment
);
178 KeReleaseSpinLock(&CacheSegment
->Bcb
->BcbLock
, oldIrql
);
179 KeReleaseGuardedMutex(&ViewLock
);
187 CcRosFlushDirtyPages(ULONG Target
, PULONG Count
)
189 PLIST_ENTRY current_entry
;
190 PCACHE_SEGMENT current
;
191 ULONG PagesPerSegment
;
194 static ULONG WriteCount
[4] = {0, 0, 0, 0};
197 DPRINT("CcRosFlushDirtyPages(Target %d)\n", Target
);
201 KeAcquireGuardedMutex(&ViewLock
);
203 WriteCount
[0] = WriteCount
[1];
204 WriteCount
[1] = WriteCount
[2];
205 WriteCount
[2] = WriteCount
[3];
208 NewTarget
= WriteCount
[0] + WriteCount
[1] + WriteCount
[2];
210 if (NewTarget
< DirtyPageCount
)
212 NewTarget
= (DirtyPageCount
- NewTarget
+ 3) / 4;
213 WriteCount
[0] += NewTarget
;
214 WriteCount
[1] += NewTarget
;
215 WriteCount
[2] += NewTarget
;
216 WriteCount
[3] += NewTarget
;
219 NewTarget
= WriteCount
[0];
221 Target
= max(NewTarget
, Target
);
223 current_entry
= DirtySegmentListHead
.Flink
;
224 if (current_entry
== &DirtySegmentListHead
)
226 DPRINT("No Dirty pages\n");
229 while (current_entry
!= &DirtySegmentListHead
&& Target
> 0)
231 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
232 DirtySegmentListEntry
);
233 current_entry
= current_entry
->Flink
;
235 Locked
= current
->Bcb
->Callbacks
->AcquireForLazyWrite(
236 current
->Bcb
->LazyWriteContext
, FALSE
);
242 Locked
= ExTryToAcquirePushLockExclusive(¤t
->Lock
);
245 current
->Bcb
->Callbacks
->ReleaseFromLazyWrite(
246 current
->Bcb
->LazyWriteContext
);
251 ASSERT(current
->Dirty
);
252 if (current
->ReferenceCount
> 1)
254 ExReleasePushLock(¤t
->Lock
);
255 current
->Bcb
->Callbacks
->ReleaseFromLazyWrite(
256 current
->Bcb
->LazyWriteContext
);
260 PagesPerSegment
= current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
262 KeReleaseGuardedMutex(&ViewLock
);
264 Status
= CcRosFlushCacheSegment(current
);
266 ExReleasePushLock(¤t
->Lock
);
267 current
->Bcb
->Callbacks
->ReleaseFromLazyWrite(
268 current
->Bcb
->LazyWriteContext
);
270 if (!NT_SUCCESS(Status
) && (Status
!= STATUS_END_OF_FILE
))
272 DPRINT1("CC: Failed to flush cache segment.\n");
276 (*Count
) += PagesPerSegment
;
277 Target
-= PagesPerSegment
;
280 KeAcquireGuardedMutex(&ViewLock
);
281 current_entry
= DirtySegmentListHead
.Flink
;
284 if (*Count
< NewTarget
)
286 WriteCount
[1] += (NewTarget
- *Count
);
289 KeReleaseGuardedMutex(&ViewLock
);
291 DPRINT("CcRosFlushDirtyPages() finished\n");
292 return(STATUS_SUCCESS
);
296 CcRosTrimCache(ULONG Target
, ULONG Priority
, PULONG NrFreed
)
298 * FUNCTION: Try to free some memory from the file cache.
300 * Target - The number of pages to be freed.
301 * Priority - The priority of free (currently unused).
302 * NrFreed - Points to a variable where the number of pages
303 * actually freed is returned.
306 PLIST_ENTRY current_entry
;
307 PCACHE_SEGMENT current
;
308 ULONG PagesPerSegment
;
313 DPRINT("CcRosTrimCache(Target %d)\n", Target
);
317 InitializeListHead(&FreeList
);
319 KeAcquireGuardedMutex(&ViewLock
);
320 current_entry
= CacheSegmentLRUListHead
.Flink
;
321 while (current_entry
!= &CacheSegmentLRUListHead
&& Target
> 0)
325 Status
= STATUS_SUCCESS
;
326 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
327 CacheSegmentLRUListEntry
);
328 current_entry
= current_entry
->Flink
;
330 KeAcquireSpinLock(¤t
->Bcb
->BcbLock
, &oldIrql
);
332 if (current
->MappedCount
> 0 && !current
->Dirty
&& !current
->PageOut
)
336 CcRosCacheSegmentIncRefCount(current
);
337 current
->PageOut
= TRUE
;
338 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
339 KeReleaseGuardedMutex(&ViewLock
);
340 for (i
= 0; i
< current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
; i
++)
343 Page
= (PFN_TYPE
)(MmGetPhysicalAddress((char*)current
->BaseAddress
+ i
* PAGE_SIZE
).QuadPart
>> PAGE_SHIFT
);
344 Status
= MmPageOutPhysicalAddress(Page
);
346 KeAcquireGuardedMutex(&ViewLock
);
347 KeAcquireSpinLock(¤t
->Bcb
->BcbLock
, &oldIrql
);
348 CcRosCacheSegmentDecRefCount(current
);
351 if (current
->ReferenceCount
== 0)
353 PagesPerSegment
= current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
354 // PagesFreed = PagesPerSegment;
355 PagesFreed
= min(PagesPerSegment
, Target
);
356 Target
-= PagesFreed
;
357 (*NrFreed
) += PagesFreed
;
360 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
363 current_entry
= CacheSegmentLRUListHead
.Flink
;
364 while (current_entry
!= &CacheSegmentLRUListHead
)
366 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
367 CacheSegmentLRUListEntry
);
368 current
->PageOut
= FALSE
;
369 current_entry
= current_entry
->Flink
;
371 KeAcquireSpinLock(¤t
->Bcb
->BcbLock
, &oldIrql
);
372 if (current
->ReferenceCount
== 0)
374 RemoveEntryList(¤t
->BcbSegmentListEntry
);
375 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
376 RemoveEntryList(¤t
->CacheSegmentListEntry
);
377 RemoveEntryList(¤t
->CacheSegmentLRUListEntry
);
378 InsertHeadList(&FreeList
, ¤t
->BcbSegmentListEntry
);
382 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
386 KeReleaseGuardedMutex(&ViewLock
);
388 while (!IsListEmpty(&FreeList
))
390 current_entry
= RemoveHeadList(&FreeList
);
391 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
392 BcbSegmentListEntry
);
393 CcRosInternalFreeCacheSegment(current
);
396 return(STATUS_SUCCESS
);
401 CcRosReleaseCacheSegment(PBCB Bcb
,
402 PCACHE_SEGMENT CacheSeg
,
407 BOOLEAN WasDirty
= CacheSeg
->Dirty
;
412 DPRINT("CcReleaseCacheSegment(Bcb 0x%p, CacheSeg 0x%p, Valid %d)\n",
413 Bcb
, CacheSeg
, Valid
);
415 CacheSeg
->Valid
= Valid
;
416 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| Dirty
;
418 KeAcquireGuardedMutex(&ViewLock
);
419 if (!WasDirty
&& CacheSeg
->Dirty
)
421 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
422 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
424 RemoveEntryList(&CacheSeg
->CacheSegmentLRUListEntry
);
425 InsertTailList(&CacheSegmentLRUListHead
, &CacheSeg
->CacheSegmentLRUListEntry
);
429 CacheSeg
->MappedCount
++;
431 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
432 CcRosCacheSegmentDecRefCount(CacheSeg
);
433 if (Mapped
&& CacheSeg
->MappedCount
== 1)
435 CcRosCacheSegmentIncRefCount(CacheSeg
);
437 if (!WasDirty
&& CacheSeg
->Dirty
)
439 CcRosCacheSegmentIncRefCount(CacheSeg
);
441 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
442 KeReleaseGuardedMutex(&ViewLock
);
443 ExReleasePushLock(&CacheSeg
->Lock
);
445 return(STATUS_SUCCESS
);
448 /* Returns with Cache Segment Lock Held! */
451 CcRosLookupCacheSegment(PBCB Bcb
, ULONG FileOffset
)
453 PLIST_ENTRY current_entry
;
454 PCACHE_SEGMENT current
;
459 DPRINT("CcRosLookupCacheSegment(Bcb -x%p, FileOffset %d)\n", Bcb
, FileOffset
);
461 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 ExAcquirePushLockExclusive(¤t
->Lock
);
475 current_entry
= current_entry
->Flink
;
477 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
483 CcRosMarkDirtyCacheSegment(PBCB Bcb
, ULONG FileOffset
)
485 PCACHE_SEGMENT CacheSeg
;
490 DPRINT("CcRosMarkDirtyCacheSegment(Bcb 0x%p, FileOffset %d)\n", Bcb
, FileOffset
);
492 CacheSeg
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
493 if (CacheSeg
== NULL
)
495 KeBugCheck(CACHE_MANAGER
);
497 if (!CacheSeg
->Dirty
)
499 KeAcquireGuardedMutex(&ViewLock
);
500 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
501 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
502 KeReleaseGuardedMutex(&ViewLock
);
506 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
507 CcRosCacheSegmentDecRefCount(CacheSeg
);
508 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
512 CacheSeg
->Dirty
= TRUE
;
513 ExReleasePushLock(&CacheSeg
->Lock
);
515 return(STATUS_SUCCESS
);
520 CcRosUnmapCacheSegment(PBCB Bcb
, ULONG FileOffset
, BOOLEAN NowDirty
)
522 PCACHE_SEGMENT CacheSeg
;
528 DPRINT("CcRosUnmapCacheSegment(Bcb 0x%p, FileOffset %d, NowDirty %d)\n",
529 Bcb
, FileOffset
, NowDirty
);
531 CacheSeg
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
532 if (CacheSeg
== NULL
)
534 return(STATUS_UNSUCCESSFUL
);
537 WasDirty
= CacheSeg
->Dirty
;
538 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| NowDirty
;
540 CacheSeg
->MappedCount
--;
542 if (!WasDirty
&& NowDirty
)
544 KeAcquireGuardedMutex(&ViewLock
);
545 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
546 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
547 KeReleaseGuardedMutex(&ViewLock
);
550 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
551 CcRosCacheSegmentDecRefCount(CacheSeg
);
552 if (!WasDirty
&& NowDirty
)
554 CcRosCacheSegmentIncRefCount(CacheSeg
);
556 if (CacheSeg
->MappedCount
== 0)
558 CcRosCacheSegmentDecRefCount(CacheSeg
);
560 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
562 ExReleasePushLock(&CacheSeg
->Lock
);
563 return(STATUS_SUCCESS
);
568 CcRosCreateCacheSegment(PBCB Bcb
,
570 PCACHE_SEGMENT
* CacheSeg
)
572 PCACHE_SEGMENT current
;
573 PCACHE_SEGMENT previous
;
574 PLIST_ENTRY current_entry
;
578 ULONG StartingOffset
;
580 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
584 DPRINT("CcRosCreateCacheSegment()\n");
586 BoundaryAddressMultiple
.QuadPart
= 0;
587 if (FileOffset
>= Bcb
->FileSize
.u
.LowPart
)
590 return STATUS_INVALID_PARAMETER
;
593 current
= ExAllocateFromNPagedLookasideList(&CacheSegLookasideList
);
594 current
->Valid
= FALSE
;
595 current
->Dirty
= FALSE
;
596 current
->PageOut
= FALSE
;
597 current
->FileOffset
= ROUND_DOWN(FileOffset
, Bcb
->CacheSegmentSize
);
599 #if defined(DBG) || defined(KDBG)
602 DPRINT1("CacheMap 0x%p: new Cache Segment: 0x%p\n", Bcb
, current
);
605 current
->MappedCount
= 0;
606 current
->DirtySegmentListEntry
.Flink
= NULL
;
607 current
->DirtySegmentListEntry
.Blink
= NULL
;
608 current
->ReferenceCount
= 1;
609 ExInitializePushLock((PULONG_PTR
)¤t
->Lock
);
610 ExAcquirePushLockExclusive(¤t
->Lock
);
611 KeAcquireGuardedMutex(&ViewLock
);
614 /* There is window between the call to CcRosLookupCacheSegment
615 * and CcRosCreateCacheSegment. We must check if a segment on
616 * the fileoffset exist. If there exist a segment, we release
617 * our new created segment and return the existing one.
619 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
620 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
622 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
624 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
625 BcbSegmentListEntry
);
626 if (current
->FileOffset
<= FileOffset
&&
627 (current
->FileOffset
+ Bcb
->CacheSegmentSize
) > FileOffset
)
629 CcRosCacheSegmentIncRefCount(current
);
630 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
631 #if defined(DBG) || defined(KDBG)
634 DPRINT1("CacheMap 0x%p: deleting newly created Cache Segment 0x%p ( found existing one 0x%p )\n",
640 ExReleasePushLock(&(*CacheSeg
)->Lock
);
641 KeReleaseGuardedMutex(&ViewLock
);
642 ExFreeToNPagedLookasideList(&CacheSegLookasideList
, *CacheSeg
);
644 ExAcquirePushLockExclusive(¤t
->Lock
);
645 return STATUS_SUCCESS
;
647 if (current
->FileOffset
< FileOffset
)
649 if (previous
== NULL
)
655 if (previous
->FileOffset
< current
->FileOffset
)
661 current_entry
= current_entry
->Flink
;
663 /* There was no existing segment. */
667 InsertHeadList(&previous
->BcbSegmentListEntry
, ¤t
->BcbSegmentListEntry
);
671 InsertHeadList(&Bcb
->BcbSegmentListHead
, ¤t
->BcbSegmentListEntry
);
673 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
674 InsertTailList(&CacheSegmentListHead
, ¤t
->CacheSegmentListEntry
);
675 InsertTailList(&CacheSegmentLRUListHead
, ¤t
->CacheSegmentLRUListEntry
);
676 KeReleaseGuardedMutex(&ViewLock
);
678 KeAcquireSpinLock(&CiCacheSegMappingRegionLock
, &oldIrql
);
680 StartingOffset
= RtlFindClearBitsAndSet(&CiCacheSegMappingRegionAllocMap
, Bcb
->CacheSegmentSize
/ PAGE_SIZE
, CiCacheSegMappingRegionHint
);
682 if (StartingOffset
== 0xffffffff)
684 DPRINT1("Out of CacheSeg mapping space\n");
685 KeBugCheck(CACHE_MANAGER
);
688 current
->BaseAddress
= CiCacheSegMappingRegionBase
+ StartingOffset
* PAGE_SIZE
;
690 if (CiCacheSegMappingRegionHint
== StartingOffset
)
692 CiCacheSegMappingRegionHint
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
695 KeReleaseSpinLock(&CiCacheSegMappingRegionLock
, oldIrql
);
697 MmLockAddressSpace(MmGetKernelAddressSpace());
698 current
->BaseAddress
= NULL
;
699 Status
= MmCreateMemoryArea(MmGetKernelAddressSpace(),
700 MEMORY_AREA_CACHE_SEGMENT
,
701 ¤t
->BaseAddress
,
702 Bcb
->CacheSegmentSize
,
704 (PMEMORY_AREA
*)¤t
->MemoryArea
,
707 BoundaryAddressMultiple
);
708 MmUnlockAddressSpace(MmGetKernelAddressSpace());
709 if (!NT_SUCCESS(Status
))
711 KeBugCheck(CACHE_MANAGER
);
715 /* Create a virtual mapping for this memory area */
716 MmMapMemoryArea(current
->BaseAddress
, Bcb
->CacheSegmentSize
,
717 MC_CACHE
, PAGE_READWRITE
);
719 return(STATUS_SUCCESS
);
724 CcRosGetCacheSegmentChain(PBCB Bcb
,
727 PCACHE_SEGMENT
* CacheSeg
)
729 PCACHE_SEGMENT current
;
731 PCACHE_SEGMENT
* CacheSegList
;
732 PCACHE_SEGMENT Previous
= NULL
;
736 DPRINT("CcRosGetCacheSegmentChain()\n");
738 Length
= ROUND_UP(Length
, Bcb
->CacheSegmentSize
);
740 #if defined(__GNUC__)
741 CacheSegList
= alloca(sizeof(PCACHE_SEGMENT
) *
742 (Length
/ Bcb
->CacheSegmentSize
));
743 #elif defined(_MSC_VER)
744 CacheSegList
= _alloca(sizeof(PCACHE_SEGMENT
) *
745 (Length
/ Bcb
->CacheSegmentSize
));
747 #error Unknown compiler for alloca intrinsic stack allocation "function"
751 * Look for a cache segment already mapping the same data.
753 for (i
= 0; i
< (Length
/ Bcb
->CacheSegmentSize
); i
++)
755 ULONG CurrentOffset
= FileOffset
+ (i
* Bcb
->CacheSegmentSize
);
756 current
= CcRosLookupCacheSegment(Bcb
, CurrentOffset
);
759 CacheSegList
[i
] = current
;
763 CcRosCreateCacheSegment(Bcb
, CurrentOffset
, ¤t
);
764 CacheSegList
[i
] = current
;
768 for (i
= 0; i
< (Length
/ Bcb
->CacheSegmentSize
); i
++)
772 *CacheSeg
= CacheSegList
[i
];
773 Previous
= CacheSegList
[i
];
777 Previous
->NextInChain
= CacheSegList
[i
];
778 Previous
= CacheSegList
[i
];
782 Previous
->NextInChain
= NULL
;
784 return(STATUS_SUCCESS
);
789 CcRosGetCacheSegment(PBCB Bcb
,
794 PCACHE_SEGMENT
* CacheSeg
)
796 PCACHE_SEGMENT current
;
801 DPRINT("CcRosGetCacheSegment()\n");
804 * Look for a cache segment already mapping the same data.
806 current
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
810 * Otherwise create a new segment.
812 Status
= CcRosCreateCacheSegment(Bcb
, FileOffset
, ¤t
);
813 if (!NT_SUCCESS(Status
))
819 * Return information about the segment to the caller.
821 *UptoDate
= current
->Valid
;
822 *BaseAddress
= current
->BaseAddress
;
823 DPRINT("*BaseAddress 0x%.8X\n", *BaseAddress
);
825 *BaseOffset
= current
->FileOffset
;
826 return(STATUS_SUCCESS
);
830 CcRosRequestCacheSegment(PBCB Bcb
,
834 PCACHE_SEGMENT
* CacheSeg
)
836 * FUNCTION: Request a page mapping for a BCB
843 if ((FileOffset
% Bcb
->CacheSegmentSize
) != 0)
845 DPRINT1("Bad fileoffset %x should be multiple of %x",
846 FileOffset
, Bcb
->CacheSegmentSize
);
847 KeBugCheck(CACHE_MANAGER
);
850 return(CcRosGetCacheSegment(Bcb
,
860 CcFreeCachePage(PVOID Context
, MEMORY_AREA
* MemoryArea
, PVOID Address
,
861 PFN_TYPE Page
, SWAPENTRY SwapEntry
, BOOLEAN Dirty
)
863 ASSERT(SwapEntry
== 0);
866 MmReleasePageMemoryConsumer(MC_CACHE
, Page
);
871 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg
)
873 * FUNCTION: Releases a cache segment associated with a BCB
883 DPRINT("Freeing cache segment 0x%p\n", CacheSeg
);
884 #if defined(DBG) || defined(KDBG)
885 if ( CacheSeg
->Bcb
->Trace
)
887 DPRINT1("CacheMap 0x%p: deleting Cache Segment: 0x%p\n", CacheSeg
->Bcb
, CacheSeg
);
891 RegionSize
= CacheSeg
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
893 /* Unmap all the pages. */
894 for (i
= 0; i
< RegionSize
; i
++)
896 MmDeleteVirtualMapping(NULL
,
897 CacheSeg
->BaseAddress
+ (i
* PAGE_SIZE
),
901 MmReleasePageMemoryConsumer(MC_CACHE
, Page
);
904 KeAcquireSpinLock(&CiCacheSegMappingRegionLock
, &oldIrql
);
905 /* Deallocate all the pages used. */
906 Base
= (ULONG
)(CacheSeg
->BaseAddress
- CiCacheSegMappingRegionBase
) / PAGE_SIZE
;
908 RtlClearBits(&CiCacheSegMappingRegionAllocMap
, Base
, RegionSize
);
910 CiCacheSegMappingRegionHint
= min (CiCacheSegMappingRegionHint
, Base
);
912 KeReleaseSpinLock(&CiCacheSegMappingRegionLock
, oldIrql
);
914 MmLockAddressSpace(MmGetKernelAddressSpace());
915 MmFreeMemoryArea(MmGetKernelAddressSpace(),
916 CacheSeg
->MemoryArea
,
919 MmUnlockAddressSpace(MmGetKernelAddressSpace());
921 ExFreeToNPagedLookasideList(&CacheSegLookasideList
, CacheSeg
);
922 return(STATUS_SUCCESS
);
927 CcRosFreeCacheSegment(PBCB Bcb
, PCACHE_SEGMENT CacheSeg
)
934 DPRINT("CcRosFreeCacheSegment(Bcb 0x%p, CacheSeg 0x%p)\n",
937 KeAcquireGuardedMutex(&ViewLock
);
938 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
939 RemoveEntryList(&CacheSeg
->BcbSegmentListEntry
);
940 RemoveEntryList(&CacheSeg
->CacheSegmentListEntry
);
941 RemoveEntryList(&CacheSeg
->CacheSegmentLRUListEntry
);
944 RemoveEntryList(&CacheSeg
->DirtySegmentListEntry
);
945 DirtyPageCount
-= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
948 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
949 KeReleaseGuardedMutex(&ViewLock
);
951 Status
= CcRosInternalFreeCacheSegment(CacheSeg
);
959 CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointers
,
960 IN PLARGE_INTEGER FileOffset OPTIONAL
,
962 OUT PIO_STATUS_BLOCK IoStatus
)
965 LARGE_INTEGER Offset
;
966 PCACHE_SEGMENT current
;
970 DPRINT("CcFlushCache(SectionObjectPointers 0x%p, FileOffset 0x%p, Length %d, IoStatus 0x%p)\n",
971 SectionObjectPointers
, FileOffset
, Length
, IoStatus
);
973 if (SectionObjectPointers
&& SectionObjectPointers
->SharedCacheMap
)
975 Bcb
= (PBCB
)SectionObjectPointers
->SharedCacheMap
;
979 Offset
= *FileOffset
;
983 Offset
.QuadPart
= (LONGLONG
)0;
984 Length
= Bcb
->FileSize
.u
.LowPart
;
989 IoStatus
->Status
= STATUS_SUCCESS
;
990 IoStatus
->Information
= 0;
995 current
= CcRosLookupCacheSegment (Bcb
, Offset
.u
.LowPart
);
1000 Status
= CcRosFlushCacheSegment(current
);
1001 if (!NT_SUCCESS(Status
) && IoStatus
!= NULL
)
1003 IoStatus
->Status
= Status
;
1006 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
1007 ExReleasePushLock(¤t
->Lock
);
1008 CcRosCacheSegmentDecRefCount(current
);
1009 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
1012 Offset
.QuadPart
+= Bcb
->CacheSegmentSize
;
1013 if (Length
> Bcb
->CacheSegmentSize
)
1015 Length
-= Bcb
->CacheSegmentSize
;
1027 IoStatus
->Status
= STATUS_INVALID_PARAMETER
;
1034 CcRosDeleteFileCache(PFILE_OBJECT FileObject
, PBCB Bcb
)
1036 * FUNCTION: Releases the BCB associated with a file object
1039 PLIST_ENTRY current_entry
;
1040 PCACHE_SEGMENT current
;
1042 LIST_ENTRY FreeList
;
1048 KeReleaseGuardedMutex(&ViewLock
);
1050 CcFlushCache(FileObject
->SectionObjectPointer
, NULL
, 0, NULL
);
1052 KeAcquireGuardedMutex(&ViewLock
);
1054 if (Bcb
->RefCount
== 0)
1056 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
1058 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1059 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1062 FileObject
->SectionObjectPointer
->SharedCacheMap
= NULL
;
1065 * Release all cache segments.
1067 InitializeListHead(&FreeList
);
1068 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
1069 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
1070 while (!IsListEmpty(&Bcb
->BcbSegmentListHead
))
1072 current_entry
= RemoveTailList(&Bcb
->BcbSegmentListHead
);
1073 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, BcbSegmentListEntry
);
1074 RemoveEntryList(¤t
->CacheSegmentListEntry
);
1075 RemoveEntryList(¤t
->CacheSegmentLRUListEntry
);
1078 RemoveEntryList(¤t
->DirtySegmentListEntry
);
1079 DirtyPageCount
-= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
1080 DPRINT1("Freeing dirty segment\n");
1082 InsertHeadList(&FreeList
, ¤t
->BcbSegmentListEntry
);
1084 #if defined(DBG) || defined(KDBG)
1087 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
1089 KeReleaseGuardedMutex(&ViewLock
);
1090 ObDereferenceObject (Bcb
->FileObject
);
1092 while (!IsListEmpty(&FreeList
))
1094 current_entry
= RemoveTailList(&FreeList
);
1095 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, BcbSegmentListEntry
);
1096 Status
= CcRosInternalFreeCacheSegment(current
);
1098 ExFreeToNPagedLookasideList(&BcbLookasideList
, Bcb
);
1099 KeAcquireGuardedMutex(&ViewLock
);
1101 return(STATUS_SUCCESS
);
1106 CcRosReferenceCache(PFILE_OBJECT FileObject
)
1109 KeAcquireGuardedMutex(&ViewLock
);
1110 Bcb
= (PBCB
)FileObject
->SectionObjectPointer
->SharedCacheMap
;
1112 if (Bcb
->RefCount
== 0)
1114 ASSERT(Bcb
->BcbRemoveListEntry
.Flink
!= NULL
);
1115 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1116 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1121 ASSERT(Bcb
->BcbRemoveListEntry
.Flink
== NULL
);
1124 KeReleaseGuardedMutex(&ViewLock
);
1129 CcRosSetRemoveOnClose(PSECTION_OBJECT_POINTERS SectionObjectPointer
)
1132 DPRINT("CcRosSetRemoveOnClose()\n");
1133 KeAcquireGuardedMutex(&ViewLock
);
1134 Bcb
= (PBCB
)SectionObjectPointer
->SharedCacheMap
;
1137 Bcb
->RemoveOnClose
= TRUE
;
1138 if (Bcb
->RefCount
== 0)
1140 CcRosDeleteFileCache(Bcb
->FileObject
, Bcb
);
1143 KeReleaseGuardedMutex(&ViewLock
);
1149 CcRosDereferenceCache(PFILE_OBJECT FileObject
)
1152 KeAcquireGuardedMutex(&ViewLock
);
1153 Bcb
= (PBCB
)FileObject
->SectionObjectPointer
->SharedCacheMap
;
1155 if (Bcb
->RefCount
> 0)
1158 if (Bcb
->RefCount
== 0)
1160 MmFreeSectionSegments(Bcb
->FileObject
);
1161 CcRosDeleteFileCache(FileObject
, Bcb
);
1164 KeReleaseGuardedMutex(&ViewLock
);
1168 CcRosReleaseFileCache(PFILE_OBJECT FileObject
)
1170 * FUNCTION: Called by the file system when a handle to a file object
1176 KeAcquireGuardedMutex(&ViewLock
);
1178 if (FileObject
->SectionObjectPointer
->SharedCacheMap
!= NULL
)
1180 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1181 if (FileObject
->PrivateCacheMap
!= NULL
)
1183 FileObject
->PrivateCacheMap
= NULL
;
1184 if (Bcb
->RefCount
> 0)
1187 if (Bcb
->RefCount
== 0)
1189 MmFreeSectionSegments(Bcb
->FileObject
);
1190 CcRosDeleteFileCache(FileObject
, Bcb
);
1195 KeReleaseGuardedMutex(&ViewLock
);
1196 return(STATUS_SUCCESS
);
1201 CcTryToInitializeFileCache(PFILE_OBJECT FileObject
)
1206 KeAcquireGuardedMutex(&ViewLock
);
1208 ASSERT(FileObject
->SectionObjectPointer
);
1209 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1212 Status
= STATUS_UNSUCCESSFUL
;
1216 if (FileObject
->PrivateCacheMap
== NULL
)
1218 FileObject
->PrivateCacheMap
= Bcb
;
1221 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
1223 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1224 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1226 Status
= STATUS_SUCCESS
;
1228 KeReleaseGuardedMutex(&ViewLock
);
1235 CcRosInitializeFileCache(PFILE_OBJECT FileObject
,
1236 ULONG CacheSegmentSize
,
1237 PCACHE_MANAGER_CALLBACKS CallBacks
,
1238 PVOID LazyWriterContext
)
1240 * FUNCTION: Initializes a BCB for a file object
1245 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1246 DPRINT("CcRosInitializeFileCache(FileObject 0x%p, Bcb 0x%p, CacheSegmentSize %d)\n",
1247 FileObject
, Bcb
, CacheSegmentSize
);
1249 KeAcquireGuardedMutex(&ViewLock
);
1252 Bcb
= ExAllocateFromNPagedLookasideList(&BcbLookasideList
);
1255 KeReleaseGuardedMutex(&ViewLock
);
1256 return(STATUS_UNSUCCESSFUL
);
1258 memset(Bcb
, 0, sizeof(BCB
));
1259 ObReferenceObjectByPointer(FileObject
,
1263 Bcb
->FileObject
= FileObject
;
1264 Bcb
->CacheSegmentSize
= CacheSegmentSize
;
1265 Bcb
->Callbacks
= CallBacks
;
1266 Bcb
->LazyWriteContext
= LazyWriterContext
;
1267 if (FileObject
->FsContext
)
1269 Bcb
->AllocationSize
=
1270 ((PFSRTL_COMMON_FCB_HEADER
)FileObject
->FsContext
)->AllocationSize
;
1272 ((PFSRTL_COMMON_FCB_HEADER
)FileObject
->FsContext
)->FileSize
;
1274 KeInitializeSpinLock(&Bcb
->BcbLock
);
1275 InitializeListHead(&Bcb
->BcbSegmentListHead
);
1276 FileObject
->SectionObjectPointer
->SharedCacheMap
= Bcb
;
1278 if (FileObject
->PrivateCacheMap
== NULL
)
1280 FileObject
->PrivateCacheMap
= Bcb
;
1283 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
1285 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1286 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1288 KeReleaseGuardedMutex(&ViewLock
);
1290 return(STATUS_SUCCESS
);
1297 CcGetFileObjectFromSectionPtrs(IN PSECTION_OBJECT_POINTERS SectionObjectPointers
)
1300 if (SectionObjectPointers
&& SectionObjectPointers
->SharedCacheMap
)
1302 Bcb
= (PBCB
)SectionObjectPointers
->SharedCacheMap
;
1304 return Bcb
->FileObject
;
1317 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
1320 DPRINT("CcInitView()\n");
1322 BoundaryAddressMultiple
.QuadPart
= 0;
1323 CiCacheSegMappingRegionHint
= 0;
1324 CiCacheSegMappingRegionBase
= NULL
;
1326 MmLockAddressSpace(MmGetKernelAddressSpace());
1328 Status
= MmCreateMemoryArea(MmGetKernelAddressSpace(),
1329 MEMORY_AREA_CACHE_SEGMENT
,
1330 &CiCacheSegMappingRegionBase
,
1331 CI_CACHESEG_MAPPING_REGION_SIZE
,
1336 BoundaryAddressMultiple
);
1337 MmUnlockAddressSpace(MmGetKernelAddressSpace());
1338 if (!NT_SUCCESS(Status
))
1340 KeBugCheck(CACHE_MANAGER
);
1343 Buffer
= ExAllocatePool(NonPagedPool
, CI_CACHESEG_MAPPING_REGION_SIZE
/ (PAGE_SIZE
* 8));
1345 RtlInitializeBitMap(&CiCacheSegMappingRegionAllocMap
, Buffer
, CI_CACHESEG_MAPPING_REGION_SIZE
/ PAGE_SIZE
);
1346 RtlClearAllBits(&CiCacheSegMappingRegionAllocMap
);
1348 KeInitializeSpinLock(&CiCacheSegMappingRegionLock
);
1350 InitializeListHead(&CacheSegmentListHead
);
1351 InitializeListHead(&DirtySegmentListHead
);
1352 InitializeListHead(&CacheSegmentLRUListHead
);
1353 InitializeListHead(&ClosedListHead
);
1354 KeInitializeGuardedMutex(&ViewLock
);
1355 ExInitializeNPagedLookasideList (&iBcbLookasideList
,
1359 sizeof(INTERNAL_BCB
),
1362 ExInitializeNPagedLookasideList (&BcbLookasideList
,
1369 ExInitializeNPagedLookasideList (&CacheSegLookasideList
,
1373 sizeof(CACHE_SEGMENT
),
1377 MmInitializeMemoryConsumer(MC_CACHE
, CcRosTrimCache
);
1379 CcInitCacheZeroPage();