[NTOSKRNL] Add an helper for marking a VACB clean (ie, not dirty).
[reactos.git] / ntoskrnl / cc / view.c
index 8fb7f53..f66f230 100644 (file)
@@ -5,6 +5,7 @@
  * PURPOSE:         Cache manager
  *
  * PROGRAMMERS:     David Welch (welch@mcmail.com)
+ *                  Pierre Schweitzer (pierre@reactos.org)
  */
 
 /* NOTES **********************************************************************
@@ -60,16 +61,26 @@ ULONG CcLazyWriteIos = 0;
 /* Internal vars (MS):
  * - Threshold above which lazy writer will start action
  * - Amount of dirty pages
+ * - List for deferred writes
+ * - Spinlock when dealing with the deferred list
+ * - List for "clean" shared cache maps
+ * - One second delay for lazy writer
  */
 ULONG CcDirtyPageThreshold = 0;
 ULONG CcTotalDirtyPages = 0;
+LIST_ENTRY CcDeferredWrites;
+KSPIN_LOCK CcDeferredWriteSpinLock;
+LIST_ENTRY CcCleanSharedCacheMapList;
+LARGE_INTEGER CcIdleDelay = RTL_CONSTANT_LARGE_INTEGER((LONGLONG)-1*1000*1000*10);
 
 /* Internal vars (ROS):
  * - Event to notify lazy writer to shutdown
  * - Event to inform watchers lazy writer is done for this loop
+ * - Lock for the CcCleanSharedCacheMapList list
  */
 KEVENT iLazyWriterShutdown;
 KEVENT iLazyWriterNotify;
+KSPIN_LOCK iSharedCacheMapLock;
 
 #if DBG
 static void CcRosVacbIncRefCount_(PROS_VACB vacb, const char* file, int line)
@@ -84,6 +95,7 @@ static void CcRosVacbIncRefCount_(PROS_VACB vacb, const char* file, int line)
 static void CcRosVacbDecRefCount_(PROS_VACB vacb, const char* file, int line)
 {
     --vacb->ReferenceCount;
+    ASSERT(!(vacb->ReferenceCount == 0 && vacb->Dirty));
     if (vacb->SharedCacheMap->Trace)
     {
         DbgPrint("(%s:%i) VACB %p --RefCount=%lu, Dirty %u, PageOut %lu\n",
@@ -155,21 +167,11 @@ CcRosFlushVacb (
     PROS_VACB Vacb)
 {
     NTSTATUS Status;
-    KIRQL oldIrql;
 
     Status = CcWriteVirtualAddress(Vacb);
     if (NT_SUCCESS(Status))
     {
-        KeAcquireGuardedMutex(&ViewLock);
-        KeAcquireSpinLock(&Vacb->SharedCacheMap->CacheMapLock, &oldIrql);
-
-        Vacb->Dirty = FALSE;
-        RemoveEntryList(&Vacb->DirtyVacbListEntry);
-        CcTotalDirtyPages -= VACB_MAPPING_GRANULARITY / PAGE_SIZE;
-        CcRosVacbDecRefCount(Vacb);
-
-        KeReleaseSpinLock(&Vacb->SharedCacheMap->CacheMapLock, oldIrql);
-        KeReleaseGuardedMutex(&ViewLock);
+        CcRosUnmarkDirtyVacb(Vacb, TRUE);
     }
 
     return Status;
@@ -241,7 +243,8 @@ CcRosFlushDirtyPages (
         ASSERT(current->Dirty);
 
         /* One reference is added above */
-        if (current->ReferenceCount > 2)
+        if ((current->ReferenceCount > 2 && current->PinCount == 0) ||
+            (current->ReferenceCount > 3 && current->PinCount > 1))
         {
             CcRosReleaseVacbLock(current);
             current->SharedCacheMap->Callbacks->ReleaseFromLazyWrite(
@@ -301,13 +304,10 @@ VOID
 NTAPI
 CciLazyWriter(PVOID Unused)
 {
-    LARGE_INTEGER OneSecond;
-
-    OneSecond.QuadPart = (LONGLONG)-1*1000*1000*10;
-
     while (TRUE)
     {
         NTSTATUS Status;
+        PLIST_ENTRY ListEntry;
         ULONG Target, Count = 0;
 
         /* One per second or until we have to stop */
@@ -315,7 +315,7 @@ CciLazyWriter(PVOID Unused)
                                        Executive,
                                        KernelMode,
                                        FALSE,
-                                       &OneSecond);
+                                       &CcIdleDelay);
 
         /* If we succeeed, we've to stop running! */
         if (Status == STATUS_SUCCESS)
@@ -326,27 +326,51 @@ CciLazyWriter(PVOID Unused)
         /* We're not sleeping anymore */
         KeClearEvent(&iLazyWriterNotify);
 
-        /* Only start operations if above threshold */
-        DPRINT("TS: %lu, Count: %lu\n", CcDirtyPageThreshold, CcTotalDirtyPages);
-        if (CcTotalDirtyPages > CcDirtyPageThreshold)
+        /* Our target is one-eighth of the dirty pages */
+        Target = CcTotalDirtyPages / 8;
+        if (Target != 0)
         {
-            /* Our target is one-eighth of the dirty pages */
-            Target = CcTotalDirtyPages / 8;
-            if (Target != 0)
-            {
-                /* Flush! */
-                DPRINT("Lazy writer starting (%d)\n", Target);
-                CcRosFlushDirtyPages(Target, &Count, FALSE, TRUE);
-
-                /* And update stats */
-                CcLazyWritePages += Count;
-                ++CcLazyWriteIos;
-                DPRINT("Lazy writer done (%d)\n", Count);
-            }
+            /* Flush! */
+            DPRINT("Lazy writer starting (%d)\n", Target);
+            CcRosFlushDirtyPages(Target, &Count, FALSE, TRUE);
+
+            /* And update stats */
+            CcLazyWritePages += Count;
+            ++CcLazyWriteIos;
+            DPRINT("Lazy writer done (%d)\n", Count);
         }
 
         /* Inform people waiting on us that we're done */
         KeSetEvent(&iLazyWriterNotify, IO_DISK_INCREMENT, FALSE);
+
+        /* Likely not optimal, but let's handle one deferred write now! */
+        ListEntry = ExInterlockedRemoveHeadList(&CcDeferredWrites, &CcDeferredWriteSpinLock);
+        if (ListEntry != NULL)
+        {
+            PDEFERRED_WRITE Context;
+
+            /* Extract the context */
+            Context = CONTAINING_RECORD(ListEntry, DEFERRED_WRITE, DeferredWriteLinks);
+            ASSERT(Context->NodeTypeCode == NODE_TYPE_DEFERRED_WRITE);
+
+            /* Can we write now? */
+            if (CcCanIWrite(Context->FileObject, Context->BytesToWrite, FALSE, TRUE))
+            {
+                /* Yes! Do it, and destroy the associated context */
+                Context->PostRoutine(Context->Context1, Context->Context2);
+                ExFreePoolWithTag(Context, 'CcDw');
+            }
+            else
+            {
+                /* Otherwise, requeue it, but in tail, so that it doesn't block others
+                 * This is clearly to improve, but given the poor algorithm used now
+                 * It's better than nothing!
+                 */
+                ExInterlockedInsertTailList(&CcDeferredWrites,
+                                            &Context->DeferredWriteLinks,
+                                            &CcDeferredWriteSpinLock);
+            }
+        }
     }
 }
 
@@ -481,26 +505,16 @@ CcRosReleaseVacb (
     BOOLEAN Dirty,
     BOOLEAN Mapped)
 {
-    BOOLEAN WasDirty;
-    KIRQL oldIrql;
-
     ASSERT(SharedCacheMap);
 
     DPRINT("CcRosReleaseVacb(SharedCacheMap 0x%p, Vacb 0x%p, Valid %u)\n",
            SharedCacheMap, Vacb, Valid);
 
-    KeAcquireGuardedMutex(&ViewLock);
-    KeAcquireSpinLock(&SharedCacheMap->CacheMapLock, &oldIrql);
-
     Vacb->Valid = Valid;
 
-    WasDirty = Vacb->Dirty;
-    Vacb->Dirty = Vacb->Dirty || Dirty;
-
-    if (!WasDirty && Vacb->Dirty)
+    if (Dirty && !Vacb->Dirty)
     {
-        InsertTailList(&DirtyVacbListHead, &Vacb->DirtyVacbListEntry);
-        CcTotalDirtyPages += VACB_MAPPING_GRANULARITY / PAGE_SIZE;
+        CcRosMarkDirtyVacb(Vacb);
     }
 
     if (Mapped)
@@ -512,13 +526,7 @@ CcRosReleaseVacb (
     {
         CcRosVacbIncRefCount(Vacb);
     }
-    if (!WasDirty && Vacb->Dirty)
-    {
-        CcRosVacbIncRefCount(Vacb);
-    }
 
-    KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, oldIrql);
-    KeReleaseGuardedMutex(&ViewLock);
     CcRosReleaseVacbLock(Vacb);
 
     return STATUS_SUCCESS;
@@ -570,14 +578,76 @@ CcRosLookupVacb (
     return NULL;
 }
 
-NTSTATUS
+VOID
 NTAPI
 CcRosMarkDirtyVacb (
+    PROS_VACB Vacb)
+{
+    KIRQL oldIrql;
+    PROS_SHARED_CACHE_MAP SharedCacheMap;
+
+    SharedCacheMap = Vacb->SharedCacheMap;
+
+    KeAcquireGuardedMutex(&ViewLock);
+    KeAcquireSpinLock(&SharedCacheMap->CacheMapLock, &oldIrql);
+
+    ASSERT(!Vacb->Dirty);
+
+    InsertTailList(&DirtyVacbListHead, &Vacb->DirtyVacbListEntry);
+    CcTotalDirtyPages += VACB_MAPPING_GRANULARITY / PAGE_SIZE;
+    Vacb->SharedCacheMap->DirtyPages += VACB_MAPPING_GRANULARITY / PAGE_SIZE;
+    CcRosVacbIncRefCount(Vacb);
+
+    /* Move to the tail of the LRU list */
+    RemoveEntryList(&Vacb->VacbLruListEntry);
+    InsertTailList(&VacbLruListHead, &Vacb->VacbLruListEntry);
+
+    Vacb->Dirty = TRUE;
+
+    KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, oldIrql);
+    KeReleaseGuardedMutex(&ViewLock);
+}
+
+VOID
+NTAPI
+CcRosUnmarkDirtyVacb (
+    PROS_VACB Vacb,
+    BOOLEAN LockViews)
+{
+    KIRQL oldIrql;
+    PROS_SHARED_CACHE_MAP SharedCacheMap;
+
+    SharedCacheMap = Vacb->SharedCacheMap;
+
+    if (LockViews)
+    {
+        KeAcquireGuardedMutex(&ViewLock);
+        KeAcquireSpinLock(&SharedCacheMap->CacheMapLock, &oldIrql);
+    }
+
+    ASSERT(Vacb->Dirty);
+
+    Vacb->Dirty = FALSE;
+
+    RemoveEntryList(&Vacb->DirtyVacbListEntry);
+    CcTotalDirtyPages -= VACB_MAPPING_GRANULARITY / PAGE_SIZE;
+    Vacb->SharedCacheMap->DirtyPages -= VACB_MAPPING_GRANULARITY / PAGE_SIZE;
+    CcRosVacbDecRefCount(Vacb);
+
+    if (LockViews)
+    {
+        KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, oldIrql);
+        KeReleaseGuardedMutex(&ViewLock);
+    }
+}
+
+NTSTATUS
+NTAPI
+CcRosMarkDirtyFile (
     PROS_SHARED_CACHE_MAP SharedCacheMap,
     LONGLONG FileOffset)
 {
     PROS_VACB Vacb;
-    KIRQL oldIrql;
 
     ASSERT(SharedCacheMap);
 
@@ -590,27 +660,11 @@ CcRosMarkDirtyVacb (
         KeBugCheck(CACHE_MANAGER);
     }
 
-    KeAcquireGuardedMutex(&ViewLock);
-    KeAcquireSpinLock(&SharedCacheMap->CacheMapLock, &oldIrql);
-
     if (!Vacb->Dirty)
     {
-        InsertTailList(&DirtyVacbListHead, &Vacb->DirtyVacbListEntry);
-        CcTotalDirtyPages += VACB_MAPPING_GRANULARITY / PAGE_SIZE;
-    }
-    else
-    {
-        CcRosVacbDecRefCount(Vacb);
+        CcRosMarkDirtyVacb(Vacb);
     }
 
-    /* Move to the tail of the LRU list */
-    RemoveEntryList(&Vacb->VacbLruListEntry);
-    InsertTailList(&VacbLruListHead, &Vacb->VacbLruListEntry);
-
-    Vacb->Dirty = TRUE;
-
-    KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, oldIrql);
-    KeReleaseGuardedMutex(&ViewLock);
     CcRosReleaseVacbLock(Vacb);
 
     return STATUS_SUCCESS;
@@ -624,8 +678,6 @@ CcRosUnmapVacb (
     BOOLEAN NowDirty)
 {
     PROS_VACB Vacb;
-    BOOLEAN WasDirty;
-    KIRQL oldIrql;
 
     ASSERT(SharedCacheMap);
 
@@ -638,32 +690,19 @@ CcRosUnmapVacb (
         return STATUS_UNSUCCESSFUL;
     }
 
-    KeAcquireGuardedMutex(&ViewLock);
-    KeAcquireSpinLock(&SharedCacheMap->CacheMapLock, &oldIrql);
-
-    WasDirty = Vacb->Dirty;
-    Vacb->Dirty = Vacb->Dirty || NowDirty;
-
-    Vacb->MappedCount--;
-
-    if (!WasDirty && NowDirty)
+    if (NowDirty && !Vacb->Dirty)
     {
-        InsertTailList(&DirtyVacbListHead, &Vacb->DirtyVacbListEntry);
-        CcTotalDirtyPages += VACB_MAPPING_GRANULARITY / PAGE_SIZE;
+        CcRosMarkDirtyVacb(Vacb);
     }
 
+    Vacb->MappedCount--;
+
     CcRosVacbDecRefCount(Vacb);
-    if (!WasDirty && NowDirty)
-    {
-        CcRosVacbIncRefCount(Vacb);
-    }
     if (Vacb->MappedCount == 0)
     {
         CcRosVacbDecRefCount(Vacb);
     }
 
-    KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, oldIrql);
-    KeReleaseGuardedMutex(&ViewLock);
     CcRosReleaseVacbLock(Vacb);
 
     return STATUS_SUCCESS;
@@ -836,7 +875,7 @@ CcRosCreateVacb (
 #if MI_TRACE_PFNS
     if ((SharedCacheMap->FileObject) && (SharedCacheMap->FileObject->FileName.Buffer))
     {
-        PWCHAR pos = NULL;
+        PWCHAR pos;
         ULONG len = 0;
         pos = wcsrchr(SharedCacheMap->FileObject->FileName.Buffer, '\\');
         if (pos)
@@ -1096,6 +1135,8 @@ CcRosDeleteFileCache (
     SharedCacheMap->OpenCount--;
     if (SharedCacheMap->OpenCount == 0)
     {
+        KIRQL OldIrql;
+
         FileObject->SectionObjectPointer->SharedCacheMap = NULL;
 
         /*
@@ -1106,15 +1147,22 @@ CcRosDeleteFileCache (
         while (!IsListEmpty(&SharedCacheMap->CacheMapVacbListHead))
         {
             current_entry = RemoveTailList(&SharedCacheMap->CacheMapVacbListHead);
+            KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, oldIrql);
+
             current = CONTAINING_RECORD(current_entry, ROS_VACB, CacheMapVacbListEntry);
+            CcRosAcquireVacbLock(current, NULL);
             RemoveEntryList(&current->VacbLruListEntry);
             if (current->Dirty)
             {
-                RemoveEntryList(&current->DirtyVacbListEntry);
-                CcTotalDirtyPages -= VACB_MAPPING_GRANULARITY / PAGE_SIZE;
+                KeAcquireSpinLock(&SharedCacheMap->CacheMapLock, &oldIrql);
+                CcRosUnmarkDirtyVacb(current, FALSE);
+                KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, oldIrql);
                 DPRINT1("Freeing dirty VACB\n");
             }
             InsertHeadList(&FreeList, &current->CacheMapVacbListEntry);
+            CcRosReleaseVacbLock(current);
+
+            KeAcquireSpinLock(&SharedCacheMap->CacheMapLock, &oldIrql);
         }
 #if DBG
         SharedCacheMap->Trace = FALSE;
@@ -1130,6 +1178,11 @@ CcRosDeleteFileCache (
             current = CONTAINING_RECORD(current_entry, ROS_VACB, CacheMapVacbListEntry);
             CcRosInternalFreeVacb(current);
         }
+
+        KeAcquireSpinLock(&iSharedCacheMapLock, &OldIrql);
+        RemoveEntryList(&SharedCacheMap->SharedCacheMapLinks);
+        KeReleaseSpinLock(&iSharedCacheMapLock, OldIrql);
+
         ExFreeToNPagedLookasideList(&SharedCacheMapLookasideList, SharedCacheMap);
         KeAcquireGuardedMutex(&ViewLock);
     }
@@ -1274,6 +1327,8 @@ CcRosInitializeFileCache (
     KeAcquireGuardedMutex(&ViewLock);
     if (SharedCacheMap == NULL)
     {
+        KIRQL OldIrql;
+
         SharedCacheMap = ExAllocateFromNPagedLookasideList(&SharedCacheMapLookasideList);
         if (SharedCacheMap == NULL)
         {
@@ -1291,9 +1346,15 @@ CcRosInitializeFileCache (
         SharedCacheMap->SectionSize = FileSizes->AllocationSize;
         SharedCacheMap->FileSize = FileSizes->FileSize;
         SharedCacheMap->PinAccess = PinAccess;
+        SharedCacheMap->DirtyPageThreshold = 0;
+        SharedCacheMap->DirtyPages = 0;
         KeInitializeSpinLock(&SharedCacheMap->CacheMapLock);
         InitializeListHead(&SharedCacheMap->CacheMapVacbListHead);
         FileObject->SectionObjectPointer->SharedCacheMap = SharedCacheMap;
+
+        KeAcquireSpinLock(&iSharedCacheMapLock, &OldIrql);
+        InsertTailList(&CcCleanSharedCacheMapList, &SharedCacheMap->SharedCacheMapLinks);
+        KeReleaseSpinLock(&iSharedCacheMapLock, OldIrql);
     }
     if (FileObject->PrivateCacheMap == NULL)
     {
@@ -1343,12 +1404,17 @@ CcInitView (
 {
     HANDLE LazyWriter;
     NTSTATUS Status;
+    KPRIORITY Priority;
     OBJECT_ATTRIBUTES ObjectAttributes;
 
     DPRINT("CcInitView()\n");
 
     InitializeListHead(&DirtyVacbListHead);
     InitializeListHead(&VacbLruListHead);
+    InitializeListHead(&CcDeferredWrites);
+    InitializeListHead(&CcCleanSharedCacheMapList);
+    KeInitializeSpinLock(&CcDeferredWriteSpinLock);
+    KeInitializeSpinLock(&iSharedCacheMapLock);
     KeInitializeGuardedMutex(&ViewLock);
     ExInitializeNPagedLookasideList(&iBcbLookasideList,
                                     NULL,
@@ -1412,6 +1478,13 @@ CcInitView (
         return FALSE;
     }
 
+    Priority = 27;
+    Status = NtSetInformationThread(LazyWriter,
+                                   ThreadPriority,
+                                   &Priority,
+                                   sizeof(Priority));
+    ASSERT(NT_SUCCESS(Status));
+
     /* Handle is not needed */
     ObCloseHandle(LazyWriter, KernelMode);
 
@@ -1420,4 +1493,61 @@ CcInitView (
     return TRUE;
 }
 
+#if DBG && defined(KDBG)
+BOOLEAN
+ExpKdbgExtFileCache(ULONG Argc, PCHAR Argv[])
+{
+    PLIST_ENTRY ListEntry;
+    UNICODE_STRING NoName = RTL_CONSTANT_STRING(L"No name for File");
+
+    KdbpPrint("  Usage Summary (in kb)\n");
+    KdbpPrint("Shared\t\tValid\tDirty\tName\n");
+    /* No need to lock the spin lock here, we're in DBG */
+    for (ListEntry = CcCleanSharedCacheMapList.Flink;
+         ListEntry != &CcCleanSharedCacheMapList;
+         ListEntry = ListEntry->Flink)
+    {
+        PLIST_ENTRY Vacbs;
+        ULONG Valid = 0, Dirty = 0;
+        PROS_SHARED_CACHE_MAP SharedCacheMap;
+        PUNICODE_STRING FileName;
+
+        SharedCacheMap = CONTAINING_RECORD(ListEntry, ROS_SHARED_CACHE_MAP, SharedCacheMapLinks);
+
+        /* Dirty size */
+        Dirty = (SharedCacheMap->DirtyPages * PAGE_SIZE) / 1024;
+
+        /* First, count for all the associated VACB */
+        for (Vacbs = SharedCacheMap->CacheMapVacbListHead.Flink;
+             Vacbs != &SharedCacheMap->CacheMapVacbListHead;
+             Vacbs = Vacbs->Flink)
+        {
+            PROS_VACB Vacb;
+
+            Vacb = CONTAINING_RECORD(Vacbs, ROS_VACB, CacheMapVacbListEntry);
+            if (Vacb->Valid)
+            {
+                Valid += VACB_MAPPING_GRANULARITY / 1024;
+            }
+        }
+
+        /* Setup name */
+        if (SharedCacheMap->FileObject != NULL &&
+            SharedCacheMap->FileObject->FileName.Length != 0)
+        {
+            FileName = &SharedCacheMap->FileObject->FileName;
+        }
+        else
+        {
+            FileName = &NoName;
+        }
+
+        /* And print */
+        KdbpPrint("%p\t%d\t%d\t%wZ\n", SharedCacheMap, Valid, Dirty, FileName);
+    }
+
+    return TRUE;
+}
+#endif
+
 /* EOF */