branch update for HEAD-2003021201
[reactos.git] / ntoskrnl / cc / view.c
index 7a91f81..7c14df8 100644 (file)
 
 /* GLOBALS *******************************************************************/
 
+/*
+ * If CACHE_BITMAP is defined, the cache manager uses one large memory region 
+ * within the kernel address space and allocate/deallocate space from this block 
+ * over a bitmap. If CACHE_BITMAP is used, the size of the mdl mapping region 
+ * must be reduced (ntoskrnl\mm\mdl.c, MI_MDLMAPPING_REGION_SIZE).
+ */
+//#define CACHE_BITMAP
+
 #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
 #define ROUND_DOWN(N, S) (((N) % (S)) ? ROUND_UP(N, S) - S : N)
 
 #define TAG_CSEG  TAG('C', 'S', 'E', 'G')
 #define TAG_BCB   TAG('B', 'C', 'B', ' ')
+#define TAG_IBCB  TAG('i', 'B', 'C', 'B')
 
 static LIST_ENTRY DirtySegmentListHead;
 static LIST_ENTRY CacheSegmentListHead;
 static LIST_ENTRY CacheSegmentLRUListHead;
+static LIST_ENTRY ClosedListHead;
+static ULONG DirtyPageCount=0;
 
 static FAST_MUTEX ViewLock;
 
+#ifndef LIBCAPTIVE
+#ifdef CACHE_BITMAP
+#define        CI_CACHESEG_MAPPING_REGION_SIZE (128*1024*1024)
+
+static PVOID CiCacheSegMappingRegionBase = NULL;
+static RTL_BITMAP CiCacheSegMappingRegionAllocMap;
+static ULONG CiCacheSegMappingRegionHint;
+static KSPIN_LOCK CiCacheSegMappingRegionLock;
+#endif
+
+NPAGED_LOOKASIDE_LIST iBcbLookasideList;
+static NPAGED_LOOKASIDE_LIST BcbLookasideList;
+static NPAGED_LOOKASIDE_LIST CacheSegLookasideList;
+
+static ULONG CcTimeStamp;
+static KEVENT LazyCloseThreadEvent;
+static HANDLE LazyCloseThreadHandle;
+static CLIENT_ID LazyCloseThreadId;
+static volatile BOOLEAN LazyCloseThreadShouldTerminate;
+
 void * alloca(size_t size);
 
-NTSTATUS STDCALL
+NTSTATUS
 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg);
+#endif /* LIBCAPTIVE */
 
 /* FUNCTIONS *****************************************************************/
 
+#ifndef LIBCAPTIVE
+
 NTSTATUS STATIC
 CcRosFlushCacheSegment(PCACHE_SEGMENT CacheSegment)
 {
@@ -94,6 +128,7 @@ CcRosFlushCacheSegment(PCACHE_SEGMENT CacheSegment)
       KeAcquireSpinLock(&CacheSegment->Bcb->BcbLock, &oldIrql);
       CacheSegment->Dirty = FALSE;
       RemoveEntryList(&CacheSegment->DirtySegmentListEntry);
+      DirtyPageCount -= CacheSegment->Bcb->CacheSegmentSize / PAGE_SIZE;
       CacheSegment->ReferenceCount--;
       KeReleaseSpinLock(&CacheSegment->Bcb->BcbLock, oldIrql);
       ExReleaseFastMutex(&ViewLock);
@@ -101,6 +136,9 @@ CcRosFlushCacheSegment(PCACHE_SEGMENT CacheSegment)
   return(Status);
 }
 
+VOID CcRosRemoveUnusedFiles(VOID);
+
+
 NTSTATUS
 CcRosFlushDirtyPages(ULONG Target, PULONG Count)
 {
@@ -109,13 +147,40 @@ CcRosFlushDirtyPages(ULONG Target, PULONG Count)
   ULONG PagesPerSegment;
   BOOLEAN Locked;
   NTSTATUS Status;
+  static ULONG WriteCount[4] = {0, 0, 0, 0};
+  ULONG NewTarget;
 
   DPRINT("CcRosFlushDirtyPages(Target %d)\n", Target);
 
   (*Count) = 0;
 
   ExAcquireFastMutex(&ViewLock);
+
+  WriteCount[0] = WriteCount[1];
+  WriteCount[1] = WriteCount[2];
+  WriteCount[2] = WriteCount[3];
+  WriteCount[3] = 0;
+
+  NewTarget = WriteCount[0] + WriteCount[1] + WriteCount[2];
+
+  if (NewTarget < DirtyPageCount)
+  {
+     NewTarget = (DirtyPageCount - NewTarget + 3) / 4;
+     WriteCount[0] += NewTarget;
+     WriteCount[1] += NewTarget;
+     WriteCount[2] += NewTarget;
+     WriteCount[3] += NewTarget;
+  }
+
+  NewTarget = WriteCount[0];
+  
+  Target = max(NewTarget, Target);
+
   current_entry = DirtySegmentListHead.Flink;
+  if (current_entry == &DirtySegmentListHead)
+  {
+     DPRINT("No Dirty pages\n");
+  }
   while (current_entry != &DirtySegmentListHead && Target > 0)
     {
       current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT, 
@@ -148,8 +213,13 @@ CcRosFlushDirtyPages(ULONG Target, PULONG Count)
       ExAcquireFastMutex(&ViewLock);
       current_entry = DirtySegmentListHead.Flink;
     }
+  if (*Count < NewTarget)
+  {
+     WriteCount[1] += (NewTarget - *Count);
+  }
   ExReleaseFastMutex(&ViewLock);
-  DPRINT("CcRosTrimCache() finished\n");
+  DPRINT("CcRosFlushDirtyPages() finished\n");
+
   return(STATUS_SUCCESS);
 }
 
@@ -217,7 +287,9 @@ CcRosTrimCache(ULONG Target, ULONG Priority, PULONG NrFreed)
   return(STATUS_SUCCESS);
 }
 
-NTSTATUS STDCALL 
+#endif /* LIBCAPTIVE */
+
+NTSTATUS 
 CcRosReleaseCacheSegment(PBCB Bcb,
                         PCACHE_SEGMENT CacheSeg,
                         BOOLEAN Valid,
@@ -227,6 +299,8 @@ CcRosReleaseCacheSegment(PBCB Bcb,
   BOOLEAN WasDirty = CacheSeg->Dirty;
   KIRQL oldIrql;
 
+  assert(Bcb);
+
   DPRINT("CcReleaseCacheSegment(Bcb %x, CacheSeg %x, Valid %d)\n",
         Bcb, CacheSeg, Valid);
 
@@ -237,6 +311,7 @@ CcRosReleaseCacheSegment(PBCB Bcb,
   if (!WasDirty && CacheSeg->Dirty)
     {
       InsertTailList(&DirtySegmentListHead, &CacheSeg->DirtySegmentListEntry);
+      DirtyPageCount += Bcb->CacheSegmentSize / PAGE_SIZE;
     }
   RemoveEntryList(&CacheSeg->CacheSegmentLRUListEntry);
   InsertTailList(&CacheSegmentLRUListHead, &CacheSeg->CacheSegmentLRUListEntry);
@@ -262,12 +337,15 @@ CcRosReleaseCacheSegment(PBCB Bcb,
   return(STATUS_SUCCESS);
 }
 
-PCACHE_SEGMENT CcRosLookupCacheSegment(PBCB Bcb, ULONG FileOffset)
+PCACHE_SEGMENT 
+CcRosLookupCacheSegment(PBCB Bcb, ULONG FileOffset)
 {
   PLIST_ENTRY current_entry;
   PCACHE_SEGMENT current;
   KIRQL oldIrql;
 
+  assert(Bcb);
+
   DPRINT("CcRosLookupCacheSegment(Bcb %x, FileOffset %d)\n", Bcb, FileOffset);
 
   KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
@@ -289,12 +367,16 @@ PCACHE_SEGMENT CcRosLookupCacheSegment(PBCB Bcb, ULONG FileOffset)
   return(NULL);
 }
 
+#ifndef LIBCAPTIVE
+
 NTSTATUS
 CcRosMarkDirtyCacheSegment(PBCB Bcb, ULONG FileOffset)
 {
   PCACHE_SEGMENT CacheSeg;
   KIRQL oldIrql;
 
+  assert(Bcb);
+
   DPRINT("CcRosMarkDirtyCacheSegment(Bcb %x, FileOffset %d)\n", Bcb, FileOffset);
 
   CacheSeg = CcRosLookupCacheSegment(Bcb, FileOffset);
@@ -307,6 +389,7 @@ CcRosMarkDirtyCacheSegment(PBCB Bcb, ULONG FileOffset)
     {
       ExAcquireFastMutex(&ViewLock);
       InsertTailList(&DirtySegmentListHead, &CacheSeg->DirtySegmentListEntry);
+      DirtyPageCount += Bcb->CacheSegmentSize / PAGE_SIZE;
       ExReleaseFastMutex(&ViewLock);
     }
   else
@@ -330,6 +413,8 @@ CcRosUnmapCacheSegment(PBCB Bcb, ULONG FileOffset, BOOLEAN NowDirty)
   BOOLEAN WasDirty;
   KIRQL oldIrql;
 
+  assert(Bcb);
+
   DPRINT("CcRosUnmapCacheSegment(Bcb %x, FileOffset %d, NowDirty %d)\n",
           Bcb, FileOffset, NowDirty);
 
@@ -349,6 +434,7 @@ CcRosUnmapCacheSegment(PBCB Bcb, ULONG FileOffset, BOOLEAN NowDirty)
   {
      ExAcquireFastMutex(&ViewLock);
      InsertTailList(&DirtySegmentListHead, &CacheSeg->DirtySegmentListEntry);
+     DirtyPageCount += Bcb->CacheSegmentSize / PAGE_SIZE;
      ExReleaseFastMutex(&ViewLock);
   }
 
@@ -368,22 +454,30 @@ CcRosUnmapCacheSegment(PBCB Bcb, ULONG FileOffset, BOOLEAN NowDirty)
   return(STATUS_SUCCESS);
 }
 
+#endif /* LIBCAPTIVE */
+
 NTSTATUS STATIC
 CcRosCreateCacheSegment(PBCB Bcb,
                        ULONG FileOffset,
                        PCACHE_SEGMENT* CacheSeg,
                        BOOLEAN Lock)
 {
+#ifndef LIBCAPTIVE
   ULONG i;
+#endif /* LIBCAPTIVE */
   PCACHE_SEGMENT current;
   PLIST_ENTRY current_entry;
   NTSTATUS Status;
   KIRQL oldIrql;
+#ifdef CACHE_BITMAP
+  ULONG StartingOffset;
+#endif
+
+  assert(Bcb);
 
   DPRINT("CcRosCreateCacheSegment()\n");
 
-  current = ExAllocatePoolWithTag(NonPagedPool, sizeof(CACHE_SEGMENT), 
-                                 TAG_CSEG);
+  current = ExAllocateFromNPagedLookasideList(&CacheSegLookasideList);
   current->Valid = FALSE;
   current->Dirty = FALSE;
   current->FileOffset = ROUND_DOWN(FileOffset, Bcb->CacheSegmentSize);
@@ -415,7 +509,7 @@ CcRosCreateCacheSegment(PBCB Bcb,
        KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
        ExReleaseFastMutex(&(*CacheSeg)->Lock);
        ExReleaseFastMutex(&ViewLock);
-       ExFreePool(*CacheSeg);
+       ExFreeToNPagedLookasideList(&CacheSegLookasideList, *CacheSeg);
        *CacheSeg = current;
        if (Lock)
        {
@@ -432,7 +526,26 @@ CcRosCreateCacheSegment(PBCB Bcb,
   InsertTailList(&CacheSegmentListHead, &current->CacheSegmentListEntry);
   InsertTailList(&CacheSegmentLRUListHead, &current->CacheSegmentLRUListEntry);
   ExReleaseFastMutex(&ViewLock);
+#ifdef CACHE_BITMAP
+  KeAcquireSpinLock(&CiCacheSegMappingRegionLock, &oldIrql);
+
+  StartingOffset = RtlFindClearBitsAndSet(&CiCacheSegMappingRegionAllocMap, Bcb->CacheSegmentSize / PAGE_SIZE, CiCacheSegMappingRegionHint);
+  
+  if (StartingOffset == 0xffffffff)
+  {
+     DPRINT1("Out of CacheSeg mapping space\n");
+     KeBugCheck(0);
+  }
+
+  current->BaseAddress = CiCacheSegMappingRegionBase + StartingOffset * PAGE_SIZE;
+
+  if (CiCacheSegMappingRegionHint == StartingOffset)
+  {
+     CiCacheSegMappingRegionHint += Bcb->CacheSegmentSize / PAGE_SIZE; 
+  }
 
+  KeReleaseSpinLock(&CiCacheSegMappingRegionLock, oldIrql);
+#else
   MmLockAddressSpace(MmGetKernelAddressSpace());
   current->BaseAddress = NULL;
   Status = MmCreateMemoryArea(NULL,
@@ -448,6 +561,8 @@ CcRosCreateCacheSegment(PBCB Bcb,
   {
      KeBugCheck(0);
   }
+#endif
+#ifndef LIBCAPTIVE
   for (i = 0; i < (Bcb->CacheSegmentSize / PAGE_SIZE); i++)
   {
      PHYSICAL_ADDRESS Page;
@@ -468,6 +583,9 @@ CcRosCreateCacheSegment(PBCB Bcb,
        KeBugCheck(0);
      }
   }
+#else /* !LIBCAPTIVE */
+  /* libcaptive MmCreateMemoryArea() returns already allocated memory */
+#endif /* !LIBCAPTIVE */
   if (!Lock)
   {
      ExReleaseFastMutex(&current->Lock);
@@ -476,6 +594,8 @@ CcRosCreateCacheSegment(PBCB Bcb,
   return(STATUS_SUCCESS);
 }
 
+#ifndef LIBCAPTIVE
+
 NTSTATUS
 CcRosGetCacheSegmentChain(PBCB Bcb,
                          ULONG FileOffset,
@@ -487,6 +607,8 @@ CcRosGetCacheSegmentChain(PBCB Bcb,
   PCACHE_SEGMENT* CacheSegList;
   PCACHE_SEGMENT Previous = NULL;
 
+  assert(Bcb);
+
   DPRINT("CcRosGetCacheSegmentChain()\n");
 
   Length = ROUND_UP(Length, Bcb->CacheSegmentSize);
@@ -531,6 +653,8 @@ CcRosGetCacheSegmentChain(PBCB Bcb,
   return(STATUS_SUCCESS);
 }
 
+#endif /* LIBCAPTIVE */
+
 NTSTATUS
 CcRosGetCacheSegment(PBCB Bcb,
                     ULONG FileOffset,
@@ -542,6 +666,8 @@ CcRosGetCacheSegment(PBCB Bcb,
    PCACHE_SEGMENT current;
    NTSTATUS Status;
 
+   assert(Bcb);
+
    DPRINT("CcRosGetCacheSegment()\n");
 
    /*
@@ -582,6 +708,8 @@ CcRosRequestCacheSegment(PBCB Bcb,
 {
   ULONG BaseOffset;
 
+  assert(Bcb);
+
   if ((FileOffset % Bcb->CacheSegmentSize) != 0)
     {
       CPRINT("Bad fileoffset %x should be multiple of %x",
@@ -597,6 +725,10 @@ CcRosRequestCacheSegment(PBCB Bcb,
                           CacheSeg));
 }
 
+#ifndef LIBCAPTIVE
+
+#ifdef CACHE_BITMAP
+#else
 STATIC VOID 
 CcFreeCachePage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address, 
                PHYSICAL_ADDRESS PhysAddr, SWAPENTRY SwapEntry, BOOLEAN Dirty)
@@ -607,16 +739,45 @@ CcFreeCachePage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address,
       MmReleasePageMemoryConsumer(MC_CACHE, PhysAddr);
     }
 }
-
-NTSTATUS STDCALL 
+#endif
+NTSTATUS 
 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg)
 /*
  * FUNCTION: Releases a cache segment associated with a BCB
  */
 {
-
+#ifdef CACHE_BITMAP
+  ULONG i;
+  ULONG RegionSize;
+  ULONG Base;
+  PHYSICAL_ADDRESS PhysicalAddr;
+  KIRQL oldIrql;
+#endif
   DPRINT("Freeing cache segment %x\n", CacheSeg);
+#ifdef CACHE_BITMAP
+  RegionSize = CacheSeg->Bcb->CacheSegmentSize / PAGE_SIZE;
+
+  /* Unmap all the pages. */
+  for (i = 0; i < RegionSize; i++)
+    {
+      MmDeleteVirtualMapping(NULL, 
+                            CacheSeg->BaseAddress + (i * PAGE_SIZE),
+                            FALSE,
+                            NULL,
+                            &PhysicalAddr);
+      MmReleasePageMemoryConsumer(MC_CACHE, PhysicalAddr);
+    }
 
+  KeAcquireSpinLock(&CiCacheSegMappingRegionLock, &oldIrql);
+  /* Deallocate all the pages used. */
+  Base = (ULONG)(CacheSeg->BaseAddress - CiCacheSegMappingRegionBase) / PAGE_SIZE;
+  
+  RtlClearBits(&CiCacheSegMappingRegionAllocMap, Base, RegionSize);
+
+  CiCacheSegMappingRegionHint = min (CiCacheSegMappingRegionHint, Base);
+
+  KeReleaseSpinLock(&CiCacheSegMappingRegionLock, oldIrql);
+#else
   MmLockAddressSpace(MmGetKernelAddressSpace());
   MmFreeMemoryArea(MmGetKernelAddressSpace(),
                   CacheSeg->BaseAddress,
@@ -624,16 +785,19 @@ CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg)
                   CcFreeCachePage,
                   NULL);
   MmUnlockAddressSpace(MmGetKernelAddressSpace());
-  ExFreePool(CacheSeg);
+#endif
+  ExFreeToNPagedLookasideList(&CacheSegLookasideList, CacheSeg);
   return(STATUS_SUCCESS);
 }
 
-NTSTATUS STDCALL 
+NTSTATUS
 CcRosFreeCacheSegment(PBCB Bcb, PCACHE_SEGMENT CacheSeg)
 {
   NTSTATUS Status;
   KIRQL oldIrql;
 
+  assert(Bcb);
+
   DPRINT("CcRosFreeCacheSegment(Bcb %x, CacheSeg %x)\n",
          Bcb, CacheSeg);
 
@@ -645,6 +809,8 @@ CcRosFreeCacheSegment(PBCB Bcb, PCACHE_SEGMENT CacheSeg)
   if (CacheSeg->Dirty)
   {
      RemoveEntryList(&CacheSeg->DirtySegmentListEntry);
+     DirtyPageCount -= Bcb->CacheSegmentSize / PAGE_SIZE;
+
   }
   KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
   ExReleaseFastMutex(&ViewLock);
@@ -671,6 +837,7 @@ CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointers,
    if (SectionObjectPointers && SectionObjectPointers->SharedCacheMap)
    {
       Bcb = (PBCB)SectionObjectPointers->SharedCacheMap;
+      assert(Bcb);
       if (FileOffset)
       {
         Offset = *FileOffset;
@@ -701,8 +868,8 @@ CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointers,
                   IoStatus->Status = Status;
               }
            }
-           ExReleaseFastMutex(&current->Lock);
             KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
+           ExReleaseFastMutex(&current->Lock);
            current->ReferenceCount--;
            KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
         }
@@ -727,7 +894,7 @@ CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointers,
    }
 }
 
-NTSTATUS STDCALL 
+NTSTATUS 
 CcRosDeleteFileCache(PFILE_OBJECT FileObject, PBCB Bcb)
 /*
  * FUNCTION: Releases the BCB associated with a file object
@@ -738,26 +905,30 @@ CcRosDeleteFileCache(PFILE_OBJECT FileObject, PBCB Bcb)
    NTSTATUS Status;
    LIST_ENTRY FreeList;
    KIRQL oldIrql;
-   
-   DPRINT("CcRosDeleteFileCache(FileObject %x, Bcb %x)\n", 
-          Bcb->FileObject, Bcb);
 
+   assert(Bcb);
+   
+   Bcb->RefCount++;
    ExReleaseFastMutex(&ViewLock);
 
    CcFlushCache(FileObject->SectionObjectPointers, NULL, 0, NULL);
 
    ExAcquireFastMutex(&ViewLock);
-
+   Bcb->RefCount--;
    if (Bcb->RefCount == 0)
    {
-      MmFreeSectionSegments(Bcb->FileObject);
+      if (Bcb->BcbRemoveListEntry.Flink != NULL)
+      {
+        RemoveEntryList(&Bcb->BcbRemoveListEntry);
+         Bcb->BcbRemoveListEntry.Flink = NULL;
+      }
+
+      FileObject->SectionObjectPointers->SharedCacheMap = NULL;  
 
       /*
        * Release all cache segments.
        */
       InitializeListHead(&FreeList);
-
-      FileObject->SectionObjectPointers->SharedCacheMap = NULL;  
       KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
       current_entry = Bcb->BcbSegmentListHead.Flink;
       while (!IsListEmpty(&Bcb->BcbSegmentListHead))
@@ -769,20 +940,24 @@ CcRosDeleteFileCache(PFILE_OBJECT FileObject, PBCB Bcb)
          if (current->Dirty)
         {
             RemoveEntryList(&current->DirtySegmentListEntry);
+            DirtyPageCount -= Bcb->CacheSegmentSize / PAGE_SIZE;
+           DPRINT1("Freeing dirty segment\n");
         }
          InsertHeadList(&FreeList, &current->BcbSegmentListEntry);
-
       }
       KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);       
+
+      ExReleaseFastMutex(&ViewLock);
+      ObDereferenceObject (Bcb->FileObject);
+
       while (!IsListEmpty(&FreeList))
       {
          current_entry = RemoveTailList(&FreeList);
          current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT, BcbSegmentListEntry);
          Status = CcRosInternalFreeCacheSegment(current);
       }
-      
-      ObDereferenceObject (Bcb->FileObject);
-      ExFreePool(Bcb);
+      ExFreeToNPagedLookasideList(&BcbLookasideList, Bcb);   
+      ExAcquireFastMutex(&ViewLock);
    }
    return(STATUS_SUCCESS);
 }
@@ -792,19 +967,51 @@ VOID CcRosReferenceCache(PFILE_OBJECT FileObject)
   PBCB Bcb;
   ExAcquireFastMutex(&ViewLock);
   Bcb = (PBCB)FileObject->SectionObjectPointers->SharedCacheMap;
+  assert(Bcb);
   Bcb->RefCount++;
   ExReleaseFastMutex(&ViewLock);
 }
 
+VOID CcRosSetRemoveOnClose(PSECTION_OBJECT_POINTERS SectionObjectPointer)
+{
+  PBCB Bcb;
+//  DPRINT1("CcRosSetRemoveOnClose()\n");
+  ExAcquireFastMutex(&ViewLock);
+  Bcb = (PBCB)SectionObjectPointer->SharedCacheMap;
+  if (Bcb)
+  {
+    Bcb->RemoveOnClose = TRUE;
+    if (Bcb->RefCount == 0)
+    {
+      CcRosDeleteFileCache(Bcb->FileObject, Bcb);
+    }
+  }
+  ExReleaseFastMutex(&ViewLock);
+}
+
+
 VOID CcRosDereferenceCache(PFILE_OBJECT FileObject)
 {
   PBCB Bcb;
   ExAcquireFastMutex(&ViewLock);
   Bcb = (PBCB)FileObject->SectionObjectPointers->SharedCacheMap;
-  Bcb->RefCount--;
-  if (Bcb->RefCount == 0)
+  assert(Bcb);
+  if (Bcb->RefCount > 0)
   {
-     CcRosDeleteFileCache(FileObject, Bcb);
+    Bcb->RefCount--;
+    if (Bcb->RefCount == 0)
+    {
+       MmFreeSectionSegments(Bcb->FileObject);
+       if (Bcb->RemoveOnClose)
+       {
+          CcRosDeleteFileCache(FileObject, Bcb);
+       }
+       else
+       {
+         Bcb->TimeStamp = CcTimeStamp;
+          InsertHeadList(&ClosedListHead, &Bcb->BcbRemoveListEntry);
+       }
+    }
   }
   ExReleaseFastMutex(&ViewLock);
 }
@@ -816,6 +1023,8 @@ CcRosReleaseFileCache(PFILE_OBJECT FileObject, PBCB Bcb)
  * has been closed.
  */
 {
+  assert(Bcb);
+
   ExAcquireFastMutex(&ViewLock);
 
   if (FileObject->SectionObjectPointers->SharedCacheMap != NULL)
@@ -823,11 +1032,22 @@ CcRosReleaseFileCache(PFILE_OBJECT FileObject, PBCB Bcb)
     if (FileObject->PrivateCacheMap != NULL)
     {
       FileObject->PrivateCacheMap = NULL;
-      Bcb->RefCount--;
-    }
-    if (Bcb->RefCount == 0)
-    {
-      CcRosDeleteFileCache(FileObject, Bcb);
+      if (Bcb->RefCount > 0)
+      {
+         Bcb->RefCount--;
+        if (Bcb->RefCount == 0)
+        {
+           if (Bcb->RemoveOnClose)
+           {
+              CcRosDeleteFileCache(FileObject, Bcb);
+           }
+           else
+           {
+              Bcb->TimeStamp = CcTimeStamp;
+              InsertHeadList(&ClosedListHead, &Bcb->BcbRemoveListEntry);
+           }
+        }
+      }
     }
   }
   ExReleaseFastMutex(&ViewLock);
@@ -849,12 +1069,13 @@ CcRosInitializeFileCache(PFILE_OBJECT FileObject,
 
    if (*Bcb == NULL)
    {
-      (*Bcb) = ExAllocatePoolWithTag(NonPagedPool, sizeof(BCB), TAG_BCB);
+      (*Bcb) = ExAllocateFromNPagedLookasideList(&BcbLookasideList);   
       if ((*Bcb) == NULL)
       {
+        ExReleaseFastMutex(&ViewLock);
        return(STATUS_UNSUCCESSFUL);
       }
-   
+      memset((*Bcb), 0, sizeof(BCB));      
       ObReferenceObjectByPointer(FileObject,
                                 FILE_ALL_ACCESS,
                                 NULL,
@@ -877,6 +1098,11 @@ CcRosInitializeFileCache(PFILE_OBJECT FileObject,
       FileObject->PrivateCacheMap = *Bcb;
       (*Bcb)->RefCount++;
    }
+   if ((*Bcb)->BcbRemoveListEntry.Flink != NULL)
+   {
+      RemoveEntryList(&(*Bcb)->BcbRemoveListEntry);
+      (*Bcb)->BcbRemoveListEntry.Flink = NULL;
+   }
    ExReleaseFastMutex(&ViewLock);
 
    return(STATUS_SUCCESS);
@@ -889,21 +1115,158 @@ CcGetFileObjectFromSectionPtrs(IN PSECTION_OBJECT_POINTERS SectionObjectPointers
    if (SectionObjectPointers && SectionObjectPointers->SharedCacheMap)
    {
       Bcb = (PBCB)SectionObjectPointers->SharedCacheMap;
+      assert(Bcb);
       return Bcb->FileObject;
    }
    return NULL;
 }
 
+#endif /* LIBCAPTIVE */
+
+VOID STDCALL
+CmLazyCloseThreadMain(PVOID Ignored)
+{
+   LARGE_INTEGER Timeout;
+   PLIST_ENTRY current_entry;
+   PBCB current;
+   ULONG RemoveTimeStamp;
+   NTSTATUS Status;
+
+   KeQuerySystemTime (&Timeout);
+
+   while (1)
+   {
+      Timeout.QuadPart += 100000000LL; // 10sec
+      Status = KeWaitForSingleObject(&LazyCloseThreadEvent,
+                                    0,
+                                    KernelMode,
+                                    FALSE,
+                                    &Timeout);
+
+      DPRINT("LazyCloseThreadMain %d\n", CcTimeStamp);
+
+      if (!NT_SUCCESS(Status))
+      {
+         DbgPrint("LazyCloseThread: Wait failed\n");
+         KeBugCheck(0);
+         break;
+      }
+      if (LazyCloseThreadShouldTerminate)
+      {
+          DbgPrint("LazyCloseThread: Terminating\n");
+         break;
+      }
+      
+      ExAcquireFastMutex(&ViewLock);
+      CcTimeStamp++;
+      if (CcTimeStamp >= 30)
+      {
+         RemoveTimeStamp = CcTimeStamp - 30; /* 5min = 10sec * 30 */
+         while (!IsListEmpty(&ClosedListHead))
+        {
+            current_entry = ClosedListHead.Blink;
+            current = CONTAINING_RECORD(current_entry, BCB, BcbRemoveListEntry);
+           if (current->TimeStamp >= RemoveTimeStamp)
+           {
+              break;
+           }
+            CcRosDeleteFileCache(current->FileObject, current);
+        }
+      }
+      ExReleaseFastMutex(&ViewLock);
+   }
+}
+
 VOID
 CcInitView(VOID)
 {
+#ifdef CACHE_BITMAP
+  PMEMORY_AREA marea;
+  PVOID Buffer;
+#endif
+  NTSTATUS Status;
+  KPRIORITY Priority;
+
   DPRINT("CcInitView()\n");
+#ifdef CACHE_BITMAP
+  CiCacheSegMappingRegionHint = 0;
+  CiCacheSegMappingRegionBase = NULL;
+
+  MmLockAddressSpace(MmGetKernelAddressSpace());
+
+  Status = MmCreateMemoryArea(NULL,
+                             MmGetKernelAddressSpace(),
+                             MEMORY_AREA_CACHE_SEGMENT,
+                             &CiCacheSegMappingRegionBase,
+                             CI_CACHESEG_MAPPING_REGION_SIZE,
+                             0,
+                             &marea,
+                             FALSE);
+  MmUnlockAddressSpace(MmGetKernelAddressSpace());
+  if (!NT_SUCCESS(Status))
+    {
+      KeBugCheck(0);
+    }
+
+  Buffer = ExAllocatePool(NonPagedPool, CI_CACHESEG_MAPPING_REGION_SIZE / (PAGE_SIZE * 8));
+
+  RtlInitializeBitMap(&CiCacheSegMappingRegionAllocMap, Buffer, CI_CACHESEG_MAPPING_REGION_SIZE / PAGE_SIZE);
+  RtlClearAllBits(&CiCacheSegMappingRegionAllocMap);
+
+  KeInitializeSpinLock(&CiCacheSegMappingRegionLock);
+#endif  
   InitializeListHead(&CacheSegmentListHead);
   InitializeListHead(&DirtySegmentListHead);
   InitializeListHead(&CacheSegmentLRUListHead);
+  InitializeListHead(&ClosedListHead);
   ExInitializeFastMutex(&ViewLock);
+#ifndef LIBCAPTIVE
+  ExInitializeNPagedLookasideList (&iBcbLookasideList,
+                                  NULL,
+                                  NULL,
+                                  0,
+                                  sizeof(INTERNAL_BCB),
+                                  TAG_IBCB,
+                                  20);
+  ExInitializeNPagedLookasideList (&BcbLookasideList,
+                                  NULL,
+                                  NULL,
+                                  0,
+                                  sizeof(BCB),
+                                  TAG_BCB,
+                                  20);
+  ExInitializeNPagedLookasideList (&CacheSegLookasideList,
+                                  NULL,
+                                  NULL,
+                                  0,
+                                  sizeof(CACHE_SEGMENT),
+                                  TAG_CSEG,
+                                  20);
+
   MmInitializeMemoryConsumer(MC_CACHE, CcRosTrimCache);
+  
   CcInitCacheZeroPage();
+#endif /* LIBCAPTIVE */
+
+  CcTimeStamp = 0;  
+  LazyCloseThreadShouldTerminate = FALSE;
+  KeInitializeEvent (&LazyCloseThreadEvent, SynchronizationEvent, FALSE);
+  Status = PsCreateSystemThread(&LazyCloseThreadHandle,
+                               THREAD_ALL_ACCESS,
+                               NULL,
+                               NULL,
+                               &LazyCloseThreadId,
+                               (PKSTART_ROUTINE)CmLazyCloseThreadMain,
+                               NULL);
+  if (NT_SUCCESS(Status))
+  {
+     Priority = LOW_REALTIME_PRIORITY;
+     NtSetInformationThread(LazyCloseThreadHandle,
+                           ThreadPriority,
+                           &Priority,
+                           sizeof(Priority));
+  }
+
 }
 
 /* EOF */