NT4/private/ntos/mm/pfnlist.c
2020-09-30 17:12:29 +02:00

1708 lines
44 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
pfnlist.c
Abstract:
This module contains the routines to manipulate pages on the
within the Page Frame Database.
Author:
Lou Perazzoli (loup) 4-Apr-1989
Revision History:
--*/
#include "mi.h"
#define MM_LOW_LIMIT 2
#define MM_HIGH_LIMIT 19
KEVENT MmAvailablePagesEventHigh;
extern ULONG MmPeakCommitment;
extern ULONG MmExtendedCommit;
#if MM_MAXIMUM_NUMBER_OF_COLORS > 1
ULONG MmColorSearch;
#endif
#if DBG
VOID
MiMemoryUsage (VOID);
VOID
MiDumpReferencedPages (VOID);
#endif //DBG
ULONG
MiCompressPage (
IN PVOID Page
);
#pragma alloc_text(PAGELK,MiUnlinkFreeOrZeroedPage)
VOID
MiRemovePageByColor (
IN ULONG Page,
IN ULONG PageColor
);
VOID
FASTCALL
MiInsertPageInList (
IN PMMPFNLIST ListHead,
IN ULONG PageFrameIndex
)
/*++
Routine Description:
This procedure inserts a page at the end of the specified list (free,
standby, bad, zeroed, modified).
Arguments:
ListHead - Supplies the list of the list in which to insert the
specified physical page.
PageFrameIndex - Supplies the physical page number to insert in the
list.
Return Value:
none.
Environment:
Must be holding the PFN database mutex with APC's disabled.
--*/
{
ULONG last;
PMMPFN Pfn1;
PMMPFN Pfn2;
ULONG Color;
ULONG PrimaryColor;
MM_PFN_LOCK_ASSERT();
ASSERT ((PageFrameIndex != 0) && (PageFrameIndex <= MmHighestPhysicalPage) &&
(PageFrameIndex >= MmLowestPhysicalPage));
//
// Check to ensure the reference count for the page
// is zero.
//
Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
#if DBG
if (MmDebug & MM_DBG_PAGE_REF_COUNT) {
PMMPTE PointerPte;
KIRQL OldIrql = 99;
if ((ListHead->ListName == StandbyPageList) ||
(ListHead->ListName == ModifiedPageList)) {
if ((Pfn1->u3.e1.PrototypePte == 1) &&
(MmIsAddressValid (Pfn1->PteAddress))) {
PointerPte = Pfn1->PteAddress;
} else {
//
// The page containing the prototype PTE is not valid,
// map the page into hyperspace and reference it that way.
//
PointerPte = MiMapPageInHyperSpace (Pfn1->PteFrame, &OldIrql);
PointerPte = (PMMPTE)((ULONG)PointerPte +
MiGetByteOffset(Pfn1->PteAddress));
}
ASSERT ((PointerPte->u.Trans.PageFrameNumber == PageFrameIndex) ||
(PointerPte->u.Hard.PageFrameNumber == PageFrameIndex));
ASSERT (PointerPte->u.Soft.Transition == 1);
ASSERT (PointerPte->u.Soft.Prototype == 0);
if (OldIrql != 99) {
MiUnmapPageInHyperSpace (OldIrql)
}
}
}
#endif //DBG
#if DBG
if ((ListHead->ListName == StandbyPageList) ||
(ListHead->ListName == ModifiedPageList)) {
if ((Pfn1->OriginalPte.u.Soft.Prototype == 0) &&
(Pfn1->OriginalPte.u.Soft.Transition == 1)) {
KeBugCheckEx (MEMORY_MANAGEMENT, 0x8888, 0,0,0);
}
}
#endif //DBG
ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
ListHead->Total += 1; // One more page on the list.
//
// On MIPS R4000 modified pages destined for the paging file are
// kept on sperate lists which group pages of the same color
// together
//
if ((ListHead == &MmModifiedPageListHead) &&
(Pfn1->OriginalPte.u.Soft.Prototype == 0)) {
//
// This page is destined for the paging file (not
// a mapped file). Change the list head to the
// appropriate colored list head.
//
ListHead = &MmModifiedPageListByColor [Pfn1->u3.e1.PageColor];
ListHead->Total += 1;
MmTotalPagesForPagingFile += 1;
}
#if MM_MAXIMUM_NUMBER_OF_COLORS > 1
if (ListHead->ListName <= FreePageList) {
ListHead = &MmFreePagesByPrimaryColor [ListHead->ListName] [Pfn1->u3.e1.PageColor];
}
if (ListHead == &MmStandbyPageListHead) {
ListHead = &MmStandbyPageListByColor [Pfn1->u3.e1.PageColor];
ListHead->Total += 1;
}
#endif // > 1
last = ListHead->Blink;
if (last == MM_EMPTY_LIST) {
//
// List is empty add the page to the ListHead.
//
ListHead->Flink = PageFrameIndex;
} else {
Pfn2 = MI_PFN_ELEMENT (last);
Pfn2->u1.Flink = PageFrameIndex;
}
ListHead->Blink = PageFrameIndex;
Pfn1->u1.Flink = MM_EMPTY_LIST;
Pfn1->u2.Blink = last;
Pfn1->u3.e1.PageLocation = ListHead->ListName;
//
// If the page was placed on the free, standby or zeroed list,
// update the count of usable pages in the system. If the count
// transitions from 0 to 1, the event associated with available
// pages should become true.
//
if (ListHead->ListName <= StandbyPageList) {
MmAvailablePages += 1;
//
// A page has just become available, check to see if the
// page wait events should be signalled.
//
if (MmAvailablePages == MM_LOW_LIMIT) {
KeSetEvent (&MmAvailablePagesEvent, 0, FALSE);
} else if (MmAvailablePages == MM_HIGH_LIMIT) {
KeSetEvent (&MmAvailablePagesEventHigh, 0, FALSE);
}
if (ListHead->ListName <= FreePageList) {
//
// We are adding a page to the free or zeroed page list.
// Add the page to the end of the correct colored page list.
//
Color = MI_GET_SECONDARY_COLOR (PageFrameIndex, Pfn1);
ASSERT (Pfn1->u3.e1.PageColor == MI_GET_COLOR_FROM_SECONDARY(Color));
if (MmFreePagesByColor[ListHead->ListName][Color].Flink ==
MM_EMPTY_LIST) {
//
// This list is empty, add this as the first and last
// entry.
//
MmFreePagesByColor[ListHead->ListName][Color].Flink =
PageFrameIndex;
MmFreePagesByColor[ListHead->ListName][Color].Blink =
(PVOID)Pfn1;
} else {
Pfn2 = (PMMPFN)MmFreePagesByColor[ListHead->ListName][Color].Blink;
Pfn2->OriginalPte.u.Long = PageFrameIndex;
MmFreePagesByColor[ListHead->ListName][Color].Blink = (PVOID)Pfn1;
}
Pfn1->OriginalPte.u.Long = MM_EMPTY_LIST;
}
if ((ListHead->ListName == FreePageList) &&
(MmFreePageListHead.Total >= MmMinimumFreePagesToZero) &&
(MmZeroingPageThreadActive == FALSE)) {
//
// There are enough pages on the free list, start
// the zeroing page thread.
//
MmZeroingPageThreadActive = TRUE;
KeSetEvent (&MmZeroingPageEvent, 0, FALSE);
}
return;
}
//
// Check to see if their are too many modified pages.
//
if (ListHead->ListName == ModifiedPageList) {
if (Pfn1->OriginalPte.u.Soft.Prototype == 0) {
ASSERT (Pfn1->OriginalPte.u.Soft.PageFileHigh == 0);
}
PsGetCurrentProcess()->ModifiedPageCount += 1;
if (MmModifiedPageListHead.Total >= MmModifiedPageMaximum ) {
//
// Start the modified page writer.
//
KeSetEvent (&MmModifiedPageWriterEvent, 0, FALSE);
}
}
return;
}
VOID
FASTCALL
MiInsertStandbyListAtFront (
IN ULONG PageFrameIndex
)
/*++
Routine Description:
This procedure inserts a page at the front of the standby list.
Arguments:
PageFrameIndex - Supplies the physical page number to insert in the
list.
Return Value:
none.
Environment:
Must be holding the PFN database mutex with APC's disabled.
--*/
{
ULONG first;
IN PMMPFNLIST ListHead;
PMMPFN Pfn1;
PMMPFN Pfn2;
KIRQL OldIrql;
PMMPTE PointerPte;
MMPTE TempPte;
MM_PFN_LOCK_ASSERT();
ASSERT ((PageFrameIndex != 0) && (PageFrameIndex <= MmHighestPhysicalPage) &&
(PageFrameIndex >= MmLowestPhysicalPage));
//
// Check to ensure the reference count for the page
// is zero.
//
Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
#if DBG
if (MmDebug & MM_DBG_PAGE_REF_COUNT) {
PMMPTE PointerPte;
KIRQL OldIrql = 99;
if ((Pfn1->u3.e1.PrototypePte == 1) &&
(MmIsAddressValid (Pfn1->PteAddress))) {
PointerPte = Pfn1->PteAddress;
} else {
//
// The page containing the prototype PTE is not valid,
// map the page into hyperspace and reference it that way.
//
PointerPte = MiMapPageInHyperSpace (Pfn1->PteFrame, &OldIrql);
PointerPte = (PMMPTE)((ULONG)PointerPte +
MiGetByteOffset(Pfn1->PteAddress));
}
ASSERT ((PointerPte->u.Trans.PageFrameNumber == PageFrameIndex) ||
(PointerPte->u.Hard.PageFrameNumber == PageFrameIndex));
ASSERT (PointerPte->u.Soft.Transition == 1);
ASSERT (PointerPte->u.Soft.Prototype == 0);
if (OldIrql != 99) {
MiUnmapPageInHyperSpace (OldIrql)
}
}
#endif //DBG
#if DBG
if ((Pfn1->OriginalPte.u.Soft.Prototype == 0) &&
(Pfn1->OriginalPte.u.Soft.Transition == 1)) {
KeBugCheckEx (MEMORY_MANAGEMENT, 0x8889, 0,0,0);
}
#endif //DBG
ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
ASSERT (Pfn1->u3.e1.PrototypePte == 1);
MmStandbyPageListHead.Total += 1; // One more page on the list.
ListHead = &MmStandbyPageListHead;
#if MM_MAXIMUM_NUMBER_OF_COLORS > 1
ListHead = &MmStandbyPageListByColor [Pfn1->u3.e1.PageColor];
ListHead->Total += 1;
#endif // > 1
first = ListHead->Flink;
if (first == MM_EMPTY_LIST) {
//
// List is empty add the page to the ListHead.
//
ListHead->Blink = PageFrameIndex;
} else {
Pfn2 = MI_PFN_ELEMENT (first);
Pfn2->u2.Blink = PageFrameIndex;
}
ListHead->Flink = PageFrameIndex;
Pfn1->u2.Blink = MM_EMPTY_LIST;
Pfn1->u1.Flink = first;
Pfn1->u3.e1.PageLocation = StandbyPageList;
//
// If the page was placed on the free, standby or zeroed list,
// update the count of usable pages in the system. If the count
// transitions from 0 to 1, the event associated with available
// pages should become true.
//
MmAvailablePages += 1;
//
// A page has just become available, check to see if the
// page wait events should be signalled.
//
if (MmAvailablePages == MM_LOW_LIMIT) {
KeSetEvent (&MmAvailablePagesEvent, 0, FALSE);
} else if (MmAvailablePages == MM_HIGH_LIMIT) {
KeSetEvent (&MmAvailablePagesEventHigh, 0, FALSE);
}
return;
}
ULONG //PageFrameIndex
FASTCALL
MiRemovePageFromList (
IN PMMPFNLIST ListHead
)
/*++
Routine Description:
This procedure removes a page from the head of the specified list (free,
standby, zeroed, modified). Note, that is makes no sense to remove
a page from the head of the bad list.
This routine clears the flags word in the PFN database, hence the
PFN information for this page must be initialized.
Arguments:
ListHead - Supplies the list of the list in which to remove the
specified physical page.
Return Value:
The physical page number removed from the specified list.
Environment:
Must be holding the PFN database mutex with APC's disabled.
--*/
{
ULONG PageFrameIndex;
PMMPFN Pfn1;
PMMPFN Pfn2;
ULONG Color;
MM_PFN_LOCK_ASSERT();
//
// If the specified list is empty return MM_EMPTY_LIST.
//
if (ListHead->Total == 0) {
KdPrint(("MM:Attempting to remove page from empty list\n"));
KeBugCheckEx (PFN_LIST_CORRUPT, 1, (ULONG)ListHead, MmAvailablePages, 0);
return 0;
}
ASSERT (ListHead->ListName != ModifiedPageList);
//
// Decrement the count of pages on the list and remove the first
// page from the list.
//
ListHead->Total -= 1;
PageFrameIndex = ListHead->Flink;
Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
ListHead->Flink = Pfn1->u1.Flink;
//
// Zero the flink and blink in the pfn database element.
//
Pfn1->u1.Flink = 0;
Pfn1->u2.Blink = 0;
//
// If the last page was removed (the ListHead->Flink is now
// MM_EMPTY_LIST) make the listhead->Blink MM_EMPTY_LIST as well.
//
if (ListHead->Flink == MM_EMPTY_LIST) {
ListHead->Blink = MM_EMPTY_LIST;
} else {
//
// Make the PFN element point to MM_EMPTY_LIST signifying this
// is the last page in the list.
//
Pfn2 = MI_PFN_ELEMENT (ListHead->Flink);
Pfn2->u2.Blink = MM_EMPTY_LIST;
}
//
// Check to see if we now have one less page available.
//
if (ListHead->ListName <= StandbyPageList) {
MmAvailablePages -= 1;
if (ListHead->ListName == StandbyPageList) {
//
// This page is currently in transition, restore the PTE to
// its original contents so this page can be reused.
//
MiRestoreTransitionPte (PageFrameIndex);
}
if (MmAvailablePages < MmMinimumFreePages) {
//
// Obtain free pages.
//
MiObtainFreePages();
}
}
ASSERT ((PageFrameIndex != 0) &&
(PageFrameIndex <= MmHighestPhysicalPage) &&
(PageFrameIndex >= MmLowestPhysicalPage));
//
// Zero the PFN flags longword.
//
Color = Pfn1->u3.e1.PageColor;
Pfn1->u3.e2.ShortFlags = 0;
Pfn1->u3.e1.PageColor = Color;
Color = MI_GET_SECONDARY_COLOR (PageFrameIndex, Pfn1);
if (ListHead->ListName <= FreePageList) {
//
// Update the color lists.
//
ASSERT (MmFreePagesByColor[ListHead->ListName][Color].Flink == PageFrameIndex);
MmFreePagesByColor[ListHead->ListName][Color].Flink =
Pfn1->OriginalPte.u.Long;
}
return PageFrameIndex;
}
VOID
FASTCALL
MiUnlinkPageFromList (
IN PMMPFN Pfn
)
/*++
Routine Description:
This procedure removes a page from the middle of a list. This is
designed for the faulting of transition pages from the standby and
modified list and making the active and valid again.
Arguments:
Pfn - Supplies a pointer to the PFN database element for the physical
page to remove from the list.
Return Value:
none.
Environment:
Must be holding the PFN database mutex with APC's disabled.
--*/
{
PMMPFNLIST ListHead;
ULONG Previous;
ULONG Next;
PMMPFN Pfn2;
MM_PFN_LOCK_ASSERT();
//
// Page not on standby or modified list, check to see if the
// page is currently being written by the modified page
// writer, if so, just return this page. The reference
// count for the page will be incremented, so when the modified
// page write completes, the page will not be put back on
// the list, rather, it will remain active and valid.
//
if (Pfn->u3.e2.ReferenceCount > 0) {
//
// The page was not on any "transition lists", check to see
// if this is has I/O in progress.
//
if (Pfn->u2.ShareCount == 0) {
#if DBG
if (MmDebug & MM_DBG_PAGE_IN_LIST) {
DbgPrint("unlinking page not in list...\n");
MiFormatPfn(Pfn);
}
#endif
return;
}
KdPrint(("MM:attempt to remove page from wrong page list\n"));
KeBugCheckEx (PFN_LIST_CORRUPT,
2,
Pfn - MmPfnDatabase,
MmHighestPhysicalPage,
Pfn->u3.e2.ReferenceCount);
return;
}
ListHead = MmPageLocationList[Pfn->u3.e1.PageLocation];
//
// On MIPS R4000 modified pages destined for the paging file are
// kept on sperate lists which group pages of the same color
// together
//
if ((ListHead == &MmModifiedPageListHead) &&
(Pfn->OriginalPte.u.Soft.Prototype == 0)) {
//
// This page is destined for the paging file (not
// a mapped file). Change the list head to the
// appropriate colored list head.
//
ListHead->Total -= 1;
MmTotalPagesForPagingFile -= 1;
ListHead = &MmModifiedPageListByColor [Pfn->u3.e1.PageColor];
}
#if MM_MAXIMUM_NUMBER_OF_COLORS > 1
if (ListHead == &MmStandbyPageListHead) {
//
// This page is destined for the paging file (not
// a mapped file). Change the list head to the
// appropriate colored list head.
//
ListHead->Total -= 1;
ListHead = &MmStandbyPageListByColor [Pfn->u3.e1.PageColor];
}
#endif //MM_MAXIMUM_NUMBER_OF_COLORS > 1
ASSERT (Pfn->u3.e1.WriteInProgress == 0);
ASSERT (Pfn->u3.e1.ReadInProgress == 0);
ASSERT (ListHead->Total != 0);
Next = Pfn->u1.Flink;
Pfn->u1.Flink = 0;
Previous = Pfn->u2.Blink;
Pfn->u2.Blink = 0;
if (Next == MM_EMPTY_LIST) {
ListHead->Blink = Previous;
} else {
Pfn2 = MI_PFN_ELEMENT(Next);
Pfn2->u2.Blink = Previous;
}
if (Previous == MM_EMPTY_LIST) {
ListHead->Flink = Next;
} else {
Pfn2 = MI_PFN_ELEMENT(Previous);
Pfn2->u1.Flink = Next;
}
ListHead->Total -= 1;
//
// Check to see if we now have one less page available.
//
if (ListHead->ListName <= StandbyPageList) {
MmAvailablePages -= 1;
if (MmAvailablePages < MmMinimumFreePages) {
//
// Obtain free pages.
//
MiObtainFreePages();
}
}
return;
}
VOID
MiUnlinkFreeOrZeroedPage (
IN ULONG Page
)
/*++
Routine Description:
This procedure removes a page from the middle of a list. This is
designed for the faulting of transition pages from the standby and
modified list and making the active and valid again.
Arguments:
Pfn - Supplies a pointer to the PFN database element for the physical
page to remove from the list.
Return Value:
none.
Environment:
Must be holding the PFN database mutex with APC's disabled.
--*/
{
PMMPFNLIST ListHead;
ULONG Previous;
ULONG Next;
PMMPFN Pfn2;
PMMPFN Pfn;
ULONG Color;
Pfn = MI_PFN_ELEMENT (Page);
MM_PFN_LOCK_ASSERT();
ListHead = MmPageLocationList[Pfn->u3.e1.PageLocation];
ASSERT (ListHead->Total != 0);
ListHead->Total -= 1;
#if MM_MAXIMUM_NUMBER_OF_COLORS > 1
ListHead = &MmFreePagesByPrimaryColor [ListHead->ListName] [Pfn->u3.e1.PageColor];
#endif
ASSERT (ListHead->ListName <= FreePageList);
ASSERT (Pfn->u3.e1.WriteInProgress == 0);
ASSERT (Pfn->u3.e1.ReadInProgress == 0);
Next = Pfn->u1.Flink;
Pfn->u1.Flink = 0;
Previous = Pfn->u2.Blink;
Pfn->u2.Blink = 0;
if (Next == MM_EMPTY_LIST) {
ListHead->Blink = Previous;
} else {
Pfn2 = MI_PFN_ELEMENT(Next);
Pfn2->u2.Blink = Previous;
}
if (Previous == MM_EMPTY_LIST) {
ListHead->Flink = Next;
} else {
Pfn2 = MI_PFN_ELEMENT(Previous);
Pfn2->u1.Flink = Next;
}
//
// We are removing a page from the middle of the free or zeroed page list.
// The secondary color tables must be updated at this time.
//
Color = MI_GET_SECONDARY_COLOR (Page, Pfn);
ASSERT (Pfn->u3.e1.PageColor == MI_GET_COLOR_FROM_SECONDARY(Color));
//
// Walk down the list and remove the page.
//
Next = MmFreePagesByColor[ListHead->ListName][Color].Flink;
if (Next == Page) {
MmFreePagesByColor[ListHead->ListName][Color].Flink =
Pfn->OriginalPte.u.Long;
} else {
//
// Walk the list to find the parent.
//
for (; ; ) {
Pfn2 = MI_PFN_ELEMENT (Next);
Next = Pfn2->OriginalPte.u.Long;
if (Page == Next) {
Pfn2->OriginalPte.u.Long = Pfn->OriginalPte.u.Long;
if (Pfn->OriginalPte.u.Long == MM_EMPTY_LIST) {
MmFreePagesByColor[ListHead->ListName][Color].Blink = Pfn2;
}
break;
}
}
}
MmAvailablePages -= 1;
return;
}
ULONG
FASTCALL
MiEnsureAvailablePageOrWait (
IN PEPROCESS Process,
IN PVOID VirtualAddress
)
/*++
Routine Description:
This procedure ensures that a physical page is available on
the zeroed, free or standby list such that the next call the remove a
page absolutely will not block. This is necessary as blocking would
require a wait which could cause a deadlock condition.
If a page is available the function returns immediately with a value
of FALSE indicating no wait operation was performed. If no physical
page is available, the thread inters a wait state and the function
returns the value TRUE when the wait operation completes.
Arguments:
Process - Supplies a pointer to the current process if, and only if,
the working set mutex is held currently held and should
be released if a wait operation is issued. Supplies
the value NULL otherwise.
VirtualAddress - Supplies the virtual address for the faulting page.
If the value is NULL, the page is treated as a
user mode address.
Return Value:
FALSE - if a page was immediately available.
TRUE - if a wait operation occurred before a page became available.
Environment:
Must be holding the PFN database mutex with APC's disabled.
--*/
{
PVOID Event;
NTSTATUS Status;
KIRQL OldIrql;
KIRQL Ignore;
ULONG Limit;
ULONG Relock;
MM_PFN_LOCK_ASSERT();
if (MmAvailablePages >= MM_HIGH_LIMIT) {
//
// Pages are available.
//
return FALSE;
}
//
// If this fault is for paged pool (or pageable kernel space,
// including page table pages), let it use the last page.
//
if (((PMMPTE)VirtualAddress > MiGetPteAddress(HYPER_SPACE)) ||
((VirtualAddress > MM_HIGHEST_USER_ADDRESS) &&
(VirtualAddress < (PVOID)PTE_BASE))) {
//
// This fault is in the system, use 1 page as the limit.
//
if (MmAvailablePages >= MM_LOW_LIMIT) {
//
// Pages are available.
//
return FALSE;
}
Limit = MM_LOW_LIMIT;
Event = (PVOID)&MmAvailablePagesEvent;
} else {
Limit = MM_HIGH_LIMIT;
Event = (PVOID)&MmAvailablePagesEventHigh;
}
while (MmAvailablePages < Limit) {
KeClearEvent ((PKEVENT)Event);
UNLOCK_PFN (APC_LEVEL);
if (Process != NULL) {
UNLOCK_WS (Process);
} else {
Relock = FALSE;
if (MmSystemLockOwner == PsGetCurrentThread()) {
UNLOCK_SYSTEM_WS (APC_LEVEL);
Relock = TRUE;
}
}
//
// Wait for ALL the objects to become available.
//
//
// Wait for 7 minutes then bugcheck.
//
Status = KeWaitForSingleObject(Event,
WrFreePage,
KernelMode,
FALSE,
(PLARGE_INTEGER)&MmSevenMinutes);
if (Status == STATUS_TIMEOUT) {
KeBugCheckEx (NO_PAGES_AVAILABLE,
MmModifiedPageListHead.Total,
MmNumberOfPhysicalPages,
MmExtendedCommit,
MmTotalCommittedPages);
return TRUE;
}
if (Process != NULL) {
LOCK_WS (Process);
} else {
if (Relock) {
LOCK_SYSTEM_WS (Ignore);
}
}
LOCK_PFN (OldIrql);
}
return TRUE;
}
ULONG //PageFrameIndex
FASTCALL
MiRemoveZeroPage (
IN ULONG PageColor
)
/*++
Routine Description:
This procedure removes a zero page from either the zeroed, free
or standby lists (in that order). If no pages exist on the zeroed
or free list a transition page is removed from the standby list
and the PTE (may be a prototype PTE) which refers to this page is
changed from transition back to its original contents.
If the page is not obtained from the zeroed list, it is zeroed.
Note, if no pages exist to satisfy this request an exception is
raised.
Arguments:
PageColor - Supplies the page color for which this page is destined.
This is used for checking virtual address aligments to
determine if the D cache needs flushing before the page
can be reused.
Return Value:
The physical page number removed from the specified list.
Environment:
Must be holding the PFN database mutex with APC's disabled.
--*/
{
ULONG Page;
PMMPFN Pfn1;
ULONG i;
ULONG Color;
ULONG PrimaryColor;
PMMCOLOR_TABLES ColorTable;
MM_PFN_LOCK_ASSERT();
ASSERT(MmAvailablePages != 0);
//
// Attempt to remove a page from the zeroed page list. If a page
// is available, then remove it and return its page frame index.
// Otherwise, attempt to remove a page from the free page list or
// the standby list.
//
// N.B. It is not necessary to change page colors even if the old
// color is not equal to the new color. The zero page thread
// ensures that all zeroed pages are removed from all caches.
//
if (MmFreePagesByColor[ZeroedPageList][PageColor].Flink != MM_EMPTY_LIST) {
//
// Remove the first entry on the zeroed by color list.
//
Page = MmFreePagesByColor[ZeroedPageList][PageColor].Flink;
#if DBG
Pfn1 = MI_PFN_ELEMENT(Page);
ASSERT (Pfn1->u3.e1.PageLocation == ZeroedPageList);
#endif //DBG
MiRemovePageByColor (Page, PageColor);
#if DBG
ASSERT (Pfn1->u3.e1.PageColor == (PageColor & MM_COLOR_MASK));
ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
ASSERT (Pfn1->u2.ShareCount == 0);
#endif //DBG
return Page;
} else {
//
// No color with the specified color exits, try a zeroed
// page of the primary color.
//
#if MM_MAXIMUM_NUMBER_OF_COLORS > 1
PrimaryColor = MI_GET_COLOR_FROM_SECONDARY(PageColor);
if (MmFreePagesByPrimaryColor[ZeroedPageList][PrimaryColor].Flink != MM_EMPTY_LIST) {
Page = MmFreePagesByPrimaryColor[ZeroedPageList][PrimaryColor].Flink;
#else
if (MmZeroedPageListHead.Flink != MM_EMPTY_LIST) {
Page = MmZeroedPageListHead.Flink;
#endif
#if DBG
Pfn1 = MI_PFN_ELEMENT(Page);
ASSERT (Pfn1->u3.e1.PageLocation == ZeroedPageList);
#endif //DBG
Color = MI_GET_SECONDARY_COLOR (Page, MI_PFN_ELEMENT(Page));
MiRemovePageByColor (Page, Color);
#if DBG
Pfn1 = MI_PFN_ELEMENT(Page);
ASSERT (Pfn1->u3.e1.PageColor == (PageColor & MM_COLOR_MASK));
ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
ASSERT (Pfn1->u2.ShareCount == 0);
#endif //DBG
return Page;
}
//
// No zeroed page at the right color exist, try a free page of the
// secondary color.
//
if (MmFreePagesByColor[FreePageList][PageColor].Flink != MM_EMPTY_LIST) {
//
// Remove the first entry on the free list by color.
//
Page = MmFreePagesByColor[FreePageList][PageColor].Flink;
#if DBG
Pfn1 = MI_PFN_ELEMENT(Page);
ASSERT (Pfn1->u3.e1.PageLocation == FreePageList);
#endif //DBG
MiRemovePageByColor (Page, PageColor);
#if DBG
Pfn1 = MI_PFN_ELEMENT(Page);
ASSERT (Pfn1->u3.e1.PageColor == (PageColor & MM_COLOR_MASK));
ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
ASSERT (Pfn1->u2.ShareCount == 0);
#endif //DBG
goto ZeroPage;
}
#if MM_MAXIMUM_NUMBER_OF_COLORS > 1
if (MmFreePagesByPrimaryColor[FreePageList][PrimaryColor].Flink != MM_EMPTY_LIST) {
Page = MmFreePagesByPrimaryColor[FreePageList][PrimaryColor].Flink;
#else
if (MmFreePageListHead.Flink != MM_EMPTY_LIST) {
Page = MmFreePageListHead.Flink;
#endif
Color = MI_GET_SECONDARY_COLOR (Page, MI_PFN_ELEMENT(Page));
MiRemovePageByColor (Page, Color);
#if DBG
Pfn1 = MI_PFN_ELEMENT(Page);
ASSERT (Pfn1->u3.e1.PageColor == (PageColor & MM_COLOR_MASK));
ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
ASSERT (Pfn1->u2.ShareCount == 0);
#endif //DBG
goto ZeroPage;
}
}
#if MM_NUMBER_OF_COLORS < 2
ASSERT (MmZeroedPageListHead.Total == 0);
ASSERT (MmFreePageListHead.Total == 0);
#endif //NUMBER_OF_COLORS
if (MmZeroedPageListHead.Total != 0) {
#if MM_MAXIMUM_NUMBER_OF_COLORS > 1
for (i = 0; i < MM_MAXIMUM_NUMBER_OF_COLORS; i++) {
MmColorSearch = (MmColorSearch + 1) & (MM_MAXIMUM_NUMBER_OF_COLORS - 1);
Page = MmFreePagesByPrimaryColor[ZeroedPageList][MmColorSearch].Flink;
if (Page != MM_EMPTY_LIST) {
break;
}
}
ASSERT (Page != MM_EMPTY_LIST);
#if DBG
Pfn1 = MI_PFN_ELEMENT(Page);
ASSERT (Pfn1->u3.e1.PageLocation == ZeroedPageList);
#endif //DBG
Color = MI_GET_SECONDARY_COLOR (Page, MI_PFN_ELEMENT(Page));
MiRemovePageByColor (Page, Color);
#else
Page = MiRemovePageFromList(&MmZeroedPageListHead);
#endif
MI_CHECK_PAGE_ALIGNMENT(Page, PageColor & MM_COLOR_MASK);
} else {
//
// Attempt to remove a page from the free list. If a page is
// available, then remove it. Otherwise, attempt to remove a
// page from the standby list.
//
if (MmFreePageListHead.Total != 0) {
#if MM_MAXIMUM_NUMBER_OF_COLORS > 1
for (i = 0; i < MM_MAXIMUM_NUMBER_OF_COLORS; i++) {
MmColorSearch = (MmColorSearch + 1) & (MM_MAXIMUM_NUMBER_OF_COLORS - 1);
Page = MmFreePagesByPrimaryColor[FreePageList][MmColorSearch].Flink;
if (Page != MM_EMPTY_LIST) {
break;
}
}
ASSERT (Page != MM_EMPTY_LIST);
Color = MI_GET_SECONDARY_COLOR (Page, MI_PFN_ELEMENT(Page));
#if DBG
Pfn1 = MI_PFN_ELEMENT(Page);
ASSERT (Pfn1->u3.e1.PageLocation == FreePageList);
#endif //DBG
MiRemovePageByColor (Page, Color);
#else
Page = MiRemovePageFromList(&MmFreePageListHead);
#endif
} else {
//
// Remove a page from the standby list and restore the original
// contents of the PTE to free the last reference to the physical
// page.
//
ASSERT (MmStandbyPageListHead.Total != 0);
#if MM_MAXIMUM_NUMBER_OF_COLORS > 1
if (MmStandbyPageListByColor[PrimaryColor].Flink != MM_EMPTY_LIST) {
Page = MiRemovePageFromList(&MmStandbyPageListByColor[PrimaryColor]);
} else {
for (i = 0; i < MM_MAXIMUM_NUMBER_OF_COLORS; i++) {
MmColorSearch = (MmColorSearch + 1) & (MM_MAXIMUM_NUMBER_OF_COLORS - 1);
if (MmStandbyPageListByColor[MmColorSearch].Flink != MM_EMPTY_LIST) {
Page = MiRemovePageFromList(&MmStandbyPageListByColor[MmColorSearch]);
break;
}
}
}
MmStandbyPageListHead.Total -= 1;
#else
Page = MiRemovePageFromList(&MmStandbyPageListHead);
#endif //MM_MAXIMUM_NUMBER_OF_COLORS > 1
}
//
// Zero the page removed from the free or standby list.
//
ZeroPage:
Pfn1 = MI_PFN_ELEMENT(Page);
#if defined(MIPS) || defined(_ALPHA_)
HalZeroPage((PVOID)((PageColor & MM_COLOR_MASK) << PAGE_SHIFT),
(PVOID)((ULONG)(Pfn1->u3.e1.PageColor) << PAGE_SHIFT),
Page);
#elif defined(_PPC_)
KeZeroPage(Page);
#else
MiZeroPhysicalPage (Page, 0);
#endif //MIPS
Pfn1->u3.e1.PageColor = PageColor & MM_COLOR_MASK;
}
#if DBG
Pfn1 = MI_PFN_ELEMENT (Page);
ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
ASSERT (Pfn1->u2.ShareCount == 0);
#endif //DBG
return Page;
}
ULONG //PageFrameIndex
FASTCALL
MiRemoveAnyPage (
IN ULONG PageColor
)
/*++
Routine Description:
This procedure removes a page from either the free, zeroed,
or standby lists (in that order). If no pages exist on the zeroed
or free list a transition page is removed from the standby list
and the PTE (may be a prototype PTE) which refers to this page is
changed from transition back to its original contents.
Note, if no pages exist to satisfy this request an exception is
raised.
Arguments:
PageColor - Supplies the page color for which this page is destined.
This is used for checking virtual address aligments to
determine if the D cache needs flushing before the page
can be reused.
Return Value:
The physical page number removed from the specified list.
Environment:
Must be holding the PFN database mutex with APC's disabled.
--*/
{
ULONG Page;
PMMPFN Pfn1;
ULONG PrimaryColor;
ULONG Color;
PMMCOLOR_TABLES ColorTable;
ULONG i;
MM_PFN_LOCK_ASSERT();
ASSERT(MmAvailablePages != 0);
//
// Check the free page list, and if a page is available
// remove it and return its value.
//
if (MmFreePagesByColor[FreePageList][PageColor].Flink != MM_EMPTY_LIST) {
//
// Remove the first entry on the free by color list.
//
Page = MmFreePagesByColor[FreePageList][PageColor].Flink;
MiRemovePageByColor (Page, PageColor);
#if DBG
Pfn1 = MI_PFN_ELEMENT(Page);
ASSERT (Pfn1->u3.e1.PageColor == (PageColor & MM_COLOR_MASK));
ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
ASSERT (Pfn1->u2.ShareCount == 0);
#endif //DBG
return Page;
} else if (MmFreePagesByColor[ZeroedPageList][PageColor].Flink
!= MM_EMPTY_LIST) {
//
// Remove the first entry on the zeroed by color list.
//
Page = MmFreePagesByColor[ZeroedPageList][PageColor].Flink;
#if DBG
Pfn1 = MI_PFN_ELEMENT(Page);
ASSERT (Pfn1->u3.e1.PageColor == (PageColor & MM_COLOR_MASK));
ASSERT (Pfn1->u3.e1.PageLocation == ZeroedPageList);
#endif //DBG
MiRemovePageByColor (Page, PageColor);
return Page;
} else {
//
// Try the free page list by primary color.
//
#if MM_MAXIMUM_NUMBER_OF_COLORS > 1
PrimaryColor = MI_GET_COLOR_FROM_SECONDARY(PageColor);
if (MmFreePagesByPrimaryColor[FreePageList][PrimaryColor].Flink != MM_EMPTY_LIST) {
Page = MmFreePagesByPrimaryColor[FreePageList][PrimaryColor].Flink;
#else
if (MmFreePageListHead.Flink != MM_EMPTY_LIST) {
Page = MmFreePageListHead.Flink;
#endif
Color = MI_GET_SECONDARY_COLOR (Page, MI_PFN_ELEMENT(Page));
#if DBG
Pfn1 = MI_PFN_ELEMENT(Page);
ASSERT (Pfn1->u3.e1.PageColor == (PageColor & MM_COLOR_MASK));
ASSERT (Pfn1->u3.e1.PageLocation == FreePageList);
#endif //DBG
MiRemovePageByColor (Page, Color);
#if DBG
Pfn1 = MI_PFN_ELEMENT(Page);
ASSERT (Pfn1->u3.e1.PageColor == (PageColor & MM_COLOR_MASK));
ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
ASSERT (Pfn1->u2.ShareCount == 0);
#endif //DBG
return Page;
#if MM_MAXIMUM_NUMBER_OF_COLORS > 1
} else if (MmFreePagesByPrimaryColor[ZeroedPageList][PrimaryColor].Flink != MM_EMPTY_LIST) {
Page = MmFreePagesByPrimaryColor[ZeroedPageList][PrimaryColor].Flink;
#else
} else if (MmZeroedPageListHead.Flink != MM_EMPTY_LIST) {
Page = MmZeroedPageListHead.Flink;
#endif
Color = MI_GET_SECONDARY_COLOR (Page, MI_PFN_ELEMENT(Page));
MiRemovePageByColor (Page, Color);
#if DBG
Pfn1 = MI_PFN_ELEMENT(Page);
ASSERT (Pfn1->u3.e1.PageColor == (PageColor & MM_COLOR_MASK));
ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
ASSERT (Pfn1->u2.ShareCount == 0);
#endif //DBG
return Page;
}
}
if (MmFreePageListHead.Total != 0) {
#if MM_MAXIMUM_NUMBER_OF_COLORS > 1
for (i = 0; i < MM_MAXIMUM_NUMBER_OF_COLORS; i++) {
MmColorSearch = (MmColorSearch + 1) & (MM_MAXIMUM_NUMBER_OF_COLORS - 1);
Page = MmFreePagesByPrimaryColor[FreePageList][MmColorSearch].Flink;
if (Page != MM_EMPTY_LIST) {
break;
}
}
ASSERT (Page != MM_EMPTY_LIST);
Color = MI_GET_SECONDARY_COLOR (Page, MI_PFN_ELEMENT(Page));
#if DBG
Pfn1 = MI_PFN_ELEMENT(Page);
ASSERT (Pfn1->u3.e1.PageLocation == FreePageList);
#endif //DBG
MiRemovePageByColor (Page, Color);
#else
Page = MiRemovePageFromList(&MmFreePageListHead);
#endif
} else {
//
// Check the zeroed page list, and if a page is available
// remove it and return its value.
//
if (MmZeroedPageListHead.Total != 0) {
#if MM_MAXIMUM_NUMBER_OF_COLORS > 1
for (i = 0; i < MM_MAXIMUM_NUMBER_OF_COLORS; i++) {
MmColorSearch = (MmColorSearch + 1) & (MM_MAXIMUM_NUMBER_OF_COLORS - 1);
Page = MmFreePagesByPrimaryColor[ZeroedPageList][MmColorSearch].Flink;
if (Page != MM_EMPTY_LIST) {
break;
}
}
ASSERT (Page != MM_EMPTY_LIST);
#if DBG
Pfn1 = MI_PFN_ELEMENT(Page);
ASSERT (Pfn1->u3.e1.PageLocation == ZeroedPageList);
#endif //DBG
Color = MI_GET_SECONDARY_COLOR (Page, MI_PFN_ELEMENT(Page));
MiRemovePageByColor (Page, Color);
#else
Page = MiRemovePageFromList(&MmZeroedPageListHead);
#endif
} else {
//
// No pages exist on the free or zeroed list, use the
// standby list.
//
ASSERT(MmStandbyPageListHead.Total != 0);
#if MM_MAXIMUM_NUMBER_OF_COLORS > 1
if (MmStandbyPageListByColor[PrimaryColor].Flink != MM_EMPTY_LIST) {
Page = MiRemovePageFromList(&MmStandbyPageListByColor[PrimaryColor]);
} else {
for (i = 0; i < MM_MAXIMUM_NUMBER_OF_COLORS; i++) {
MmColorSearch = (MmColorSearch + 1) & (MM_MAXIMUM_NUMBER_OF_COLORS - 1);
if (MmStandbyPageListByColor[MmColorSearch].Flink != MM_EMPTY_LIST) {
Page = MiRemovePageFromList(&MmStandbyPageListByColor[MmColorSearch]);
break;
}
}
}
MmStandbyPageListHead.Total -= 1;
#else
Page = MiRemovePageFromList(&MmStandbyPageListHead);
#endif //MM_MAXIMUM_NUMBER_OF_COLORS > 1
}
}
MI_CHECK_PAGE_ALIGNMENT(Page, PageColor & MM_COLOR_MASK);
#if DBG
Pfn1 = MI_PFN_ELEMENT (Page);
ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
ASSERT (Pfn1->u2.ShareCount == 0);
#endif //DBG
return Page;
}
VOID
MiRemovePageByColor (
IN ULONG Page,
IN ULONG Color
)
/*++
Routine Description:
This procedure removes a page from the middle of the free or
zered page list.
Arguments:
PageFrameIndex - Supplies the physical page number to unlink from the
list.
Return Value:
none.
Environment:
Must be holding the PFN database mutex with APC's disabled.
--*/
{
PMMPFNLIST ListHead;
PMMPFNLIST PrimaryListHead;
ULONG Previous;
ULONG Next;
PMMPFN Pfn1;
PMMPFN Pfn2;
ULONG PrimaryColor;
MM_PFN_LOCK_ASSERT();
Pfn1 = MI_PFN_ELEMENT (Page);
PrimaryColor = Pfn1->u3.e1.PageColor;
ListHead = MmPageLocationList[Pfn1->u3.e1.PageLocation];
ListHead->Total -= 1;
#if MM_MAXIMUM_NUMBER_OF_COLORS > 1
PrimaryListHead =
&MmFreePagesByPrimaryColor[Pfn1->u3.e1.PageLocation][PrimaryColor];
#else
PrimaryListHead = ListHead;
#endif
Next = Pfn1->u1.Flink;
Pfn1->u1.Flink = 0;
Previous = Pfn1->u2.Blink;
Pfn1->u2.Blink = 0;
if (Next == MM_EMPTY_LIST) {
PrimaryListHead->Blink = Previous;
} else {
Pfn2 = MI_PFN_ELEMENT(Next);
Pfn2->u2.Blink = Previous;
}
if (Previous == MM_EMPTY_LIST) {
PrimaryListHead->Flink = Next;
} else {
Pfn2 = MI_PFN_ELEMENT(Previous);
Pfn2->u1.Flink = Next;
}
//
// Zero the flags longword, but keep the color information.
//
Pfn1->u3.e2.ShortFlags = 0;
Pfn1->u3.e1.PageColor = PrimaryColor;
//
// Update the color lists.
//
MmFreePagesByColor[ListHead->ListName][Color].Flink =
Pfn1->OriginalPte.u.Long;
//
// Note that we now have one less page available.
//
MmAvailablePages -= 1;
if (MmAvailablePages < MmMinimumFreePages) {
//
// Obtain free pages.
//
MiObtainFreePages();
}
return;
}
VOID
FASTCALL
MiInsertFrontModifiedNoWrite (
IN ULONG PageFrameIndex
)
/*++
Routine Description:
This procedure inserts a page at the FRONT of the modified no
write list.
Arguments:
PageFrameIndex - Supplies the physical page number to insert in the
list.
Return Value:
none.
Environment:
Must be holding the PFN database mutex with APC's disabled.
--*/
{
ULONG first;
PMMPFN Pfn1;
PMMPFN Pfn2;
MM_PFN_LOCK_ASSERT();
ASSERT ((PageFrameIndex != 0) && (PageFrameIndex <= MmHighestPhysicalPage) &&
(PageFrameIndex >= MmLowestPhysicalPage));
//
// Check to ensure the reference count for the page
// is zero.
//
Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
MmModifiedNoWritePageListHead.Total += 1; // One more page on the list.
first = MmModifiedNoWritePageListHead.Flink;
if (first == MM_EMPTY_LIST) {
//
// List is empty add the page to the ListHead.
//
MmModifiedNoWritePageListHead.Blink = PageFrameIndex;
} else {
Pfn2 = MI_PFN_ELEMENT (first);
Pfn2->u2.Blink = PageFrameIndex;
}
MmModifiedNoWritePageListHead.Flink = PageFrameIndex;
Pfn1->u1.Flink = first;
Pfn1->u2.Blink = MM_EMPTY_LIST;
Pfn1->u3.e1.PageLocation = ModifiedNoWritePageList;
return;
}
#if 0
PVOID MmCompressionWorkSpace;
ULONG MmCompressionWorkSpaceSize;
PCHAR MmCompressedBuffer;
VOID
MiInitializeCompression (VOID)
{
NTSTATUS status;
ULONG Frag;
status = RtlGetCompressionWorkSpaceSize (COMPRESSION_FORMAT_LZNT1,
&MmCompressionWorkSpaceSize,
&Frag
);
ASSERT (NT_SUCCESS (status));
MmCompressionWorkSpace = ExAllocatePoolWithTag (NonPagedPool,
MmCompressionWorkSpaceSize,' mM');
MmCompressedBuffer = ExAllocatePoolWithTag (NonPagedPool, PAGE_SIZE,' mM');
return;
}
ULONG MmCompressionStats[(PAGE_SIZE/256) + 1];
ULONG
MiCompressPage (
IN PVOID Input
)
{
ULONG Size;
NTSTATUS status;
status = RtlCompressBuffer (COMPRESSION_FORMAT_LZNT1,
(PCHAR)Input,
PAGE_SIZE,
MmCompressedBuffer,
PAGE_SIZE,
4096,
&Size,
(PVOID)MmCompressionWorkSpace);
if (!NT_SUCCESS (status)) {
KdPrint(("MM:compress failed %lx\n",status));
MmCompressionStats[4096/256] += 1;
} else {
MmCompressionStats[Size/256] += 1;
}
return Size;
}
#endif //0