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"
86 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg
);
88 /* FUNCTIONS *****************************************************************/
91 CcRosFlushCacheSegment(PCACHE_SEGMENT CacheSegment
)
95 Status
= WriteCacheSegment(CacheSegment
);
96 if (NT_SUCCESS(Status
))
98 ExAcquireFastMutex(&ViewLock
);
99 KeAcquireSpinLock(&CacheSegment
->Bcb
->BcbLock
, &oldIrql
);
100 CacheSegment
->Dirty
= FALSE
;
101 RemoveEntryList(&CacheSegment
->DirtySegmentListEntry
);
102 DirtyPageCount
-= CacheSegment
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
103 CacheSegment
->ReferenceCount
--;
104 KeReleaseSpinLock(&CacheSegment
->Bcb
->BcbLock
, oldIrql
);
105 ExReleaseFastMutex(&ViewLock
);
111 CcRosFlushDirtyPages(ULONG Target
, PULONG Count
)
113 PLIST_ENTRY current_entry
;
114 PCACHE_SEGMENT current
;
115 ULONG PagesPerSegment
;
118 static ULONG WriteCount
[4] = {0, 0, 0, 0};
121 DPRINT("CcRosFlushDirtyPages(Target %d)\n", Target
);
125 ExAcquireFastMutex(&ViewLock
);
127 WriteCount
[0] = WriteCount
[1];
128 WriteCount
[1] = WriteCount
[2];
129 WriteCount
[2] = WriteCount
[3];
132 NewTarget
= WriteCount
[0] + WriteCount
[1] + WriteCount
[2];
134 if (NewTarget
< DirtyPageCount
)
136 NewTarget
= (DirtyPageCount
- NewTarget
+ 3) / 4;
137 WriteCount
[0] += NewTarget
;
138 WriteCount
[1] += NewTarget
;
139 WriteCount
[2] += NewTarget
;
140 WriteCount
[3] += NewTarget
;
143 NewTarget
= WriteCount
[0];
145 Target
= max(NewTarget
, Target
);
147 current_entry
= DirtySegmentListHead
.Flink
;
148 if (current_entry
== &DirtySegmentListHead
)
150 DPRINT("No Dirty pages\n");
152 while (current_entry
!= &DirtySegmentListHead
&& Target
> 0)
154 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
155 DirtySegmentListEntry
);
156 current_entry
= current_entry
->Flink
;
157 Locked
= ExTryToAcquireFastMutex(¤t
->Lock
);
162 ASSERT(current
->Dirty
);
163 if (current
->ReferenceCount
> 1)
165 ExReleaseFastMutex(¤t
->Lock
);
168 ExReleaseFastMutex(&ViewLock
);
169 PagesPerSegment
= current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
170 Status
= CcRosFlushCacheSegment(current
);
171 ExReleaseFastMutex(¤t
->Lock
);
172 if (!NT_SUCCESS(Status
) && (Status
!= STATUS_END_OF_FILE
))
174 DPRINT1("CC: Failed to flush cache segment.\n");
178 (*Count
) += PagesPerSegment
;
179 Target
-= PagesPerSegment
;
181 ExAcquireFastMutex(&ViewLock
);
182 current_entry
= DirtySegmentListHead
.Flink
;
184 if (*Count
< NewTarget
)
186 WriteCount
[1] += (NewTarget
- *Count
);
188 ExReleaseFastMutex(&ViewLock
);
189 DPRINT("CcRosFlushDirtyPages() finished\n");
191 return(STATUS_SUCCESS
);
195 CcRosTrimCache(ULONG Target
, ULONG Priority
, PULONG NrFreed
)
197 * FUNCTION: Try to free some memory from the file cache.
199 * Target - The number of pages to be freed.
200 * Priority - The priority of free (currently unused).
201 * NrFreed - Points to a variable where the number of pages
202 * actually freed is returned.
205 PLIST_ENTRY current_entry
;
206 PCACHE_SEGMENT current
, last
= NULL
;
207 ULONG PagesPerSegment
;
212 DPRINT("CcRosTrimCache(Target %d)\n", Target
);
216 InitializeListHead(&FreeList
);
218 ExAcquireFastMutex(&ViewLock
);
219 current_entry
= CacheSegmentLRUListHead
.Flink
;
220 while (current_entry
!= &CacheSegmentLRUListHead
&& Target
> 0)
222 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
223 CacheSegmentLRUListEntry
);
224 current_entry
= current_entry
->Flink
;
226 KeAcquireSpinLock(¤t
->Bcb
->BcbLock
, &oldIrql
);
227 if (current
->ReferenceCount
== 0)
229 RemoveEntryList(¤t
->BcbSegmentListEntry
);
230 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
231 RemoveEntryList(¤t
->CacheSegmentListEntry
);
232 RemoveEntryList(¤t
->CacheSegmentLRUListEntry
);
233 InsertHeadList(&FreeList
, ¤t
->BcbSegmentListEntry
);
234 PagesPerSegment
= current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
235 PagesFreed
= min(PagesPerSegment
, Target
);
236 Target
-= PagesFreed
;
237 (*NrFreed
) += PagesFreed
;
241 if (last
!= current
&& current
->MappedCount
> 0 && !current
->Dirty
&& !current
->PageOut
)
246 current
->ReferenceCount
++;
248 current
->PageOut
= TRUE
;
249 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
250 ExReleaseFastMutex(&ViewLock
);
251 for (i
= 0; i
< current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
; i
++)
254 Page
= MmGetPhysicalAddress((char*)current
->BaseAddress
+ i
* PAGE_SIZE
).QuadPart
>> PAGE_SHIFT
;
255 Status
= MmPageOutPhysicalAddress(Page
);
256 if (!NT_SUCCESS(Status
))
261 ExAcquireFastMutex(&ViewLock
);
262 KeAcquireSpinLock(¤t
->Bcb
->BcbLock
, &oldIrql
);
263 current
->ReferenceCount
--;
264 current
->PageOut
= FALSE
;
265 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
266 current_entry
= ¤t
->CacheSegmentLRUListEntry
;
269 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
272 ExReleaseFastMutex(&ViewLock
);
274 while (!IsListEmpty(&FreeList
))
276 current_entry
= RemoveHeadList(&FreeList
);
277 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
278 BcbSegmentListEntry
);
279 CcRosInternalFreeCacheSegment(current
);
282 DPRINT("CcRosTrimCache() finished\n");
283 return(STATUS_SUCCESS
);
287 CcRosReleaseCacheSegment(PBCB Bcb
,
288 PCACHE_SEGMENT CacheSeg
,
293 BOOLEAN WasDirty
= CacheSeg
->Dirty
;
298 DPRINT("CcReleaseCacheSegment(Bcb 0x%p, CacheSeg 0x%p, Valid %d)\n",
299 Bcb
, CacheSeg
, Valid
);
301 CacheSeg
->Valid
= Valid
;
302 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| Dirty
;
304 ExAcquireFastMutex(&ViewLock
);
305 if (!WasDirty
&& CacheSeg
->Dirty
)
307 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
308 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
310 RemoveEntryList(&CacheSeg
->CacheSegmentLRUListEntry
);
311 InsertTailList(&CacheSegmentLRUListHead
, &CacheSeg
->CacheSegmentLRUListEntry
);
315 CacheSeg
->MappedCount
++;
317 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
318 CacheSeg
->ReferenceCount
--;
319 if (Mapped
&& CacheSeg
->MappedCount
== 1)
321 CacheSeg
->ReferenceCount
++;
323 if (!WasDirty
&& CacheSeg
->Dirty
)
325 CacheSeg
->ReferenceCount
++;
327 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
328 ExReleaseFastMutex(&ViewLock
);
329 ExReleaseFastMutex(&CacheSeg
->Lock
);
331 return(STATUS_SUCCESS
);
335 CcRosLookupCacheSegment(PBCB Bcb
, ULONG FileOffset
)
337 PLIST_ENTRY current_entry
;
338 PCACHE_SEGMENT current
;
343 DPRINT("CcRosLookupCacheSegment(Bcb -x%p, FileOffset %d)\n", Bcb
, FileOffset
);
345 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
346 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
347 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
349 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
350 BcbSegmentListEntry
);
351 if (current
->FileOffset
<= FileOffset
&&
352 (current
->FileOffset
+ Bcb
->CacheSegmentSize
) > FileOffset
)
354 current
->ReferenceCount
++;
355 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
356 ExAcquireFastMutex(¤t
->Lock
);
359 current_entry
= current_entry
->Flink
;
361 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
366 CcRosMarkDirtyCacheSegment(PBCB Bcb
, ULONG FileOffset
)
368 PCACHE_SEGMENT CacheSeg
;
373 DPRINT("CcRosMarkDirtyCacheSegment(Bcb 0x%p, FileOffset %d)\n", Bcb
, FileOffset
);
375 CacheSeg
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
376 if (CacheSeg
== NULL
)
380 if (!CacheSeg
->Dirty
)
382 ExAcquireFastMutex(&ViewLock
);
383 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
384 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
385 ExReleaseFastMutex(&ViewLock
);
389 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
390 CacheSeg
->ReferenceCount
--;
391 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
395 CacheSeg
->Dirty
= TRUE
;
396 ExReleaseFastMutex(&CacheSeg
->Lock
);
398 return(STATUS_SUCCESS
);
402 CcRosUnmapCacheSegment(PBCB Bcb
, ULONG FileOffset
, BOOLEAN NowDirty
)
404 PCACHE_SEGMENT CacheSeg
;
410 DPRINT("CcRosUnmapCacheSegment(Bcb 0x%p, FileOffset %d, NowDirty %d)\n",
411 Bcb
, FileOffset
, NowDirty
);
413 CacheSeg
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
414 if (CacheSeg
== NULL
)
416 return(STATUS_UNSUCCESSFUL
);
419 WasDirty
= CacheSeg
->Dirty
;
420 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| NowDirty
;
422 CacheSeg
->MappedCount
--;
424 if (!WasDirty
&& NowDirty
)
426 ExAcquireFastMutex(&ViewLock
);
427 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
428 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
429 ExReleaseFastMutex(&ViewLock
);
432 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
433 CacheSeg
->ReferenceCount
--;
434 if (!WasDirty
&& NowDirty
)
436 CacheSeg
->ReferenceCount
++;
438 if (CacheSeg
->MappedCount
== 0)
440 CacheSeg
->ReferenceCount
--;
442 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
444 ExReleaseFastMutex(&CacheSeg
->Lock
);
445 return(STATUS_SUCCESS
);
449 CcRosCreateCacheSegment(PBCB Bcb
,
451 PCACHE_SEGMENT
* CacheSeg
)
454 PCACHE_SEGMENT current
;
455 PCACHE_SEGMENT previous
;
456 PLIST_ENTRY current_entry
;
461 ULONG StartingOffset
;
464 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
468 DPRINT("CcRosCreateCacheSegment()\n");
470 BoundaryAddressMultiple
.QuadPart
= 0;
471 if (FileOffset
>= Bcb
->FileSize
.u
.LowPart
)
474 return STATUS_INVALID_PARAMETER
;
477 current
= ExAllocateFromNPagedLookasideList(&CacheSegLookasideList
);
478 current
->Valid
= FALSE
;
479 current
->Dirty
= FALSE
;
480 current
->PageOut
= FALSE
;
481 current
->FileOffset
= ROUND_DOWN(FileOffset
, Bcb
->CacheSegmentSize
);
483 current
->MappedCount
= 0;
484 current
->DirtySegmentListEntry
.Flink
= NULL
;
485 current
->DirtySegmentListEntry
.Blink
= NULL
;
486 current
->ReferenceCount
= 1;
487 ExInitializeFastMutex(¤t
->Lock
);
488 ExAcquireFastMutex(¤t
->Lock
);
489 ExAcquireFastMutex(&ViewLock
);
492 /* There is window between the call to CcRosLookupCacheSegment
493 * and CcRosCreateCacheSegment. We must check if a segment on
494 * the fileoffset exist. If there exist a segment, we release
495 * our new created segment and return the existing one.
497 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
498 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
500 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
502 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
503 BcbSegmentListEntry
);
504 if (current
->FileOffset
<= FileOffset
&&
505 (current
->FileOffset
+ Bcb
->CacheSegmentSize
) > FileOffset
)
507 current
->ReferenceCount
++;
508 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
509 ExReleaseFastMutex(&(*CacheSeg
)->Lock
);
510 ExReleaseFastMutex(&ViewLock
);
511 ExFreeToNPagedLookasideList(&CacheSegLookasideList
, *CacheSeg
);
513 ExAcquireFastMutex(¤t
->Lock
);
514 return STATUS_SUCCESS
;
516 if (current
->FileOffset
< FileOffset
)
518 if (previous
== NULL
)
524 if (previous
->FileOffset
< current
->FileOffset
)
530 current_entry
= current_entry
->Flink
;
532 /* There was no existing segment. */
536 InsertHeadList(&previous
->BcbSegmentListEntry
, ¤t
->BcbSegmentListEntry
);
540 InsertHeadList(&Bcb
->BcbSegmentListHead
, ¤t
->BcbSegmentListEntry
);
542 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
543 InsertTailList(&CacheSegmentListHead
, ¤t
->CacheSegmentListEntry
);
544 InsertTailList(&CacheSegmentLRUListHead
, ¤t
->CacheSegmentLRUListEntry
);
545 ExReleaseFastMutex(&ViewLock
);
547 KeAcquireSpinLock(&CiCacheSegMappingRegionLock
, &oldIrql
);
549 StartingOffset
= RtlFindClearBitsAndSet(&CiCacheSegMappingRegionAllocMap
, Bcb
->CacheSegmentSize
/ PAGE_SIZE
, CiCacheSegMappingRegionHint
);
551 if (StartingOffset
== 0xffffffff)
553 DPRINT1("Out of CacheSeg mapping space\n");
557 current
->BaseAddress
= CiCacheSegMappingRegionBase
+ StartingOffset
* PAGE_SIZE
;
559 if (CiCacheSegMappingRegionHint
== StartingOffset
)
561 CiCacheSegMappingRegionHint
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
564 KeReleaseSpinLock(&CiCacheSegMappingRegionLock
, oldIrql
);
566 MmLockAddressSpace(MmGetKernelAddressSpace());
567 current
->BaseAddress
= NULL
;
568 Status
= MmCreateMemoryArea(NULL
,
569 MmGetKernelAddressSpace(),
570 MEMORY_AREA_CACHE_SEGMENT
,
571 ¤t
->BaseAddress
,
572 Bcb
->CacheSegmentSize
,
574 (PMEMORY_AREA
*)¤t
->MemoryArea
,
577 BoundaryAddressMultiple
);
578 MmUnlockAddressSpace(MmGetKernelAddressSpace());
579 if (!NT_SUCCESS(Status
))
584 Pfn
= alloca(sizeof(PFN_TYPE
) * (Bcb
->CacheSegmentSize
/ PAGE_SIZE
));
585 for (i
= 0; i
< (Bcb
->CacheSegmentSize
/ PAGE_SIZE
); i
++)
587 Status
= MmRequestPageMemoryConsumer(MC_CACHE
, TRUE
, &Pfn
[i
]);
588 if (!NT_SUCCESS(Status
))
593 Status
= MmCreateVirtualMapping(NULL
,
594 current
->BaseAddress
,
597 Bcb
->CacheSegmentSize
/ PAGE_SIZE
);
598 if (!NT_SUCCESS(Status
))
602 return(STATUS_SUCCESS
);
606 CcRosGetCacheSegmentChain(PBCB Bcb
,
609 PCACHE_SEGMENT
* CacheSeg
)
611 PCACHE_SEGMENT current
;
613 PCACHE_SEGMENT
* CacheSegList
;
614 PCACHE_SEGMENT Previous
= NULL
;
618 DPRINT("CcRosGetCacheSegmentChain()\n");
620 Length
= ROUND_UP(Length
, Bcb
->CacheSegmentSize
);
622 #if defined(__GNUC__)
623 CacheSegList
= alloca(sizeof(PCACHE_SEGMENT
) *
624 (Length
/ Bcb
->CacheSegmentSize
));
625 #elif defined(_MSC_VER)
626 CacheSegList
= _alloca(sizeof(PCACHE_SEGMENT
) *
627 (Length
/ Bcb
->CacheSegmentSize
));
629 #error Unknown compiler for alloca intrinsic stack allocation "function"
633 * Look for a cache segment already mapping the same data.
635 for (i
= 0; i
< (Length
/ Bcb
->CacheSegmentSize
); i
++)
637 ULONG CurrentOffset
= FileOffset
+ (i
* Bcb
->CacheSegmentSize
);
638 current
= CcRosLookupCacheSegment(Bcb
, CurrentOffset
);
641 CacheSegList
[i
] = current
;
645 CcRosCreateCacheSegment(Bcb
, CurrentOffset
, ¤t
);
646 CacheSegList
[i
] = current
;
650 for (i
= 0; i
< (Length
/ Bcb
->CacheSegmentSize
); i
++)
654 *CacheSeg
= CacheSegList
[i
];
655 Previous
= CacheSegList
[i
];
659 Previous
->NextInChain
= CacheSegList
[i
];
660 Previous
= CacheSegList
[i
];
663 Previous
->NextInChain
= NULL
;
665 return(STATUS_SUCCESS
);
669 CcRosGetCacheSegment(PBCB Bcb
,
674 PCACHE_SEGMENT
* CacheSeg
)
676 PCACHE_SEGMENT current
;
681 DPRINT("CcRosGetCacheSegment()\n");
684 * Look for a cache segment already mapping the same data.
686 current
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
690 * Otherwise create a new segment.
692 Status
= CcRosCreateCacheSegment(Bcb
, FileOffset
, ¤t
);
693 if (!NT_SUCCESS(Status
))
699 * Return information about the segment to the caller.
701 *UptoDate
= current
->Valid
;
702 *BaseAddress
= current
->BaseAddress
;
703 DPRINT("*BaseAddress 0x%.8X\n", *BaseAddress
);
705 *BaseOffset
= current
->FileOffset
;
706 return(STATUS_SUCCESS
);
710 CcRosRequestCacheSegment(PBCB Bcb
,
714 PCACHE_SEGMENT
* CacheSeg
)
716 * FUNCTION: Request a page mapping for a BCB
723 if ((FileOffset
% Bcb
->CacheSegmentSize
) != 0)
725 CPRINT("Bad fileoffset %x should be multiple of %x",
726 FileOffset
, Bcb
->CacheSegmentSize
);
730 return(CcRosGetCacheSegment(Bcb
,
740 CcFreeCachePage(PVOID Context
, MEMORY_AREA
* MemoryArea
, PVOID Address
,
741 PFN_TYPE Page
, SWAPENTRY SwapEntry
, BOOLEAN Dirty
)
743 ASSERT(SwapEntry
== 0);
746 MmReleasePageMemoryConsumer(MC_CACHE
, Page
);
751 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg
)
753 * FUNCTION: Releases a cache segment associated with a BCB
763 DPRINT("Freeing cache segment 0x%p\n", CacheSeg
);
765 RegionSize
= CacheSeg
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
767 /* Unmap all the pages. */
768 for (i
= 0; i
< RegionSize
; i
++)
770 MmDeleteVirtualMapping(NULL
,
771 CacheSeg
->BaseAddress
+ (i
* PAGE_SIZE
),
775 MmReleasePageMemoryConsumer(MC_CACHE
, Page
);
778 KeAcquireSpinLock(&CiCacheSegMappingRegionLock
, &oldIrql
);
779 /* Deallocate all the pages used. */
780 Base
= (ULONG
)(CacheSeg
->BaseAddress
- CiCacheSegMappingRegionBase
) / PAGE_SIZE
;
782 RtlClearBits(&CiCacheSegMappingRegionAllocMap
, Base
, RegionSize
);
784 CiCacheSegMappingRegionHint
= min (CiCacheSegMappingRegionHint
, Base
);
786 KeReleaseSpinLock(&CiCacheSegMappingRegionLock
, oldIrql
);
788 MmLockAddressSpace(MmGetKernelAddressSpace());
789 MmFreeMemoryArea(MmGetKernelAddressSpace(),
790 CacheSeg
->MemoryArea
,
793 MmUnlockAddressSpace(MmGetKernelAddressSpace());
795 ExFreeToNPagedLookasideList(&CacheSegLookasideList
, CacheSeg
);
796 return(STATUS_SUCCESS
);
800 CcRosFreeCacheSegment(PBCB Bcb
, PCACHE_SEGMENT CacheSeg
)
807 DPRINT("CcRosFreeCacheSegment(Bcb 0x%p, CacheSeg 0x%p)\n",
810 ExAcquireFastMutex(&ViewLock
);
811 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
812 RemoveEntryList(&CacheSeg
->BcbSegmentListEntry
);
813 RemoveEntryList(&CacheSeg
->CacheSegmentListEntry
);
814 RemoveEntryList(&CacheSeg
->CacheSegmentLRUListEntry
);
817 RemoveEntryList(&CacheSeg
->DirtySegmentListEntry
);
818 DirtyPageCount
-= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
821 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
822 ExReleaseFastMutex(&ViewLock
);
824 Status
= CcRosInternalFreeCacheSegment(CacheSeg
);
832 CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointers
,
833 IN PLARGE_INTEGER FileOffset OPTIONAL
,
835 OUT PIO_STATUS_BLOCK IoStatus
)
838 LARGE_INTEGER Offset
;
839 PCACHE_SEGMENT current
;
843 DPRINT("CcFlushCache(SectionObjectPointers 0x%p, FileOffset 0x%p, Length %d, IoStatus 0x%p)\n",
844 SectionObjectPointers
, FileOffset
, Length
, IoStatus
);
846 if (SectionObjectPointers
&& SectionObjectPointers
->SharedCacheMap
)
848 Bcb
= (PBCB
)SectionObjectPointers
->SharedCacheMap
;
852 Offset
= *FileOffset
;
856 Offset
.QuadPart
= (LONGLONG
)0;
857 Length
= Bcb
->FileSize
.u
.LowPart
;
862 IoStatus
->Status
= STATUS_SUCCESS
;
863 IoStatus
->Information
= 0;
868 current
= CcRosLookupCacheSegment (Bcb
, Offset
.u
.LowPart
);
873 Status
= CcRosFlushCacheSegment(current
);
874 if (!NT_SUCCESS(Status
) && IoStatus
!= NULL
)
876 IoStatus
->Status
= Status
;
879 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
880 ExReleaseFastMutex(¤t
->Lock
);
881 current
->ReferenceCount
--;
882 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
885 Offset
.QuadPart
+= Bcb
->CacheSegmentSize
;
886 if (Length
> Bcb
->CacheSegmentSize
)
888 Length
-= Bcb
->CacheSegmentSize
;
900 IoStatus
->Status
= STATUS_INVALID_PARAMETER
;
906 CcRosDeleteFileCache(PFILE_OBJECT FileObject
, PBCB Bcb
)
908 * FUNCTION: Releases the BCB associated with a file object
911 PLIST_ENTRY current_entry
;
912 PCACHE_SEGMENT current
;
920 ExReleaseFastMutex(&ViewLock
);
922 CcFlushCache(FileObject
->SectionObjectPointer
, NULL
, 0, NULL
);
924 ExAcquireFastMutex(&ViewLock
);
926 if (Bcb
->RefCount
== 0)
928 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
930 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
931 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
934 FileObject
->SectionObjectPointer
->SharedCacheMap
= NULL
;
937 * Release all cache segments.
939 InitializeListHead(&FreeList
);
940 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
941 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
942 while (!IsListEmpty(&Bcb
->BcbSegmentListHead
))
944 current_entry
= RemoveTailList(&Bcb
->BcbSegmentListHead
);
945 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, BcbSegmentListEntry
);
946 RemoveEntryList(¤t
->CacheSegmentListEntry
);
947 RemoveEntryList(¤t
->CacheSegmentLRUListEntry
);
950 RemoveEntryList(¤t
->DirtySegmentListEntry
);
951 DirtyPageCount
-= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
952 DPRINT1("Freeing dirty segment\n");
954 InsertHeadList(&FreeList
, ¤t
->BcbSegmentListEntry
);
956 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
958 ExReleaseFastMutex(&ViewLock
);
959 ObDereferenceObject (Bcb
->FileObject
);
961 while (!IsListEmpty(&FreeList
))
963 current_entry
= RemoveTailList(&FreeList
);
964 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, BcbSegmentListEntry
);
965 Status
= CcRosInternalFreeCacheSegment(current
);
967 ExFreeToNPagedLookasideList(&BcbLookasideList
, Bcb
);
968 ExAcquireFastMutex(&ViewLock
);
970 return(STATUS_SUCCESS
);
973 VOID
CcRosReferenceCache(PFILE_OBJECT FileObject
)
976 ExAcquireFastMutex(&ViewLock
);
977 Bcb
= (PBCB
)FileObject
->SectionObjectPointer
->SharedCacheMap
;
979 if (Bcb
->RefCount
== 0)
981 ASSERT(Bcb
->BcbRemoveListEntry
.Flink
!= NULL
);
982 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
983 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
988 ASSERT(Bcb
->BcbRemoveListEntry
.Flink
== NULL
);
991 ExReleaseFastMutex(&ViewLock
);
994 VOID
CcRosSetRemoveOnClose(PSECTION_OBJECT_POINTERS SectionObjectPointer
)
997 DPRINT("CcRosSetRemoveOnClose()\n");
998 ExAcquireFastMutex(&ViewLock
);
999 Bcb
= (PBCB
)SectionObjectPointer
->SharedCacheMap
;
1002 Bcb
->RemoveOnClose
= TRUE
;
1003 if (Bcb
->RefCount
== 0)
1005 CcRosDeleteFileCache(Bcb
->FileObject
, Bcb
);
1008 ExReleaseFastMutex(&ViewLock
);
1012 VOID
CcRosDereferenceCache(PFILE_OBJECT FileObject
)
1015 ExAcquireFastMutex(&ViewLock
);
1016 Bcb
= (PBCB
)FileObject
->SectionObjectPointer
->SharedCacheMap
;
1018 if (Bcb
->RefCount
> 0)
1021 if (Bcb
->RefCount
== 0)
1023 MmFreeSectionSegments(Bcb
->FileObject
);
1024 if (Bcb
->RemoveOnClose
)
1026 CcRosDeleteFileCache(FileObject
, Bcb
);
1030 Bcb
->TimeStamp
= CcTimeStamp
;
1031 InsertHeadList(&ClosedListHead
, &Bcb
->BcbRemoveListEntry
);
1035 ExReleaseFastMutex(&ViewLock
);
1039 CcRosReleaseFileCache(PFILE_OBJECT FileObject
)
1041 * FUNCTION: Called by the file system when a handle to a file object
1047 ExAcquireFastMutex(&ViewLock
);
1049 if (FileObject
->SectionObjectPointer
->SharedCacheMap
!= NULL
)
1051 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1052 if (FileObject
->PrivateCacheMap
!= NULL
)
1054 FileObject
->PrivateCacheMap
= NULL
;
1055 if (Bcb
->RefCount
> 0)
1058 if (Bcb
->RefCount
== 0)
1060 MmFreeSectionSegments(Bcb
->FileObject
);
1061 if (Bcb
->RemoveOnClose
)
1063 CcRosDeleteFileCache(FileObject
, Bcb
);
1067 Bcb
->TimeStamp
= CcTimeStamp
;
1068 InsertHeadList(&ClosedListHead
, &Bcb
->BcbRemoveListEntry
);
1074 ExReleaseFastMutex(&ViewLock
);
1075 return(STATUS_SUCCESS
);
1079 CcTryToInitializeFileCache(PFILE_OBJECT FileObject
)
1084 ExAcquireFastMutex(&ViewLock
);
1086 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1089 Status
= STATUS_UNSUCCESSFUL
;
1093 if (FileObject
->PrivateCacheMap
== NULL
)
1095 FileObject
->PrivateCacheMap
= Bcb
;
1098 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
1100 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1101 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1103 Status
= STATUS_SUCCESS
;
1105 ExReleaseFastMutex(&ViewLock
);
1112 CcRosInitializeFileCache(PFILE_OBJECT FileObject
,
1113 ULONG CacheSegmentSize
)
1115 * FUNCTION: Initializes a BCB for a file object
1120 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1121 DPRINT("CcRosInitializeFileCache(FileObject 0x%p, Bcb 0x%p, CacheSegmentSize %d)\n",
1122 FileObject
, Bcb
, CacheSegmentSize
);
1124 ExAcquireFastMutex(&ViewLock
);
1127 Bcb
= ExAllocateFromNPagedLookasideList(&BcbLookasideList
);
1130 ExReleaseFastMutex(&ViewLock
);
1131 return(STATUS_UNSUCCESSFUL
);
1133 memset(Bcb
, 0, sizeof(BCB
));
1134 ObReferenceObjectByPointer(FileObject
,
1138 Bcb
->FileObject
= FileObject
;
1139 Bcb
->CacheSegmentSize
= CacheSegmentSize
;
1140 if (FileObject
->FsContext
)
1142 Bcb
->AllocationSize
=
1143 ((PFSRTL_COMMON_FCB_HEADER
)FileObject
->FsContext
)->AllocationSize
;
1145 ((PFSRTL_COMMON_FCB_HEADER
)FileObject
->FsContext
)->FileSize
;
1147 KeInitializeSpinLock(&Bcb
->BcbLock
);
1148 InitializeListHead(&Bcb
->BcbSegmentListHead
);
1149 FileObject
->SectionObjectPointer
->SharedCacheMap
= Bcb
;
1151 if (FileObject
->PrivateCacheMap
== NULL
)
1153 FileObject
->PrivateCacheMap
= Bcb
;
1156 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
1158 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1159 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1161 ExReleaseFastMutex(&ViewLock
);
1163 return(STATUS_SUCCESS
);
1169 PFILE_OBJECT STDCALL
1170 CcGetFileObjectFromSectionPtrs(IN PSECTION_OBJECT_POINTERS SectionObjectPointers
)
1173 if (SectionObjectPointers
&& SectionObjectPointers
->SharedCacheMap
)
1175 Bcb
= (PBCB
)SectionObjectPointers
->SharedCacheMap
;
1177 return Bcb
->FileObject
;
1183 CmLazyCloseThreadMain(PVOID Ignored
)
1185 LARGE_INTEGER Timeout
;
1186 PLIST_ENTRY current_entry
;
1188 ULONG RemoveTimeStamp
;
1191 KeQuerySystemTime (&Timeout
);
1195 Timeout
.QuadPart
+= (LONGLONG
)100000000; // 10sec
1196 Status
= KeWaitForSingleObject(&LazyCloseThreadEvent
,
1202 DPRINT("LazyCloseThreadMain %d\n", CcTimeStamp
);
1204 if (!NT_SUCCESS(Status
))
1206 DbgPrint("LazyCloseThread: Wait failed\n");
1210 if (LazyCloseThreadShouldTerminate
)
1212 DbgPrint("LazyCloseThread: Terminating\n");
1216 ExAcquireFastMutex(&ViewLock
);
1218 if (CcTimeStamp
>= 30)
1220 RemoveTimeStamp
= CcTimeStamp
- 30; /* 5min = 10sec * 30 */
1221 while (!IsListEmpty(&ClosedListHead
))
1223 current_entry
= ClosedListHead
.Blink
;
1224 current
= CONTAINING_RECORD(current_entry
, BCB
, BcbRemoveListEntry
);
1225 if (current
->TimeStamp
>= RemoveTimeStamp
)
1229 CcRosDeleteFileCache(current
->FileObject
, current
);
1232 ExReleaseFastMutex(&ViewLock
);
1242 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
1247 DPRINT("CcInitView()\n");
1249 BoundaryAddressMultiple
.QuadPart
= 0;
1250 CiCacheSegMappingRegionHint
= 0;
1251 CiCacheSegMappingRegionBase
= NULL
;
1253 MmLockAddressSpace(MmGetKernelAddressSpace());
1255 Status
= MmCreateMemoryArea(NULL
,
1256 MmGetKernelAddressSpace(),
1257 MEMORY_AREA_CACHE_SEGMENT
,
1258 &CiCacheSegMappingRegionBase
,
1259 CI_CACHESEG_MAPPING_REGION_SIZE
,
1264 BoundaryAddressMultiple
);
1265 MmUnlockAddressSpace(MmGetKernelAddressSpace());
1266 if (!NT_SUCCESS(Status
))
1271 Buffer
= ExAllocatePool(NonPagedPool
, CI_CACHESEG_MAPPING_REGION_SIZE
/ (PAGE_SIZE
* 8));
1273 RtlInitializeBitMap(&CiCacheSegMappingRegionAllocMap
, Buffer
, CI_CACHESEG_MAPPING_REGION_SIZE
/ PAGE_SIZE
);
1274 RtlClearAllBits(&CiCacheSegMappingRegionAllocMap
);
1276 KeInitializeSpinLock(&CiCacheSegMappingRegionLock
);
1278 InitializeListHead(&CacheSegmentListHead
);
1279 InitializeListHead(&DirtySegmentListHead
);
1280 InitializeListHead(&CacheSegmentLRUListHead
);
1281 InitializeListHead(&ClosedListHead
);
1282 ExInitializeFastMutex(&ViewLock
);
1283 ExInitializeNPagedLookasideList (&iBcbLookasideList
,
1287 sizeof(INTERNAL_BCB
),
1290 ExInitializeNPagedLookasideList (&BcbLookasideList
,
1297 ExInitializeNPagedLookasideList (&CacheSegLookasideList
,
1301 sizeof(CACHE_SEGMENT
),
1305 MmInitializeMemoryConsumer(MC_CACHE
, CcRosTrimCache
);
1307 CcInitCacheZeroPage();
1310 LazyCloseThreadShouldTerminate
= FALSE
;
1311 KeInitializeEvent (&LazyCloseThreadEvent
, SynchronizationEvent
, FALSE
);
1312 Status
= PsCreateSystemThread(&LazyCloseThreadHandle
,
1317 (PKSTART_ROUTINE
)CmLazyCloseThreadMain
,
1319 if (NT_SUCCESS(Status
))
1321 Priority
= LOW_REALTIME_PRIORITY
;
1322 NtSetInformationThread(LazyCloseThreadHandle
,