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 ******************************************************************/
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);
136 CcRosFlushDirtyPages(ULONG Target, PULONG Count)
138 PLIST_ENTRY current_entry;
139 PCACHE_SEGMENT current;
140 ULONG PagesPerSegment;
143 static ULONG WriteCount[4] = {0, 0, 0, 0};
146 DPRINT("CcRosFlushDirtyPages(Target %d)\n", Target);
150 ExAcquireFastMutex(&ViewLock);
152 WriteCount[0] = WriteCount[1];
153 WriteCount[1] = WriteCount[2];
154 WriteCount[2] = WriteCount[3];
157 NewTarget = WriteCount[0] + WriteCount[1] + WriteCount[2];
159 if (NewTarget < DirtyPageCount)
161 NewTarget = (DirtyPageCount - NewTarget + 3) / 4;
162 WriteCount[0] += NewTarget;
163 WriteCount[1] += NewTarget;
164 WriteCount[2] += NewTarget;
165 WriteCount[3] += NewTarget;
168 NewTarget = WriteCount[0];
170 Target = max(NewTarget, Target);
172 current_entry = DirtySegmentListHead.Flink;
173 if (current_entry == &DirtySegmentListHead)
175 DPRINT("No Dirty pages\n");
177 while (current_entry != &DirtySegmentListHead && Target > 0)
179 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
180 DirtySegmentListEntry);
181 current_entry = current_entry->Flink;
182 Locked = ExTryToAcquireFastMutex(¤t->Lock);
187 assert(current->Dirty);
188 if (current->ReferenceCount > 1)
190 ExReleaseFastMutex(¤t->Lock);
193 ExReleaseFastMutex(&ViewLock);
194 PagesPerSegment = current->Bcb->CacheSegmentSize / PAGE_SIZE;
195 Status = CcRosFlushCacheSegment(current);
196 ExReleaseFastMutex(¤t->Lock);
197 if (!NT_SUCCESS(Status) && (Status != STATUS_END_OF_FILE))
199 DPRINT1("CC: Failed to flush cache segment.\n");
203 (*Count) += PagesPerSegment;
204 Target -= PagesPerSegment;
206 ExAcquireFastMutex(&ViewLock);
207 current_entry = DirtySegmentListHead.Flink;
209 if (*Count < NewTarget)
211 WriteCount[1] += (NewTarget - *Count);
213 ExReleaseFastMutex(&ViewLock);
214 DPRINT("CcRosFlushDirtyPages() finished\n");
216 return(STATUS_SUCCESS);
220 CcRosTrimCache(ULONG Target, ULONG Priority, PULONG NrFreed)
222 * FUNCTION: Try to free some memory from the file cache.
224 * Target - The number of pages to be freed.
225 * Priority - The priority of free (currently unused).
226 * NrFreed - Points to a variable where the number of pages
227 * actually freed is returned.
230 PLIST_ENTRY current_entry;
231 PCACHE_SEGMENT current, last = NULL;
232 ULONG PagesPerSegment;
237 DPRINT("CcRosTrimCache(Target %d)\n", Target);
241 InitializeListHead(&FreeList);
243 ExAcquireFastMutex(&ViewLock);
244 current_entry = CacheSegmentLRUListHead.Flink;
245 while (current_entry != &CacheSegmentLRUListHead && Target > 0)
247 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
248 CacheSegmentLRUListEntry);
249 current_entry = current_entry->Flink;
251 KeAcquireSpinLock(¤t->Bcb->BcbLock, &oldIrql);
252 if (current->ReferenceCount == 0)
254 RemoveEntryList(¤t->BcbSegmentListEntry);
255 KeReleaseSpinLock(¤t->Bcb->BcbLock, oldIrql);
256 RemoveEntryList(¤t->CacheSegmentListEntry);
257 RemoveEntryList(¤t->CacheSegmentLRUListEntry);
258 InsertHeadList(&FreeList, ¤t->BcbSegmentListEntry);
259 PagesPerSegment = current->Bcb->CacheSegmentSize / PAGE_SIZE;
260 PagesFreed = min(PagesPerSegment, Target);
261 Target -= PagesFreed;
262 (*NrFreed) += PagesFreed;
266 if (last != current && current->MappedCount > 0 && !current->Dirty)
271 current->ReferenceCount++;
273 KeReleaseSpinLock(¤t->Bcb->BcbLock, oldIrql);
274 ExReleaseFastMutex(&ViewLock);
275 for (i = 0; i < current->Bcb->CacheSegmentSize / PAGE_SIZE; i++)
277 PHYSICAL_ADDRESS Page;
278 Page = MmGetPhysicalAddress(current->BaseAddress + i * PAGE_SIZE);
279 Status = MmPageOutPhysicalAddress(Page);
280 if (!NT_SUCCESS(Status))
285 ExAcquireFastMutex(&ViewLock);
286 KeAcquireSpinLock(¤t->Bcb->BcbLock, &oldIrql);
287 current->ReferenceCount--;
288 KeReleaseSpinLock(¤t->Bcb->BcbLock, oldIrql);
289 current_entry = ¤t->CacheSegmentLRUListEntry;
292 KeReleaseSpinLock(¤t->Bcb->BcbLock, oldIrql);
295 ExReleaseFastMutex(&ViewLock);
297 while (!IsListEmpty(&FreeList))
299 current_entry = RemoveHeadList(&FreeList);
300 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
301 BcbSegmentListEntry);
302 CcRosInternalFreeCacheSegment(current);
305 DPRINT("CcRosTrimCache() finished\n");
306 return(STATUS_SUCCESS);
310 CcRosReleaseCacheSegment(PBCB Bcb,
311 PCACHE_SEGMENT CacheSeg,
316 BOOLEAN WasDirty = CacheSeg->Dirty;
321 DPRINT("CcReleaseCacheSegment(Bcb %x, CacheSeg %x, Valid %d)\n",
322 Bcb, CacheSeg, Valid);
324 CacheSeg->Valid = Valid;
325 CacheSeg->Dirty = CacheSeg->Dirty || Dirty;
327 ExAcquireFastMutex(&ViewLock);
328 if (!WasDirty && CacheSeg->Dirty)
330 InsertTailList(&DirtySegmentListHead, &CacheSeg->DirtySegmentListEntry);
331 DirtyPageCount += Bcb->CacheSegmentSize / PAGE_SIZE;
333 RemoveEntryList(&CacheSeg->CacheSegmentLRUListEntry);
334 InsertTailList(&CacheSegmentLRUListHead, &CacheSeg->CacheSegmentLRUListEntry);
338 CacheSeg->MappedCount++;
340 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
341 CacheSeg->ReferenceCount--;
342 if (Mapped && CacheSeg->MappedCount == 1)
344 CacheSeg->ReferenceCount++;
346 if (!WasDirty && CacheSeg->Dirty)
348 CacheSeg->ReferenceCount++;
350 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
351 ExReleaseFastMutex(&ViewLock);
352 ExReleaseFastMutex(&CacheSeg->Lock);
354 return(STATUS_SUCCESS);
358 CcRosLookupCacheSegment(PBCB Bcb, ULONG FileOffset)
360 PLIST_ENTRY current_entry;
361 PCACHE_SEGMENT current;
366 DPRINT("CcRosLookupCacheSegment(Bcb %x, FileOffset %d)\n", Bcb, FileOffset);
368 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
369 current_entry = Bcb->BcbSegmentListHead.Flink;
370 while (current_entry != &Bcb->BcbSegmentListHead)
372 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
373 BcbSegmentListEntry);
374 if (current->FileOffset <= FileOffset &&
375 (current->FileOffset + Bcb->CacheSegmentSize) > FileOffset)
377 current->ReferenceCount++;
378 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
379 ExAcquireFastMutex(¤t->Lock);
382 current_entry = current_entry->Flink;
384 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
389 CcRosMarkDirtyCacheSegment(PBCB Bcb, ULONG FileOffset)
391 PCACHE_SEGMENT CacheSeg;
396 DPRINT("CcRosMarkDirtyCacheSegment(Bcb %x, FileOffset %d)\n", Bcb, FileOffset);
398 CacheSeg = CcRosLookupCacheSegment(Bcb, FileOffset);
399 if (CacheSeg == NULL)
403 if (!CacheSeg->Dirty)
405 ExAcquireFastMutex(&ViewLock);
406 InsertTailList(&DirtySegmentListHead, &CacheSeg->DirtySegmentListEntry);
407 DirtyPageCount += Bcb->CacheSegmentSize / PAGE_SIZE;
408 ExReleaseFastMutex(&ViewLock);
412 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
413 CacheSeg->ReferenceCount--;
414 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
418 CacheSeg->Dirty = TRUE;
419 ExReleaseFastMutex(&CacheSeg->Lock);
421 return(STATUS_SUCCESS);
425 CcRosUnmapCacheSegment(PBCB Bcb, ULONG FileOffset, BOOLEAN NowDirty)
427 PCACHE_SEGMENT CacheSeg;
433 DPRINT("CcRosUnmapCacheSegment(Bcb %x, FileOffset %d, NowDirty %d)\n",
434 Bcb, FileOffset, NowDirty);
436 CacheSeg = CcRosLookupCacheSegment(Bcb, FileOffset);
437 if (CacheSeg == NULL)
439 return(STATUS_UNSUCCESSFUL);
442 WasDirty = CacheSeg->Dirty;
443 CacheSeg->Dirty = CacheSeg->Dirty || NowDirty;
445 CacheSeg->MappedCount--;
447 if (!WasDirty && NowDirty)
449 ExAcquireFastMutex(&ViewLock);
450 InsertTailList(&DirtySegmentListHead, &CacheSeg->DirtySegmentListEntry);
451 DirtyPageCount += Bcb->CacheSegmentSize / PAGE_SIZE;
452 ExReleaseFastMutex(&ViewLock);
455 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
456 CacheSeg->ReferenceCount--;
457 if (!WasDirty && NowDirty)
459 CacheSeg->ReferenceCount++;
461 if (CacheSeg->MappedCount == 0)
463 CacheSeg->ReferenceCount--;
465 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
467 ExReleaseFastMutex(&CacheSeg->Lock);
468 return(STATUS_SUCCESS);
472 CcRosCreateCacheSegment(PBCB Bcb,
474 PCACHE_SEGMENT* CacheSeg)
477 PCACHE_SEGMENT current;
478 PCACHE_SEGMENT previous;
479 PLIST_ENTRY current_entry;
483 ULONG StartingOffset;
488 DPRINT("CcRosCreateCacheSegment()\n");
490 if (FileOffset >= Bcb->FileSize.u.LowPart)
493 return STATUS_INVALID_PARAMETER;
496 current = ExAllocateFromNPagedLookasideList(&CacheSegLookasideList);
497 current->Valid = FALSE;
498 current->Dirty = FALSE;
499 current->FileOffset = ROUND_DOWN(FileOffset, Bcb->CacheSegmentSize);
501 current->MappedCount = 0;
502 current->DirtySegmentListEntry.Flink = NULL;
503 current->DirtySegmentListEntry.Blink = NULL;
504 current->ReferenceCount = 1;
505 ExInitializeFastMutex(¤t->Lock);
506 ExAcquireFastMutex(¤t->Lock);
507 ExAcquireFastMutex(&ViewLock);
510 /* There is window between the call to CcRosLookupCacheSegment
511 * and CcRosCreateCacheSegment. We must check if a segment on
512 * the fileoffset exist. If there exist a segment, we release
513 * our new created segment and return the existing one.
515 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
516 current_entry = Bcb->BcbSegmentListHead.Flink;
518 while (current_entry != &Bcb->BcbSegmentListHead)
520 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
521 BcbSegmentListEntry);
522 if (current->FileOffset <= FileOffset &&
523 (current->FileOffset + Bcb->CacheSegmentSize) > FileOffset)
525 current->ReferenceCount++;
526 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
527 ExReleaseFastMutex(&(*CacheSeg)->Lock);
528 ExReleaseFastMutex(&ViewLock);
529 ExFreeToNPagedLookasideList(&CacheSegLookasideList, *CacheSeg);
531 ExAcquireFastMutex(¤t->Lock);
532 return STATUS_SUCCESS;
534 if (current->FileOffset < FileOffset)
536 if (previous == NULL)
542 if (previous->FileOffset < current->FileOffset)
548 current_entry = current_entry->Flink;
550 /* There was no existing segment. */
554 InsertHeadList(&previous->BcbSegmentListEntry, ¤t->BcbSegmentListEntry);
558 InsertHeadList(&Bcb->BcbSegmentListHead, ¤t->BcbSegmentListEntry);
560 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
561 InsertTailList(&CacheSegmentListHead, ¤t->CacheSegmentListEntry);
562 InsertTailList(&CacheSegmentLRUListHead, ¤t->CacheSegmentLRUListEntry);
563 ExReleaseFastMutex(&ViewLock);
565 KeAcquireSpinLock(&CiCacheSegMappingRegionLock, &oldIrql);
567 StartingOffset = RtlFindClearBitsAndSet(&CiCacheSegMappingRegionAllocMap, Bcb->CacheSegmentSize / PAGE_SIZE, CiCacheSegMappingRegionHint);
569 if (StartingOffset == 0xffffffff)
571 DPRINT1("Out of CacheSeg mapping space\n");
575 current->BaseAddress = CiCacheSegMappingRegionBase + StartingOffset * PAGE_SIZE;
577 if (CiCacheSegMappingRegionHint == StartingOffset)
579 CiCacheSegMappingRegionHint += Bcb->CacheSegmentSize / PAGE_SIZE;
582 KeReleaseSpinLock(&CiCacheSegMappingRegionLock, oldIrql);
584 MmLockAddressSpace(MmGetKernelAddressSpace());
585 current->BaseAddress = NULL;
586 Status = MmCreateMemoryArea(NULL,
587 MmGetKernelAddressSpace(),
588 MEMORY_AREA_CACHE_SEGMENT,
589 ¤t->BaseAddress,
590 Bcb->CacheSegmentSize,
592 (PMEMORY_AREA*)¤t->MemoryArea,
595 MmUnlockAddressSpace(MmGetKernelAddressSpace());
596 if (!NT_SUCCESS(Status))
601 for (i = 0; i < (Bcb->CacheSegmentSize / PAGE_SIZE); i++)
603 PHYSICAL_ADDRESS Page;
605 Status = MmRequestPageMemoryConsumer(MC_CACHE, TRUE, &Page);
606 if (!NT_SUCCESS(Status))
611 Status = MmCreateVirtualMapping(NULL,
612 current->BaseAddress + (i * PAGE_SIZE),
616 if (!NT_SUCCESS(Status))
621 return(STATUS_SUCCESS);
625 CcRosGetCacheSegmentChain(PBCB Bcb,
628 PCACHE_SEGMENT* CacheSeg)
630 PCACHE_SEGMENT current;
632 PCACHE_SEGMENT* CacheSegList;
633 PCACHE_SEGMENT Previous = NULL;
637 DPRINT("CcRosGetCacheSegmentChain()\n");
639 Length = ROUND_UP(Length, Bcb->CacheSegmentSize);
641 CacheSegList = alloca(sizeof(PCACHE_SEGMENT) *
642 (Length / Bcb->CacheSegmentSize));
645 * Look for a cache segment already mapping the same data.
647 for (i = 0; i < (Length / Bcb->CacheSegmentSize); i++)
649 ULONG CurrentOffset = FileOffset + (i * Bcb->CacheSegmentSize);
650 current = CcRosLookupCacheSegment(Bcb, CurrentOffset);
653 CacheSegList[i] = current;
657 CcRosCreateCacheSegment(Bcb, CurrentOffset, ¤t);
658 CacheSegList[i] = current;
662 for (i = 0; i < (Length / Bcb->CacheSegmentSize); i++)
666 *CacheSeg = CacheSegList[i];
667 Previous = CacheSegList[i];
671 Previous->NextInChain = CacheSegList[i];
672 Previous = CacheSegList[i];
675 Previous->NextInChain = NULL;
677 return(STATUS_SUCCESS);
681 CcRosGetCacheSegment(PBCB Bcb,
686 PCACHE_SEGMENT* CacheSeg)
688 PCACHE_SEGMENT current;
693 DPRINT("CcRosGetCacheSegment()\n");
696 * Look for a cache segment already mapping the same data.
698 current = CcRosLookupCacheSegment(Bcb, FileOffset);
702 * Otherwise create a new segment.
704 Status = CcRosCreateCacheSegment(Bcb, FileOffset, ¤t);
705 if (!NT_SUCCESS(Status))
711 * Return information about the segment to the caller.
713 *UptoDate = current->Valid;
714 *BaseAddress = current->BaseAddress;
715 DPRINT("*BaseAddress 0x%.8X\n", *BaseAddress);
717 *BaseOffset = current->FileOffset;
718 return(STATUS_SUCCESS);
722 CcRosRequestCacheSegment(PBCB Bcb,
726 PCACHE_SEGMENT* CacheSeg)
728 * FUNCTION: Request a page mapping for a BCB
735 if ((FileOffset % Bcb->CacheSegmentSize) != 0)
737 CPRINT("Bad fileoffset %x should be multiple of %x",
738 FileOffset, Bcb->CacheSegmentSize);
742 return(CcRosGetCacheSegment(Bcb,
752 CcFreeCachePage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address,
753 PHYSICAL_ADDRESS PhysAddr, SWAPENTRY SwapEntry, BOOLEAN Dirty)
755 assert(SwapEntry == 0);
756 if (PhysAddr.QuadPart != 0)
758 MmReleasePageMemoryConsumer(MC_CACHE, PhysAddr);
763 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg)
765 * FUNCTION: Releases a cache segment associated with a BCB
772 PHYSICAL_ADDRESS PhysicalAddr;
775 DPRINT("Freeing cache segment %x\n", CacheSeg);
777 RegionSize = CacheSeg->Bcb->CacheSegmentSize / PAGE_SIZE;
779 /* Unmap all the pages. */
780 for (i = 0; i < RegionSize; i++)
782 MmDeleteVirtualMapping(NULL,
783 CacheSeg->BaseAddress + (i * PAGE_SIZE),
787 MmReleasePageMemoryConsumer(MC_CACHE, PhysicalAddr);
790 KeAcquireSpinLock(&CiCacheSegMappingRegionLock, &oldIrql);
791 /* Deallocate all the pages used. */
792 Base = (ULONG)(CacheSeg->BaseAddress - CiCacheSegMappingRegionBase) / PAGE_SIZE;
794 RtlClearBits(&CiCacheSegMappingRegionAllocMap, Base, RegionSize);
796 CiCacheSegMappingRegionHint = min (CiCacheSegMappingRegionHint, Base);
798 KeReleaseSpinLock(&CiCacheSegMappingRegionLock, oldIrql);
800 MmLockAddressSpace(MmGetKernelAddressSpace());
801 MmFreeMemoryArea(MmGetKernelAddressSpace(),
802 CacheSeg->BaseAddress,
803 CacheSeg->Bcb->CacheSegmentSize,
806 MmUnlockAddressSpace(MmGetKernelAddressSpace());
808 ExFreeToNPagedLookasideList(&CacheSegLookasideList, CacheSeg);
809 return(STATUS_SUCCESS);
813 CcRosFreeCacheSegment(PBCB Bcb, PCACHE_SEGMENT CacheSeg)
820 DPRINT("CcRosFreeCacheSegment(Bcb %x, CacheSeg %x)\n",
823 ExAcquireFastMutex(&ViewLock);
824 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
825 RemoveEntryList(&CacheSeg->BcbSegmentListEntry);
826 RemoveEntryList(&CacheSeg->CacheSegmentListEntry);
827 RemoveEntryList(&CacheSeg->CacheSegmentLRUListEntry);
830 RemoveEntryList(&CacheSeg->DirtySegmentListEntry);
831 DirtyPageCount -= Bcb->CacheSegmentSize / PAGE_SIZE;
834 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
835 ExReleaseFastMutex(&ViewLock);
837 Status = CcRosInternalFreeCacheSegment(CacheSeg);
845 CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointers,
846 IN PLARGE_INTEGER FileOffset OPTIONAL,
848 OUT PIO_STATUS_BLOCK IoStatus)
851 LARGE_INTEGER Offset;
852 PCACHE_SEGMENT current;
856 DPRINT("CcFlushCache(SectionObjectPointers %x, FileOffset %x, Length %d, IoStatus %x)\n",
857 SectionObjectPointers, FileOffset, Length, IoStatus);
859 if (SectionObjectPointers && SectionObjectPointers->SharedCacheMap)
861 Bcb = (PBCB)SectionObjectPointers->SharedCacheMap;
865 Offset = *FileOffset;
869 Offset.QuadPart = 0LL;
870 Length = Bcb->FileSize.u.LowPart;
875 IoStatus->Status = STATUS_SUCCESS;
876 IoStatus->Information = 0;
881 current = CcRosLookupCacheSegment (Bcb, Offset.u.LowPart);
886 Status = CcRosFlushCacheSegment(current);
887 if (!NT_SUCCESS(Status) && IoStatus != NULL)
889 IoStatus->Status = Status;
892 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
893 ExReleaseFastMutex(¤t->Lock);
894 current->ReferenceCount--;
895 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
898 Offset.QuadPart += Bcb->CacheSegmentSize;
899 if (Length > Bcb->CacheSegmentSize)
901 Length -= Bcb->CacheSegmentSize;
913 IoStatus->Status = STATUS_INVALID_PARAMETER;
919 CcRosDeleteFileCache(PFILE_OBJECT FileObject, PBCB Bcb)
921 * FUNCTION: Releases the BCB associated with a file object
924 PLIST_ENTRY current_entry;
925 PCACHE_SEGMENT current;
933 ExReleaseFastMutex(&ViewLock);
935 CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, NULL);
937 ExAcquireFastMutex(&ViewLock);
939 if (Bcb->RefCount == 0)
941 if (Bcb->BcbRemoveListEntry.Flink != NULL)
943 RemoveEntryList(&Bcb->BcbRemoveListEntry);
944 Bcb->BcbRemoveListEntry.Flink = NULL;
947 FileObject->SectionObjectPointer->SharedCacheMap = NULL;
950 * Release all cache segments.
952 InitializeListHead(&FreeList);
953 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
954 current_entry = Bcb->BcbSegmentListHead.Flink;
955 while (!IsListEmpty(&Bcb->BcbSegmentListHead))
957 current_entry = RemoveTailList(&Bcb->BcbSegmentListHead);
958 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT, BcbSegmentListEntry);
959 RemoveEntryList(¤t->CacheSegmentListEntry);
960 RemoveEntryList(¤t->CacheSegmentLRUListEntry);
963 RemoveEntryList(¤t->DirtySegmentListEntry);
964 DirtyPageCount -= Bcb->CacheSegmentSize / PAGE_SIZE;
965 DPRINT1("Freeing dirty segment\n");
967 InsertHeadList(&FreeList, ¤t->BcbSegmentListEntry);
969 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
971 ExReleaseFastMutex(&ViewLock);
972 ObDereferenceObject (Bcb->FileObject);
974 while (!IsListEmpty(&FreeList))
976 current_entry = RemoveTailList(&FreeList);
977 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT, BcbSegmentListEntry);
978 Status = CcRosInternalFreeCacheSegment(current);
980 ExFreeToNPagedLookasideList(&BcbLookasideList, Bcb);
981 ExAcquireFastMutex(&ViewLock);
983 return(STATUS_SUCCESS);
986 VOID CcRosReferenceCache(PFILE_OBJECT FileObject)
989 ExAcquireFastMutex(&ViewLock);
990 Bcb = (PBCB)FileObject->SectionObjectPointer->SharedCacheMap;
992 if (Bcb->RefCount == 0)
994 assert(Bcb->BcbRemoveListEntry.Flink != NULL);
995 RemoveEntryList(&Bcb->BcbRemoveListEntry);
996 Bcb->BcbRemoveListEntry.Flink = NULL;
1001 assert(Bcb->BcbRemoveListEntry.Flink == NULL);
1004 ExReleaseFastMutex(&ViewLock);
1007 VOID CcRosSetRemoveOnClose(PSECTION_OBJECT_POINTERS SectionObjectPointer)
1010 DPRINT("CcRosSetRemoveOnClose()\n");
1011 ExAcquireFastMutex(&ViewLock);
1012 Bcb = (PBCB)SectionObjectPointer->SharedCacheMap;
1015 Bcb->RemoveOnClose = TRUE;
1016 if (Bcb->RefCount == 0)
1018 CcRosDeleteFileCache(Bcb->FileObject, Bcb);
1021 ExReleaseFastMutex(&ViewLock);
1025 VOID CcRosDereferenceCache(PFILE_OBJECT FileObject)
1028 ExAcquireFastMutex(&ViewLock);
1029 Bcb = (PBCB)FileObject->SectionObjectPointer->SharedCacheMap;
1031 if (Bcb->RefCount > 0)
1034 if (Bcb->RefCount == 0)
1036 MmFreeSectionSegments(Bcb->FileObject);
1037 if (Bcb->RemoveOnClose)
1039 CcRosDeleteFileCache(FileObject, Bcb);
1043 Bcb->TimeStamp = CcTimeStamp;
1044 InsertHeadList(&ClosedListHead, &Bcb->BcbRemoveListEntry);
1048 ExReleaseFastMutex(&ViewLock);
1052 CcRosReleaseFileCache(PFILE_OBJECT FileObject)
1054 * FUNCTION: Called by the file system when a handle to a file object
1060 ExAcquireFastMutex(&ViewLock);
1062 if (FileObject->SectionObjectPointer->SharedCacheMap != NULL)
1064 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
1065 if (FileObject->PrivateCacheMap != NULL)
1067 FileObject->PrivateCacheMap = NULL;
1068 if (Bcb->RefCount > 0)
1071 if (Bcb->RefCount == 0)
1073 if (Bcb->RemoveOnClose)
1075 CcRosDeleteFileCache(FileObject, Bcb);
1079 Bcb->TimeStamp = CcTimeStamp;
1080 InsertHeadList(&ClosedListHead, &Bcb->BcbRemoveListEntry);
1086 ExReleaseFastMutex(&ViewLock);
1087 return(STATUS_SUCCESS);
1091 CcTryToInitializeFileCache(PFILE_OBJECT FileObject)
1096 ExAcquireFastMutex(&ViewLock);
1098 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
1101 Status = STATUS_UNSUCCESSFUL;
1105 if (FileObject->PrivateCacheMap == NULL)
1107 FileObject->PrivateCacheMap = Bcb;
1110 if (Bcb->BcbRemoveListEntry.Flink != NULL)
1112 RemoveEntryList(&Bcb->BcbRemoveListEntry);
1113 Bcb->BcbRemoveListEntry.Flink = NULL;
1115 Status = STATUS_SUCCESS;
1117 ExReleaseFastMutex(&ViewLock);
1124 CcRosInitializeFileCache(PFILE_OBJECT FileObject,
1125 ULONG CacheSegmentSize)
1127 * FUNCTION: Initializes a BCB for a file object
1131 DPRINT("CcRosInitializeFileCache(FileObject %x, *Bcb %x, CacheSegmentSize %d)\n",
1132 FileObject, Bcb, CacheSegmentSize);
1134 ExAcquireFastMutex(&ViewLock);
1136 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
1139 Bcb = ExAllocateFromNPagedLookasideList(&BcbLookasideList);
1142 ExReleaseFastMutex(&ViewLock);
1143 return(STATUS_UNSUCCESSFUL);
1145 memset(Bcb, 0, sizeof(BCB));
1146 ObReferenceObjectByPointer(FileObject,
1150 Bcb->FileObject = FileObject;
1151 Bcb->CacheSegmentSize = CacheSegmentSize;
1152 if (FileObject->FsContext)
1154 Bcb->AllocationSize =
1155 ((PFSRTL_COMMON_FCB_HEADER)FileObject->FsContext)->AllocationSize;
1157 ((PFSRTL_COMMON_FCB_HEADER)FileObject->FsContext)->FileSize;
1159 KeInitializeSpinLock(&Bcb->BcbLock);
1160 InitializeListHead(&Bcb->BcbSegmentListHead);
1161 FileObject->SectionObjectPointer->SharedCacheMap = Bcb;
1163 if (FileObject->PrivateCacheMap == NULL)
1165 FileObject->PrivateCacheMap = Bcb;
1168 if (Bcb->BcbRemoveListEntry.Flink != NULL)
1170 RemoveEntryList(&Bcb->BcbRemoveListEntry);
1171 Bcb->BcbRemoveListEntry.Flink = NULL;
1173 ExReleaseFastMutex(&ViewLock);
1175 return(STATUS_SUCCESS);
1181 PFILE_OBJECT STDCALL
1182 CcGetFileObjectFromSectionPtrs(IN PSECTION_OBJECT_POINTERS SectionObjectPointers)
1185 if (SectionObjectPointers && SectionObjectPointers->SharedCacheMap)
1187 Bcb = (PBCB)SectionObjectPointers->SharedCacheMap;
1189 return Bcb->FileObject;
1195 CmLazyCloseThreadMain(PVOID Ignored)
1197 LARGE_INTEGER Timeout;
1198 PLIST_ENTRY current_entry;
1200 ULONG RemoveTimeStamp;
1203 KeQuerySystemTime (&Timeout);
1207 Timeout.QuadPart += 100000000LL; // 10sec
1208 Status = KeWaitForSingleObject(&LazyCloseThreadEvent,
1214 DPRINT("LazyCloseThreadMain %d\n", CcTimeStamp);
1216 if (!NT_SUCCESS(Status))
1218 DbgPrint("LazyCloseThread: Wait failed\n");
1222 if (LazyCloseThreadShouldTerminate)
1224 DbgPrint("LazyCloseThread: Terminating\n");
1228 ExAcquireFastMutex(&ViewLock);
1230 if (CcTimeStamp >= 30)
1232 RemoveTimeStamp = CcTimeStamp - 30; /* 5min = 10sec * 30 */
1233 while (!IsListEmpty(&ClosedListHead))
1235 current_entry = ClosedListHead.Blink;
1236 current = CONTAINING_RECORD(current_entry, BCB, BcbRemoveListEntry);
1237 if (current->TimeStamp >= RemoveTimeStamp)
1241 CcRosDeleteFileCache(current->FileObject, current);
1244 ExReleaseFastMutex(&ViewLock);
1258 DPRINT("CcInitView()\n");
1260 CiCacheSegMappingRegionHint = 0;
1261 CiCacheSegMappingRegionBase = NULL;
1263 MmLockAddressSpace(MmGetKernelAddressSpace());
1265 Status = MmCreateMemoryArea(NULL,
1266 MmGetKernelAddressSpace(),
1267 MEMORY_AREA_CACHE_SEGMENT,
1268 &CiCacheSegMappingRegionBase,
1269 CI_CACHESEG_MAPPING_REGION_SIZE,
1274 MmUnlockAddressSpace(MmGetKernelAddressSpace());
1275 if (!NT_SUCCESS(Status))
1280 Buffer = ExAllocatePool(NonPagedPool, CI_CACHESEG_MAPPING_REGION_SIZE / (PAGE_SIZE * 8));
1282 RtlInitializeBitMap(&CiCacheSegMappingRegionAllocMap, Buffer, CI_CACHESEG_MAPPING_REGION_SIZE / PAGE_SIZE);
1283 RtlClearAllBits(&CiCacheSegMappingRegionAllocMap);
1285 KeInitializeSpinLock(&CiCacheSegMappingRegionLock);
1287 InitializeListHead(&CacheSegmentListHead);
1288 InitializeListHead(&DirtySegmentListHead);
1289 InitializeListHead(&CacheSegmentLRUListHead);
1290 InitializeListHead(&ClosedListHead);
1291 ExInitializeFastMutex(&ViewLock);
1292 ExInitializeNPagedLookasideList (&iBcbLookasideList,
1296 sizeof(INTERNAL_BCB),
1299 ExInitializeNPagedLookasideList (&BcbLookasideList,
1306 ExInitializeNPagedLookasideList (&CacheSegLookasideList,
1310 sizeof(CACHE_SEGMENT),
1314 MmInitializeMemoryConsumer(MC_CACHE, CcRosTrimCache);
1316 CcInitCacheZeroPage();
1319 LazyCloseThreadShouldTerminate = FALSE;
1320 KeInitializeEvent (&LazyCloseThreadEvent, SynchronizationEvent, FALSE);
1321 Status = PsCreateSystemThread(&LazyCloseThreadHandle,
1326 (PKSTART_ROUTINE)CmLazyCloseThreadMain,
1328 if (NT_SUCCESS(Status))
1330 Priority = LOW_REALTIME_PRIORITY;
1331 NtSetInformationThread(LazyCloseThreadHandle,