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.73 2004/08/01 07:24:57 hbirr 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 #if defined(__GNUC__)
109 void * alloca(size_t size
);
110 #elif defined(_MSC_VER)
111 void* _alloca(size_t size
);
113 #error Unknown compiler for alloca intrinsic stack allocation "function"
118 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg
);
120 /* FUNCTIONS *****************************************************************/
123 CcRosFlushCacheSegment(PCACHE_SEGMENT CacheSegment
)
127 Status
= WriteCacheSegment(CacheSegment
);
128 if (NT_SUCCESS(Status
))
130 ExAcquireFastMutex(&ViewLock
);
131 KeAcquireSpinLock(&CacheSegment
->Bcb
->BcbLock
, &oldIrql
);
132 CacheSegment
->Dirty
= FALSE
;
133 RemoveEntryList(&CacheSegment
->DirtySegmentListEntry
);
134 DirtyPageCount
-= CacheSegment
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
135 CacheSegment
->ReferenceCount
--;
136 KeReleaseSpinLock(&CacheSegment
->Bcb
->BcbLock
, oldIrql
);
137 ExReleaseFastMutex(&ViewLock
);
143 CcRosFlushDirtyPages(ULONG Target
, PULONG Count
)
145 PLIST_ENTRY current_entry
;
146 PCACHE_SEGMENT current
;
147 ULONG PagesPerSegment
;
150 static ULONG WriteCount
[4] = {0, 0, 0, 0};
153 DPRINT("CcRosFlushDirtyPages(Target %d)\n", Target
);
157 ExAcquireFastMutex(&ViewLock
);
159 WriteCount
[0] = WriteCount
[1];
160 WriteCount
[1] = WriteCount
[2];
161 WriteCount
[2] = WriteCount
[3];
164 NewTarget
= WriteCount
[0] + WriteCount
[1] + WriteCount
[2];
166 if (NewTarget
< DirtyPageCount
)
168 NewTarget
= (DirtyPageCount
- NewTarget
+ 3) / 4;
169 WriteCount
[0] += NewTarget
;
170 WriteCount
[1] += NewTarget
;
171 WriteCount
[2] += NewTarget
;
172 WriteCount
[3] += NewTarget
;
175 NewTarget
= WriteCount
[0];
177 Target
= max(NewTarget
, Target
);
179 current_entry
= DirtySegmentListHead
.Flink
;
180 if (current_entry
== &DirtySegmentListHead
)
182 DPRINT("No Dirty pages\n");
184 while (current_entry
!= &DirtySegmentListHead
&& Target
> 0)
186 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
187 DirtySegmentListEntry
);
188 current_entry
= current_entry
->Flink
;
189 Locked
= ExTryToAcquireFastMutex(¤t
->Lock
);
194 assert(current
->Dirty
);
195 if (current
->ReferenceCount
> 1)
197 ExReleaseFastMutex(¤t
->Lock
);
200 ExReleaseFastMutex(&ViewLock
);
201 PagesPerSegment
= current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
202 Status
= CcRosFlushCacheSegment(current
);
203 ExReleaseFastMutex(¤t
->Lock
);
204 if (!NT_SUCCESS(Status
) && (Status
!= STATUS_END_OF_FILE
))
206 DPRINT1("CC: Failed to flush cache segment.\n");
210 (*Count
) += PagesPerSegment
;
211 Target
-= PagesPerSegment
;
213 ExAcquireFastMutex(&ViewLock
);
214 current_entry
= DirtySegmentListHead
.Flink
;
216 if (*Count
< NewTarget
)
218 WriteCount
[1] += (NewTarget
- *Count
);
220 ExReleaseFastMutex(&ViewLock
);
221 DPRINT("CcRosFlushDirtyPages() finished\n");
223 return(STATUS_SUCCESS
);
227 CcRosTrimCache(ULONG Target
, ULONG Priority
, PULONG NrFreed
)
229 * FUNCTION: Try to free some memory from the file cache.
231 * Target - The number of pages to be freed.
232 * Priority - The priority of free (currently unused).
233 * NrFreed - Points to a variable where the number of pages
234 * actually freed is returned.
237 PLIST_ENTRY current_entry
;
238 PCACHE_SEGMENT current
, last
= NULL
;
239 ULONG PagesPerSegment
;
244 DPRINT("CcRosTrimCache(Target %d)\n", Target
);
248 InitializeListHead(&FreeList
);
250 ExAcquireFastMutex(&ViewLock
);
251 current_entry
= CacheSegmentLRUListHead
.Flink
;
252 while (current_entry
!= &CacheSegmentLRUListHead
&& Target
> 0)
254 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
255 CacheSegmentLRUListEntry
);
256 current_entry
= current_entry
->Flink
;
258 KeAcquireSpinLock(¤t
->Bcb
->BcbLock
, &oldIrql
);
259 if (current
->ReferenceCount
== 0)
261 RemoveEntryList(¤t
->BcbSegmentListEntry
);
262 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
263 RemoveEntryList(¤t
->CacheSegmentListEntry
);
264 RemoveEntryList(¤t
->CacheSegmentLRUListEntry
);
265 InsertHeadList(&FreeList
, ¤t
->BcbSegmentListEntry
);
266 PagesPerSegment
= current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
267 PagesFreed
= min(PagesPerSegment
, Target
);
268 Target
-= PagesFreed
;
269 (*NrFreed
) += PagesFreed
;
273 if (last
!= current
&& current
->MappedCount
> 0 && !current
->Dirty
&& !current
->PageOut
)
278 current
->ReferenceCount
++;
280 current
->PageOut
= TRUE
;
281 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
282 ExReleaseFastMutex(&ViewLock
);
283 for (i
= 0; i
< current
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
; i
++)
286 Page
= MmGetPhysicalAddress((char*)current
->BaseAddress
+ i
* PAGE_SIZE
).QuadPart
>> PAGE_SHIFT
;
287 Status
= MmPageOutPhysicalAddress(Page
);
288 if (!NT_SUCCESS(Status
))
293 ExAcquireFastMutex(&ViewLock
);
294 KeAcquireSpinLock(¤t
->Bcb
->BcbLock
, &oldIrql
);
295 current
->ReferenceCount
--;
296 current
->PageOut
= FALSE
;
297 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
298 current_entry
= ¤t
->CacheSegmentLRUListEntry
;
301 KeReleaseSpinLock(¤t
->Bcb
->BcbLock
, oldIrql
);
304 ExReleaseFastMutex(&ViewLock
);
306 while (!IsListEmpty(&FreeList
))
308 current_entry
= RemoveHeadList(&FreeList
);
309 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
310 BcbSegmentListEntry
);
311 CcRosInternalFreeCacheSegment(current
);
314 DPRINT("CcRosTrimCache() finished\n");
315 return(STATUS_SUCCESS
);
319 CcRosReleaseCacheSegment(PBCB Bcb
,
320 PCACHE_SEGMENT CacheSeg
,
325 BOOLEAN WasDirty
= CacheSeg
->Dirty
;
330 DPRINT("CcReleaseCacheSegment(Bcb %x, CacheSeg %x, Valid %d)\n",
331 Bcb
, CacheSeg
, Valid
);
333 CacheSeg
->Valid
= Valid
;
334 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| Dirty
;
336 ExAcquireFastMutex(&ViewLock
);
337 if (!WasDirty
&& CacheSeg
->Dirty
)
339 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
340 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
342 RemoveEntryList(&CacheSeg
->CacheSegmentLRUListEntry
);
343 InsertTailList(&CacheSegmentLRUListHead
, &CacheSeg
->CacheSegmentLRUListEntry
);
347 CacheSeg
->MappedCount
++;
349 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
350 CacheSeg
->ReferenceCount
--;
351 if (Mapped
&& CacheSeg
->MappedCount
== 1)
353 CacheSeg
->ReferenceCount
++;
355 if (!WasDirty
&& CacheSeg
->Dirty
)
357 CacheSeg
->ReferenceCount
++;
359 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
360 ExReleaseFastMutex(&ViewLock
);
361 ExReleaseFastMutex(&CacheSeg
->Lock
);
363 return(STATUS_SUCCESS
);
367 CcRosLookupCacheSegment(PBCB Bcb
, ULONG FileOffset
)
369 PLIST_ENTRY current_entry
;
370 PCACHE_SEGMENT current
;
375 DPRINT("CcRosLookupCacheSegment(Bcb %x, FileOffset %d)\n", Bcb
, FileOffset
);
377 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
378 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
379 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
381 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
382 BcbSegmentListEntry
);
383 if (current
->FileOffset
<= FileOffset
&&
384 (current
->FileOffset
+ Bcb
->CacheSegmentSize
) > FileOffset
)
386 current
->ReferenceCount
++;
387 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
388 ExAcquireFastMutex(¤t
->Lock
);
391 current_entry
= current_entry
->Flink
;
393 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
398 CcRosMarkDirtyCacheSegment(PBCB Bcb
, ULONG FileOffset
)
400 PCACHE_SEGMENT CacheSeg
;
405 DPRINT("CcRosMarkDirtyCacheSegment(Bcb %x, FileOffset %d)\n", Bcb
, FileOffset
);
407 CacheSeg
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
408 if (CacheSeg
== NULL
)
412 if (!CacheSeg
->Dirty
)
414 ExAcquireFastMutex(&ViewLock
);
415 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
416 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
417 ExReleaseFastMutex(&ViewLock
);
421 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
422 CacheSeg
->ReferenceCount
--;
423 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
427 CacheSeg
->Dirty
= TRUE
;
428 ExReleaseFastMutex(&CacheSeg
->Lock
);
430 return(STATUS_SUCCESS
);
434 CcRosUnmapCacheSegment(PBCB Bcb
, ULONG FileOffset
, BOOLEAN NowDirty
)
436 PCACHE_SEGMENT CacheSeg
;
442 DPRINT("CcRosUnmapCacheSegment(Bcb %x, FileOffset %d, NowDirty %d)\n",
443 Bcb
, FileOffset
, NowDirty
);
445 CacheSeg
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
446 if (CacheSeg
== NULL
)
448 return(STATUS_UNSUCCESSFUL
);
451 WasDirty
= CacheSeg
->Dirty
;
452 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| NowDirty
;
454 CacheSeg
->MappedCount
--;
456 if (!WasDirty
&& NowDirty
)
458 ExAcquireFastMutex(&ViewLock
);
459 InsertTailList(&DirtySegmentListHead
, &CacheSeg
->DirtySegmentListEntry
);
460 DirtyPageCount
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
461 ExReleaseFastMutex(&ViewLock
);
464 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
465 CacheSeg
->ReferenceCount
--;
466 if (!WasDirty
&& NowDirty
)
468 CacheSeg
->ReferenceCount
++;
470 if (CacheSeg
->MappedCount
== 0)
472 CacheSeg
->ReferenceCount
--;
474 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
476 ExReleaseFastMutex(&CacheSeg
->Lock
);
477 return(STATUS_SUCCESS
);
481 CcRosCreateCacheSegment(PBCB Bcb
,
483 PCACHE_SEGMENT
* CacheSeg
)
486 PCACHE_SEGMENT current
;
487 PCACHE_SEGMENT previous
;
488 PLIST_ENTRY current_entry
;
493 ULONG StartingOffset
;
496 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
500 DPRINT("CcRosCreateCacheSegment()\n");
502 BoundaryAddressMultiple
.QuadPart
= 0;
503 if (FileOffset
>= Bcb
->FileSize
.u
.LowPart
)
506 return STATUS_INVALID_PARAMETER
;
509 current
= ExAllocateFromNPagedLookasideList(&CacheSegLookasideList
);
510 current
->Valid
= FALSE
;
511 current
->Dirty
= FALSE
;
512 current
->PageOut
= FALSE
;
513 current
->FileOffset
= ROUND_DOWN(FileOffset
, Bcb
->CacheSegmentSize
);
515 current
->MappedCount
= 0;
516 current
->DirtySegmentListEntry
.Flink
= NULL
;
517 current
->DirtySegmentListEntry
.Blink
= NULL
;
518 current
->ReferenceCount
= 1;
519 ExInitializeFastMutex(¤t
->Lock
);
520 ExAcquireFastMutex(¤t
->Lock
);
521 ExAcquireFastMutex(&ViewLock
);
524 /* There is window between the call to CcRosLookupCacheSegment
525 * and CcRosCreateCacheSegment. We must check if a segment on
526 * the fileoffset exist. If there exist a segment, we release
527 * our new created segment and return the existing one.
529 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
530 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
532 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
534 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
535 BcbSegmentListEntry
);
536 if (current
->FileOffset
<= FileOffset
&&
537 (current
->FileOffset
+ Bcb
->CacheSegmentSize
) > FileOffset
)
539 current
->ReferenceCount
++;
540 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
541 ExReleaseFastMutex(&(*CacheSeg
)->Lock
);
542 ExReleaseFastMutex(&ViewLock
);
543 ExFreeToNPagedLookasideList(&CacheSegLookasideList
, *CacheSeg
);
545 ExAcquireFastMutex(¤t
->Lock
);
546 return STATUS_SUCCESS
;
548 if (current
->FileOffset
< FileOffset
)
550 if (previous
== NULL
)
556 if (previous
->FileOffset
< current
->FileOffset
)
562 current_entry
= current_entry
->Flink
;
564 /* There was no existing segment. */
568 InsertHeadList(&previous
->BcbSegmentListEntry
, ¤t
->BcbSegmentListEntry
);
572 InsertHeadList(&Bcb
->BcbSegmentListHead
, ¤t
->BcbSegmentListEntry
);
574 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
575 InsertTailList(&CacheSegmentListHead
, ¤t
->CacheSegmentListEntry
);
576 InsertTailList(&CacheSegmentLRUListHead
, ¤t
->CacheSegmentLRUListEntry
);
577 ExReleaseFastMutex(&ViewLock
);
579 KeAcquireSpinLock(&CiCacheSegMappingRegionLock
, &oldIrql
);
581 StartingOffset
= RtlFindClearBitsAndSet(&CiCacheSegMappingRegionAllocMap
, Bcb
->CacheSegmentSize
/ PAGE_SIZE
, CiCacheSegMappingRegionHint
);
583 if (StartingOffset
== 0xffffffff)
585 DPRINT1("Out of CacheSeg mapping space\n");
589 current
->BaseAddress
= CiCacheSegMappingRegionBase
+ StartingOffset
* PAGE_SIZE
;
591 if (CiCacheSegMappingRegionHint
== StartingOffset
)
593 CiCacheSegMappingRegionHint
+= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
596 KeReleaseSpinLock(&CiCacheSegMappingRegionLock
, oldIrql
);
598 MmLockAddressSpace(MmGetKernelAddressSpace());
599 current
->BaseAddress
= NULL
;
600 Status
= MmCreateMemoryArea(NULL
,
601 MmGetKernelAddressSpace(),
602 MEMORY_AREA_CACHE_SEGMENT
,
603 ¤t
->BaseAddress
,
604 Bcb
->CacheSegmentSize
,
606 (PMEMORY_AREA
*)¤t
->MemoryArea
,
609 BoundaryAddressMultiple
);
610 MmUnlockAddressSpace(MmGetKernelAddressSpace());
611 if (!NT_SUCCESS(Status
))
616 Pfn
= alloca(sizeof(PFN_TYPE
) * (Bcb
->CacheSegmentSize
/ PAGE_SIZE
));
617 for (i
= 0; i
< (Bcb
->CacheSegmentSize
/ PAGE_SIZE
); i
++)
619 Status
= MmRequestPageMemoryConsumer(MC_CACHE
, TRUE
, &Pfn
[i
]);
620 if (!NT_SUCCESS(Status
))
625 Status
= MmCreateVirtualMapping(NULL
,
626 current
->BaseAddress
,
629 Bcb
->CacheSegmentSize
/ PAGE_SIZE
);
630 if (!NT_SUCCESS(Status
))
634 return(STATUS_SUCCESS
);
638 CcRosGetCacheSegmentChain(PBCB Bcb
,
641 PCACHE_SEGMENT
* CacheSeg
)
643 PCACHE_SEGMENT current
;
645 PCACHE_SEGMENT
* CacheSegList
;
646 PCACHE_SEGMENT Previous
= NULL
;
650 DPRINT("CcRosGetCacheSegmentChain()\n");
652 Length
= ROUND_UP(Length
, Bcb
->CacheSegmentSize
);
654 #if defined(__GNUC__)
655 CacheSegList
= alloca(sizeof(PCACHE_SEGMENT
) *
656 (Length
/ Bcb
->CacheSegmentSize
));
657 #elif defined(_MSC_VER)
658 CacheSegList
= _alloca(sizeof(PCACHE_SEGMENT
) *
659 (Length
/ Bcb
->CacheSegmentSize
));
661 #error Unknown compiler for alloca intrinsic stack allocation "function"
665 * Look for a cache segment already mapping the same data.
667 for (i
= 0; i
< (Length
/ Bcb
->CacheSegmentSize
); i
++)
669 ULONG CurrentOffset
= FileOffset
+ (i
* Bcb
->CacheSegmentSize
);
670 current
= CcRosLookupCacheSegment(Bcb
, CurrentOffset
);
673 CacheSegList
[i
] = current
;
677 CcRosCreateCacheSegment(Bcb
, CurrentOffset
, ¤t
);
678 CacheSegList
[i
] = current
;
682 for (i
= 0; i
< (Length
/ Bcb
->CacheSegmentSize
); i
++)
686 *CacheSeg
= CacheSegList
[i
];
687 Previous
= CacheSegList
[i
];
691 Previous
->NextInChain
= CacheSegList
[i
];
692 Previous
= CacheSegList
[i
];
695 Previous
->NextInChain
= NULL
;
697 return(STATUS_SUCCESS
);
701 CcRosGetCacheSegment(PBCB Bcb
,
706 PCACHE_SEGMENT
* CacheSeg
)
708 PCACHE_SEGMENT current
;
713 DPRINT("CcRosGetCacheSegment()\n");
716 * Look for a cache segment already mapping the same data.
718 current
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
722 * Otherwise create a new segment.
724 Status
= CcRosCreateCacheSegment(Bcb
, FileOffset
, ¤t
);
725 if (!NT_SUCCESS(Status
))
731 * Return information about the segment to the caller.
733 *UptoDate
= current
->Valid
;
734 *BaseAddress
= current
->BaseAddress
;
735 DPRINT("*BaseAddress 0x%.8X\n", *BaseAddress
);
737 *BaseOffset
= current
->FileOffset
;
738 return(STATUS_SUCCESS
);
742 CcRosRequestCacheSegment(PBCB Bcb
,
746 PCACHE_SEGMENT
* CacheSeg
)
748 * FUNCTION: Request a page mapping for a BCB
755 if ((FileOffset
% Bcb
->CacheSegmentSize
) != 0)
757 CPRINT("Bad fileoffset %x should be multiple of %x",
758 FileOffset
, Bcb
->CacheSegmentSize
);
762 return(CcRosGetCacheSegment(Bcb
,
772 CcFreeCachePage(PVOID Context
, MEMORY_AREA
* MemoryArea
, PVOID Address
,
773 PFN_TYPE Page
, SWAPENTRY SwapEntry
, BOOLEAN Dirty
)
775 assert(SwapEntry
== 0);
778 MmReleasePageMemoryConsumer(MC_CACHE
, Page
);
783 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg
)
785 * FUNCTION: Releases a cache segment associated with a BCB
795 DPRINT("Freeing cache segment %x\n", CacheSeg
);
797 RegionSize
= CacheSeg
->Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
799 /* Unmap all the pages. */
800 for (i
= 0; i
< RegionSize
; i
++)
802 MmDeleteVirtualMapping(NULL
,
803 CacheSeg
->BaseAddress
+ (i
* PAGE_SIZE
),
807 MmReleasePageMemoryConsumer(MC_CACHE
, Page
);
810 KeAcquireSpinLock(&CiCacheSegMappingRegionLock
, &oldIrql
);
811 /* Deallocate all the pages used. */
812 Base
= (ULONG
)(CacheSeg
->BaseAddress
- CiCacheSegMappingRegionBase
) / PAGE_SIZE
;
814 RtlClearBits(&CiCacheSegMappingRegionAllocMap
, Base
, RegionSize
);
816 CiCacheSegMappingRegionHint
= min (CiCacheSegMappingRegionHint
, Base
);
818 KeReleaseSpinLock(&CiCacheSegMappingRegionLock
, oldIrql
);
820 MmLockAddressSpace(MmGetKernelAddressSpace());
821 MmFreeMemoryArea(MmGetKernelAddressSpace(),
822 CacheSeg
->BaseAddress
,
823 CacheSeg
->Bcb
->CacheSegmentSize
,
826 MmUnlockAddressSpace(MmGetKernelAddressSpace());
828 ExFreeToNPagedLookasideList(&CacheSegLookasideList
, CacheSeg
);
829 return(STATUS_SUCCESS
);
833 CcRosFreeCacheSegment(PBCB Bcb
, PCACHE_SEGMENT CacheSeg
)
840 DPRINT("CcRosFreeCacheSegment(Bcb %x, CacheSeg %x)\n",
843 ExAcquireFastMutex(&ViewLock
);
844 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
845 RemoveEntryList(&CacheSeg
->BcbSegmentListEntry
);
846 RemoveEntryList(&CacheSeg
->CacheSegmentListEntry
);
847 RemoveEntryList(&CacheSeg
->CacheSegmentLRUListEntry
);
850 RemoveEntryList(&CacheSeg
->DirtySegmentListEntry
);
851 DirtyPageCount
-= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
854 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
855 ExReleaseFastMutex(&ViewLock
);
857 Status
= CcRosInternalFreeCacheSegment(CacheSeg
);
865 CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointers
,
866 IN PLARGE_INTEGER FileOffset OPTIONAL
,
868 OUT PIO_STATUS_BLOCK IoStatus
)
871 LARGE_INTEGER Offset
;
872 PCACHE_SEGMENT current
;
876 DPRINT("CcFlushCache(SectionObjectPointers %x, FileOffset %x, Length %d, IoStatus %x)\n",
877 SectionObjectPointers
, FileOffset
, Length
, IoStatus
);
879 if (SectionObjectPointers
&& SectionObjectPointers
->SharedCacheMap
)
881 Bcb
= (PBCB
)SectionObjectPointers
->SharedCacheMap
;
885 Offset
= *FileOffset
;
889 #if defined(__GNUC__)
890 Offset
.QuadPart
= 0LL;
894 Length
= Bcb
->FileSize
.u
.LowPart
;
899 IoStatus
->Status
= STATUS_SUCCESS
;
900 IoStatus
->Information
= 0;
905 current
= CcRosLookupCacheSegment (Bcb
, Offset
.u
.LowPart
);
910 Status
= CcRosFlushCacheSegment(current
);
911 if (!NT_SUCCESS(Status
) && IoStatus
!= NULL
)
913 IoStatus
->Status
= Status
;
916 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
917 ExReleaseFastMutex(¤t
->Lock
);
918 current
->ReferenceCount
--;
919 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
922 Offset
.QuadPart
+= Bcb
->CacheSegmentSize
;
923 if (Length
> Bcb
->CacheSegmentSize
)
925 Length
-= Bcb
->CacheSegmentSize
;
937 IoStatus
->Status
= STATUS_INVALID_PARAMETER
;
943 CcRosDeleteFileCache(PFILE_OBJECT FileObject
, PBCB Bcb
)
945 * FUNCTION: Releases the BCB associated with a file object
948 PLIST_ENTRY current_entry
;
949 PCACHE_SEGMENT current
;
957 ExReleaseFastMutex(&ViewLock
);
959 CcFlushCache(FileObject
->SectionObjectPointer
, NULL
, 0, NULL
);
961 ExAcquireFastMutex(&ViewLock
);
963 if (Bcb
->RefCount
== 0)
965 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
967 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
968 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
971 FileObject
->SectionObjectPointer
->SharedCacheMap
= NULL
;
974 * Release all cache segments.
976 InitializeListHead(&FreeList
);
977 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
978 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
979 while (!IsListEmpty(&Bcb
->BcbSegmentListHead
))
981 current_entry
= RemoveTailList(&Bcb
->BcbSegmentListHead
);
982 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, BcbSegmentListEntry
);
983 RemoveEntryList(¤t
->CacheSegmentListEntry
);
984 RemoveEntryList(¤t
->CacheSegmentLRUListEntry
);
987 RemoveEntryList(¤t
->DirtySegmentListEntry
);
988 DirtyPageCount
-= Bcb
->CacheSegmentSize
/ PAGE_SIZE
;
989 DPRINT1("Freeing dirty segment\n");
991 InsertHeadList(&FreeList
, ¤t
->BcbSegmentListEntry
);
993 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
995 ExReleaseFastMutex(&ViewLock
);
996 ObDereferenceObject (Bcb
->FileObject
);
998 while (!IsListEmpty(&FreeList
))
1000 current_entry
= RemoveTailList(&FreeList
);
1001 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, BcbSegmentListEntry
);
1002 Status
= CcRosInternalFreeCacheSegment(current
);
1004 ExFreeToNPagedLookasideList(&BcbLookasideList
, Bcb
);
1005 ExAcquireFastMutex(&ViewLock
);
1007 return(STATUS_SUCCESS
);
1010 VOID
CcRosReferenceCache(PFILE_OBJECT FileObject
)
1013 ExAcquireFastMutex(&ViewLock
);
1014 Bcb
= (PBCB
)FileObject
->SectionObjectPointer
->SharedCacheMap
;
1016 if (Bcb
->RefCount
== 0)
1018 assert(Bcb
->BcbRemoveListEntry
.Flink
!= NULL
);
1019 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1020 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1025 assert(Bcb
->BcbRemoveListEntry
.Flink
== NULL
);
1028 ExReleaseFastMutex(&ViewLock
);
1031 VOID
CcRosSetRemoveOnClose(PSECTION_OBJECT_POINTERS SectionObjectPointer
)
1034 DPRINT("CcRosSetRemoveOnClose()\n");
1035 ExAcquireFastMutex(&ViewLock
);
1036 Bcb
= (PBCB
)SectionObjectPointer
->SharedCacheMap
;
1039 Bcb
->RemoveOnClose
= TRUE
;
1040 if (Bcb
->RefCount
== 0)
1042 CcRosDeleteFileCache(Bcb
->FileObject
, Bcb
);
1045 ExReleaseFastMutex(&ViewLock
);
1049 VOID
CcRosDereferenceCache(PFILE_OBJECT FileObject
)
1052 ExAcquireFastMutex(&ViewLock
);
1053 Bcb
= (PBCB
)FileObject
->SectionObjectPointer
->SharedCacheMap
;
1055 if (Bcb
->RefCount
> 0)
1058 if (Bcb
->RefCount
== 0)
1060 MmFreeSectionSegments(Bcb
->FileObject
);
1061 if (Bcb
->RemoveOnClose
)
1063 CcRosDeleteFileCache(FileObject
, Bcb
);
1067 Bcb
->TimeStamp
= CcTimeStamp
;
1068 InsertHeadList(&ClosedListHead
, &Bcb
->BcbRemoveListEntry
);
1072 ExReleaseFastMutex(&ViewLock
);
1076 CcRosReleaseFileCache(PFILE_OBJECT FileObject
)
1078 * FUNCTION: Called by the file system when a handle to a file object
1084 ExAcquireFastMutex(&ViewLock
);
1086 if (FileObject
->SectionObjectPointer
->SharedCacheMap
!= NULL
)
1088 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1089 if (FileObject
->PrivateCacheMap
!= NULL
)
1091 FileObject
->PrivateCacheMap
= NULL
;
1092 if (Bcb
->RefCount
> 0)
1095 if (Bcb
->RefCount
== 0)
1097 if (Bcb
->RemoveOnClose
)
1099 CcRosDeleteFileCache(FileObject
, Bcb
);
1103 Bcb
->TimeStamp
= CcTimeStamp
;
1104 InsertHeadList(&ClosedListHead
, &Bcb
->BcbRemoveListEntry
);
1110 ExReleaseFastMutex(&ViewLock
);
1111 return(STATUS_SUCCESS
);
1115 CcTryToInitializeFileCache(PFILE_OBJECT FileObject
)
1120 ExAcquireFastMutex(&ViewLock
);
1122 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1125 Status
= STATUS_UNSUCCESSFUL
;
1129 if (FileObject
->PrivateCacheMap
== NULL
)
1131 FileObject
->PrivateCacheMap
= Bcb
;
1134 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
1136 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1137 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1139 Status
= STATUS_SUCCESS
;
1141 ExReleaseFastMutex(&ViewLock
);
1148 CcRosInitializeFileCache(PFILE_OBJECT FileObject
,
1149 ULONG CacheSegmentSize
)
1151 * FUNCTION: Initializes a BCB for a file object
1155 DPRINT("CcRosInitializeFileCache(FileObject %x, *Bcb %x, CacheSegmentSize %d)\n",
1156 FileObject
, Bcb
, CacheSegmentSize
);
1158 ExAcquireFastMutex(&ViewLock
);
1160 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
1163 Bcb
= ExAllocateFromNPagedLookasideList(&BcbLookasideList
);
1166 ExReleaseFastMutex(&ViewLock
);
1167 return(STATUS_UNSUCCESSFUL
);
1169 memset(Bcb
, 0, sizeof(BCB
));
1170 ObReferenceObjectByPointer(FileObject
,
1174 Bcb
->FileObject
= FileObject
;
1175 Bcb
->CacheSegmentSize
= CacheSegmentSize
;
1176 if (FileObject
->FsContext
)
1178 Bcb
->AllocationSize
=
1179 ((PFSRTL_COMMON_FCB_HEADER
)FileObject
->FsContext
)->AllocationSize
;
1181 ((PFSRTL_COMMON_FCB_HEADER
)FileObject
->FsContext
)->FileSize
;
1183 KeInitializeSpinLock(&Bcb
->BcbLock
);
1184 InitializeListHead(&Bcb
->BcbSegmentListHead
);
1185 FileObject
->SectionObjectPointer
->SharedCacheMap
= Bcb
;
1187 if (FileObject
->PrivateCacheMap
== NULL
)
1189 FileObject
->PrivateCacheMap
= Bcb
;
1192 if (Bcb
->BcbRemoveListEntry
.Flink
!= NULL
)
1194 RemoveEntryList(&Bcb
->BcbRemoveListEntry
);
1195 Bcb
->BcbRemoveListEntry
.Flink
= NULL
;
1197 ExReleaseFastMutex(&ViewLock
);
1199 return(STATUS_SUCCESS
);
1205 PFILE_OBJECT STDCALL
1206 CcGetFileObjectFromSectionPtrs(IN PSECTION_OBJECT_POINTERS SectionObjectPointers
)
1209 if (SectionObjectPointers
&& SectionObjectPointers
->SharedCacheMap
)
1211 Bcb
= (PBCB
)SectionObjectPointers
->SharedCacheMap
;
1213 return Bcb
->FileObject
;
1219 CmLazyCloseThreadMain(PVOID Ignored
)
1221 LARGE_INTEGER Timeout
;
1222 PLIST_ENTRY current_entry
;
1224 ULONG RemoveTimeStamp
;
1227 KeQuerySystemTime (&Timeout
);
1231 #if defined(__GNUC__)
1232 Timeout
.QuadPart
+= 100000000LL; // 10sec
1234 Timeout
.QuadPart
+= 100000000; // 10sec
1236 Status
= KeWaitForSingleObject(&LazyCloseThreadEvent
,
1242 DPRINT("LazyCloseThreadMain %d\n", CcTimeStamp
);
1244 if (!NT_SUCCESS(Status
))
1246 DbgPrint("LazyCloseThread: Wait failed\n");
1250 if (LazyCloseThreadShouldTerminate
)
1252 DbgPrint("LazyCloseThread: Terminating\n");
1256 ExAcquireFastMutex(&ViewLock
);
1258 if (CcTimeStamp
>= 30)
1260 RemoveTimeStamp
= CcTimeStamp
- 30; /* 5min = 10sec * 30 */
1261 while (!IsListEmpty(&ClosedListHead
))
1263 current_entry
= ClosedListHead
.Blink
;
1264 current
= CONTAINING_RECORD(current_entry
, BCB
, BcbRemoveListEntry
);
1265 if (current
->TimeStamp
>= RemoveTimeStamp
)
1269 CcRosDeleteFileCache(current
->FileObject
, current
);
1272 ExReleaseFastMutex(&ViewLock
);
1282 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
1287 DPRINT("CcInitView()\n");
1289 BoundaryAddressMultiple
.QuadPart
= 0;
1290 CiCacheSegMappingRegionHint
= 0;
1291 CiCacheSegMappingRegionBase
= NULL
;
1293 MmLockAddressSpace(MmGetKernelAddressSpace());
1295 Status
= MmCreateMemoryArea(NULL
,
1296 MmGetKernelAddressSpace(),
1297 MEMORY_AREA_CACHE_SEGMENT
,
1298 &CiCacheSegMappingRegionBase
,
1299 CI_CACHESEG_MAPPING_REGION_SIZE
,
1304 BoundaryAddressMultiple
);
1305 MmUnlockAddressSpace(MmGetKernelAddressSpace());
1306 if (!NT_SUCCESS(Status
))
1311 Buffer
= ExAllocatePool(NonPagedPool
, CI_CACHESEG_MAPPING_REGION_SIZE
/ (PAGE_SIZE
* 8));
1313 RtlInitializeBitMap(&CiCacheSegMappingRegionAllocMap
, Buffer
, CI_CACHESEG_MAPPING_REGION_SIZE
/ PAGE_SIZE
);
1314 RtlClearAllBits(&CiCacheSegMappingRegionAllocMap
);
1316 KeInitializeSpinLock(&CiCacheSegMappingRegionLock
);
1318 InitializeListHead(&CacheSegmentListHead
);
1319 InitializeListHead(&DirtySegmentListHead
);
1320 InitializeListHead(&CacheSegmentLRUListHead
);
1321 InitializeListHead(&ClosedListHead
);
1322 ExInitializeFastMutex(&ViewLock
);
1323 ExInitializeNPagedLookasideList (&iBcbLookasideList
,
1327 sizeof(INTERNAL_BCB
),
1330 ExInitializeNPagedLookasideList (&BcbLookasideList
,
1337 ExInitializeNPagedLookasideList (&CacheSegLookasideList
,
1341 sizeof(CACHE_SEGMENT
),
1345 MmInitializeMemoryConsumer(MC_CACHE
, CcRosTrimCache
);
1347 CcInitCacheZeroPage();
1350 LazyCloseThreadShouldTerminate
= FALSE
;
1351 KeInitializeEvent (&LazyCloseThreadEvent
, SynchronizationEvent
, FALSE
);
1352 Status
= PsCreateSystemThread(&LazyCloseThreadHandle
,
1357 (PKSTART_ROUTINE
)CmLazyCloseThreadMain
,
1359 if (NT_SUCCESS(Status
))
1361 Priority
= LOW_REALTIME_PRIORITY
;
1362 NtSetInformationThread(LazyCloseThreadHandle
,