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 #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
50 #define ROUND_DOWN(N, S) (((N) % (S)) ? ROUND_UP(N, S) - S : N)
52 static LIST_ENTRY DirtySegmentListHead
;
53 static LIST_ENTRY CacheSegmentListHead
;
54 static LIST_ENTRY CacheSegmentLRUListHead
;
55 static LIST_ENTRY ClosedListHead
;
56 ULONG DirtyPageCount
=0;
61 #define CI_CACHESEG_MAPPING_REGION_SIZE (128*1024*1024)
63 static PVOID CiCacheSegMappingRegionBase
= NULL
;
64 static RTL_BITMAP CiCacheSegMappingRegionAllocMap
;
65 static ULONG CiCacheSegMappingRegionHint
;
66 static KSPIN_LOCK CiCacheSegMappingRegionLock
;
69 NPAGED_LOOKASIDE_LIST iBcbLookasideList
;
70 static NPAGED_LOOKASIDE_LIST BcbLookasideList
;
71 static NPAGED_LOOKASIDE_LIST CacheSegLookasideList
;
73 static ULONG CcTimeStamp
;
74 static KEVENT LazyCloseThreadEvent
;
75 static HANDLE LazyCloseThreadHandle
;
76 static CLIENT_ID LazyCloseThreadId
;
77 static volatile BOOLEAN LazyCloseThreadShouldTerminate
;
80 /* void * alloca(size_t size); */
81 #elif defined(_MSC_VER)
82 void* _alloca(size_t size
);
84 #error Unknown compiler for alloca intrinsic stack allocation "function"
89 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg
);
91 /* FUNCTIONS *****************************************************************/
94 CcRosFlushCacheSegment(PCACHE_SEGMENT CacheSegment
)
98 Status
= WriteCacheSegment(CacheSegment
);
99 if (NT_SUCCESS(Status
))
101 ExAcquireFastMutex(&ViewLock
);
102 KeAcquireSpinLock(&CacheSegment
->Bcb
->BcbLock
, &oldIrql
);
103 CacheSegment
->Dirty
= FALSE
;
104 RemoveEntryList(&CacheSegment
->DirtySegmentListEntry
);
105 DirtyPageCount
-= CacheSegment
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
106 CacheSegment
->ReferenceCount
--;
107 KeReleaseSpinLock(&CacheSegment
->Bcb
->BcbLock
, oldIrql
);
108 ExReleaseFastMutex(&ViewLock
);
114 CcRosFlushDirtyPages(ULONG Target
, PULONG Count
)
116 PLIST_ENTRY current_entry
;
117 PCACHE_SEGMENT current
;
118 ULONG PagesPerSegment
;
121 static ULONG WriteCount
[4] = {0, 0, 0, 0};
124 DPRINT("CcRosFlushDirtyPages(Target %d)\n", Target
);
128 ExAcquireFastMutex(&ViewLock
);
130 WriteCount
[0] = WriteCount
[1];
131 WriteCount
[1] = WriteCount
[2];
132 WriteCount
[2] = WriteCount
[3];
135 NewTarget
= WriteCount
[0] + WriteCount
[1] + WriteCount
[2];
137 if (NewTarget
< DirtyPageCount
)
139 NewTarget
= (DirtyPageCount
- NewTarget
+ 3) / 4;
140 WriteCount
[0] += NewTarget
;
141 WriteCount
[1] += NewTarget
;
142 WriteCount
[2] += NewTarget
;
143 WriteCount
[3] += NewTarget
;
146 NewTarget
= WriteCount
[0];
148 Target
= max(NewTarget
, Target
);
150 current_entry
= DirtySegmentListHead
.Flink
;
151 if (current_entry
== &DirtySegmentListHead
)
153 DPRINT("No Dirty pages\n");
155 while (current_entry
!= &DirtySegmentListHead
&& Target
> 0)
157 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
158 DirtySegmentListEntry
);
159 current_entry
= current_entry
->Flink
;
160 Locked
= ExTryToAcquireFastMutex(¤t
->Lock
);
165 ASSERT(current
->Dirty
);
166 if (current
->ReferenceCount
> 1)
168 ExReleaseFastMutex(¤t
->Lock
);
171 ExReleaseFastMutex(&ViewLock
);
172 PagesPerSegment
= current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
173 Status
= CcRosFlushCacheSegment(current
);
174 ExReleaseFastMutex(¤t
->Lock
);
175 if (!NT_SUCCESS(Status
) && (Status
!= STATUS_END_OF_FILE
))
177 DPRINT1("CC: Failed to flush cache segment.\n");
181 (*Count
) += PagesPerSegment
;
182 Target
-= PagesPerSegment
;
184 ExAcquireFastMutex(&ViewLock
);
185 current_entry
= DirtySegmentListHead
.Flink
;
187 if (*Count
< NewTarget
)
189 WriteCount
[1] += (NewTarget
- *Count
);
191 ExReleaseFastMutex(&ViewLock
);
192 DPRINT("CcRosFlushDirtyPages() finished\n");
194 return(STATUS_SUCCESS
);
198 CcRosTrimCache(ULONG Target
, ULONG Priority
, PULONG NrFreed
)
200 * FUNCTION: Try to free some memory from the file cache.
202 * Target - The number of pages to be freed.
203 * Priority - The priority of free (currently unused).
204 * NrFreed - Points to a variable where the number of pages
205 * actually freed is returned.
208 PLIST_ENTRY current_entry
;
209 PCACHE_SEGMENT current
, last
= NULL
;
210 ULONG PagesPerSegment
;
215 DPRINT("CcRosTrimCache(Target %d)\n", Target
);
219 InitializeListHead(&FreeList
);
221 ExAcquireFastMutex(&ViewLock
);
222 current_entry
= CacheSegmentLRUListHead
.Flink
;
223 while (current_entry
!= &CacheSegmentLRUListHead
&& Target
> 0)
225 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
226 CacheSegmentLRUListEntry
);
227 current_entry
= current_entry
->Flink
;
229 KeAcquireSpinLock(¤t
->Bcb
->BcbLock
, &oldIrql
);
230 if (current
->ReferenceCount
== 0)
232 RemoveEntryList(¤t
->BcbSegmentListEntry
);
233 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
234 RemoveEntryList(¤t
->CacheSegmentListEntry
);
235 RemoveEntryList(¤t
->CacheSegmentLRUListEntry
);
236 InsertHeadList(&FreeList
, ¤t
->BcbSegmentListEntry
);
237 PagesPerSegment
= current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
238 PagesFreed
= min(PagesPerSegment
, Target
);
239 Target
-= PagesFreed
;
240 (*NrFreed
) += PagesFreed
;
244 if (last
!= current
&& current
->MappedCount
> 0 && !current
->Dirty
&& !current
->PageOut
)
249 current
->ReferenceCount
++;
251 current
->PageOut
= TRUE
;
252 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
253 ExReleaseFastMutex(&ViewLock
);
254 for (i
= 0; i
< current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
; i
++)
257 Page
= MmGetPhysicalAddress((char*)current
->BaseAddress
+ i
* PAGE_SIZE
).QuadPart
>> PAGE_SHIFT
;
258 Status
= MmPageOutPhysicalAddress(Page
);
259 if (!NT_SUCCESS(Status
))
264 ExAcquireFastMutex(&ViewLock
);
265 KeAcquireSpinLock(¤t
->Bcb
->BcbLock
, &oldIrql
);
266 current
->ReferenceCount
--;
267 current
->PageOut
= FALSE
;
268 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
269 current_entry
= ¤t
->CacheSegmentLRUListEntry
;
272 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
275 ExReleaseFastMutex(&ViewLock
);
277 while (!IsListEmpty(&FreeList
))
279 current_entry
= RemoveHeadList(&FreeList
);
280 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
281 BcbSegmentListEntry
);
282 CcRosInternalFreeCacheSegment(current
);
285 DPRINT("CcRosTrimCache() finished\n");
286 return(STATUS_SUCCESS
);
290 CcRosReleaseCacheSegment(PBCB Bcb
,
291 PCACHE_SEGMENT CacheSeg
,
296 BOOLEAN WasDirty
= CacheSeg
->Dirty
;
301 DPRINT("CcReleaseCacheSegment(Bcb %x, CacheSeg %x, Valid %d)\n",
302 Bcb
, CacheSeg
, Valid
);
304 CacheSeg
->Valid
= Valid
;
305 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| Dirty
;
307 ExAcquireFastMutex(&ViewLock
);
308 if (!WasDirty
&& CacheSeg
->Dirty
)
310 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
311 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
313 RemoveEntryList(&CacheSeg
->CacheSegmentLRUListEntry
);
314 InsertTailList(&CacheSegmentLRUListHead
, &CacheSeg
->CacheSegmentLRUListEntry
);
318 CacheSeg
->MappedCount
++;
320 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
321 CacheSeg
->ReferenceCount
--;
322 if (Mapped
&& CacheSeg
->MappedCount
== 1)
324 CacheSeg
->ReferenceCount
++;
326 if (!WasDirty
&& CacheSeg
->Dirty
)
328 CacheSeg
->ReferenceCount
++;
330 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
331 ExReleaseFastMutex(&ViewLock
);
332 ExReleaseFastMutex(&CacheSeg
->Lock
);
334 return(STATUS_SUCCESS
);
338 CcRosLookupCacheSegment(PBCB Bcb
, ULONG FileOffset
)
340 PLIST_ENTRY current_entry
;
341 PCACHE_SEGMENT current
;
346 DPRINT("CcRosLookupCacheSegment(Bcb %x, FileOffset %d)\n", Bcb
, FileOffset
);
348 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
349 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
350 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
352 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
353 BcbSegmentListEntry
);
354 if (current
->FileOffset
<= FileOffset
&&
355 (current
->FileOffset
+ Bcb
->CacheSegmentSize
) > FileOffset
)
357 current
->ReferenceCount
++;
358 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
359 ExAcquireFastMutex(¤t
->Lock
);
362 current_entry
= current_entry
->Flink
;
364 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
369 CcRosMarkDirtyCacheSegment(PBCB Bcb
, ULONG FileOffset
)
371 PCACHE_SEGMENT CacheSeg
;
376 DPRINT("CcRosMarkDirtyCacheSegment(Bcb %x, FileOffset %d)\n", Bcb
, FileOffset
);
378 CacheSeg
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
379 if (CacheSeg
== NULL
)
383 if (!CacheSeg
->Dirty
)
385 ExAcquireFastMutex(&ViewLock
);
386 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
387 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
388 ExReleaseFastMutex(&ViewLock
);
392 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
393 CacheSeg
->ReferenceCount
--;
394 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
398 CacheSeg
->Dirty
= TRUE
;
399 ExReleaseFastMutex(&CacheSeg
->Lock
);
401 return(STATUS_SUCCESS
);
405 CcRosUnmapCacheSegment(PBCB Bcb
, ULONG FileOffset
, BOOLEAN NowDirty
)
407 PCACHE_SEGMENT CacheSeg
;
413 DPRINT("CcRosUnmapCacheSegment(Bcb %x, FileOffset %d, NowDirty %d)\n",
414 Bcb
, FileOffset
, NowDirty
);
416 CacheSeg
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
417 if (CacheSeg
== NULL
)
419 return(STATUS_UNSUCCESSFUL
);
422 WasDirty
= CacheSeg
->Dirty
;
423 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| NowDirty
;
425 CacheSeg
->MappedCount
--;
427 if (!WasDirty
&& NowDirty
)
429 ExAcquireFastMutex(&ViewLock
);
430 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
431 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
432 ExReleaseFastMutex(&ViewLock
);
435 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
436 CacheSeg
->ReferenceCount
--;
437 if (!WasDirty
&& NowDirty
)
439 CacheSeg
->ReferenceCount
++;
441 if (CacheSeg
->MappedCount
== 0)
443 CacheSeg
->ReferenceCount
--;
445 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
447 ExReleaseFastMutex(&CacheSeg
->Lock
);
448 return(STATUS_SUCCESS
);
452 CcRosCreateCacheSegment(PBCB Bcb
,
454 PCACHE_SEGMENT
* CacheSeg
)
457 PCACHE_SEGMENT current
;
458 PCACHE_SEGMENT previous
;
459 PLIST_ENTRY current_entry
;
464 ULONG StartingOffset
;
467 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
471 DPRINT("CcRosCreateCacheSegment()\n");
473 BoundaryAddressMultiple
.QuadPart
= 0;
474 if (FileOffset
>= Bcb
->FileSize
.u
.LowPart
)
477 return STATUS_INVALID_PARAMETER
;
480 current
= ExAllocateFromNPagedLookasideList(&CacheSegLookasideList
);
481 current
->Valid
= FALSE
;
482 current
->Dirty
= FALSE
;
483 current
->PageOut
= FALSE
;
484 current
->FileOffset
= ROUND_DOWN(FileOffset
, Bcb
->CacheSegmentSize
);
486 current
->MappedCount
= 0;
487 current
->DirtySegmentListEntry
.Flink
= NULL
;
488 current
->DirtySegmentListEntry
.Blink
= NULL
;
489 current
->ReferenceCount
= 1;
490 ExInitializeFastMutex(¤t
->Lock
);
491 ExAcquireFastMutex(¤t
->Lock
);
492 ExAcquireFastMutex(&ViewLock
);
495 /* There is window between the call to CcRosLookupCacheSegment
496 * and CcRosCreateCacheSegment. We must check if a segment on
497 * the fileoffset exist. If there exist a segment, we release
498 * our new created segment and return the existing one.
500 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
501 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
503 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
505 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
506 BcbSegmentListEntry
);
507 if (current
->FileOffset
<= FileOffset
&&
508 (current
->FileOffset
+ Bcb
->CacheSegmentSize
) > FileOffset
)
510 current
->ReferenceCount
++;
511 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
512 ExReleaseFastMutex(&(*CacheSeg
)->Lock
);
513 ExReleaseFastMutex(&ViewLock
);
514 ExFreeToNPagedLookasideList(&CacheSegLookasideList
, *CacheSeg
);
516 ExAcquireFastMutex(¤t
->Lock
);
517 return STATUS_SUCCESS
;
519 if (current
->FileOffset
< FileOffset
)
521 if (previous
== NULL
)
527 if (previous
->FileOffset
< current
->FileOffset
)
533 current_entry
= current_entry
->Flink
;
535 /* There was no existing segment. */
539 InsertHeadList(&previous
->BcbSegmentListEntry
, ¤t
->BcbSegmentListEntry
);
543 InsertHeadList(&Bcb
->BcbSegmentListHead
, ¤t
->BcbSegmentListEntry
);
545 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
546 InsertTailList(&CacheSegmentListHead
, ¤t
->CacheSegmentListEntry
);
547 InsertTailList(&CacheSegmentLRUListHead
, ¤t
->CacheSegmentLRUListEntry
);
548 ExReleaseFastMutex(&ViewLock
);
550 KeAcquireSpinLock(&CiCacheSegMappingRegionLock
, &oldIrql
);
552 StartingOffset
= RtlFindClearBitsAndSet(&CiCacheSegMappingRegionAllocMap
, Bcb
->CacheSegmentSize
/ PAGE_SIZE
, CiCacheSegMappingRegionHint
);
554 if (StartingOffset
== 0xffffffff)
556 DPRINT1("Out of CacheSeg mapping space\n");
560 current
->BaseAddress
= CiCacheSegMappingRegionBase
+ StartingOffset
* PAGE_SIZE
;
562 if (CiCacheSegMappingRegionHint
== StartingOffset
)
564 CiCacheSegMappingRegionHint
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
567 KeReleaseSpinLock(&CiCacheSegMappingRegionLock
, oldIrql
);
569 MmLockAddressSpace(MmGetKernelAddressSpace());
570 current
->BaseAddress
= NULL
;
571 Status
= MmCreateMemoryArea(NULL
,
572 MmGetKernelAddressSpace(),
573 MEMORY_AREA_CACHE_SEGMENT
,
574 ¤t
->BaseAddress
,
575 Bcb
->CacheSegmentSize
,
577 (PMEMORY_AREA
*)¤t
->MemoryArea
,
580 BoundaryAddressMultiple
);
581 MmUnlockAddressSpace(MmGetKernelAddressSpace());
582 if (!NT_SUCCESS(Status
))
587 Pfn
= alloca(sizeof(PFN_TYPE
) * (Bcb
->CacheSegmentSize
/ PAGE_SIZE
));
588 for (i
= 0; i
< (Bcb
->CacheSegmentSize
/ PAGE_SIZE
); i
++)
590 Status
= MmRequestPageMemoryConsumer(MC_CACHE
, TRUE
, &Pfn
[i
]);
591 if (!NT_SUCCESS(Status
))
596 Status
= MmCreateVirtualMapping(NULL
,
597 current
->BaseAddress
,
600 Bcb
->CacheSegmentSize
/ PAGE_SIZE
);
601 if (!NT_SUCCESS(Status
))
605 return(STATUS_SUCCESS
);
609 CcRosGetCacheSegmentChain(PBCB Bcb
,
612 PCACHE_SEGMENT
* CacheSeg
)
614 PCACHE_SEGMENT current
;
616 PCACHE_SEGMENT
* CacheSegList
;
617 PCACHE_SEGMENT Previous
= NULL
;
621 DPRINT("CcRosGetCacheSegmentChain()\n");
623 Length
= ROUND_UP(Length
, Bcb
->CacheSegmentSize
);
625 #if defined(__GNUC__)
626 CacheSegList
= alloca(sizeof(PCACHE_SEGMENT
) *
627 (Length
/ Bcb
->CacheSegmentSize
));
628 #elif defined(_MSC_VER)
629 CacheSegList
= _alloca(sizeof(PCACHE_SEGMENT
) *
630 (Length
/ Bcb
->CacheSegmentSize
));
632 #error Unknown compiler for alloca intrinsic stack allocation "function"
636 * Look for a cache segment already mapping the same data.
638 for (i
= 0; i
< (Length
/ Bcb
->CacheSegmentSize
); i
++)
640 ULONG CurrentOffset
= FileOffset
+ (i
* Bcb
->CacheSegmentSize
);
641 current
= CcRosLookupCacheSegment(Bcb
, CurrentOffset
);
644 CacheSegList
[i
] = current
;
648 CcRosCreateCacheSegment(Bcb
, CurrentOffset
, ¤t
);
649 CacheSegList
[i
] = current
;
653 for (i
= 0; i
< (Length
/ Bcb
->CacheSegmentSize
); i
++)
657 *CacheSeg
= CacheSegList
[i
];
658 Previous
= CacheSegList
[i
];
662 Previous
->NextInChain
= CacheSegList
[i
];
663 Previous
= CacheSegList
[i
];
666 Previous
->NextInChain
= NULL
;
668 return(STATUS_SUCCESS
);
672 CcRosGetCacheSegment(PBCB Bcb
,
677 PCACHE_SEGMENT
* CacheSeg
)
679 PCACHE_SEGMENT current
;
684 DPRINT("CcRosGetCacheSegment()\n");
687 * Look for a cache segment already mapping the same data.
689 current
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
693 * Otherwise create a new segment.
695 Status
= CcRosCreateCacheSegment(Bcb
, FileOffset
, ¤t
);
696 if (!NT_SUCCESS(Status
))
702 * Return information about the segment to the caller.
704 *UptoDate
= current
->Valid
;
705 *BaseAddress
= current
->BaseAddress
;
706 DPRINT("*BaseAddress 0x%.8X\n", *BaseAddress
);
708 *BaseOffset
= current
->FileOffset
;
709 return(STATUS_SUCCESS
);
713 CcRosRequestCacheSegment(PBCB Bcb
,
717 PCACHE_SEGMENT
* CacheSeg
)
719 * FUNCTION: Request a page mapping for a BCB
726 if ((FileOffset
% Bcb
->CacheSegmentSize
) != 0)
728 CPRINT("Bad fileoffset %x should be multiple of %x",
729 FileOffset
, Bcb
->CacheSegmentSize
);
733 return(CcRosGetCacheSegment(Bcb
,
743 CcFreeCachePage(PVOID Context
, MEMORY_AREA
* MemoryArea
, PVOID Address
,
744 PFN_TYPE Page
, SWAPENTRY SwapEntry
, BOOLEAN Dirty
)
746 ASSERT(SwapEntry
== 0);
749 MmReleasePageMemoryConsumer(MC_CACHE
, Page
);
754 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg
)
756 * FUNCTION: Releases a cache segment associated with a BCB
766 DPRINT("Freeing cache segment %x\n", CacheSeg
);
768 RegionSize
= CacheSeg
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
770 /* Unmap all the pages. */
771 for (i
= 0; i
< RegionSize
; i
++)
773 MmDeleteVirtualMapping(NULL
,
774 CacheSeg
->BaseAddress
+ (i
* PAGE_SIZE
),
778 MmReleasePageMemoryConsumer(MC_CACHE
, Page
);
781 KeAcquireSpinLock(&CiCacheSegMappingRegionLock
, &oldIrql
);
782 /* Deallocate all the pages used. */
783 Base
= (ULONG
)(CacheSeg
->BaseAddress
- CiCacheSegMappingRegionBase
) / PAGE_SIZE
;
785 RtlClearBits(&CiCacheSegMappingRegionAllocMap
, Base
, RegionSize
);
787 CiCacheSegMappingRegionHint
= min (CiCacheSegMappingRegionHint
, Base
);
789 KeReleaseSpinLock(&CiCacheSegMappingRegionLock
, oldIrql
);
791 MmLockAddressSpace(MmGetKernelAddressSpace());
792 MmFreeMemoryArea(MmGetKernelAddressSpace(),
793 CacheSeg
->MemoryArea
,
796 MmUnlockAddressSpace(MmGetKernelAddressSpace());
798 ExFreeToNPagedLookasideList(&CacheSegLookasideList
, CacheSeg
);
799 return(STATUS_SUCCESS
);
803 CcRosFreeCacheSegment(PBCB Bcb
, PCACHE_SEGMENT CacheSeg
)
810 DPRINT("CcRosFreeCacheSegment(Bcb %x, CacheSeg %x)\n",
813 ExAcquireFastMutex(&ViewLock
);
814 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
815 RemoveEntryList(&CacheSeg
->BcbSegmentListEntry
);
816 RemoveEntryList(&CacheSeg
->CacheSegmentListEntry
);
817 RemoveEntryList(&CacheSeg
->CacheSegmentLRUListEntry
);
820 RemoveEntryList(&CacheSeg
->DirtySegmentListEntry
);
821 DirtyPageCount
-= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
824 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
825 ExReleaseFastMutex(&ViewLock
);
827 Status
= CcRosInternalFreeCacheSegment(CacheSeg
);
835 CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointers
,
836 IN PLARGE_INTEGER FileOffset OPTIONAL
,
838 OUT PIO_STATUS_BLOCK IoStatus
)
841 LARGE_INTEGER Offset
;
842 PCACHE_SEGMENT current
;
846 DPRINT("CcFlushCache(SectionObjectPointers %x, FileOffset %x, Length %d, IoStatus %x)\n",
847 SectionObjectPointers
, FileOffset
, Length
, IoStatus
);
849 if (SectionObjectPointers
&& SectionObjectPointers
->SharedCacheMap
)
851 Bcb
= (PBCB
)SectionObjectPointers
->SharedCacheMap
;
855 Offset
= *FileOffset
;
859 Offset
.QuadPart
= (LONGLONG
)0;
860 Length
= Bcb
->FileSize
.u
.LowPart
;
865 IoStatus
->Status
= STATUS_SUCCESS
;
866 IoStatus
->Information
= 0;
871 current
= CcRosLookupCacheSegment (Bcb
, Offset
.u
.LowPart
);
876 Status
= CcRosFlushCacheSegment(current
);
877 if (!NT_SUCCESS(Status
) && IoStatus
!= NULL
)
879 IoStatus
->Status
= Status
;
882 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
883 ExReleaseFastMutex(¤t
->Lock
);
884 current
->ReferenceCount
--;
885 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
888 Offset
.QuadPart
+= Bcb
->CacheSegmentSize
;
889 if (Length
> Bcb
->CacheSegmentSize
)
891 Length
-= Bcb
->CacheSegmentSize
;
903 IoStatus
->Status
= STATUS_INVALID_PARAMETER
;
909 CcRosDeleteFileCache(PFILE_OBJECT FileObject
, PBCB Bcb
)
911 * FUNCTION: Releases the BCB associated with a file object
914 PLIST_ENTRY current_entry
;
915 PCACHE_SEGMENT current
;
923 ExReleaseFastMutex(&ViewLock
);
925 CcFlushCache(FileObject
->SectionObjectPointer
, NULL
, 0, NULL
);
927 ExAcquireFastMutex(&ViewLock
);
929 if (Bcb
->RefCount
== 0)
931 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
933 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
934 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
937 FileObject
->SectionObjectPointer
->SharedCacheMap
= NULL
;
940 * Release all cache segments.
942 InitializeListHead(&FreeList
);
943 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
944 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
945 while (!IsListEmpty(&Bcb
->BcbSegmentListHead
))
947 current_entry
= RemoveTailList(&Bcb
->BcbSegmentListHead
);
948 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, BcbSegmentListEntry
);
949 RemoveEntryList(¤t
->CacheSegmentListEntry
);
950 RemoveEntryList(¤t
->CacheSegmentLRUListEntry
);
953 RemoveEntryList(¤t
->DirtySegmentListEntry
);
954 DirtyPageCount
-= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
955 DPRINT1("Freeing dirty segment\n");
957 InsertHeadList(&FreeList
, ¤t
->BcbSegmentListEntry
);
959 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
961 ExReleaseFastMutex(&ViewLock
);
962 ObDereferenceObject (Bcb
->FileObject
);
964 while (!IsListEmpty(&FreeList
))
966 current_entry
= RemoveTailList(&FreeList
);
967 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, BcbSegmentListEntry
);
968 Status
= CcRosInternalFreeCacheSegment(current
);
970 ExFreeToNPagedLookasideList(&BcbLookasideList
, Bcb
);
971 ExAcquireFastMutex(&ViewLock
);
973 return(STATUS_SUCCESS
);
976 VOID
CcRosReferenceCache(PFILE_OBJECT FileObject
)
979 ExAcquireFastMutex(&ViewLock
);
980 Bcb
= (PBCB
)FileObject
->SectionObjectPointer
->SharedCacheMap
;
982 if (Bcb
->RefCount
== 0)
984 ASSERT(Bcb
->BcbRemoveListEntry
.Flink
!= NULL
);
985 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
986 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
991 ASSERT(Bcb
->BcbRemoveListEntry
.Flink
== NULL
);
994 ExReleaseFastMutex(&ViewLock
);
997 VOID
CcRosSetRemoveOnClose(PSECTION_OBJECT_POINTERS SectionObjectPointer
)
1000 DPRINT("CcRosSetRemoveOnClose()\n");
1001 ExAcquireFastMutex(&ViewLock
);
1002 Bcb
= (PBCB
)SectionObjectPointer
->SharedCacheMap
;
1005 Bcb
->RemoveOnClose
= TRUE
;
1006 if (Bcb
->RefCount
== 0)
1008 CcRosDeleteFileCache(Bcb
->FileObject
, Bcb
);
1011 ExReleaseFastMutex(&ViewLock
);
1015 VOID
CcRosDereferenceCache(PFILE_OBJECT FileObject
)
1018 ExAcquireFastMutex(&ViewLock
);
1019 Bcb
= (PBCB
)FileObject
->SectionObjectPointer
->SharedCacheMap
;
1021 if (Bcb
->RefCount
> 0)
1024 if (Bcb
->RefCount
== 0)
1026 MmFreeSectionSegments(Bcb
->FileObject
);
1027 if (Bcb
->RemoveOnClose
)
1029 CcRosDeleteFileCache(FileObject
, Bcb
);
1033 Bcb
->TimeStamp
= CcTimeStamp
;
1034 InsertHeadList(&ClosedListHead
, &Bcb
->BcbRemoveListEntry
);
1038 ExReleaseFastMutex(&ViewLock
);
1042 CcRosReleaseFileCache(PFILE_OBJECT FileObject
)
1044 * FUNCTION: Called by the file system when a handle to a file object
1050 ExAcquireFastMutex(&ViewLock
);
1052 if (FileObject
->SectionObjectPointer
->SharedCacheMap
!= NULL
)
1054 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1055 if (FileObject
->PrivateCacheMap
!= NULL
)
1057 FileObject
->PrivateCacheMap
= NULL
;
1058 if (Bcb
->RefCount
> 0)
1061 if (Bcb
->RefCount
== 0)
1063 MmFreeSectionSegments(Bcb
->FileObject
);
1064 if (Bcb
->RemoveOnClose
)
1066 CcRosDeleteFileCache(FileObject
, Bcb
);
1070 Bcb
->TimeStamp
= CcTimeStamp
;
1071 InsertHeadList(&ClosedListHead
, &Bcb
->BcbRemoveListEntry
);
1077 ExReleaseFastMutex(&ViewLock
);
1078 return(STATUS_SUCCESS
);
1082 CcTryToInitializeFileCache(PFILE_OBJECT FileObject
)
1087 ExAcquireFastMutex(&ViewLock
);
1089 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1092 Status
= STATUS_UNSUCCESSFUL
;
1096 if (FileObject
->PrivateCacheMap
== NULL
)
1098 FileObject
->PrivateCacheMap
= Bcb
;
1101 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
1103 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1104 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1106 Status
= STATUS_SUCCESS
;
1108 ExReleaseFastMutex(&ViewLock
);
1115 CcRosInitializeFileCache(PFILE_OBJECT FileObject
,
1116 ULONG CacheSegmentSize
)
1118 * FUNCTION: Initializes a BCB for a file object
1123 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1124 DPRINT("CcRosInitializeFileCache(FileObject %x, *Bcb %x, CacheSegmentSize %d)\n",
1125 FileObject
, Bcb
, CacheSegmentSize
);
1127 ExAcquireFastMutex(&ViewLock
);
1130 Bcb
= ExAllocateFromNPagedLookasideList(&BcbLookasideList
);
1133 ExReleaseFastMutex(&ViewLock
);
1134 return(STATUS_UNSUCCESSFUL
);
1136 memset(Bcb
, 0, sizeof(BCB
));
1137 ObReferenceObjectByPointer(FileObject
,
1141 Bcb
->FileObject
= FileObject
;
1142 Bcb
->CacheSegmentSize
= CacheSegmentSize
;
1143 if (FileObject
->FsContext
)
1145 Bcb
->AllocationSize
=
1146 ((PFSRTL_COMMON_FCB_HEADER
)FileObject
->FsContext
)->AllocationSize
;
1148 ((PFSRTL_COMMON_FCB_HEADER
)FileObject
->FsContext
)->FileSize
;
1150 KeInitializeSpinLock(&Bcb
->BcbLock
);
1151 InitializeListHead(&Bcb
->BcbSegmentListHead
);
1152 FileObject
->SectionObjectPointer
->SharedCacheMap
= Bcb
;
1154 if (FileObject
->PrivateCacheMap
== NULL
)
1156 FileObject
->PrivateCacheMap
= Bcb
;
1159 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
1161 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1162 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1164 ExReleaseFastMutex(&ViewLock
);
1166 return(STATUS_SUCCESS
);
1172 PFILE_OBJECT STDCALL
1173 CcGetFileObjectFromSectionPtrs(IN PSECTION_OBJECT_POINTERS SectionObjectPointers
)
1176 if (SectionObjectPointers
&& SectionObjectPointers
->SharedCacheMap
)
1178 Bcb
= (PBCB
)SectionObjectPointers
->SharedCacheMap
;
1180 return Bcb
->FileObject
;
1186 CmLazyCloseThreadMain(PVOID Ignored
)
1188 LARGE_INTEGER Timeout
;
1189 PLIST_ENTRY current_entry
;
1191 ULONG RemoveTimeStamp
;
1194 KeQuerySystemTime (&Timeout
);
1198 Timeout
.QuadPart
+= (LONGLONG
)100000000; // 10sec
1199 Status
= KeWaitForSingleObject(&LazyCloseThreadEvent
,
1205 DPRINT("LazyCloseThreadMain %d\n", CcTimeStamp
);
1207 if (!NT_SUCCESS(Status
))
1209 DbgPrint("LazyCloseThread: Wait failed\n");
1213 if (LazyCloseThreadShouldTerminate
)
1215 DbgPrint("LazyCloseThread: Terminating\n");
1219 ExAcquireFastMutex(&ViewLock
);
1221 if (CcTimeStamp
>= 30)
1223 RemoveTimeStamp
= CcTimeStamp
- 30; /* 5min = 10sec * 30 */
1224 while (!IsListEmpty(&ClosedListHead
))
1226 current_entry
= ClosedListHead
.Blink
;
1227 current
= CONTAINING_RECORD(current_entry
, BCB
, BcbRemoveListEntry
);
1228 if (current
->TimeStamp
>= RemoveTimeStamp
)
1232 CcRosDeleteFileCache(current
->FileObject
, current
);
1235 ExReleaseFastMutex(&ViewLock
);
1245 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
1250 DPRINT("CcInitView()\n");
1252 BoundaryAddressMultiple
.QuadPart
= 0;
1253 CiCacheSegMappingRegionHint
= 0;
1254 CiCacheSegMappingRegionBase
= NULL
;
1256 MmLockAddressSpace(MmGetKernelAddressSpace());
1258 Status
= MmCreateMemoryArea(NULL
,
1259 MmGetKernelAddressSpace(),
1260 MEMORY_AREA_CACHE_SEGMENT
,
1261 &CiCacheSegMappingRegionBase
,
1262 CI_CACHESEG_MAPPING_REGION_SIZE
,
1267 BoundaryAddressMultiple
);
1268 MmUnlockAddressSpace(MmGetKernelAddressSpace());
1269 if (!NT_SUCCESS(Status
))
1274 Buffer
= ExAllocatePool(NonPagedPool
, CI_CACHESEG_MAPPING_REGION_SIZE
/ (PAGE_SIZE
* 8));
1276 RtlInitializeBitMap(&CiCacheSegMappingRegionAllocMap
, Buffer
, CI_CACHESEG_MAPPING_REGION_SIZE
/ PAGE_SIZE
);
1277 RtlClearAllBits(&CiCacheSegMappingRegionAllocMap
);
1279 KeInitializeSpinLock(&CiCacheSegMappingRegionLock
);
1281 InitializeListHead(&CacheSegmentListHead
);
1282 InitializeListHead(&DirtySegmentListHead
);
1283 InitializeListHead(&CacheSegmentLRUListHead
);
1284 InitializeListHead(&ClosedListHead
);
1285 ExInitializeFastMutex(&ViewLock
);
1286 ExInitializeNPagedLookasideList (&iBcbLookasideList
,
1290 sizeof(INTERNAL_BCB
),
1293 ExInitializeNPagedLookasideList (&BcbLookasideList
,
1300 ExInitializeNPagedLookasideList (&CacheSegLookasideList
,
1304 sizeof(CACHE_SEGMENT
),
1308 MmInitializeMemoryConsumer(MC_CACHE
, CcRosTrimCache
);
1310 CcInitCacheZeroPage();
1313 LazyCloseThreadShouldTerminate
= FALSE
;
1314 KeInitializeEvent (&LazyCloseThreadEvent
, SynchronizationEvent
, FALSE
);
1315 Status
= PsCreateSystemThread(&LazyCloseThreadHandle
,
1320 (PKSTART_ROUTINE
)CmLazyCloseThreadMain
,
1322 if (NT_SUCCESS(Status
))
1324 Priority
= LOW_REALTIME_PRIORITY
;
1325 NtSetInformationThread(LazyCloseThreadHandle
,