[NTOSKRNL] Reimplement the lazy writer in Cc and remove the "basic" one in Mm.
authorPierre Schweitzer <pierre@reactos.org>
Tue, 23 Jan 2018 18:07:25 +0000 (19:07 +0100)
committerPierre Schweitzer <pierre@reactos.org>
Tue, 23 Jan 2018 18:33:59 +0000 (19:33 +0100)
This removes the "modified page writer" thread in Mm that was regularly blindly
attempting to flush dirty pages to the disk.
Instead, this commit introduces a lazy writer that will monitor dirty pages count
and will flush them to disk when this count is above a threshold. The threshold is
computed on Cc init.
Compared to what was done previously, this lazy writer will only write down files
that are not marked as temporary.
The mechanisms involved in this lazy writer worker are well described in Windows
Internals 4th editions (constants are coming from it ;-)).
Also fixed a bad (and old!) bug in CcRosFlushDirtyPages() where target count could
be overflow and the function would spin forever while holding the VACBs lock. This is
mandatory as now lazy writer will call it with "random" values.
This also allows implementing CcWaitForCurrentLazyWriterActivity() :-).
Also renamed DirtyPageCount to its MS equivalent.

CORE-14235

ntoskrnl/cache/newcc.h
ntoskrnl/cc/cacheman.c
ntoskrnl/cc/copy.c
ntoskrnl/cc/fs.c
ntoskrnl/cc/view.c
ntoskrnl/include/internal/cc.h
ntoskrnl/mm/mminit.c
ntoskrnl/po/poshtdwn.c
ntoskrnl/po/power.c

index adbf1c5..63f4841 100644 (file)
@@ -53,7 +53,7 @@ CcMdlWriteComplete2(IN PFILE_OBJECT FileObject,
                     IN PLARGE_INTEGER FileOffset,
                     IN PMDL MdlChain);
 
                     IN PLARGE_INTEGER FileOffset,
                     IN PMDL MdlChain);
 
-VOID
+BOOLEAN
 NTAPI
 CcInitView(VOID);
 
 NTAPI
 CcInitView(VOID);
 
index 4d62314..2c2c223 100644 (file)
@@ -41,8 +41,15 @@ NTAPI
 INIT_FUNCTION
 CcInitializeCacheManager(VOID)
 {
 INIT_FUNCTION
 CcInitializeCacheManager(VOID)
 {
-    CcInitView();
-    return TRUE;
+    return CcInitView();
+}
+
+VOID
+NTAPI
+CcShutdownSystem(VOID)
+{
+    /* Inform the lazy writer it has to stop activity */
+    CcShutdownLazyWriter();
 }
 
 /*
 }
 
 /*
index 15c6ceb..dadae02 100644 (file)
@@ -34,6 +34,8 @@ ULONG CcFastReadWait;
 ULONG CcFastReadNoWait;
 ULONG CcFastReadResourceMiss;
 
 ULONG CcFastReadNoWait;
 ULONG CcFastReadResourceMiss;
 
+extern KEVENT iLazyWriterNotify;
+
 /* FUNCTIONS *****************************************************************/
 
 VOID
 /* FUNCTIONS *****************************************************************/
 
 VOID
@@ -516,14 +518,26 @@ CcFastCopyWrite (
 }
 
 /*
 }
 
 /*
- * @unimplemented
+ * @implemented
  */
 NTSTATUS
 NTAPI
 CcWaitForCurrentLazyWriterActivity (
     VOID)
 {
  */
 NTSTATUS
 NTAPI
 CcWaitForCurrentLazyWriterActivity (
     VOID)
 {
-    UNIMPLEMENTED;
+    NTSTATUS Status;
+
+    /* Lazy writer is done when its event is set */
+    Status = KeWaitForSingleObject(&iLazyWriterNotify,
+                                   Executive,
+                                   KernelMode,
+                                   FALSE,
+                                   NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
+
     return STATUS_SUCCESS;
 }
 
     return STATUS_SUCCESS;
 }
 
index 4958478..76a073a 100644 (file)
@@ -20,7 +20,7 @@
 /* GLOBALS   *****************************************************************/
 
 extern KGUARDED_MUTEX ViewLock;
 /* GLOBALS   *****************************************************************/
 
 extern KGUARDED_MUTEX ViewLock;
-extern ULONG DirtyPageCount;
+extern ULONG CcTotalDirtyPages;
 
 NTSTATUS CcRosInternalFreeVacb(PROS_VACB Vacb);
 
 
 NTSTATUS CcRosInternalFreeVacb(PROS_VACB Vacb);
 
@@ -239,7 +239,7 @@ CcPurgeCacheSection (
         if (Vacb->Dirty)
         {
             RemoveEntryList(&Vacb->DirtyVacbListEntry);
         if (Vacb->Dirty)
         {
             RemoveEntryList(&Vacb->DirtyVacbListEntry);
-            DirtyPageCount -= VACB_MAPPING_GRANULARITY / PAGE_SIZE;
+            CcTotalDirtyPages -= VACB_MAPPING_GRANULARITY / PAGE_SIZE;
         }
         RemoveEntryList(&Vacb->CacheMapVacbListEntry);
         InsertHeadList(&FreeList, &Vacb->CacheMapVacbListEntry);
         }
         RemoveEntryList(&Vacb->CacheMapVacbListEntry);
         InsertHeadList(&FreeList, &Vacb->CacheMapVacbListEntry);
index 0e37b77..8fb7f53 100644 (file)
@@ -43,7 +43,6 @@
 
 LIST_ENTRY DirtyVacbListHead;
 static LIST_ENTRY VacbLruListHead;
 
 LIST_ENTRY DirtyVacbListHead;
 static LIST_ENTRY VacbLruListHead;
-ULONG DirtyPageCount = 0;
 
 KGUARDED_MUTEX ViewLock;
 
 
 KGUARDED_MUTEX ViewLock;
 
@@ -51,6 +50,27 @@ NPAGED_LOOKASIDE_LIST iBcbLookasideList;
 static NPAGED_LOOKASIDE_LIST SharedCacheMapLookasideList;
 static NPAGED_LOOKASIDE_LIST VacbLookasideList;
 
 static NPAGED_LOOKASIDE_LIST SharedCacheMapLookasideList;
 static NPAGED_LOOKASIDE_LIST VacbLookasideList;
 
+/* Counters:
+ * - Amount of pages flushed by lazy writer
+ * - Number of times lazy writer ran
+ */
+ULONG CcLazyWritePages = 0;
+ULONG CcLazyWriteIos = 0;
+
+/* Internal vars (MS):
+ * - Threshold above which lazy writer will start action
+ * - Amount of dirty pages
+ */
+ULONG CcDirtyPageThreshold = 0;
+ULONG CcTotalDirtyPages = 0;
+
+/* Internal vars (ROS):
+ * - Event to notify lazy writer to shutdown
+ * - Event to inform watchers lazy writer is done for this loop
+ */
+KEVENT iLazyWriterShutdown;
+KEVENT iLazyWriterNotify;
+
 #if DBG
 static void CcRosVacbIncRefCount_(PROS_VACB vacb, const char* file, int line)
 {
 #if DBG
 static void CcRosVacbIncRefCount_(PROS_VACB vacb, const char* file, int line)
 {
@@ -145,7 +165,7 @@ CcRosFlushVacb (
 
         Vacb->Dirty = FALSE;
         RemoveEntryList(&Vacb->DirtyVacbListEntry);
 
         Vacb->Dirty = FALSE;
         RemoveEntryList(&Vacb->DirtyVacbListEntry);
-        DirtyPageCount -= VACB_MAPPING_GRANULARITY / PAGE_SIZE;
+        CcTotalDirtyPages -= VACB_MAPPING_GRANULARITY / PAGE_SIZE;
         CcRosVacbDecRefCount(Vacb);
 
         KeReleaseSpinLock(&Vacb->SharedCacheMap->CacheMapLock, oldIrql);
         CcRosVacbDecRefCount(Vacb);
 
         KeReleaseSpinLock(&Vacb->SharedCacheMap->CacheMapLock, oldIrql);
@@ -160,7 +180,8 @@ NTAPI
 CcRosFlushDirtyPages (
     ULONG Target,
     PULONG Count,
 CcRosFlushDirtyPages (
     ULONG Target,
     PULONG Count,
-    BOOLEAN Wait)
+    BOOLEAN Wait,
+    BOOLEAN CalledFromLazy)
 {
     PLIST_ENTRY current_entry;
     PROS_VACB current;
 {
     PLIST_ENTRY current_entry;
     PROS_VACB current;
@@ -191,6 +212,14 @@ CcRosFlushDirtyPages (
 
         CcRosVacbIncRefCount(current);
 
 
         CcRosVacbIncRefCount(current);
 
+        /* When performing lazy write, don't handle temporary files */
+        if (CalledFromLazy &&
+            BooleanFlagOn(current->SharedCacheMap->FileObject->Flags, FO_TEMPORARY_FILE))
+        {
+            CcRosVacbDecRefCount(current);
+            continue;
+        }
+
         Locked = current->SharedCacheMap->Callbacks->AcquireForLazyWrite(
                      current->SharedCacheMap->LazyWriteContext, Wait);
         if (!Locked)
         Locked = current->SharedCacheMap->Callbacks->AcquireForLazyWrite(
                      current->SharedCacheMap->LazyWriteContext, Wait);
         if (!Locked)
@@ -239,8 +268,22 @@ CcRosFlushDirtyPages (
         }
         else
         {
         }
         else
         {
-            (*Count) += VACB_MAPPING_GRANULARITY / PAGE_SIZE;
-            Target -= VACB_MAPPING_GRANULARITY / PAGE_SIZE;
+            ULONG PagesFreed;
+
+            /* How many pages did we free? */
+            PagesFreed = VACB_MAPPING_GRANULARITY / PAGE_SIZE;
+            (*Count) += PagesFreed;
+
+            /* Make sure we don't overflow target! */
+            if (Target < PagesFreed)
+            {
+                /* If we would have, jump to zero directly */
+                Target = 0;
+            }
+            else
+            {
+                Target -= PagesFreed;
+            }
         }
 
         current_entry = DirtyVacbListHead.Flink;
         }
 
         current_entry = DirtyVacbListHead.Flink;
@@ -253,6 +296,60 @@ CcRosFlushDirtyPages (
     return STATUS_SUCCESS;
 }
 
     return STATUS_SUCCESS;
 }
 
+/* FIXME: Someday this could somewhat implement write-behind/read-ahead */
+VOID
+NTAPI
+CciLazyWriter(PVOID Unused)
+{
+    LARGE_INTEGER OneSecond;
+
+    OneSecond.QuadPart = (LONGLONG)-1*1000*1000*10;
+
+    while (TRUE)
+    {
+        NTSTATUS Status;
+        ULONG Target, Count = 0;
+
+        /* One per second or until we have to stop */
+        Status = KeWaitForSingleObject(&iLazyWriterShutdown,
+                                       Executive,
+                                       KernelMode,
+                                       FALSE,
+                                       &OneSecond);
+
+        /* If we succeeed, we've to stop running! */
+        if (Status == STATUS_SUCCESS)
+        {
+            break;
+        }
+
+        /* 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)
+            {
+                /* 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);
+    }
+}
+
 NTSTATUS
 CcRosTrimCache (
     ULONG Target,
 NTSTATUS
 CcRosTrimCache (
     ULONG Target,
@@ -346,7 +443,7 @@ retry:
     if ((Target > 0) && !FlushedPages)
     {
         /* Flush dirty pages to disk */
     if ((Target > 0) && !FlushedPages)
     {
         /* Flush dirty pages to disk */
-        CcRosFlushDirtyPages(Target, &PagesFreed, FALSE);
+        CcRosFlushDirtyPages(Target, &PagesFreed, FALSE, FALSE);
         FlushedPages = TRUE;
 
         /* We can only swap as many pages as we flushed */
         FlushedPages = TRUE;
 
         /* We can only swap as many pages as we flushed */
@@ -403,7 +500,7 @@ CcRosReleaseVacb (
     if (!WasDirty && Vacb->Dirty)
     {
         InsertTailList(&DirtyVacbListHead, &Vacb->DirtyVacbListEntry);
     if (!WasDirty && Vacb->Dirty)
     {
         InsertTailList(&DirtyVacbListHead, &Vacb->DirtyVacbListEntry);
-        DirtyPageCount += VACB_MAPPING_GRANULARITY / PAGE_SIZE;
+        CcTotalDirtyPages += VACB_MAPPING_GRANULARITY / PAGE_SIZE;
     }
 
     if (Mapped)
     }
 
     if (Mapped)
@@ -499,7 +596,7 @@ CcRosMarkDirtyVacb (
     if (!Vacb->Dirty)
     {
         InsertTailList(&DirtyVacbListHead, &Vacb->DirtyVacbListEntry);
     if (!Vacb->Dirty)
     {
         InsertTailList(&DirtyVacbListHead, &Vacb->DirtyVacbListEntry);
-        DirtyPageCount += VACB_MAPPING_GRANULARITY / PAGE_SIZE;
+        CcTotalDirtyPages += VACB_MAPPING_GRANULARITY / PAGE_SIZE;
     }
     else
     {
     }
     else
     {
@@ -552,7 +649,7 @@ CcRosUnmapVacb (
     if (!WasDirty && NowDirty)
     {
         InsertTailList(&DirtyVacbListHead, &Vacb->DirtyVacbListEntry);
     if (!WasDirty && NowDirty)
     {
         InsertTailList(&DirtyVacbListHead, &Vacb->DirtyVacbListEntry);
-        DirtyPageCount += VACB_MAPPING_GRANULARITY / PAGE_SIZE;
+        CcTotalDirtyPages += VACB_MAPPING_GRANULARITY / PAGE_SIZE;
     }
 
     CcRosVacbDecRefCount(Vacb);
     }
 
     CcRosVacbDecRefCount(Vacb);
@@ -1014,7 +1111,7 @@ CcRosDeleteFileCache (
             if (current->Dirty)
             {
                 RemoveEntryList(&current->DirtyVacbListEntry);
             if (current->Dirty)
             {
                 RemoveEntryList(&current->DirtyVacbListEntry);
-                DirtyPageCount -= VACB_MAPPING_GRANULARITY / PAGE_SIZE;
+                CcTotalDirtyPages -= VACB_MAPPING_GRANULARITY / PAGE_SIZE;
                 DPRINT1("Freeing dirty VACB\n");
             }
             InsertHeadList(&FreeList, &current->CacheMapVacbListEntry);
                 DPRINT1("Freeing dirty VACB\n");
             }
             InsertHeadList(&FreeList, &current->CacheMapVacbListEntry);
@@ -1230,11 +1327,24 @@ CcGetFileObjectFromSectionPtrs (
 }
 
 VOID
 }
 
 VOID
+NTAPI
+CcShutdownLazyWriter (
+    VOID)
+{
+    /* Simply set the event, lazy writer will stop when it's done */
+    KeSetEvent(&iLazyWriterShutdown, IO_DISK_INCREMENT, FALSE);
+}
+
+BOOLEAN
 INIT_FUNCTION
 NTAPI
 CcInitView (
     VOID)
 {
 INIT_FUNCTION
 NTAPI
 CcInitView (
     VOID)
 {
+    HANDLE LazyWriter;
+    NTSTATUS Status;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+
     DPRINT("CcInitView()\n");
 
     InitializeListHead(&DirtyVacbListHead);
     DPRINT("CcInitView()\n");
 
     InitializeListHead(&DirtyVacbListHead);
@@ -1264,7 +1374,50 @@ CcInitView (
 
     MmInitializeMemoryConsumer(MC_CACHE, CcRosTrimCache);
 
 
     MmInitializeMemoryConsumer(MC_CACHE, CcRosTrimCache);
 
+    /* Initialize lazy writer events */
+    KeInitializeEvent(&iLazyWriterShutdown, SynchronizationEvent, FALSE);
+    KeInitializeEvent(&iLazyWriterNotify, NotificationEvent, FALSE);
+
+    /* Define lazy writer threshold, depending on system type */
+    switch (MmQuerySystemSize())
+    {
+        case MmSmallSystem:
+            CcDirtyPageThreshold = MmNumberOfPhysicalPages / 8;
+            break;
+
+        case MmMediumSystem:
+            CcDirtyPageThreshold = MmNumberOfPhysicalPages / 4;
+            break;
+
+        case MmLargeSystem:
+            CcDirtyPageThreshold = MmNumberOfPhysicalPages / 8 + MmNumberOfPhysicalPages / 4;
+            break;
+    }
+
+    /* Start the lazy writer thread */
+    InitializeObjectAttributes(&ObjectAttributes,
+                               NULL,
+                               OBJ_KERNEL_HANDLE,
+                               NULL,
+                               NULL);
+    Status = PsCreateSystemThread(&LazyWriter,
+                                  THREAD_ALL_ACCESS,
+                                  &ObjectAttributes,
+                                  NULL,
+                                  NULL,
+                                  CciLazyWriter,
+                                  NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        return FALSE;
+    }
+
+    /* Handle is not needed */
+    ObCloseHandle(LazyWriter, KernelMode);
+
     CcInitCacheZeroPage();
     CcInitCacheZeroPage();
+
+    return TRUE;
 }
 
 /* EOF */
 }
 
 /* EOF */
index 58d2b15..b1bb1ab 100644 (file)
@@ -240,10 +240,14 @@ CcRosGetVacb(
     PROS_VACB *Vacb
 );
 
     PROS_VACB *Vacb
 );
 
-VOID
+BOOLEAN
 NTAPI
 CcInitView(VOID);
 
 NTAPI
 CcInitView(VOID);
 
+VOID
+NTAPI
+CcShutdownLazyWriter(VOID);
+
 NTSTATUS
 NTAPI
 CcReadVirtualAddress(PROS_VACB Vacb);
 NTSTATUS
 NTAPI
 CcReadVirtualAddress(PROS_VACB Vacb);
@@ -287,7 +291,8 @@ NTAPI
 CcRosFlushDirtyPages(
     ULONG Target,
     PULONG Count,
 CcRosFlushDirtyPages(
     ULONG Target,
     PULONG Count,
-    BOOLEAN Wait
+    BOOLEAN Wait,
+    BOOLEAN CalledFromLazy
 );
 
 VOID
 );
 
 VOID
@@ -342,6 +347,10 @@ NTSTATUS
 NTAPI
 CcTryToInitializeFileCache(PFILE_OBJECT FileObject);
 
 NTAPI
 CcTryToInitializeFileCache(PFILE_OBJECT FileObject);
 
+VOID
+NTAPI
+CcShutdownSystem(VOID);
+
 FORCEINLINE
 NTSTATUS
 CcRosAcquireVacbLock(
 FORCEINLINE
 NTSTATUS
 CcRosAcquireVacbLock(
index 3fff72b..7f756cb 100644 (file)
@@ -19,9 +19,6 @@
 
 VOID NTAPI MiInitializeUserPfnBitmap(VOID);
 
 
 VOID NTAPI MiInitializeUserPfnBitmap(VOID);
 
-HANDLE MpwThreadHandle;
-KEVENT MpwThreadEvent;
-
 BOOLEAN Mm64BitPhysicalAddress = FALSE;
 ULONG MmReadClusterSize;
 //
 BOOLEAN Mm64BitPhysicalAddress = FALSE;
 ULONG MmReadClusterSize;
 //
@@ -169,76 +166,6 @@ MiDbgDumpAddressSpace(VOID)
             "Non Paged Pool Expansion PTE Space");
 }
 
             "Non Paged Pool Expansion PTE Space");
 }
 
-VOID
-NTAPI
-MmMpwThreadMain(PVOID Parameter)
-{
-    NTSTATUS Status;
-#ifndef NEWCC
-    ULONG PagesWritten;
-#endif
-    LARGE_INTEGER Timeout;
-
-    UNREFERENCED_PARAMETER(Parameter);
-
-    Timeout.QuadPart = -50000000;
-
-    for(;;)
-    {
-        Status = KeWaitForSingleObject(&MpwThreadEvent,
-                                       0,
-                                       KernelMode,
-                                       FALSE,
-                                       &Timeout);
-        if (!NT_SUCCESS(Status))
-        {
-            DbgPrint("MpwThread: Wait failed\n");
-            KeBugCheck(MEMORY_MANAGEMENT);
-            return;
-        }
-
-#ifndef NEWCC
-        PagesWritten = 0;
-
-        // XXX arty -- we flush when evicting pages or destorying cache
-        // sections.
-        CcRosFlushDirtyPages(128, &PagesWritten, FALSE);
-#endif
-    }
-}
-
-NTSTATUS
-NTAPI
-INIT_FUNCTION
-MmInitMpwThread(VOID)
-{
-    KPRIORITY Priority;
-    NTSTATUS Status;
-    CLIENT_ID MpwThreadId;
-
-    KeInitializeEvent(&MpwThreadEvent, SynchronizationEvent, FALSE);
-
-    Status = PsCreateSystemThread(&MpwThreadHandle,
-                                  THREAD_ALL_ACCESS,
-                                  NULL,
-                                  NULL,
-                                  &MpwThreadId,
-                                  MmMpwThreadMain,
-                                  NULL);
-    if (!NT_SUCCESS(Status))
-    {
-        return(Status);
-    }
-
-    Priority = 27;
-    NtSetInformationThread(MpwThreadHandle,
-                           ThreadPriority,
-                           &Priority,
-                           sizeof(Priority));
-
-    return(STATUS_SUCCESS);
-}
-
 NTSTATUS
 NTAPI
 INIT_FUNCTION
 NTSTATUS
 NTAPI
 INIT_FUNCTION
@@ -338,11 +265,6 @@ MmInitSystem(IN ULONG Phase,
      */
     MiInitBalancerThread();
 
      */
     MiInitBalancerThread();
 
-    /*
-     * Initialise the modified page writer.
-     */
-    MmInitMpwThread();
-
     /* Initialize the balance set manager */
     MmInitBsmThread();
 
     /* Initialize the balance set manager */
     MmInitBsmThread();
 
index 3ebf899..04618c9 100644 (file)
@@ -279,11 +279,10 @@ PopGracefulShutdown(IN PVOID Context)
     CmShutdownSystem();
 
     /* Note that modified pages should be written here (MiShutdownSystem) */
     CmShutdownSystem();
 
     /* Note that modified pages should be written here (MiShutdownSystem) */
-#ifdef NEWCC
+
     /* Flush all user files before we start shutting down IO */
     /* This is where modified pages are written back by the IO manager */
     CcShutdownSystem();
     /* Flush all user files before we start shutting down IO */
     /* This is where modified pages are written back by the IO manager */
     CcShutdownSystem();
-#endif
 
     /* In this step, the I/O manager does last-chance shutdown notification */
     DPRINT("I/O manager shutting down in phase 1\n");
 
     /* In this step, the I/O manager does last-chance shutdown notification */
     DPRINT("I/O manager shutting down in phase 1\n");
index 9b96b60..2dee988 100644 (file)
@@ -952,7 +952,8 @@ NtSetSystemPowerState(IN POWER_ACTION SystemAction,
 
 #ifndef NEWCC
         /* Flush dirty cache pages */
 
 #ifndef NEWCC
         /* Flush dirty cache pages */
-        CcRosFlushDirtyPages(-1, &Dummy, FALSE); //HACK: We really should wait here!
+        /* XXX: Is that still mandatory? As now we'll wait on lazy writer to complete? */
+        CcRosFlushDirtyPages(-1, &Dummy, FALSE, FALSE); //HACK: We really should wait here!
 #else
         Dummy = 0;
 #endif
 #else
         Dummy = 0;
 #endif