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 #define TAG_CSEG TAG('C', 'S', 'E', 'G')
53 #define TAG_BCB TAG('B', 'C', 'B', ' ')
54 #define TAG_IBCB TAG('i', 'B', 'C', 'B')
56 static LIST_ENTRY DirtySegmentListHead
;
57 static LIST_ENTRY CacheSegmentListHead
;
58 static LIST_ENTRY CacheSegmentLRUListHead
;
59 static LIST_ENTRY ClosedListHead
;
60 ULONG DirtyPageCount
=0;
65 #define CI_CACHESEG_MAPPING_REGION_SIZE (128*1024*1024)
67 static PVOID CiCacheSegMappingRegionBase
= NULL
;
68 static RTL_BITMAP CiCacheSegMappingRegionAllocMap
;
69 static ULONG CiCacheSegMappingRegionHint
;
70 static KSPIN_LOCK CiCacheSegMappingRegionLock
;
73 NPAGED_LOOKASIDE_LIST iBcbLookasideList
;
74 static NPAGED_LOOKASIDE_LIST BcbLookasideList
;
75 static NPAGED_LOOKASIDE_LIST CacheSegLookasideList
;
77 static ULONG CcTimeStamp
;
78 static KEVENT LazyCloseThreadEvent
;
79 static HANDLE LazyCloseThreadHandle
;
80 static CLIENT_ID LazyCloseThreadId
;
81 static volatile BOOLEAN LazyCloseThreadShouldTerminate
;
84 /* void * alloca(size_t size); */
85 #elif defined(_MSC_VER)
86 void* _alloca(size_t size
);
88 #error Unknown compiler for alloca intrinsic stack allocation "function"
93 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg
);
95 /* FUNCTIONS *****************************************************************/
98 CcRosFlushCacheSegment(PCACHE_SEGMENT CacheSegment
)
102 Status
= WriteCacheSegment(CacheSegment
);
103 if (NT_SUCCESS(Status
))
105 ExAcquireFastMutex(&ViewLock
);
106 KeAcquireSpinLock(&CacheSegment
->Bcb
->BcbLock
, &oldIrql
);
107 CacheSegment
->Dirty
= FALSE
;
108 RemoveEntryList(&CacheSegment
->DirtySegmentListEntry
);
109 DirtyPageCount
-= CacheSegment
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
110 CacheSegment
->ReferenceCount
--;
111 KeReleaseSpinLock(&CacheSegment
->Bcb
->BcbLock
, oldIrql
);
112 ExReleaseFastMutex(&ViewLock
);
118 CcRosFlushDirtyPages(ULONG Target
, PULONG Count
)
120 PLIST_ENTRY current_entry
;
121 PCACHE_SEGMENT current
;
122 ULONG PagesPerSegment
;
125 static ULONG WriteCount
[4] = {0, 0, 0, 0};
128 DPRINT("CcRosFlushDirtyPages(Target %d)\n", Target
);
132 ExAcquireFastMutex(&ViewLock
);
134 WriteCount
[0] = WriteCount
[1];
135 WriteCount
[1] = WriteCount
[2];
136 WriteCount
[2] = WriteCount
[3];
139 NewTarget
= WriteCount
[0] + WriteCount
[1] + WriteCount
[2];
141 if (NewTarget
< DirtyPageCount
)
143 NewTarget
= (DirtyPageCount
- NewTarget
+ 3) / 4;
144 WriteCount
[0] += NewTarget
;
145 WriteCount
[1] += NewTarget
;
146 WriteCount
[2] += NewTarget
;
147 WriteCount
[3] += NewTarget
;
150 NewTarget
= WriteCount
[0];
152 Target
= max(NewTarget
, Target
);
154 current_entry
= DirtySegmentListHead
.Flink
;
155 if (current_entry
== &DirtySegmentListHead
)
157 DPRINT("No Dirty pages\n");
159 while (current_entry
!= &DirtySegmentListHead
&& Target
> 0)
161 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
162 DirtySegmentListEntry
);
163 current_entry
= current_entry
->Flink
;
164 Locked
= ExTryToAcquireFastMutex(¤t
->Lock
);
169 ASSERT(current
->Dirty
);
170 if (current
->ReferenceCount
> 1)
172 ExReleaseFastMutex(¤t
->Lock
);
175 ExReleaseFastMutex(&ViewLock
);
176 PagesPerSegment
= current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
177 Status
= CcRosFlushCacheSegment(current
);
178 ExReleaseFastMutex(¤t
->Lock
);
179 if (!NT_SUCCESS(Status
) && (Status
!= STATUS_END_OF_FILE
))
181 DPRINT1("CC: Failed to flush cache segment.\n");
185 (*Count
) += PagesPerSegment
;
186 Target
-= PagesPerSegment
;
188 ExAcquireFastMutex(&ViewLock
);
189 current_entry
= DirtySegmentListHead
.Flink
;
191 if (*Count
< NewTarget
)
193 WriteCount
[1] += (NewTarget
- *Count
);
195 ExReleaseFastMutex(&ViewLock
);
196 DPRINT("CcRosFlushDirtyPages() finished\n");
198 return(STATUS_SUCCESS
);
202 CcRosTrimCache(ULONG Target
, ULONG Priority
, PULONG NrFreed
)
204 * FUNCTION: Try to free some memory from the file cache.
206 * Target - The number of pages to be freed.
207 * Priority - The priority of free (currently unused).
208 * NrFreed - Points to a variable where the number of pages
209 * actually freed is returned.
212 PLIST_ENTRY current_entry
;
213 PCACHE_SEGMENT current
, last
= NULL
;
214 ULONG PagesPerSegment
;
219 DPRINT("CcRosTrimCache(Target %d)\n", Target
);
223 InitializeListHead(&FreeList
);
225 ExAcquireFastMutex(&ViewLock
);
226 current_entry
= CacheSegmentLRUListHead
.Flink
;
227 while (current_entry
!= &CacheSegmentLRUListHead
&& Target
> 0)
229 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
230 CacheSegmentLRUListEntry
);
231 current_entry
= current_entry
->Flink
;
233 KeAcquireSpinLock(¤t
->Bcb
->BcbLock
, &oldIrql
);
234 if (current
->ReferenceCount
== 0)
236 RemoveEntryList(¤t
->BcbSegmentListEntry
);
237 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
238 RemoveEntryList(¤t
->CacheSegmentListEntry
);
239 RemoveEntryList(¤t
->CacheSegmentLRUListEntry
);
240 InsertHeadList(&FreeList
, ¤t
->BcbSegmentListEntry
);
241 PagesPerSegment
= current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
242 PagesFreed
= min(PagesPerSegment
, Target
);
243 Target
-= PagesFreed
;
244 (*NrFreed
) += PagesFreed
;
248 if (last
!= current
&& current
->MappedCount
> 0 && !current
->Dirty
&& !current
->PageOut
)
253 current
->ReferenceCount
++;
255 current
->PageOut
= TRUE
;
256 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
257 ExReleaseFastMutex(&ViewLock
);
258 for (i
= 0; i
< current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
; i
++)
261 Page
= MmGetPhysicalAddress((char*)current
->BaseAddress
+ i
* PAGE_SIZE
).QuadPart
>> PAGE_SHIFT
;
262 Status
= MmPageOutPhysicalAddress(Page
);
263 if (!NT_SUCCESS(Status
))
268 ExAcquireFastMutex(&ViewLock
);
269 KeAcquireSpinLock(¤t
->Bcb
->BcbLock
, &oldIrql
);
270 current
->ReferenceCount
--;
271 current
->PageOut
= FALSE
;
272 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
273 current_entry
= ¤t
->CacheSegmentLRUListEntry
;
276 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
279 ExReleaseFastMutex(&ViewLock
);
281 while (!IsListEmpty(&FreeList
))
283 current_entry
= RemoveHeadList(&FreeList
);
284 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
285 BcbSegmentListEntry
);
286 CcRosInternalFreeCacheSegment(current
);
289 DPRINT("CcRosTrimCache() finished\n");
290 return(STATUS_SUCCESS
);
294 CcRosReleaseCacheSegment(PBCB Bcb
,
295 PCACHE_SEGMENT CacheSeg
,
300 BOOLEAN WasDirty
= CacheSeg
->Dirty
;
305 DPRINT("CcReleaseCacheSegment(Bcb %x, CacheSeg %x, Valid %d)\n",
306 Bcb
, CacheSeg
, Valid
);
308 CacheSeg
->Valid
= Valid
;
309 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| Dirty
;
311 ExAcquireFastMutex(&ViewLock
);
312 if (!WasDirty
&& CacheSeg
->Dirty
)
314 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
315 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
317 RemoveEntryList(&CacheSeg
->CacheSegmentLRUListEntry
);
318 InsertTailList(&CacheSegmentLRUListHead
, &CacheSeg
->CacheSegmentLRUListEntry
);
322 CacheSeg
->MappedCount
++;
324 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
325 CacheSeg
->ReferenceCount
--;
326 if (Mapped
&& CacheSeg
->MappedCount
== 1)
328 CacheSeg
->ReferenceCount
++;
330 if (!WasDirty
&& CacheSeg
->Dirty
)
332 CacheSeg
->ReferenceCount
++;
334 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
335 ExReleaseFastMutex(&ViewLock
);
336 ExReleaseFastMutex(&CacheSeg
->Lock
);
338 return(STATUS_SUCCESS
);
342 CcRosLookupCacheSegment(PBCB Bcb
, ULONG FileOffset
)
344 PLIST_ENTRY current_entry
;
345 PCACHE_SEGMENT current
;
350 DPRINT("CcRosLookupCacheSegment(Bcb %x, FileOffset %d)\n", Bcb
, FileOffset
);
352 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
353 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
354 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
356 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
357 BcbSegmentListEntry
);
358 if (current
->FileOffset
<= FileOffset
&&
359 (current
->FileOffset
+ Bcb
->CacheSegmentSize
) > FileOffset
)
361 current
->ReferenceCount
++;
362 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
363 ExAcquireFastMutex(¤t
->Lock
);
366 current_entry
= current_entry
->Flink
;
368 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
373 CcRosMarkDirtyCacheSegment(PBCB Bcb
, ULONG FileOffset
)
375 PCACHE_SEGMENT CacheSeg
;
380 DPRINT("CcRosMarkDirtyCacheSegment(Bcb %x, FileOffset %d)\n", Bcb
, FileOffset
);
382 CacheSeg
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
383 if (CacheSeg
== NULL
)
387 if (!CacheSeg
->Dirty
)
389 ExAcquireFastMutex(&ViewLock
);
390 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
391 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
392 ExReleaseFastMutex(&ViewLock
);
396 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
397 CacheSeg
->ReferenceCount
--;
398 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
402 CacheSeg
->Dirty
= TRUE
;
403 ExReleaseFastMutex(&CacheSeg
->Lock
);
405 return(STATUS_SUCCESS
);
409 CcRosUnmapCacheSegment(PBCB Bcb
, ULONG FileOffset
, BOOLEAN NowDirty
)
411 PCACHE_SEGMENT CacheSeg
;
417 DPRINT("CcRosUnmapCacheSegment(Bcb %x, FileOffset %d, NowDirty %d)\n",
418 Bcb
, FileOffset
, NowDirty
);
420 CacheSeg
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
421 if (CacheSeg
== NULL
)
423 return(STATUS_UNSUCCESSFUL
);
426 WasDirty
= CacheSeg
->Dirty
;
427 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| NowDirty
;
429 CacheSeg
->MappedCount
--;
431 if (!WasDirty
&& NowDirty
)
433 ExAcquireFastMutex(&ViewLock
);
434 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
435 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
436 ExReleaseFastMutex(&ViewLock
);
439 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
440 CacheSeg
->ReferenceCount
--;
441 if (!WasDirty
&& NowDirty
)
443 CacheSeg
->ReferenceCount
++;
445 if (CacheSeg
->MappedCount
== 0)
447 CacheSeg
->ReferenceCount
--;
449 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
451 ExReleaseFastMutex(&CacheSeg
->Lock
);
452 return(STATUS_SUCCESS
);
456 CcRosCreateCacheSegment(PBCB Bcb
,
458 PCACHE_SEGMENT
* CacheSeg
)
461 PCACHE_SEGMENT current
;
462 PCACHE_SEGMENT previous
;
463 PLIST_ENTRY current_entry
;
468 ULONG StartingOffset
;
471 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
475 DPRINT("CcRosCreateCacheSegment()\n");
477 BoundaryAddressMultiple
.QuadPart
= 0;
478 if (FileOffset
>= Bcb
->FileSize
.u
.LowPart
)
481 return STATUS_INVALID_PARAMETER
;
484 current
= ExAllocateFromNPagedLookasideList(&CacheSegLookasideList
);
485 current
->Valid
= FALSE
;
486 current
->Dirty
= FALSE
;
487 current
->PageOut
= FALSE
;
488 current
->FileOffset
= ROUND_DOWN(FileOffset
, Bcb
->CacheSegmentSize
);
490 current
->MappedCount
= 0;
491 current
->DirtySegmentListEntry
.Flink
= NULL
;
492 current
->DirtySegmentListEntry
.Blink
= NULL
;
493 current
->ReferenceCount
= 1;
494 ExInitializeFastMutex(¤t
->Lock
);
495 ExAcquireFastMutex(¤t
->Lock
);
496 ExAcquireFastMutex(&ViewLock
);
499 /* There is window between the call to CcRosLookupCacheSegment
500 * and CcRosCreateCacheSegment. We must check if a segment on
501 * the fileoffset exist. If there exist a segment, we release
502 * our new created segment and return the existing one.
504 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
505 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
507 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
509 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
510 BcbSegmentListEntry
);
511 if (current
->FileOffset
<= FileOffset
&&
512 (current
->FileOffset
+ Bcb
->CacheSegmentSize
) > FileOffset
)
514 current
->ReferenceCount
++;
515 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
516 ExReleaseFastMutex(&(*CacheSeg
)->Lock
);
517 ExReleaseFastMutex(&ViewLock
);
518 ExFreeToNPagedLookasideList(&CacheSegLookasideList
, *CacheSeg
);
520 ExAcquireFastMutex(¤t
->Lock
);
521 return STATUS_SUCCESS
;
523 if (current
->FileOffset
< FileOffset
)
525 if (previous
== NULL
)
531 if (previous
->FileOffset
< current
->FileOffset
)
537 current_entry
= current_entry
->Flink
;
539 /* There was no existing segment. */
543 InsertHeadList(&previous
->BcbSegmentListEntry
, ¤t
->BcbSegmentListEntry
);
547 InsertHeadList(&Bcb
->BcbSegmentListHead
, ¤t
->BcbSegmentListEntry
);
549 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
550 InsertTailList(&CacheSegmentListHead
, ¤t
->CacheSegmentListEntry
);
551 InsertTailList(&CacheSegmentLRUListHead
, ¤t
->CacheSegmentLRUListEntry
);
552 ExReleaseFastMutex(&ViewLock
);
554 KeAcquireSpinLock(&CiCacheSegMappingRegionLock
, &oldIrql
);
556 StartingOffset
= RtlFindClearBitsAndSet(&CiCacheSegMappingRegionAllocMap
, Bcb
->CacheSegmentSize
/ PAGE_SIZE
, CiCacheSegMappingRegionHint
);
558 if (StartingOffset
== 0xffffffff)
560 DPRINT1("Out of CacheSeg mapping space\n");
564 current
->BaseAddress
= CiCacheSegMappingRegionBase
+ StartingOffset
* PAGE_SIZE
;
566 if (CiCacheSegMappingRegionHint
== StartingOffset
)
568 CiCacheSegMappingRegionHint
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
571 KeReleaseSpinLock(&CiCacheSegMappingRegionLock
, oldIrql
);
573 MmLockAddressSpace(MmGetKernelAddressSpace());
574 current
->BaseAddress
= NULL
;
575 Status
= MmCreateMemoryArea(NULL
,
576 MmGetKernelAddressSpace(),
577 MEMORY_AREA_CACHE_SEGMENT
,
578 ¤t
->BaseAddress
,
579 Bcb
->CacheSegmentSize
,
581 (PMEMORY_AREA
*)¤t
->MemoryArea
,
584 BoundaryAddressMultiple
);
585 MmUnlockAddressSpace(MmGetKernelAddressSpace());
586 if (!NT_SUCCESS(Status
))
591 Pfn
= alloca(sizeof(PFN_TYPE
) * (Bcb
->CacheSegmentSize
/ PAGE_SIZE
));
592 for (i
= 0; i
< (Bcb
->CacheSegmentSize
/ PAGE_SIZE
); i
++)
594 Status
= MmRequestPageMemoryConsumer(MC_CACHE
, TRUE
, &Pfn
[i
]);
595 if (!NT_SUCCESS(Status
))
600 Status
= MmCreateVirtualMapping(NULL
,
601 current
->BaseAddress
,
604 Bcb
->CacheSegmentSize
/ PAGE_SIZE
);
605 if (!NT_SUCCESS(Status
))
609 return(STATUS_SUCCESS
);
613 CcRosGetCacheSegmentChain(PBCB Bcb
,
616 PCACHE_SEGMENT
* CacheSeg
)
618 PCACHE_SEGMENT current
;
620 PCACHE_SEGMENT
* CacheSegList
;
621 PCACHE_SEGMENT Previous
= NULL
;
625 DPRINT("CcRosGetCacheSegmentChain()\n");
627 Length
= ROUND_UP(Length
, Bcb
->CacheSegmentSize
);
629 #if defined(__GNUC__)
630 CacheSegList
= alloca(sizeof(PCACHE_SEGMENT
) *
631 (Length
/ Bcb
->CacheSegmentSize
));
632 #elif defined(_MSC_VER)
633 CacheSegList
= _alloca(sizeof(PCACHE_SEGMENT
) *
634 (Length
/ Bcb
->CacheSegmentSize
));
636 #error Unknown compiler for alloca intrinsic stack allocation "function"
640 * Look for a cache segment already mapping the same data.
642 for (i
= 0; i
< (Length
/ Bcb
->CacheSegmentSize
); i
++)
644 ULONG CurrentOffset
= FileOffset
+ (i
* Bcb
->CacheSegmentSize
);
645 current
= CcRosLookupCacheSegment(Bcb
, CurrentOffset
);
648 CacheSegList
[i
] = current
;
652 CcRosCreateCacheSegment(Bcb
, CurrentOffset
, ¤t
);
653 CacheSegList
[i
] = current
;
657 for (i
= 0; i
< (Length
/ Bcb
->CacheSegmentSize
); i
++)
661 *CacheSeg
= CacheSegList
[i
];
662 Previous
= CacheSegList
[i
];
666 Previous
->NextInChain
= CacheSegList
[i
];
667 Previous
= CacheSegList
[i
];
670 Previous
->NextInChain
= NULL
;
672 return(STATUS_SUCCESS
);
676 CcRosGetCacheSegment(PBCB Bcb
,
681 PCACHE_SEGMENT
* CacheSeg
)
683 PCACHE_SEGMENT current
;
688 DPRINT("CcRosGetCacheSegment()\n");
691 * Look for a cache segment already mapping the same data.
693 current
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
697 * Otherwise create a new segment.
699 Status
= CcRosCreateCacheSegment(Bcb
, FileOffset
, ¤t
);
700 if (!NT_SUCCESS(Status
))
706 * Return information about the segment to the caller.
708 *UptoDate
= current
->Valid
;
709 *BaseAddress
= current
->BaseAddress
;
710 DPRINT("*BaseAddress 0x%.8X\n", *BaseAddress
);
712 *BaseOffset
= current
->FileOffset
;
713 return(STATUS_SUCCESS
);
717 CcRosRequestCacheSegment(PBCB Bcb
,
721 PCACHE_SEGMENT
* CacheSeg
)
723 * FUNCTION: Request a page mapping for a BCB
730 if ((FileOffset
% Bcb
->CacheSegmentSize
) != 0)
732 CPRINT("Bad fileoffset %x should be multiple of %x",
733 FileOffset
, Bcb
->CacheSegmentSize
);
737 return(CcRosGetCacheSegment(Bcb
,
747 CcFreeCachePage(PVOID Context
, MEMORY_AREA
* MemoryArea
, PVOID Address
,
748 PFN_TYPE Page
, SWAPENTRY SwapEntry
, BOOLEAN Dirty
)
750 ASSERT(SwapEntry
== 0);
753 MmReleasePageMemoryConsumer(MC_CACHE
, Page
);
758 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg
)
760 * FUNCTION: Releases a cache segment associated with a BCB
770 DPRINT("Freeing cache segment %x\n", CacheSeg
);
772 RegionSize
= CacheSeg
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
774 /* Unmap all the pages. */
775 for (i
= 0; i
< RegionSize
; i
++)
777 MmDeleteVirtualMapping(NULL
,
778 CacheSeg
->BaseAddress
+ (i
* PAGE_SIZE
),
782 MmReleasePageMemoryConsumer(MC_CACHE
, Page
);
785 KeAcquireSpinLock(&CiCacheSegMappingRegionLock
, &oldIrql
);
786 /* Deallocate all the pages used. */
787 Base
= (ULONG
)(CacheSeg
->BaseAddress
- CiCacheSegMappingRegionBase
) / PAGE_SIZE
;
789 RtlClearBits(&CiCacheSegMappingRegionAllocMap
, Base
, RegionSize
);
791 CiCacheSegMappingRegionHint
= min (CiCacheSegMappingRegionHint
, Base
);
793 KeReleaseSpinLock(&CiCacheSegMappingRegionLock
, oldIrql
);
795 MmLockAddressSpace(MmGetKernelAddressSpace());
796 MmFreeMemoryArea(MmGetKernelAddressSpace(),
797 CacheSeg
->MemoryArea
,
800 MmUnlockAddressSpace(MmGetKernelAddressSpace());
802 ExFreeToNPagedLookasideList(&CacheSegLookasideList
, CacheSeg
);
803 return(STATUS_SUCCESS
);
807 CcRosFreeCacheSegment(PBCB Bcb
, PCACHE_SEGMENT CacheSeg
)
814 DPRINT("CcRosFreeCacheSegment(Bcb %x, CacheSeg %x)\n",
817 ExAcquireFastMutex(&ViewLock
);
818 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
819 RemoveEntryList(&CacheSeg
->BcbSegmentListEntry
);
820 RemoveEntryList(&CacheSeg
->CacheSegmentListEntry
);
821 RemoveEntryList(&CacheSeg
->CacheSegmentLRUListEntry
);
824 RemoveEntryList(&CacheSeg
->DirtySegmentListEntry
);
825 DirtyPageCount
-= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
828 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
829 ExReleaseFastMutex(&ViewLock
);
831 Status
= CcRosInternalFreeCacheSegment(CacheSeg
);
839 CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointers
,
840 IN PLARGE_INTEGER FileOffset OPTIONAL
,
842 OUT PIO_STATUS_BLOCK IoStatus
)
845 LARGE_INTEGER Offset
;
846 PCACHE_SEGMENT current
;
850 DPRINT("CcFlushCache(SectionObjectPointers %x, FileOffset %x, Length %d, IoStatus %x)\n",
851 SectionObjectPointers
, FileOffset
, Length
, IoStatus
);
853 if (SectionObjectPointers
&& SectionObjectPointers
->SharedCacheMap
)
855 Bcb
= (PBCB
)SectionObjectPointers
->SharedCacheMap
;
859 Offset
= *FileOffset
;
863 Offset
.QuadPart
= (LONGLONG
)0;
864 Length
= Bcb
->FileSize
.u
.LowPart
;
869 IoStatus
->Status
= STATUS_SUCCESS
;
870 IoStatus
->Information
= 0;
875 current
= CcRosLookupCacheSegment (Bcb
, Offset
.u
.LowPart
);
880 Status
= CcRosFlushCacheSegment(current
);
881 if (!NT_SUCCESS(Status
) && IoStatus
!= NULL
)
883 IoStatus
->Status
= Status
;
886 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
887 ExReleaseFastMutex(¤t
->Lock
);
888 current
->ReferenceCount
--;
889 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
892 Offset
.QuadPart
+= Bcb
->CacheSegmentSize
;
893 if (Length
> Bcb
->CacheSegmentSize
)
895 Length
-= Bcb
->CacheSegmentSize
;
907 IoStatus
->Status
= STATUS_INVALID_PARAMETER
;
913 CcRosDeleteFileCache(PFILE_OBJECT FileObject
, PBCB Bcb
)
915 * FUNCTION: Releases the BCB associated with a file object
918 PLIST_ENTRY current_entry
;
919 PCACHE_SEGMENT current
;
927 ExReleaseFastMutex(&ViewLock
);
929 CcFlushCache(FileObject
->SectionObjectPointer
, NULL
, 0, NULL
);
931 ExAcquireFastMutex(&ViewLock
);
933 if (Bcb
->RefCount
== 0)
935 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
937 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
938 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
941 FileObject
->SectionObjectPointer
->SharedCacheMap
= NULL
;
944 * Release all cache segments.
946 InitializeListHead(&FreeList
);
947 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
948 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
949 while (!IsListEmpty(&Bcb
->BcbSegmentListHead
))
951 current_entry
= RemoveTailList(&Bcb
->BcbSegmentListHead
);
952 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, BcbSegmentListEntry
);
953 RemoveEntryList(¤t
->CacheSegmentListEntry
);
954 RemoveEntryList(¤t
->CacheSegmentLRUListEntry
);
957 RemoveEntryList(¤t
->DirtySegmentListEntry
);
958 DirtyPageCount
-= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
959 DPRINT1("Freeing dirty segment\n");
961 InsertHeadList(&FreeList
, ¤t
->BcbSegmentListEntry
);
963 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
965 ExReleaseFastMutex(&ViewLock
);
966 ObDereferenceObject (Bcb
->FileObject
);
968 while (!IsListEmpty(&FreeList
))
970 current_entry
= RemoveTailList(&FreeList
);
971 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, BcbSegmentListEntry
);
972 Status
= CcRosInternalFreeCacheSegment(current
);
974 ExFreeToNPagedLookasideList(&BcbLookasideList
, Bcb
);
975 ExAcquireFastMutex(&ViewLock
);
977 return(STATUS_SUCCESS
);
980 VOID
CcRosReferenceCache(PFILE_OBJECT FileObject
)
983 ExAcquireFastMutex(&ViewLock
);
984 Bcb
= (PBCB
)FileObject
->SectionObjectPointer
->SharedCacheMap
;
986 if (Bcb
->RefCount
== 0)
988 ASSERT(Bcb
->BcbRemoveListEntry
.Flink
!= NULL
);
989 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
990 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
995 ASSERT(Bcb
->BcbRemoveListEntry
.Flink
== NULL
);
998 ExReleaseFastMutex(&ViewLock
);
1001 VOID
CcRosSetRemoveOnClose(PSECTION_OBJECT_POINTERS SectionObjectPointer
)
1004 DPRINT("CcRosSetRemoveOnClose()\n");
1005 ExAcquireFastMutex(&ViewLock
);
1006 Bcb
= (PBCB
)SectionObjectPointer
->SharedCacheMap
;
1009 Bcb
->RemoveOnClose
= TRUE
;
1010 if (Bcb
->RefCount
== 0)
1012 CcRosDeleteFileCache(Bcb
->FileObject
, Bcb
);
1015 ExReleaseFastMutex(&ViewLock
);
1019 VOID
CcRosDereferenceCache(PFILE_OBJECT FileObject
)
1022 ExAcquireFastMutex(&ViewLock
);
1023 Bcb
= (PBCB
)FileObject
->SectionObjectPointer
->SharedCacheMap
;
1025 if (Bcb
->RefCount
> 0)
1028 if (Bcb
->RefCount
== 0)
1030 MmFreeSectionSegments(Bcb
->FileObject
);
1031 if (Bcb
->RemoveOnClose
)
1033 CcRosDeleteFileCache(FileObject
, Bcb
);
1037 Bcb
->TimeStamp
= CcTimeStamp
;
1038 InsertHeadList(&ClosedListHead
, &Bcb
->BcbRemoveListEntry
);
1042 ExReleaseFastMutex(&ViewLock
);
1046 CcRosReleaseFileCache(PFILE_OBJECT FileObject
)
1048 * FUNCTION: Called by the file system when a handle to a file object
1054 ExAcquireFastMutex(&ViewLock
);
1056 if (FileObject
->SectionObjectPointer
->SharedCacheMap
!= NULL
)
1058 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1059 if (FileObject
->PrivateCacheMap
!= NULL
)
1061 FileObject
->PrivateCacheMap
= NULL
;
1062 if (Bcb
->RefCount
> 0)
1065 if (Bcb
->RefCount
== 0)
1067 MmFreeSectionSegments(Bcb
->FileObject
);
1068 if (Bcb
->RemoveOnClose
)
1070 CcRosDeleteFileCache(FileObject
, Bcb
);
1074 Bcb
->TimeStamp
= CcTimeStamp
;
1075 InsertHeadList(&ClosedListHead
, &Bcb
->BcbRemoveListEntry
);
1081 ExReleaseFastMutex(&ViewLock
);
1082 return(STATUS_SUCCESS
);
1086 CcTryToInitializeFileCache(PFILE_OBJECT FileObject
)
1091 ExAcquireFastMutex(&ViewLock
);
1093 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1096 Status
= STATUS_UNSUCCESSFUL
;
1100 if (FileObject
->PrivateCacheMap
== NULL
)
1102 FileObject
->PrivateCacheMap
= Bcb
;
1105 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
1107 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1108 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1110 Status
= STATUS_SUCCESS
;
1112 ExReleaseFastMutex(&ViewLock
);
1119 CcRosInitializeFileCache(PFILE_OBJECT FileObject
,
1120 ULONG CacheSegmentSize
)
1122 * FUNCTION: Initializes a BCB for a file object
1127 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1128 DPRINT("CcRosInitializeFileCache(FileObject %x, *Bcb %x, CacheSegmentSize %d)\n",
1129 FileObject
, Bcb
, CacheSegmentSize
);
1131 ExAcquireFastMutex(&ViewLock
);
1134 Bcb
= ExAllocateFromNPagedLookasideList(&BcbLookasideList
);
1137 ExReleaseFastMutex(&ViewLock
);
1138 return(STATUS_UNSUCCESSFUL
);
1140 memset(Bcb
, 0, sizeof(BCB
));
1141 ObReferenceObjectByPointer(FileObject
,
1145 Bcb
->FileObject
= FileObject
;
1146 Bcb
->CacheSegmentSize
= CacheSegmentSize
;
1147 if (FileObject
->FsContext
)
1149 Bcb
->AllocationSize
=
1150 ((PFSRTL_COMMON_FCB_HEADER
)FileObject
->FsContext
)->AllocationSize
;
1152 ((PFSRTL_COMMON_FCB_HEADER
)FileObject
->FsContext
)->FileSize
;
1154 KeInitializeSpinLock(&Bcb
->BcbLock
);
1155 InitializeListHead(&Bcb
->BcbSegmentListHead
);
1156 FileObject
->SectionObjectPointer
->SharedCacheMap
= Bcb
;
1158 if (FileObject
->PrivateCacheMap
== NULL
)
1160 FileObject
->PrivateCacheMap
= Bcb
;
1163 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
1165 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1166 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1168 ExReleaseFastMutex(&ViewLock
);
1170 return(STATUS_SUCCESS
);
1176 PFILE_OBJECT STDCALL
1177 CcGetFileObjectFromSectionPtrs(IN PSECTION_OBJECT_POINTERS SectionObjectPointers
)
1180 if (SectionObjectPointers
&& SectionObjectPointers
->SharedCacheMap
)
1182 Bcb
= (PBCB
)SectionObjectPointers
->SharedCacheMap
;
1184 return Bcb
->FileObject
;
1190 CmLazyCloseThreadMain(PVOID Ignored
)
1192 LARGE_INTEGER Timeout
;
1193 PLIST_ENTRY current_entry
;
1195 ULONG RemoveTimeStamp
;
1198 KeQuerySystemTime (&Timeout
);
1202 Timeout
.QuadPart
+= (LONGLONG
)100000000; // 10sec
1203 Status
= KeWaitForSingleObject(&LazyCloseThreadEvent
,
1209 DPRINT("LazyCloseThreadMain %d\n", CcTimeStamp
);
1211 if (!NT_SUCCESS(Status
))
1213 DbgPrint("LazyCloseThread: Wait failed\n");
1217 if (LazyCloseThreadShouldTerminate
)
1219 DbgPrint("LazyCloseThread: Terminating\n");
1223 ExAcquireFastMutex(&ViewLock
);
1225 if (CcTimeStamp
>= 30)
1227 RemoveTimeStamp
= CcTimeStamp
- 30; /* 5min = 10sec * 30 */
1228 while (!IsListEmpty(&ClosedListHead
))
1230 current_entry
= ClosedListHead
.Blink
;
1231 current
= CONTAINING_RECORD(current_entry
, BCB
, BcbRemoveListEntry
);
1232 if (current
->TimeStamp
>= RemoveTimeStamp
)
1236 CcRosDeleteFileCache(current
->FileObject
, current
);
1239 ExReleaseFastMutex(&ViewLock
);
1249 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
1254 DPRINT("CcInitView()\n");
1256 BoundaryAddressMultiple
.QuadPart
= 0;
1257 CiCacheSegMappingRegionHint
= 0;
1258 CiCacheSegMappingRegionBase
= NULL
;
1260 MmLockAddressSpace(MmGetKernelAddressSpace());
1262 Status
= MmCreateMemoryArea(NULL
,
1263 MmGetKernelAddressSpace(),
1264 MEMORY_AREA_CACHE_SEGMENT
,
1265 &CiCacheSegMappingRegionBase
,
1266 CI_CACHESEG_MAPPING_REGION_SIZE
,
1271 BoundaryAddressMultiple
);
1272 MmUnlockAddressSpace(MmGetKernelAddressSpace());
1273 if (!NT_SUCCESS(Status
))
1278 Buffer
= ExAllocatePool(NonPagedPool
, CI_CACHESEG_MAPPING_REGION_SIZE
/ (PAGE_SIZE
* 8));
1280 RtlInitializeBitMap(&CiCacheSegMappingRegionAllocMap
, Buffer
, CI_CACHESEG_MAPPING_REGION_SIZE
/ PAGE_SIZE
);
1281 RtlClearAllBits(&CiCacheSegMappingRegionAllocMap
);
1283 KeInitializeSpinLock(&CiCacheSegMappingRegionLock
);
1285 InitializeListHead(&CacheSegmentListHead
);
1286 InitializeListHead(&DirtySegmentListHead
);
1287 InitializeListHead(&CacheSegmentLRUListHead
);
1288 InitializeListHead(&ClosedListHead
);
1289 ExInitializeFastMutex(&ViewLock
);
1290 ExInitializeNPagedLookasideList (&iBcbLookasideList
,
1294 sizeof(INTERNAL_BCB
),
1297 ExInitializeNPagedLookasideList (&BcbLookasideList
,
1304 ExInitializeNPagedLookasideList (&CacheSegLookasideList
,
1308 sizeof(CACHE_SEGMENT
),
1312 MmInitializeMemoryConsumer(MC_CACHE
, CcRosTrimCache
);
1314 CcInitCacheZeroPage();
1317 LazyCloseThreadShouldTerminate
= FALSE
;
1318 KeInitializeEvent (&LazyCloseThreadEvent
, SynchronizationEvent
, FALSE
);
1319 Status
= PsCreateSystemThread(&LazyCloseThreadHandle
,
1324 (PKSTART_ROUTINE
)CmLazyCloseThreadMain
,
1326 if (NT_SUCCESS(Status
))
1328 Priority
= LOW_REALTIME_PRIORITY
;
1329 NtSetInformationThread(LazyCloseThreadHandle
,