3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 * PROJECT: ReactOS kernel
22 * FILE: ntoskrnl/cc/view.c
23 * PURPOSE: Cache manager
24 * PROGRAMMER: David Welch (welch@mcmail.com)
25 * PORTABILITY: Checked
30 /* NOTES **********************************************************************
32 * This is not the NT implementation of a file cache nor anything much like
35 * The general procedure for a filesystem to implement a read or write
36 * dispatch routine is as follows
38 * (1) If caching for the FCB hasn't been initiated then so do by calling
39 * CcInitializeFileCache.
41 * (2) For each 4k region which is being read or written obtain a cache page
42 * by calling CcRequestCachePage.
44 * (3) If either the page is being read or not completely written, and it is
45 * not up to date then read its data from the underlying medium. If the read
46 * fails then call CcReleaseCachePage with VALID as FALSE and return a error.
48 * (4) Copy the data into or out of the page as necessary.
50 * (5) Release the cache page
52 /* INCLUDES ******************************************************************/
56 #include <internal/debug.h>
58 /* GLOBALS *******************************************************************/
61 * If CACHE_BITMAP is defined, the cache manager uses one large memory region
62 * within the kernel address space and allocate/deallocate space from this block
63 * over a bitmap. If CACHE_BITMAP is used, the size of the mdl mapping region
64 * must be reduced (ntoskrnl\mm\mdl.c, MI_MDLMAPPING_REGION_SIZE).
66 //#define CACHE_BITMAP
68 #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
69 #define ROUND_DOWN(N, S) (((N) % (S)) ? ROUND_UP(N, S) - S : N)
71 #define TAG_CSEG TAG('C', 'S', 'E', 'G')
72 #define TAG_BCB TAG('B', 'C', 'B', ' ')
73 #define TAG_IBCB TAG('i', 'B', 'C', 'B')
75 static LIST_ENTRY DirtySegmentListHead
;
76 static LIST_ENTRY CacheSegmentListHead
;
77 static LIST_ENTRY CacheSegmentLRUListHead
;
78 static LIST_ENTRY ClosedListHead
;
79 ULONG DirtyPageCount
=0;
84 #define CI_CACHESEG_MAPPING_REGION_SIZE (128*1024*1024)
86 static PVOID CiCacheSegMappingRegionBase
= NULL
;
87 static RTL_BITMAP CiCacheSegMappingRegionAllocMap
;
88 static ULONG CiCacheSegMappingRegionHint
;
89 static KSPIN_LOCK CiCacheSegMappingRegionLock
;
92 NPAGED_LOOKASIDE_LIST iBcbLookasideList
;
93 static NPAGED_LOOKASIDE_LIST BcbLookasideList
;
94 static NPAGED_LOOKASIDE_LIST CacheSegLookasideList
;
96 static ULONG CcTimeStamp
;
97 static KEVENT LazyCloseThreadEvent
;
98 static HANDLE LazyCloseThreadHandle
;
99 static CLIENT_ID LazyCloseThreadId
;
100 static volatile BOOLEAN LazyCloseThreadShouldTerminate
;
102 #if defined(__GNUC__)
103 /* void * alloca(size_t size); */
104 #elif defined(_MSC_VER)
105 void* _alloca(size_t size
);
107 #error Unknown compiler for alloca intrinsic stack allocation "function"
112 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg
);
114 /* FUNCTIONS *****************************************************************/
117 CcRosFlushCacheSegment(PCACHE_SEGMENT CacheSegment
)
121 Status
= WriteCacheSegment(CacheSegment
);
122 if (NT_SUCCESS(Status
))
124 ExAcquireFastMutex(&ViewLock
);
125 KeAcquireSpinLock(&CacheSegment
->Bcb
->BcbLock
, &oldIrql
);
126 CacheSegment
->Dirty
= FALSE
;
127 RemoveEntryList(&CacheSegment
->DirtySegmentListEntry
);
128 DirtyPageCount
-= CacheSegment
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
129 CacheSegment
->ReferenceCount
--;
130 KeReleaseSpinLock(&CacheSegment
->Bcb
->BcbLock
, oldIrql
);
131 ExReleaseFastMutex(&ViewLock
);
137 CcRosFlushDirtyPages(ULONG Target
, PULONG Count
)
139 PLIST_ENTRY current_entry
;
140 PCACHE_SEGMENT current
;
141 ULONG PagesPerSegment
;
144 static ULONG WriteCount
[4] = {0, 0, 0, 0};
147 DPRINT("CcRosFlushDirtyPages(Target %d)\n", Target
);
151 ExAcquireFastMutex(&ViewLock
);
153 WriteCount
[0] = WriteCount
[1];
154 WriteCount
[1] = WriteCount
[2];
155 WriteCount
[2] = WriteCount
[3];
158 NewTarget
= WriteCount
[0] + WriteCount
[1] + WriteCount
[2];
160 if (NewTarget
< DirtyPageCount
)
162 NewTarget
= (DirtyPageCount
- NewTarget
+ 3) / 4;
163 WriteCount
[0] += NewTarget
;
164 WriteCount
[1] += NewTarget
;
165 WriteCount
[2] += NewTarget
;
166 WriteCount
[3] += NewTarget
;
169 NewTarget
= WriteCount
[0];
171 Target
= max(NewTarget
, Target
);
173 current_entry
= DirtySegmentListHead
.Flink
;
174 if (current_entry
== &DirtySegmentListHead
)
176 DPRINT("No Dirty pages\n");
178 while (current_entry
!= &DirtySegmentListHead
&& Target
> 0)
180 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
181 DirtySegmentListEntry
);
182 current_entry
= current_entry
->Flink
;
183 Locked
= ExTryToAcquireFastMutex(¤t
->Lock
);
188 ASSERT(current
->Dirty
);
189 if (current
->ReferenceCount
> 1)
191 ExReleaseFastMutex(¤t
->Lock
);
194 ExReleaseFastMutex(&ViewLock
);
195 PagesPerSegment
= current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
196 Status
= CcRosFlushCacheSegment(current
);
197 ExReleaseFastMutex(¤t
->Lock
);
198 if (!NT_SUCCESS(Status
) && (Status
!= STATUS_END_OF_FILE
))
200 DPRINT1("CC: Failed to flush cache segment.\n");
204 (*Count
) += PagesPerSegment
;
205 Target
-= PagesPerSegment
;
207 ExAcquireFastMutex(&ViewLock
);
208 current_entry
= DirtySegmentListHead
.Flink
;
210 if (*Count
< NewTarget
)
212 WriteCount
[1] += (NewTarget
- *Count
);
214 ExReleaseFastMutex(&ViewLock
);
215 DPRINT("CcRosFlushDirtyPages() finished\n");
217 return(STATUS_SUCCESS
);
221 CcRosTrimCache(ULONG Target
, ULONG Priority
, PULONG NrFreed
)
223 * FUNCTION: Try to free some memory from the file cache.
225 * Target - The number of pages to be freed.
226 * Priority - The priority of free (currently unused).
227 * NrFreed - Points to a variable where the number of pages
228 * actually freed is returned.
231 PLIST_ENTRY current_entry
;
232 PCACHE_SEGMENT current
, last
= NULL
;
233 ULONG PagesPerSegment
;
238 DPRINT("CcRosTrimCache(Target %d)\n", Target
);
242 InitializeListHead(&FreeList
);
244 ExAcquireFastMutex(&ViewLock
);
245 current_entry
= CacheSegmentLRUListHead
.Flink
;
246 while (current_entry
!= &CacheSegmentLRUListHead
&& Target
> 0)
248 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
249 CacheSegmentLRUListEntry
);
250 current_entry
= current_entry
->Flink
;
252 KeAcquireSpinLock(¤t
->Bcb
->BcbLock
, &oldIrql
);
253 if (current
->ReferenceCount
== 0)
255 RemoveEntryList(¤t
->BcbSegmentListEntry
);
256 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
257 RemoveEntryList(¤t
->CacheSegmentListEntry
);
258 RemoveEntryList(¤t
->CacheSegmentLRUListEntry
);
259 InsertHeadList(&FreeList
, ¤t
->BcbSegmentListEntry
);
260 PagesPerSegment
= current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
261 PagesFreed
= min(PagesPerSegment
, Target
);
262 Target
-= PagesFreed
;
263 (*NrFreed
) += PagesFreed
;
267 if (last
!= current
&& current
->MappedCount
> 0 && !current
->Dirty
&& !current
->PageOut
)
272 current
->ReferenceCount
++;
274 current
->PageOut
= TRUE
;
275 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
276 ExReleaseFastMutex(&ViewLock
);
277 for (i
= 0; i
< current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
; i
++)
280 Page
= MmGetPhysicalAddress((char*)current
->BaseAddress
+ i
* PAGE_SIZE
).QuadPart
>> PAGE_SHIFT
;
281 Status
= MmPageOutPhysicalAddress(Page
);
282 if (!NT_SUCCESS(Status
))
287 ExAcquireFastMutex(&ViewLock
);
288 KeAcquireSpinLock(¤t
->Bcb
->BcbLock
, &oldIrql
);
289 current
->ReferenceCount
--;
290 current
->PageOut
= FALSE
;
291 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
292 current_entry
= ¤t
->CacheSegmentLRUListEntry
;
295 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
298 ExReleaseFastMutex(&ViewLock
);
300 while (!IsListEmpty(&FreeList
))
302 current_entry
= RemoveHeadList(&FreeList
);
303 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
304 BcbSegmentListEntry
);
305 CcRosInternalFreeCacheSegment(current
);
308 DPRINT("CcRosTrimCache() finished\n");
309 return(STATUS_SUCCESS
);
313 CcRosReleaseCacheSegment(PBCB Bcb
,
314 PCACHE_SEGMENT CacheSeg
,
319 BOOLEAN WasDirty
= CacheSeg
->Dirty
;
324 DPRINT("CcReleaseCacheSegment(Bcb %x, CacheSeg %x, Valid %d)\n",
325 Bcb
, CacheSeg
, Valid
);
327 CacheSeg
->Valid
= Valid
;
328 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| Dirty
;
330 ExAcquireFastMutex(&ViewLock
);
331 if (!WasDirty
&& CacheSeg
->Dirty
)
333 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
334 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
336 RemoveEntryList(&CacheSeg
->CacheSegmentLRUListEntry
);
337 InsertTailList(&CacheSegmentLRUListHead
, &CacheSeg
->CacheSegmentLRUListEntry
);
341 CacheSeg
->MappedCount
++;
343 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
344 CacheSeg
->ReferenceCount
--;
345 if (Mapped
&& CacheSeg
->MappedCount
== 1)
347 CacheSeg
->ReferenceCount
++;
349 if (!WasDirty
&& CacheSeg
->Dirty
)
351 CacheSeg
->ReferenceCount
++;
353 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
354 ExReleaseFastMutex(&ViewLock
);
355 ExReleaseFastMutex(&CacheSeg
->Lock
);
357 return(STATUS_SUCCESS
);
361 CcRosLookupCacheSegment(PBCB Bcb
, ULONG FileOffset
)
363 PLIST_ENTRY current_entry
;
364 PCACHE_SEGMENT current
;
369 DPRINT("CcRosLookupCacheSegment(Bcb %x, FileOffset %d)\n", Bcb
, FileOffset
);
371 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
372 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
373 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
375 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
376 BcbSegmentListEntry
);
377 if (current
->FileOffset
<= FileOffset
&&
378 (current
->FileOffset
+ Bcb
->CacheSegmentSize
) > FileOffset
)
380 current
->ReferenceCount
++;
381 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
382 ExAcquireFastMutex(¤t
->Lock
);
385 current_entry
= current_entry
->Flink
;
387 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
392 CcRosMarkDirtyCacheSegment(PBCB Bcb
, ULONG FileOffset
)
394 PCACHE_SEGMENT CacheSeg
;
399 DPRINT("CcRosMarkDirtyCacheSegment(Bcb %x, FileOffset %d)\n", Bcb
, FileOffset
);
401 CacheSeg
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
402 if (CacheSeg
== NULL
)
406 if (!CacheSeg
->Dirty
)
408 ExAcquireFastMutex(&ViewLock
);
409 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
410 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
411 ExReleaseFastMutex(&ViewLock
);
415 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
416 CacheSeg
->ReferenceCount
--;
417 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
421 CacheSeg
->Dirty
= TRUE
;
422 ExReleaseFastMutex(&CacheSeg
->Lock
);
424 return(STATUS_SUCCESS
);
428 CcRosUnmapCacheSegment(PBCB Bcb
, ULONG FileOffset
, BOOLEAN NowDirty
)
430 PCACHE_SEGMENT CacheSeg
;
436 DPRINT("CcRosUnmapCacheSegment(Bcb %x, FileOffset %d, NowDirty %d)\n",
437 Bcb
, FileOffset
, NowDirty
);
439 CacheSeg
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
440 if (CacheSeg
== NULL
)
442 return(STATUS_UNSUCCESSFUL
);
445 WasDirty
= CacheSeg
->Dirty
;
446 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| NowDirty
;
448 CacheSeg
->MappedCount
--;
450 if (!WasDirty
&& NowDirty
)
452 ExAcquireFastMutex(&ViewLock
);
453 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
454 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
455 ExReleaseFastMutex(&ViewLock
);
458 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
459 CacheSeg
->ReferenceCount
--;
460 if (!WasDirty
&& NowDirty
)
462 CacheSeg
->ReferenceCount
++;
464 if (CacheSeg
->MappedCount
== 0)
466 CacheSeg
->ReferenceCount
--;
468 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
470 ExReleaseFastMutex(&CacheSeg
->Lock
);
471 return(STATUS_SUCCESS
);
475 CcRosCreateCacheSegment(PBCB Bcb
,
477 PCACHE_SEGMENT
* CacheSeg
)
480 PCACHE_SEGMENT current
;
481 PCACHE_SEGMENT previous
;
482 PLIST_ENTRY current_entry
;
487 ULONG StartingOffset
;
490 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
494 DPRINT("CcRosCreateCacheSegment()\n");
496 BoundaryAddressMultiple
.QuadPart
= 0;
497 if (FileOffset
>= Bcb
->FileSize
.u
.LowPart
)
500 return STATUS_INVALID_PARAMETER
;
503 current
= ExAllocateFromNPagedLookasideList(&CacheSegLookasideList
);
504 current
->Valid
= FALSE
;
505 current
->Dirty
= FALSE
;
506 current
->PageOut
= FALSE
;
507 current
->FileOffset
= ROUND_DOWN(FileOffset
, Bcb
->CacheSegmentSize
);
509 current
->MappedCount
= 0;
510 current
->DirtySegmentListEntry
.Flink
= NULL
;
511 current
->DirtySegmentListEntry
.Blink
= NULL
;
512 current
->ReferenceCount
= 1;
513 ExInitializeFastMutex(¤t
->Lock
);
514 ExAcquireFastMutex(¤t
->Lock
);
515 ExAcquireFastMutex(&ViewLock
);
518 /* There is window between the call to CcRosLookupCacheSegment
519 * and CcRosCreateCacheSegment. We must check if a segment on
520 * the fileoffset exist. If there exist a segment, we release
521 * our new created segment and return the existing one.
523 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
524 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
526 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
528 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
529 BcbSegmentListEntry
);
530 if (current
->FileOffset
<= FileOffset
&&
531 (current
->FileOffset
+ Bcb
->CacheSegmentSize
) > FileOffset
)
533 current
->ReferenceCount
++;
534 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
535 ExReleaseFastMutex(&(*CacheSeg
)->Lock
);
536 ExReleaseFastMutex(&ViewLock
);
537 ExFreeToNPagedLookasideList(&CacheSegLookasideList
, *CacheSeg
);
539 ExAcquireFastMutex(¤t
->Lock
);
540 return STATUS_SUCCESS
;
542 if (current
->FileOffset
< FileOffset
)
544 if (previous
== NULL
)
550 if (previous
->FileOffset
< current
->FileOffset
)
556 current_entry
= current_entry
->Flink
;
558 /* There was no existing segment. */
562 InsertHeadList(&previous
->BcbSegmentListEntry
, ¤t
->BcbSegmentListEntry
);
566 InsertHeadList(&Bcb
->BcbSegmentListHead
, ¤t
->BcbSegmentListEntry
);
568 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
569 InsertTailList(&CacheSegmentListHead
, ¤t
->CacheSegmentListEntry
);
570 InsertTailList(&CacheSegmentLRUListHead
, ¤t
->CacheSegmentLRUListEntry
);
571 ExReleaseFastMutex(&ViewLock
);
573 KeAcquireSpinLock(&CiCacheSegMappingRegionLock
, &oldIrql
);
575 StartingOffset
= RtlFindClearBitsAndSet(&CiCacheSegMappingRegionAllocMap
, Bcb
->CacheSegmentSize
/ PAGE_SIZE
, CiCacheSegMappingRegionHint
);
577 if (StartingOffset
== 0xffffffff)
579 DPRINT1("Out of CacheSeg mapping space\n");
583 current
->BaseAddress
= CiCacheSegMappingRegionBase
+ StartingOffset
* PAGE_SIZE
;
585 if (CiCacheSegMappingRegionHint
== StartingOffset
)
587 CiCacheSegMappingRegionHint
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
590 KeReleaseSpinLock(&CiCacheSegMappingRegionLock
, oldIrql
);
592 MmLockAddressSpace(MmGetKernelAddressSpace());
593 current
->BaseAddress
= NULL
;
594 Status
= MmCreateMemoryArea(NULL
,
595 MmGetKernelAddressSpace(),
596 MEMORY_AREA_CACHE_SEGMENT
,
597 ¤t
->BaseAddress
,
598 Bcb
->CacheSegmentSize
,
600 (PMEMORY_AREA
*)¤t
->MemoryArea
,
603 BoundaryAddressMultiple
);
604 MmUnlockAddressSpace(MmGetKernelAddressSpace());
605 if (!NT_SUCCESS(Status
))
610 Pfn
= alloca(sizeof(PFN_TYPE
) * (Bcb
->CacheSegmentSize
/ PAGE_SIZE
));
611 for (i
= 0; i
< (Bcb
->CacheSegmentSize
/ PAGE_SIZE
); i
++)
613 Status
= MmRequestPageMemoryConsumer(MC_CACHE
, TRUE
, &Pfn
[i
]);
614 if (!NT_SUCCESS(Status
))
619 Status
= MmCreateVirtualMapping(NULL
,
620 current
->BaseAddress
,
623 Bcb
->CacheSegmentSize
/ PAGE_SIZE
);
624 if (!NT_SUCCESS(Status
))
628 return(STATUS_SUCCESS
);
632 CcRosGetCacheSegmentChain(PBCB Bcb
,
635 PCACHE_SEGMENT
* CacheSeg
)
637 PCACHE_SEGMENT current
;
639 PCACHE_SEGMENT
* CacheSegList
;
640 PCACHE_SEGMENT Previous
= NULL
;
644 DPRINT("CcRosGetCacheSegmentChain()\n");
646 Length
= ROUND_UP(Length
, Bcb
->CacheSegmentSize
);
648 #if defined(__GNUC__)
649 CacheSegList
= alloca(sizeof(PCACHE_SEGMENT
) *
650 (Length
/ Bcb
->CacheSegmentSize
));
651 #elif defined(_MSC_VER)
652 CacheSegList
= _alloca(sizeof(PCACHE_SEGMENT
) *
653 (Length
/ Bcb
->CacheSegmentSize
));
655 #error Unknown compiler for alloca intrinsic stack allocation "function"
659 * Look for a cache segment already mapping the same data.
661 for (i
= 0; i
< (Length
/ Bcb
->CacheSegmentSize
); i
++)
663 ULONG CurrentOffset
= FileOffset
+ (i
* Bcb
->CacheSegmentSize
);
664 current
= CcRosLookupCacheSegment(Bcb
, CurrentOffset
);
667 CacheSegList
[i
] = current
;
671 CcRosCreateCacheSegment(Bcb
, CurrentOffset
, ¤t
);
672 CacheSegList
[i
] = current
;
676 for (i
= 0; i
< (Length
/ Bcb
->CacheSegmentSize
); i
++)
680 *CacheSeg
= CacheSegList
[i
];
681 Previous
= CacheSegList
[i
];
685 Previous
->NextInChain
= CacheSegList
[i
];
686 Previous
= CacheSegList
[i
];
689 Previous
->NextInChain
= NULL
;
691 return(STATUS_SUCCESS
);
695 CcRosGetCacheSegment(PBCB Bcb
,
700 PCACHE_SEGMENT
* CacheSeg
)
702 PCACHE_SEGMENT current
;
707 DPRINT("CcRosGetCacheSegment()\n");
710 * Look for a cache segment already mapping the same data.
712 current
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
716 * Otherwise create a new segment.
718 Status
= CcRosCreateCacheSegment(Bcb
, FileOffset
, ¤t
);
719 if (!NT_SUCCESS(Status
))
725 * Return information about the segment to the caller.
727 *UptoDate
= current
->Valid
;
728 *BaseAddress
= current
->BaseAddress
;
729 DPRINT("*BaseAddress 0x%.8X\n", *BaseAddress
);
731 *BaseOffset
= current
->FileOffset
;
732 return(STATUS_SUCCESS
);
736 CcRosRequestCacheSegment(PBCB Bcb
,
740 PCACHE_SEGMENT
* CacheSeg
)
742 * FUNCTION: Request a page mapping for a BCB
749 if ((FileOffset
% Bcb
->CacheSegmentSize
) != 0)
751 CPRINT("Bad fileoffset %x should be multiple of %x",
752 FileOffset
, Bcb
->CacheSegmentSize
);
756 return(CcRosGetCacheSegment(Bcb
,
766 CcFreeCachePage(PVOID Context
, MEMORY_AREA
* MemoryArea
, PVOID Address
,
767 PFN_TYPE Page
, SWAPENTRY SwapEntry
, BOOLEAN Dirty
)
769 ASSERT(SwapEntry
== 0);
772 MmReleasePageMemoryConsumer(MC_CACHE
, Page
);
777 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg
)
779 * FUNCTION: Releases a cache segment associated with a BCB
789 DPRINT("Freeing cache segment %x\n", CacheSeg
);
791 RegionSize
= CacheSeg
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
793 /* Unmap all the pages. */
794 for (i
= 0; i
< RegionSize
; i
++)
796 MmDeleteVirtualMapping(NULL
,
797 CacheSeg
->BaseAddress
+ (i
* PAGE_SIZE
),
801 MmReleasePageMemoryConsumer(MC_CACHE
, Page
);
804 KeAcquireSpinLock(&CiCacheSegMappingRegionLock
, &oldIrql
);
805 /* Deallocate all the pages used. */
806 Base
= (ULONG
)(CacheSeg
->BaseAddress
- CiCacheSegMappingRegionBase
) / PAGE_SIZE
;
808 RtlClearBits(&CiCacheSegMappingRegionAllocMap
, Base
, RegionSize
);
810 CiCacheSegMappingRegionHint
= min (CiCacheSegMappingRegionHint
, Base
);
812 KeReleaseSpinLock(&CiCacheSegMappingRegionLock
, oldIrql
);
814 MmLockAddressSpace(MmGetKernelAddressSpace());
815 MmFreeMemoryArea(MmGetKernelAddressSpace(),
816 CacheSeg
->MemoryArea
,
819 MmUnlockAddressSpace(MmGetKernelAddressSpace());
821 ExFreeToNPagedLookasideList(&CacheSegLookasideList
, CacheSeg
);
822 return(STATUS_SUCCESS
);
826 CcRosFreeCacheSegment(PBCB Bcb
, PCACHE_SEGMENT CacheSeg
)
833 DPRINT("CcRosFreeCacheSegment(Bcb %x, CacheSeg %x)\n",
836 ExAcquireFastMutex(&ViewLock
);
837 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
838 RemoveEntryList(&CacheSeg
->BcbSegmentListEntry
);
839 RemoveEntryList(&CacheSeg
->CacheSegmentListEntry
);
840 RemoveEntryList(&CacheSeg
->CacheSegmentLRUListEntry
);
843 RemoveEntryList(&CacheSeg
->DirtySegmentListEntry
);
844 DirtyPageCount
-= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
847 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
848 ExReleaseFastMutex(&ViewLock
);
850 Status
= CcRosInternalFreeCacheSegment(CacheSeg
);
858 CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointers
,
859 IN PLARGE_INTEGER FileOffset OPTIONAL
,
861 OUT PIO_STATUS_BLOCK IoStatus
)
864 LARGE_INTEGER Offset
;
865 PCACHE_SEGMENT current
;
869 DPRINT("CcFlushCache(SectionObjectPointers %x, FileOffset %x, Length %d, IoStatus %x)\n",
870 SectionObjectPointers
, FileOffset
, Length
, IoStatus
);
872 if (SectionObjectPointers
&& SectionObjectPointers
->SharedCacheMap
)
874 Bcb
= (PBCB
)SectionObjectPointers
->SharedCacheMap
;
878 Offset
= *FileOffset
;
882 Offset
.QuadPart
= (LONGLONG
)0;
883 Length
= Bcb
->FileSize
.u
.LowPart
;
888 IoStatus
->Status
= STATUS_SUCCESS
;
889 IoStatus
->Information
= 0;
894 current
= CcRosLookupCacheSegment (Bcb
, Offset
.u
.LowPart
);
899 Status
= CcRosFlushCacheSegment(current
);
900 if (!NT_SUCCESS(Status
) && IoStatus
!= NULL
)
902 IoStatus
->Status
= Status
;
905 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
906 ExReleaseFastMutex(¤t
->Lock
);
907 current
->ReferenceCount
--;
908 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
911 Offset
.QuadPart
+= Bcb
->CacheSegmentSize
;
912 if (Length
> Bcb
->CacheSegmentSize
)
914 Length
-= Bcb
->CacheSegmentSize
;
926 IoStatus
->Status
= STATUS_INVALID_PARAMETER
;
932 CcRosDeleteFileCache(PFILE_OBJECT FileObject
, PBCB Bcb
)
934 * FUNCTION: Releases the BCB associated with a file object
937 PLIST_ENTRY current_entry
;
938 PCACHE_SEGMENT current
;
946 ExReleaseFastMutex(&ViewLock
);
948 CcFlushCache(FileObject
->SectionObjectPointer
, NULL
, 0, NULL
);
950 ExAcquireFastMutex(&ViewLock
);
952 if (Bcb
->RefCount
== 0)
954 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
956 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
957 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
960 FileObject
->SectionObjectPointer
->SharedCacheMap
= NULL
;
963 * Release all cache segments.
965 InitializeListHead(&FreeList
);
966 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
967 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
968 while (!IsListEmpty(&Bcb
->BcbSegmentListHead
))
970 current_entry
= RemoveTailList(&Bcb
->BcbSegmentListHead
);
971 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, BcbSegmentListEntry
);
972 RemoveEntryList(¤t
->CacheSegmentListEntry
);
973 RemoveEntryList(¤t
->CacheSegmentLRUListEntry
);
976 RemoveEntryList(¤t
->DirtySegmentListEntry
);
977 DirtyPageCount
-= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
978 DPRINT1("Freeing dirty segment\n");
980 InsertHeadList(&FreeList
, ¤t
->BcbSegmentListEntry
);
982 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
984 ExReleaseFastMutex(&ViewLock
);
985 ObDereferenceObject (Bcb
->FileObject
);
987 while (!IsListEmpty(&FreeList
))
989 current_entry
= RemoveTailList(&FreeList
);
990 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, BcbSegmentListEntry
);
991 Status
= CcRosInternalFreeCacheSegment(current
);
993 ExFreeToNPagedLookasideList(&BcbLookasideList
, Bcb
);
994 ExAcquireFastMutex(&ViewLock
);
996 return(STATUS_SUCCESS
);
999 VOID
CcRosReferenceCache(PFILE_OBJECT FileObject
)
1002 ExAcquireFastMutex(&ViewLock
);
1003 Bcb
= (PBCB
)FileObject
->SectionObjectPointer
->SharedCacheMap
;
1005 if (Bcb
->RefCount
== 0)
1007 ASSERT(Bcb
->BcbRemoveListEntry
.Flink
!= NULL
);
1008 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1009 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1014 ASSERT(Bcb
->BcbRemoveListEntry
.Flink
== NULL
);
1017 ExReleaseFastMutex(&ViewLock
);
1020 VOID
CcRosSetRemoveOnClose(PSECTION_OBJECT_POINTERS SectionObjectPointer
)
1023 DPRINT("CcRosSetRemoveOnClose()\n");
1024 ExAcquireFastMutex(&ViewLock
);
1025 Bcb
= (PBCB
)SectionObjectPointer
->SharedCacheMap
;
1028 Bcb
->RemoveOnClose
= TRUE
;
1029 if (Bcb
->RefCount
== 0)
1031 CcRosDeleteFileCache(Bcb
->FileObject
, Bcb
);
1034 ExReleaseFastMutex(&ViewLock
);
1038 VOID
CcRosDereferenceCache(PFILE_OBJECT FileObject
)
1041 ExAcquireFastMutex(&ViewLock
);
1042 Bcb
= (PBCB
)FileObject
->SectionObjectPointer
->SharedCacheMap
;
1044 if (Bcb
->RefCount
> 0)
1047 if (Bcb
->RefCount
== 0)
1049 MmFreeSectionSegments(Bcb
->FileObject
);
1050 if (Bcb
->RemoveOnClose
)
1052 CcRosDeleteFileCache(FileObject
, Bcb
);
1056 Bcb
->TimeStamp
= CcTimeStamp
;
1057 InsertHeadList(&ClosedListHead
, &Bcb
->BcbRemoveListEntry
);
1061 ExReleaseFastMutex(&ViewLock
);
1065 CcRosReleaseFileCache(PFILE_OBJECT FileObject
)
1067 * FUNCTION: Called by the file system when a handle to a file object
1073 ExAcquireFastMutex(&ViewLock
);
1075 if (FileObject
->SectionObjectPointer
->SharedCacheMap
!= NULL
)
1077 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1078 if (FileObject
->PrivateCacheMap
!= NULL
)
1080 FileObject
->PrivateCacheMap
= NULL
;
1081 if (Bcb
->RefCount
> 0)
1084 if (Bcb
->RefCount
== 0)
1086 MmFreeSectionSegments(Bcb
->FileObject
);
1087 if (Bcb
->RemoveOnClose
)
1089 CcRosDeleteFileCache(FileObject
, Bcb
);
1093 Bcb
->TimeStamp
= CcTimeStamp
;
1094 InsertHeadList(&ClosedListHead
, &Bcb
->BcbRemoveListEntry
);
1100 ExReleaseFastMutex(&ViewLock
);
1101 return(STATUS_SUCCESS
);
1105 CcTryToInitializeFileCache(PFILE_OBJECT FileObject
)
1110 ExAcquireFastMutex(&ViewLock
);
1112 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1115 Status
= STATUS_UNSUCCESSFUL
;
1119 if (FileObject
->PrivateCacheMap
== NULL
)
1121 FileObject
->PrivateCacheMap
= Bcb
;
1124 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
1126 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1127 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1129 Status
= STATUS_SUCCESS
;
1131 ExReleaseFastMutex(&ViewLock
);
1138 CcRosInitializeFileCache(PFILE_OBJECT FileObject
,
1139 ULONG CacheSegmentSize
)
1141 * FUNCTION: Initializes a BCB for a file object
1146 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1147 DPRINT("CcRosInitializeFileCache(FileObject %x, *Bcb %x, CacheSegmentSize %d)\n",
1148 FileObject
, Bcb
, CacheSegmentSize
);
1150 ExAcquireFastMutex(&ViewLock
);
1153 Bcb
= ExAllocateFromNPagedLookasideList(&BcbLookasideList
);
1156 ExReleaseFastMutex(&ViewLock
);
1157 return(STATUS_UNSUCCESSFUL
);
1159 memset(Bcb
, 0, sizeof(BCB
));
1160 ObReferenceObjectByPointer(FileObject
,
1164 Bcb
->FileObject
= FileObject
;
1165 Bcb
->CacheSegmentSize
= CacheSegmentSize
;
1166 if (FileObject
->FsContext
)
1168 Bcb
->AllocationSize
=
1169 ((PFSRTL_COMMON_FCB_HEADER
)FileObject
->FsContext
)->AllocationSize
;
1171 ((PFSRTL_COMMON_FCB_HEADER
)FileObject
->FsContext
)->FileSize
;
1173 KeInitializeSpinLock(&Bcb
->BcbLock
);
1174 InitializeListHead(&Bcb
->BcbSegmentListHead
);
1175 FileObject
->SectionObjectPointer
->SharedCacheMap
= Bcb
;
1177 if (FileObject
->PrivateCacheMap
== NULL
)
1179 FileObject
->PrivateCacheMap
= Bcb
;
1182 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
1184 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1185 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1187 ExReleaseFastMutex(&ViewLock
);
1189 return(STATUS_SUCCESS
);
1195 PFILE_OBJECT STDCALL
1196 CcGetFileObjectFromSectionPtrs(IN PSECTION_OBJECT_POINTERS SectionObjectPointers
)
1199 if (SectionObjectPointers
&& SectionObjectPointers
->SharedCacheMap
)
1201 Bcb
= (PBCB
)SectionObjectPointers
->SharedCacheMap
;
1203 return Bcb
->FileObject
;
1209 CmLazyCloseThreadMain(PVOID Ignored
)
1211 LARGE_INTEGER Timeout
;
1212 PLIST_ENTRY current_entry
;
1214 ULONG RemoveTimeStamp
;
1217 KeQuerySystemTime (&Timeout
);
1221 Timeout
.QuadPart
+= (LONGLONG
)100000000; // 10sec
1222 Status
= KeWaitForSingleObject(&LazyCloseThreadEvent
,
1228 DPRINT("LazyCloseThreadMain %d\n", CcTimeStamp
);
1230 if (!NT_SUCCESS(Status
))
1232 DbgPrint("LazyCloseThread: Wait failed\n");
1236 if (LazyCloseThreadShouldTerminate
)
1238 DbgPrint("LazyCloseThread: Terminating\n");
1242 ExAcquireFastMutex(&ViewLock
);
1244 if (CcTimeStamp
>= 30)
1246 RemoveTimeStamp
= CcTimeStamp
- 30; /* 5min = 10sec * 30 */
1247 while (!IsListEmpty(&ClosedListHead
))
1249 current_entry
= ClosedListHead
.Blink
;
1250 current
= CONTAINING_RECORD(current_entry
, BCB
, BcbRemoveListEntry
);
1251 if (current
->TimeStamp
>= RemoveTimeStamp
)
1255 CcRosDeleteFileCache(current
->FileObject
, current
);
1258 ExReleaseFastMutex(&ViewLock
);
1268 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
1273 DPRINT("CcInitView()\n");
1275 BoundaryAddressMultiple
.QuadPart
= 0;
1276 CiCacheSegMappingRegionHint
= 0;
1277 CiCacheSegMappingRegionBase
= NULL
;
1279 MmLockAddressSpace(MmGetKernelAddressSpace());
1281 Status
= MmCreateMemoryArea(NULL
,
1282 MmGetKernelAddressSpace(),
1283 MEMORY_AREA_CACHE_SEGMENT
,
1284 &CiCacheSegMappingRegionBase
,
1285 CI_CACHESEG_MAPPING_REGION_SIZE
,
1290 BoundaryAddressMultiple
);
1291 MmUnlockAddressSpace(MmGetKernelAddressSpace());
1292 if (!NT_SUCCESS(Status
))
1297 Buffer
= ExAllocatePool(NonPagedPool
, CI_CACHESEG_MAPPING_REGION_SIZE
/ (PAGE_SIZE
* 8));
1299 RtlInitializeBitMap(&CiCacheSegMappingRegionAllocMap
, Buffer
, CI_CACHESEG_MAPPING_REGION_SIZE
/ PAGE_SIZE
);
1300 RtlClearAllBits(&CiCacheSegMappingRegionAllocMap
);
1302 KeInitializeSpinLock(&CiCacheSegMappingRegionLock
);
1304 InitializeListHead(&CacheSegmentListHead
);
1305 InitializeListHead(&DirtySegmentListHead
);
1306 InitializeListHead(&CacheSegmentLRUListHead
);
1307 InitializeListHead(&ClosedListHead
);
1308 ExInitializeFastMutex(&ViewLock
);
1309 ExInitializeNPagedLookasideList (&iBcbLookasideList
,
1313 sizeof(INTERNAL_BCB
),
1316 ExInitializeNPagedLookasideList (&BcbLookasideList
,
1323 ExInitializeNPagedLookasideList (&CacheSegLookasideList
,
1327 sizeof(CACHE_SEGMENT
),
1331 MmInitializeMemoryConsumer(MC_CACHE
, CcRosTrimCache
);
1333 CcInitCacheZeroPage();
1336 LazyCloseThreadShouldTerminate
= FALSE
;
1337 KeInitializeEvent (&LazyCloseThreadEvent
, SynchronizationEvent
, FALSE
);
1338 Status
= PsCreateSystemThread(&LazyCloseThreadHandle
,
1343 (PKSTART_ROUTINE
)CmLazyCloseThreadMain
,
1345 if (NT_SUCCESS(Status
))
1347 Priority
= LOW_REALTIME_PRIORITY
;
1348 NtSetInformationThread(LazyCloseThreadHandle
,