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
);
162 CcRosFlushCacheSegment(PCACHE_SEGMENT CacheSegment
)
166 Status
= WriteCacheSegment(CacheSegment
);
167 if (NT_SUCCESS(Status
))
169 ExAcquireFastMutex(&ViewLock
);
170 KeAcquireSpinLock(&CacheSegment
->Bcb
->BcbLock
, &oldIrql
);
171 CacheSegment
->Dirty
= FALSE
;
172 RemoveEntryList(&CacheSegment
->DirtySegmentListEntry
);
173 DirtyPageCount
-= CacheSegment
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
174 CcRosCacheSegmentDecRefCount ( CacheSegment
);
175 KeReleaseSpinLock(&CacheSegment
->Bcb
->BcbLock
, oldIrql
);
176 ExReleaseFastMutex(&ViewLock
);
182 CcRosFlushDirtyPages(ULONG Target
, PULONG Count
)
184 PLIST_ENTRY current_entry
;
185 PCACHE_SEGMENT current
;
186 ULONG PagesPerSegment
;
189 static ULONG WriteCount
[4] = {0, 0, 0, 0};
192 DPRINT("CcRosFlushDirtyPages(Target %d)\n", Target
);
196 ExAcquireFastMutex(&ViewLock
);
198 WriteCount
[0] = WriteCount
[1];
199 WriteCount
[1] = WriteCount
[2];
200 WriteCount
[2] = WriteCount
[3];
203 NewTarget
= WriteCount
[0] + WriteCount
[1] + WriteCount
[2];
205 if (NewTarget
< DirtyPageCount
)
207 NewTarget
= (DirtyPageCount
- NewTarget
+ 3) / 4;
208 WriteCount
[0] += NewTarget
;
209 WriteCount
[1] += NewTarget
;
210 WriteCount
[2] += NewTarget
;
211 WriteCount
[3] += NewTarget
;
214 NewTarget
= WriteCount
[0];
216 Target
= max(NewTarget
, Target
);
218 current_entry
= DirtySegmentListHead
.Flink
;
219 if (current_entry
== &DirtySegmentListHead
)
221 DPRINT("No Dirty pages\n");
223 while (current_entry
!= &DirtySegmentListHead
&& Target
> 0)
225 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
226 DirtySegmentListEntry
);
227 current_entry
= current_entry
->Flink
;
228 Locked
= ExTryToAcquireFastMutex(¤t
->Lock
);
233 ASSERT(current
->Dirty
);
234 if (current
->ReferenceCount
> 1)
236 ExReleaseFastMutex(¤t
->Lock
);
239 ExReleaseFastMutex(&ViewLock
);
240 PagesPerSegment
= current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
241 Status
= CcRosFlushCacheSegment(current
);
242 ExReleaseFastMutex(¤t
->Lock
);
243 if (!NT_SUCCESS(Status
) && (Status
!= STATUS_END_OF_FILE
))
245 DPRINT1("CC: Failed to flush cache segment.\n");
249 (*Count
) += PagesPerSegment
;
250 Target
-= PagesPerSegment
;
252 ExAcquireFastMutex(&ViewLock
);
253 current_entry
= DirtySegmentListHead
.Flink
;
255 if (*Count
< NewTarget
)
257 WriteCount
[1] += (NewTarget
- *Count
);
259 ExReleaseFastMutex(&ViewLock
);
260 DPRINT("CcRosFlushDirtyPages() finished\n");
262 return(STATUS_SUCCESS
);
266 CcRosTrimCache(ULONG Target
, ULONG Priority
, PULONG NrFreed
)
268 * FUNCTION: Try to free some memory from the file cache.
270 * Target - The number of pages to be freed.
271 * Priority - The priority of free (currently unused).
272 * NrFreed - Points to a variable where the number of pages
273 * actually freed is returned.
276 PLIST_ENTRY current_entry
;
277 PCACHE_SEGMENT current
, last
= NULL
;
278 ULONG PagesPerSegment
;
283 DPRINT("CcRosTrimCache(Target %d)\n", Target
);
287 InitializeListHead(&FreeList
);
289 ExAcquireFastMutex(&ViewLock
);
290 current_entry
= CacheSegmentLRUListHead
.Flink
;
291 while (current_entry
!= &CacheSegmentLRUListHead
&& Target
> 0)
293 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
294 CacheSegmentLRUListEntry
);
295 current_entry
= current_entry
->Flink
;
297 KeAcquireSpinLock(¤t
->Bcb
->BcbLock
, &oldIrql
);
298 if (current
->ReferenceCount
== 0)
300 RemoveEntryList(¤t
->BcbSegmentListEntry
);
301 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
302 RemoveEntryList(¤t
->CacheSegmentListEntry
);
303 RemoveEntryList(¤t
->CacheSegmentLRUListEntry
);
304 InsertHeadList(&FreeList
, ¤t
->BcbSegmentListEntry
);
305 PagesPerSegment
= current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
306 PagesFreed
= min(PagesPerSegment
, Target
);
307 Target
-= PagesFreed
;
308 (*NrFreed
) += PagesFreed
;
312 if (last
!= current
&& current
->MappedCount
> 0 && !current
->Dirty
&& !current
->PageOut
)
317 CcRosCacheSegmentIncRefCount(current
);
319 current
->PageOut
= TRUE
;
320 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
321 ExReleaseFastMutex(&ViewLock
);
322 for (i
= 0; i
< current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
; i
++)
325 Page
= MmGetPhysicalAddress((char*)current
->BaseAddress
+ i
* PAGE_SIZE
).QuadPart
>> PAGE_SHIFT
;
326 Status
= MmPageOutPhysicalAddress(Page
);
327 if (!NT_SUCCESS(Status
))
332 ExAcquireFastMutex(&ViewLock
);
333 KeAcquireSpinLock(¤t
->Bcb
->BcbLock
, &oldIrql
);
334 CcRosCacheSegmentDecRefCount(current
);
335 current
->PageOut
= FALSE
;
336 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
337 current_entry
= ¤t
->CacheSegmentLRUListEntry
;
340 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
343 ExReleaseFastMutex(&ViewLock
);
345 while (!IsListEmpty(&FreeList
))
347 current_entry
= RemoveHeadList(&FreeList
);
348 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
349 BcbSegmentListEntry
);
350 CcRosInternalFreeCacheSegment(current
);
353 DPRINT("CcRosTrimCache() finished\n");
354 return(STATUS_SUCCESS
);
358 CcRosReleaseCacheSegment(PBCB Bcb
,
359 PCACHE_SEGMENT CacheSeg
,
364 BOOLEAN WasDirty
= CacheSeg
->Dirty
;
369 DPRINT("CcReleaseCacheSegment(Bcb 0x%p, CacheSeg 0x%p, Valid %d)\n",
370 Bcb
, CacheSeg
, Valid
);
372 CacheSeg
->Valid
= Valid
;
373 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| Dirty
;
375 ExAcquireFastMutex(&ViewLock
);
376 if (!WasDirty
&& CacheSeg
->Dirty
)
378 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
379 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
381 RemoveEntryList(&CacheSeg
->CacheSegmentLRUListEntry
);
382 InsertTailList(&CacheSegmentLRUListHead
, &CacheSeg
->CacheSegmentLRUListEntry
);
386 CacheSeg
->MappedCount
++;
388 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
389 CcRosCacheSegmentDecRefCount(CacheSeg
);
390 if (Mapped
&& CacheSeg
->MappedCount
== 1)
392 CcRosCacheSegmentIncRefCount(CacheSeg
);
394 if (!WasDirty
&& CacheSeg
->Dirty
)
396 CcRosCacheSegmentIncRefCount(CacheSeg
);
398 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
399 ExReleaseFastMutex(&ViewLock
);
400 ExReleaseFastMutex(&CacheSeg
->Lock
);
402 return(STATUS_SUCCESS
);
406 CcRosLookupCacheSegment(PBCB Bcb
, ULONG FileOffset
)
408 PLIST_ENTRY current_entry
;
409 PCACHE_SEGMENT current
;
414 DPRINT("CcRosLookupCacheSegment(Bcb -x%p, FileOffset %d)\n", Bcb
, FileOffset
);
416 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
417 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
418 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
420 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
421 BcbSegmentListEntry
);
422 if (current
->FileOffset
<= FileOffset
&&
423 (current
->FileOffset
+ Bcb
->CacheSegmentSize
) > FileOffset
)
425 CcRosCacheSegmentIncRefCount(current
);
426 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
427 ExAcquireFastMutex(¤t
->Lock
);
430 current_entry
= current_entry
->Flink
;
432 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
437 CcRosMarkDirtyCacheSegment(PBCB Bcb
, ULONG FileOffset
)
439 PCACHE_SEGMENT CacheSeg
;
444 DPRINT("CcRosMarkDirtyCacheSegment(Bcb 0x%p, FileOffset %d)\n", Bcb
, FileOffset
);
446 CacheSeg
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
447 if (CacheSeg
== NULL
)
451 if (!CacheSeg
->Dirty
)
453 ExAcquireFastMutex(&ViewLock
);
454 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
455 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
456 ExReleaseFastMutex(&ViewLock
);
460 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
461 CcRosCacheSegmentDecRefCount(CacheSeg
);
462 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
466 CacheSeg
->Dirty
= TRUE
;
467 ExReleaseFastMutex(&CacheSeg
->Lock
);
469 return(STATUS_SUCCESS
);
473 CcRosUnmapCacheSegment(PBCB Bcb
, ULONG FileOffset
, BOOLEAN NowDirty
)
475 PCACHE_SEGMENT CacheSeg
;
481 DPRINT("CcRosUnmapCacheSegment(Bcb 0x%p, FileOffset %d, NowDirty %d)\n",
482 Bcb
, FileOffset
, NowDirty
);
484 CacheSeg
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
485 if (CacheSeg
== NULL
)
487 return(STATUS_UNSUCCESSFUL
);
490 WasDirty
= CacheSeg
->Dirty
;
491 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| NowDirty
;
493 CacheSeg
->MappedCount
--;
495 if (!WasDirty
&& NowDirty
)
497 ExAcquireFastMutex(&ViewLock
);
498 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
499 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
500 ExReleaseFastMutex(&ViewLock
);
503 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
504 CcRosCacheSegmentDecRefCount(CacheSeg
);
505 if (!WasDirty
&& NowDirty
)
507 CcRosCacheSegmentIncRefCount(CacheSeg
);
509 if (CacheSeg
->MappedCount
== 0)
511 CcRosCacheSegmentDecRefCount(CacheSeg
);
513 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
515 ExReleaseFastMutex(&CacheSeg
->Lock
);
516 return(STATUS_SUCCESS
);
520 CcRosCreateCacheSegment(PBCB Bcb
,
522 PCACHE_SEGMENT
* CacheSeg
)
525 PCACHE_SEGMENT current
;
526 PCACHE_SEGMENT previous
;
527 PLIST_ENTRY current_entry
;
532 ULONG StartingOffset
;
535 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
539 DPRINT("CcRosCreateCacheSegment()\n");
541 BoundaryAddressMultiple
.QuadPart
= 0;
542 if (FileOffset
>= Bcb
->FileSize
.u
.LowPart
)
545 return STATUS_INVALID_PARAMETER
;
548 current
= ExAllocateFromNPagedLookasideList(&CacheSegLookasideList
);
549 current
->Valid
= FALSE
;
550 current
->Dirty
= FALSE
;
551 current
->PageOut
= FALSE
;
552 current
->FileOffset
= ROUND_DOWN(FileOffset
, Bcb
->CacheSegmentSize
);
554 #if defined(DBG) || defined(KDBG)
557 DPRINT1("CacheMap 0x%p: new Cache Segment: 0x%p\n", Bcb
, current
);
560 current
->MappedCount
= 0;
561 current
->DirtySegmentListEntry
.Flink
= NULL
;
562 current
->DirtySegmentListEntry
.Blink
= NULL
;
563 current
->ReferenceCount
= 1;
564 ExInitializeFastMutex(¤t
->Lock
);
565 ExAcquireFastMutex(¤t
->Lock
);
566 ExAcquireFastMutex(&ViewLock
);
569 /* There is window between the call to CcRosLookupCacheSegment
570 * and CcRosCreateCacheSegment. We must check if a segment on
571 * the fileoffset exist. If there exist a segment, we release
572 * our new created segment and return the existing one.
574 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
575 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
577 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
579 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
580 BcbSegmentListEntry
);
581 if (current
->FileOffset
<= FileOffset
&&
582 (current
->FileOffset
+ Bcb
->CacheSegmentSize
) > FileOffset
)
584 CcRosCacheSegmentIncRefCount(current
);
585 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
586 #if defined(DBG) || defined(KDBG)
589 DPRINT1("CacheMap 0x%p: deleting newly created Cache Segment 0x%p ( found existing one 0x%p )\n",
595 ExReleaseFastMutex(&(*CacheSeg
)->Lock
);
596 ExReleaseFastMutex(&ViewLock
);
597 ExFreeToNPagedLookasideList(&CacheSegLookasideList
, *CacheSeg
);
599 ExAcquireFastMutex(¤t
->Lock
);
600 return STATUS_SUCCESS
;
602 if (current
->FileOffset
< FileOffset
)
604 if (previous
== NULL
)
610 if (previous
->FileOffset
< current
->FileOffset
)
616 current_entry
= current_entry
->Flink
;
618 /* There was no existing segment. */
622 InsertHeadList(&previous
->BcbSegmentListEntry
, ¤t
->BcbSegmentListEntry
);
626 InsertHeadList(&Bcb
->BcbSegmentListHead
, ¤t
->BcbSegmentListEntry
);
628 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
629 InsertTailList(&CacheSegmentListHead
, ¤t
->CacheSegmentListEntry
);
630 InsertTailList(&CacheSegmentLRUListHead
, ¤t
->CacheSegmentLRUListEntry
);
631 ExReleaseFastMutex(&ViewLock
);
633 KeAcquireSpinLock(&CiCacheSegMappingRegionLock
, &oldIrql
);
635 StartingOffset
= RtlFindClearBitsAndSet(&CiCacheSegMappingRegionAllocMap
, Bcb
->CacheSegmentSize
/ PAGE_SIZE
, CiCacheSegMappingRegionHint
);
637 if (StartingOffset
== 0xffffffff)
639 DPRINT1("Out of CacheSeg mapping space\n");
643 current
->BaseAddress
= CiCacheSegMappingRegionBase
+ StartingOffset
* PAGE_SIZE
;
645 if (CiCacheSegMappingRegionHint
== StartingOffset
)
647 CiCacheSegMappingRegionHint
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
650 KeReleaseSpinLock(&CiCacheSegMappingRegionLock
, oldIrql
);
652 MmLockAddressSpace(MmGetKernelAddressSpace());
653 current
->BaseAddress
= NULL
;
654 Status
= MmCreateMemoryArea(NULL
,
655 MmGetKernelAddressSpace(),
656 MEMORY_AREA_CACHE_SEGMENT
,
657 ¤t
->BaseAddress
,
658 Bcb
->CacheSegmentSize
,
660 (PMEMORY_AREA
*)¤t
->MemoryArea
,
663 BoundaryAddressMultiple
);
664 MmUnlockAddressSpace(MmGetKernelAddressSpace());
665 if (!NT_SUCCESS(Status
))
670 Pfn
= alloca(sizeof(PFN_TYPE
) * (Bcb
->CacheSegmentSize
/ PAGE_SIZE
));
671 for (i
= 0; i
< (Bcb
->CacheSegmentSize
/ PAGE_SIZE
); i
++)
673 Status
= MmRequestPageMemoryConsumer(MC_CACHE
, TRUE
, &Pfn
[i
]);
674 if (!NT_SUCCESS(Status
))
679 Status
= MmCreateVirtualMapping(NULL
,
680 current
->BaseAddress
,
683 Bcb
->CacheSegmentSize
/ PAGE_SIZE
);
684 if (!NT_SUCCESS(Status
))
688 return(STATUS_SUCCESS
);
692 CcRosGetCacheSegmentChain(PBCB Bcb
,
695 PCACHE_SEGMENT
* CacheSeg
)
697 PCACHE_SEGMENT current
;
699 PCACHE_SEGMENT
* CacheSegList
;
700 PCACHE_SEGMENT Previous
= NULL
;
704 DPRINT("CcRosGetCacheSegmentChain()\n");
706 Length
= ROUND_UP(Length
, Bcb
->CacheSegmentSize
);
708 #if defined(__GNUC__)
709 CacheSegList
= alloca(sizeof(PCACHE_SEGMENT
) *
710 (Length
/ Bcb
->CacheSegmentSize
));
711 #elif defined(_MSC_VER)
712 CacheSegList
= _alloca(sizeof(PCACHE_SEGMENT
) *
713 (Length
/ Bcb
->CacheSegmentSize
));
715 #error Unknown compiler for alloca intrinsic stack allocation "function"
719 * Look for a cache segment already mapping the same data.
721 for (i
= 0; i
< (Length
/ Bcb
->CacheSegmentSize
); i
++)
723 ULONG CurrentOffset
= FileOffset
+ (i
* Bcb
->CacheSegmentSize
);
724 current
= CcRosLookupCacheSegment(Bcb
, CurrentOffset
);
727 CacheSegList
[i
] = current
;
731 CcRosCreateCacheSegment(Bcb
, CurrentOffset
, ¤t
);
732 CacheSegList
[i
] = current
;
736 for (i
= 0; i
< (Length
/ Bcb
->CacheSegmentSize
); i
++)
740 *CacheSeg
= CacheSegList
[i
];
741 Previous
= CacheSegList
[i
];
745 Previous
->NextInChain
= CacheSegList
[i
];
746 Previous
= CacheSegList
[i
];
749 Previous
->NextInChain
= NULL
;
751 return(STATUS_SUCCESS
);
755 CcRosGetCacheSegment(PBCB Bcb
,
760 PCACHE_SEGMENT
* CacheSeg
)
762 PCACHE_SEGMENT current
;
767 DPRINT("CcRosGetCacheSegment()\n");
770 * Look for a cache segment already mapping the same data.
772 current
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
776 * Otherwise create a new segment.
778 Status
= CcRosCreateCacheSegment(Bcb
, FileOffset
, ¤t
);
779 if (!NT_SUCCESS(Status
))
785 * Return information about the segment to the caller.
787 *UptoDate
= current
->Valid
;
788 *BaseAddress
= current
->BaseAddress
;
789 DPRINT("*BaseAddress 0x%.8X\n", *BaseAddress
);
791 *BaseOffset
= current
->FileOffset
;
792 return(STATUS_SUCCESS
);
796 CcRosRequestCacheSegment(PBCB Bcb
,
800 PCACHE_SEGMENT
* CacheSeg
)
802 * FUNCTION: Request a page mapping for a BCB
809 if ((FileOffset
% Bcb
->CacheSegmentSize
) != 0)
811 CPRINT("Bad fileoffset %x should be multiple of %x",
812 FileOffset
, Bcb
->CacheSegmentSize
);
816 return(CcRosGetCacheSegment(Bcb
,
826 CcFreeCachePage(PVOID Context
, MEMORY_AREA
* MemoryArea
, PVOID Address
,
827 PFN_TYPE Page
, SWAPENTRY SwapEntry
, BOOLEAN Dirty
)
829 ASSERT(SwapEntry
== 0);
832 MmReleasePageMemoryConsumer(MC_CACHE
, Page
);
837 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg
)
839 * FUNCTION: Releases a cache segment associated with a BCB
849 DPRINT("Freeing cache segment 0x%p\n", CacheSeg
);
850 #if defined(DBG) || defined(KDBG)
851 if ( CacheSeg
->Bcb
->Trace
)
853 DPRINT1("CacheMap 0x%p: deleting Cache Segment: 0x%p\n", CacheSeg
->Bcb
, CacheSeg
);
857 RegionSize
= CacheSeg
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
859 /* Unmap all the pages. */
860 for (i
= 0; i
< RegionSize
; i
++)
862 MmDeleteVirtualMapping(NULL
,
863 CacheSeg
->BaseAddress
+ (i
* PAGE_SIZE
),
867 MmReleasePageMemoryConsumer(MC_CACHE
, Page
);
870 KeAcquireSpinLock(&CiCacheSegMappingRegionLock
, &oldIrql
);
871 /* Deallocate all the pages used. */
872 Base
= (ULONG
)(CacheSeg
->BaseAddress
- CiCacheSegMappingRegionBase
) / PAGE_SIZE
;
874 RtlClearBits(&CiCacheSegMappingRegionAllocMap
, Base
, RegionSize
);
876 CiCacheSegMappingRegionHint
= min (CiCacheSegMappingRegionHint
, Base
);
878 KeReleaseSpinLock(&CiCacheSegMappingRegionLock
, oldIrql
);
880 MmLockAddressSpace(MmGetKernelAddressSpace());
881 MmFreeMemoryArea(MmGetKernelAddressSpace(),
882 CacheSeg
->MemoryArea
,
885 MmUnlockAddressSpace(MmGetKernelAddressSpace());
887 ExFreeToNPagedLookasideList(&CacheSegLookasideList
, CacheSeg
);
888 return(STATUS_SUCCESS
);
892 CcRosFreeCacheSegment(PBCB Bcb
, PCACHE_SEGMENT CacheSeg
)
899 DPRINT("CcRosFreeCacheSegment(Bcb 0x%p, CacheSeg 0x%p)\n",
902 ExAcquireFastMutex(&ViewLock
);
903 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
904 RemoveEntryList(&CacheSeg
->BcbSegmentListEntry
);
905 RemoveEntryList(&CacheSeg
->CacheSegmentListEntry
);
906 RemoveEntryList(&CacheSeg
->CacheSegmentLRUListEntry
);
909 RemoveEntryList(&CacheSeg
->DirtySegmentListEntry
);
910 DirtyPageCount
-= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
913 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
914 ExReleaseFastMutex(&ViewLock
);
916 Status
= CcRosInternalFreeCacheSegment(CacheSeg
);
924 CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointers
,
925 IN PLARGE_INTEGER FileOffset OPTIONAL
,
927 OUT PIO_STATUS_BLOCK IoStatus
)
930 LARGE_INTEGER Offset
;
931 PCACHE_SEGMENT current
;
935 DPRINT("CcFlushCache(SectionObjectPointers 0x%p, FileOffset 0x%p, Length %d, IoStatus 0x%p)\n",
936 SectionObjectPointers
, FileOffset
, Length
, IoStatus
);
938 if (SectionObjectPointers
&& SectionObjectPointers
->SharedCacheMap
)
940 Bcb
= (PBCB
)SectionObjectPointers
->SharedCacheMap
;
944 Offset
= *FileOffset
;
948 Offset
.QuadPart
= (LONGLONG
)0;
949 Length
= Bcb
->FileSize
.u
.LowPart
;
954 IoStatus
->Status
= STATUS_SUCCESS
;
955 IoStatus
->Information
= 0;
960 current
= CcRosLookupCacheSegment (Bcb
, Offset
.u
.LowPart
);
965 Status
= CcRosFlushCacheSegment(current
);
966 if (!NT_SUCCESS(Status
) && IoStatus
!= NULL
)
968 IoStatus
->Status
= Status
;
971 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
972 ExReleaseFastMutex(¤t
->Lock
);
973 CcRosCacheSegmentDecRefCount(current
);
974 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
977 Offset
.QuadPart
+= Bcb
->CacheSegmentSize
;
978 if (Length
> Bcb
->CacheSegmentSize
)
980 Length
-= Bcb
->CacheSegmentSize
;
992 IoStatus
->Status
= STATUS_INVALID_PARAMETER
;
998 CcRosDeleteFileCache(PFILE_OBJECT FileObject
, PBCB Bcb
)
1000 * FUNCTION: Releases the BCB associated with a file object
1003 PLIST_ENTRY current_entry
;
1004 PCACHE_SEGMENT current
;
1006 LIST_ENTRY FreeList
;
1012 ExReleaseFastMutex(&ViewLock
);
1014 CcFlushCache(FileObject
->SectionObjectPointer
, NULL
, 0, NULL
);
1016 ExAcquireFastMutex(&ViewLock
);
1018 if (Bcb
->RefCount
== 0)
1020 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
1022 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1023 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1026 FileObject
->SectionObjectPointer
->SharedCacheMap
= NULL
;
1029 * Release all cache segments.
1031 InitializeListHead(&FreeList
);
1032 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
1033 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
1034 while (!IsListEmpty(&Bcb
->BcbSegmentListHead
))
1036 current_entry
= RemoveTailList(&Bcb
->BcbSegmentListHead
);
1037 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, BcbSegmentListEntry
);
1038 RemoveEntryList(¤t
->CacheSegmentListEntry
);
1039 RemoveEntryList(¤t
->CacheSegmentLRUListEntry
);
1042 RemoveEntryList(¤t
->DirtySegmentListEntry
);
1043 DirtyPageCount
-= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
1044 DPRINT1("Freeing dirty segment\n");
1046 InsertHeadList(&FreeList
, ¤t
->BcbSegmentListEntry
);
1048 #if defined(DBG) || defined(KDBG)
1051 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
1053 ExReleaseFastMutex(&ViewLock
);
1054 ObDereferenceObject (Bcb
->FileObject
);
1056 while (!IsListEmpty(&FreeList
))
1058 current_entry
= RemoveTailList(&FreeList
);
1059 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, BcbSegmentListEntry
);
1060 Status
= CcRosInternalFreeCacheSegment(current
);
1062 ExFreeToNPagedLookasideList(&BcbLookasideList
, Bcb
);
1063 ExAcquireFastMutex(&ViewLock
);
1065 return(STATUS_SUCCESS
);
1068 VOID
CcRosReferenceCache(PFILE_OBJECT FileObject
)
1071 ExAcquireFastMutex(&ViewLock
);
1072 Bcb
= (PBCB
)FileObject
->SectionObjectPointer
->SharedCacheMap
;
1074 if (Bcb
->RefCount
== 0)
1076 ASSERT(Bcb
->BcbRemoveListEntry
.Flink
!= NULL
);
1077 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1078 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1083 ASSERT(Bcb
->BcbRemoveListEntry
.Flink
== NULL
);
1086 ExReleaseFastMutex(&ViewLock
);
1089 VOID
CcRosSetRemoveOnClose(PSECTION_OBJECT_POINTERS SectionObjectPointer
)
1092 DPRINT("CcRosSetRemoveOnClose()\n");
1093 ExAcquireFastMutex(&ViewLock
);
1094 Bcb
= (PBCB
)SectionObjectPointer
->SharedCacheMap
;
1097 Bcb
->RemoveOnClose
= TRUE
;
1098 if (Bcb
->RefCount
== 0)
1100 CcRosDeleteFileCache(Bcb
->FileObject
, Bcb
);
1103 ExReleaseFastMutex(&ViewLock
);
1107 VOID
CcRosDereferenceCache(PFILE_OBJECT FileObject
)
1110 ExAcquireFastMutex(&ViewLock
);
1111 Bcb
= (PBCB
)FileObject
->SectionObjectPointer
->SharedCacheMap
;
1113 if (Bcb
->RefCount
> 0)
1116 if (Bcb
->RefCount
== 0)
1118 MmFreeSectionSegments(Bcb
->FileObject
);
1119 if (Bcb
->RemoveOnClose
)
1121 CcRosDeleteFileCache(FileObject
, Bcb
);
1125 Bcb
->TimeStamp
= CcTimeStamp
;
1126 InsertHeadList(&ClosedListHead
, &Bcb
->BcbRemoveListEntry
);
1130 ExReleaseFastMutex(&ViewLock
);
1134 CcRosReleaseFileCache(PFILE_OBJECT FileObject
)
1136 * FUNCTION: Called by the file system when a handle to a file object
1142 ExAcquireFastMutex(&ViewLock
);
1144 if (FileObject
->SectionObjectPointer
->SharedCacheMap
!= NULL
)
1146 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1147 if (FileObject
->PrivateCacheMap
!= NULL
)
1149 FileObject
->PrivateCacheMap
= NULL
;
1150 if (Bcb
->RefCount
> 0)
1153 if (Bcb
->RefCount
== 0)
1155 MmFreeSectionSegments(Bcb
->FileObject
);
1156 if (Bcb
->RemoveOnClose
)
1158 CcRosDeleteFileCache(FileObject
, Bcb
);
1162 Bcb
->TimeStamp
= CcTimeStamp
;
1163 InsertHeadList(&ClosedListHead
, &Bcb
->BcbRemoveListEntry
);
1169 ExReleaseFastMutex(&ViewLock
);
1170 return(STATUS_SUCCESS
);
1174 CcTryToInitializeFileCache(PFILE_OBJECT FileObject
)
1179 ExAcquireFastMutex(&ViewLock
);
1181 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1184 Status
= STATUS_UNSUCCESSFUL
;
1188 if (FileObject
->PrivateCacheMap
== NULL
)
1190 FileObject
->PrivateCacheMap
= Bcb
;
1193 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
1195 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1196 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1198 Status
= STATUS_SUCCESS
;
1200 ExReleaseFastMutex(&ViewLock
);
1207 CcRosInitializeFileCache(PFILE_OBJECT FileObject
,
1208 ULONG CacheSegmentSize
)
1210 * FUNCTION: Initializes a BCB for a file object
1215 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1216 DPRINT("CcRosInitializeFileCache(FileObject 0x%p, Bcb 0x%p, CacheSegmentSize %d)\n",
1217 FileObject
, Bcb
, CacheSegmentSize
);
1219 ExAcquireFastMutex(&ViewLock
);
1222 Bcb
= ExAllocateFromNPagedLookasideList(&BcbLookasideList
);
1225 ExReleaseFastMutex(&ViewLock
);
1226 return(STATUS_UNSUCCESSFUL
);
1228 memset(Bcb
, 0, sizeof(BCB
));
1229 ObReferenceObjectByPointer(FileObject
,
1233 Bcb
->FileObject
= FileObject
;
1234 Bcb
->CacheSegmentSize
= CacheSegmentSize
;
1235 if (FileObject
->FsContext
)
1237 Bcb
->AllocationSize
=
1238 ((PFSRTL_COMMON_FCB_HEADER
)FileObject
->FsContext
)->AllocationSize
;
1240 ((PFSRTL_COMMON_FCB_HEADER
)FileObject
->FsContext
)->FileSize
;
1242 KeInitializeSpinLock(&Bcb
->BcbLock
);
1243 InitializeListHead(&Bcb
->BcbSegmentListHead
);
1244 FileObject
->SectionObjectPointer
->SharedCacheMap
= Bcb
;
1246 if (FileObject
->PrivateCacheMap
== NULL
)
1248 FileObject
->PrivateCacheMap
= Bcb
;
1251 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
1253 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1254 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1256 ExReleaseFastMutex(&ViewLock
);
1258 return(STATUS_SUCCESS
);
1264 PFILE_OBJECT STDCALL
1265 CcGetFileObjectFromSectionPtrs(IN PSECTION_OBJECT_POINTERS SectionObjectPointers
)
1268 if (SectionObjectPointers
&& SectionObjectPointers
->SharedCacheMap
)
1270 Bcb
= (PBCB
)SectionObjectPointers
->SharedCacheMap
;
1272 return Bcb
->FileObject
;
1278 CmLazyCloseThreadMain(PVOID Ignored
)
1280 LARGE_INTEGER Timeout
;
1281 PLIST_ENTRY current_entry
;
1283 ULONG RemoveTimeStamp
;
1286 KeQuerySystemTime (&Timeout
);
1290 Timeout
.QuadPart
+= (LONGLONG
)100000000; // 10sec
1291 Status
= KeWaitForSingleObject(&LazyCloseThreadEvent
,
1297 DPRINT("LazyCloseThreadMain %d\n", CcTimeStamp
);
1299 if (!NT_SUCCESS(Status
))
1301 DbgPrint("LazyCloseThread: Wait failed\n");
1305 if (LazyCloseThreadShouldTerminate
)
1307 DbgPrint("LazyCloseThread: Terminating\n");
1311 ExAcquireFastMutex(&ViewLock
);
1313 if (CcTimeStamp
>= 30)
1315 RemoveTimeStamp
= CcTimeStamp
- 30; /* 5min = 10sec * 30 */
1316 while (!IsListEmpty(&ClosedListHead
))
1318 current_entry
= ClosedListHead
.Blink
;
1319 current
= CONTAINING_RECORD(current_entry
, BCB
, BcbRemoveListEntry
);
1320 if (current
->TimeStamp
>= RemoveTimeStamp
)
1324 CcRosDeleteFileCache(current
->FileObject
, current
);
1327 ExReleaseFastMutex(&ViewLock
);
1337 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
1342 DPRINT("CcInitView()\n");
1344 BoundaryAddressMultiple
.QuadPart
= 0;
1345 CiCacheSegMappingRegionHint
= 0;
1346 CiCacheSegMappingRegionBase
= NULL
;
1348 MmLockAddressSpace(MmGetKernelAddressSpace());
1350 Status
= MmCreateMemoryArea(NULL
,
1351 MmGetKernelAddressSpace(),
1352 MEMORY_AREA_CACHE_SEGMENT
,
1353 &CiCacheSegMappingRegionBase
,
1354 CI_CACHESEG_MAPPING_REGION_SIZE
,
1359 BoundaryAddressMultiple
);
1360 MmUnlockAddressSpace(MmGetKernelAddressSpace());
1361 if (!NT_SUCCESS(Status
))
1366 Buffer
= ExAllocatePool(NonPagedPool
, CI_CACHESEG_MAPPING_REGION_SIZE
/ (PAGE_SIZE
* 8));
1368 RtlInitializeBitMap(&CiCacheSegMappingRegionAllocMap
, Buffer
, CI_CACHESEG_MAPPING_REGION_SIZE
/ PAGE_SIZE
);
1369 RtlClearAllBits(&CiCacheSegMappingRegionAllocMap
);
1371 KeInitializeSpinLock(&CiCacheSegMappingRegionLock
);
1373 InitializeListHead(&CacheSegmentListHead
);
1374 InitializeListHead(&DirtySegmentListHead
);
1375 InitializeListHead(&CacheSegmentLRUListHead
);
1376 InitializeListHead(&ClosedListHead
);
1377 ExInitializeFastMutex(&ViewLock
);
1378 ExInitializeNPagedLookasideList (&iBcbLookasideList
,
1382 sizeof(INTERNAL_BCB
),
1385 ExInitializeNPagedLookasideList (&BcbLookasideList
,
1392 ExInitializeNPagedLookasideList (&CacheSegLookasideList
,
1396 sizeof(CACHE_SEGMENT
),
1400 MmInitializeMemoryConsumer(MC_CACHE
, CcRosTrimCache
);
1402 CcInitCacheZeroPage();
1405 LazyCloseThreadShouldTerminate
= FALSE
;
1406 KeInitializeEvent (&LazyCloseThreadEvent
, SynchronizationEvent
, FALSE
);
1407 Status
= PsCreateSystemThread(&LazyCloseThreadHandle
,
1412 (PKSTART_ROUTINE
)CmLazyCloseThreadMain
,
1414 if (NT_SUCCESS(Status
))
1416 Priority
= LOW_REALTIME_PRIORITY
;
1417 NtSetInformationThread(LazyCloseThreadHandle
,