From: Pierre Schweitzer Date: Tue, 23 Jan 2018 18:07:25 +0000 (+0100) Subject: [NTOSKRNL] Reimplement the lazy writer in Cc and remove the "basic" one in Mm. X-Git-Tag: 0.4.9-dev~229 X-Git-Url: https://git.reactos.org/?p=reactos.git;a=commitdiff_plain;h=c7ad200f8bc1b40c6ed942176eb43d0a04bfaf8a [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 --- diff --git a/ntoskrnl/cache/newcc.h b/ntoskrnl/cache/newcc.h index adbf1c5d926..63f4841229b 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 4d62314ccd7..2c2c2238c95 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 15c6ceb81b6..dadae020c76 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 4958478f3f2..76a073af1be 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 0e37b77efca..8fb7f5333c9 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 58d2b15a507..b1bb1ab165d 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 3fff72b7713..7f756cb7786 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 3ebf899bd58..04618c9f546 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 9b96b6016e2..2dee988b347 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