3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/cc/view.c
6 * PURPOSE: Cache manager
8 * PROGRAMMERS: David Welch (welch@mcmail.com)
11 /* NOTES **********************************************************************
13 * This is not the NT implementation of a file cache nor anything much like
16 * The general procedure for a filesystem to implement a read or write
17 * dispatch routine is as follows
19 * (1) If caching for the FCB hasn't been initiated then so do by calling
20 * CcInitializeFileCache.
22 * (2) For each 4k region which is being read or written obtain a cache page
23 * by calling CcRequestCachePage.
25 * (3) If either the page is being read or not completely written, and it is
26 * not up to date then read its data from the underlying medium. If the read
27 * fails then call CcReleaseCachePage with VALID as FALSE and return a error.
29 * (4) Copy the data into or out of the page as necessary.
31 * (5) Release the cache page
33 /* INCLUDES ******************************************************************/
37 #include <internal/debug.h>
39 /* GLOBALS *******************************************************************/
42 * If CACHE_BITMAP is defined, the cache manager uses one large memory region
43 * within the kernel address space and allocate/deallocate space from this block
44 * over a bitmap. If CACHE_BITMAP is used, the size of the mdl mapping region
45 * must be reduced (ntoskrnl\mm\mdl.c, MI_MDLMAPPING_REGION_SIZE).
47 //#define CACHE_BITMAP
49 static LIST_ENTRY DirtySegmentListHead
;
50 static LIST_ENTRY CacheSegmentListHead
;
51 static LIST_ENTRY CacheSegmentLRUListHead
;
52 static LIST_ENTRY ClosedListHead
;
53 ULONG DirtyPageCount
=0;
58 #define CI_CACHESEG_MAPPING_REGION_SIZE (128*1024*1024)
60 static PVOID CiCacheSegMappingRegionBase
= NULL
;
61 static RTL_BITMAP CiCacheSegMappingRegionAllocMap
;
62 static ULONG CiCacheSegMappingRegionHint
;
63 static KSPIN_LOCK CiCacheSegMappingRegionLock
;
66 NPAGED_LOOKASIDE_LIST iBcbLookasideList
;
67 static NPAGED_LOOKASIDE_LIST BcbLookasideList
;
68 static NPAGED_LOOKASIDE_LIST CacheSegLookasideList
;
70 static ULONG CcTimeStamp
;
71 static KEVENT LazyCloseThreadEvent
;
72 static HANDLE LazyCloseThreadHandle
;
73 static CLIENT_ID LazyCloseThreadId
;
74 static volatile BOOLEAN LazyCloseThreadShouldTerminate
;
77 /* void * alloca(size_t size); */
78 #elif defined(_MSC_VER)
79 void* _alloca(size_t size
);
81 #error Unknown compiler for alloca intrinsic stack allocation "function"
84 #if defined(DBG) || defined(KDBG)
85 static void CcRosCacheSegmentIncRefCount_ ( PCACHE_SEGMENT cs
, const char* file
, int line
)
90 DbgPrint("(%s:%i) CacheSegment %p ++RefCount=%d, Dirty %d, PageOut %d\n",
91 file
, line
, cs
, cs
->ReferenceCount
, cs
->Dirty
, cs
->PageOut
);
94 static void CcRosCacheSegmentDecRefCount_ ( PCACHE_SEGMENT cs
, const char* file
, int line
)
99 DbgPrint("(%s:%i) CacheSegment %p --RefCount=%d, Dirty %d, PageOut %d\n",
100 file
, line
, cs
, cs
->ReferenceCount
, cs
->Dirty
, cs
->PageOut
);
103 #define CcRosCacheSegmentIncRefCount(cs) CcRosCacheSegmentIncRefCount_(cs,__FILE__,__LINE__)
104 #define CcRosCacheSegmentDecRefCount(cs) CcRosCacheSegmentDecRefCount_(cs,__FILE__,__LINE__)
106 #define CcRosCacheSegmentIncRefCount(cs) (++((cs)->ReferenceCount))
107 #define CcRosCacheSegmentDecRefCount(cs) (--((cs)->ReferenceCount))
111 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg
);
115 CcTryToAcquireBrokenMutex(PFAST_MUTEX FastMutex
)
117 KeEnterCriticalRegion();
118 if (InterlockedCompareExchange(&FastMutex
->Count
, 0, 1) == 1)
120 FastMutex
->Owner
= KeGetCurrentThread();
125 KeLeaveCriticalRegion();
130 /* FUNCTIONS *****************************************************************/
138 #if defined(DBG) || defined(KDBG)
140 PLIST_ENTRY current_entry
;
141 PCACHE_SEGMENT current
;
150 DPRINT1("Enabling Tracing for CacheMap 0x%p:\n", Bcb
);
152 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&ViewLock
);
153 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldirql
);
155 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
156 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
158 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, BcbSegmentListEntry
);
159 current_entry
= current_entry
->Flink
;
161 DPRINT1(" CacheSegment 0x%p enabled, RefCount %d, Dirty %d, PageOut %d\n",
162 current
, current
->ReferenceCount
, current
->Dirty
, current
->PageOut
);
164 KeReleaseSpinLock(&Bcb
->BcbLock
, oldirql
);
165 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&ViewLock
);
169 DPRINT1("Disabling Tracing for CacheMap 0x%p:\n", Bcb
);
180 CcRosFlushCacheSegment(PCACHE_SEGMENT CacheSegment
)
184 Status
= WriteCacheSegment(CacheSegment
);
185 if (NT_SUCCESS(Status
))
187 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&ViewLock
);
188 KeAcquireSpinLock(&CacheSegment
->Bcb
->BcbLock
, &oldIrql
);
189 CacheSegment
->Dirty
= FALSE
;
190 RemoveEntryList(&CacheSegment
->DirtySegmentListEntry
);
191 DirtyPageCount
-= CacheSegment
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
192 CcRosCacheSegmentDecRefCount ( CacheSegment
);
193 KeReleaseSpinLock(&CacheSegment
->Bcb
->BcbLock
, oldIrql
);
194 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&ViewLock
);
201 CcRosFlushDirtyPages(ULONG Target
, PULONG Count
)
203 PLIST_ENTRY current_entry
;
204 PCACHE_SEGMENT current
;
205 ULONG PagesPerSegment
;
208 static ULONG WriteCount
[4] = {0, 0, 0, 0};
211 DPRINT("CcRosFlushDirtyPages(Target %d)\n", Target
);
215 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&ViewLock
);
217 WriteCount
[0] = WriteCount
[1];
218 WriteCount
[1] = WriteCount
[2];
219 WriteCount
[2] = WriteCount
[3];
222 NewTarget
= WriteCount
[0] + WriteCount
[1] + WriteCount
[2];
224 if (NewTarget
< DirtyPageCount
)
226 NewTarget
= (DirtyPageCount
- NewTarget
+ 3) / 4;
227 WriteCount
[0] += NewTarget
;
228 WriteCount
[1] += NewTarget
;
229 WriteCount
[2] += NewTarget
;
230 WriteCount
[3] += NewTarget
;
233 NewTarget
= WriteCount
[0];
235 Target
= max(NewTarget
, Target
);
237 current_entry
= DirtySegmentListHead
.Flink
;
238 if (current_entry
== &DirtySegmentListHead
)
240 DPRINT("No Dirty pages\n");
242 while (current_entry
!= &DirtySegmentListHead
&& Target
> 0)
244 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
245 DirtySegmentListEntry
);
246 current_entry
= current_entry
->Flink
;
248 // Locked = current->Bcb->Callbacks.AcquireForLazyWrite(current->Bcb->Context, FALSE);
249 Locked
= ExTryToAcquireResourceExclusiveLite(((FSRTL_COMMON_FCB_HEADER
*)(current
->Bcb
->FileObject
->FsContext
))->Resource
);
254 Locked
= CcTryToAcquireBrokenMutex(¤t
->Lock
);
257 // current->Bcb->Callbacks.ReleaseFromLazyWrite(current->Bcb->Context);
258 ExReleaseResourceLite(((FSRTL_COMMON_FCB_HEADER
*)(current
->Bcb
->FileObject
->FsContext
))->Resource
);
261 ASSERT(current
->Dirty
);
262 if (current
->ReferenceCount
> 1)
264 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(¤t
->Lock
);
265 // current->Bcb->Callbacks.ReleaseFromLazyWrite(current->Bcb->Context);
266 ExReleaseResourceLite(((FSRTL_COMMON_FCB_HEADER
*)(current
->Bcb
->FileObject
->FsContext
))->Resource
);
269 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&ViewLock
);
270 PagesPerSegment
= current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
271 Status
= CcRosFlushCacheSegment(current
);
272 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(¤t
->Lock
);
273 // current->Bcb->Callbacks.ReleaseFromLazyWrite(current->Bcb->Context);
274 ExReleaseResourceLite(((FSRTL_COMMON_FCB_HEADER
*)(current
->Bcb
->FileObject
->FsContext
))->Resource
);
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
;
284 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&ViewLock
);
285 current_entry
= DirtySegmentListHead
.Flink
;
287 if (*Count
< NewTarget
)
289 WriteCount
[1] += (NewTarget
- *Count
);
291 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&ViewLock
);
292 DPRINT("CcRosFlushDirtyPages() finished\n");
294 return(STATUS_SUCCESS
);
298 CcRosTrimCache(ULONG Target
, ULONG Priority
, PULONG NrFreed
)
300 * FUNCTION: Try to free some memory from the file cache.
302 * Target - The number of pages to be freed.
303 * Priority - The priority of free (currently unused).
304 * NrFreed - Points to a variable where the number of pages
305 * actually freed is returned.
308 PLIST_ENTRY current_entry
;
309 PCACHE_SEGMENT current
, last
= NULL
;
310 ULONG PagesPerSegment
;
315 DPRINT("CcRosTrimCache(Target %d)\n", Target
);
319 InitializeListHead(&FreeList
);
321 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&ViewLock
);
322 current_entry
= CacheSegmentLRUListHead
.Flink
;
323 while (current_entry
!= &CacheSegmentLRUListHead
&& Target
> 0)
325 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
326 CacheSegmentLRUListEntry
);
327 current_entry
= current_entry
->Flink
;
329 KeAcquireSpinLock(¤t
->Bcb
->BcbLock
, &oldIrql
);
330 if (current
->ReferenceCount
== 0)
332 RemoveEntryList(¤t
->BcbSegmentListEntry
);
333 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
334 RemoveEntryList(¤t
->CacheSegmentListEntry
);
335 RemoveEntryList(¤t
->CacheSegmentLRUListEntry
);
336 InsertHeadList(&FreeList
, ¤t
->BcbSegmentListEntry
);
337 PagesPerSegment
= current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
338 PagesFreed
= min(PagesPerSegment
, Target
);
339 Target
-= PagesFreed
;
340 (*NrFreed
) += PagesFreed
;
344 if (last
!= current
&& current
->MappedCount
> 0 && !current
->Dirty
&& !current
->PageOut
)
349 CcRosCacheSegmentIncRefCount(current
);
351 current
->PageOut
= TRUE
;
352 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
353 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&ViewLock
);
354 for (i
= 0; i
< current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
; i
++)
357 Page
= MmGetPhysicalAddress((char*)current
->BaseAddress
+ i
* PAGE_SIZE
).QuadPart
>> PAGE_SHIFT
;
358 Status
= MmPageOutPhysicalAddress(Page
);
359 if (!NT_SUCCESS(Status
))
364 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&ViewLock
);
365 KeAcquireSpinLock(¤t
->Bcb
->BcbLock
, &oldIrql
);
366 CcRosCacheSegmentDecRefCount(current
);
367 current
->PageOut
= FALSE
;
368 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
369 current_entry
= ¤t
->CacheSegmentLRUListEntry
;
372 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
375 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&ViewLock
);
377 while (!IsListEmpty(&FreeList
))
379 current_entry
= RemoveHeadList(&FreeList
);
380 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
381 BcbSegmentListEntry
);
382 CcRosInternalFreeCacheSegment(current
);
385 DPRINT("CcRosTrimCache() finished\n");
386 return(STATUS_SUCCESS
);
391 CcRosReleaseCacheSegment(PBCB Bcb
,
392 PCACHE_SEGMENT CacheSeg
,
397 BOOLEAN WasDirty
= CacheSeg
->Dirty
;
402 DPRINT("CcReleaseCacheSegment(Bcb 0x%p, CacheSeg 0x%p, Valid %d)\n",
403 Bcb
, CacheSeg
, Valid
);
405 CacheSeg
->Valid
= Valid
;
406 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| Dirty
;
408 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&ViewLock
);
409 if (!WasDirty
&& CacheSeg
->Dirty
)
411 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
412 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
414 RemoveEntryList(&CacheSeg
->CacheSegmentLRUListEntry
);
415 InsertTailList(&CacheSegmentLRUListHead
, &CacheSeg
->CacheSegmentLRUListEntry
);
419 CacheSeg
->MappedCount
++;
421 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
422 CcRosCacheSegmentDecRefCount(CacheSeg
);
423 if (Mapped
&& CacheSeg
->MappedCount
== 1)
425 CcRosCacheSegmentIncRefCount(CacheSeg
);
427 if (!WasDirty
&& CacheSeg
->Dirty
)
429 CcRosCacheSegmentIncRefCount(CacheSeg
);
431 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
432 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&ViewLock
);
433 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&CacheSeg
->Lock
);
435 return(STATUS_SUCCESS
);
440 CcRosLookupCacheSegment(PBCB Bcb
, ULONG FileOffset
)
442 PLIST_ENTRY current_entry
;
443 PCACHE_SEGMENT current
;
448 DPRINT("CcRosLookupCacheSegment(Bcb -x%p, FileOffset %d)\n", Bcb
, FileOffset
);
450 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
451 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
452 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
454 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
455 BcbSegmentListEntry
);
456 if (current
->FileOffset
<= FileOffset
&&
457 (current
->FileOffset
+ Bcb
->CacheSegmentSize
) > FileOffset
)
459 CcRosCacheSegmentIncRefCount(current
);
460 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
461 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(¤t
->Lock
);
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
)
486 if (!CacheSeg
->Dirty
)
488 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&ViewLock
);
489 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
490 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
491 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&ViewLock
);
495 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
496 CcRosCacheSegmentDecRefCount(CacheSeg
);
497 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
501 CacheSeg
->Dirty
= TRUE
;
502 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&CacheSeg
->Lock
);
504 return(STATUS_SUCCESS
);
509 CcRosUnmapCacheSegment(PBCB Bcb
, ULONG FileOffset
, BOOLEAN NowDirty
)
511 PCACHE_SEGMENT CacheSeg
;
517 DPRINT("CcRosUnmapCacheSegment(Bcb 0x%p, FileOffset %d, NowDirty %d)\n",
518 Bcb
, FileOffset
, NowDirty
);
520 CacheSeg
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
521 if (CacheSeg
== NULL
)
523 return(STATUS_UNSUCCESSFUL
);
526 WasDirty
= CacheSeg
->Dirty
;
527 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| NowDirty
;
529 CacheSeg
->MappedCount
--;
531 if (!WasDirty
&& NowDirty
)
533 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&ViewLock
);
534 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
535 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
536 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&ViewLock
);
539 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
540 CcRosCacheSegmentDecRefCount(CacheSeg
);
541 if (!WasDirty
&& NowDirty
)
543 CcRosCacheSegmentIncRefCount(CacheSeg
);
545 if (CacheSeg
->MappedCount
== 0)
547 CcRosCacheSegmentDecRefCount(CacheSeg
);
549 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
551 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&CacheSeg
->Lock
);
552 return(STATUS_SUCCESS
);
556 CcRosCreateCacheSegment(PBCB Bcb
,
558 PCACHE_SEGMENT
* CacheSeg
)
561 PCACHE_SEGMENT current
;
562 PCACHE_SEGMENT previous
;
563 PLIST_ENTRY current_entry
;
568 ULONG StartingOffset
;
571 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
575 DPRINT("CcRosCreateCacheSegment()\n");
577 BoundaryAddressMultiple
.QuadPart
= 0;
578 if (FileOffset
>= Bcb
->FileSize
.u
.LowPart
)
581 return STATUS_INVALID_PARAMETER
;
584 current
= ExAllocateFromNPagedLookasideList(&CacheSegLookasideList
);
585 current
->Valid
= FALSE
;
586 current
->Dirty
= FALSE
;
587 current
->PageOut
= FALSE
;
588 current
->FileOffset
= ROUND_DOWN(FileOffset
, Bcb
->CacheSegmentSize
);
590 #if defined(DBG) || defined(KDBG)
593 DPRINT1("CacheMap 0x%p: new Cache Segment: 0x%p\n", Bcb
, current
);
596 current
->MappedCount
= 0;
597 current
->DirtySegmentListEntry
.Flink
= NULL
;
598 current
->DirtySegmentListEntry
.Blink
= NULL
;
599 current
->ReferenceCount
= 1;
600 ExInitializeFastMutex(¤t
->Lock
);
601 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(¤t
->Lock
);
602 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&ViewLock
);
605 /* There is window between the call to CcRosLookupCacheSegment
606 * and CcRosCreateCacheSegment. We must check if a segment on
607 * the fileoffset exist. If there exist a segment, we release
608 * our new created segment and return the existing one.
610 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
611 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
613 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
615 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
616 BcbSegmentListEntry
);
617 if (current
->FileOffset
<= FileOffset
&&
618 (current
->FileOffset
+ Bcb
->CacheSegmentSize
) > FileOffset
)
620 CcRosCacheSegmentIncRefCount(current
);
621 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
622 #if defined(DBG) || defined(KDBG)
625 DPRINT1("CacheMap 0x%p: deleting newly created Cache Segment 0x%p ( found existing one 0x%p )\n",
631 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&(*CacheSeg
)->Lock
);
632 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&ViewLock
);
633 ExFreeToNPagedLookasideList(&CacheSegLookasideList
, *CacheSeg
);
635 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(¤t
->Lock
);
636 return STATUS_SUCCESS
;
638 if (current
->FileOffset
< FileOffset
)
640 if (previous
== NULL
)
646 if (previous
->FileOffset
< current
->FileOffset
)
652 current_entry
= current_entry
->Flink
;
654 /* There was no existing segment. */
658 InsertHeadList(&previous
->BcbSegmentListEntry
, ¤t
->BcbSegmentListEntry
);
662 InsertHeadList(&Bcb
->BcbSegmentListHead
, ¤t
->BcbSegmentListEntry
);
664 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
665 InsertTailList(&CacheSegmentListHead
, ¤t
->CacheSegmentListEntry
);
666 InsertTailList(&CacheSegmentLRUListHead
, ¤t
->CacheSegmentLRUListEntry
);
667 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&ViewLock
);
669 KeAcquireSpinLock(&CiCacheSegMappingRegionLock
, &oldIrql
);
671 StartingOffset
= RtlFindClearBitsAndSet(&CiCacheSegMappingRegionAllocMap
, Bcb
->CacheSegmentSize
/ PAGE_SIZE
, CiCacheSegMappingRegionHint
);
673 if (StartingOffset
== 0xffffffff)
675 DPRINT1("Out of CacheSeg mapping space\n");
679 current
->BaseAddress
= CiCacheSegMappingRegionBase
+ StartingOffset
* PAGE_SIZE
;
681 if (CiCacheSegMappingRegionHint
== StartingOffset
)
683 CiCacheSegMappingRegionHint
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
686 KeReleaseSpinLock(&CiCacheSegMappingRegionLock
, oldIrql
);
688 MmLockAddressSpace(MmGetKernelAddressSpace());
689 current
->BaseAddress
= NULL
;
690 Status
= MmCreateMemoryArea(MmGetKernelAddressSpace(),
691 MEMORY_AREA_CACHE_SEGMENT
,
692 ¤t
->BaseAddress
,
693 Bcb
->CacheSegmentSize
,
695 (PMEMORY_AREA
*)¤t
->MemoryArea
,
698 BoundaryAddressMultiple
);
699 MmUnlockAddressSpace(MmGetKernelAddressSpace());
700 if (!NT_SUCCESS(Status
))
705 Pfn
= alloca(sizeof(PFN_TYPE
) * (Bcb
->CacheSegmentSize
/ PAGE_SIZE
));
706 for (i
= 0; i
< (Bcb
->CacheSegmentSize
/ PAGE_SIZE
); i
++)
708 Status
= MmRequestPageMemoryConsumer(MC_CACHE
, TRUE
, &Pfn
[i
]);
709 if (!NT_SUCCESS(Status
))
714 Status
= MmCreateVirtualMapping(NULL
,
715 current
->BaseAddress
,
718 Bcb
->CacheSegmentSize
/ PAGE_SIZE
);
719 if (!NT_SUCCESS(Status
))
723 return(STATUS_SUCCESS
);
728 CcRosGetCacheSegmentChain(PBCB Bcb
,
731 PCACHE_SEGMENT
* CacheSeg
)
733 PCACHE_SEGMENT current
;
735 PCACHE_SEGMENT
* CacheSegList
;
736 PCACHE_SEGMENT Previous
= NULL
;
740 DPRINT("CcRosGetCacheSegmentChain()\n");
742 Length
= ROUND_UP(Length
, Bcb
->CacheSegmentSize
);
744 #if defined(__GNUC__)
745 CacheSegList
= alloca(sizeof(PCACHE_SEGMENT
) *
746 (Length
/ Bcb
->CacheSegmentSize
));
747 #elif defined(_MSC_VER)
748 CacheSegList
= _alloca(sizeof(PCACHE_SEGMENT
) *
749 (Length
/ Bcb
->CacheSegmentSize
));
751 #error Unknown compiler for alloca intrinsic stack allocation "function"
755 * Look for a cache segment already mapping the same data.
757 for (i
= 0; i
< (Length
/ Bcb
->CacheSegmentSize
); i
++)
759 ULONG CurrentOffset
= FileOffset
+ (i
* Bcb
->CacheSegmentSize
);
760 current
= CcRosLookupCacheSegment(Bcb
, CurrentOffset
);
763 CacheSegList
[i
] = current
;
767 CcRosCreateCacheSegment(Bcb
, CurrentOffset
, ¤t
);
768 CacheSegList
[i
] = current
;
772 for (i
= 0; i
< (Length
/ Bcb
->CacheSegmentSize
); i
++)
776 *CacheSeg
= CacheSegList
[i
];
777 Previous
= CacheSegList
[i
];
781 Previous
->NextInChain
= CacheSegList
[i
];
782 Previous
= CacheSegList
[i
];
785 Previous
->NextInChain
= NULL
;
787 return(STATUS_SUCCESS
);
792 CcRosGetCacheSegment(PBCB Bcb
,
797 PCACHE_SEGMENT
* CacheSeg
)
799 PCACHE_SEGMENT current
;
804 DPRINT("CcRosGetCacheSegment()\n");
807 * Look for a cache segment already mapping the same data.
809 current
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
813 * Otherwise create a new segment.
815 Status
= CcRosCreateCacheSegment(Bcb
, FileOffset
, ¤t
);
816 if (!NT_SUCCESS(Status
))
822 * Return information about the segment to the caller.
824 *UptoDate
= current
->Valid
;
825 *BaseAddress
= current
->BaseAddress
;
826 DPRINT("*BaseAddress 0x%.8X\n", *BaseAddress
);
828 *BaseOffset
= current
->FileOffset
;
829 return(STATUS_SUCCESS
);
833 CcRosRequestCacheSegment(PBCB Bcb
,
837 PCACHE_SEGMENT
* CacheSeg
)
839 * FUNCTION: Request a page mapping for a BCB
846 if ((FileOffset
% Bcb
->CacheSegmentSize
) != 0)
848 CPRINT("Bad fileoffset %x should be multiple of %x",
849 FileOffset
, Bcb
->CacheSegmentSize
);
853 return(CcRosGetCacheSegment(Bcb
,
863 CcFreeCachePage(PVOID Context
, MEMORY_AREA
* MemoryArea
, PVOID Address
,
864 PFN_TYPE Page
, SWAPENTRY SwapEntry
, BOOLEAN Dirty
)
866 ASSERT(SwapEntry
== 0);
869 MmReleasePageMemoryConsumer(MC_CACHE
, Page
);
874 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg
)
876 * FUNCTION: Releases a cache segment associated with a BCB
886 DPRINT("Freeing cache segment 0x%p\n", CacheSeg
);
887 #if defined(DBG) || defined(KDBG)
888 if ( CacheSeg
->Bcb
->Trace
)
890 DPRINT1("CacheMap 0x%p: deleting Cache Segment: 0x%p\n", CacheSeg
->Bcb
, CacheSeg
);
894 RegionSize
= CacheSeg
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
896 /* Unmap all the pages. */
897 for (i
= 0; i
< RegionSize
; i
++)
899 MmDeleteVirtualMapping(NULL
,
900 CacheSeg
->BaseAddress
+ (i
* PAGE_SIZE
),
904 MmReleasePageMemoryConsumer(MC_CACHE
, Page
);
907 KeAcquireSpinLock(&CiCacheSegMappingRegionLock
, &oldIrql
);
908 /* Deallocate all the pages used. */
909 Base
= (ULONG
)(CacheSeg
->BaseAddress
- CiCacheSegMappingRegionBase
) / PAGE_SIZE
;
911 RtlClearBits(&CiCacheSegMappingRegionAllocMap
, Base
, RegionSize
);
913 CiCacheSegMappingRegionHint
= min (CiCacheSegMappingRegionHint
, Base
);
915 KeReleaseSpinLock(&CiCacheSegMappingRegionLock
, oldIrql
);
917 MmLockAddressSpace(MmGetKernelAddressSpace());
918 MmFreeMemoryArea(MmGetKernelAddressSpace(),
919 CacheSeg
->MemoryArea
,
922 MmUnlockAddressSpace(MmGetKernelAddressSpace());
924 ExFreeToNPagedLookasideList(&CacheSegLookasideList
, CacheSeg
);
925 return(STATUS_SUCCESS
);
930 CcRosFreeCacheSegment(PBCB Bcb
, PCACHE_SEGMENT CacheSeg
)
937 DPRINT("CcRosFreeCacheSegment(Bcb 0x%p, CacheSeg 0x%p)\n",
940 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&ViewLock
);
941 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
942 RemoveEntryList(&CacheSeg
->BcbSegmentListEntry
);
943 RemoveEntryList(&CacheSeg
->CacheSegmentListEntry
);
944 RemoveEntryList(&CacheSeg
->CacheSegmentLRUListEntry
);
947 RemoveEntryList(&CacheSeg
->DirtySegmentListEntry
);
948 DirtyPageCount
-= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
951 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
952 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&ViewLock
);
954 Status
= CcRosInternalFreeCacheSegment(CacheSeg
);
962 CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointers
,
963 IN PLARGE_INTEGER FileOffset OPTIONAL
,
965 OUT PIO_STATUS_BLOCK IoStatus
)
968 LARGE_INTEGER Offset
;
969 PCACHE_SEGMENT current
;
973 DPRINT("CcFlushCache(SectionObjectPointers 0x%p, FileOffset 0x%p, Length %d, IoStatus 0x%p)\n",
974 SectionObjectPointers
, FileOffset
, Length
, IoStatus
);
976 if (SectionObjectPointers
&& SectionObjectPointers
->SharedCacheMap
)
978 Bcb
= (PBCB
)SectionObjectPointers
->SharedCacheMap
;
982 Offset
= *FileOffset
;
986 Offset
.QuadPart
= (LONGLONG
)0;
987 Length
= Bcb
->FileSize
.u
.LowPart
;
992 IoStatus
->Status
= STATUS_SUCCESS
;
993 IoStatus
->Information
= 0;
998 current
= CcRosLookupCacheSegment (Bcb
, Offset
.u
.LowPart
);
1003 Status
= CcRosFlushCacheSegment(current
);
1004 if (!NT_SUCCESS(Status
) && IoStatus
!= NULL
)
1006 IoStatus
->Status
= Status
;
1009 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
1010 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(¤t
->Lock
);
1011 CcRosCacheSegmentDecRefCount(current
);
1012 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
1015 Offset
.QuadPart
+= Bcb
->CacheSegmentSize
;
1016 if (Length
> Bcb
->CacheSegmentSize
)
1018 Length
-= Bcb
->CacheSegmentSize
;
1030 IoStatus
->Status
= STATUS_INVALID_PARAMETER
;
1037 CcRosDeleteFileCache(PFILE_OBJECT FileObject
, PBCB Bcb
)
1039 * FUNCTION: Releases the BCB associated with a file object
1042 PLIST_ENTRY current_entry
;
1043 PCACHE_SEGMENT current
;
1045 LIST_ENTRY FreeList
;
1051 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&ViewLock
);
1053 CcFlushCache(FileObject
->SectionObjectPointer
, NULL
, 0, NULL
);
1055 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&ViewLock
);
1057 if (Bcb
->RefCount
== 0)
1059 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
1061 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1062 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1065 FileObject
->SectionObjectPointer
->SharedCacheMap
= NULL
;
1068 * Release all cache segments.
1070 InitializeListHead(&FreeList
);
1071 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
1072 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
1073 while (!IsListEmpty(&Bcb
->BcbSegmentListHead
))
1075 current_entry
= RemoveTailList(&Bcb
->BcbSegmentListHead
);
1076 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, BcbSegmentListEntry
);
1077 RemoveEntryList(¤t
->CacheSegmentListEntry
);
1078 RemoveEntryList(¤t
->CacheSegmentLRUListEntry
);
1081 RemoveEntryList(¤t
->DirtySegmentListEntry
);
1082 DirtyPageCount
-= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
1083 DPRINT1("Freeing dirty segment\n");
1085 InsertHeadList(&FreeList
, ¤t
->BcbSegmentListEntry
);
1087 #if defined(DBG) || defined(KDBG)
1090 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
1092 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&ViewLock
);
1093 ObDereferenceObject (Bcb
->FileObject
);
1095 while (!IsListEmpty(&FreeList
))
1097 current_entry
= RemoveTailList(&FreeList
);
1098 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, BcbSegmentListEntry
);
1099 Status
= CcRosInternalFreeCacheSegment(current
);
1101 ExFreeToNPagedLookasideList(&BcbLookasideList
, Bcb
);
1102 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&ViewLock
);
1104 return(STATUS_SUCCESS
);
1109 CcRosReferenceCache(PFILE_OBJECT FileObject
)
1112 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&ViewLock
);
1113 Bcb
= (PBCB
)FileObject
->SectionObjectPointer
->SharedCacheMap
;
1115 if (Bcb
->RefCount
== 0)
1117 ASSERT(Bcb
->BcbRemoveListEntry
.Flink
!= NULL
);
1118 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1119 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1124 ASSERT(Bcb
->BcbRemoveListEntry
.Flink
== NULL
);
1127 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&ViewLock
);
1132 CcRosSetRemoveOnClose(PSECTION_OBJECT_POINTERS SectionObjectPointer
)
1135 DPRINT("CcRosSetRemoveOnClose()\n");
1136 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&ViewLock
);
1137 Bcb
= (PBCB
)SectionObjectPointer
->SharedCacheMap
;
1140 Bcb
->RemoveOnClose
= TRUE
;
1141 if (Bcb
->RefCount
== 0)
1143 CcRosDeleteFileCache(Bcb
->FileObject
, Bcb
);
1146 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&ViewLock
);
1152 CcRosDereferenceCache(PFILE_OBJECT FileObject
)
1155 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&ViewLock
);
1156 Bcb
= (PBCB
)FileObject
->SectionObjectPointer
->SharedCacheMap
;
1158 if (Bcb
->RefCount
> 0)
1161 if (Bcb
->RefCount
== 0)
1163 MmFreeSectionSegments(Bcb
->FileObject
);
1164 if (Bcb
->RemoveOnClose
)
1166 CcRosDeleteFileCache(FileObject
, Bcb
);
1170 Bcb
->TimeStamp
= CcTimeStamp
;
1171 InsertHeadList(&ClosedListHead
, &Bcb
->BcbRemoveListEntry
);
1175 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&ViewLock
);
1179 CcRosReleaseFileCache(PFILE_OBJECT FileObject
)
1181 * FUNCTION: Called by the file system when a handle to a file object
1187 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&ViewLock
);
1189 if (FileObject
->SectionObjectPointer
->SharedCacheMap
!= NULL
)
1191 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1192 if (FileObject
->PrivateCacheMap
!= NULL
)
1194 FileObject
->PrivateCacheMap
= NULL
;
1195 if (Bcb
->RefCount
> 0)
1198 if (Bcb
->RefCount
== 0)
1200 MmFreeSectionSegments(Bcb
->FileObject
);
1201 if (Bcb
->RemoveOnClose
)
1203 CcRosDeleteFileCache(FileObject
, Bcb
);
1207 Bcb
->TimeStamp
= CcTimeStamp
;
1208 InsertHeadList(&ClosedListHead
, &Bcb
->BcbRemoveListEntry
);
1214 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&ViewLock
);
1215 return(STATUS_SUCCESS
);
1220 CcTryToInitializeFileCache(PFILE_OBJECT FileObject
)
1225 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&ViewLock
);
1227 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1230 Status
= STATUS_UNSUCCESSFUL
;
1234 if (FileObject
->PrivateCacheMap
== NULL
)
1236 FileObject
->PrivateCacheMap
= Bcb
;
1239 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
1241 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1242 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1244 Status
= STATUS_SUCCESS
;
1246 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&ViewLock
);
1253 CcRosInitializeFileCache(PFILE_OBJECT FileObject
,
1254 ULONG CacheSegmentSize
)
1256 * FUNCTION: Initializes a BCB for a file object
1261 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1262 DPRINT("CcRosInitializeFileCache(FileObject 0x%p, Bcb 0x%p, CacheSegmentSize %d)\n",
1263 FileObject
, Bcb
, CacheSegmentSize
);
1265 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&ViewLock
);
1268 Bcb
= ExAllocateFromNPagedLookasideList(&BcbLookasideList
);
1271 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&ViewLock
);
1272 return(STATUS_UNSUCCESSFUL
);
1274 memset(Bcb
, 0, sizeof(BCB
));
1275 ObReferenceObjectByPointer(FileObject
,
1279 Bcb
->FileObject
= FileObject
;
1280 Bcb
->CacheSegmentSize
= CacheSegmentSize
;
1281 if (FileObject
->FsContext
)
1283 Bcb
->AllocationSize
=
1284 ((PFSRTL_COMMON_FCB_HEADER
)FileObject
->FsContext
)->AllocationSize
;
1286 ((PFSRTL_COMMON_FCB_HEADER
)FileObject
->FsContext
)->FileSize
;
1288 KeInitializeSpinLock(&Bcb
->BcbLock
);
1289 InitializeListHead(&Bcb
->BcbSegmentListHead
);
1290 FileObject
->SectionObjectPointer
->SharedCacheMap
= Bcb
;
1292 if (FileObject
->PrivateCacheMap
== NULL
)
1294 FileObject
->PrivateCacheMap
= Bcb
;
1297 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
1299 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1300 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1302 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&ViewLock
);
1304 return(STATUS_SUCCESS
);
1310 PFILE_OBJECT STDCALL
1311 CcGetFileObjectFromSectionPtrs(IN PSECTION_OBJECT_POINTERS SectionObjectPointers
)
1314 if (SectionObjectPointers
&& SectionObjectPointers
->SharedCacheMap
)
1316 Bcb
= (PBCB
)SectionObjectPointers
->SharedCacheMap
;
1318 return Bcb
->FileObject
;
1324 CmLazyCloseThreadMain(PVOID Ignored
)
1326 LARGE_INTEGER Timeout
;
1327 PLIST_ENTRY current_entry
;
1329 ULONG RemoveTimeStamp
;
1332 KeQuerySystemTime (&Timeout
);
1336 Timeout
.QuadPart
+= (LONGLONG
)100000000; // 10sec
1337 Status
= KeWaitForSingleObject(&LazyCloseThreadEvent
,
1343 DPRINT("LazyCloseThreadMain %d\n", CcTimeStamp
);
1345 if (!NT_SUCCESS(Status
))
1347 DbgPrint("LazyCloseThread: Wait failed\n");
1351 if (LazyCloseThreadShouldTerminate
)
1353 DbgPrint("LazyCloseThread: Terminating\n");
1357 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&ViewLock
);
1359 if (CcTimeStamp
>= 30)
1361 RemoveTimeStamp
= CcTimeStamp
- 30; /* 5min = 10sec * 30 */
1362 while (!IsListEmpty(&ClosedListHead
))
1364 current_entry
= ClosedListHead
.Blink
;
1365 current
= CONTAINING_RECORD(current_entry
, BCB
, BcbRemoveListEntry
);
1366 if (current
->TimeStamp
>= RemoveTimeStamp
)
1370 CcRosDeleteFileCache(current
->FileObject
, current
);
1373 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&ViewLock
);
1385 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
1390 DPRINT("CcInitView()\n");
1392 BoundaryAddressMultiple
.QuadPart
= 0;
1393 CiCacheSegMappingRegionHint
= 0;
1394 CiCacheSegMappingRegionBase
= NULL
;
1396 MmLockAddressSpace(MmGetKernelAddressSpace());
1398 Status
= MmCreateMemoryArea(MmGetKernelAddressSpace(),
1399 MEMORY_AREA_CACHE_SEGMENT
,
1400 &CiCacheSegMappingRegionBase
,
1401 CI_CACHESEG_MAPPING_REGION_SIZE
,
1406 BoundaryAddressMultiple
);
1407 MmUnlockAddressSpace(MmGetKernelAddressSpace());
1408 if (!NT_SUCCESS(Status
))
1413 Buffer
= ExAllocatePool(NonPagedPool
, CI_CACHESEG_MAPPING_REGION_SIZE
/ (PAGE_SIZE
* 8));
1415 RtlInitializeBitMap(&CiCacheSegMappingRegionAllocMap
, Buffer
, CI_CACHESEG_MAPPING_REGION_SIZE
/ PAGE_SIZE
);
1416 RtlClearAllBits(&CiCacheSegMappingRegionAllocMap
);
1418 KeInitializeSpinLock(&CiCacheSegMappingRegionLock
);
1420 InitializeListHead(&CacheSegmentListHead
);
1421 InitializeListHead(&DirtySegmentListHead
);
1422 InitializeListHead(&CacheSegmentLRUListHead
);
1423 InitializeListHead(&ClosedListHead
);
1424 ExInitializeFastMutex(&ViewLock
);
1425 ExInitializeNPagedLookasideList (&iBcbLookasideList
,
1429 sizeof(INTERNAL_BCB
),
1432 ExInitializeNPagedLookasideList (&BcbLookasideList
,
1439 ExInitializeNPagedLookasideList (&CacheSegLookasideList
,
1443 sizeof(CACHE_SEGMENT
),
1447 MmInitializeMemoryConsumer(MC_CACHE
, CcRosTrimCache
);
1449 CcInitCacheZeroPage();
1452 LazyCloseThreadShouldTerminate
= FALSE
;
1453 KeInitializeEvent (&LazyCloseThreadEvent
, SynchronizationEvent
, FALSE
);
1454 Status
= PsCreateSystemThread(&LazyCloseThreadHandle
,
1459 (PKSTART_ROUTINE
)CmLazyCloseThreadMain
,
1461 if (NT_SUCCESS(Status
))
1463 Priority
= LOW_REALTIME_PRIORITY
;
1464 NtSetInformationThread(LazyCloseThreadHandle
,