263 lines
6.4 KiB
C
263 lines
6.4 KiB
C
/*++
|
|
|
|
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);
|
|
}
|