[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
This commit is contained in:
Pierre Schweitzer 2018-01-23 19:07:25 +01:00
parent 2382435e88
commit c7ad200f8b
No known key found for this signature in database
GPG key ID: 7545556C3D585B0B
9 changed files with 205 additions and 100 deletions

View file

@ -53,7 +53,7 @@ CcMdlWriteComplete2(IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER FileOffset,
IN PMDL MdlChain);
VOID
BOOLEAN
NTAPI
CcInitView(VOID);

View file

@ -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();
}
/*

View file

@ -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;
}

View file

@ -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);

View file

@ -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(&current->DirtyVacbListEntry);
DirtyPageCount -= VACB_MAPPING_GRANULARITY / PAGE_SIZE;
CcTotalDirtyPages -= VACB_MAPPING_GRANULARITY / PAGE_SIZE;
DPRINT1("Freeing dirty VACB\n");
}
InsertHeadList(&FreeList, &current->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 */

View file

@ -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(

View file

@ -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();

View file

@ -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");

View file

@ -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