windows-nt/Source/XPSP1/NT/base/ntos/mm/mmquota.c

1431 lines
37 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
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;
}