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
;
73 static ULONG CcTimeStamp
;
74 static KEVENT LazyCloseThreadEvent
;
75 static HANDLE LazyCloseThreadHandle
;
76 static CLIENT_ID LazyCloseThreadId
;
77 static volatile BOOLEAN LazyCloseThreadShouldTerminate
;
80 /* void * alloca(size_t size); */
81 #elif defined(_MSC_VER)
82 void* _alloca(size_t size
);
84 #error Unknown compiler for alloca intrinsic stack allocation "function"
87 #if defined(DBG) || defined(KDBG)
88 static void CcRosCacheSegmentIncRefCount_ ( PCACHE_SEGMENT cs
, const char* file
, int line
)
93 DbgPrint("(%s:%i) CacheSegment %p ++RefCount=%d, Dirty %d, PageOut %d\n",
94 file
, line
, cs
, cs
->ReferenceCount
, cs
->Dirty
, cs
->PageOut
);
97 static void CcRosCacheSegmentDecRefCount_ ( PCACHE_SEGMENT cs
, const char* file
, int line
)
100 if ( cs
->Bcb
->Trace
)
102 DbgPrint("(%s:%i) CacheSegment %p --RefCount=%d, Dirty %d, PageOut %d\n",
103 file
, line
, cs
, cs
->ReferenceCount
, cs
->Dirty
, cs
->PageOut
);
106 #define CcRosCacheSegmentIncRefCount(cs) CcRosCacheSegmentIncRefCount_(cs,__FILE__,__LINE__)
107 #define CcRosCacheSegmentDecRefCount(cs) CcRosCacheSegmentDecRefCount_(cs,__FILE__,__LINE__)
109 #define CcRosCacheSegmentIncRefCount(cs) (++((cs)->ReferenceCount))
110 #define CcRosCacheSegmentDecRefCount(cs) (--((cs)->ReferenceCount))
114 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg
);
117 /* FUNCTIONS *****************************************************************/
125 #if defined(DBG) || defined(KDBG)
127 PLIST_ENTRY current_entry
;
128 PCACHE_SEGMENT current
;
137 DPRINT1("Enabling Tracing for CacheMap 0x%p:\n", Bcb
);
139 KeAcquireGuardedMutex(&ViewLock
);
140 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldirql
);
142 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
143 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
145 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, BcbSegmentListEntry
);
146 current_entry
= current_entry
->Flink
;
148 DPRINT1(" CacheSegment 0x%p enabled, RefCount %d, Dirty %d, PageOut %d\n",
149 current
, current
->ReferenceCount
, current
->Dirty
, current
->PageOut
);
151 KeReleaseSpinLock(&Bcb
->BcbLock
, oldirql
);
152 KeReleaseGuardedMutex(&ViewLock
);
156 DPRINT1("Disabling Tracing for CacheMap 0x%p:\n", Bcb
);
167 CcRosFlushCacheSegment(PCACHE_SEGMENT CacheSegment
)
172 Status
= WriteCacheSegment(CacheSegment
);
173 if (NT_SUCCESS(Status
))
175 KeAcquireGuardedMutex(&ViewLock
);
176 KeAcquireSpinLock(&CacheSegment
->Bcb
->BcbLock
, &oldIrql
);
178 CacheSegment
->Dirty
= FALSE
;
179 RemoveEntryList(&CacheSegment
->DirtySegmentListEntry
);
180 DirtyPageCount
-= CacheSegment
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
181 CcRosCacheSegmentDecRefCount ( CacheSegment
);
183 KeReleaseSpinLock(&CacheSegment
->Bcb
->BcbLock
, oldIrql
);
184 KeReleaseGuardedMutex(&ViewLock
);
192 CcRosFlushDirtyPages(ULONG Target
, PULONG Count
)
194 PLIST_ENTRY current_entry
;
195 PCACHE_SEGMENT current
;
196 ULONG PagesPerSegment
;
199 static ULONG WriteCount
[4] = {0, 0, 0, 0};
202 DPRINT("CcRosFlushDirtyPages(Target %d)\n", Target
);
206 KeAcquireGuardedMutex(&ViewLock
);
208 WriteCount
[0] = WriteCount
[1];
209 WriteCount
[1] = WriteCount
[2];
210 WriteCount
[2] = WriteCount
[3];
213 NewTarget
= WriteCount
[0] + WriteCount
[1] + WriteCount
[2];
215 if (NewTarget
< DirtyPageCount
)
217 NewTarget
= (DirtyPageCount
- NewTarget
+ 3) / 4;
218 WriteCount
[0] += NewTarget
;
219 WriteCount
[1] += NewTarget
;
220 WriteCount
[2] += NewTarget
;
221 WriteCount
[3] += NewTarget
;
224 NewTarget
= WriteCount
[0];
226 Target
= max(NewTarget
, Target
);
228 current_entry
= DirtySegmentListHead
.Flink
;
229 if (current_entry
== &DirtySegmentListHead
)
231 DPRINT("No Dirty pages\n");
234 while (current_entry
!= &DirtySegmentListHead
&& Target
> 0)
236 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
237 DirtySegmentListEntry
);
238 current_entry
= current_entry
->Flink
;
240 Locked
= current
->Bcb
->Callbacks
->AcquireForLazyWrite(
241 current
->Bcb
->LazyWriteContext
, FALSE
);
247 Locked
= ExTryToAcquirePushLockExclusive(¤t
->Lock
);
250 current
->Bcb
->Callbacks
->ReleaseFromLazyWrite(
251 current
->Bcb
->LazyWriteContext
);
256 ASSERT(current
->Dirty
);
257 if (current
->ReferenceCount
> 1)
259 ExReleasePushLock(¤t
->Lock
);
260 current
->Bcb
->Callbacks
->ReleaseFromLazyWrite(
261 current
->Bcb
->LazyWriteContext
);
265 PagesPerSegment
= current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
267 KeReleaseGuardedMutex(&ViewLock
);
269 Status
= CcRosFlushCacheSegment(current
);
271 ExReleasePushLock(¤t
->Lock
);
272 current
->Bcb
->Callbacks
->ReleaseFromLazyWrite(
273 current
->Bcb
->LazyWriteContext
);
275 if (!NT_SUCCESS(Status
) && (Status
!= STATUS_END_OF_FILE
))
277 DPRINT1("CC: Failed to flush cache segment.\n");
281 (*Count
) += PagesPerSegment
;
282 Target
-= PagesPerSegment
;
285 KeAcquireGuardedMutex(&ViewLock
);
286 current_entry
= DirtySegmentListHead
.Flink
;
289 if (*Count
< NewTarget
)
291 WriteCount
[1] += (NewTarget
- *Count
);
294 KeReleaseGuardedMutex(&ViewLock
);
296 DPRINT("CcRosFlushDirtyPages() finished\n");
297 return(STATUS_SUCCESS
);
301 CcRosTrimCache(ULONG Target
, ULONG Priority
, PULONG NrFreed
)
303 * FUNCTION: Try to free some memory from the file cache.
305 * Target - The number of pages to be freed.
306 * Priority - The priority of free (currently unused).
307 * NrFreed - Points to a variable where the number of pages
308 * actually freed is returned.
311 PLIST_ENTRY current_entry
;
312 PCACHE_SEGMENT current
, last
= NULL
;
313 ULONG PagesPerSegment
;
318 DPRINT("CcRosTrimCache(Target %d)\n", Target
);
322 InitializeListHead(&FreeList
);
324 KeAcquireGuardedMutex(&ViewLock
);
325 current_entry
= CacheSegmentLRUListHead
.Flink
;
326 while (current_entry
!= &CacheSegmentLRUListHead
&& Target
> 0)
328 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
329 CacheSegmentLRUListEntry
);
330 current_entry
= current_entry
->Flink
;
332 KeAcquireSpinLock(¤t
->Bcb
->BcbLock
, &oldIrql
);
333 if (current
->ReferenceCount
== 0)
335 RemoveEntryList(¤t
->BcbSegmentListEntry
);
336 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
337 RemoveEntryList(¤t
->CacheSegmentListEntry
);
338 RemoveEntryList(¤t
->CacheSegmentLRUListEntry
);
339 InsertHeadList(&FreeList
, ¤t
->BcbSegmentListEntry
);
340 PagesPerSegment
= current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
341 PagesFreed
= min(PagesPerSegment
, Target
);
342 Target
-= PagesFreed
;
343 (*NrFreed
) += PagesFreed
;
347 if (last
!= current
&& current
->MappedCount
> 0 && !current
->Dirty
&& !current
->PageOut
)
352 CcRosCacheSegmentIncRefCount(current
);
354 current
->PageOut
= TRUE
;
355 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
356 KeReleaseGuardedMutex(&ViewLock
);
357 for (i
= 0; i
< current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
; i
++)
360 Page
= (PFN_TYPE
)(MmGetPhysicalAddress((char*)current
->BaseAddress
+ i
* PAGE_SIZE
).QuadPart
>> PAGE_SHIFT
);
361 Status
= MmPageOutPhysicalAddress(Page
);
362 if (!NT_SUCCESS(Status
))
367 KeAcquireGuardedMutex(&ViewLock
);
368 KeAcquireSpinLock(¤t
->Bcb
->BcbLock
, &oldIrql
);
369 CcRosCacheSegmentDecRefCount(current
);
370 current
->PageOut
= FALSE
;
371 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
372 current_entry
= ¤t
->CacheSegmentLRUListEntry
;
375 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
378 KeReleaseGuardedMutex(&ViewLock
);
380 while (!IsListEmpty(&FreeList
))
382 current_entry
= RemoveHeadList(&FreeList
);
383 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
384 BcbSegmentListEntry
);
385 CcRosInternalFreeCacheSegment(current
);
388 DPRINT("CcRosTrimCache() finished\n");
389 return(STATUS_SUCCESS
);
394 CcRosReleaseCacheSegment(PBCB Bcb
,
395 PCACHE_SEGMENT CacheSeg
,
400 BOOLEAN WasDirty
= CacheSeg
->Dirty
;
405 DPRINT("CcReleaseCacheSegment(Bcb 0x%p, CacheSeg 0x%p, Valid %d)\n",
406 Bcb
, CacheSeg
, Valid
);
408 CacheSeg
->Valid
= Valid
;
409 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| Dirty
;
411 KeAcquireGuardedMutex(&ViewLock
);
412 if (!WasDirty
&& CacheSeg
->Dirty
)
414 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
415 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
417 RemoveEntryList(&CacheSeg
->CacheSegmentLRUListEntry
);
418 InsertTailList(&CacheSegmentLRUListHead
, &CacheSeg
->CacheSegmentLRUListEntry
);
422 CacheSeg
->MappedCount
++;
424 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
425 CcRosCacheSegmentDecRefCount(CacheSeg
);
426 if (Mapped
&& CacheSeg
->MappedCount
== 1)
428 CcRosCacheSegmentIncRefCount(CacheSeg
);
430 if (!WasDirty
&& CacheSeg
->Dirty
)
432 CcRosCacheSegmentIncRefCount(CacheSeg
);
434 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
435 KeReleaseGuardedMutex(&ViewLock
);
436 ExReleasePushLock(&CacheSeg
->Lock
);
438 return(STATUS_SUCCESS
);
441 /* Returns with Cache Segment Lock Held! */
444 CcRosLookupCacheSegment(PBCB Bcb
, ULONG FileOffset
)
446 PLIST_ENTRY current_entry
;
447 PCACHE_SEGMENT current
;
452 DPRINT("CcRosLookupCacheSegment(Bcb -x%p, FileOffset %d)\n", Bcb
, FileOffset
);
454 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
455 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
456 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
458 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
459 BcbSegmentListEntry
);
460 if (current
->FileOffset
<= FileOffset
&&
461 (current
->FileOffset
+ Bcb
->CacheSegmentSize
) > FileOffset
)
463 CcRosCacheSegmentIncRefCount(current
);
464 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
465 ExAcquirePushLockExclusive(¤t
->Lock
);
468 current_entry
= current_entry
->Flink
;
470 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
476 CcRosMarkDirtyCacheSegment(PBCB Bcb
, ULONG FileOffset
)
478 PCACHE_SEGMENT CacheSeg
;
483 DPRINT("CcRosMarkDirtyCacheSegment(Bcb 0x%p, FileOffset %d)\n", Bcb
, FileOffset
);
485 CacheSeg
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
486 if (CacheSeg
== NULL
)
488 KeBugCheck(CACHE_MANAGER
);
490 if (!CacheSeg
->Dirty
)
492 KeAcquireGuardedMutex(&ViewLock
);
493 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
494 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
495 KeReleaseGuardedMutex(&ViewLock
);
499 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
500 CcRosCacheSegmentDecRefCount(CacheSeg
);
501 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
505 CacheSeg
->Dirty
= TRUE
;
506 ExReleasePushLock(&CacheSeg
->Lock
);
508 return(STATUS_SUCCESS
);
513 CcRosUnmapCacheSegment(PBCB Bcb
, ULONG FileOffset
, BOOLEAN NowDirty
)
515 PCACHE_SEGMENT CacheSeg
;
521 DPRINT("CcRosUnmapCacheSegment(Bcb 0x%p, FileOffset %d, NowDirty %d)\n",
522 Bcb
, FileOffset
, NowDirty
);
524 CacheSeg
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
525 if (CacheSeg
== NULL
)
527 return(STATUS_UNSUCCESSFUL
);
530 WasDirty
= CacheSeg
->Dirty
;
531 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| NowDirty
;
533 CacheSeg
->MappedCount
--;
535 if (!WasDirty
&& NowDirty
)
537 KeAcquireGuardedMutex(&ViewLock
);
538 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
539 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
540 KeReleaseGuardedMutex(&ViewLock
);
543 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
544 CcRosCacheSegmentDecRefCount(CacheSeg
);
545 if (!WasDirty
&& NowDirty
)
547 CcRosCacheSegmentIncRefCount(CacheSeg
);
549 if (CacheSeg
->MappedCount
== 0)
551 CcRosCacheSegmentDecRefCount(CacheSeg
);
553 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
555 ExReleasePushLock(&CacheSeg
->Lock
);
556 return(STATUS_SUCCESS
);
560 CcRosCreateCacheSegment(PBCB Bcb
,
562 PCACHE_SEGMENT
* CacheSeg
)
564 PCACHE_SEGMENT current
;
565 PCACHE_SEGMENT previous
;
566 PLIST_ENTRY current_entry
;
570 ULONG StartingOffset
;
572 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
576 DPRINT("CcRosCreateCacheSegment()\n");
578 BoundaryAddressMultiple
.QuadPart
= 0;
579 if (FileOffset
>= Bcb
->FileSize
.u
.LowPart
)
582 return STATUS_INVALID_PARAMETER
;
585 current
= ExAllocateFromNPagedLookasideList(&CacheSegLookasideList
);
586 current
->Valid
= FALSE
;
587 current
->Dirty
= FALSE
;
588 current
->PageOut
= FALSE
;
589 current
->FileOffset
= ROUND_DOWN(FileOffset
, Bcb
->CacheSegmentSize
);
591 #if defined(DBG) || defined(KDBG)
594 DPRINT1("CacheMap 0x%p: new Cache Segment: 0x%p\n", Bcb
, current
);
597 current
->MappedCount
= 0;
598 current
->DirtySegmentListEntry
.Flink
= NULL
;
599 current
->DirtySegmentListEntry
.Blink
= NULL
;
600 current
->ReferenceCount
= 1;
601 ExInitializePushLock((PULONG_PTR
)¤t
->Lock
);
602 ExAcquirePushLockExclusive(¤t
->Lock
);
603 KeAcquireGuardedMutex(&ViewLock
);
606 /* There is window between the call to CcRosLookupCacheSegment
607 * and CcRosCreateCacheSegment. We must check if a segment on
608 * the fileoffset exist. If there exist a segment, we release
609 * our new created segment and return the existing one.
611 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
612 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
614 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
616 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
617 BcbSegmentListEntry
);
618 if (current
->FileOffset
<= FileOffset
&&
619 (current
->FileOffset
+ Bcb
->CacheSegmentSize
) > FileOffset
)
621 CcRosCacheSegmentIncRefCount(current
);
622 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
623 #if defined(DBG) || defined(KDBG)
626 DPRINT1("CacheMap 0x%p: deleting newly created Cache Segment 0x%p ( found existing one 0x%p )\n",
632 ExReleasePushLock(&(*CacheSeg
)->Lock
);
633 KeReleaseGuardedMutex(&ViewLock
);
634 ExFreeToNPagedLookasideList(&CacheSegLookasideList
, *CacheSeg
);
636 ExAcquirePushLockExclusive(¤t
->Lock
);
637 return STATUS_SUCCESS
;
639 if (current
->FileOffset
< FileOffset
)
641 if (previous
== NULL
)
647 if (previous
->FileOffset
< current
->FileOffset
)
653 current_entry
= current_entry
->Flink
;
655 /* There was no existing segment. */
659 InsertHeadList(&previous
->BcbSegmentListEntry
, ¤t
->BcbSegmentListEntry
);
663 InsertHeadList(&Bcb
->BcbSegmentListHead
, ¤t
->BcbSegmentListEntry
);
665 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
666 InsertTailList(&CacheSegmentListHead
, ¤t
->CacheSegmentListEntry
);
667 InsertTailList(&CacheSegmentLRUListHead
, ¤t
->CacheSegmentLRUListEntry
);
668 KeReleaseGuardedMutex(&ViewLock
);
670 KeAcquireSpinLock(&CiCacheSegMappingRegionLock
, &oldIrql
);
672 StartingOffset
= RtlFindClearBitsAndSet(&CiCacheSegMappingRegionAllocMap
, Bcb
->CacheSegmentSize
/ PAGE_SIZE
, CiCacheSegMappingRegionHint
);
674 if (StartingOffset
== 0xffffffff)
676 DPRINT1("Out of CacheSeg mapping space\n");
677 KeBugCheck(CACHE_MANAGER
);
680 current
->BaseAddress
= CiCacheSegMappingRegionBase
+ StartingOffset
* PAGE_SIZE
;
682 if (CiCacheSegMappingRegionHint
== StartingOffset
)
684 CiCacheSegMappingRegionHint
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
687 KeReleaseSpinLock(&CiCacheSegMappingRegionLock
, oldIrql
);
689 MmLockAddressSpace(MmGetKernelAddressSpace());
690 current
->BaseAddress
= NULL
;
691 Status
= MmCreateMemoryArea(MmGetKernelAddressSpace(),
692 MEMORY_AREA_CACHE_SEGMENT
,
693 ¤t
->BaseAddress
,
694 Bcb
->CacheSegmentSize
,
696 (PMEMORY_AREA
*)¤t
->MemoryArea
,
699 BoundaryAddressMultiple
);
700 MmUnlockAddressSpace(MmGetKernelAddressSpace());
701 if (!NT_SUCCESS(Status
))
703 KeBugCheck(CACHE_MANAGER
);
707 /* Create a virtual mapping for this memory area */
708 MmMapMemoryArea(current
->BaseAddress
, Bcb
->CacheSegmentSize
,
709 MC_CACHE
, PAGE_READWRITE
);
711 return(STATUS_SUCCESS
);
716 CcRosGetCacheSegmentChain(PBCB Bcb
,
719 PCACHE_SEGMENT
* CacheSeg
)
721 PCACHE_SEGMENT current
;
723 PCACHE_SEGMENT
* CacheSegList
;
724 PCACHE_SEGMENT Previous
= NULL
;
728 DPRINT("CcRosGetCacheSegmentChain()\n");
730 Length
= ROUND_UP(Length
, Bcb
->CacheSegmentSize
);
732 #if defined(__GNUC__)
733 CacheSegList
= alloca(sizeof(PCACHE_SEGMENT
) *
734 (Length
/ Bcb
->CacheSegmentSize
));
735 #elif defined(_MSC_VER)
736 CacheSegList
= _alloca(sizeof(PCACHE_SEGMENT
) *
737 (Length
/ Bcb
->CacheSegmentSize
));
739 #error Unknown compiler for alloca intrinsic stack allocation "function"
743 * Look for a cache segment already mapping the same data.
745 for (i
= 0; i
< (Length
/ Bcb
->CacheSegmentSize
); i
++)
747 ULONG CurrentOffset
= FileOffset
+ (i
* Bcb
->CacheSegmentSize
);
748 current
= CcRosLookupCacheSegment(Bcb
, CurrentOffset
);
751 CacheSegList
[i
] = current
;
755 CcRosCreateCacheSegment(Bcb
, CurrentOffset
, ¤t
);
756 CacheSegList
[i
] = current
;
760 for (i
= 0; i
< (Length
/ Bcb
->CacheSegmentSize
); i
++)
764 *CacheSeg
= CacheSegList
[i
];
765 Previous
= CacheSegList
[i
];
769 Previous
->NextInChain
= CacheSegList
[i
];
770 Previous
= CacheSegList
[i
];
774 Previous
->NextInChain
= NULL
;
776 return(STATUS_SUCCESS
);
781 CcRosGetCacheSegment(PBCB Bcb
,
786 PCACHE_SEGMENT
* CacheSeg
)
788 PCACHE_SEGMENT current
;
793 DPRINT("CcRosGetCacheSegment()\n");
796 * Look for a cache segment already mapping the same data.
798 current
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
802 * Otherwise create a new segment.
804 Status
= CcRosCreateCacheSegment(Bcb
, FileOffset
, ¤t
);
805 if (!NT_SUCCESS(Status
))
811 * Return information about the segment to the caller.
813 *UptoDate
= current
->Valid
;
814 *BaseAddress
= current
->BaseAddress
;
815 DPRINT("*BaseAddress 0x%.8X\n", *BaseAddress
);
817 *BaseOffset
= current
->FileOffset
;
818 return(STATUS_SUCCESS
);
822 CcRosRequestCacheSegment(PBCB Bcb
,
826 PCACHE_SEGMENT
* CacheSeg
)
828 * FUNCTION: Request a page mapping for a BCB
835 if ((FileOffset
% Bcb
->CacheSegmentSize
) != 0)
837 DPRINT1("Bad fileoffset %x should be multiple of %x",
838 FileOffset
, Bcb
->CacheSegmentSize
);
839 KeBugCheck(CACHE_MANAGER
);
842 return(CcRosGetCacheSegment(Bcb
,
852 CcFreeCachePage(PVOID Context
, MEMORY_AREA
* MemoryArea
, PVOID Address
,
853 PFN_TYPE Page
, SWAPENTRY SwapEntry
, BOOLEAN Dirty
)
855 ASSERT(SwapEntry
== 0);
858 MmReleasePageMemoryConsumer(MC_CACHE
, Page
);
863 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg
)
865 * FUNCTION: Releases a cache segment associated with a BCB
875 DPRINT("Freeing cache segment 0x%p\n", CacheSeg
);
876 #if defined(DBG) || defined(KDBG)
877 if ( CacheSeg
->Bcb
->Trace
)
879 DPRINT1("CacheMap 0x%p: deleting Cache Segment: 0x%p\n", CacheSeg
->Bcb
, CacheSeg
);
883 RegionSize
= CacheSeg
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
885 /* Unmap all the pages. */
886 for (i
= 0; i
< RegionSize
; i
++)
888 MmDeleteVirtualMapping(NULL
,
889 CacheSeg
->BaseAddress
+ (i
* PAGE_SIZE
),
893 MmReleasePageMemoryConsumer(MC_CACHE
, Page
);
896 KeAcquireSpinLock(&CiCacheSegMappingRegionLock
, &oldIrql
);
897 /* Deallocate all the pages used. */
898 Base
= (ULONG
)(CacheSeg
->BaseAddress
- CiCacheSegMappingRegionBase
) / PAGE_SIZE
;
900 RtlClearBits(&CiCacheSegMappingRegionAllocMap
, Base
, RegionSize
);
902 CiCacheSegMappingRegionHint
= min (CiCacheSegMappingRegionHint
, Base
);
904 KeReleaseSpinLock(&CiCacheSegMappingRegionLock
, oldIrql
);
906 MmLockAddressSpace(MmGetKernelAddressSpace());
907 MmFreeMemoryArea(MmGetKernelAddressSpace(),
908 CacheSeg
->MemoryArea
,
911 MmUnlockAddressSpace(MmGetKernelAddressSpace());
913 ExFreeToNPagedLookasideList(&CacheSegLookasideList
, CacheSeg
);
914 return(STATUS_SUCCESS
);
919 CcRosFreeCacheSegment(PBCB Bcb
, PCACHE_SEGMENT CacheSeg
)
926 DPRINT("CcRosFreeCacheSegment(Bcb 0x%p, CacheSeg 0x%p)\n",
929 KeAcquireGuardedMutex(&ViewLock
);
930 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
931 RemoveEntryList(&CacheSeg
->BcbSegmentListEntry
);
932 RemoveEntryList(&CacheSeg
->CacheSegmentListEntry
);
933 RemoveEntryList(&CacheSeg
->CacheSegmentLRUListEntry
);
936 RemoveEntryList(&CacheSeg
->DirtySegmentListEntry
);
937 DirtyPageCount
-= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
940 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
941 KeReleaseGuardedMutex(&ViewLock
);
943 Status
= CcRosInternalFreeCacheSegment(CacheSeg
);
951 CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointers
,
952 IN PLARGE_INTEGER FileOffset OPTIONAL
,
954 OUT PIO_STATUS_BLOCK IoStatus
)
957 LARGE_INTEGER Offset
;
958 PCACHE_SEGMENT current
;
962 DPRINT("CcFlushCache(SectionObjectPointers 0x%p, FileOffset 0x%p, Length %d, IoStatus 0x%p)\n",
963 SectionObjectPointers
, FileOffset
, Length
, IoStatus
);
965 if (SectionObjectPointers
&& SectionObjectPointers
->SharedCacheMap
)
967 Bcb
= (PBCB
)SectionObjectPointers
->SharedCacheMap
;
971 Offset
= *FileOffset
;
975 Offset
.QuadPart
= (LONGLONG
)0;
976 Length
= Bcb
->FileSize
.u
.LowPart
;
981 IoStatus
->Status
= STATUS_SUCCESS
;
982 IoStatus
->Information
= 0;
987 current
= CcRosLookupCacheSegment (Bcb
, Offset
.u
.LowPart
);
992 Status
= CcRosFlushCacheSegment(current
);
993 if (!NT_SUCCESS(Status
) && IoStatus
!= NULL
)
995 IoStatus
->Status
= Status
;
998 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
999 ExReleasePushLock(¤t
->Lock
);
1000 CcRosCacheSegmentDecRefCount(current
);
1001 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
1004 Offset
.QuadPart
+= Bcb
->CacheSegmentSize
;
1005 if (Length
> Bcb
->CacheSegmentSize
)
1007 Length
-= Bcb
->CacheSegmentSize
;
1019 IoStatus
->Status
= STATUS_INVALID_PARAMETER
;
1026 CcRosDeleteFileCache(PFILE_OBJECT FileObject
, PBCB Bcb
)
1028 * FUNCTION: Releases the BCB associated with a file object
1031 PLIST_ENTRY current_entry
;
1032 PCACHE_SEGMENT current
;
1034 LIST_ENTRY FreeList
;
1040 KeReleaseGuardedMutex(&ViewLock
);
1042 CcFlushCache(FileObject
->SectionObjectPointer
, NULL
, 0, NULL
);
1044 KeAcquireGuardedMutex(&ViewLock
);
1046 if (Bcb
->RefCount
== 0)
1048 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
1050 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1051 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1054 FileObject
->SectionObjectPointer
->SharedCacheMap
= NULL
;
1057 * Release all cache segments.
1059 InitializeListHead(&FreeList
);
1060 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
1061 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
1062 while (!IsListEmpty(&Bcb
->BcbSegmentListHead
))
1064 current_entry
= RemoveTailList(&Bcb
->BcbSegmentListHead
);
1065 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, BcbSegmentListEntry
);
1066 RemoveEntryList(¤t
->CacheSegmentListEntry
);
1067 RemoveEntryList(¤t
->CacheSegmentLRUListEntry
);
1070 RemoveEntryList(¤t
->DirtySegmentListEntry
);
1071 DirtyPageCount
-= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
1072 DPRINT1("Freeing dirty segment\n");
1074 InsertHeadList(&FreeList
, ¤t
->BcbSegmentListEntry
);
1076 #if defined(DBG) || defined(KDBG)
1079 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
1081 KeReleaseGuardedMutex(&ViewLock
);
1082 ObDereferenceObject (Bcb
->FileObject
);
1084 while (!IsListEmpty(&FreeList
))
1086 current_entry
= RemoveTailList(&FreeList
);
1087 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, BcbSegmentListEntry
);
1088 Status
= CcRosInternalFreeCacheSegment(current
);
1090 ExFreeToNPagedLookasideList(&BcbLookasideList
, Bcb
);
1091 KeAcquireGuardedMutex(&ViewLock
);
1093 return(STATUS_SUCCESS
);
1098 CcRosReferenceCache(PFILE_OBJECT FileObject
)
1101 KeAcquireGuardedMutex(&ViewLock
);
1102 Bcb
= (PBCB
)FileObject
->SectionObjectPointer
->SharedCacheMap
;
1104 if (Bcb
->RefCount
== 0)
1106 ASSERT(Bcb
->BcbRemoveListEntry
.Flink
!= NULL
);
1107 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1108 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1113 ASSERT(Bcb
->BcbRemoveListEntry
.Flink
== NULL
);
1116 KeReleaseGuardedMutex(&ViewLock
);
1121 CcRosSetRemoveOnClose(PSECTION_OBJECT_POINTERS SectionObjectPointer
)
1124 DPRINT("CcRosSetRemoveOnClose()\n");
1125 KeAcquireGuardedMutex(&ViewLock
);
1126 Bcb
= (PBCB
)SectionObjectPointer
->SharedCacheMap
;
1129 Bcb
->RemoveOnClose
= TRUE
;
1130 if (Bcb
->RefCount
== 0)
1132 CcRosDeleteFileCache(Bcb
->FileObject
, Bcb
);
1135 KeReleaseGuardedMutex(&ViewLock
);
1141 CcRosDereferenceCache(PFILE_OBJECT FileObject
)
1144 KeAcquireGuardedMutex(&ViewLock
);
1145 Bcb
= (PBCB
)FileObject
->SectionObjectPointer
->SharedCacheMap
;
1147 if (Bcb
->RefCount
> 0)
1150 if (Bcb
->RefCount
== 0)
1152 MmFreeSectionSegments(Bcb
->FileObject
);
1153 if (Bcb
->RemoveOnClose
)
1155 CcRosDeleteFileCache(FileObject
, Bcb
);
1159 Bcb
->TimeStamp
= CcTimeStamp
;
1160 InsertHeadList(&ClosedListHead
, &Bcb
->BcbRemoveListEntry
);
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 if (Bcb
->RemoveOnClose
)
1192 CcRosDeleteFileCache(FileObject
, Bcb
);
1196 Bcb
->TimeStamp
= CcTimeStamp
;
1197 InsertHeadList(&ClosedListHead
, &Bcb
->BcbRemoveListEntry
);
1203 KeReleaseGuardedMutex(&ViewLock
);
1204 return(STATUS_SUCCESS
);
1209 CcTryToInitializeFileCache(PFILE_OBJECT FileObject
)
1214 KeAcquireGuardedMutex(&ViewLock
);
1216 ASSERT(FileObject
->SectionObjectPointer
);
1217 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1220 Status
= STATUS_UNSUCCESSFUL
;
1224 if (FileObject
->PrivateCacheMap
== NULL
)
1226 FileObject
->PrivateCacheMap
= Bcb
;
1229 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
1231 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1232 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1234 Status
= STATUS_SUCCESS
;
1236 KeReleaseGuardedMutex(&ViewLock
);
1243 CcRosInitializeFileCache(PFILE_OBJECT FileObject
,
1244 ULONG CacheSegmentSize
,
1245 PCACHE_MANAGER_CALLBACKS CallBacks
,
1246 PVOID LazyWriterContext
)
1248 * FUNCTION: Initializes a BCB for a file object
1253 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1254 DPRINT("CcRosInitializeFileCache(FileObject 0x%p, Bcb 0x%p, CacheSegmentSize %d)\n",
1255 FileObject
, Bcb
, CacheSegmentSize
);
1257 KeAcquireGuardedMutex(&ViewLock
);
1260 Bcb
= ExAllocateFromNPagedLookasideList(&BcbLookasideList
);
1263 KeReleaseGuardedMutex(&ViewLock
);
1264 return(STATUS_UNSUCCESSFUL
);
1266 memset(Bcb
, 0, sizeof(BCB
));
1267 ObReferenceObjectByPointer(FileObject
,
1271 Bcb
->FileObject
= FileObject
;
1272 Bcb
->CacheSegmentSize
= CacheSegmentSize
;
1273 Bcb
->Callbacks
= CallBacks
;
1274 Bcb
->LazyWriteContext
= LazyWriterContext
;
1275 if (FileObject
->FsContext
)
1277 Bcb
->AllocationSize
=
1278 ((PFSRTL_COMMON_FCB_HEADER
)FileObject
->FsContext
)->AllocationSize
;
1280 ((PFSRTL_COMMON_FCB_HEADER
)FileObject
->FsContext
)->FileSize
;
1282 KeInitializeSpinLock(&Bcb
->BcbLock
);
1283 InitializeListHead(&Bcb
->BcbSegmentListHead
);
1284 FileObject
->SectionObjectPointer
->SharedCacheMap
= Bcb
;
1286 if (FileObject
->PrivateCacheMap
== NULL
)
1288 FileObject
->PrivateCacheMap
= Bcb
;
1291 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
1293 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1294 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1296 KeReleaseGuardedMutex(&ViewLock
);
1298 return(STATUS_SUCCESS
);
1304 PFILE_OBJECT STDCALL
1305 CcGetFileObjectFromSectionPtrs(IN PSECTION_OBJECT_POINTERS SectionObjectPointers
)
1308 if (SectionObjectPointers
&& SectionObjectPointers
->SharedCacheMap
)
1310 Bcb
= (PBCB
)SectionObjectPointers
->SharedCacheMap
;
1312 return Bcb
->FileObject
;
1318 CmLazyCloseThreadMain(PVOID Ignored
)
1320 LARGE_INTEGER Timeout
;
1321 PLIST_ENTRY current_entry
;
1323 ULONG RemoveTimeStamp
;
1326 KeQuerySystemTime (&Timeout
);
1330 Timeout
.QuadPart
+= (LONGLONG
)100000000; // 10sec
1331 Status
= KeWaitForSingleObject(&LazyCloseThreadEvent
,
1337 DPRINT("LazyCloseThreadMain %d\n", CcTimeStamp
);
1339 if (!NT_SUCCESS(Status
))
1341 DbgPrint("LazyCloseThread: Wait failed\n");
1342 KeBugCheck(CACHE_MANAGER
);
1345 if (LazyCloseThreadShouldTerminate
)
1347 DbgPrint("LazyCloseThread: Terminating\n");
1351 KeAcquireGuardedMutex(&ViewLock
);
1353 if (CcTimeStamp
>= 30)
1355 RemoveTimeStamp
= CcTimeStamp
- 30; /* 5min = 10sec * 30 */
1356 while (!IsListEmpty(&ClosedListHead
))
1358 current_entry
= ClosedListHead
.Blink
;
1359 current
= CONTAINING_RECORD(current_entry
, BCB
, BcbRemoveListEntry
);
1360 if (current
->TimeStamp
>= RemoveTimeStamp
)
1364 CcRosDeleteFileCache(current
->FileObject
, current
);
1367 KeReleaseGuardedMutex(&ViewLock
);
1379 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
1384 DPRINT("CcInitView()\n");
1386 BoundaryAddressMultiple
.QuadPart
= 0;
1387 CiCacheSegMappingRegionHint
= 0;
1388 CiCacheSegMappingRegionBase
= NULL
;
1390 MmLockAddressSpace(MmGetKernelAddressSpace());
1392 Status
= MmCreateMemoryArea(MmGetKernelAddressSpace(),
1393 MEMORY_AREA_CACHE_SEGMENT
,
1394 &CiCacheSegMappingRegionBase
,
1395 CI_CACHESEG_MAPPING_REGION_SIZE
,
1400 BoundaryAddressMultiple
);
1401 MmUnlockAddressSpace(MmGetKernelAddressSpace());
1402 if (!NT_SUCCESS(Status
))
1404 KeBugCheck(CACHE_MANAGER
);
1407 Buffer
= ExAllocatePool(NonPagedPool
, CI_CACHESEG_MAPPING_REGION_SIZE
/ (PAGE_SIZE
* 8));
1409 RtlInitializeBitMap(&CiCacheSegMappingRegionAllocMap
, Buffer
, CI_CACHESEG_MAPPING_REGION_SIZE
/ PAGE_SIZE
);
1410 RtlClearAllBits(&CiCacheSegMappingRegionAllocMap
);
1412 KeInitializeSpinLock(&CiCacheSegMappingRegionLock
);
1414 InitializeListHead(&CacheSegmentListHead
);
1415 InitializeListHead(&DirtySegmentListHead
);
1416 InitializeListHead(&CacheSegmentLRUListHead
);
1417 InitializeListHead(&ClosedListHead
);
1418 KeInitializeGuardedMutex(&ViewLock
);
1419 ExInitializeNPagedLookasideList (&iBcbLookasideList
,
1423 sizeof(INTERNAL_BCB
),
1426 ExInitializeNPagedLookasideList (&BcbLookasideList
,
1433 ExInitializeNPagedLookasideList (&CacheSegLookasideList
,
1437 sizeof(CACHE_SEGMENT
),
1441 MmInitializeMemoryConsumer(MC_CACHE
, CcRosTrimCache
);
1443 CcInitCacheZeroPage();
1446 LazyCloseThreadShouldTerminate
= FALSE
;
1447 KeInitializeEvent (&LazyCloseThreadEvent
, SynchronizationEvent
, FALSE
);
1448 Status
= PsCreateSystemThread(&LazyCloseThreadHandle
,
1453 (PKSTART_ROUTINE
)CmLazyCloseThreadMain
,
1455 if (NT_SUCCESS(Status
))
1457 Priority
= LOW_REALTIME_PRIORITY
;
1458 NtSetInformationThread(LazyCloseThreadHandle
,