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.
19 /* $Id: view.c,v 1.62 2003/06/07 11:34:36 chorns Exp $
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 ******************************************************************/
54 #include <ddk/ntddk.h>
55 #include <ddk/ntifs.h>
56 #include <internal/mm.h>
57 #include <internal/cc.h>
58 #include <internal/pool.h>
59 #include <ntos/minmax.h>
62 #include <internal/debug.h>
64 /* GLOBALS *******************************************************************/
67 * If CACHE_BITMAP is defined, the cache manager uses one large memory region
68 * within the kernel address space and allocate/deallocate space from this block
69 * over a bitmap. If CACHE_BITMAP is used, the size of the mdl mapping region
70 * must be reduced (ntoskrnl\mm\mdl.c, MI_MDLMAPPING_REGION_SIZE).
72 //#define CACHE_BITMAP
74 #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
75 #define ROUND_DOWN(N, S) (((N) % (S)) ? ROUND_UP(N, S) - S : N)
77 #define TAG_CSEG TAG('C', 'S', 'E', 'G')
78 #define TAG_BCB TAG('B', 'C', 'B', ' ')
79 #define TAG_IBCB TAG('i', 'B', 'C', 'B')
81 static LIST_ENTRY DirtySegmentListHead
;
82 static LIST_ENTRY CacheSegmentListHead
;
83 static LIST_ENTRY CacheSegmentLRUListHead
;
84 static LIST_ENTRY ClosedListHead
;
85 ULONG DirtyPageCount
=0;
90 #define CI_CACHESEG_MAPPING_REGION_SIZE (128*1024*1024)
92 static PVOID CiCacheSegMappingRegionBase
= NULL
;
93 static RTL_BITMAP CiCacheSegMappingRegionAllocMap
;
94 static ULONG CiCacheSegMappingRegionHint
;
95 static KSPIN_LOCK CiCacheSegMappingRegionLock
;
98 NPAGED_LOOKASIDE_LIST iBcbLookasideList
;
99 static NPAGED_LOOKASIDE_LIST BcbLookasideList
;
100 static NPAGED_LOOKASIDE_LIST CacheSegLookasideList
;
102 static ULONG CcTimeStamp
;
103 static KEVENT LazyCloseThreadEvent
;
104 static HANDLE LazyCloseThreadHandle
;
105 static CLIENT_ID LazyCloseThreadId
;
106 static volatile BOOLEAN LazyCloseThreadShouldTerminate
;
108 void * alloca(size_t size
);
111 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg
);
113 /* FUNCTIONS *****************************************************************/
116 CcRosFlushCacheSegment(PCACHE_SEGMENT CacheSegment
)
120 Status
= WriteCacheSegment(CacheSegment
);
121 if (NT_SUCCESS(Status
))
123 ExAcquireFastMutex(&ViewLock
);
124 KeAcquireSpinLock(&CacheSegment
->Bcb
->BcbLock
, &oldIrql
);
125 CacheSegment
->Dirty
= FALSE
;
126 RemoveEntryList(&CacheSegment
->DirtySegmentListEntry
);
127 DirtyPageCount
-= CacheSegment
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
128 CacheSegment
->ReferenceCount
--;
129 KeReleaseSpinLock(&CacheSegment
->Bcb
->BcbLock
, oldIrql
);
130 ExReleaseFastMutex(&ViewLock
);
135 VOID
CcRosRemoveUnusedFiles(VOID
);
139 CcRosFlushDirtyPages(ULONG Target
, PULONG Count
)
141 PLIST_ENTRY current_entry
;
142 PCACHE_SEGMENT current
;
143 ULONG PagesPerSegment
;
146 static ULONG WriteCount
[4] = {0, 0, 0, 0};
149 DPRINT("CcRosFlushDirtyPages(Target %d)\n", Target
);
153 ExAcquireFastMutex(&ViewLock
);
155 WriteCount
[0] = WriteCount
[1];
156 WriteCount
[1] = WriteCount
[2];
157 WriteCount
[2] = WriteCount
[3];
160 NewTarget
= WriteCount
[0] + WriteCount
[1] + WriteCount
[2];
162 if (NewTarget
< DirtyPageCount
)
164 NewTarget
= (DirtyPageCount
- NewTarget
+ 3) / 4;
165 WriteCount
[0] += NewTarget
;
166 WriteCount
[1] += NewTarget
;
167 WriteCount
[2] += NewTarget
;
168 WriteCount
[3] += NewTarget
;
171 NewTarget
= WriteCount
[0];
173 Target
= max(NewTarget
, Target
);
175 current_entry
= DirtySegmentListHead
.Flink
;
176 if (current_entry
== &DirtySegmentListHead
)
178 DPRINT("No Dirty pages\n");
180 while (current_entry
!= &DirtySegmentListHead
&& Target
> 0)
182 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
183 DirtySegmentListEntry
);
184 current_entry
= current_entry
->Flink
;
185 Locked
= ExTryToAcquireFastMutex(¤t
->Lock
);
190 assert(current
->Dirty
);
191 if (current
->ReferenceCount
> 1)
193 ExReleaseFastMutex(¤t
->Lock
);
196 ExReleaseFastMutex(&ViewLock
);
197 PagesPerSegment
= current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
198 Status
= CcRosFlushCacheSegment(current
);
199 ExReleaseFastMutex(¤t
->Lock
);
200 if (!NT_SUCCESS(Status
) && (Status
!= STATUS_END_OF_FILE
))
202 DPRINT1("CC: Failed to flush cache segment.\n");
206 (*Count
) += PagesPerSegment
;
207 Target
-= PagesPerSegment
;
209 ExAcquireFastMutex(&ViewLock
);
210 current_entry
= DirtySegmentListHead
.Flink
;
212 if (*Count
< NewTarget
)
214 WriteCount
[1] += (NewTarget
- *Count
);
216 ExReleaseFastMutex(&ViewLock
);
217 DPRINT("CcRosFlushDirtyPages() finished\n");
219 return(STATUS_SUCCESS
);
223 CcRosTrimCache(ULONG Target
, ULONG Priority
, PULONG NrFreed
)
225 * FUNCTION: Try to free some memory from the file cache.
227 * Target - The number of pages to be freed.
228 * Priority - The priority of free (currently unused).
229 * NrFreed - Points to a variable where the number of pages
230 * actually freed is returned.
233 PLIST_ENTRY current_entry
;
234 PCACHE_SEGMENT current
;
235 ULONG PagesPerSegment
;
240 DPRINT("CcRosTrimCache(Target %d)\n", Target
);
244 InitializeListHead(&FreeList
);
246 ExAcquireFastMutex(&ViewLock
);
247 current_entry
= CacheSegmentLRUListHead
.Flink
;
248 while (current_entry
!= &CacheSegmentLRUListHead
&& Target
> 0)
250 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
251 CacheSegmentLRUListEntry
);
252 current_entry
= current_entry
->Flink
;
254 KeAcquireSpinLock(¤t
->Bcb
->BcbLock
, &oldIrql
);
255 if (current
->ReferenceCount
== 0)
257 RemoveEntryList(¤t
->BcbSegmentListEntry
);
258 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
259 RemoveEntryList(¤t
->CacheSegmentListEntry
);
260 RemoveEntryList(¤t
->CacheSegmentLRUListEntry
);
261 InsertHeadList(&FreeList
, ¤t
->BcbSegmentListEntry
);
265 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
268 ExReleaseFastMutex(&ViewLock
);
270 while (!IsListEmpty(&FreeList
))
272 current_entry
= RemoveHeadList(&FreeList
);
273 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
274 BcbSegmentListEntry
);
275 PagesPerSegment
= current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
276 PagesFreed
= min(PagesPerSegment
, Target
);
277 Target
-= PagesFreed
;
278 (*NrFreed
) += PagesFreed
;
279 CcRosInternalFreeCacheSegment(current
);
282 DPRINT("CcRosTrimCache() finished\n");
283 return(STATUS_SUCCESS
);
287 CcRosReleaseCacheSegment(PBCB Bcb
,
288 PCACHE_SEGMENT CacheSeg
,
293 BOOLEAN WasDirty
= CacheSeg
->Dirty
;
298 DPRINT("CcReleaseCacheSegment(Bcb %x, CacheSeg %x, Valid %d)\n",
299 Bcb
, CacheSeg
, Valid
);
301 CacheSeg
->Valid
= Valid
;
302 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| Dirty
;
304 ExAcquireFastMutex(&ViewLock
);
305 if (!WasDirty
&& CacheSeg
->Dirty
)
307 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
308 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
310 RemoveEntryList(&CacheSeg
->CacheSegmentLRUListEntry
);
311 InsertTailList(&CacheSegmentLRUListHead
, &CacheSeg
->CacheSegmentLRUListEntry
);
315 CacheSeg
->MappedCount
++;
317 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
318 CacheSeg
->ReferenceCount
--;
319 if (Mapped
&& CacheSeg
->MappedCount
== 1)
321 CacheSeg
->ReferenceCount
++;
323 if (!WasDirty
&& CacheSeg
->Dirty
)
325 CacheSeg
->ReferenceCount
++;
327 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
328 ExReleaseFastMutex(&ViewLock
);
329 ExReleaseFastMutex(&CacheSeg
->Lock
);
331 return(STATUS_SUCCESS
);
335 CcRosLookupCacheSegment(PBCB Bcb
, ULONG FileOffset
)
337 PLIST_ENTRY current_entry
;
338 PCACHE_SEGMENT current
;
343 DPRINT("CcRosLookupCacheSegment(Bcb %x, FileOffset %d)\n", Bcb
, FileOffset
);
345 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
346 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
347 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
349 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
350 BcbSegmentListEntry
);
351 if (current
->FileOffset
<= FileOffset
&&
352 (current
->FileOffset
+ Bcb
->CacheSegmentSize
) > FileOffset
)
354 current
->ReferenceCount
++;
355 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
358 current_entry
= current_entry
->Flink
;
360 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
365 CcRosMarkDirtyCacheSegment(PBCB Bcb
, ULONG FileOffset
)
367 PCACHE_SEGMENT CacheSeg
;
372 DPRINT("CcRosMarkDirtyCacheSegment(Bcb %x, FileOffset %d)\n", Bcb
, FileOffset
);
374 CacheSeg
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
375 if (CacheSeg
== NULL
)
379 ExAcquireFastMutex(&CacheSeg
->Lock
);
380 if (!CacheSeg
->Dirty
)
382 ExAcquireFastMutex(&ViewLock
);
383 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
384 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
385 ExReleaseFastMutex(&ViewLock
);
389 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
390 CacheSeg
->ReferenceCount
--;
391 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
395 CacheSeg
->Dirty
= TRUE
;
396 ExReleaseFastMutex(&CacheSeg
->Lock
);
398 return(STATUS_SUCCESS
);
402 CcRosUnmapCacheSegment(PBCB Bcb
, ULONG FileOffset
, BOOLEAN NowDirty
)
404 PCACHE_SEGMENT CacheSeg
;
410 DPRINT("CcRosUnmapCacheSegment(Bcb %x, FileOffset %d, NowDirty %d)\n",
411 Bcb
, FileOffset
, NowDirty
);
413 CacheSeg
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
414 if (CacheSeg
== NULL
)
416 return(STATUS_UNSUCCESSFUL
);
418 ExAcquireFastMutex(&CacheSeg
->Lock
);
420 WasDirty
= CacheSeg
->Dirty
;
421 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| NowDirty
;
423 CacheSeg
->MappedCount
--;
425 if (!WasDirty
&& NowDirty
)
427 ExAcquireFastMutex(&ViewLock
);
428 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
429 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
430 ExReleaseFastMutex(&ViewLock
);
433 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
434 CacheSeg
->ReferenceCount
--;
435 if (!WasDirty
&& NowDirty
)
437 CacheSeg
->ReferenceCount
++;
439 if (CacheSeg
->MappedCount
== 0)
441 CacheSeg
->ReferenceCount
--;
443 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
445 ExReleaseFastMutex(&CacheSeg
->Lock
);
446 return(STATUS_SUCCESS
);
450 CcRosCreateCacheSegment(PBCB Bcb
,
452 PCACHE_SEGMENT
* CacheSeg
,
456 PCACHE_SEGMENT current
;
457 PCACHE_SEGMENT previous
;
458 PLIST_ENTRY current_entry
;
462 ULONG StartingOffset
;
467 DPRINT("CcRosCreateCacheSegment()\n");
469 if (FileOffset
>= Bcb
->FileSize
.u
.LowPart
)
472 return STATUS_INVALID_PARAMETER
;
475 current
= ExAllocateFromNPagedLookasideList(&CacheSegLookasideList
);
476 current
->Valid
= FALSE
;
477 current
->Dirty
= FALSE
;
478 current
->FileOffset
= ROUND_DOWN(FileOffset
, Bcb
->CacheSegmentSize
);
480 current
->MappedCount
= 0;
481 current
->DirtySegmentListEntry
.Flink
= NULL
;
482 current
->DirtySegmentListEntry
.Blink
= NULL
;
483 current
->ReferenceCount
= 1;
484 ExInitializeFastMutex(¤t
->Lock
);
485 ExAcquireFastMutex(¤t
->Lock
);
486 ExAcquireFastMutex(&ViewLock
);
489 /* There is window between the call to CcRosLookupCacheSegment
490 * and CcRosCreateCacheSegment. We must check if a segment on
491 * the fileoffset exist. If there exist a segment, we release
492 * our new created segment and return the existing one.
494 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
495 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
497 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
499 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
500 BcbSegmentListEntry
);
501 if (current
->FileOffset
<= FileOffset
&&
502 (current
->FileOffset
+ Bcb
->CacheSegmentSize
) > FileOffset
)
504 current
->ReferenceCount
++;
505 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
506 ExReleaseFastMutex(&(*CacheSeg
)->Lock
);
507 ExReleaseFastMutex(&ViewLock
);
508 ExFreeToNPagedLookasideList(&CacheSegLookasideList
, *CacheSeg
);
512 ExAcquireFastMutex(¤t
->Lock
);
514 return STATUS_SUCCESS
;
516 if (current
->FileOffset
< FileOffset
)
518 if (previous
== NULL
)
524 if (previous
->FileOffset
< current
->FileOffset
)
530 current_entry
= current_entry
->Flink
;
532 /* There was no existing segment. */
536 InsertHeadList(&previous
->BcbSegmentListEntry
, ¤t
->BcbSegmentListEntry
);
540 InsertHeadList(&Bcb
->BcbSegmentListHead
, ¤t
->BcbSegmentListEntry
);
542 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
543 InsertTailList(&CacheSegmentListHead
, ¤t
->CacheSegmentListEntry
);
544 InsertTailList(&CacheSegmentLRUListHead
, ¤t
->CacheSegmentLRUListEntry
);
545 ExReleaseFastMutex(&ViewLock
);
547 KeAcquireSpinLock(&CiCacheSegMappingRegionLock
, &oldIrql
);
549 StartingOffset
= RtlFindClearBitsAndSet(&CiCacheSegMappingRegionAllocMap
, Bcb
->CacheSegmentSize
/ PAGE_SIZE
, CiCacheSegMappingRegionHint
);
551 if (StartingOffset
== 0xffffffff)
553 DPRINT1("Out of CacheSeg mapping space\n");
557 current
->BaseAddress
= CiCacheSegMappingRegionBase
+ StartingOffset
* PAGE_SIZE
;
559 if (CiCacheSegMappingRegionHint
== StartingOffset
)
561 CiCacheSegMappingRegionHint
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
564 KeReleaseSpinLock(&CiCacheSegMappingRegionLock
, oldIrql
);
566 MmLockAddressSpace(MmGetKernelAddressSpace());
567 current
->BaseAddress
= NULL
;
568 Status
= MmCreateMemoryArea(NULL
,
569 MmGetKernelAddressSpace(),
570 MEMORY_AREA_CACHE_SEGMENT
,
571 ¤t
->BaseAddress
,
572 Bcb
->CacheSegmentSize
,
574 (PMEMORY_AREA
*)¤t
->MemoryArea
,
577 MmUnlockAddressSpace(MmGetKernelAddressSpace());
578 if (!NT_SUCCESS(Status
))
583 for (i
= 0; i
< (Bcb
->CacheSegmentSize
/ PAGE_SIZE
); i
++)
585 PHYSICAL_ADDRESS Page
;
587 Status
= MmRequestPageMemoryConsumer(MC_CACHE
, TRUE
, &Page
);
588 if (!NT_SUCCESS(Status
))
593 Status
= MmCreateVirtualMapping(NULL
,
594 current
->BaseAddress
+ (i
* PAGE_SIZE
),
598 if (!NT_SUCCESS(Status
))
605 ExReleaseFastMutex(¤t
->Lock
);
608 return(STATUS_SUCCESS
);
612 CcRosGetCacheSegmentChain(PBCB Bcb
,
615 PCACHE_SEGMENT
* CacheSeg
)
617 PCACHE_SEGMENT current
;
619 PCACHE_SEGMENT
* CacheSegList
;
620 PCACHE_SEGMENT Previous
= NULL
;
624 DPRINT("CcRosGetCacheSegmentChain()\n");
626 Length
= ROUND_UP(Length
, Bcb
->CacheSegmentSize
);
628 CacheSegList
= alloca(sizeof(PCACHE_SEGMENT
) *
629 (Length
/ Bcb
->CacheSegmentSize
));
632 * Look for a cache segment already mapping the same data.
634 for (i
= 0; i
< (Length
/ Bcb
->CacheSegmentSize
); i
++)
636 ULONG CurrentOffset
= FileOffset
+ (i
* Bcb
->CacheSegmentSize
);
637 current
= CcRosLookupCacheSegment(Bcb
, CurrentOffset
);
640 CacheSegList
[i
] = current
;
644 CcRosCreateCacheSegment(Bcb
, CurrentOffset
, ¤t
, FALSE
);
645 CacheSegList
[i
] = current
;
649 for (i
= 0; i
< (Length
/ Bcb
->CacheSegmentSize
); i
++)
651 ExAcquireFastMutex(&CacheSegList
[i
]->Lock
);
654 *CacheSeg
= CacheSegList
[i
];
655 Previous
= CacheSegList
[i
];
659 Previous
->NextInChain
= CacheSegList
[i
];
660 Previous
= CacheSegList
[i
];
663 Previous
->NextInChain
= NULL
;
665 return(STATUS_SUCCESS
);
669 CcRosGetCacheSegment(PBCB Bcb
,
674 PCACHE_SEGMENT
* CacheSeg
)
676 PCACHE_SEGMENT current
;
681 DPRINT("CcRosGetCacheSegment()\n");
684 * Look for a cache segment already mapping the same data.
686 current
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
689 ExAcquireFastMutex(¤t
->Lock
);
694 * Otherwise create a new segment.
696 Status
= CcRosCreateCacheSegment(Bcb
, FileOffset
, ¤t
, TRUE
);
697 if (!NT_SUCCESS(Status
))
703 * Return information about the segment to the caller.
705 *UptoDate
= current
->Valid
;
706 *BaseAddress
= current
->BaseAddress
;
707 DPRINT("*BaseAddress 0x%.8X\n", *BaseAddress
);
709 *BaseOffset
= current
->FileOffset
;
710 return(STATUS_SUCCESS
);
714 CcRosRequestCacheSegment(PBCB Bcb
,
718 PCACHE_SEGMENT
* CacheSeg
)
720 * FUNCTION: Request a page mapping for a BCB
727 if ((FileOffset
% Bcb
->CacheSegmentSize
) != 0)
729 CPRINT("Bad fileoffset %x should be multiple of %x",
730 FileOffset
, Bcb
->CacheSegmentSize
);
734 return(CcRosGetCacheSegment(Bcb
,
744 CcFreeCachePage(PVOID Context
, MEMORY_AREA
* MemoryArea
, PVOID Address
,
745 PHYSICAL_ADDRESS PhysAddr
, SWAPENTRY SwapEntry
, BOOLEAN Dirty
)
747 assert(SwapEntry
== 0);
748 if (PhysAddr
.QuadPart
!= 0)
750 MmReleasePageMemoryConsumer(MC_CACHE
, PhysAddr
);
755 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg
)
757 * FUNCTION: Releases a cache segment associated with a BCB
764 PHYSICAL_ADDRESS PhysicalAddr
;
767 DPRINT("Freeing cache segment %x\n", CacheSeg
);
769 RegionSize
= CacheSeg
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
771 /* Unmap all the pages. */
772 for (i
= 0; i
< RegionSize
; i
++)
774 MmDeleteVirtualMapping(NULL
,
775 CacheSeg
->BaseAddress
+ (i
* PAGE_SIZE
),
779 MmReleasePageMemoryConsumer(MC_CACHE
, PhysicalAddr
);
782 KeAcquireSpinLock(&CiCacheSegMappingRegionLock
, &oldIrql
);
783 /* Deallocate all the pages used. */
784 Base
= (ULONG
)(CacheSeg
->BaseAddress
- CiCacheSegMappingRegionBase
) / PAGE_SIZE
;
786 RtlClearBits(&CiCacheSegMappingRegionAllocMap
, Base
, RegionSize
);
788 CiCacheSegMappingRegionHint
= min (CiCacheSegMappingRegionHint
, Base
);
790 KeReleaseSpinLock(&CiCacheSegMappingRegionLock
, oldIrql
);
792 MmLockAddressSpace(MmGetKernelAddressSpace());
793 MmFreeMemoryArea(MmGetKernelAddressSpace(),
794 CacheSeg
->BaseAddress
,
795 CacheSeg
->Bcb
->CacheSegmentSize
,
798 MmUnlockAddressSpace(MmGetKernelAddressSpace());
800 ExFreeToNPagedLookasideList(&CacheSegLookasideList
, CacheSeg
);
801 return(STATUS_SUCCESS
);
805 CcRosFreeCacheSegment(PBCB Bcb
, PCACHE_SEGMENT CacheSeg
)
812 DPRINT("CcRosFreeCacheSegment(Bcb %x, CacheSeg %x)\n",
815 ExAcquireFastMutex(&ViewLock
);
816 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
817 RemoveEntryList(&CacheSeg
->BcbSegmentListEntry
);
818 RemoveEntryList(&CacheSeg
->CacheSegmentListEntry
);
819 RemoveEntryList(&CacheSeg
->CacheSegmentLRUListEntry
);
822 RemoveEntryList(&CacheSeg
->DirtySegmentListEntry
);
823 DirtyPageCount
-= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
826 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
827 ExReleaseFastMutex(&ViewLock
);
829 Status
= CcRosInternalFreeCacheSegment(CacheSeg
);
834 CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointers
,
835 IN PLARGE_INTEGER FileOffset OPTIONAL
,
837 OUT PIO_STATUS_BLOCK IoStatus
)
840 LARGE_INTEGER Offset
;
841 PCACHE_SEGMENT current
;
845 DPRINT("CcFlushCache(SectionObjectPointers %x, FileOffset %x, Length %d, IoStatus %x)\n",
846 SectionObjectPointers
, FileOffset
, Length
, IoStatus
);
848 if (SectionObjectPointers
&& SectionObjectPointers
->SharedCacheMap
)
850 Bcb
= (PBCB
)SectionObjectPointers
->SharedCacheMap
;
854 Offset
= *FileOffset
;
858 Offset
.QuadPart
= 0LL;
859 Length
= Bcb
->FileSize
.u
.LowPart
;
864 IoStatus
->Status
= STATUS_SUCCESS
;
865 IoStatus
->Information
= 0;
870 current
= CcRosLookupCacheSegment (Bcb
, Offset
.u
.LowPart
);
873 ExAcquireFastMutex(¤t
->Lock
);
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 // DPRINT1("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 if (Bcb
->RemoveOnClose
)
1065 CcRosDeleteFileCache(FileObject
, Bcb
);
1069 Bcb
->TimeStamp
= CcTimeStamp
;
1070 InsertHeadList(&ClosedListHead
, &Bcb
->BcbRemoveListEntry
);
1076 ExReleaseFastMutex(&ViewLock
);
1077 return(STATUS_SUCCESS
);
1081 CcRosInitializeFileCache(PFILE_OBJECT FileObject
,
1082 ULONG CacheSegmentSize
)
1084 * FUNCTION: Initializes a BCB for a file object
1088 DPRINT("CcRosInitializeFileCache(FileObject %x, *Bcb %x, CacheSegmentSize %d)\n",
1089 FileObject
, Bcb
, CacheSegmentSize
);
1091 ExAcquireFastMutex(&ViewLock
);
1093 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1096 Bcb
= ExAllocateFromNPagedLookasideList(&BcbLookasideList
);
1099 ExReleaseFastMutex(&ViewLock
);
1100 return(STATUS_UNSUCCESSFUL
);
1102 memset(Bcb
, 0, sizeof(BCB
));
1103 ObReferenceObjectByPointer(FileObject
,
1107 Bcb
->FileObject
= FileObject
;
1108 Bcb
->CacheSegmentSize
= CacheSegmentSize
;
1109 if (FileObject
->FsContext
)
1111 Bcb
->AllocationSize
=
1112 ((PFSRTL_COMMON_FCB_HEADER
)FileObject
->FsContext
)->AllocationSize
;
1114 ((PFSRTL_COMMON_FCB_HEADER
)FileObject
->FsContext
)->FileSize
;
1116 KeInitializeSpinLock(&Bcb
->BcbLock
);
1117 InitializeListHead(&Bcb
->BcbSegmentListHead
);
1118 FileObject
->SectionObjectPointer
->SharedCacheMap
= Bcb
;
1120 if (FileObject
->PrivateCacheMap
== NULL
)
1122 FileObject
->PrivateCacheMap
= Bcb
;
1125 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
1127 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1128 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1130 ExReleaseFastMutex(&ViewLock
);
1132 return(STATUS_SUCCESS
);
1135 PFILE_OBJECT STDCALL
1136 CcGetFileObjectFromSectionPtrs(IN PSECTION_OBJECT_POINTERS SectionObjectPointers
)
1139 if (SectionObjectPointers
&& SectionObjectPointers
->SharedCacheMap
)
1141 Bcb
= (PBCB
)SectionObjectPointers
->SharedCacheMap
;
1143 return Bcb
->FileObject
;
1149 CmLazyCloseThreadMain(PVOID Ignored
)
1151 LARGE_INTEGER Timeout
;
1152 PLIST_ENTRY current_entry
;
1154 ULONG RemoveTimeStamp
;
1157 KeQuerySystemTime (&Timeout
);
1161 Timeout
.QuadPart
+= 100000000LL; // 10sec
1162 Status
= KeWaitForSingleObject(&LazyCloseThreadEvent
,
1168 DPRINT("LazyCloseThreadMain %d\n", CcTimeStamp
);
1170 if (!NT_SUCCESS(Status
))
1172 DbgPrint("LazyCloseThread: Wait failed\n");
1176 if (LazyCloseThreadShouldTerminate
)
1178 DbgPrint("LazyCloseThread: Terminating\n");
1182 ExAcquireFastMutex(&ViewLock
);
1184 if (CcTimeStamp
>= 30)
1186 RemoveTimeStamp
= CcTimeStamp
- 30; /* 5min = 10sec * 30 */
1187 while (!IsListEmpty(&ClosedListHead
))
1189 current_entry
= ClosedListHead
.Blink
;
1190 current
= CONTAINING_RECORD(current_entry
, BCB
, BcbRemoveListEntry
);
1191 if (current
->TimeStamp
>= RemoveTimeStamp
)
1195 CcRosDeleteFileCache(current
->FileObject
, current
);
1198 ExReleaseFastMutex(&ViewLock
);
1212 DPRINT("CcInitView()\n");
1214 CiCacheSegMappingRegionHint
= 0;
1215 CiCacheSegMappingRegionBase
= NULL
;
1217 MmLockAddressSpace(MmGetKernelAddressSpace());
1219 Status
= MmCreateMemoryArea(NULL
,
1220 MmGetKernelAddressSpace(),
1221 MEMORY_AREA_CACHE_SEGMENT
,
1222 &CiCacheSegMappingRegionBase
,
1223 CI_CACHESEG_MAPPING_REGION_SIZE
,
1228 MmUnlockAddressSpace(MmGetKernelAddressSpace());
1229 if (!NT_SUCCESS(Status
))
1234 Buffer
= ExAllocatePool(NonPagedPool
, CI_CACHESEG_MAPPING_REGION_SIZE
/ (PAGE_SIZE
* 8));
1236 RtlInitializeBitMap(&CiCacheSegMappingRegionAllocMap
, Buffer
, CI_CACHESEG_MAPPING_REGION_SIZE
/ PAGE_SIZE
);
1237 RtlClearAllBits(&CiCacheSegMappingRegionAllocMap
);
1239 KeInitializeSpinLock(&CiCacheSegMappingRegionLock
);
1241 InitializeListHead(&CacheSegmentListHead
);
1242 InitializeListHead(&DirtySegmentListHead
);
1243 InitializeListHead(&CacheSegmentLRUListHead
);
1244 InitializeListHead(&ClosedListHead
);
1245 ExInitializeFastMutex(&ViewLock
);
1246 ExInitializeNPagedLookasideList (&iBcbLookasideList
,
1250 sizeof(INTERNAL_BCB
),
1253 ExInitializeNPagedLookasideList (&BcbLookasideList
,
1260 ExInitializeNPagedLookasideList (&CacheSegLookasideList
,
1264 sizeof(CACHE_SEGMENT
),
1268 MmInitializeMemoryConsumer(MC_CACHE
, CcRosTrimCache
);
1270 CcInitCacheZeroPage();
1273 LazyCloseThreadShouldTerminate
= FALSE
;
1274 KeInitializeEvent (&LazyCloseThreadEvent
, SynchronizationEvent
, FALSE
);
1275 Status
= PsCreateSystemThread(&LazyCloseThreadHandle
,
1280 (PKSTART_ROUTINE
)CmLazyCloseThreadMain
,
1282 if (NT_SUCCESS(Status
))
1284 Priority
= LOW_REALTIME_PRIORITY
;
1285 NtSetInformationThread(LazyCloseThreadHandle
,