https://git.reactos.org/?p=reactos.git;a=commitdiff;h=c7ad200f8bc1b40c6ed94…
commit c7ad200f8bc1b40c6ed942176eb43d0a04bfaf8a
Author: Pierre Schweitzer <pierre(a)reactos.org>
AuthorDate: Tue Jan 23 19:07:25 2018 +0100
Commit: Pierre Schweitzer <pierre(a)reactos.org>
CommitDate: Tue Jan 23 19:33:59 2018 +0100
[NTOSKRNL] Reimplement the lazy writer in Cc and remove the "basic" one in
Mm.
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 | 2 +-
ntoskrnl/cc/cacheman.c | 11 ++-
ntoskrnl/cc/copy.c | 18 ++++-
ntoskrnl/cc/fs.c | 4 +-
ntoskrnl/cc/view.c | 173 ++++++++++++++++++++++++++++++++++++++---
ntoskrnl/include/internal/cc.h | 13 +++-
ntoskrnl/mm/mminit.c | 78 -------------------
ntoskrnl/po/poshtdwn.c | 3 +-
ntoskrnl/po/power.c | 3 +-
9 files changed, 205 insertions(+), 100 deletions(-)
diff --git a/ntoskrnl/cache/newcc.h b/ntoskrnl/cache/newcc.h
index adbf1c5d92..63f4841229 100644
--- a/ntoskrnl/cache/newcc.h
+++ b/ntoskrnl/cache/newcc.h
@@ -53,7 +53,7 @@ CcMdlWriteComplete2(IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER FileOffset,
IN PMDL MdlChain);
-VOID
+BOOLEAN
NTAPI
CcInitView(VOID);
diff --git a/ntoskrnl/cc/cacheman.c b/ntoskrnl/cc/cacheman.c
index 4d62314ccd..2c2c2238c9 100644
--- a/ntoskrnl/cc/cacheman.c
+++ b/ntoskrnl/cc/cacheman.c
@@ -41,8 +41,15 @@ NTAPI
INIT_FUNCTION
CcInitializeCacheManager(VOID)
{
- CcInitView();
- return TRUE;
+ return CcInitView();
+}
+
+VOID
+NTAPI
+CcShutdownSystem(VOID)
+{
+ /* Inform the lazy writer it has to stop activity */
+ CcShutdownLazyWriter();
}
/*
diff --git a/ntoskrnl/cc/copy.c b/ntoskrnl/cc/copy.c
index 15c6ceb81b..dadae020c7 100644
--- a/ntoskrnl/cc/copy.c
+++ b/ntoskrnl/cc/copy.c
@@ -34,6 +34,8 @@ ULONG CcFastReadWait;
ULONG CcFastReadNoWait;
ULONG CcFastReadResourceMiss;
+extern KEVENT iLazyWriterNotify;
+
/* FUNCTIONS *****************************************************************/
VOID
@@ -516,14 +518,26 @@ CcFastCopyWrite (
}
/*
- * @unimplemented
+ * @implemented
*/
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;
}
diff --git a/ntoskrnl/cc/fs.c b/ntoskrnl/cc/fs.c
index 4958478f3f..76a073af1b 100644
--- a/ntoskrnl/cc/fs.c
+++ b/ntoskrnl/cc/fs.c
@@ -20,7 +20,7 @@
/* GLOBALS *****************************************************************/
extern KGUARDED_MUTEX ViewLock;
-extern ULONG DirtyPageCount;
+extern ULONG CcTotalDirtyPages;
NTSTATUS CcRosInternalFreeVacb(PROS_VACB Vacb);
@@ -239,7 +239,7 @@ CcPurgeCacheSection (
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);
diff --git a/ntoskrnl/cc/view.c b/ntoskrnl/cc/view.c
index 0e37b77efc..8fb7f5333c 100644
--- a/ntoskrnl/cc/view.c
+++ b/ntoskrnl/cc/view.c
@@ -43,7 +43,6 @@
LIST_ENTRY DirtyVacbListHead;
static LIST_ENTRY VacbLruListHead;
-ULONG DirtyPageCount = 0;
KGUARDED_MUTEX ViewLock;
@@ -51,6 +50,27 @@ NPAGED_LOOKASIDE_LIST iBcbLookasideList;
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)
{
@@ -145,7 +165,7 @@ CcRosFlushVacb (
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);
@@ -160,7 +180,8 @@ NTAPI
CcRosFlushDirtyPages (
ULONG Target,
PULONG Count,
- BOOLEAN Wait)
+ BOOLEAN Wait,
+ BOOLEAN CalledFromLazy)
{
PLIST_ENTRY current_entry;
PROS_VACB current;
@@ -191,6 +212,14 @@ CcRosFlushDirtyPages (
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)
@@ -239,8 +268,22 @@ CcRosFlushDirtyPages (
}
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;
@@ -253,6 +296,60 @@ CcRosFlushDirtyPages (
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,
@@ -346,7 +443,7 @@ retry:
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 */
@@ -403,7 +500,7 @@ CcRosReleaseVacb (
if (!WasDirty && Vacb->Dirty)
{
InsertTailList(&DirtyVacbListHead, &Vacb->DirtyVacbListEntry);
- DirtyPageCount += VACB_MAPPING_GRANULARITY / PAGE_SIZE;
+ CcTotalDirtyPages += VACB_MAPPING_GRANULARITY / PAGE_SIZE;
}
if (Mapped)
@@ -499,7 +596,7 @@ CcRosMarkDirtyVacb (
if (!Vacb->Dirty)
{
InsertTailList(&DirtyVacbListHead, &Vacb->DirtyVacbListEntry);
- DirtyPageCount += VACB_MAPPING_GRANULARITY / PAGE_SIZE;
+ CcTotalDirtyPages += VACB_MAPPING_GRANULARITY / PAGE_SIZE;
}
else
{
@@ -552,7 +649,7 @@ CcRosUnmapVacb (
if (!WasDirty && NowDirty)
{
InsertTailList(&DirtyVacbListHead, &Vacb->DirtyVacbListEntry);
- DirtyPageCount += VACB_MAPPING_GRANULARITY / PAGE_SIZE;
+ CcTotalDirtyPages += VACB_MAPPING_GRANULARITY / PAGE_SIZE;
}
CcRosVacbDecRefCount(Vacb);
@@ -1014,7 +1111,7 @@ CcRosDeleteFileCache (
if (current->Dirty)
{
RemoveEntryList(¤t->DirtyVacbListEntry);
- DirtyPageCount -= VACB_MAPPING_GRANULARITY / PAGE_SIZE;
+ CcTotalDirtyPages -= VACB_MAPPING_GRANULARITY / PAGE_SIZE;
DPRINT1("Freeing dirty VACB\n");
}
InsertHeadList(&FreeList, ¤t->CacheMapVacbListEntry);
@@ -1230,11 +1327,24 @@ CcGetFileObjectFromSectionPtrs (
}
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)
{
+ HANDLE LazyWriter;
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+
DPRINT("CcInitView()\n");
InitializeListHead(&DirtyVacbListHead);
@@ -1264,7 +1374,50 @@ CcInitView (
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();
+
+ return TRUE;
}
/* EOF */
diff --git a/ntoskrnl/include/internal/cc.h b/ntoskrnl/include/internal/cc.h
index 58d2b15a50..b1bb1ab165 100644
--- a/ntoskrnl/include/internal/cc.h
+++ b/ntoskrnl/include/internal/cc.h
@@ -240,10 +240,14 @@ CcRosGetVacb(
PROS_VACB *Vacb
);
-VOID
+BOOLEAN
NTAPI
CcInitView(VOID);
+VOID
+NTAPI
+CcShutdownLazyWriter(VOID);
+
NTSTATUS
NTAPI
CcReadVirtualAddress(PROS_VACB Vacb);
@@ -287,7 +291,8 @@ NTAPI
CcRosFlushDirtyPages(
ULONG Target,
PULONG Count,
- BOOLEAN Wait
+ BOOLEAN Wait,
+ BOOLEAN CalledFromLazy
);
VOID
@@ -342,6 +347,10 @@ NTSTATUS
NTAPI
CcTryToInitializeFileCache(PFILE_OBJECT FileObject);
+VOID
+NTAPI
+CcShutdownSystem(VOID);
+
FORCEINLINE
NTSTATUS
CcRosAcquireVacbLock(
diff --git a/ntoskrnl/mm/mminit.c b/ntoskrnl/mm/mminit.c
index 3fff72b771..7f756cb778 100644
--- a/ntoskrnl/mm/mminit.c
+++ b/ntoskrnl/mm/mminit.c
@@ -19,9 +19,6 @@
VOID NTAPI MiInitializeUserPfnBitmap(VOID);
-HANDLE MpwThreadHandle;
-KEVENT MpwThreadEvent;
-
BOOLEAN Mm64BitPhysicalAddress = FALSE;
ULONG MmReadClusterSize;
//
@@ -169,76 +166,6 @@ MiDbgDumpAddressSpace(VOID)
"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
@@ -338,11 +265,6 @@ MmInitSystem(IN ULONG Phase,
*/
MiInitBalancerThread();
- /*
- * Initialise the modified page writer.
- */
- MmInitMpwThread();
-
/* Initialize the balance set manager */
MmInitBsmThread();
diff --git a/ntoskrnl/po/poshtdwn.c b/ntoskrnl/po/poshtdwn.c
index 3ebf899bd5..04618c9f54 100644
--- a/ntoskrnl/po/poshtdwn.c
+++ b/ntoskrnl/po/poshtdwn.c
@@ -279,11 +279,10 @@ PopGracefulShutdown(IN PVOID Context)
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();
-#endif
/* In this step, the I/O manager does last-chance shutdown notification */
DPRINT("I/O manager shutting down in phase 1\n");
diff --git a/ntoskrnl/po/power.c b/ntoskrnl/po/power.c
index 9b96b6016e..2dee988b34 100644
--- a/ntoskrnl/po/power.c
+++ b/ntoskrnl/po/power.c
@@ -952,7 +952,8 @@ NtSetSystemPowerState(IN POWER_ACTION SystemAction,
#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