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
);
113 /* FUNCTIONS *****************************************************************/
121 #if defined(DBG) || defined(KDBG)
123 PLIST_ENTRY current_entry
;
124 PCACHE_SEGMENT current
;
133 DPRINT1("Enabling Tracing for CacheMap 0x%p:\n", Bcb
);
135 ExAcquireFastMutex(&ViewLock
);
136 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldirql
);
138 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
139 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
141 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, BcbSegmentListEntry
);
142 current_entry
= current_entry
->Flink
;
144 DPRINT1(" CacheSegment 0x%p enabled, RefCount %d, Dirty %d, PageOut %d\n",
145 current
, current
->ReferenceCount
, current
->Dirty
, current
->PageOut
);
147 KeReleaseSpinLock(&Bcb
->BcbLock
, oldirql
);
148 ExReleaseFastMutex(&ViewLock
);
152 DPRINT1("Disabling Tracing for CacheMap 0x%p:\n", Bcb
);
163 CcRosFlushCacheSegment(PCACHE_SEGMENT CacheSegment
)
167 Status
= WriteCacheSegment(CacheSegment
);
168 if (NT_SUCCESS(Status
))
170 ExAcquireFastMutex(&ViewLock
);
171 KeAcquireSpinLock(&CacheSegment
->Bcb
->BcbLock
, &oldIrql
);
172 CacheSegment
->Dirty
= FALSE
;
173 RemoveEntryList(&CacheSegment
->DirtySegmentListEntry
);
174 DirtyPageCount
-= CacheSegment
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
175 CcRosCacheSegmentDecRefCount ( CacheSegment
);
176 KeReleaseSpinLock(&CacheSegment
->Bcb
->BcbLock
, oldIrql
);
177 ExReleaseFastMutex(&ViewLock
);
184 CcRosFlushDirtyPages(ULONG Target
, PULONG Count
)
186 PLIST_ENTRY current_entry
;
187 PCACHE_SEGMENT current
;
188 ULONG PagesPerSegment
;
191 static ULONG WriteCount
[4] = {0, 0, 0, 0};
194 DPRINT("CcRosFlushDirtyPages(Target %d)\n", Target
);
198 ExAcquireFastMutex(&ViewLock
);
200 WriteCount
[0] = WriteCount
[1];
201 WriteCount
[1] = WriteCount
[2];
202 WriteCount
[2] = WriteCount
[3];
205 NewTarget
= WriteCount
[0] + WriteCount
[1] + WriteCount
[2];
207 if (NewTarget
< DirtyPageCount
)
209 NewTarget
= (DirtyPageCount
- NewTarget
+ 3) / 4;
210 WriteCount
[0] += NewTarget
;
211 WriteCount
[1] += NewTarget
;
212 WriteCount
[2] += NewTarget
;
213 WriteCount
[3] += NewTarget
;
216 NewTarget
= WriteCount
[0];
218 Target
= max(NewTarget
, Target
);
220 current_entry
= DirtySegmentListHead
.Flink
;
221 if (current_entry
== &DirtySegmentListHead
)
223 DPRINT("No Dirty pages\n");
225 while (current_entry
!= &DirtySegmentListHead
&& Target
> 0)
227 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
228 DirtySegmentListEntry
);
229 current_entry
= current_entry
->Flink
;
230 Locked
= ExTryToAcquireFastMutex(¤t
->Lock
);
235 ASSERT(current
->Dirty
);
236 if (current
->ReferenceCount
> 1)
238 ExReleaseFastMutex(¤t
->Lock
);
241 ExReleaseFastMutex(&ViewLock
);
242 PagesPerSegment
= current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
243 Status
= CcRosFlushCacheSegment(current
);
244 ExReleaseFastMutex(¤t
->Lock
);
245 if (!NT_SUCCESS(Status
) && (Status
!= STATUS_END_OF_FILE
))
247 DPRINT1("CC: Failed to flush cache segment.\n");
251 (*Count
) += PagesPerSegment
;
252 Target
-= PagesPerSegment
;
254 ExAcquireFastMutex(&ViewLock
);
255 current_entry
= DirtySegmentListHead
.Flink
;
257 if (*Count
< NewTarget
)
259 WriteCount
[1] += (NewTarget
- *Count
);
261 ExReleaseFastMutex(&ViewLock
);
262 DPRINT("CcRosFlushDirtyPages() finished\n");
264 return(STATUS_SUCCESS
);
268 CcRosTrimCache(ULONG Target
, ULONG Priority
, PULONG NrFreed
)
270 * FUNCTION: Try to free some memory from the file cache.
272 * Target - The number of pages to be freed.
273 * Priority - The priority of free (currently unused).
274 * NrFreed - Points to a variable where the number of pages
275 * actually freed is returned.
278 PLIST_ENTRY current_entry
;
279 PCACHE_SEGMENT current
, last
= NULL
;
280 ULONG PagesPerSegment
;
285 DPRINT("CcRosTrimCache(Target %d)\n", Target
);
289 InitializeListHead(&FreeList
);
291 ExAcquireFastMutex(&ViewLock
);
292 current_entry
= CacheSegmentLRUListHead
.Flink
;
293 while (current_entry
!= &CacheSegmentLRUListHead
&& Target
> 0)
295 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
296 CacheSegmentLRUListEntry
);
297 current_entry
= current_entry
->Flink
;
299 KeAcquireSpinLock(¤t
->Bcb
->BcbLock
, &oldIrql
);
300 if (current
->ReferenceCount
== 0)
302 RemoveEntryList(¤t
->BcbSegmentListEntry
);
303 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
304 RemoveEntryList(¤t
->CacheSegmentListEntry
);
305 RemoveEntryList(¤t
->CacheSegmentLRUListEntry
);
306 InsertHeadList(&FreeList
, ¤t
->BcbSegmentListEntry
);
307 PagesPerSegment
= current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
308 PagesFreed
= min(PagesPerSegment
, Target
);
309 Target
-= PagesFreed
;
310 (*NrFreed
) += PagesFreed
;
314 if (last
!= current
&& current
->MappedCount
> 0 && !current
->Dirty
&& !current
->PageOut
)
319 CcRosCacheSegmentIncRefCount(current
);
321 current
->PageOut
= TRUE
;
322 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
323 ExReleaseFastMutex(&ViewLock
);
324 for (i
= 0; i
< current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
; i
++)
327 Page
= MmGetPhysicalAddress((char*)current
->BaseAddress
+ i
* PAGE_SIZE
).QuadPart
>> PAGE_SHIFT
;
328 Status
= MmPageOutPhysicalAddress(Page
);
329 if (!NT_SUCCESS(Status
))
334 ExAcquireFastMutex(&ViewLock
);
335 KeAcquireSpinLock(¤t
->Bcb
->BcbLock
, &oldIrql
);
336 CcRosCacheSegmentDecRefCount(current
);
337 current
->PageOut
= FALSE
;
338 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
339 current_entry
= ¤t
->CacheSegmentLRUListEntry
;
342 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
345 ExReleaseFastMutex(&ViewLock
);
347 while (!IsListEmpty(&FreeList
))
349 current_entry
= RemoveHeadList(&FreeList
);
350 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
351 BcbSegmentListEntry
);
352 CcRosInternalFreeCacheSegment(current
);
355 DPRINT("CcRosTrimCache() finished\n");
356 return(STATUS_SUCCESS
);
361 CcRosReleaseCacheSegment(PBCB Bcb
,
362 PCACHE_SEGMENT CacheSeg
,
367 BOOLEAN WasDirty
= CacheSeg
->Dirty
;
372 DPRINT("CcReleaseCacheSegment(Bcb 0x%p, CacheSeg 0x%p, Valid %d)\n",
373 Bcb
, CacheSeg
, Valid
);
375 CacheSeg
->Valid
= Valid
;
376 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| Dirty
;
378 ExAcquireFastMutex(&ViewLock
);
379 if (!WasDirty
&& CacheSeg
->Dirty
)
381 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
382 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
384 RemoveEntryList(&CacheSeg
->CacheSegmentLRUListEntry
);
385 InsertTailList(&CacheSegmentLRUListHead
, &CacheSeg
->CacheSegmentLRUListEntry
);
389 CacheSeg
->MappedCount
++;
391 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
392 CcRosCacheSegmentDecRefCount(CacheSeg
);
393 if (Mapped
&& CacheSeg
->MappedCount
== 1)
395 CcRosCacheSegmentIncRefCount(CacheSeg
);
397 if (!WasDirty
&& CacheSeg
->Dirty
)
399 CcRosCacheSegmentIncRefCount(CacheSeg
);
401 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
402 ExReleaseFastMutex(&ViewLock
);
403 ExReleaseFastMutex(&CacheSeg
->Lock
);
405 return(STATUS_SUCCESS
);
410 CcRosLookupCacheSegment(PBCB Bcb
, ULONG FileOffset
)
412 PLIST_ENTRY current_entry
;
413 PCACHE_SEGMENT current
;
418 DPRINT("CcRosLookupCacheSegment(Bcb -x%p, FileOffset %d)\n", Bcb
, FileOffset
);
420 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
421 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
422 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
424 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
425 BcbSegmentListEntry
);
426 if (current
->FileOffset
<= FileOffset
&&
427 (current
->FileOffset
+ Bcb
->CacheSegmentSize
) > FileOffset
)
429 CcRosCacheSegmentIncRefCount(current
);
430 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
431 ExAcquireFastMutex(¤t
->Lock
);
434 current_entry
= current_entry
->Flink
;
436 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
442 CcRosMarkDirtyCacheSegment(PBCB Bcb
, ULONG FileOffset
)
444 PCACHE_SEGMENT CacheSeg
;
449 DPRINT("CcRosMarkDirtyCacheSegment(Bcb 0x%p, FileOffset %d)\n", Bcb
, FileOffset
);
451 CacheSeg
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
452 if (CacheSeg
== NULL
)
456 if (!CacheSeg
->Dirty
)
458 ExAcquireFastMutex(&ViewLock
);
459 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
460 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
461 ExReleaseFastMutex(&ViewLock
);
465 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
466 CcRosCacheSegmentDecRefCount(CacheSeg
);
467 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
471 CacheSeg
->Dirty
= TRUE
;
472 ExReleaseFastMutex(&CacheSeg
->Lock
);
474 return(STATUS_SUCCESS
);
479 CcRosUnmapCacheSegment(PBCB Bcb
, ULONG FileOffset
, BOOLEAN NowDirty
)
481 PCACHE_SEGMENT CacheSeg
;
487 DPRINT("CcRosUnmapCacheSegment(Bcb 0x%p, FileOffset %d, NowDirty %d)\n",
488 Bcb
, FileOffset
, NowDirty
);
490 CacheSeg
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
491 if (CacheSeg
== NULL
)
493 return(STATUS_UNSUCCESSFUL
);
496 WasDirty
= CacheSeg
->Dirty
;
497 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| NowDirty
;
499 CacheSeg
->MappedCount
--;
501 if (!WasDirty
&& NowDirty
)
503 ExAcquireFastMutex(&ViewLock
);
504 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
505 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
506 ExReleaseFastMutex(&ViewLock
);
509 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
510 CcRosCacheSegmentDecRefCount(CacheSeg
);
511 if (!WasDirty
&& NowDirty
)
513 CcRosCacheSegmentIncRefCount(CacheSeg
);
515 if (CacheSeg
->MappedCount
== 0)
517 CcRosCacheSegmentDecRefCount(CacheSeg
);
519 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
521 ExReleaseFastMutex(&CacheSeg
->Lock
);
522 return(STATUS_SUCCESS
);
526 CcRosCreateCacheSegment(PBCB Bcb
,
528 PCACHE_SEGMENT
* CacheSeg
)
531 PCACHE_SEGMENT current
;
532 PCACHE_SEGMENT previous
;
533 PLIST_ENTRY current_entry
;
538 ULONG StartingOffset
;
541 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
545 DPRINT("CcRosCreateCacheSegment()\n");
547 BoundaryAddressMultiple
.QuadPart
= 0;
548 if (FileOffset
>= Bcb
->FileSize
.u
.LowPart
)
551 return STATUS_INVALID_PARAMETER
;
554 current
= ExAllocateFromNPagedLookasideList(&CacheSegLookasideList
);
555 current
->Valid
= FALSE
;
556 current
->Dirty
= FALSE
;
557 current
->PageOut
= FALSE
;
558 current
->FileOffset
= ROUND_DOWN(FileOffset
, Bcb
->CacheSegmentSize
);
560 #if defined(DBG) || defined(KDBG)
563 DPRINT1("CacheMap 0x%p: new Cache Segment: 0x%p\n", Bcb
, current
);
566 current
->MappedCount
= 0;
567 current
->DirtySegmentListEntry
.Flink
= NULL
;
568 current
->DirtySegmentListEntry
.Blink
= NULL
;
569 current
->ReferenceCount
= 1;
570 ExInitializeFastMutex(¤t
->Lock
);
571 ExAcquireFastMutex(¤t
->Lock
);
572 ExAcquireFastMutex(&ViewLock
);
575 /* There is window between the call to CcRosLookupCacheSegment
576 * and CcRosCreateCacheSegment. We must check if a segment on
577 * the fileoffset exist. If there exist a segment, we release
578 * our new created segment and return the existing one.
580 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
581 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
583 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
585 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
586 BcbSegmentListEntry
);
587 if (current
->FileOffset
<= FileOffset
&&
588 (current
->FileOffset
+ Bcb
->CacheSegmentSize
) > FileOffset
)
590 CcRosCacheSegmentIncRefCount(current
);
591 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
592 #if defined(DBG) || defined(KDBG)
595 DPRINT1("CacheMap 0x%p: deleting newly created Cache Segment 0x%p ( found existing one 0x%p )\n",
601 ExReleaseFastMutex(&(*CacheSeg
)->Lock
);
602 ExReleaseFastMutex(&ViewLock
);
603 ExFreeToNPagedLookasideList(&CacheSegLookasideList
, *CacheSeg
);
605 ExAcquireFastMutex(¤t
->Lock
);
606 return STATUS_SUCCESS
;
608 if (current
->FileOffset
< FileOffset
)
610 if (previous
== NULL
)
616 if (previous
->FileOffset
< current
->FileOffset
)
622 current_entry
= current_entry
->Flink
;
624 /* There was no existing segment. */
628 InsertHeadList(&previous
->BcbSegmentListEntry
, ¤t
->BcbSegmentListEntry
);
632 InsertHeadList(&Bcb
->BcbSegmentListHead
, ¤t
->BcbSegmentListEntry
);
634 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
635 InsertTailList(&CacheSegmentListHead
, ¤t
->CacheSegmentListEntry
);
636 InsertTailList(&CacheSegmentLRUListHead
, ¤t
->CacheSegmentLRUListEntry
);
637 ExReleaseFastMutex(&ViewLock
);
639 KeAcquireSpinLock(&CiCacheSegMappingRegionLock
, &oldIrql
);
641 StartingOffset
= RtlFindClearBitsAndSet(&CiCacheSegMappingRegionAllocMap
, Bcb
->CacheSegmentSize
/ PAGE_SIZE
, CiCacheSegMappingRegionHint
);
643 if (StartingOffset
== 0xffffffff)
645 DPRINT1("Out of CacheSeg mapping space\n");
649 current
->BaseAddress
= CiCacheSegMappingRegionBase
+ StartingOffset
* PAGE_SIZE
;
651 if (CiCacheSegMappingRegionHint
== StartingOffset
)
653 CiCacheSegMappingRegionHint
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
656 KeReleaseSpinLock(&CiCacheSegMappingRegionLock
, oldIrql
);
658 MmLockAddressSpace(MmGetKernelAddressSpace());
659 current
->BaseAddress
= NULL
;
660 Status
= MmCreateMemoryArea(MmGetKernelAddressSpace(),
661 MEMORY_AREA_CACHE_SEGMENT
,
662 ¤t
->BaseAddress
,
663 Bcb
->CacheSegmentSize
,
665 (PMEMORY_AREA
*)¤t
->MemoryArea
,
668 BoundaryAddressMultiple
);
669 MmUnlockAddressSpace(MmGetKernelAddressSpace());
670 if (!NT_SUCCESS(Status
))
675 Pfn
= alloca(sizeof(PFN_TYPE
) * (Bcb
->CacheSegmentSize
/ PAGE_SIZE
));
676 for (i
= 0; i
< (Bcb
->CacheSegmentSize
/ PAGE_SIZE
); i
++)
678 Status
= MmRequestPageMemoryConsumer(MC_CACHE
, TRUE
, &Pfn
[i
]);
679 if (!NT_SUCCESS(Status
))
684 Status
= MmCreateVirtualMapping(NULL
,
685 current
->BaseAddress
,
688 Bcb
->CacheSegmentSize
/ PAGE_SIZE
);
689 if (!NT_SUCCESS(Status
))
693 return(STATUS_SUCCESS
);
698 CcRosGetCacheSegmentChain(PBCB Bcb
,
701 PCACHE_SEGMENT
* CacheSeg
)
703 PCACHE_SEGMENT current
;
705 PCACHE_SEGMENT
* CacheSegList
;
706 PCACHE_SEGMENT Previous
= NULL
;
710 DPRINT("CcRosGetCacheSegmentChain()\n");
712 Length
= ROUND_UP(Length
, Bcb
->CacheSegmentSize
);
714 #if defined(__GNUC__)
715 CacheSegList
= alloca(sizeof(PCACHE_SEGMENT
) *
716 (Length
/ Bcb
->CacheSegmentSize
));
717 #elif defined(_MSC_VER)
718 CacheSegList
= _alloca(sizeof(PCACHE_SEGMENT
) *
719 (Length
/ Bcb
->CacheSegmentSize
));
721 #error Unknown compiler for alloca intrinsic stack allocation "function"
725 * Look for a cache segment already mapping the same data.
727 for (i
= 0; i
< (Length
/ Bcb
->CacheSegmentSize
); i
++)
729 ULONG CurrentOffset
= FileOffset
+ (i
* Bcb
->CacheSegmentSize
);
730 current
= CcRosLookupCacheSegment(Bcb
, CurrentOffset
);
733 CacheSegList
[i
] = current
;
737 CcRosCreateCacheSegment(Bcb
, CurrentOffset
, ¤t
);
738 CacheSegList
[i
] = current
;
742 for (i
= 0; i
< (Length
/ Bcb
->CacheSegmentSize
); i
++)
746 *CacheSeg
= CacheSegList
[i
];
747 Previous
= CacheSegList
[i
];
751 Previous
->NextInChain
= CacheSegList
[i
];
752 Previous
= CacheSegList
[i
];
755 Previous
->NextInChain
= NULL
;
757 return(STATUS_SUCCESS
);
762 CcRosGetCacheSegment(PBCB Bcb
,
767 PCACHE_SEGMENT
* CacheSeg
)
769 PCACHE_SEGMENT current
;
774 DPRINT("CcRosGetCacheSegment()\n");
777 * Look for a cache segment already mapping the same data.
779 current
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
783 * Otherwise create a new segment.
785 Status
= CcRosCreateCacheSegment(Bcb
, FileOffset
, ¤t
);
786 if (!NT_SUCCESS(Status
))
792 * Return information about the segment to the caller.
794 *UptoDate
= current
->Valid
;
795 *BaseAddress
= current
->BaseAddress
;
796 DPRINT("*BaseAddress 0x%.8X\n", *BaseAddress
);
798 *BaseOffset
= current
->FileOffset
;
799 return(STATUS_SUCCESS
);
803 CcRosRequestCacheSegment(PBCB Bcb
,
807 PCACHE_SEGMENT
* CacheSeg
)
809 * FUNCTION: Request a page mapping for a BCB
816 if ((FileOffset
% Bcb
->CacheSegmentSize
) != 0)
818 CPRINT("Bad fileoffset %x should be multiple of %x",
819 FileOffset
, Bcb
->CacheSegmentSize
);
823 return(CcRosGetCacheSegment(Bcb
,
833 CcFreeCachePage(PVOID Context
, MEMORY_AREA
* MemoryArea
, PVOID Address
,
834 PFN_TYPE Page
, SWAPENTRY SwapEntry
, BOOLEAN Dirty
)
836 ASSERT(SwapEntry
== 0);
839 MmReleasePageMemoryConsumer(MC_CACHE
, Page
);
844 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg
)
846 * FUNCTION: Releases a cache segment associated with a BCB
856 DPRINT("Freeing cache segment 0x%p\n", CacheSeg
);
857 #if defined(DBG) || defined(KDBG)
858 if ( CacheSeg
->Bcb
->Trace
)
860 DPRINT1("CacheMap 0x%p: deleting Cache Segment: 0x%p\n", CacheSeg
->Bcb
, CacheSeg
);
864 RegionSize
= CacheSeg
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
866 /* Unmap all the pages. */
867 for (i
= 0; i
< RegionSize
; i
++)
869 MmDeleteVirtualMapping(NULL
,
870 CacheSeg
->BaseAddress
+ (i
* PAGE_SIZE
),
874 MmReleasePageMemoryConsumer(MC_CACHE
, Page
);
877 KeAcquireSpinLock(&CiCacheSegMappingRegionLock
, &oldIrql
);
878 /* Deallocate all the pages used. */
879 Base
= (ULONG
)(CacheSeg
->BaseAddress
- CiCacheSegMappingRegionBase
) / PAGE_SIZE
;
881 RtlClearBits(&CiCacheSegMappingRegionAllocMap
, Base
, RegionSize
);
883 CiCacheSegMappingRegionHint
= min (CiCacheSegMappingRegionHint
, Base
);
885 KeReleaseSpinLock(&CiCacheSegMappingRegionLock
, oldIrql
);
887 MmLockAddressSpace(MmGetKernelAddressSpace());
888 MmFreeMemoryArea(MmGetKernelAddressSpace(),
889 CacheSeg
->MemoryArea
,
892 MmUnlockAddressSpace(MmGetKernelAddressSpace());
894 ExFreeToNPagedLookasideList(&CacheSegLookasideList
, CacheSeg
);
895 return(STATUS_SUCCESS
);
900 CcRosFreeCacheSegment(PBCB Bcb
, PCACHE_SEGMENT CacheSeg
)
907 DPRINT("CcRosFreeCacheSegment(Bcb 0x%p, CacheSeg 0x%p)\n",
910 ExAcquireFastMutex(&ViewLock
);
911 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
912 RemoveEntryList(&CacheSeg
->BcbSegmentListEntry
);
913 RemoveEntryList(&CacheSeg
->CacheSegmentListEntry
);
914 RemoveEntryList(&CacheSeg
->CacheSegmentLRUListEntry
);
917 RemoveEntryList(&CacheSeg
->DirtySegmentListEntry
);
918 DirtyPageCount
-= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
921 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
922 ExReleaseFastMutex(&ViewLock
);
924 Status
= CcRosInternalFreeCacheSegment(CacheSeg
);
932 CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointers
,
933 IN PLARGE_INTEGER FileOffset OPTIONAL
,
935 OUT PIO_STATUS_BLOCK IoStatus
)
938 LARGE_INTEGER Offset
;
939 PCACHE_SEGMENT current
;
943 DPRINT("CcFlushCache(SectionObjectPointers 0x%p, FileOffset 0x%p, Length %d, IoStatus 0x%p)\n",
944 SectionObjectPointers
, FileOffset
, Length
, IoStatus
);
946 if (SectionObjectPointers
&& SectionObjectPointers
->SharedCacheMap
)
948 Bcb
= (PBCB
)SectionObjectPointers
->SharedCacheMap
;
952 Offset
= *FileOffset
;
956 Offset
.QuadPart
= (LONGLONG
)0;
957 Length
= Bcb
->FileSize
.u
.LowPart
;
962 IoStatus
->Status
= STATUS_SUCCESS
;
963 IoStatus
->Information
= 0;
968 current
= CcRosLookupCacheSegment (Bcb
, Offset
.u
.LowPart
);
973 Status
= CcRosFlushCacheSegment(current
);
974 if (!NT_SUCCESS(Status
) && IoStatus
!= NULL
)
976 IoStatus
->Status
= Status
;
979 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
980 ExReleaseFastMutex(¤t
->Lock
);
981 CcRosCacheSegmentDecRefCount(current
);
982 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
985 Offset
.QuadPart
+= Bcb
->CacheSegmentSize
;
986 if (Length
> Bcb
->CacheSegmentSize
)
988 Length
-= Bcb
->CacheSegmentSize
;
1000 IoStatus
->Status
= STATUS_INVALID_PARAMETER
;
1007 CcRosDeleteFileCache(PFILE_OBJECT FileObject
, PBCB Bcb
)
1009 * FUNCTION: Releases the BCB associated with a file object
1012 PLIST_ENTRY current_entry
;
1013 PCACHE_SEGMENT current
;
1015 LIST_ENTRY FreeList
;
1021 ExReleaseFastMutex(&ViewLock
);
1023 CcFlushCache(FileObject
->SectionObjectPointer
, NULL
, 0, NULL
);
1025 ExAcquireFastMutex(&ViewLock
);
1027 if (Bcb
->RefCount
== 0)
1029 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
1031 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1032 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1035 FileObject
->SectionObjectPointer
->SharedCacheMap
= NULL
;
1038 * Release all cache segments.
1040 InitializeListHead(&FreeList
);
1041 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
1042 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
1043 while (!IsListEmpty(&Bcb
->BcbSegmentListHead
))
1045 current_entry
= RemoveTailList(&Bcb
->BcbSegmentListHead
);
1046 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, BcbSegmentListEntry
);
1047 RemoveEntryList(¤t
->CacheSegmentListEntry
);
1048 RemoveEntryList(¤t
->CacheSegmentLRUListEntry
);
1051 RemoveEntryList(¤t
->DirtySegmentListEntry
);
1052 DirtyPageCount
-= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
1053 DPRINT1("Freeing dirty segment\n");
1055 InsertHeadList(&FreeList
, ¤t
->BcbSegmentListEntry
);
1057 #if defined(DBG) || defined(KDBG)
1060 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
1062 ExReleaseFastMutex(&ViewLock
);
1063 ObDereferenceObject (Bcb
->FileObject
);
1065 while (!IsListEmpty(&FreeList
))
1067 current_entry
= RemoveTailList(&FreeList
);
1068 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, BcbSegmentListEntry
);
1069 Status
= CcRosInternalFreeCacheSegment(current
);
1071 ExFreeToNPagedLookasideList(&BcbLookasideList
, Bcb
);
1072 ExAcquireFastMutex(&ViewLock
);
1074 return(STATUS_SUCCESS
);
1079 CcRosReferenceCache(PFILE_OBJECT FileObject
)
1082 ExAcquireFastMutex(&ViewLock
);
1083 Bcb
= (PBCB
)FileObject
->SectionObjectPointer
->SharedCacheMap
;
1085 if (Bcb
->RefCount
== 0)
1087 ASSERT(Bcb
->BcbRemoveListEntry
.Flink
!= NULL
);
1088 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1089 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1094 ASSERT(Bcb
->BcbRemoveListEntry
.Flink
== NULL
);
1097 ExReleaseFastMutex(&ViewLock
);
1102 CcRosSetRemoveOnClose(PSECTION_OBJECT_POINTERS SectionObjectPointer
)
1105 DPRINT("CcRosSetRemoveOnClose()\n");
1106 ExAcquireFastMutex(&ViewLock
);
1107 Bcb
= (PBCB
)SectionObjectPointer
->SharedCacheMap
;
1110 Bcb
->RemoveOnClose
= TRUE
;
1111 if (Bcb
->RefCount
== 0)
1113 CcRosDeleteFileCache(Bcb
->FileObject
, Bcb
);
1116 ExReleaseFastMutex(&ViewLock
);
1122 CcRosDereferenceCache(PFILE_OBJECT FileObject
)
1125 ExAcquireFastMutex(&ViewLock
);
1126 Bcb
= (PBCB
)FileObject
->SectionObjectPointer
->SharedCacheMap
;
1128 if (Bcb
->RefCount
> 0)
1131 if (Bcb
->RefCount
== 0)
1133 MmFreeSectionSegments(Bcb
->FileObject
);
1134 if (Bcb
->RemoveOnClose
)
1136 CcRosDeleteFileCache(FileObject
, Bcb
);
1140 Bcb
->TimeStamp
= CcTimeStamp
;
1141 InsertHeadList(&ClosedListHead
, &Bcb
->BcbRemoveListEntry
);
1145 ExReleaseFastMutex(&ViewLock
);
1149 CcRosReleaseFileCache(PFILE_OBJECT FileObject
)
1151 * FUNCTION: Called by the file system when a handle to a file object
1157 ExAcquireFastMutex(&ViewLock
);
1159 if (FileObject
->SectionObjectPointer
->SharedCacheMap
!= NULL
)
1161 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1162 if (FileObject
->PrivateCacheMap
!= NULL
)
1164 FileObject
->PrivateCacheMap
= NULL
;
1165 if (Bcb
->RefCount
> 0)
1168 if (Bcb
->RefCount
== 0)
1170 MmFreeSectionSegments(Bcb
->FileObject
);
1171 if (Bcb
->RemoveOnClose
)
1173 CcRosDeleteFileCache(FileObject
, Bcb
);
1177 Bcb
->TimeStamp
= CcTimeStamp
;
1178 InsertHeadList(&ClosedListHead
, &Bcb
->BcbRemoveListEntry
);
1184 ExReleaseFastMutex(&ViewLock
);
1185 return(STATUS_SUCCESS
);
1190 CcTryToInitializeFileCache(PFILE_OBJECT FileObject
)
1195 ExAcquireFastMutex(&ViewLock
);
1197 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1200 Status
= STATUS_UNSUCCESSFUL
;
1204 if (FileObject
->PrivateCacheMap
== NULL
)
1206 FileObject
->PrivateCacheMap
= Bcb
;
1209 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
1211 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1212 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1214 Status
= STATUS_SUCCESS
;
1216 ExReleaseFastMutex(&ViewLock
);
1223 CcRosInitializeFileCache(PFILE_OBJECT FileObject
,
1224 ULONG CacheSegmentSize
)
1226 * FUNCTION: Initializes a BCB for a file object
1231 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1232 DPRINT("CcRosInitializeFileCache(FileObject 0x%p, Bcb 0x%p, CacheSegmentSize %d)\n",
1233 FileObject
, Bcb
, CacheSegmentSize
);
1235 ExAcquireFastMutex(&ViewLock
);
1238 Bcb
= ExAllocateFromNPagedLookasideList(&BcbLookasideList
);
1241 ExReleaseFastMutex(&ViewLock
);
1242 return(STATUS_UNSUCCESSFUL
);
1244 memset(Bcb
, 0, sizeof(BCB
));
1245 ObReferenceObjectByPointer(FileObject
,
1249 Bcb
->FileObject
= FileObject
;
1250 Bcb
->CacheSegmentSize
= CacheSegmentSize
;
1251 if (FileObject
->FsContext
)
1253 Bcb
->AllocationSize
=
1254 ((PFSRTL_COMMON_FCB_HEADER
)FileObject
->FsContext
)->AllocationSize
;
1256 ((PFSRTL_COMMON_FCB_HEADER
)FileObject
->FsContext
)->FileSize
;
1258 KeInitializeSpinLock(&Bcb
->BcbLock
);
1259 InitializeListHead(&Bcb
->BcbSegmentListHead
);
1260 FileObject
->SectionObjectPointer
->SharedCacheMap
= Bcb
;
1262 if (FileObject
->PrivateCacheMap
== NULL
)
1264 FileObject
->PrivateCacheMap
= Bcb
;
1267 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
1269 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1270 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1272 ExReleaseFastMutex(&ViewLock
);
1274 return(STATUS_SUCCESS
);
1280 PFILE_OBJECT STDCALL
1281 CcGetFileObjectFromSectionPtrs(IN PSECTION_OBJECT_POINTERS SectionObjectPointers
)
1284 if (SectionObjectPointers
&& SectionObjectPointers
->SharedCacheMap
)
1286 Bcb
= (PBCB
)SectionObjectPointers
->SharedCacheMap
;
1288 return Bcb
->FileObject
;
1294 CmLazyCloseThreadMain(PVOID Ignored
)
1296 LARGE_INTEGER Timeout
;
1297 PLIST_ENTRY current_entry
;
1299 ULONG RemoveTimeStamp
;
1302 KeQuerySystemTime (&Timeout
);
1306 Timeout
.QuadPart
+= (LONGLONG
)100000000; // 10sec
1307 Status
= KeWaitForSingleObject(&LazyCloseThreadEvent
,
1313 DPRINT("LazyCloseThreadMain %d\n", CcTimeStamp
);
1315 if (!NT_SUCCESS(Status
))
1317 DbgPrint("LazyCloseThread: Wait failed\n");
1321 if (LazyCloseThreadShouldTerminate
)
1323 DbgPrint("LazyCloseThread: Terminating\n");
1327 ExAcquireFastMutex(&ViewLock
);
1329 if (CcTimeStamp
>= 30)
1331 RemoveTimeStamp
= CcTimeStamp
- 30; /* 5min = 10sec * 30 */
1332 while (!IsListEmpty(&ClosedListHead
))
1334 current_entry
= ClosedListHead
.Blink
;
1335 current
= CONTAINING_RECORD(current_entry
, BCB
, BcbRemoveListEntry
);
1336 if (current
->TimeStamp
>= RemoveTimeStamp
)
1340 CcRosDeleteFileCache(current
->FileObject
, current
);
1343 ExReleaseFastMutex(&ViewLock
);
1355 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
1360 DPRINT("CcInitView()\n");
1362 BoundaryAddressMultiple
.QuadPart
= 0;
1363 CiCacheSegMappingRegionHint
= 0;
1364 CiCacheSegMappingRegionBase
= NULL
;
1366 MmLockAddressSpace(MmGetKernelAddressSpace());
1368 Status
= MmCreateMemoryArea(MmGetKernelAddressSpace(),
1369 MEMORY_AREA_CACHE_SEGMENT
,
1370 &CiCacheSegMappingRegionBase
,
1371 CI_CACHESEG_MAPPING_REGION_SIZE
,
1376 BoundaryAddressMultiple
);
1377 MmUnlockAddressSpace(MmGetKernelAddressSpace());
1378 if (!NT_SUCCESS(Status
))
1383 Buffer
= ExAllocatePool(NonPagedPool
, CI_CACHESEG_MAPPING_REGION_SIZE
/ (PAGE_SIZE
* 8));
1385 RtlInitializeBitMap(&CiCacheSegMappingRegionAllocMap
, Buffer
, CI_CACHESEG_MAPPING_REGION_SIZE
/ PAGE_SIZE
);
1386 RtlClearAllBits(&CiCacheSegMappingRegionAllocMap
);
1388 KeInitializeSpinLock(&CiCacheSegMappingRegionLock
);
1390 InitializeListHead(&CacheSegmentListHead
);
1391 InitializeListHead(&DirtySegmentListHead
);
1392 InitializeListHead(&CacheSegmentLRUListHead
);
1393 InitializeListHead(&ClosedListHead
);
1394 ExInitializeFastMutex(&ViewLock
);
1395 ExInitializeNPagedLookasideList (&iBcbLookasideList
,
1399 sizeof(INTERNAL_BCB
),
1402 ExInitializeNPagedLookasideList (&BcbLookasideList
,
1409 ExInitializeNPagedLookasideList (&CacheSegLookasideList
,
1413 sizeof(CACHE_SEGMENT
),
1417 MmInitializeMemoryConsumer(MC_CACHE
, CcRosTrimCache
);
1419 CcInitCacheZeroPage();
1422 LazyCloseThreadShouldTerminate
= FALSE
;
1423 KeInitializeEvent (&LazyCloseThreadEvent
, SynchronizationEvent
, FALSE
);
1424 Status
= PsCreateSystemThread(&LazyCloseThreadHandle
,
1429 (PKSTART_ROUTINE
)CmLazyCloseThreadMain
,
1431 if (NT_SUCCESS(Status
))
1433 Priority
= LOW_REALTIME_PRIORITY
;
1434 NtSetInformationThread(LazyCloseThreadHandle
,