Windows2000/private/ntos/mm/vadtree.c
2020-09-30 17:12:32 +02:00

527 lines
14 KiB
C

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
vadtree.c
Abstract:
This module contains the routine to manipulate the virtual address
descriptor tree.
Author:
Lou Perazzoli (loup) 19-May-1989
Landy Wang (landyw) 02-June-1997
Environment:
Kernel mode only, working set mutex held, APCs disabled.
Revision History:
--*/
#include "mi.h"
VOID
MiInsertVad (
IN PMMVAD Vad
)
/*++
Routine Description:
This function inserts a virtual address descriptor into the tree and
reorders the splay tree as appropriate.
Arguments:
Vad - Supplies a pointer to a virtual address descriptor
Return Value:
None - An exception is raised if quota is exceeded.
--*/
{
PMMADDRESS_NODE *Root;
PEPROCESS CurrentProcess;
SIZE_T RealCharge;
SIZE_T PageCharge;
SIZE_T PagedQuotaCharged;
ULONG FirstPage;
ULONG LastPage;
SIZE_T PagedPoolCharge;
LOGICAL ChargedPageFileQuota;
LOGICAL ChargedJobCommit;
ASSERT (Vad->EndingVpn >= Vad->StartingVpn);
CurrentProcess = PsGetCurrentProcess();
// Commit charge of MAX_COMMIT means don't charge quota.
if (Vad->u.VadFlags.CommitCharge != MM_MAX_COMMIT) {
PageCharge = 0;
PagedQuotaCharged = 0;
ChargedPageFileQuota = FALSE;
ChargedJobCommit = FALSE;
// Charge quota for the nonpaged pool for the VAD. This is
// done here rather than by using ExAllocatePoolWithQuota
// so the process object is not referenced by the quota charge.
PsChargePoolQuota (CurrentProcess, NonPagedPool, sizeof(MMVAD));
try {
// Charge quota for the prototype PTEs if this is a mapped view.
if ((Vad->u.VadFlags.PrivateMemory == 0) &&
(Vad->ControlArea != NULL)) {
PagedPoolCharge =
(Vad->EndingVpn - Vad->StartingVpn) << PTE_SHIFT;
PsChargePoolQuota (CurrentProcess, PagedPool, PagedPoolCharge);
PagedQuotaCharged = PagedPoolCharge;
}
#if !defined (_WIN64)
// Add in the charge for page table pages.
FirstPage = MiGetPpePdeOffset (MI_VPN_TO_VA (Vad->StartingVpn));
LastPage = MiGetPpePdeOffset (MI_VPN_TO_VA (Vad->EndingVpn));
while (FirstPage <= LastPage) {
if (!MI_CHECK_BIT (MmWorkingSetList->CommittedPageTables,
FirstPage)) {
PageCharge += 1;
}
FirstPage += 1;
}
#endif
RealCharge = Vad->u.VadFlags.CommitCharge + PageCharge;
if (RealCharge != 0) {
MiChargePageFileQuota (RealCharge, CurrentProcess);
ChargedPageFileQuota = TRUE;
#if 0 //commented out so page file quota is meaningful.
if (Vad->u.VadFlags.PrivateMemory == 0) {
if ((Vad->ControlArea->FilePointer == NULL) &&
(Vad->u.VadFlags.PhysicalMapping == 0)) {
// Don't charge commitment for the page file space
// occupied by a page file section. This will be
// charged as the shared memory is committed.
RealCharge -= 1 + (Vad->EndingVpn -
Vad->StartingVpn);
}
}
#endif //0
if (CurrentProcess->CommitChargeLimit) {
if (CurrentProcess->CommitCharge + RealCharge > CurrentProcess->CommitChargeLimit) {
if (CurrentProcess->Job) {
PsReportProcessMemoryLimitViolation ();
}
ExRaiseStatus (STATUS_COMMITMENT_LIMIT);
}
}
if (CurrentProcess->JobStatus & PS_JOB_STATUS_REPORT_COMMIT_CHANGES) {
if (PsChangeJobMemoryUsage(RealCharge) == FALSE) {
ExRaiseStatus (STATUS_COMMITMENT_LIMIT);
}
ChargedJobCommit = TRUE;
}
if (MiChargeCommitment (RealCharge, CurrentProcess) == FALSE) {
ExRaiseStatus (STATUS_COMMITMENT_LIMIT);
}
CurrentProcess->CommitCharge += RealCharge;
if (CurrentProcess->CommitCharge > CurrentProcess->CommitChargePeak) {
CurrentProcess->CommitChargePeak = CurrentProcess->CommitCharge;
}
}
} except (EXCEPTION_EXECUTE_HANDLER) {
// Return any quotas charged thus far.
PsReturnPoolQuota (CurrentProcess, NonPagedPool, sizeof(MMVAD));
if (PagedQuotaCharged != 0) {
PsReturnPoolQuota (CurrentProcess, PagedPool, PagedPoolCharge);
}
if (ChargedPageFileQuota == TRUE) {
MiReturnPageFileQuota (RealCharge,
CurrentProcess);
}
if (ChargedJobCommit == TRUE) {
// Temporarily up the process commit charge as the
// job code will be referencing it as though everything
// has succeeded.
CurrentProcess->CommitCharge += RealCharge;
PsChangeJobMemoryUsage(-(SSIZE_T)RealCharge);
CurrentProcess->CommitCharge -= RealCharge;
}
ExRaiseStatus (GetExceptionCode());
}
MM_TRACK_COMMIT (MM_DBG_COMMIT_INSERT_VAD, RealCharge);
#if !defined (_WIN64)
if (PageCharge != 0) {
// Since the commitment was successful, charge the page
// table pages.
FirstPage = MiGetPpePdeOffset (MI_VPN_TO_VA (Vad->StartingVpn));
while (FirstPage <= LastPage) {
if (!MI_CHECK_BIT (MmWorkingSetList->CommittedPageTables,
FirstPage)) {
MI_SET_BIT (MmWorkingSetList->CommittedPageTables,
FirstPage);
MmWorkingSetList->NumberOfCommittedPageTables += 1;
#if defined (_X86PAE_)
ASSERT (MmWorkingSetList->NumberOfCommittedPageTables <
PD_PER_SYSTEM * PDE_PER_PAGE);
#else
ASSERT (MmWorkingSetList->NumberOfCommittedPageTables <
PDE_PER_PAGE);
#endif
}
FirstPage += 1;
}
}
#endif
}
Root = (PMMADDRESS_NODE *)&CurrentProcess->VadRoot;
// Set the hint field in the process to this Vad.
CurrentProcess->VadHint = Vad;
if (CurrentProcess->VadFreeHint != NULL) {
if (((ULONG)((PMMVAD)CurrentProcess->VadFreeHint)->EndingVpn +
MI_VA_TO_VPN (X64K)) >=
Vad->StartingVpn) {
CurrentProcess->VadFreeHint = Vad;
}
}
MiInsertNode ( (PMMADDRESS_NODE)Vad, Root);
return;
}
VOID
MiRemoveVad (
IN PMMVAD Vad
)
/*++
Routine Description:
This function removes a virtual address descriptor from the tree and
reorders the splay tree as appropriate. If any quota or commitment
was charged by the VAD (as indicated by the CommitCharge field) it
is released.
Arguments:
Vad - Supplies a pointer to a virtual address descriptor.
Return Value:
None.
--*/
{
PMMADDRESS_NODE *Root;
PEPROCESS CurrentProcess;
SIZE_T RealCharge;
PLIST_ENTRY Next;
PMMSECURE_ENTRY Entry;
CurrentProcess = PsGetCurrentProcess();
// Commit charge of MAX_COMMIT means don't charge quota.
if (Vad->u.VadFlags.CommitCharge != MM_MAX_COMMIT) {
// Return the quota charge to the process.
PsReturnPoolQuota (CurrentProcess, NonPagedPool, sizeof(MMVAD));
if ((Vad->u.VadFlags.PrivateMemory == 0) &&
(Vad->ControlArea != NULL)) {
PsReturnPoolQuota (CurrentProcess,
PagedPool,
(Vad->EndingVpn - Vad->StartingVpn) << PTE_SHIFT);
}
RealCharge = Vad->u.VadFlags.CommitCharge;
if (RealCharge != 0) {
MiReturnPageFileQuota (RealCharge, CurrentProcess);
if ((Vad->u.VadFlags.PrivateMemory == 0) &&
(Vad->ControlArea != NULL)) {
#if 0 //commented out so page file quota is meaningful.
if (Vad->ControlArea->FilePointer == NULL) {
// Don't release commitment for the page file space
// occupied by a page file section. This will be charged
// as the shared memory is committed.
RealCharge -= BYTES_TO_PAGES ((ULONG)Vad->EndingVa -
(ULONG)Vad->StartingVa);
}
#endif
}
MiReturnCommitment (RealCharge);
MM_TRACK_COMMIT (MM_DBG_COMMIT_RETURN_VAD, RealCharge);
if (CurrentProcess->JobStatus & PS_JOB_STATUS_REPORT_COMMIT_CHANGES) {
PsChangeJobMemoryUsage(-(SSIZE_T)RealCharge);
}
CurrentProcess->CommitCharge -= RealCharge;
}
}
if (Vad == CurrentProcess->VadFreeHint) {
CurrentProcess->VadFreeHint = MiGetPreviousVad (Vad);
}
Root = (PMMADDRESS_NODE *)&CurrentProcess->VadRoot;
MiRemoveNode ( (PMMADDRESS_NODE)Vad, Root);
if (Vad->u.VadFlags.NoChange) {
if (Vad->u2.VadFlags2.MultipleSecured) {
// Free the oustanding pool allocations.
Next = Vad->u3.List.Flink;
do {
Entry = CONTAINING_RECORD( Next,
MMSECURE_ENTRY,
List);
Next = Entry->List.Flink;
ExFreePool (Entry);
} while (Next != &Vad->u3.List);
}
}
// If the VadHint was the removed Vad, change the Hint.
if (CurrentProcess->VadHint == Vad) {
CurrentProcess->VadHint = CurrentProcess->VadRoot;
}
return;
}
PMMVAD
FASTCALL
MiLocateAddress (
IN PVOID VirtualAddress
)
/*++
Routine Description:
The function locates the virtual address descriptor which describes
a given address.
Arguments:
VirtualAddress - Supplies the virtual address to locate a descriptor
for.
Return Value:
Returns a pointer to the virtual address descriptor which contains
the supplied virtual address or NULL if none was located.
--*/
{
PMMVAD FoundVad;
PEPROCESS CurrentProcess;
ULONG_PTR Vpn;
CurrentProcess = PsGetCurrentProcess();
if (CurrentProcess->VadHint == NULL) {
return NULL;
}
Vpn = MI_VA_TO_VPN (VirtualAddress);
if ((Vpn >= ((PMMADDRESS_NODE)CurrentProcess->VadHint)->StartingVpn) &&
(Vpn <= ((PMMADDRESS_NODE)CurrentProcess->VadHint)->EndingVpn)) {
return (PMMVAD)CurrentProcess->VadHint;
}
FoundVad = (PMMVAD)MiLocateAddressInTree ( Vpn,
(PMMADDRESS_NODE *)&(CurrentProcess->VadRoot));
if (FoundVad != NULL) {
CurrentProcess->VadHint = (PVOID)FoundVad;
}
return FoundVad;
}
PVOID
MiFindEmptyAddressRange (
IN SIZE_T SizeOfRange,
IN ULONG_PTR Alignment,
IN ULONG QuickCheck
)
/*++
Routine Description:
The function examines the virtual address descriptors to locate
an unused range of the specified size and returns the starting
address of the range.
Arguments:
SizeOfRange - Supplies the size in bytes of the range to locate.
Alignment - Supplies the alignment for the address. Must be
a power of 2 and greater than the page_size.
QuickCheck - Supplies a zero if a quick check for free memory
after the VadFreeHint exists, non-zero if checking
should start at the lowest address.
Return Value:
Returns the starting address of a suitable range.
--*/
{
PMMVAD NextVad;
PMMVAD FreeHint;
PEPROCESS CurrentProcess;
PVOID StartingVa;
PVOID EndingVa;
CurrentProcess = PsGetCurrentProcess();
FreeHint = CurrentProcess->VadFreeHint;
if ((QuickCheck == 0) && (FreeHint != NULL)) {
EndingVa = MI_VPN_TO_VA_ENDING (FreeHint->EndingVpn);
NextVad = MiGetNextVad (FreeHint);
if (NextVad == NULL) {
if (SizeOfRange <
(((ULONG_PTR)MM_HIGHEST_USER_ADDRESS + 1) -
MI_ROUND_TO_SIZE((ULONG_PTR)EndingVa, Alignment))) {
return (PMMADDRESS_NODE)MI_ROUND_TO_SIZE((ULONG_PTR)EndingVa,
Alignment);
}
} else {
StartingVa = MI_VPN_TO_VA (NextVad->StartingVpn);
if (SizeOfRange <
((ULONG_PTR)StartingVa -
MI_ROUND_TO_SIZE((ULONG_PTR)EndingVa, Alignment))) {
// Check to ensure that the ending address aligned upwards
// is not greater than the starting address.
if ((ULONG_PTR)StartingVa >
MI_ROUND_TO_SIZE((ULONG_PTR)EndingVa,Alignment)) {
return (PMMADDRESS_NODE)MI_ROUND_TO_SIZE((ULONG_PTR)EndingVa,
Alignment);
}
}
}
}
return (PMMVAD)MiFindEmptyAddressRangeInTree (
SizeOfRange,
Alignment,
(PMMADDRESS_NODE)(CurrentProcess->VadRoot),
(PMMADDRESS_NODE *)&CurrentProcess->VadFreeHint);
}
#if DBG
VOID
VadTreeWalk (
PMMVAD Start
)
{
UNREFERENCED_PARAMETER (Start);
NodeTreeWalk ( (PMMADDRESS_NODE)(PsGetCurrentProcess()->VadRoot));
return;
}
#endif //DBG