2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/cc/view.c
5 * PURPOSE: Cache manager
7 * PROGRAMMERS: David Welch (welch@mcmail.com)
10 /* NOTES **********************************************************************
12 * This is not the NT implementation of a file cache nor anything much like
15 * The general procedure for a filesystem to implement a read or write
16 * dispatch routine is as follows
18 * (1) If caching for the FCB hasn't been initiated then so do by calling
19 * CcInitializeFileCache.
21 * (2) For each 4k region which is being read or written obtain a cache page
22 * by calling CcRequestCachePage.
24 * (3) If either the page is being read or not completely written, and it is
25 * not up to date then read its data from the underlying medium. If the read
26 * fails then call CcReleaseCachePage with VALID as FALSE and return a error.
28 * (4) Copy the data into or out of the page as necessary.
30 * (5) Release the cache page
32 /* INCLUDES ******************************************************************/
38 #if defined (ALLOC_PRAGMA)
39 #pragma alloc_text(INIT, CcInitView)
42 /* GLOBALS *******************************************************************/
45 * If CACHE_BITMAP is defined, the cache manager uses one large memory region
46 * within the kernel address space and allocate/deallocate space from this block
47 * over a bitmap. If CACHE_BITMAP is used, the size of the mdl mapping region
48 * must be reduced (ntoskrnl\mm\mdl.c, MI_MDLMAPPING_REGION_SIZE).
50 //#define CACHE_BITMAP
52 static LIST_ENTRY DirtySegmentListHead
;
53 static LIST_ENTRY CacheSegmentListHead
;
54 static LIST_ENTRY CacheSegmentLRUListHead
;
55 static LIST_ENTRY ClosedListHead
;
56 ULONG DirtyPageCount
=0;
58 KGUARDED_MUTEX ViewLock
;
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
;
74 static void CcRosCacheSegmentIncRefCount_ ( PCACHE_SEGMENT cs
, const char* file
, int line
)
79 DbgPrint("(%s:%i) CacheSegment %p ++RefCount=%d, Dirty %d, PageOut %d\n",
80 file
, line
, cs
, cs
->ReferenceCount
, cs
->Dirty
, cs
->PageOut
);
83 static void CcRosCacheSegmentDecRefCount_ ( PCACHE_SEGMENT cs
, const char* file
, int line
)
88 DbgPrint("(%s:%i) CacheSegment %p --RefCount=%d, Dirty %d, PageOut %d\n",
89 file
, line
, cs
, cs
->ReferenceCount
, cs
->Dirty
, cs
->PageOut
);
92 #define CcRosCacheSegmentIncRefCount(cs) CcRosCacheSegmentIncRefCount_(cs,__FILE__,__LINE__)
93 #define CcRosCacheSegmentDecRefCount(cs) CcRosCacheSegmentDecRefCount_(cs,__FILE__,__LINE__)
95 #define CcRosCacheSegmentIncRefCount(cs) (++((cs)->ReferenceCount))
96 #define CcRosCacheSegmentDecRefCount(cs) (--((cs)->ReferenceCount))
100 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg
);
103 /* FUNCTIONS *****************************************************************/
113 PLIST_ENTRY current_entry
;
114 PCACHE_SEGMENT current
;
123 DPRINT1("Enabling Tracing for CacheMap 0x%p:\n", Bcb
);
125 KeAcquireGuardedMutex(&ViewLock
);
126 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldirql
);
128 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
129 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
131 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, BcbSegmentListEntry
);
132 current_entry
= current_entry
->Flink
;
134 DPRINT1(" CacheSegment 0x%p enabled, RefCount %d, Dirty %d, PageOut %d\n",
135 current
, current
->ReferenceCount
, current
->Dirty
, current
->PageOut
);
137 KeReleaseSpinLock(&Bcb
->BcbLock
, oldirql
);
138 KeReleaseGuardedMutex(&ViewLock
);
142 DPRINT1("Disabling Tracing for CacheMap 0x%p:\n", Bcb
);
153 CcRosFlushCacheSegment(PCACHE_SEGMENT CacheSegment
)
158 Status
= WriteCacheSegment(CacheSegment
);
159 if (NT_SUCCESS(Status
))
161 KeAcquireGuardedMutex(&ViewLock
);
162 KeAcquireSpinLock(&CacheSegment
->Bcb
->BcbLock
, &oldIrql
);
164 CacheSegment
->Dirty
= FALSE
;
165 RemoveEntryList(&CacheSegment
->DirtySegmentListEntry
);
166 DirtyPageCount
-= CacheSegment
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
167 CcRosCacheSegmentDecRefCount ( CacheSegment
);
169 KeReleaseSpinLock(&CacheSegment
->Bcb
->BcbLock
, oldIrql
);
170 KeReleaseGuardedMutex(&ViewLock
);
178 CcRosFlushDirtyPages(ULONG Target
, PULONG Count
)
180 PLIST_ENTRY current_entry
;
181 PCACHE_SEGMENT current
;
182 ULONG PagesPerSegment
;
185 static ULONG WriteCount
[4] = {0, 0, 0, 0};
188 DPRINT("CcRosFlushDirtyPages(Target %d)\n", Target
);
192 KeAcquireGuardedMutex(&ViewLock
);
194 WriteCount
[0] = WriteCount
[1];
195 WriteCount
[1] = WriteCount
[2];
196 WriteCount
[2] = WriteCount
[3];
199 NewTarget
= WriteCount
[0] + WriteCount
[1] + WriteCount
[2];
201 if (NewTarget
< DirtyPageCount
)
203 NewTarget
= (DirtyPageCount
- NewTarget
+ 3) / 4;
204 WriteCount
[0] += NewTarget
;
205 WriteCount
[1] += NewTarget
;
206 WriteCount
[2] += NewTarget
;
207 WriteCount
[3] += NewTarget
;
210 NewTarget
= WriteCount
[0];
212 Target
= max(NewTarget
, Target
);
214 current_entry
= DirtySegmentListHead
.Flink
;
215 if (current_entry
== &DirtySegmentListHead
)
217 DPRINT("No Dirty pages\n");
220 while (current_entry
!= &DirtySegmentListHead
&& Target
> 0)
222 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
223 DirtySegmentListEntry
);
224 current_entry
= current_entry
->Flink
;
226 Locked
= current
->Bcb
->Callbacks
->AcquireForLazyWrite(
227 current
->Bcb
->LazyWriteContext
, FALSE
);
233 Locked
= ExTryToAcquirePushLockExclusive(¤t
->Lock
);
236 current
->Bcb
->Callbacks
->ReleaseFromLazyWrite(
237 current
->Bcb
->LazyWriteContext
);
242 ASSERT(current
->Dirty
);
243 if (current
->ReferenceCount
> 1)
245 ExReleasePushLock(¤t
->Lock
);
246 current
->Bcb
->Callbacks
->ReleaseFromLazyWrite(
247 current
->Bcb
->LazyWriteContext
);
251 PagesPerSegment
= current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
253 KeReleaseGuardedMutex(&ViewLock
);
255 Status
= CcRosFlushCacheSegment(current
);
257 ExReleasePushLock(¤t
->Lock
);
258 current
->Bcb
->Callbacks
->ReleaseFromLazyWrite(
259 current
->Bcb
->LazyWriteContext
);
261 if (!NT_SUCCESS(Status
) && (Status
!= STATUS_END_OF_FILE
))
263 DPRINT1("CC: Failed to flush cache segment.\n");
267 (*Count
) += PagesPerSegment
;
268 Target
-= PagesPerSegment
;
271 KeAcquireGuardedMutex(&ViewLock
);
272 current_entry
= DirtySegmentListHead
.Flink
;
275 if (*Count
< NewTarget
)
277 WriteCount
[1] += (NewTarget
- *Count
);
280 KeReleaseGuardedMutex(&ViewLock
);
282 DPRINT("CcRosFlushDirtyPages() finished\n");
283 return(STATUS_SUCCESS
);
287 CcRosTrimCache(ULONG Target
, ULONG Priority
, PULONG NrFreed
)
289 * FUNCTION: Try to free some memory from the file cache.
291 * Target - The number of pages to be freed.
292 * Priority - The priority of free (currently unused).
293 * NrFreed - Points to a variable where the number of pages
294 * actually freed is returned.
297 PLIST_ENTRY current_entry
;
298 PCACHE_SEGMENT current
;
299 ULONG PagesPerSegment
;
304 DPRINT("CcRosTrimCache(Target %d)\n", Target
);
308 InitializeListHead(&FreeList
);
310 KeAcquireGuardedMutex(&ViewLock
);
311 current_entry
= CacheSegmentLRUListHead
.Flink
;
312 while (current_entry
!= &CacheSegmentLRUListHead
&& Target
> 0)
316 Status
= STATUS_SUCCESS
;
317 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
318 CacheSegmentLRUListEntry
);
319 current_entry
= current_entry
->Flink
;
321 KeAcquireSpinLock(¤t
->Bcb
->BcbLock
, &oldIrql
);
323 if (current
->MappedCount
> 0 && !current
->Dirty
&& !current
->PageOut
)
327 CcRosCacheSegmentIncRefCount(current
);
328 current
->PageOut
= TRUE
;
329 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
330 KeReleaseGuardedMutex(&ViewLock
);
331 for (i
= 0; i
< current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
; i
++)
334 Page
= (PFN_TYPE
)(MmGetPhysicalAddress((char*)current
->BaseAddress
+ i
* PAGE_SIZE
).QuadPart
>> PAGE_SHIFT
);
335 Status
= MmPageOutPhysicalAddress(Page
);
337 KeAcquireGuardedMutex(&ViewLock
);
338 KeAcquireSpinLock(¤t
->Bcb
->BcbLock
, &oldIrql
);
339 CcRosCacheSegmentDecRefCount(current
);
342 if (current
->ReferenceCount
== 0)
344 PagesPerSegment
= current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
345 // PagesFreed = PagesPerSegment;
346 PagesFreed
= min(PagesPerSegment
, Target
);
347 Target
-= PagesFreed
;
348 (*NrFreed
) += PagesFreed
;
351 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
354 current_entry
= CacheSegmentLRUListHead
.Flink
;
355 while (current_entry
!= &CacheSegmentLRUListHead
)
357 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
358 CacheSegmentLRUListEntry
);
359 current
->PageOut
= FALSE
;
360 current_entry
= current_entry
->Flink
;
362 KeAcquireSpinLock(¤t
->Bcb
->BcbLock
, &oldIrql
);
363 if (current
->ReferenceCount
== 0)
365 RemoveEntryList(¤t
->BcbSegmentListEntry
);
366 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
367 RemoveEntryList(¤t
->CacheSegmentListEntry
);
368 RemoveEntryList(¤t
->CacheSegmentLRUListEntry
);
369 InsertHeadList(&FreeList
, ¤t
->BcbSegmentListEntry
);
373 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
377 KeReleaseGuardedMutex(&ViewLock
);
379 while (!IsListEmpty(&FreeList
))
381 current_entry
= RemoveHeadList(&FreeList
);
382 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
383 BcbSegmentListEntry
);
384 CcRosInternalFreeCacheSegment(current
);
387 return(STATUS_SUCCESS
);
392 CcRosReleaseCacheSegment(PBCB Bcb
,
393 PCACHE_SEGMENT CacheSeg
,
398 BOOLEAN WasDirty
= CacheSeg
->Dirty
;
403 DPRINT("CcReleaseCacheSegment(Bcb 0x%p, CacheSeg 0x%p, Valid %d)\n",
404 Bcb
, CacheSeg
, Valid
);
406 CacheSeg
->Valid
= Valid
;
407 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| Dirty
;
409 KeAcquireGuardedMutex(&ViewLock
);
410 if (!WasDirty
&& CacheSeg
->Dirty
)
412 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
413 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
415 RemoveEntryList(&CacheSeg
->CacheSegmentLRUListEntry
);
416 InsertTailList(&CacheSegmentLRUListHead
, &CacheSeg
->CacheSegmentLRUListEntry
);
420 CacheSeg
->MappedCount
++;
422 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
423 CcRosCacheSegmentDecRefCount(CacheSeg
);
424 if (Mapped
&& CacheSeg
->MappedCount
== 1)
426 CcRosCacheSegmentIncRefCount(CacheSeg
);
428 if (!WasDirty
&& CacheSeg
->Dirty
)
430 CcRosCacheSegmentIncRefCount(CacheSeg
);
432 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
433 KeReleaseGuardedMutex(&ViewLock
);
434 ExReleasePushLock(&CacheSeg
->Lock
);
436 return(STATUS_SUCCESS
);
439 /* Returns with Cache Segment Lock Held! */
442 CcRosLookupCacheSegment(PBCB Bcb
, ULONG FileOffset
)
444 PLIST_ENTRY current_entry
;
445 PCACHE_SEGMENT current
;
450 DPRINT("CcRosLookupCacheSegment(Bcb -x%p, FileOffset %d)\n", Bcb
, FileOffset
);
452 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
453 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
454 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
456 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
457 BcbSegmentListEntry
);
458 if (current
->FileOffset
<= FileOffset
&&
459 (current
->FileOffset
+ Bcb
->CacheSegmentSize
) > FileOffset
)
461 CcRosCacheSegmentIncRefCount(current
);
462 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
463 ExAcquirePushLockExclusive(¤t
->Lock
);
466 current_entry
= current_entry
->Flink
;
468 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
474 CcRosMarkDirtyCacheSegment(PBCB Bcb
, ULONG FileOffset
)
476 PCACHE_SEGMENT CacheSeg
;
481 DPRINT("CcRosMarkDirtyCacheSegment(Bcb 0x%p, FileOffset %d)\n", Bcb
, FileOffset
);
483 CacheSeg
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
484 if (CacheSeg
== NULL
)
486 KeBugCheck(CACHE_MANAGER
);
488 if (!CacheSeg
->Dirty
)
490 KeAcquireGuardedMutex(&ViewLock
);
491 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
492 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
493 KeReleaseGuardedMutex(&ViewLock
);
497 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
498 CcRosCacheSegmentDecRefCount(CacheSeg
);
499 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
503 CacheSeg
->Dirty
= TRUE
;
504 ExReleasePushLock(&CacheSeg
->Lock
);
506 return(STATUS_SUCCESS
);
511 CcRosUnmapCacheSegment(PBCB Bcb
, ULONG FileOffset
, BOOLEAN NowDirty
)
513 PCACHE_SEGMENT CacheSeg
;
519 DPRINT("CcRosUnmapCacheSegment(Bcb 0x%p, FileOffset %d, NowDirty %d)\n",
520 Bcb
, FileOffset
, NowDirty
);
522 CacheSeg
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
523 if (CacheSeg
== NULL
)
525 return(STATUS_UNSUCCESSFUL
);
528 WasDirty
= CacheSeg
->Dirty
;
529 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| NowDirty
;
531 CacheSeg
->MappedCount
--;
533 if (!WasDirty
&& NowDirty
)
535 KeAcquireGuardedMutex(&ViewLock
);
536 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
537 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
538 KeReleaseGuardedMutex(&ViewLock
);
541 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
542 CcRosCacheSegmentDecRefCount(CacheSeg
);
543 if (!WasDirty
&& NowDirty
)
545 CcRosCacheSegmentIncRefCount(CacheSeg
);
547 if (CacheSeg
->MappedCount
== 0)
549 CcRosCacheSegmentDecRefCount(CacheSeg
);
551 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
553 ExReleasePushLock(&CacheSeg
->Lock
);
554 return(STATUS_SUCCESS
);
559 CcRosCreateCacheSegment(PBCB Bcb
,
561 PCACHE_SEGMENT
* CacheSeg
)
563 PCACHE_SEGMENT current
;
564 PCACHE_SEGMENT previous
;
565 PLIST_ENTRY current_entry
;
569 ULONG StartingOffset
;
571 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
575 DPRINT("CcRosCreateCacheSegment()\n");
577 BoundaryAddressMultiple
.QuadPart
= 0;
578 if (FileOffset
>= Bcb
->FileSize
.u
.LowPart
)
581 return STATUS_INVALID_PARAMETER
;
584 current
= ExAllocateFromNPagedLookasideList(&CacheSegLookasideList
);
585 current
->Valid
= FALSE
;
586 current
->Dirty
= FALSE
;
587 current
->PageOut
= FALSE
;
588 current
->FileOffset
= ROUND_DOWN(FileOffset
, Bcb
->CacheSegmentSize
);
593 DPRINT1("CacheMap 0x%p: new Cache Segment: 0x%p\n", Bcb
, current
);
596 current
->MappedCount
= 0;
597 current
->DirtySegmentListEntry
.Flink
= NULL
;
598 current
->DirtySegmentListEntry
.Blink
= NULL
;
599 current
->ReferenceCount
= 1;
600 ExInitializePushLock((PULONG_PTR
)¤t
->Lock
);
601 ExAcquirePushLockExclusive(¤t
->Lock
);
602 KeAcquireGuardedMutex(&ViewLock
);
605 /* There is window between the call to CcRosLookupCacheSegment
606 * and CcRosCreateCacheSegment. We must check if a segment on
607 * the fileoffset exist. If there exist a segment, we release
608 * our new created segment and return the existing one.
610 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
611 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
613 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
615 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
616 BcbSegmentListEntry
);
617 if (current
->FileOffset
<= FileOffset
&&
618 (current
->FileOffset
+ Bcb
->CacheSegmentSize
) > FileOffset
)
620 CcRosCacheSegmentIncRefCount(current
);
621 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
625 DPRINT1("CacheMap 0x%p: deleting newly created Cache Segment 0x%p ( found existing one 0x%p )\n",
631 ExReleasePushLock(&(*CacheSeg
)->Lock
);
632 KeReleaseGuardedMutex(&ViewLock
);
633 ExFreeToNPagedLookasideList(&CacheSegLookasideList
, *CacheSeg
);
635 ExAcquirePushLockExclusive(¤t
->Lock
);
636 return STATUS_SUCCESS
;
638 if (current
->FileOffset
< FileOffset
)
640 if (previous
== NULL
)
646 if (previous
->FileOffset
< current
->FileOffset
)
652 current_entry
= current_entry
->Flink
;
654 /* There was no existing segment. */
658 InsertHeadList(&previous
->BcbSegmentListEntry
, ¤t
->BcbSegmentListEntry
);
662 InsertHeadList(&Bcb
->BcbSegmentListHead
, ¤t
->BcbSegmentListEntry
);
664 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
665 InsertTailList(&CacheSegmentListHead
, ¤t
->CacheSegmentListEntry
);
666 InsertTailList(&CacheSegmentLRUListHead
, ¤t
->CacheSegmentLRUListEntry
);
667 KeReleaseGuardedMutex(&ViewLock
);
669 KeAcquireSpinLock(&CiCacheSegMappingRegionLock
, &oldIrql
);
671 StartingOffset
= RtlFindClearBitsAndSet(&CiCacheSegMappingRegionAllocMap
, Bcb
->CacheSegmentSize
/ PAGE_SIZE
, CiCacheSegMappingRegionHint
);
673 if (StartingOffset
== 0xffffffff)
675 DPRINT1("Out of CacheSeg mapping space\n");
676 KeBugCheck(CACHE_MANAGER
);
679 current
->BaseAddress
= CiCacheSegMappingRegionBase
+ StartingOffset
* PAGE_SIZE
;
681 if (CiCacheSegMappingRegionHint
== StartingOffset
)
683 CiCacheSegMappingRegionHint
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
686 KeReleaseSpinLock(&CiCacheSegMappingRegionLock
, oldIrql
);
688 MmLockAddressSpace(MmGetKernelAddressSpace());
689 current
->BaseAddress
= NULL
;
690 Status
= MmCreateMemoryArea(MmGetKernelAddressSpace(),
691 MEMORY_AREA_CACHE_SEGMENT
,
692 ¤t
->BaseAddress
,
693 Bcb
->CacheSegmentSize
,
695 (PMEMORY_AREA
*)¤t
->MemoryArea
,
698 BoundaryAddressMultiple
);
699 MmUnlockAddressSpace(MmGetKernelAddressSpace());
700 if (!NT_SUCCESS(Status
))
702 KeBugCheck(CACHE_MANAGER
);
706 /* Create a virtual mapping for this memory area */
707 MmMapMemoryArea(current
->BaseAddress
, Bcb
->CacheSegmentSize
,
708 MC_CACHE
, PAGE_READWRITE
);
710 return(STATUS_SUCCESS
);
715 CcRosGetCacheSegmentChain(PBCB Bcb
,
718 PCACHE_SEGMENT
* CacheSeg
)
720 PCACHE_SEGMENT current
;
722 PCACHE_SEGMENT
* CacheSegList
;
723 PCACHE_SEGMENT Previous
= NULL
;
727 DPRINT("CcRosGetCacheSegmentChain()\n");
729 Length
= ROUND_UP(Length
, Bcb
->CacheSegmentSize
);
731 CacheSegList
= _alloca(sizeof(PCACHE_SEGMENT
) *
732 (Length
/ Bcb
->CacheSegmentSize
));
735 * Look for a cache segment already mapping the same data.
737 for (i
= 0; i
< (Length
/ Bcb
->CacheSegmentSize
); i
++)
739 ULONG CurrentOffset
= FileOffset
+ (i
* Bcb
->CacheSegmentSize
);
740 current
= CcRosLookupCacheSegment(Bcb
, CurrentOffset
);
743 CacheSegList
[i
] = current
;
747 CcRosCreateCacheSegment(Bcb
, CurrentOffset
, ¤t
);
748 CacheSegList
[i
] = current
;
752 for (i
= 0; i
< (Length
/ Bcb
->CacheSegmentSize
); i
++)
756 *CacheSeg
= CacheSegList
[i
];
757 Previous
= CacheSegList
[i
];
761 Previous
->NextInChain
= CacheSegList
[i
];
762 Previous
= CacheSegList
[i
];
766 Previous
->NextInChain
= NULL
;
768 return(STATUS_SUCCESS
);
773 CcRosGetCacheSegment(PBCB Bcb
,
778 PCACHE_SEGMENT
* CacheSeg
)
780 PCACHE_SEGMENT current
;
785 DPRINT("CcRosGetCacheSegment()\n");
788 * Look for a cache segment already mapping the same data.
790 current
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
794 * Otherwise create a new segment.
796 Status
= CcRosCreateCacheSegment(Bcb
, FileOffset
, ¤t
);
797 if (!NT_SUCCESS(Status
))
803 * Return information about the segment to the caller.
805 *UptoDate
= current
->Valid
;
806 *BaseAddress
= current
->BaseAddress
;
807 DPRINT("*BaseAddress 0x%.8X\n", *BaseAddress
);
809 *BaseOffset
= current
->FileOffset
;
810 return(STATUS_SUCCESS
);
814 CcRosRequestCacheSegment(PBCB Bcb
,
818 PCACHE_SEGMENT
* CacheSeg
)
820 * FUNCTION: Request a page mapping for a BCB
827 if ((FileOffset
% Bcb
->CacheSegmentSize
) != 0)
829 DPRINT1("Bad fileoffset %x should be multiple of %x",
830 FileOffset
, Bcb
->CacheSegmentSize
);
831 KeBugCheck(CACHE_MANAGER
);
834 return(CcRosGetCacheSegment(Bcb
,
844 CcFreeCachePage(PVOID Context
, MEMORY_AREA
* MemoryArea
, PVOID Address
,
845 PFN_TYPE Page
, SWAPENTRY SwapEntry
, BOOLEAN Dirty
)
847 ASSERT(SwapEntry
== 0);
850 MmReleasePageMemoryConsumer(MC_CACHE
, Page
);
855 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg
)
857 * FUNCTION: Releases a cache segment associated with a BCB
867 DPRINT("Freeing cache segment 0x%p\n", CacheSeg
);
869 if ( CacheSeg
->Bcb
->Trace
)
871 DPRINT1("CacheMap 0x%p: deleting Cache Segment: 0x%p\n", CacheSeg
->Bcb
, CacheSeg
);
875 RegionSize
= CacheSeg
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
877 /* Unmap all the pages. */
878 for (i
= 0; i
< RegionSize
; i
++)
880 MmDeleteVirtualMapping(NULL
,
881 CacheSeg
->BaseAddress
+ (i
* PAGE_SIZE
),
885 MmReleasePageMemoryConsumer(MC_CACHE
, Page
);
888 KeAcquireSpinLock(&CiCacheSegMappingRegionLock
, &oldIrql
);
889 /* Deallocate all the pages used. */
890 Base
= (ULONG
)(CacheSeg
->BaseAddress
- CiCacheSegMappingRegionBase
) / PAGE_SIZE
;
892 RtlClearBits(&CiCacheSegMappingRegionAllocMap
, Base
, RegionSize
);
894 CiCacheSegMappingRegionHint
= min (CiCacheSegMappingRegionHint
, Base
);
896 KeReleaseSpinLock(&CiCacheSegMappingRegionLock
, oldIrql
);
898 MmLockAddressSpace(MmGetKernelAddressSpace());
899 MmFreeMemoryArea(MmGetKernelAddressSpace(),
900 CacheSeg
->MemoryArea
,
903 MmUnlockAddressSpace(MmGetKernelAddressSpace());
905 ExFreeToNPagedLookasideList(&CacheSegLookasideList
, CacheSeg
);
906 return(STATUS_SUCCESS
);
911 CcRosFreeCacheSegment(PBCB Bcb
, PCACHE_SEGMENT CacheSeg
)
918 DPRINT("CcRosFreeCacheSegment(Bcb 0x%p, CacheSeg 0x%p)\n",
921 KeAcquireGuardedMutex(&ViewLock
);
922 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
923 RemoveEntryList(&CacheSeg
->BcbSegmentListEntry
);
924 RemoveEntryList(&CacheSeg
->CacheSegmentListEntry
);
925 RemoveEntryList(&CacheSeg
->CacheSegmentLRUListEntry
);
928 RemoveEntryList(&CacheSeg
->DirtySegmentListEntry
);
929 DirtyPageCount
-= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
932 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
933 KeReleaseGuardedMutex(&ViewLock
);
935 Status
= CcRosInternalFreeCacheSegment(CacheSeg
);
943 CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointers
,
944 IN PLARGE_INTEGER FileOffset OPTIONAL
,
946 OUT PIO_STATUS_BLOCK IoStatus
)
949 LARGE_INTEGER Offset
;
950 PCACHE_SEGMENT current
;
954 DPRINT("CcFlushCache(SectionObjectPointers 0x%p, FileOffset 0x%p, Length %d, IoStatus 0x%p)\n",
955 SectionObjectPointers
, FileOffset
, Length
, IoStatus
);
957 if (SectionObjectPointers
&& SectionObjectPointers
->SharedCacheMap
)
959 Bcb
= (PBCB
)SectionObjectPointers
->SharedCacheMap
;
963 Offset
= *FileOffset
;
967 Offset
.QuadPart
= (LONGLONG
)0;
968 Length
= Bcb
->FileSize
.u
.LowPart
;
973 IoStatus
->Status
= STATUS_SUCCESS
;
974 IoStatus
->Information
= 0;
979 current
= CcRosLookupCacheSegment (Bcb
, Offset
.u
.LowPart
);
984 Status
= CcRosFlushCacheSegment(current
);
985 if (!NT_SUCCESS(Status
) && IoStatus
!= NULL
)
987 IoStatus
->Status
= Status
;
990 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
991 ExReleasePushLock(¤t
->Lock
);
992 CcRosCacheSegmentDecRefCount(current
);
993 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
996 Offset
.QuadPart
+= Bcb
->CacheSegmentSize
;
997 if (Length
> Bcb
->CacheSegmentSize
)
999 Length
-= Bcb
->CacheSegmentSize
;
1011 IoStatus
->Status
= STATUS_INVALID_PARAMETER
;
1018 CcRosDeleteFileCache(PFILE_OBJECT FileObject
, PBCB Bcb
)
1020 * FUNCTION: Releases the BCB associated with a file object
1023 PLIST_ENTRY current_entry
;
1024 PCACHE_SEGMENT current
;
1026 LIST_ENTRY FreeList
;
1032 KeReleaseGuardedMutex(&ViewLock
);
1034 CcFlushCache(FileObject
->SectionObjectPointer
, NULL
, 0, NULL
);
1036 KeAcquireGuardedMutex(&ViewLock
);
1038 if (Bcb
->RefCount
== 0)
1040 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
1042 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1043 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1046 FileObject
->SectionObjectPointer
->SharedCacheMap
= NULL
;
1049 * Release all cache segments.
1051 InitializeListHead(&FreeList
);
1052 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
1053 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
1054 while (!IsListEmpty(&Bcb
->BcbSegmentListHead
))
1056 current_entry
= RemoveTailList(&Bcb
->BcbSegmentListHead
);
1057 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, BcbSegmentListEntry
);
1058 RemoveEntryList(¤t
->CacheSegmentListEntry
);
1059 RemoveEntryList(¤t
->CacheSegmentLRUListEntry
);
1062 RemoveEntryList(¤t
->DirtySegmentListEntry
);
1063 DirtyPageCount
-= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
1064 DPRINT1("Freeing dirty segment\n");
1066 InsertHeadList(&FreeList
, ¤t
->BcbSegmentListEntry
);
1071 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
1073 KeReleaseGuardedMutex(&ViewLock
);
1074 ObDereferenceObject (Bcb
->FileObject
);
1076 while (!IsListEmpty(&FreeList
))
1078 current_entry
= RemoveTailList(&FreeList
);
1079 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, BcbSegmentListEntry
);
1080 Status
= CcRosInternalFreeCacheSegment(current
);
1082 ExFreeToNPagedLookasideList(&BcbLookasideList
, Bcb
);
1083 KeAcquireGuardedMutex(&ViewLock
);
1085 return(STATUS_SUCCESS
);
1090 CcRosReferenceCache(PFILE_OBJECT FileObject
)
1093 KeAcquireGuardedMutex(&ViewLock
);
1094 Bcb
= (PBCB
)FileObject
->SectionObjectPointer
->SharedCacheMap
;
1096 if (Bcb
->RefCount
== 0)
1098 ASSERT(Bcb
->BcbRemoveListEntry
.Flink
!= NULL
);
1099 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1100 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1105 ASSERT(Bcb
->BcbRemoveListEntry
.Flink
== NULL
);
1108 KeReleaseGuardedMutex(&ViewLock
);
1113 CcRosSetRemoveOnClose(PSECTION_OBJECT_POINTERS SectionObjectPointer
)
1116 DPRINT("CcRosSetRemoveOnClose()\n");
1117 KeAcquireGuardedMutex(&ViewLock
);
1118 Bcb
= (PBCB
)SectionObjectPointer
->SharedCacheMap
;
1121 Bcb
->RemoveOnClose
= TRUE
;
1122 if (Bcb
->RefCount
== 0)
1124 CcRosDeleteFileCache(Bcb
->FileObject
, Bcb
);
1127 KeReleaseGuardedMutex(&ViewLock
);
1133 CcRosDereferenceCache(PFILE_OBJECT FileObject
)
1136 KeAcquireGuardedMutex(&ViewLock
);
1137 Bcb
= (PBCB
)FileObject
->SectionObjectPointer
->SharedCacheMap
;
1139 if (Bcb
->RefCount
> 0)
1142 if (Bcb
->RefCount
== 0)
1144 MmFreeSectionSegments(Bcb
->FileObject
);
1145 CcRosDeleteFileCache(FileObject
, Bcb
);
1148 KeReleaseGuardedMutex(&ViewLock
);
1152 CcRosReleaseFileCache(PFILE_OBJECT FileObject
)
1154 * FUNCTION: Called by the file system when a handle to a file object
1160 KeAcquireGuardedMutex(&ViewLock
);
1162 if (FileObject
->SectionObjectPointer
->SharedCacheMap
!= NULL
)
1164 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1165 if (FileObject
->PrivateCacheMap
!= NULL
)
1167 FileObject
->PrivateCacheMap
= NULL
;
1168 if (Bcb
->RefCount
> 0)
1171 if (Bcb
->RefCount
== 0)
1173 MmFreeSectionSegments(Bcb
->FileObject
);
1174 CcRosDeleteFileCache(FileObject
, Bcb
);
1179 KeReleaseGuardedMutex(&ViewLock
);
1180 return(STATUS_SUCCESS
);
1185 CcTryToInitializeFileCache(PFILE_OBJECT FileObject
)
1190 KeAcquireGuardedMutex(&ViewLock
);
1192 ASSERT(FileObject
->SectionObjectPointer
);
1193 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1196 Status
= STATUS_UNSUCCESSFUL
;
1200 if (FileObject
->PrivateCacheMap
== NULL
)
1202 FileObject
->PrivateCacheMap
= Bcb
;
1205 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
1207 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1208 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1210 Status
= STATUS_SUCCESS
;
1212 KeReleaseGuardedMutex(&ViewLock
);
1219 CcRosInitializeFileCache(PFILE_OBJECT FileObject
,
1220 ULONG CacheSegmentSize
,
1221 PCACHE_MANAGER_CALLBACKS CallBacks
,
1222 PVOID LazyWriterContext
)
1224 * FUNCTION: Initializes a BCB for a file object
1229 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1230 DPRINT("CcRosInitializeFileCache(FileObject 0x%p, Bcb 0x%p, CacheSegmentSize %d)\n",
1231 FileObject
, Bcb
, CacheSegmentSize
);
1233 KeAcquireGuardedMutex(&ViewLock
);
1236 Bcb
= ExAllocateFromNPagedLookasideList(&BcbLookasideList
);
1239 KeReleaseGuardedMutex(&ViewLock
);
1240 return(STATUS_UNSUCCESSFUL
);
1242 memset(Bcb
, 0, sizeof(BCB
));
1243 ObReferenceObjectByPointer(FileObject
,
1247 Bcb
->FileObject
= FileObject
;
1248 Bcb
->CacheSegmentSize
= CacheSegmentSize
;
1249 Bcb
->Callbacks
= CallBacks
;
1250 Bcb
->LazyWriteContext
= LazyWriterContext
;
1251 if (FileObject
->FsContext
)
1253 Bcb
->AllocationSize
=
1254 ((PFSRTL_COMMON_FCB_HEADER
)FileObject
->FsContext
)->AllocationSize
;
1256 ((PFSRTL_COMMON_FCB_HEADER
)FileObject
->FsContext
)->FileSize
;
1258 KeInitializeSpinLock(&Bcb
->BcbLock
);
1259 InitializeListHead(&Bcb
->BcbSegmentListHead
);
1260 FileObject
->SectionObjectPointer
->SharedCacheMap
= Bcb
;
1262 if (FileObject
->PrivateCacheMap
== NULL
)
1264 FileObject
->PrivateCacheMap
= Bcb
;
1267 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
1269 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1270 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1272 KeReleaseGuardedMutex(&ViewLock
);
1274 return(STATUS_SUCCESS
);
1281 CcGetFileObjectFromSectionPtrs(IN PSECTION_OBJECT_POINTERS SectionObjectPointers
)
1284 if (SectionObjectPointers
&& SectionObjectPointers
->SharedCacheMap
)
1286 Bcb
= (PBCB
)SectionObjectPointers
->SharedCacheMap
;
1288 return Bcb
->FileObject
;
1301 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
1304 DPRINT("CcInitView()\n");
1306 BoundaryAddressMultiple
.QuadPart
= 0;
1307 CiCacheSegMappingRegionHint
= 0;
1308 CiCacheSegMappingRegionBase
= NULL
;
1310 MmLockAddressSpace(MmGetKernelAddressSpace());
1312 Status
= MmCreateMemoryArea(MmGetKernelAddressSpace(),
1313 MEMORY_AREA_CACHE_SEGMENT
,
1314 &CiCacheSegMappingRegionBase
,
1315 CI_CACHESEG_MAPPING_REGION_SIZE
,
1320 BoundaryAddressMultiple
);
1321 MmUnlockAddressSpace(MmGetKernelAddressSpace());
1322 if (!NT_SUCCESS(Status
))
1324 KeBugCheck(CACHE_MANAGER
);
1327 Buffer
= ExAllocatePool(NonPagedPool
, CI_CACHESEG_MAPPING_REGION_SIZE
/ (PAGE_SIZE
* 8));
1330 KeBugCheck(CACHE_MANAGER
);
1333 RtlInitializeBitMap(&CiCacheSegMappingRegionAllocMap
, Buffer
, CI_CACHESEG_MAPPING_REGION_SIZE
/ PAGE_SIZE
);
1334 RtlClearAllBits(&CiCacheSegMappingRegionAllocMap
);
1336 KeInitializeSpinLock(&CiCacheSegMappingRegionLock
);
1338 InitializeListHead(&CacheSegmentListHead
);
1339 InitializeListHead(&DirtySegmentListHead
);
1340 InitializeListHead(&CacheSegmentLRUListHead
);
1341 InitializeListHead(&ClosedListHead
);
1342 KeInitializeGuardedMutex(&ViewLock
);
1343 ExInitializeNPagedLookasideList (&iBcbLookasideList
,
1347 sizeof(INTERNAL_BCB
),
1350 ExInitializeNPagedLookasideList (&BcbLookasideList
,
1357 ExInitializeNPagedLookasideList (&CacheSegLookasideList
,
1361 sizeof(CACHE_SEGMENT
),
1365 MmInitializeMemoryConsumer(MC_CACHE
, CcRosTrimCache
);
1367 CcInitCacheZeroPage();