1431 lines
37 KiB
C
1431 lines
37 KiB
C
/*++
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
mmquota.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the routines which implement the quota and
|
||
commitment charging for memory management.
|
||
|
||
Author:
|
||
|
||
Lou Perazzoli (loup) 12-December-89
|
||
Landy Wang (landyw) 02-Jun-1997
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "mi.h"
|
||
|
||
#define MM_MINIMAL_COMMIT_INCREASE 512
|
||
|
||
SIZE_T MmPeakCommitment;
|
||
|
||
LONG MiCommitPopups[2];
|
||
|
||
ULONG MiChargeCommitmentFailures[2];
|
||
|
||
extern ULONG_PTR MmAllocatedPagedPool;
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(INIT,MiInitializeCommitment)
|
||
#pragma alloc_text(PAGE,MiCalculatePageCommitment)
|
||
#pragma alloc_text(PAGE,MiReturnPageTablePageCommitment)
|
||
#endif
|
||
|
||
SIZE_T MmSystemCommitReserve = (5 * 1024 * 1024) / PAGE_SIZE;
|
||
|
||
VOID
|
||
MiInitializeCommitment (
|
||
VOID
|
||
)
|
||
{
|
||
if (MmNumberOfPhysicalPages < (33 * 1024 * 1024) / PAGE_SIZE) {
|
||
MmSystemCommitReserve = (1 * 1024 * 1024) / PAGE_SIZE;
|
||
}
|
||
|
||
#if defined (_MI_DEBUG_COMMIT_LEAKS)
|
||
MiCommitTraces = ExAllocatePoolWithTag (NonPagedPool,
|
||
MI_COMMIT_TRACE_MAX * sizeof (MI_COMMIT_TRACES),
|
||
'tCmM');
|
||
#endif
|
||
}
|
||
|
||
LOGICAL
|
||
FASTCALL
|
||
MiChargeCommitment (
|
||
IN SIZE_T QuotaCharge,
|
||
IN PEPROCESS Process OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine checks to ensure the system has sufficient page file
|
||
space remaining.
|
||
|
||
Since this routine is generally used to charge commitment on behalf of
|
||
usermode or other optional actions, this routine does not allow the
|
||
caller to use up the very last morsels of commit on the premise that
|
||
the operating system and drivers can put those to better use than any
|
||
application in order to prevent the appearance of system hangs.
|
||
|
||
Arguments:
|
||
|
||
QuotaCharge - Supplies the quota amount to charge.
|
||
|
||
Process - Optionally supplies the current process IF AND ONLY IF
|
||
the working set mutex is held. If the paging file
|
||
is being extended, the working set mutex is released if
|
||
this is non-null.
|
||
|
||
Return Value:
|
||
|
||
TRUE if there is sufficient space, FALSE if not.
|
||
|
||
Environment:
|
||
|
||
Kernel mode, APCs disabled, WorkingSetLock and AddressCreation mutexes
|
||
held.
|
||
|
||
--*/
|
||
|
||
{
|
||
SIZE_T OldCommitValue;
|
||
SIZE_T NewCommitValue;
|
||
SIZE_T CommitLimit;
|
||
MMPAGE_FILE_EXPANSION PageExtend;
|
||
LOGICAL WsHeldSafe;
|
||
|
||
ASSERT ((SSIZE_T)QuotaCharge > 0);
|
||
|
||
#if DBG
|
||
if (InitializationPhase > 1) {
|
||
ULONG i;
|
||
PKTHREAD Thread;
|
||
|
||
Thread = KeGetCurrentThread ();
|
||
for (i = 0; i < (ULONG)KeNumberProcessors; i += 1) {
|
||
if (KiProcessorBlock[i]->IdleThread == Thread) {
|
||
DbgPrint ("MMQUOTA: %x %p\n", i, Thread);
|
||
DbgBreakPoint ();
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// Initializing WsHeldSafe is not needed for correctness, but without it
|
||
// the compiler cannot compile this code W4 to check for use of
|
||
// uninitialized variables.
|
||
//
|
||
|
||
WsHeldSafe = FALSE;
|
||
|
||
do {
|
||
|
||
OldCommitValue = MmTotalCommittedPages;
|
||
|
||
NewCommitValue = OldCommitValue + QuotaCharge;
|
||
|
||
while (NewCommitValue + MmSystemCommitReserve > MmTotalCommitLimit) {
|
||
|
||
//
|
||
// If the pagefiles are already at the maximum, then don't
|
||
// bother trying to extend them, but do trim the cache.
|
||
//
|
||
|
||
if (MmTotalCommitLimit + 100 >= MmTotalCommitLimitMaximum) {
|
||
|
||
MiChargeCommitmentFailures[1] += 1;
|
||
|
||
MiTrimSegmentCache ();
|
||
|
||
if (MmTotalCommitLimit >= MmTotalCommitLimitMaximum) {
|
||
MiCauseOverCommitPopup ();
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
if (Process != NULL) {
|
||
|
||
//
|
||
// The working set lock may have been acquired safely or
|
||
// unsafely by our caller. Handle both cases here and below.
|
||
//
|
||
|
||
UNLOCK_WS_REGARDLESS(Process, WsHeldSafe);
|
||
}
|
||
|
||
//
|
||
// Queue a message to the segment dereferencing / pagefile extending
|
||
// thread to see if the page file can be extended. This is done
|
||
// in the context of a system thread due to mutexes which may
|
||
// currently be held.
|
||
//
|
||
|
||
PageExtend.InProgress = 1;
|
||
PageExtend.ActualExpansion = 0;
|
||
PageExtend.RequestedExpansionSize = QuotaCharge;
|
||
PageExtend.Segment = NULL;
|
||
PageExtend.PageFileNumber = MI_EXTEND_ANY_PAGEFILE;
|
||
KeInitializeEvent (&PageExtend.Event, NotificationEvent, FALSE);
|
||
|
||
if ((MiIssuePageExtendRequest (&PageExtend) == FALSE) ||
|
||
(PageExtend.ActualExpansion == 0)) {
|
||
|
||
MiCauseOverCommitPopup ();
|
||
|
||
MiChargeCommitmentFailures[0] += 1;
|
||
|
||
if (Process != NULL) {
|
||
LOCK_WS_REGARDLESS(Process, WsHeldSafe);
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
if (Process != NULL) {
|
||
LOCK_WS_REGARDLESS(Process, WsHeldSafe);
|
||
}
|
||
|
||
OldCommitValue = MmTotalCommittedPages;
|
||
|
||
NewCommitValue = OldCommitValue + QuotaCharge;
|
||
}
|
||
|
||
#if defined(_WIN64)
|
||
NewCommitValue = InterlockedCompareExchange64 (
|
||
(PLONGLONG) &MmTotalCommittedPages,
|
||
(LONGLONG) NewCommitValue,
|
||
(LONGLONG) OldCommitValue);
|
||
#else
|
||
NewCommitValue = InterlockedCompareExchange (
|
||
(PLONG) &MmTotalCommittedPages,
|
||
(LONG) NewCommitValue,
|
||
(LONG) OldCommitValue);
|
||
#endif
|
||
|
||
} while (NewCommitValue != OldCommitValue);
|
||
|
||
//
|
||
// Success.
|
||
//
|
||
|
||
MM_TRACK_COMMIT (MM_DBG_COMMIT_CHARGE_NORMAL, QuotaCharge);
|
||
|
||
if (MmTotalCommittedPages > MmPeakCommitment) {
|
||
MmPeakCommitment = MmTotalCommittedPages;
|
||
}
|
||
|
||
//
|
||
// Success. If system commit exceeds 90%, attempt a preemptive pagefile
|
||
// increase anyway.
|
||
//
|
||
|
||
NewCommitValue = MmTotalCommittedPages;
|
||
CommitLimit = MmTotalCommitLimit;
|
||
|
||
if (NewCommitValue > ((CommitLimit/10)*9)) {
|
||
|
||
if (CommitLimit < MmTotalCommitLimitMaximum) {
|
||
|
||
//
|
||
// Attempt to expand the paging file, but don't wait
|
||
// to see if it succeeds.
|
||
//
|
||
|
||
NewCommitValue = NewCommitValue - ((CommitLimit/100)*85);
|
||
|
||
MiIssuePageExtendRequestNoWait (NewCommitValue);
|
||
}
|
||
else {
|
||
|
||
//
|
||
// If the pagefiles are already at the maximum, then don't
|
||
// bother trying to extend them, but do trim the cache.
|
||
//
|
||
|
||
if (MmTotalCommitLimit + 100 >= MmTotalCommitLimitMaximum) {
|
||
MiTrimSegmentCache ();
|
||
}
|
||
}
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
LOGICAL
|
||
FASTCALL
|
||
MiChargeCommitmentCantExpand (
|
||
IN SIZE_T QuotaCharge,
|
||
IN ULONG MustSucceed
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine charges the specified commitment without attempting
|
||
to expand paging files and waiting for the expansion. The routine
|
||
determines if the paging file space is exhausted, and if so,
|
||
it attempts to ascertain if the paging file space could be expanded.
|
||
|
||
Arguments:
|
||
|
||
QuotaCharge - Supplies the quota amount to charge.
|
||
|
||
MustSucceed - Supplies TRUE if the charge must succeed.
|
||
|
||
Return Value:
|
||
|
||
TRUE if the commitment was permitted, FALSE if not.
|
||
|
||
Environment:
|
||
|
||
Kernel mode, APCs disabled.
|
||
|
||
--*/
|
||
|
||
{
|
||
SIZE_T CommitLimit;
|
||
SIZE_T ExtendAmount;
|
||
SIZE_T OldCommitValue;
|
||
SIZE_T NewCommitValue;
|
||
|
||
ASSERT ((SSIZE_T)QuotaCharge > 0);
|
||
|
||
ASSERT32 ((QuotaCharge < 0x100000) || (QuotaCharge < MmTotalCommitLimit));
|
||
|
||
do {
|
||
|
||
OldCommitValue = MmTotalCommittedPages;
|
||
|
||
NewCommitValue = OldCommitValue + QuotaCharge;
|
||
|
||
if ((NewCommitValue > MmTotalCommitLimit) && (!MustSucceed)) {
|
||
|
||
if ((NewCommitValue < MmTotalCommittedPages) ||
|
||
(MmTotalCommitLimit + 100 >= MmTotalCommitLimitMaximum)) {
|
||
|
||
MiChargeCommitmentFailures[1] += 1;
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Attempt to expand the paging file, but don't wait
|
||
// to see if it succeeds.
|
||
//
|
||
|
||
MiChargeCommitmentFailures[0] += 1;
|
||
MiIssuePageExtendRequestNoWait (MM_MINIMAL_COMMIT_INCREASE);
|
||
return FALSE;
|
||
}
|
||
|
||
#if defined(_WIN64)
|
||
NewCommitValue = InterlockedCompareExchange64 (
|
||
(PLONGLONG) &MmTotalCommittedPages,
|
||
(LONGLONG) NewCommitValue,
|
||
(LONGLONG) OldCommitValue);
|
||
#else
|
||
NewCommitValue = InterlockedCompareExchange (
|
||
(PLONG) &MmTotalCommittedPages,
|
||
(LONG) NewCommitValue,
|
||
(LONG) OldCommitValue);
|
||
#endif
|
||
|
||
} while (NewCommitValue != OldCommitValue);
|
||
|
||
MM_TRACK_COMMIT (MM_DBG_COMMIT_CHARGE_CANT_EXPAND, QuotaCharge);
|
||
|
||
//
|
||
// Success. If system commit exceeds 90%, attempt a preemptive pagefile
|
||
// increase anyway.
|
||
//
|
||
|
||
NewCommitValue = MmTotalCommittedPages;
|
||
CommitLimit = MmTotalCommitLimit;
|
||
|
||
if ((NewCommitValue > ((CommitLimit/10)*9)) &&
|
||
(CommitLimit < MmTotalCommitLimitMaximum)) {
|
||
|
||
//
|
||
// Attempt to expand the paging file, but don't wait
|
||
// to see if it succeeds.
|
||
//
|
||
// Queue a message to the segment dereferencing / pagefile extending
|
||
// thread to see if the page file can be extended. This is done
|
||
// in the context of a system thread due to mutexes which may
|
||
// currently be held.
|
||
//
|
||
|
||
ExtendAmount = NewCommitValue - ((CommitLimit/100)*85);
|
||
|
||
if (QuotaCharge > ExtendAmount) {
|
||
ExtendAmount = QuotaCharge;
|
||
}
|
||
|
||
MiIssuePageExtendRequestNoWait (ExtendAmount);
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
LOGICAL
|
||
FASTCALL
|
||
MiChargeTemporaryCommitmentForReduction (
|
||
IN SIZE_T QuotaCharge
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine attempts to charge the specified commitment without
|
||
expanding the paging file.
|
||
|
||
This is typically called just prior to reducing the pagefile size.
|
||
|
||
Arguments:
|
||
|
||
QuotaCharge - Supplies the quota amount to charge.
|
||
|
||
Return Value:
|
||
|
||
TRUE if the commitment was permitted, FALSE if not.
|
||
|
||
Environment:
|
||
|
||
Kernel mode, APCs disabled.
|
||
|
||
--*/
|
||
|
||
{
|
||
SIZE_T OldCommitValue;
|
||
SIZE_T NewCommitValue;
|
||
|
||
ASSERT ((SSIZE_T)QuotaCharge > 0);
|
||
|
||
ASSERT32 (QuotaCharge < 0x100000);
|
||
|
||
do {
|
||
|
||
OldCommitValue = MmTotalCommittedPages;
|
||
|
||
NewCommitValue = OldCommitValue + QuotaCharge;
|
||
|
||
if (NewCommitValue > MmTotalCommitLimit) {
|
||
return FALSE;
|
||
}
|
||
|
||
#if defined(_WIN64)
|
||
NewCommitValue = InterlockedCompareExchange64 (
|
||
(PLONGLONG) &MmTotalCommittedPages,
|
||
(LONGLONG) NewCommitValue,
|
||
(LONGLONG) OldCommitValue);
|
||
#else
|
||
NewCommitValue = InterlockedCompareExchange (
|
||
(PLONG) &MmTotalCommittedPages,
|
||
(LONG) NewCommitValue,
|
||
(LONG) OldCommitValue);
|
||
#endif
|
||
|
||
} while (NewCommitValue != OldCommitValue);
|
||
|
||
//
|
||
// Success.
|
||
//
|
||
|
||
MM_TRACK_COMMIT (MM_DBG_COMMIT_CHARGE_NORMAL, QuotaCharge);
|
||
|
||
if (MmTotalCommittedPages > MmPeakCommitment) {
|
||
MmPeakCommitment = MmTotalCommittedPages;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
SIZE_T
|
||
MiCalculatePageCommitment (
|
||
IN PVOID StartingAddress,
|
||
IN PVOID EndingAddress,
|
||
IN PMMVAD Vad,
|
||
IN PEPROCESS Process
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine examines the range of pages from the starting address
|
||
up to and including the ending address and returns the commit charge
|
||
for the pages within the range.
|
||
|
||
Arguments:
|
||
|
||
StartingAddress - Supplies the starting address of the range.
|
||
|
||
EndingAddress - Supplies the ending address of the range.
|
||
|
||
Vad - Supplies the virtual address descriptor which describes the range.
|
||
|
||
Process - Supplies the current process.
|
||
|
||
Return Value:
|
||
|
||
Commitment charge for the range.
|
||
|
||
Environment:
|
||
|
||
Kernel mode, APCs disabled, WorkingSetLock and AddressCreation mutexes
|
||
held.
|
||
|
||
--*/
|
||
|
||
{
|
||
PMMPTE PointerPte;
|
||
PMMPTE LastPte;
|
||
PMMPTE PointerPde;
|
||
PMMPTE PointerPpe;
|
||
PMMPTE PointerPxe;
|
||
SIZE_T NumberOfCommittedPages;
|
||
ULONG Waited;
|
||
|
||
PointerPxe = MiGetPxeAddress (StartingAddress);
|
||
PointerPpe = MiGetPpeAddress (StartingAddress);
|
||
PointerPde = MiGetPdeAddress (StartingAddress);
|
||
PointerPte = MiGetPteAddress (StartingAddress);
|
||
|
||
LastPte = MiGetPteAddress (EndingAddress);
|
||
|
||
if (Vad->u.VadFlags.MemCommit == 1) {
|
||
|
||
//
|
||
// All the pages are committed within this range.
|
||
//
|
||
|
||
NumberOfCommittedPages = BYTES_TO_PAGES ((PCHAR)EndingAddress -
|
||
(PCHAR)StartingAddress);
|
||
|
||
//
|
||
// Examine the PTEs to determine how many pages are committed.
|
||
//
|
||
|
||
do {
|
||
|
||
#if (_MI_PAGING_LEVELS >= 4)
|
||
retry:
|
||
#endif
|
||
|
||
while (!MiDoesPxeExistAndMakeValid (PointerPxe,
|
||
Process,
|
||
FALSE,
|
||
&Waited)) {
|
||
|
||
//
|
||
// No PXE exists for the starting address, therefore the page
|
||
// is not committed.
|
||
//
|
||
|
||
PointerPxe += 1;
|
||
PointerPpe = MiGetVirtualAddressMappedByPte (PointerPxe);
|
||
PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
|
||
PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
|
||
if (PointerPte > LastPte) {
|
||
return NumberOfCommittedPages;
|
||
}
|
||
}
|
||
|
||
#if (_MI_PAGING_LEVELS >= 4)
|
||
Waited = 0;
|
||
#endif
|
||
|
||
while (!MiDoesPpeExistAndMakeValid (PointerPpe,
|
||
Process,
|
||
FALSE,
|
||
&Waited)) {
|
||
|
||
//
|
||
// No PPE exists for the starting address, therefore the page
|
||
// is not committed.
|
||
//
|
||
|
||
PointerPpe += 1;
|
||
PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
|
||
PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
|
||
if (PointerPte > LastPte) {
|
||
return NumberOfCommittedPages;
|
||
}
|
||
#if (_MI_PAGING_LEVELS >= 4)
|
||
if (MiIsPteOnPdeBoundary (PointerPpe)) {
|
||
PointerPxe = MiGetPteAddress (PointerPpe);
|
||
goto retry;
|
||
}
|
||
#endif
|
||
}
|
||
|
||
#if (_MI_PAGING_LEVELS < 4)
|
||
Waited = 0;
|
||
#endif
|
||
|
||
while (!MiDoesPdeExistAndMakeValid (PointerPde,
|
||
Process,
|
||
FALSE,
|
||
&Waited)) {
|
||
|
||
//
|
||
// No PDE exists for the starting address, therefore the page
|
||
// is not committed.
|
||
//
|
||
|
||
PointerPde += 1;
|
||
PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
|
||
if (PointerPte > LastPte) {
|
||
return NumberOfCommittedPages;
|
||
}
|
||
#if (_MI_PAGING_LEVELS >= 3)
|
||
if (MiIsPteOnPdeBoundary (PointerPde)) {
|
||
PointerPpe = MiGetPteAddress (PointerPde);
|
||
PointerPxe = MiGetPdeAddress (PointerPde);
|
||
Waited = 1;
|
||
break;
|
||
}
|
||
#endif
|
||
}
|
||
|
||
} while (Waited != 0);
|
||
|
||
restart:
|
||
|
||
while (PointerPte <= LastPte) {
|
||
|
||
if (MiIsPteOnPdeBoundary (PointerPte)) {
|
||
|
||
//
|
||
// This is a PDE boundary, check to see if the all the
|
||
// PXE/PPE/PDE pages exist.
|
||
//
|
||
|
||
PointerPde = MiGetPteAddress (PointerPte);
|
||
PointerPpe = MiGetPteAddress (PointerPde);
|
||
PointerPxe = MiGetPteAddress (PointerPpe);
|
||
|
||
do {
|
||
|
||
if (!MiDoesPxeExistAndMakeValid (PointerPxe,
|
||
Process,
|
||
FALSE,
|
||
&Waited)) {
|
||
|
||
//
|
||
// No PDE exists for the starting address, check the VAD
|
||
// to see if the pages are not committed.
|
||
//
|
||
|
||
PointerPxe += 1;
|
||
PointerPpe = MiGetVirtualAddressMappedByPte (PointerPxe);
|
||
PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
|
||
PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
|
||
|
||
//
|
||
// Check next page.
|
||
//
|
||
|
||
goto restart;
|
||
}
|
||
|
||
#if (_MI_PAGING_LEVELS >= 4)
|
||
Waited = 0;
|
||
#endif
|
||
|
||
if (!MiDoesPpeExistAndMakeValid (PointerPpe,
|
||
Process,
|
||
FALSE,
|
||
&Waited)) {
|
||
|
||
//
|
||
// No PDE exists for the starting address, check the VAD
|
||
// to see if the pages are not committed.
|
||
//
|
||
|
||
PointerPpe += 1;
|
||
PointerPxe = MiGetPteAddress (PointerPpe);
|
||
PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
|
||
PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
|
||
|
||
//
|
||
// Check next page.
|
||
//
|
||
|
||
goto restart;
|
||
}
|
||
|
||
#if (_MI_PAGING_LEVELS < 4)
|
||
Waited = 0;
|
||
#endif
|
||
|
||
if (!MiDoesPdeExistAndMakeValid (PointerPde,
|
||
Process,
|
||
FALSE,
|
||
&Waited)) {
|
||
|
||
//
|
||
// No PDE exists for the starting address, check the VAD
|
||
// to see if the pages are not committed.
|
||
//
|
||
|
||
PointerPde += 1;
|
||
PointerPpe = MiGetPteAddress (PointerPde);
|
||
PointerPxe = MiGetPteAddress (PointerPpe);
|
||
PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
|
||
|
||
//
|
||
// Check next page.
|
||
//
|
||
|
||
goto restart;
|
||
}
|
||
} while (Waited != 0);
|
||
}
|
||
|
||
//
|
||
// The PDE exists, examine the PTE.
|
||
//
|
||
|
||
if (PointerPte->u.Long != 0) {
|
||
|
||
//
|
||
// Has this page been explicitly decommitted?
|
||
//
|
||
|
||
if (MiIsPteDecommittedPage (PointerPte)) {
|
||
|
||
//
|
||
// This page is decommitted, remove it from the count.
|
||
//
|
||
|
||
NumberOfCommittedPages -= 1;
|
||
|
||
}
|
||
}
|
||
|
||
PointerPte += 1;
|
||
}
|
||
|
||
return NumberOfCommittedPages;
|
||
}
|
||
|
||
//
|
||
// Examine non committed range.
|
||
//
|
||
|
||
NumberOfCommittedPages = 0;
|
||
|
||
do {
|
||
|
||
#if (_MI_PAGING_LEVELS >= 4)
|
||
retry2:
|
||
#endif
|
||
while (!MiDoesPxeExistAndMakeValid (PointerPxe,
|
||
Process,
|
||
FALSE,
|
||
&Waited)) {
|
||
|
||
|
||
//
|
||
// No PXE exists for the starting address, therefore the page
|
||
// is not committed.
|
||
//
|
||
|
||
PointerPxe += 1;
|
||
PointerPpe = MiGetVirtualAddressMappedByPte (PointerPxe);
|
||
PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
|
||
PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
|
||
if (PointerPte > LastPte) {
|
||
return NumberOfCommittedPages;
|
||
}
|
||
}
|
||
|
||
#if (_MI_PAGING_LEVELS >= 4)
|
||
Waited = 0;
|
||
#endif
|
||
|
||
while (!MiDoesPpeExistAndMakeValid (PointerPpe,
|
||
Process,
|
||
FALSE,
|
||
&Waited)) {
|
||
|
||
|
||
//
|
||
// No PPE exists for the starting address, therefore the page
|
||
// is not committed.
|
||
//
|
||
|
||
PointerPpe += 1;
|
||
PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
|
||
PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
|
||
if (PointerPte > LastPte) {
|
||
return NumberOfCommittedPages;
|
||
}
|
||
#if (_MI_PAGING_LEVELS >= 4)
|
||
if (MiIsPteOnPdeBoundary (PointerPpe)) {
|
||
PointerPxe = MiGetPteAddress (PointerPpe);
|
||
goto retry2;
|
||
}
|
||
#endif
|
||
}
|
||
|
||
#if (_MI_PAGING_LEVELS < 4)
|
||
Waited = 0;
|
||
#endif
|
||
|
||
while (!MiDoesPdeExistAndMakeValid (PointerPde,
|
||
Process,
|
||
FALSE,
|
||
&Waited)) {
|
||
|
||
//
|
||
// No PDE exists for the starting address, therefore the page
|
||
// is not committed.
|
||
//
|
||
|
||
PointerPde += 1;
|
||
PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
|
||
if (PointerPte > LastPte) {
|
||
return NumberOfCommittedPages;
|
||
}
|
||
#if (_MI_PAGING_LEVELS >= 3)
|
||
if (MiIsPteOnPdeBoundary (PointerPde)) {
|
||
PointerPpe = MiGetPteAddress (PointerPde);
|
||
PointerPxe = MiGetPdeAddress (PointerPde);
|
||
Waited = 1;
|
||
break;
|
||
}
|
||
#endif
|
||
}
|
||
|
||
} while (Waited != 0);
|
||
|
||
restart2:
|
||
|
||
while (PointerPte <= LastPte) {
|
||
|
||
if (MiIsPteOnPdeBoundary (PointerPte)) {
|
||
|
||
//
|
||
// This is a PDE boundary, check to see if the entire
|
||
// PXE/PPE/PDE pages exist.
|
||
//
|
||
|
||
PointerPde = MiGetPteAddress (PointerPte);
|
||
PointerPpe = MiGetPteAddress (PointerPde);
|
||
PointerPxe = MiGetPdeAddress (PointerPde);
|
||
|
||
do {
|
||
|
||
if (!MiDoesPxeExistAndMakeValid (PointerPxe,
|
||
Process,
|
||
FALSE,
|
||
&Waited)) {
|
||
|
||
//
|
||
// No PXE exists for the starting address, check the VAD
|
||
// to see if the pages are not committed.
|
||
//
|
||
|
||
PointerPxe += 1;
|
||
PointerPpe = MiGetVirtualAddressMappedByPte (PointerPxe);
|
||
PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
|
||
PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
|
||
|
||
//
|
||
// Check next page.
|
||
//
|
||
|
||
goto restart2;
|
||
}
|
||
|
||
#if (_MI_PAGING_LEVELS >= 4)
|
||
Waited = 0;
|
||
#endif
|
||
|
||
if (!MiDoesPpeExistAndMakeValid (PointerPpe,
|
||
Process,
|
||
FALSE,
|
||
&Waited)) {
|
||
|
||
//
|
||
// No PPE exists for the starting address, check the VAD
|
||
// to see if the pages are not committed.
|
||
//
|
||
|
||
PointerPpe += 1;
|
||
PointerPxe = MiGetPteAddress (PointerPpe);
|
||
PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
|
||
PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
|
||
|
||
//
|
||
// Check next page.
|
||
//
|
||
|
||
goto restart2;
|
||
}
|
||
|
||
#if (_MI_PAGING_LEVELS < 4)
|
||
Waited = 0;
|
||
#endif
|
||
|
||
if (!MiDoesPdeExistAndMakeValid (PointerPde,
|
||
Process,
|
||
FALSE,
|
||
&Waited)) {
|
||
|
||
//
|
||
// No PDE exists for the starting address, check the VAD
|
||
// to see if the pages are not committed.
|
||
//
|
||
|
||
PointerPde += 1;
|
||
PointerPpe = MiGetPteAddress (PointerPde);
|
||
PointerPxe = MiGetPteAddress (PointerPpe);
|
||
PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
|
||
|
||
//
|
||
// Check next page.
|
||
//
|
||
|
||
goto restart2;
|
||
}
|
||
|
||
} while (Waited != 0);
|
||
}
|
||
|
||
//
|
||
// The PDE exists, examine the PTE.
|
||
//
|
||
|
||
if ((PointerPte->u.Long != 0) &&
|
||
(!MiIsPteDecommittedPage (PointerPte))) {
|
||
|
||
//
|
||
// This page is committed, count it.
|
||
//
|
||
|
||
NumberOfCommittedPages += 1;
|
||
}
|
||
|
||
PointerPte += 1;
|
||
}
|
||
|
||
return NumberOfCommittedPages;
|
||
}
|
||
|
||
VOID
|
||
MiReturnPageTablePageCommitment (
|
||
IN PVOID StartingAddress,
|
||
IN PVOID EndingAddress,
|
||
IN PEPROCESS CurrentProcess,
|
||
IN PMMVAD PreviousVad,
|
||
IN PMMVAD NextVad
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine returns commitment for COMPLETE page table pages which
|
||
span the virtual address range. For example (assuming 4k pages),
|
||
if the StartingAddress = 64k and the EndingAddress = 5mb, no
|
||
page table charges would be freed as a complete page table page is
|
||
not covered by the range. However, if the StartingAddress was 4mb
|
||
and the EndingAddress was 9mb, 1 page table page would be freed.
|
||
|
||
Arguments:
|
||
|
||
StartingAddress - Supplies the starting address of the range.
|
||
|
||
EndingAddress - Supplies the ending address of the range.
|
||
|
||
CurrentProcess - Supplies a pointer to the current process.
|
||
|
||
PreviousVad - Supplies a pointer to the previous VAD, NULL if none.
|
||
|
||
NextVad - Supplies a pointer to the next VAD, NULL if none.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Environment:
|
||
|
||
Kernel mode, APCs disabled, WorkingSetLock and AddressCreation mutexes
|
||
held.
|
||
|
||
--*/
|
||
|
||
{
|
||
RTL_BITMAP VadBitMap;
|
||
ULONG NumberToClear;
|
||
ULONG StartBit;
|
||
ULONG EndBit;
|
||
LONG FirstPage;
|
||
LONG LastPage;
|
||
LONG PreviousPage;
|
||
LONG NextPage;
|
||
#if (_MI_PAGING_LEVELS >= 3)
|
||
LONG FirstPdPage;
|
||
LONG LastPdPage;
|
||
LONG PreviousPdPage;
|
||
LONG NextPdPage;
|
||
#endif
|
||
#if (_MI_PAGING_LEVELS >= 4)
|
||
LONG FirstPpPage;
|
||
LONG LastPpPage;
|
||
LONG PreviousPpPage;
|
||
LONG NextPpPage;
|
||
#endif
|
||
|
||
//
|
||
// Check to see if any page table pages would be freed.
|
||
//
|
||
|
||
ASSERT (StartingAddress != EndingAddress);
|
||
|
||
StartBit = (ULONG) (((ULONG_PTR) MI_64K_ALIGN (StartingAddress)) / X64K);
|
||
EndBit = (ULONG) (((ULONG_PTR) MI_64K_ALIGN (EndingAddress)) / X64K);
|
||
|
||
if (PreviousVad == NULL) {
|
||
PreviousPage = -1;
|
||
#if (_MI_PAGING_LEVELS >= 3)
|
||
PreviousPdPage = -1;
|
||
#endif
|
||
#if (_MI_PAGING_LEVELS >= 4)
|
||
PreviousPpPage = -1;
|
||
#endif
|
||
}
|
||
else {
|
||
PreviousPage = MiGetPdeIndex (MI_VPN_TO_VA (PreviousVad->EndingVpn));
|
||
#if (_MI_PAGING_LEVELS >= 3)
|
||
PreviousPdPage = MiGetPpeIndex (MI_VPN_TO_VA (PreviousVad->EndingVpn));
|
||
#endif
|
||
#if (_MI_PAGING_LEVELS >= 4)
|
||
PreviousPpPage = MiGetPxeIndex (MI_VPN_TO_VA (PreviousVad->EndingVpn));
|
||
#endif
|
||
if (MI_64K_ALIGN (MI_VPN_TO_VA (PreviousVad->EndingVpn)) ==
|
||
MI_64K_ALIGN (StartingAddress)) {
|
||
StartBit += 1;
|
||
}
|
||
}
|
||
|
||
if (NextVad == NULL) {
|
||
NextPage = MiGetPdeIndex (MM_HIGHEST_USER_ADDRESS) + 1;
|
||
#if (_MI_PAGING_LEVELS >= 3)
|
||
NextPdPage = MiGetPpeIndex (MM_HIGHEST_USER_ADDRESS) + 1;
|
||
#endif
|
||
#if (_MI_PAGING_LEVELS >= 4)
|
||
NextPpPage = MiGetPxeIndex (MM_HIGHEST_USER_ADDRESS) + 1;
|
||
#endif
|
||
}
|
||
else {
|
||
NextPage = MiGetPdeIndex (MI_VPN_TO_VA (NextVad->StartingVpn));
|
||
#if (_MI_PAGING_LEVELS >= 3)
|
||
NextPdPage = MiGetPpeIndex (MI_VPN_TO_VA (NextVad->StartingVpn));
|
||
#endif
|
||
#if (_MI_PAGING_LEVELS >= 4)
|
||
NextPpPage = MiGetPxeIndex (MI_VPN_TO_VA (NextVad->StartingVpn));
|
||
#endif
|
||
if (MI_64K_ALIGN (MI_VPN_TO_VA (NextVad->StartingVpn)) ==
|
||
MI_64K_ALIGN (EndingAddress)) {
|
||
EndBit -= 1;
|
||
}
|
||
}
|
||
|
||
ASSERT (PreviousPage <= NextPage);
|
||
ASSERT64 (PreviousPdPage <= NextPdPage);
|
||
#if (_MI_PAGING_LEVELS >= 4)
|
||
ASSERT64 (PreviousPpPage <= NextPpPage);
|
||
#endif
|
||
|
||
FirstPage = MiGetPdeIndex (StartingAddress);
|
||
|
||
LastPage = MiGetPdeIndex (EndingAddress);
|
||
|
||
if (PreviousPage == FirstPage) {
|
||
|
||
//
|
||
// A VAD is within the starting page table page.
|
||
//
|
||
|
||
FirstPage += 1;
|
||
}
|
||
|
||
if (NextPage == LastPage) {
|
||
|
||
//
|
||
// A VAD is within the ending page table page.
|
||
//
|
||
|
||
LastPage -= 1;
|
||
}
|
||
|
||
if (StartBit <= EndBit) {
|
||
|
||
//
|
||
// Initialize the bitmap inline for speed.
|
||
//
|
||
|
||
VadBitMap.SizeOfBitMap = MiLastVadBit + 1;
|
||
VadBitMap.Buffer = VAD_BITMAP_SPACE;
|
||
|
||
#if defined (_WIN64) || defined (_X86PAE_)
|
||
|
||
//
|
||
// Only the first (PAGE_SIZE*8*64K) of VA space on NT64 is bitmapped.
|
||
//
|
||
|
||
if (EndBit > MiLastVadBit) {
|
||
EndBit = MiLastVadBit;
|
||
}
|
||
|
||
if (StartBit <= MiLastVadBit) {
|
||
RtlClearBits (&VadBitMap, StartBit, EndBit - StartBit + 1);
|
||
|
||
if (MmWorkingSetList->VadBitMapHint > StartBit) {
|
||
MmWorkingSetList->VadBitMapHint = StartBit;
|
||
}
|
||
}
|
||
#else
|
||
RtlClearBits (&VadBitMap, StartBit, EndBit - StartBit + 1);
|
||
|
||
if (MmWorkingSetList->VadBitMapHint > StartBit) {
|
||
MmWorkingSetList->VadBitMapHint = StartBit;
|
||
}
|
||
#endif
|
||
}
|
||
|
||
//
|
||
// Indicate that the page table page is not in use.
|
||
//
|
||
|
||
if (FirstPage > LastPage) {
|
||
return;
|
||
}
|
||
|
||
NumberToClear = 1 + LastPage - FirstPage;
|
||
|
||
while (FirstPage <= LastPage) {
|
||
ASSERT (MI_CHECK_BIT (MmWorkingSetList->CommittedPageTables,
|
||
FirstPage));
|
||
|
||
MI_CLEAR_BIT (MmWorkingSetList->CommittedPageTables, FirstPage);
|
||
FirstPage += 1;
|
||
}
|
||
|
||
MmWorkingSetList->NumberOfCommittedPageTables -= NumberToClear;
|
||
|
||
#if (_MI_PAGING_LEVELS >= 4)
|
||
|
||
//
|
||
// Return page directory parent charges here.
|
||
//
|
||
|
||
FirstPpPage = MiGetPxeIndex (StartingAddress);
|
||
|
||
LastPpPage = MiGetPxeIndex (EndingAddress);
|
||
|
||
if (PreviousPpPage == FirstPpPage) {
|
||
|
||
//
|
||
// A VAD is within the starting page directory parent page.
|
||
//
|
||
|
||
FirstPpPage += 1;
|
||
}
|
||
|
||
if (NextPpPage == LastPpPage) {
|
||
|
||
//
|
||
// A VAD is within the ending page directory parent page.
|
||
//
|
||
|
||
LastPpPage -= 1;
|
||
}
|
||
|
||
//
|
||
// Indicate that the page directory page parent is not in use.
|
||
//
|
||
|
||
if (FirstPpPage <= LastPpPage) {
|
||
|
||
MmWorkingSetList->NumberOfCommittedPageDirectoryParents -= (1 + LastPpPage - FirstPpPage);
|
||
|
||
NumberToClear += (1 + LastPpPage - FirstPpPage);
|
||
|
||
while (FirstPpPage <= LastPpPage) {
|
||
ASSERT (MI_CHECK_BIT (MmWorkingSetList->CommittedPageDirectoryParents,
|
||
FirstPpPage));
|
||
|
||
MI_CLEAR_BIT (MmWorkingSetList->CommittedPageDirectoryParents, FirstPpPage);
|
||
FirstPpPage += 1;
|
||
}
|
||
}
|
||
|
||
#endif
|
||
|
||
#if (_MI_PAGING_LEVELS >= 3)
|
||
|
||
//
|
||
// Return page directory charges here.
|
||
//
|
||
|
||
FirstPdPage = MiGetPpeIndex (StartingAddress);
|
||
|
||
LastPdPage = MiGetPpeIndex (EndingAddress);
|
||
|
||
if (PreviousPdPage == FirstPdPage) {
|
||
|
||
//
|
||
// A VAD is within the starting page directory page.
|
||
//
|
||
|
||
FirstPdPage += 1;
|
||
}
|
||
|
||
if (NextPdPage == LastPdPage) {
|
||
|
||
//
|
||
// A VAD is within the ending page directory page.
|
||
//
|
||
|
||
LastPdPage -= 1;
|
||
}
|
||
|
||
//
|
||
// Indicate that the page directory page is not in use.
|
||
//
|
||
|
||
if (FirstPdPage <= LastPdPage) {
|
||
|
||
MmWorkingSetList->NumberOfCommittedPageDirectories -= (1 + LastPdPage - FirstPdPage);
|
||
|
||
NumberToClear += (1 + LastPdPage - FirstPdPage);
|
||
|
||
while (FirstPdPage <= LastPdPage) {
|
||
ASSERT (MI_CHECK_BIT (MmWorkingSetList->CommittedPageDirectories,
|
||
FirstPdPage));
|
||
|
||
MI_CLEAR_BIT (MmWorkingSetList->CommittedPageDirectories, FirstPdPage);
|
||
FirstPdPage += 1;
|
||
}
|
||
}
|
||
|
||
#endif
|
||
|
||
MiReturnCommitment (NumberToClear);
|
||
MM_TRACK_COMMIT (MM_DBG_COMMIT_RETURN_PAGETABLES, NumberToClear);
|
||
PsReturnProcessPageFileQuota (CurrentProcess, NumberToClear);
|
||
|
||
if (CurrentProcess->JobStatus & PS_JOB_STATUS_REPORT_COMMIT_CHANGES) {
|
||
PsChangeJobMemoryUsage(-(SSIZE_T)NumberToClear);
|
||
}
|
||
CurrentProcess->CommitCharge -= NumberToClear;
|
||
|
||
MI_INCREMENT_TOTAL_PROCESS_COMMIT (0 - NumberToClear);
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
MiCauseOverCommitPopup (
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function causes an over commit popup to occur (if the popup has never
|
||
been sent before).
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
LONG PopupNumber;
|
||
|
||
//
|
||
// Give the user a meaningful message - either to increase the minimum,
|
||
// maximum, or both.
|
||
//
|
||
|
||
if (MmTotalCommittedPages > MmTotalCommitLimitMaximum - 100) {
|
||
if (InterlockedIncrement (&MiCommitPopups[0]) > 1) {
|
||
InterlockedDecrement (&MiCommitPopups[0]);
|
||
return;
|
||
}
|
||
PopupNumber = STATUS_COMMITMENT_LIMIT;
|
||
}
|
||
else {
|
||
if (InterlockedIncrement (&MiCommitPopups[1]) > 1) {
|
||
InterlockedDecrement (&MiCommitPopups[1]);
|
||
return;
|
||
}
|
||
PopupNumber = STATUS_COMMITMENT_MINIMUM;
|
||
}
|
||
|
||
IoRaiseInformationalHardError (PopupNumber, NULL, NULL);
|
||
}
|
||
|
||
|
||
SIZE_T MmTotalPagedPoolQuota;
|
||
SIZE_T MmTotalNonPagedPoolQuota;
|
||
|
||
BOOLEAN
|
||
MmRaisePoolQuota(
|
||
IN POOL_TYPE PoolType,
|
||
IN SIZE_T OldQuotaLimit,
|
||
OUT PSIZE_T NewQuotaLimit
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is called (with a spinlock) whenever PS detects a quota
|
||
limit has been exceeded. The purpose of this function is to attempt to
|
||
increase the specified quota.
|
||
|
||
Arguments:
|
||
|
||
PoolType - Supplies the pool type of the quota to be raised
|
||
|
||
OldQuotaLimit - Supplies the current quota limit for this pool type
|
||
|
||
NewQuotaLimit - Returns the new limit
|
||
|
||
Return Value:
|
||
|
||
TRUE - The API succeeded and the quota limit was raised.
|
||
|
||
FALSE - We were unable to raise the quota limit.
|
||
|
||
Environment:
|
||
|
||
Kernel mode, QUOTA SPIN LOCK HELD!!
|
||
|
||
--*/
|
||
|
||
{
|
||
SIZE_T Limit;
|
||
PMM_PAGED_POOL_INFO PagedPoolInfo;
|
||
|
||
if (PoolType == PagedPool) {
|
||
|
||
//
|
||
// Check commit limit and make sure at least 1mb is available.
|
||
// Check to make sure 4mb of paged pool still exists.
|
||
//
|
||
|
||
PagedPoolInfo = &MmPagedPoolInfo;
|
||
|
||
if ((MmSizeOfPagedPoolInBytes >> PAGE_SHIFT) <
|
||
(PagedPoolInfo->AllocatedPagedPool + ((MMPAGED_QUOTA_CHECK) >> PAGE_SHIFT))) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
MmTotalPagedPoolQuota += (MMPAGED_QUOTA_INCREASE);
|
||
*NewQuotaLimit = OldQuotaLimit + (MMPAGED_QUOTA_INCREASE);
|
||
return TRUE;
|
||
|
||
} else {
|
||
|
||
if ( (ULONG_PTR)(MmAllocatedNonPagedPool + ((1*1024*1024) >> PAGE_SHIFT)) < (MmMaximumNonPagedPoolInBytes >> PAGE_SHIFT)) {
|
||
goto aok;
|
||
}
|
||
|
||
//
|
||
// Make sure 200 pages and 5mb of nonpaged pool expansion
|
||
// available. Raise quota by 64k.
|
||
//
|
||
|
||
if ((MmAvailablePages < 200) ||
|
||
(MmResidentAvailablePages < ((MMNONPAGED_QUOTA_CHECK) >> PAGE_SHIFT))) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
if (MmAvailablePages > ((4*1024*1024) >> PAGE_SHIFT)) {
|
||
Limit = (1*1024*1024) >> PAGE_SHIFT;
|
||
} else {
|
||
Limit = (4*1024*1024) >> PAGE_SHIFT;
|
||
}
|
||
|
||
if ((ULONG_PTR)((MmMaximumNonPagedPoolInBytes >> PAGE_SHIFT)) <
|
||
(MmAllocatedNonPagedPool + Limit)) {
|
||
|
||
return FALSE;
|
||
}
|
||
aok:
|
||
MmTotalNonPagedPoolQuota += (MMNONPAGED_QUOTA_INCREASE);
|
||
*NewQuotaLimit = OldQuotaLimit + (MMNONPAGED_QUOTA_INCREASE);
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
MmReturnPoolQuota(
|
||
IN POOL_TYPE PoolType,
|
||
IN SIZE_T ReturnedQuota
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Returns pool quota.
|
||
|
||
Arguments:
|
||
|
||
PoolType - Supplies the pool type of the quota to be returned.
|
||
|
||
ReturnedQuota - Number of bytes returned.
|
||
|
||
Return Value:
|
||
|
||
NONE.
|
||
|
||
Environment:
|
||
|
||
Kernel mode, QUOTA SPIN LOCK HELD!!
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
if (PoolType == PagedPool) {
|
||
MmTotalPagedPoolQuota -= ReturnedQuota;
|
||
} else {
|
||
MmTotalNonPagedPoolQuota -= ReturnedQuota;
|
||
}
|
||
|
||
return;
|
||
}
|