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;
91 #define CI_CACHESEG_MAPPING_REGION_SIZE (128*1024*1024)
93 static PVOID CiCacheSegMappingRegionBase = NULL;
94 static RTL_BITMAP CiCacheSegMappingRegionAllocMap;
95 static ULONG CiCacheSegMappingRegionHint;
96 static KSPIN_LOCK CiCacheSegMappingRegionLock;
99 NPAGED_LOOKASIDE_LIST iBcbLookasideList;
100 static NPAGED_LOOKASIDE_LIST BcbLookasideList;
101 static NPAGED_LOOKASIDE_LIST CacheSegLookasideList;
103 static ULONG CcTimeStamp;
104 static KEVENT LazyCloseThreadEvent;
105 static HANDLE LazyCloseThreadHandle;
106 static CLIENT_ID LazyCloseThreadId;
107 static volatile BOOLEAN LazyCloseThreadShouldTerminate;
109 void * alloca(size_t size);
112 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg);
113 #endif /* LIBCAPTIVE */
115 /* FUNCTIONS *****************************************************************/
120 CcRosFlushCacheSegment(PCACHE_SEGMENT CacheSegment)
124 Status = WriteCacheSegment(CacheSegment);
125 if (NT_SUCCESS(Status))
127 ExAcquireFastMutex(&ViewLock);
128 KeAcquireSpinLock(&CacheSegment->Bcb->BcbLock, &oldIrql);
129 CacheSegment->Dirty = FALSE;
130 RemoveEntryList(&CacheSegment->DirtySegmentListEntry);
131 DirtyPageCount -= CacheSegment->Bcb->CacheSegmentSize / PAGE_SIZE;
132 CacheSegment->ReferenceCount--;
133 KeReleaseSpinLock(&CacheSegment->Bcb->BcbLock, oldIrql);
134 ExReleaseFastMutex(&ViewLock);
139 VOID CcRosRemoveUnusedFiles(VOID);
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;
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);
269 KeReleaseSpinLock(¤t->Bcb->BcbLock, oldIrql);
272 ExReleaseFastMutex(&ViewLock);
274 while (!IsListEmpty(&FreeList))
276 current_entry = RemoveHeadList(&FreeList);
277 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
278 BcbSegmentListEntry);
279 PagesPerSegment = current->Bcb->CacheSegmentSize / PAGE_SIZE;
280 PagesFreed = min(PagesPerSegment, Target);
281 Target -= PagesFreed;
282 (*NrFreed) += PagesFreed;
283 CcRosInternalFreeCacheSegment(current);
286 DPRINT("CcRosTrimCache() finished\n");
287 return(STATUS_SUCCESS);
290 #endif /* LIBCAPTIVE */
293 CcRosReleaseCacheSegment(PBCB Bcb,
294 PCACHE_SEGMENT CacheSeg,
299 BOOLEAN WasDirty = CacheSeg->Dirty;
304 DPRINT("CcReleaseCacheSegment(Bcb %x, CacheSeg %x, Valid %d)\n",
305 Bcb, CacheSeg, Valid);
307 CacheSeg->Valid = Valid;
308 CacheSeg->Dirty = CacheSeg->Dirty || Dirty;
310 ExAcquireFastMutex(&ViewLock);
311 if (!WasDirty && CacheSeg->Dirty)
313 InsertTailList(&DirtySegmentListHead, &CacheSeg->DirtySegmentListEntry);
314 DirtyPageCount += Bcb->CacheSegmentSize / PAGE_SIZE;
316 RemoveEntryList(&CacheSeg->CacheSegmentLRUListEntry);
317 InsertTailList(&CacheSegmentLRUListHead, &CacheSeg->CacheSegmentLRUListEntry);
321 CacheSeg->MappedCount++;
323 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
324 CacheSeg->ReferenceCount--;
325 if (Mapped && CacheSeg->MappedCount == 1)
327 CacheSeg->ReferenceCount++;
329 if (!WasDirty && CacheSeg->Dirty)
331 CacheSeg->ReferenceCount++;
333 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
334 ExReleaseFastMutex(&ViewLock);
335 ExReleaseFastMutex(&CacheSeg->Lock);
337 return(STATUS_SUCCESS);
341 CcRosLookupCacheSegment(PBCB Bcb, ULONG FileOffset)
343 PLIST_ENTRY current_entry;
344 PCACHE_SEGMENT current;
349 DPRINT("CcRosLookupCacheSegment(Bcb %x, FileOffset %d)\n", Bcb, FileOffset);
351 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
352 current_entry = Bcb->BcbSegmentListHead.Flink;
353 while (current_entry != &Bcb->BcbSegmentListHead)
355 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
356 BcbSegmentListEntry);
357 if (current->FileOffset <= FileOffset &&
358 (current->FileOffset + Bcb->CacheSegmentSize) > FileOffset)
360 current->ReferenceCount++;
361 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
364 current_entry = current_entry->Flink;
366 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
373 CcRosMarkDirtyCacheSegment(PBCB Bcb, ULONG FileOffset)
375 PCACHE_SEGMENT CacheSeg;
380 DPRINT("CcRosMarkDirtyCacheSegment(Bcb %x, FileOffset %d)\n", Bcb, FileOffset);
382 CacheSeg = CcRosLookupCacheSegment(Bcb, FileOffset);
383 if (CacheSeg == NULL)
387 ExAcquireFastMutex(&CacheSeg->Lock);
388 if (!CacheSeg->Dirty)
390 ExAcquireFastMutex(&ViewLock);
391 InsertTailList(&DirtySegmentListHead, &CacheSeg->DirtySegmentListEntry);
392 DirtyPageCount += Bcb->CacheSegmentSize / PAGE_SIZE;
393 ExReleaseFastMutex(&ViewLock);
397 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
398 CacheSeg->ReferenceCount--;
399 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
403 CacheSeg->Dirty = TRUE;
404 ExReleaseFastMutex(&CacheSeg->Lock);
406 return(STATUS_SUCCESS);
410 CcRosUnmapCacheSegment(PBCB Bcb, ULONG FileOffset, BOOLEAN NowDirty)
412 PCACHE_SEGMENT CacheSeg;
418 DPRINT("CcRosUnmapCacheSegment(Bcb %x, FileOffset %d, NowDirty %d)\n",
419 Bcb, FileOffset, NowDirty);
421 CacheSeg = CcRosLookupCacheSegment(Bcb, FileOffset);
422 if (CacheSeg == NULL)
424 return(STATUS_UNSUCCESSFUL);
426 ExAcquireFastMutex(&CacheSeg->Lock);
428 WasDirty = CacheSeg->Dirty;
429 CacheSeg->Dirty = CacheSeg->Dirty || NowDirty;
431 CacheSeg->MappedCount--;
433 if (!WasDirty && NowDirty)
435 ExAcquireFastMutex(&ViewLock);
436 InsertTailList(&DirtySegmentListHead, &CacheSeg->DirtySegmentListEntry);
437 DirtyPageCount += Bcb->CacheSegmentSize / PAGE_SIZE;
438 ExReleaseFastMutex(&ViewLock);
441 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
442 CacheSeg->ReferenceCount--;
443 if (!WasDirty && NowDirty)
445 CacheSeg->ReferenceCount++;
447 if (CacheSeg->MappedCount == 0)
449 CacheSeg->ReferenceCount--;
451 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
453 ExReleaseFastMutex(&CacheSeg->Lock);
454 return(STATUS_SUCCESS);
457 #endif /* LIBCAPTIVE */
460 CcRosCreateCacheSegment(PBCB Bcb,
462 PCACHE_SEGMENT* CacheSeg,
467 #endif /* LIBCAPTIVE */
468 PCACHE_SEGMENT current;
469 PLIST_ENTRY current_entry;
473 ULONG StartingOffset;
478 DPRINT("CcRosCreateCacheSegment()\n");
480 current = ExAllocateFromNPagedLookasideList(&CacheSegLookasideList);
481 current->Valid = FALSE;
482 current->Dirty = FALSE;
483 current->FileOffset = ROUND_DOWN(FileOffset, Bcb->CacheSegmentSize);
485 current->MappedCount = 0;
486 current->DirtySegmentListEntry.Flink = NULL;
487 current->DirtySegmentListEntry.Blink = NULL;
488 current->ReferenceCount = 1;
489 ExInitializeFastMutex(¤t->Lock);
490 ExAcquireFastMutex(¤t->Lock);
491 ExAcquireFastMutex(&ViewLock);
494 /* There is window between the call to CcRosLookupCacheSegment
495 * and CcRosCreateCacheSegment. We must check if a segment on
496 * the fileoffset exist. If there exist a segment, we release
497 * our new created segment and return the existing one.
499 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
500 current_entry = Bcb->BcbSegmentListHead.Flink;
501 while (current_entry != &Bcb->BcbSegmentListHead)
503 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
504 BcbSegmentListEntry);
505 if (current->FileOffset <= FileOffset &&
506 (current->FileOffset + Bcb->CacheSegmentSize) > FileOffset)
508 current->ReferenceCount++;
509 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
510 ExReleaseFastMutex(&(*CacheSeg)->Lock);
511 ExReleaseFastMutex(&ViewLock);
512 ExFreeToNPagedLookasideList(&CacheSegLookasideList, *CacheSeg);
516 ExAcquireFastMutex(¤t->Lock);
518 return STATUS_SUCCESS;
520 current_entry = current_entry->Flink;
522 /* There was no existing segment. */
524 InsertTailList(&Bcb->BcbSegmentListHead, ¤t->BcbSegmentListEntry);
525 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
526 InsertTailList(&CacheSegmentListHead, ¤t->CacheSegmentListEntry);
527 InsertTailList(&CacheSegmentLRUListHead, ¤t->CacheSegmentLRUListEntry);
528 ExReleaseFastMutex(&ViewLock);
530 KeAcquireSpinLock(&CiCacheSegMappingRegionLock, &oldIrql);
532 StartingOffset = RtlFindClearBitsAndSet(&CiCacheSegMappingRegionAllocMap, Bcb->CacheSegmentSize / PAGE_SIZE, CiCacheSegMappingRegionHint);
534 if (StartingOffset == 0xffffffff)
536 DPRINT1("Out of CacheSeg mapping space\n");
540 current->BaseAddress = CiCacheSegMappingRegionBase + StartingOffset * PAGE_SIZE;
542 if (CiCacheSegMappingRegionHint == StartingOffset)
544 CiCacheSegMappingRegionHint += Bcb->CacheSegmentSize / PAGE_SIZE;
547 KeReleaseSpinLock(&CiCacheSegMappingRegionLock, oldIrql);
549 MmLockAddressSpace(MmGetKernelAddressSpace());
550 current->BaseAddress = NULL;
551 Status = MmCreateMemoryArea(NULL,
552 MmGetKernelAddressSpace(),
553 MEMORY_AREA_CACHE_SEGMENT,
554 ¤t->BaseAddress,
555 Bcb->CacheSegmentSize,
557 (PMEMORY_AREA*)¤t->MemoryArea,
559 MmUnlockAddressSpace(MmGetKernelAddressSpace());
560 if (!NT_SUCCESS(Status))
566 for (i = 0; i < (Bcb->CacheSegmentSize / PAGE_SIZE); i++)
568 PHYSICAL_ADDRESS Page;
570 Status = MmRequestPageMemoryConsumer(MC_CACHE, TRUE, &Page);
571 if (!NT_SUCCESS(Status))
576 Status = MmCreateVirtualMapping(NULL,
577 current->BaseAddress + (i * PAGE_SIZE),
581 if (!NT_SUCCESS(Status))
586 #else /* !LIBCAPTIVE */
587 /* libcaptive MmCreateMemoryArea() returns already allocated memory */
588 #endif /* !LIBCAPTIVE */
591 ExReleaseFastMutex(¤t->Lock);
594 return(STATUS_SUCCESS);
600 CcRosGetCacheSegmentChain(PBCB Bcb,
603 PCACHE_SEGMENT* CacheSeg)
605 PCACHE_SEGMENT current;
607 PCACHE_SEGMENT* CacheSegList;
608 PCACHE_SEGMENT Previous = NULL;
612 DPRINT("CcRosGetCacheSegmentChain()\n");
614 Length = ROUND_UP(Length, Bcb->CacheSegmentSize);
616 CacheSegList = alloca(sizeof(PCACHE_SEGMENT) *
617 (Length / Bcb->CacheSegmentSize));
620 * Look for a cache segment already mapping the same data.
622 for (i = 0; i < (Length / Bcb->CacheSegmentSize); i++)
624 ULONG CurrentOffset = FileOffset + (i * Bcb->CacheSegmentSize);
625 current = CcRosLookupCacheSegment(Bcb, CurrentOffset);
628 CacheSegList[i] = current;
632 CcRosCreateCacheSegment(Bcb, CurrentOffset, ¤t, FALSE);
633 CacheSegList[i] = current;
637 for (i = 0; i < (Length / Bcb->CacheSegmentSize); i++)
639 ExAcquireFastMutex(&CacheSegList[i]->Lock);
642 *CacheSeg = CacheSegList[i];
643 Previous = CacheSegList[i];
647 Previous->NextInChain = CacheSegList[i];
648 Previous = CacheSegList[i];
651 Previous->NextInChain = NULL;
653 return(STATUS_SUCCESS);
656 #endif /* LIBCAPTIVE */
659 CcRosGetCacheSegment(PBCB Bcb,
664 PCACHE_SEGMENT* CacheSeg)
666 PCACHE_SEGMENT current;
671 DPRINT("CcRosGetCacheSegment()\n");
674 * Look for a cache segment already mapping the same data.
676 current = CcRosLookupCacheSegment(Bcb, FileOffset);
679 ExAcquireFastMutex(¤t->Lock);
684 * Otherwise create a new segment.
686 Status = CcRosCreateCacheSegment(Bcb, FileOffset, ¤t, TRUE);
689 * Return information about the segment to the caller.
691 *UptoDate = current->Valid;
692 *BaseAddress = current->BaseAddress;
693 DPRINT("*BaseAddress 0x%.8X\n", *BaseAddress);
695 *BaseOffset = current->FileOffset;
696 return(STATUS_SUCCESS);
700 CcRosRequestCacheSegment(PBCB Bcb,
704 PCACHE_SEGMENT* CacheSeg)
706 * FUNCTION: Request a page mapping for a BCB
713 if ((FileOffset % Bcb->CacheSegmentSize) != 0)
715 CPRINT("Bad fileoffset %x should be multiple of %x",
716 FileOffset, Bcb->CacheSegmentSize);
720 return(CcRosGetCacheSegment(Bcb,
733 CcFreeCachePage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address,
734 PHYSICAL_ADDRESS PhysAddr, SWAPENTRY SwapEntry, BOOLEAN Dirty)
736 assert(SwapEntry == 0);
737 if (PhysAddr.QuadPart != 0)
739 MmReleasePageMemoryConsumer(MC_CACHE, PhysAddr);
744 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg)
746 * FUNCTION: Releases a cache segment associated with a BCB
753 PHYSICAL_ADDRESS PhysicalAddr;
756 DPRINT("Freeing cache segment %x\n", CacheSeg);
758 RegionSize = CacheSeg->Bcb->CacheSegmentSize / PAGE_SIZE;
760 /* Unmap all the pages. */
761 for (i = 0; i < RegionSize; i++)
763 MmDeleteVirtualMapping(NULL,
764 CacheSeg->BaseAddress + (i * PAGE_SIZE),
768 MmReleasePageMemoryConsumer(MC_CACHE, PhysicalAddr);
771 KeAcquireSpinLock(&CiCacheSegMappingRegionLock, &oldIrql);
772 /* Deallocate all the pages used. */
773 Base = (ULONG)(CacheSeg->BaseAddress - CiCacheSegMappingRegionBase) / PAGE_SIZE;
775 RtlClearBits(&CiCacheSegMappingRegionAllocMap, Base, RegionSize);
777 CiCacheSegMappingRegionHint = min (CiCacheSegMappingRegionHint, Base);
779 KeReleaseSpinLock(&CiCacheSegMappingRegionLock, oldIrql);
781 MmLockAddressSpace(MmGetKernelAddressSpace());
782 MmFreeMemoryArea(MmGetKernelAddressSpace(),
783 CacheSeg->BaseAddress,
784 CacheSeg->Bcb->CacheSegmentSize,
787 MmUnlockAddressSpace(MmGetKernelAddressSpace());
789 ExFreeToNPagedLookasideList(&CacheSegLookasideList, CacheSeg);
790 return(STATUS_SUCCESS);
794 CcRosFreeCacheSegment(PBCB Bcb, PCACHE_SEGMENT CacheSeg)
801 DPRINT("CcRosFreeCacheSegment(Bcb %x, CacheSeg %x)\n",
804 ExAcquireFastMutex(&ViewLock);
805 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
806 RemoveEntryList(&CacheSeg->BcbSegmentListEntry);
807 RemoveEntryList(&CacheSeg->CacheSegmentListEntry);
808 RemoveEntryList(&CacheSeg->CacheSegmentLRUListEntry);
811 RemoveEntryList(&CacheSeg->DirtySegmentListEntry);
812 DirtyPageCount -= Bcb->CacheSegmentSize / PAGE_SIZE;
815 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
816 ExReleaseFastMutex(&ViewLock);
818 Status = CcRosInternalFreeCacheSegment(CacheSeg);
823 CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointers,
824 IN PLARGE_INTEGER FileOffset OPTIONAL,
826 OUT PIO_STATUS_BLOCK IoStatus)
829 LARGE_INTEGER Offset;
830 PCACHE_SEGMENT current;
834 DPRINT("CcFlushCache(SectionObjectPointers %x, FileOffset %x, Length %d, IoStatus %x)\n",
835 SectionObjectPointers, FileOffset, Length, IoStatus);
837 if (SectionObjectPointers && SectionObjectPointers->SharedCacheMap)
839 Bcb = (PBCB)SectionObjectPointers->SharedCacheMap;
843 Offset = *FileOffset;
847 Offset.QuadPart = 0LL;
848 Length = Bcb->FileSize.u.LowPart;
853 IoStatus->Status = STATUS_SUCCESS;
854 IoStatus->Information = 0;
859 current = CcRosLookupCacheSegment (Bcb, Offset.u.LowPart);
862 ExAcquireFastMutex(¤t->Lock);
865 Status = CcRosFlushCacheSegment(current);
866 if (!NT_SUCCESS(Status) && IoStatus != NULL)
868 IoStatus->Status = Status;
871 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
872 ExReleaseFastMutex(¤t->Lock);
873 current->ReferenceCount--;
874 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
877 Offset.QuadPart += Bcb->CacheSegmentSize;
878 if (Length > Bcb->CacheSegmentSize)
880 Length -= Bcb->CacheSegmentSize;
892 IoStatus->Status = STATUS_INVALID_PARAMETER;
898 CcRosDeleteFileCache(PFILE_OBJECT FileObject, PBCB Bcb)
900 * FUNCTION: Releases the BCB associated with a file object
903 PLIST_ENTRY current_entry;
904 PCACHE_SEGMENT current;
912 ExReleaseFastMutex(&ViewLock);
914 CcFlushCache(FileObject->SectionObjectPointers, NULL, 0, NULL);
916 ExAcquireFastMutex(&ViewLock);
918 if (Bcb->RefCount == 0)
920 if (Bcb->BcbRemoveListEntry.Flink != NULL)
922 RemoveEntryList(&Bcb->BcbRemoveListEntry);
923 Bcb->BcbRemoveListEntry.Flink = NULL;
926 FileObject->SectionObjectPointers->SharedCacheMap = NULL;
929 * Release all cache segments.
931 InitializeListHead(&FreeList);
932 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
933 current_entry = Bcb->BcbSegmentListHead.Flink;
934 while (!IsListEmpty(&Bcb->BcbSegmentListHead))
936 current_entry = RemoveTailList(&Bcb->BcbSegmentListHead);
937 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT, BcbSegmentListEntry);
938 RemoveEntryList(¤t->CacheSegmentListEntry);
939 RemoveEntryList(¤t->CacheSegmentLRUListEntry);
942 RemoveEntryList(¤t->DirtySegmentListEntry);
943 DirtyPageCount -= Bcb->CacheSegmentSize / PAGE_SIZE;
944 DPRINT1("Freeing dirty segment\n");
946 InsertHeadList(&FreeList, ¤t->BcbSegmentListEntry);
948 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
950 ExReleaseFastMutex(&ViewLock);
951 ObDereferenceObject (Bcb->FileObject);
953 while (!IsListEmpty(&FreeList))
955 current_entry = RemoveTailList(&FreeList);
956 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT, BcbSegmentListEntry);
957 Status = CcRosInternalFreeCacheSegment(current);
959 ExFreeToNPagedLookasideList(&BcbLookasideList, Bcb);
960 ExAcquireFastMutex(&ViewLock);
962 return(STATUS_SUCCESS);
965 VOID CcRosReferenceCache(PFILE_OBJECT FileObject)
968 ExAcquireFastMutex(&ViewLock);
969 Bcb = (PBCB)FileObject->SectionObjectPointers->SharedCacheMap;
972 ExReleaseFastMutex(&ViewLock);
975 VOID CcRosSetRemoveOnClose(PSECTION_OBJECT_POINTERS SectionObjectPointer)
978 // DPRINT1("CcRosSetRemoveOnClose()\n");
979 ExAcquireFastMutex(&ViewLock);
980 Bcb = (PBCB)SectionObjectPointer->SharedCacheMap;
983 Bcb->RemoveOnClose = TRUE;
984 if (Bcb->RefCount == 0)
986 CcRosDeleteFileCache(Bcb->FileObject, Bcb);
989 ExReleaseFastMutex(&ViewLock);
993 VOID CcRosDereferenceCache(PFILE_OBJECT FileObject)
996 ExAcquireFastMutex(&ViewLock);
997 Bcb = (PBCB)FileObject->SectionObjectPointers->SharedCacheMap;
999 if (Bcb->RefCount > 0)
1002 if (Bcb->RefCount == 0)
1004 MmFreeSectionSegments(Bcb->FileObject);
1005 if (Bcb->RemoveOnClose)
1007 CcRosDeleteFileCache(FileObject, Bcb);
1011 Bcb->TimeStamp = CcTimeStamp;
1012 InsertHeadList(&ClosedListHead, &Bcb->BcbRemoveListEntry);
1016 ExReleaseFastMutex(&ViewLock);
1020 CcRosReleaseFileCache(PFILE_OBJECT FileObject)
1022 * FUNCTION: Called by the file system when a handle to a file object
1028 ExAcquireFastMutex(&ViewLock);
1030 if (FileObject->SectionObjectPointers->SharedCacheMap != NULL)
1032 Bcb = FileObject->SectionObjectPointers->SharedCacheMap;
1033 if (FileObject->PrivateCacheMap != NULL)
1035 FileObject->PrivateCacheMap = NULL;
1036 if (Bcb->RefCount > 0)
1039 if (Bcb->RefCount == 0)
1041 if (Bcb->RemoveOnClose)
1043 CcRosDeleteFileCache(FileObject, Bcb);
1047 Bcb->TimeStamp = CcTimeStamp;
1048 InsertHeadList(&ClosedListHead, &Bcb->BcbRemoveListEntry);
1054 ExReleaseFastMutex(&ViewLock);
1055 return(STATUS_SUCCESS);
1059 CcRosInitializeFileCache(PFILE_OBJECT FileObject,
1060 ULONG CacheSegmentSize)
1062 * FUNCTION: Initializes a BCB for a file object
1066 DPRINT("CcRosInitializeFileCache(FileObject %x, *Bcb %x, CacheSegmentSize %d)\n",
1067 FileObject, Bcb, CacheSegmentSize);
1069 ExAcquireFastMutex(&ViewLock);
1071 Bcb = FileObject->SectionObjectPointers->SharedCacheMap;
1074 Bcb = ExAllocateFromNPagedLookasideList(&BcbLookasideList);
1077 ExReleaseFastMutex(&ViewLock);
1078 return(STATUS_UNSUCCESSFUL);
1080 memset(Bcb, 0, sizeof(BCB));
1081 ObReferenceObjectByPointer(FileObject,
1085 Bcb->FileObject = FileObject;
1086 Bcb->CacheSegmentSize = CacheSegmentSize;
1087 if (FileObject->FsContext)
1089 Bcb->AllocationSize =
1090 ((REACTOS_COMMON_FCB_HEADER*)FileObject->FsContext)->AllocationSize;
1092 ((REACTOS_COMMON_FCB_HEADER*)FileObject->FsContext)->FileSize;
1094 KeInitializeSpinLock(&Bcb->BcbLock);
1095 InitializeListHead(&Bcb->BcbSegmentListHead);
1096 FileObject->SectionObjectPointers->SharedCacheMap = Bcb;
1098 if (FileObject->PrivateCacheMap == NULL)
1100 FileObject->PrivateCacheMap = Bcb;
1103 if (Bcb->BcbRemoveListEntry.Flink != NULL)
1105 RemoveEntryList(&Bcb->BcbRemoveListEntry);
1106 Bcb->BcbRemoveListEntry.Flink = NULL;
1108 ExReleaseFastMutex(&ViewLock);
1110 return(STATUS_SUCCESS);
1113 PFILE_OBJECT STDCALL
1114 CcGetFileObjectFromSectionPtrs(IN PSECTION_OBJECT_POINTERS SectionObjectPointers)
1117 if (SectionObjectPointers && SectionObjectPointers->SharedCacheMap)
1119 Bcb = (PBCB)SectionObjectPointers->SharedCacheMap;
1121 return Bcb->FileObject;
1126 #endif /* LIBCAPTIVE */
1129 CmLazyCloseThreadMain(PVOID Ignored)
1131 LARGE_INTEGER Timeout;
1132 PLIST_ENTRY current_entry;
1134 ULONG RemoveTimeStamp;
1137 KeQuerySystemTime (&Timeout);
1141 Timeout.QuadPart += 100000000LL; // 10sec
1142 Status = KeWaitForSingleObject(&LazyCloseThreadEvent,
1148 DPRINT("LazyCloseThreadMain %d\n", CcTimeStamp);
1150 if (!NT_SUCCESS(Status))
1152 DbgPrint("LazyCloseThread: Wait failed\n");
1156 if (LazyCloseThreadShouldTerminate)
1158 DbgPrint("LazyCloseThread: Terminating\n");
1162 ExAcquireFastMutex(&ViewLock);
1164 if (CcTimeStamp >= 30)
1166 RemoveTimeStamp = CcTimeStamp - 30; /* 5min = 10sec * 30 */
1167 while (!IsListEmpty(&ClosedListHead))
1169 current_entry = ClosedListHead.Blink;
1170 current = CONTAINING_RECORD(current_entry, BCB, BcbRemoveListEntry);
1171 if (current->TimeStamp >= RemoveTimeStamp)
1175 CcRosDeleteFileCache(current->FileObject, current);
1178 ExReleaseFastMutex(&ViewLock);
1192 DPRINT("CcInitView()\n");
1194 CiCacheSegMappingRegionHint = 0;
1195 CiCacheSegMappingRegionBase = NULL;
1197 MmLockAddressSpace(MmGetKernelAddressSpace());
1199 Status = MmCreateMemoryArea(NULL,
1200 MmGetKernelAddressSpace(),
1201 MEMORY_AREA_CACHE_SEGMENT,
1202 &CiCacheSegMappingRegionBase,
1203 CI_CACHESEG_MAPPING_REGION_SIZE,
1207 MmUnlockAddressSpace(MmGetKernelAddressSpace());
1208 if (!NT_SUCCESS(Status))
1213 Buffer = ExAllocatePool(NonPagedPool, CI_CACHESEG_MAPPING_REGION_SIZE / (PAGE_SIZE * 8));
1215 RtlInitializeBitMap(&CiCacheSegMappingRegionAllocMap, Buffer, CI_CACHESEG_MAPPING_REGION_SIZE / PAGE_SIZE);
1216 RtlClearAllBits(&CiCacheSegMappingRegionAllocMap);
1218 KeInitializeSpinLock(&CiCacheSegMappingRegionLock);
1220 InitializeListHead(&CacheSegmentListHead);
1221 InitializeListHead(&DirtySegmentListHead);
1222 InitializeListHead(&CacheSegmentLRUListHead);
1223 InitializeListHead(&ClosedListHead);
1224 ExInitializeFastMutex(&ViewLock);
1226 ExInitializeNPagedLookasideList (&iBcbLookasideList,
1230 sizeof(INTERNAL_BCB),
1233 ExInitializeNPagedLookasideList (&BcbLookasideList,
1240 ExInitializeNPagedLookasideList (&CacheSegLookasideList,
1244 sizeof(CACHE_SEGMENT),
1248 MmInitializeMemoryConsumer(MC_CACHE, CcRosTrimCache);
1250 CcInitCacheZeroPage();
1251 #endif /* LIBCAPTIVE */
1254 LazyCloseThreadShouldTerminate = FALSE;
1255 KeInitializeEvent (&LazyCloseThreadEvent, SynchronizationEvent, FALSE);
1256 Status = PsCreateSystemThread(&LazyCloseThreadHandle,
1261 (PKSTART_ROUTINE)CmLazyCloseThreadMain,
1263 if (NT_SUCCESS(Status))
1265 Priority = LOW_REALTIME_PRIORITY;
1266 NtSetInformationThread(LazyCloseThreadHandle,