/*++ Copyright (c) 1989 Microsoft Corporation Module Name: zeropage.c Abstract: This module contains the zero page thread for memory management. Author: Lou Perazzoli (loup) 6-Apr-1991 Landy Wang (landyw) 02-June-1997 Revision History: --*/ #include "mi.h" #define MM_ZERO_PAGE_OBJECT 0 #define PO_SYS_IDLE_OBJECT 1 #define NUMBER_WAIT_OBJECTS 2 #define MACHINE_ZERO_PAGE() KeZeroPageFromIdleThread(ZeroBase) LOGICAL MiZeroingDisabled = FALSE; ULONG MmLastNodeZeroing = 0; VOID MmZeroPageThread ( VOID ) /*++ Routine Description: Implements the NT zeroing page thread. This thread runs at priority zero and removes a page from the free list, zeroes it, and places it on the zeroed page list. Arguments: StartContext - not used. Return Value: None. Environment: Kernel mode. --*/ { KIRQL OldIrql; PFN_NUMBER PageFrame; PMMPFN Pfn1; PKTHREAD Thread; PVOID ZeroBase; PFN_NUMBER NewPage; PVOID WaitObjects[NUMBER_WAIT_OBJECTS]; NTSTATUS Status; PVOID StartVa; PVOID EndVa; #if defined(MI_MULTINODE) ULONG i, n; ULONG Color = 0; ULONG StartColor; #endif // // Before this becomes the zero page thread, free the kernel // initialization code. // MiFindInitializationCode (&StartVa, &EndVa); if (StartVa != NULL) { MiFreeInitializationCode (StartVa, EndVa); } // // The following code sets the current thread's base priority to zero // and then sets its current priority to zero. This ensures that the // thread always runs at a priority of zero. // Thread = KeGetCurrentThread(); Thread->BasePriority = 0; KeSetPriorityThread (Thread, 0); // // Initialize wait object array for multiple wait // WaitObjects[MM_ZERO_PAGE_OBJECT] = &MmZeroingPageEvent; WaitObjects[PO_SYS_IDLE_OBJECT] = &PoSystemIdleTimer; // // Loop forever zeroing pages. // do { // // Wait until there are at least MmZeroPageMinimum pages // on the free list. // Status = KeWaitForMultipleObjects (NUMBER_WAIT_OBJECTS, WaitObjects, WaitAny, WrFreePage, KernelMode, FALSE, (PLARGE_INTEGER) NULL, (PKWAIT_BLOCK) NULL ); if (Status == PO_SYS_IDLE_OBJECT) { PoSystemIdleWorker (TRUE); continue; } LOCK_PFN_WITH_TRY (OldIrql); do { if (*(PFN_NUMBER volatile*)&MmFreePageListHead.Total == 0) { // // No pages on the free list at this time, wait for // some more. // MmZeroingPageThreadActive = FALSE; UNLOCK_PFN (OldIrql); break; } if (MiZeroingDisabled == TRUE) { MmZeroingPageThreadActive = FALSE; UNLOCK_PFN (OldIrql); KeDelayExecutionThread (KernelMode, FALSE, (PLARGE_INTEGER)&MmHalfSecond); break; } // // In a multinode system, zero pages by node. Starting with // the last node examined, find a node with free pages that // need to be zeroed. // #if defined(MI_MULTINODE) if (KeNumberNodes > 1) { n = MmLastNodeZeroing; for (i = 0; i < KeNumberNodes; i += 1) { if (KeNodeBlock[n]->FreeCount[FreePageList] != 0) { break; } n = (n + 1) % KeNumberNodes; } ASSERT (KeNodeBlock[n]->FreeCount[FreePageList]); if (n != MmLastNodeZeroing) { Color = KeNodeBlock[n]->MmShiftedColor; } // // Must start with a color MiRemoveAnyPage will // satisfy from the free list otherwise it will // return an already zeroed page. // StartColor = Color; do { if (MmFreePagesByColor[FreePageList][Color].Flink != MM_EMPTY_LIST) { break; } Color = (Color & ~MmSecondaryColorMask) | ((Color + 1) & MmSecondaryColorMask); ASSERT(StartColor != Color); } while (TRUE); PageFrame = MiRemoveAnyPage(Color); } else { n = 0; #endif PageFrame = MmFreePageListHead.Flink; ASSERT (PageFrame != MM_EMPTY_LIST); Pfn1 = MI_PFN_ELEMENT(PageFrame); NewPage = MiRemoveAnyPage(MI_GET_COLOR_FROM_LIST_ENTRY(PageFrame, Pfn1)); if (NewPage != PageFrame) { // // Someone has removed a page from the colored lists // chain without updating the freelist chain. // KeBugCheckEx (PFN_LIST_CORRUPT, 0x8F, NewPage, PageFrame, 0); } #if defined(MI_MULTINODE) } #endif // // Zero the page using the last color used to map the page. // UNLOCK_PFN (OldIrql); ZeroBase = MiMapPageToZeroInHyperSpace (PageFrame); // // If a node switch is in order, do it now that the PFN // lock has been released. // #if defined(MI_MULTINODE) if ((KeNumberNodes > 1) && (n != MmLastNodeZeroing)) { MmLastNodeZeroing = n; KeFindFirstSetLeftAffinity(KeNodeBlock[n]->ProcessorMask, &i); KeSetIdealProcessorThread(Thread, (UCHAR)i); } #endif MACHINE_ZERO_PAGE(); MiUnmapPageInZeroSpace (ZeroBase); LOCK_PFN_WITH_TRY (OldIrql); MiInsertPageInList (&MmZeroedPageListHead, PageFrame); } while(TRUE); } while (TRUE); }