1662 lines
47 KiB
C
1662 lines
47 KiB
C
/*++
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
deleteva.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the routines for deleting virtual address space.
|
||
|
||
Author:
|
||
|
||
Lou Perazzoli (loup) 11-May-1989
|
||
Landy Wang (landyw) 02-June-1997
|
||
|
||
--*/
|
||
|
||
#include "mi.h"
|
||
|
||
#if defined (_WIN64_) && defined (DBG_VERBOSE)
|
||
|
||
typedef struct _MI_TRACK_USE {
|
||
|
||
PFN_NUMBER Pfn;
|
||
PVOID Va;
|
||
ULONG Id;
|
||
ULONG PfnUse;
|
||
ULONG PfnUseCounted;
|
||
ULONG TickCount;
|
||
PKTHREAD Thread;
|
||
PEPROCESS Process;
|
||
} MI_TRACK_USE, *PMI_TRACK_USE;
|
||
|
||
ULONG MiTrackUseSize = 8192;
|
||
PMI_TRACK_USE MiTrackUse;
|
||
LONG MiTrackUseIndex;
|
||
|
||
VOID
|
||
MiInitUseCounts (
|
||
VOID
|
||
)
|
||
{
|
||
MiTrackUse = ExAllocatePoolWithTag (NonPagedPool,
|
||
MiTrackUseSize * sizeof (MI_TRACK_USE),
|
||
'lqrI');
|
||
ASSERT (MiTrackUse != NULL);
|
||
}
|
||
|
||
|
||
VOID
|
||
MiCheckUseCounts (
|
||
PVOID TempHandle,
|
||
PVOID Va,
|
||
ULONG Id
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine ensures that all the counters are correct.
|
||
|
||
Arguments:
|
||
|
||
TempHandle - Supplies the handle for used page table counts.
|
||
|
||
Va - Supplies the virtual address.
|
||
|
||
Id - Supplies the ID.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Environment:
|
||
|
||
Kernel mode, called with APCs disabled, working set mutex and PFN lock
|
||
held.
|
||
|
||
--*/
|
||
|
||
{
|
||
LOGICAL LogIt;
|
||
ULONG i;
|
||
ULONG TempHandleCount;
|
||
ULONG TempCounted;
|
||
PMMPTE TempPage;
|
||
KIRQL OldIrql;
|
||
ULONG Index;
|
||
PFN_NUMBER PageFrameIndex;
|
||
PMI_TRACK_USE Information;
|
||
LARGE_INTEGER TimeStamp;
|
||
PMMPFN Pfn1;
|
||
PEPROCESS Process;
|
||
|
||
Process = PsGetCurrentProcess ();
|
||
|
||
//
|
||
// TempHandle is really the PMMPFN containing the UsedPageTableEntries.
|
||
//
|
||
|
||
Pfn1 = (PMMPFN)TempHandle;
|
||
PageFrameIndex = Pfn1 - MmPfnDatabase;
|
||
|
||
TempHandleCount = MI_GET_USED_PTES_FROM_HANDLE (TempHandle);
|
||
|
||
if (Id & 0x80000000) {
|
||
ASSERT (TempHandleCount != 0);
|
||
}
|
||
|
||
TempPage = (PMMPTE) MiMapPageInHyperSpace (Process, PageFrameIndex, &OldIrql);
|
||
|
||
TempCounted = 0;
|
||
|
||
for (i = 0; i < PTE_PER_PAGE; i += 1) {
|
||
if (TempPage->u.Long != 0) {
|
||
TempCounted += 1;
|
||
}
|
||
TempPage += 1;
|
||
}
|
||
|
||
#if 0
|
||
if (zz & 0x4) {
|
||
LogIt = FALSE;
|
||
if (Pfn1->PteFrame == PageFrameIndex) {
|
||
// TopLevel parent page, not interesting to us.
|
||
}
|
||
else {
|
||
PMMPFN Pfn2;
|
||
|
||
Pfn2 = MI_PFN_ELEMENT (Pfn1->PteFrame);
|
||
if (Pfn2->PteFrame == Pfn1->PteFrame) {
|
||
// our parent is the toplevel, so very interesting.
|
||
LogIt = TRUE;
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
LogIt = TRUE;
|
||
}
|
||
#else
|
||
LogIt = TRUE;
|
||
#endif
|
||
|
||
if (LogIt == TRUE) {
|
||
|
||
//
|
||
// Capture information
|
||
//
|
||
|
||
Index = InterlockedExchangeAdd(&MiTrackUseIndex, 1);
|
||
Index &= (MiTrackUseSize - 1);
|
||
|
||
Information = &(MiTrackUse[Index]);
|
||
|
||
Information->Thread = KeGetCurrentThread();
|
||
Information->Process = (PEPROCESS)((ULONG_PTR)PsGetCurrentProcess() + KeGetCurrentProcessorNumber());
|
||
Information->Va = Va;
|
||
Information->Id = Id;
|
||
KeQueryTickCount(&TimeStamp);
|
||
Information->TickCount = TimeStamp.LowPart;
|
||
Information->Pfn = PageFrameIndex;
|
||
Information->PfnUse = TempHandleCount;
|
||
Information->PfnUseCounted = TempCounted;
|
||
|
||
if (TempCounted != TempHandleCount) {
|
||
DbgPrint ("MiCheckUseCounts %p %x %x %x %x\n", Va, Id, PageFrameIndex, TempHandleCount, TempCounted);
|
||
DbgBreakPoint ();
|
||
}
|
||
}
|
||
|
||
MiUnmapPageInHyperSpace (Process, TempPage, OldIrql);
|
||
return;
|
||
}
|
||
#endif
|
||
|
||
|
||
VOID
|
||
MiDeleteVirtualAddresses (
|
||
IN PUCHAR StartingAddress,
|
||
IN PUCHAR EndingAddress,
|
||
IN ULONG AddressSpaceDeletion,
|
||
IN PMMVAD Vad
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine deletes the specified virtual address range within
|
||
the current process.
|
||
|
||
Arguments:
|
||
|
||
StartingAddress - Supplies the first virtual address to delete.
|
||
|
||
EndingAddress - Supplies the last address to delete.
|
||
|
||
AddressSpaceDeletion - Supplies TRUE if the address space is being
|
||
deleted, FALSE otherwise. If TRUE is specified
|
||
the TB is not flushed and valid addresses are
|
||
not removed from the working set.
|
||
|
||
Vad - Supplies the virtual address descriptor which maps this range
|
||
or NULL if we are not concerned about views. From the Vad the
|
||
range of prototype PTEs is determined and this information is
|
||
used to uncover if the PTE refers to a prototype PTE or a
|
||
fork PTE.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Environment:
|
||
|
||
Kernel mode, called with APCs disabled, working set mutex and PFN lock
|
||
held. These mutexes may be released and reacquired to fault pages in.
|
||
|
||
--*/
|
||
|
||
{
|
||
PUCHAR Va;
|
||
PVOID TempVa;
|
||
PMMPTE PointerPte;
|
||
PMMPTE PointerPde;
|
||
PMMPTE PointerPpe;
|
||
PMMPTE PointerPxe;
|
||
PMMPTE OriginalPointerPte;
|
||
PMMPTE ProtoPte;
|
||
PMMPTE LastProtoPte;
|
||
PEPROCESS CurrentProcess;
|
||
PSUBSECTION Subsection;
|
||
PVOID UsedPageTableHandle;
|
||
KIRQL OldIrql;
|
||
MMPTE_FLUSH_LIST FlushList;
|
||
ULONG Waited;
|
||
LOGICAL Skipped;
|
||
#if DBG
|
||
PMMPTE ProtoPte2;
|
||
PMMPTE LastProtoPte2;
|
||
#endif
|
||
#if (_MI_PAGING_LEVELS >= 3)
|
||
PVOID UsedPageDirectoryHandle;
|
||
PVOID TempHandle;
|
||
#endif
|
||
|
||
OldIrql = APC_LEVEL;
|
||
FlushList.Count = 0;
|
||
|
||
MM_PFN_LOCK_ASSERT();
|
||
CurrentProcess = PsGetCurrentProcess();
|
||
|
||
Va = StartingAddress;
|
||
PointerPpe = MiGetPpeAddress (Va);
|
||
PointerPde = MiGetPdeAddress (Va);
|
||
PointerPte = MiGetPteAddress (Va);
|
||
PointerPxe = MiGetPxeAddress (Va);
|
||
OriginalPointerPte = PointerPte;
|
||
|
||
do {
|
||
|
||
#if (_MI_PAGING_LEVELS >= 3)
|
||
restart:
|
||
#endif
|
||
|
||
while (MiDoesPxeExistAndMakeValid (PointerPxe,
|
||
CurrentProcess,
|
||
TRUE,
|
||
&Waited) == FALSE) {
|
||
|
||
//
|
||
// This extended page directory parent entry is empty,
|
||
// go to the next one.
|
||
//
|
||
|
||
PointerPxe += 1;
|
||
PointerPpe = MiGetVirtualAddressMappedByPte (PointerPxe);
|
||
PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
|
||
PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
|
||
Va = MiGetVirtualAddressMappedByPte (PointerPte);
|
||
|
||
if (Va > EndingAddress) {
|
||
|
||
//
|
||
// All done, return.
|
||
//
|
||
|
||
return;
|
||
}
|
||
}
|
||
#if (_MI_PAGING_LEVELS >= 4)
|
||
Waited = 0;
|
||
#endif
|
||
|
||
while (MiDoesPpeExistAndMakeValid (PointerPpe,
|
||
CurrentProcess,
|
||
TRUE,
|
||
&Waited) == FALSE) {
|
||
|
||
//
|
||
// This page directory parent entry is empty, go to the next one.
|
||
//
|
||
|
||
PointerPpe += 1;
|
||
PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
|
||
PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
|
||
Va = MiGetVirtualAddressMappedByPte (PointerPte);
|
||
|
||
if (Va > EndingAddress) {
|
||
|
||
//
|
||
// All done, return.
|
||
//
|
||
|
||
return;
|
||
}
|
||
#if (_MI_PAGING_LEVELS >= 4)
|
||
if (MiIsPteOnPdeBoundary (PointerPpe)) {
|
||
PointerPxe += 1;
|
||
ASSERT (PointerPxe == MiGetPteAddress (PointerPpe));
|
||
goto restart;
|
||
}
|
||
#endif
|
||
}
|
||
|
||
#if (_MI_PAGING_LEVELS < 4)
|
||
Waited = 0;
|
||
#endif
|
||
|
||
#if (_MI_PAGING_LEVELS >= 3) && defined (DBG)
|
||
MI_CHECK_USED_PTES_HANDLE (PointerPte);
|
||
TempHandle = MI_GET_USED_PTES_HANDLE (PointerPte);
|
||
ASSERT ((MI_GET_USED_PTES_FROM_HANDLE (TempHandle) != 0) ||
|
||
(PointerPde->u.Long == 0));
|
||
#endif
|
||
|
||
while (MiDoesPdeExistAndMakeValid (PointerPde,
|
||
CurrentProcess,
|
||
TRUE,
|
||
&Waited) == FALSE) {
|
||
|
||
//
|
||
// This page directory entry is empty, go to the next one.
|
||
//
|
||
|
||
PointerPde += 1;
|
||
PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
|
||
Va = MiGetVirtualAddressMappedByPte (PointerPte);
|
||
|
||
if (Va > EndingAddress) {
|
||
|
||
//
|
||
// All done, return.
|
||
//
|
||
|
||
return;
|
||
}
|
||
|
||
#if (_MI_PAGING_LEVELS >= 3)
|
||
if (MiIsPteOnPdeBoundary (PointerPde)) {
|
||
PointerPpe += 1;
|
||
ASSERT (PointerPpe == MiGetPteAddress (PointerPde));
|
||
PointerPxe = MiGetPteAddress (PointerPpe);
|
||
goto restart;
|
||
}
|
||
#endif
|
||
}
|
||
|
||
#if (_MI_PAGING_LEVELS >= 3) && defined (DBG)
|
||
MI_CHECK_USED_PTES_HANDLE (Va);
|
||
TempHandle = MI_GET_USED_PTES_HANDLE (Va);
|
||
ASSERT ((MI_GET_USED_PTES_FROM_HANDLE (TempHandle) != 0) ||
|
||
(PointerPte->u.Long == 0));
|
||
#endif
|
||
|
||
} while (Waited != 0);
|
||
|
||
//
|
||
// A valid PDE has been located, examine each PTE and delete them.
|
||
//
|
||
|
||
ASSERT64 (PointerPpe->u.Hard.Valid == 1);
|
||
ASSERT (PointerPde->u.Hard.Valid == 1);
|
||
ASSERT (Va <= EndingAddress);
|
||
|
||
MI_CHECK_USED_PTES_HANDLE (Va);
|
||
UsedPageTableHandle = MI_GET_USED_PTES_HANDLE (Va);
|
||
|
||
if ((Vad == (PMMVAD)NULL) ||
|
||
(Vad->u.VadFlags.PrivateMemory) ||
|
||
(Vad->FirstPrototypePte == (PMMPTE)NULL)) {
|
||
ProtoPte = (PMMPTE)NULL;
|
||
LastProtoPte = (PMMPTE)NULL;
|
||
}
|
||
else {
|
||
ProtoPte = Vad->FirstPrototypePte;
|
||
LastProtoPte = (PMMPTE)4;
|
||
}
|
||
|
||
//
|
||
// Initializing Subsection is not needed for correctness, but
|
||
// without it the compiler cannot compile this code W4 to check
|
||
// for use of uninitialized variables.
|
||
//
|
||
|
||
Subsection = NULL;
|
||
|
||
//
|
||
// Examine each PTE within the address range and delete it.
|
||
//
|
||
|
||
do {
|
||
|
||
//
|
||
// The PPE and PDE are now valid, delete the PTE.
|
||
//
|
||
|
||
ASSERT64 (PointerPpe->u.Hard.Valid == 1);
|
||
ASSERT (PointerPde->u.Hard.Valid == 1);
|
||
ASSERT (Va <= EndingAddress);
|
||
|
||
MI_CHECK_USED_PTES_HANDLE (Va);
|
||
ASSERT (UsedPageTableHandle == MI_GET_USED_PTES_HANDLE (Va));
|
||
|
||
if (PointerPte->u.Long != 0) {
|
||
|
||
//
|
||
// One less used page table entry in this page table page.
|
||
//
|
||
|
||
MI_DECREMENT_USED_PTES_BY_HANDLE (UsedPageTableHandle);
|
||
|
||
if (IS_PTE_NOT_DEMAND_ZERO (*PointerPte)) {
|
||
|
||
if (LastProtoPte != NULL) {
|
||
if (ProtoPte >= LastProtoPte) {
|
||
ProtoPte = MiGetProtoPteAddress(Vad, MI_VA_TO_VPN(Va));
|
||
Subsection = MiLocateSubsection (Vad, MI_VA_TO_VPN(Va));
|
||
LastProtoPte = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
|
||
}
|
||
#if DBG
|
||
if (Vad->u.VadFlags.ImageMap != 1) {
|
||
if ((ProtoPte < Subsection->SubsectionBase) ||
|
||
(ProtoPte >= LastProtoPte)) {
|
||
DbgPrint ("bad proto PTE %p va %p Vad %p sub %p\n",
|
||
ProtoPte,Va,Vad,Subsection);
|
||
DbgBreakPoint();
|
||
}
|
||
}
|
||
#endif
|
||
}
|
||
|
||
Waited = MiDeletePte (PointerPte,
|
||
(PVOID)Va,
|
||
AddressSpaceDeletion,
|
||
CurrentProcess,
|
||
ProtoPte,
|
||
&FlushList);
|
||
|
||
#if (_MI_PAGING_LEVELS >= 3)
|
||
|
||
//
|
||
// This must be recalculated here if MiDeletePte dropped the
|
||
// PFN lock (this can happen when dealing with POSIX forked
|
||
// pages. Since the used PTE count is kept in the PFN entry
|
||
// which could have been paged out and replaced during this
|
||
// window, recomputation of its address (not the contents)
|
||
// is necessary.
|
||
//
|
||
|
||
if (Waited != 0) {
|
||
MI_CHECK_USED_PTES_HANDLE (Va);
|
||
UsedPageTableHandle = MI_GET_USED_PTES_HANDLE (Va);
|
||
}
|
||
#endif
|
||
|
||
}
|
||
else {
|
||
MI_WRITE_INVALID_PTE (PointerPte, ZeroPte);
|
||
}
|
||
}
|
||
|
||
Va += PAGE_SIZE;
|
||
PointerPte += 1;
|
||
ProtoPte += 1;
|
||
|
||
ASSERT64 (PointerPpe->u.Hard.Valid == 1);
|
||
ASSERT (PointerPde->u.Hard.Valid == 1);
|
||
|
||
//
|
||
// If not at the end of a page table and still within the specified
|
||
// range, then just march directly on to the next PTE.
|
||
//
|
||
|
||
if ((!MiIsVirtualAddressOnPdeBoundary(Va)) && (Va <= EndingAddress)) {
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// The virtual address is on a page directory boundary:
|
||
//
|
||
// 1. Flush the PTEs for the previous page table page.
|
||
// 2. Delete the previous page directory & page table if appropriate.
|
||
// 3. Attempt to leap forward skipping over empty page directories
|
||
// and page tables where possible.
|
||
//
|
||
|
||
//
|
||
// If all the entries have been eliminated from the previous
|
||
// page table page, delete the page table page itself.
|
||
//
|
||
|
||
MiFlushPteList (&FlushList, FALSE, ZeroPte);
|
||
|
||
//
|
||
// If all the entries have been eliminated from the previous
|
||
// page table page, delete the page table page itself.
|
||
//
|
||
|
||
ASSERT64 (PointerPpe->u.Hard.Valid == 1);
|
||
ASSERT (PointerPde->u.Hard.Valid == 1);
|
||
|
||
#if (_MI_PAGING_LEVELS >= 3)
|
||
MI_CHECK_USED_PTES_HANDLE (PointerPte - 1);
|
||
#endif
|
||
|
||
if ((MI_GET_USED_PTES_FROM_HANDLE (UsedPageTableHandle) == 0) &&
|
||
(PointerPde->u.Long != 0)) {
|
||
|
||
#if (_MI_PAGING_LEVELS >= 3)
|
||
UsedPageDirectoryHandle = MI_GET_USED_PTES_HANDLE (PointerPte - 1);
|
||
MI_DECREMENT_USED_PTES_BY_HANDLE (UsedPageDirectoryHandle);
|
||
#endif
|
||
|
||
TempVa = MiGetVirtualAddressMappedByPte(PointerPde);
|
||
MiDeletePte (PointerPde,
|
||
TempVa,
|
||
AddressSpaceDeletion,
|
||
CurrentProcess,
|
||
NULL,
|
||
NULL);
|
||
|
||
#if (_MI_PAGING_LEVELS >= 3)
|
||
if ((MI_GET_USED_PTES_FROM_HANDLE (UsedPageDirectoryHandle) == 0) &&
|
||
(PointerPpe->u.Long != 0)) {
|
||
|
||
#if (_MI_PAGING_LEVELS >= 4)
|
||
UsedPageDirectoryHandle = MI_GET_USED_PTES_HANDLE (PointerPde);
|
||
MI_DECREMENT_USED_PTES_BY_HANDLE (UsedPageDirectoryHandle);
|
||
#endif
|
||
|
||
TempVa = MiGetVirtualAddressMappedByPte(PointerPpe);
|
||
MiDeletePte (PointerPpe,
|
||
TempVa,
|
||
AddressSpaceDeletion,
|
||
CurrentProcess,
|
||
NULL,
|
||
NULL);
|
||
|
||
#if (_MI_PAGING_LEVELS >= 4)
|
||
if ((MI_GET_USED_PTES_FROM_HANDLE (UsedPageDirectoryHandle) == 0) &&
|
||
(PointerPxe->u.Long != 0)) {
|
||
|
||
TempVa = MiGetVirtualAddressMappedByPte(PointerPxe);
|
||
MiDeletePte (PointerPxe,
|
||
TempVa,
|
||
AddressSpaceDeletion,
|
||
CurrentProcess,
|
||
NULL,
|
||
NULL);
|
||
}
|
||
#endif
|
||
|
||
}
|
||
#endif
|
||
}
|
||
|
||
if (Va > EndingAddress) {
|
||
|
||
//
|
||
// All done, return.
|
||
//
|
||
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Release the PFN lock. This prevents a single thread
|
||
// from forcing other high priority threads from being
|
||
// blocked while a large address range is deleted. There
|
||
// is nothing magic about the instructions within the
|
||
// lock and unlock.
|
||
//
|
||
|
||
UNLOCK_PFN (OldIrql);
|
||
PointerPde = MiGetPdeAddress (Va);
|
||
PointerPpe = MiGetPpeAddress (Va);
|
||
PointerPxe = MiGetPxeAddress (Va);
|
||
Skipped = FALSE;
|
||
LOCK_PFN (OldIrql);
|
||
|
||
//
|
||
// Attempt to leap forward skipping over empty page directories
|
||
// and page tables where possible.
|
||
//
|
||
|
||
do {
|
||
|
||
#if (_MI_PAGING_LEVELS >= 3)
|
||
restart2:
|
||
#endif
|
||
|
||
while (MiDoesPxeExistAndMakeValid (PointerPxe,
|
||
CurrentProcess,
|
||
TRUE,
|
||
&Waited) == FALSE) {
|
||
|
||
//
|
||
// This extended page directory parent entry is empty,
|
||
// go to the next one.
|
||
//
|
||
|
||
Skipped = TRUE;
|
||
PointerPxe += 1;
|
||
PointerPpe = MiGetVirtualAddressMappedByPte (PointerPxe);
|
||
PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
|
||
PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
|
||
Va = MiGetVirtualAddressMappedByPte (PointerPte);
|
||
|
||
if (Va > EndingAddress) {
|
||
|
||
//
|
||
// All done, return.
|
||
//
|
||
|
||
return;
|
||
}
|
||
}
|
||
|
||
#if (_MI_PAGING_LEVELS >= 4)
|
||
Waited = 0;
|
||
#endif
|
||
while (MiDoesPpeExistAndMakeValid (PointerPpe,
|
||
CurrentProcess,
|
||
TRUE,
|
||
&Waited) == FALSE) {
|
||
|
||
//
|
||
// This page directory parent entry is empty,
|
||
// go to the next one.
|
||
//
|
||
|
||
Skipped = TRUE;
|
||
PointerPpe += 1;
|
||
PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
|
||
PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
|
||
Va = MiGetVirtualAddressMappedByPte (PointerPte);
|
||
|
||
if (Va > EndingAddress) {
|
||
|
||
//
|
||
// All done, return.
|
||
//
|
||
|
||
return;
|
||
}
|
||
#if (_MI_PAGING_LEVELS >= 4)
|
||
if (MiIsPteOnPdeBoundary (PointerPpe)) {
|
||
PointerPxe += 1;
|
||
ASSERT (PointerPxe == MiGetPteAddress (PointerPpe));
|
||
goto restart2;
|
||
}
|
||
#endif
|
||
}
|
||
|
||
#if (_MI_PAGING_LEVELS >= 3) && defined (DBG)
|
||
MI_CHECK_USED_PTES_HANDLE (PointerPte);
|
||
TempHandle = MI_GET_USED_PTES_HANDLE (PointerPte);
|
||
ASSERT ((MI_GET_USED_PTES_FROM_HANDLE (TempHandle) != 0) ||
|
||
(PointerPde->u.Long == 0));
|
||
#endif
|
||
|
||
#if (_MI_PAGING_LEVELS < 4)
|
||
Waited = 0;
|
||
#endif
|
||
|
||
while (MiDoesPdeExistAndMakeValid (PointerPde,
|
||
CurrentProcess,
|
||
TRUE,
|
||
&Waited) == FALSE) {
|
||
|
||
//
|
||
// This page directory entry is empty, go to the next one.
|
||
//
|
||
|
||
Skipped = TRUE;
|
||
PointerPde += 1;
|
||
PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
|
||
Va = MiGetVirtualAddressMappedByPte (PointerPte);
|
||
|
||
if (Va > EndingAddress) {
|
||
|
||
//
|
||
// All done, remove any straggling page directories and
|
||
// return.
|
||
//
|
||
|
||
return;
|
||
}
|
||
|
||
#if (_MI_PAGING_LEVELS >= 3)
|
||
if (MiIsPteOnPdeBoundary (PointerPde)) {
|
||
PointerPpe += 1;
|
||
ASSERT (PointerPpe == MiGetPteAddress (PointerPde));
|
||
PointerPxe = MiGetPteAddress (PointerPpe);
|
||
goto restart2;
|
||
}
|
||
#endif
|
||
|
||
#if DBG
|
||
if ((LastProtoPte != NULL) &&
|
||
(Vad->u2.VadFlags2.ExtendableFile == 0)) {
|
||
ProtoPte2 = MiGetProtoPteAddress(Vad, MI_VA_TO_VPN (Va));
|
||
Subsection = MiLocateSubsection (Vad,MI_VA_TO_VPN (Va));
|
||
LastProtoPte2 = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
|
||
if (Vad->u.VadFlags.ImageMap != 1) {
|
||
if ((ProtoPte2 < Subsection->SubsectionBase) ||
|
||
(ProtoPte2 >= LastProtoPte2)) {
|
||
DbgPrint ("bad proto PTE %p va %p Vad %p sub %p\n",
|
||
ProtoPte2,Va,Vad,Subsection);
|
||
DbgBreakPoint();
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
}
|
||
#if (_MI_PAGING_LEVELS >= 3) && defined (DBG)
|
||
MI_CHECK_USED_PTES_HANDLE (Va);
|
||
TempHandle = MI_GET_USED_PTES_HANDLE (Va);
|
||
ASSERT ((MI_GET_USED_PTES_FROM_HANDLE (TempHandle) != 0) ||
|
||
(PointerPte->u.Long == 0));
|
||
#endif
|
||
|
||
} while (Waited != 0);
|
||
|
||
//
|
||
// The PPE and PDE are now valid, get the page table use address
|
||
// as it changes whenever the PDE does.
|
||
//
|
||
|
||
#if (_MI_PAGING_LEVELS >= 4)
|
||
ASSERT64 (PointerPxe->u.Hard.Valid == 1);
|
||
#endif
|
||
ASSERT64 (PointerPpe->u.Hard.Valid == 1);
|
||
ASSERT (PointerPde->u.Hard.Valid == 1);
|
||
ASSERT (Va <= EndingAddress);
|
||
|
||
MI_CHECK_USED_PTES_HANDLE (Va);
|
||
UsedPageTableHandle = MI_GET_USED_PTES_HANDLE (Va);
|
||
|
||
//
|
||
// If we skipped chunks of address space, the prototype PTE pointer
|
||
// must be updated now so VADs that span multiple subsections
|
||
// are handled properly.
|
||
//
|
||
|
||
if ((Skipped == TRUE) && (LastProtoPte != NULL)) {
|
||
|
||
ProtoPte = MiGetProtoPteAddress(Vad, MI_VA_TO_VPN(Va));
|
||
Subsection = MiLocateSubsection (Vad, MI_VA_TO_VPN(Va));
|
||
|
||
if (Subsection != NULL) {
|
||
LastProtoPte = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
|
||
#if DBG
|
||
if (Vad->u.VadFlags.ImageMap != 1) {
|
||
if ((ProtoPte < Subsection->SubsectionBase) ||
|
||
(ProtoPte >= LastProtoPte)) {
|
||
DbgPrint ("bad proto PTE %p va %p Vad %p sub %p\n",
|
||
ProtoPte,Va,Vad,Subsection);
|
||
DbgBreakPoint();
|
||
}
|
||
}
|
||
#endif
|
||
}
|
||
else {
|
||
|
||
//
|
||
// The Vad span is larger than the section being mapped.
|
||
// Null the proto PTE local as no more proto PTEs will
|
||
// need to be deleted at this point.
|
||
//
|
||
|
||
LastProtoPte = NULL;
|
||
}
|
||
}
|
||
|
||
ASSERT (Va <= EndingAddress);
|
||
|
||
} while (TRUE);
|
||
}
|
||
|
||
|
||
ULONG
|
||
MiDeletePte (
|
||
IN PMMPTE PointerPte,
|
||
IN PVOID VirtualAddress,
|
||
IN ULONG AddressSpaceDeletion,
|
||
IN PEPROCESS CurrentProcess,
|
||
IN PMMPTE PrototypePte,
|
||
IN PMMPTE_FLUSH_LIST PteFlushList OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine deletes the contents of the specified PTE. The PTE
|
||
can be in one of the following states:
|
||
|
||
- active and valid
|
||
- transition
|
||
- in paging file
|
||
- in prototype PTE format
|
||
|
||
Arguments:
|
||
|
||
PointerPte - Supplies a pointer to the PTE to delete.
|
||
|
||
VirtualAddress - Supplies the virtual address which corresponds to
|
||
the PTE. This is used to locate the working set entry
|
||
to eliminate it.
|
||
|
||
AddressSpaceDeletion - Supplies TRUE if the address space is being
|
||
deleted, FALSE otherwise. If TRUE is specified
|
||
the TB is not flushed and valid addresses are
|
||
not removed from the working set.
|
||
|
||
|
||
CurrentProcess - Supplies a pointer to the current process.
|
||
|
||
PrototypePte - Supplies a pointer to the prototype PTE which currently
|
||
or originally mapped this page. This is used to determine
|
||
if the PTE is a fork PTE and should have its reference block
|
||
decremented.
|
||
|
||
Return Value:
|
||
|
||
Nonzero if this routine released mutexes and locks, FALSE if not.
|
||
|
||
Environment:
|
||
|
||
Kernel mode, APCs disabled, PFN lock and working set mutex held.
|
||
|
||
--*/
|
||
|
||
{
|
||
PMMPTE PointerPde;
|
||
PMMPTE PointerPpe;
|
||
PMMPTE PointerPxe;
|
||
PMMPFN Pfn1;
|
||
PMMPFN Pfn2;
|
||
MMPTE PteContents;
|
||
WSLE_NUMBER WorkingSetIndex;
|
||
WSLE_NUMBER Entry;
|
||
MMWSLENTRY Locked;
|
||
WSLE_NUMBER WsPfnIndex;
|
||
PMMCLONE_BLOCK CloneBlock;
|
||
PMMCLONE_DESCRIPTOR CloneDescriptor;
|
||
ULONG Waited;
|
||
ULONG DroppedLocks;
|
||
PFN_NUMBER PageFrameIndex;
|
||
PFN_NUMBER PageTableFrameIndex;
|
||
|
||
MM_PFN_LOCK_ASSERT();
|
||
|
||
DroppedLocks = 0;
|
||
|
||
#if DBG
|
||
if (MmDebug & MM_DBG_PTE_UPDATE) {
|
||
DbgPrint("deleting PTE\n");
|
||
MiFormatPte(PointerPte);
|
||
}
|
||
#endif
|
||
|
||
PteContents = *PointerPte;
|
||
|
||
if (PteContents.u.Hard.Valid == 1) {
|
||
|
||
#ifdef _X86_
|
||
#if DBG
|
||
#if !defined(NT_UP)
|
||
|
||
if (PteContents.u.Hard.Writable == 1) {
|
||
ASSERT (PteContents.u.Hard.Dirty == 1);
|
||
}
|
||
#endif //NTUP
|
||
#endif //DBG
|
||
#endif //X86
|
||
|
||
//
|
||
// PTE is valid. Check PFN database to see if this is a prototype PTE.
|
||
//
|
||
|
||
PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE(&PteContents);
|
||
Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
|
||
WsPfnIndex = Pfn1->u1.WsIndex;
|
||
|
||
#if DBG
|
||
if (MmDebug & MM_DBG_PTE_UPDATE) {
|
||
MiFormatPfn(Pfn1);
|
||
}
|
||
#endif
|
||
|
||
CloneDescriptor = NULL;
|
||
|
||
if (Pfn1->u3.e1.PrototypePte == 1) {
|
||
|
||
CloneBlock = (PMMCLONE_BLOCK)Pfn1->PteAddress;
|
||
|
||
//
|
||
// Capture the state of the modified bit for this PTE.
|
||
//
|
||
|
||
MI_CAPTURE_DIRTY_BIT_TO_PFN (PointerPte, Pfn1);
|
||
|
||
//
|
||
// Decrement the share and valid counts of the page table
|
||
// page which maps this PTE.
|
||
//
|
||
|
||
PointerPde = MiGetPteAddress (PointerPte);
|
||
if (PointerPde->u.Hard.Valid == 0) {
|
||
#if (_MI_PAGING_LEVELS < 3)
|
||
if (!NT_SUCCESS(MiCheckPdeForPagedPool (PointerPte))) {
|
||
#endif
|
||
KeBugCheckEx (MEMORY_MANAGEMENT,
|
||
0x61940,
|
||
(ULONG_PTR)PointerPte,
|
||
(ULONG_PTR)PointerPde->u.Long,
|
||
(ULONG_PTR)MiGetVirtualAddressMappedByPte(PointerPte));
|
||
#if (_MI_PAGING_LEVELS < 3)
|
||
}
|
||
#endif
|
||
}
|
||
|
||
PageTableFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE(PointerPde);
|
||
Pfn2 = MI_PFN_ELEMENT (PageTableFrameIndex);
|
||
|
||
MiDecrementShareCountInline (Pfn2, PageTableFrameIndex);
|
||
|
||
//
|
||
// Decrement the share count for the physical page.
|
||
//
|
||
|
||
MiDecrementShareCountInline (Pfn1, PageFrameIndex);
|
||
|
||
//
|
||
// Check to see if this is a fork prototype PTE and if so
|
||
// update the clone descriptor address.
|
||
//
|
||
|
||
if (PointerPte <= MiHighestUserPte) {
|
||
|
||
if (PrototypePte != Pfn1->PteAddress) {
|
||
|
||
//
|
||
// Locate the clone descriptor within the clone tree.
|
||
//
|
||
|
||
CloneDescriptor = MiLocateCloneAddress (CurrentProcess, (PVOID)CloneBlock);
|
||
|
||
#if DBG
|
||
if (CloneDescriptor == NULL) {
|
||
DbgPrint("1PrototypePte %p Clone desc %p pfn PTE addr %p\n",
|
||
PrototypePte, CloneDescriptor, Pfn1->PteAddress);
|
||
MiFormatPte(PointerPte);
|
||
ASSERT (FALSE);
|
||
}
|
||
#endif
|
||
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
|
||
//
|
||
// Initializing CloneBlock & PointerPde is not needed for
|
||
// correctness but without it the compiler cannot compile this code
|
||
// W4 to check for use of uninitialized variables.
|
||
//
|
||
|
||
CloneBlock = NULL;
|
||
PointerPde = NULL;
|
||
|
||
ASSERT (Pfn1->u2.ShareCount == 1);
|
||
|
||
//
|
||
// This PTE is a NOT a prototype PTE, delete the physical page.
|
||
//
|
||
|
||
//
|
||
// Decrement the share and valid counts of the page table
|
||
// page which maps this PTE.
|
||
//
|
||
|
||
MiDecrementShareCountInline (MI_PFN_ELEMENT(Pfn1->u4.PteFrame),
|
||
Pfn1->u4.PteFrame);
|
||
|
||
MI_SET_PFN_DELETED (Pfn1);
|
||
|
||
//
|
||
// Decrement the share count for the physical page. As the page
|
||
// is private it will be put on the free list.
|
||
//
|
||
|
||
MiDecrementShareCountOnly (PageFrameIndex);
|
||
|
||
//
|
||
// Decrement the count for the number of private pages.
|
||
//
|
||
|
||
CurrentProcess->NumberOfPrivatePages -= 1;
|
||
}
|
||
|
||
//
|
||
// Find the WSLE for this page and eliminate it.
|
||
//
|
||
|
||
//
|
||
// If we are deleting the system portion of the address space, do
|
||
// not remove WSLEs or flush translation buffers as there can be
|
||
// no other usage of this address space.
|
||
//
|
||
|
||
if (AddressSpaceDeletion == FALSE) {
|
||
|
||
WorkingSetIndex = MiLocateWsle (VirtualAddress,
|
||
MmWorkingSetList,
|
||
WsPfnIndex);
|
||
|
||
ASSERT (WorkingSetIndex != WSLE_NULL_INDEX);
|
||
|
||
//
|
||
// Check to see if this entry is locked in the working set
|
||
// or locked in memory.
|
||
//
|
||
|
||
Locked = MmWsle[WorkingSetIndex].u1.e1;
|
||
|
||
MiRemoveWsle (WorkingSetIndex, MmWorkingSetList);
|
||
|
||
//
|
||
// Add this entry to the list of free working set entries
|
||
// and adjust the working set count.
|
||
//
|
||
|
||
MiReleaseWsle (WorkingSetIndex, &CurrentProcess->Vm);
|
||
|
||
if ((Locked.LockedInWs == 1) || (Locked.LockedInMemory == 1)) {
|
||
|
||
//
|
||
// This entry is locked.
|
||
//
|
||
|
||
ASSERT (WorkingSetIndex < MmWorkingSetList->FirstDynamic);
|
||
MmWorkingSetList->FirstDynamic -= 1;
|
||
|
||
if (WorkingSetIndex != MmWorkingSetList->FirstDynamic) {
|
||
|
||
Entry = MmWorkingSetList->FirstDynamic;
|
||
ASSERT (MmWsle[Entry].u1.e1.Valid);
|
||
#if 0
|
||
SwapVa = MmWsle[Entry].u1.VirtualAddress;
|
||
SwapVa = PAGE_ALIGN (SwapVa);
|
||
Pfn2 = MI_PFN_ELEMENT (
|
||
MiGetPteAddress (SwapVa)->u.Hard.PageFrameNumber);
|
||
Entry = MiLocateWsleAndParent (SwapVa,
|
||
&Parent,
|
||
MmWorkingSetList,
|
||
Pfn2->u1.WsIndex);
|
||
|
||
//
|
||
// Swap the removed entry with the last locked entry
|
||
// which is located at first dynamic.
|
||
//
|
||
|
||
MiSwapWslEntries (Entry,
|
||
Parent,
|
||
WorkingSetIndex,
|
||
MmWorkingSetList);
|
||
#endif //0
|
||
|
||
MiSwapWslEntries (Entry,
|
||
WorkingSetIndex,
|
||
&CurrentProcess->Vm);
|
||
}
|
||
}
|
||
else {
|
||
ASSERT (WorkingSetIndex >= MmWorkingSetList->FirstDynamic);
|
||
}
|
||
|
||
//
|
||
// Flush the entry out of the TB.
|
||
//
|
||
|
||
if (!ARGUMENT_PRESENT (PteFlushList)) {
|
||
KeFlushSingleTb (VirtualAddress,
|
||
TRUE,
|
||
FALSE,
|
||
(PHARDWARE_PTE)PointerPte,
|
||
ZeroPte.u.Flush);
|
||
}
|
||
else {
|
||
if (PteFlushList->Count != MM_MAXIMUM_FLUSH_COUNT) {
|
||
PteFlushList->FlushPte[PteFlushList->Count] = PointerPte;
|
||
PteFlushList->FlushVa[PteFlushList->Count] = VirtualAddress;
|
||
PteFlushList->Count += 1;
|
||
}
|
||
MI_WRITE_INVALID_PTE (PointerPte, ZeroPte);
|
||
}
|
||
|
||
if (CloneDescriptor != NULL) {
|
||
|
||
//
|
||
// Flush PTEs as this could release the PFN lock.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT (PteFlushList)) {
|
||
MiFlushPteList (PteFlushList, FALSE, ZeroPte);
|
||
}
|
||
|
||
//
|
||
// Decrement the reference count for the clone block,
|
||
// note that this could release and reacquire
|
||
// the mutexes hence cannot be done until after the
|
||
// working set index has been removed.
|
||
//
|
||
|
||
if (MiDecrementCloneBlockReference (CloneDescriptor,
|
||
CloneBlock,
|
||
CurrentProcess)) {
|
||
|
||
//
|
||
// The working set mutex was released. This may
|
||
// have removed the current page directory & table page.
|
||
//
|
||
|
||
DroppedLocks = 1;
|
||
|
||
PointerPpe = MiGetPteAddress (PointerPde);
|
||
PointerPxe = MiGetPdeAddress (PointerPde);
|
||
|
||
do {
|
||
|
||
MiDoesPxeExistAndMakeValid (PointerPxe,
|
||
CurrentProcess,
|
||
TRUE,
|
||
&Waited);
|
||
|
||
#if (_MI_PAGING_LEVELS >= 4)
|
||
Waited = 0;
|
||
#endif
|
||
|
||
MiDoesPpeExistAndMakeValid (PointerPpe,
|
||
CurrentProcess,
|
||
TRUE,
|
||
&Waited);
|
||
|
||
#if (_MI_PAGING_LEVELS < 4)
|
||
Waited = 0;
|
||
#endif
|
||
|
||
//
|
||
// If the call below results in a PFN release and
|
||
// reacquire, then we must redo them both.
|
||
//
|
||
|
||
MiDoesPdeExistAndMakeValid (PointerPde,
|
||
CurrentProcess,
|
||
TRUE,
|
||
&Waited);
|
||
|
||
} while (Waited != 0);
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
else if (PteContents.u.Soft.Prototype == 1) {
|
||
|
||
//
|
||
// This is a prototype PTE, if it is a fork PTE clean up the
|
||
// fork structures.
|
||
//
|
||
|
||
if (PteContents.u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED) {
|
||
|
||
//
|
||
// Check to see if the prototype PTE is a fork prototype PTE.
|
||
//
|
||
|
||
if (PointerPte <= MiHighestUserPte) {
|
||
|
||
if (PrototypePte != MiPteToProto (PointerPte)) {
|
||
|
||
CloneBlock = (PMMCLONE_BLOCK)MiPteToProto (PointerPte);
|
||
CloneDescriptor = MiLocateCloneAddress (CurrentProcess, (PVOID)CloneBlock);
|
||
|
||
|
||
#if DBG
|
||
if (CloneDescriptor == NULL) {
|
||
DbgPrint("1PrototypePte %p Clone desc %p \n",
|
||
PrototypePte, CloneDescriptor);
|
||
MiFormatPte(PointerPte);
|
||
ASSERT (FALSE);
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// Decrement the reference count for the clone block,
|
||
// note that this could release and reacquire
|
||
// the mutexes.
|
||
//
|
||
|
||
MI_WRITE_INVALID_PTE (PointerPte, ZeroPte);
|
||
|
||
if (ARGUMENT_PRESENT (PteFlushList)) {
|
||
MiFlushPteList (PteFlushList, FALSE, ZeroPte);
|
||
}
|
||
|
||
if (MiDecrementCloneBlockReference ( CloneDescriptor,
|
||
CloneBlock,
|
||
CurrentProcess )) {
|
||
|
||
//
|
||
// The working set mutex was released. This may
|
||
// have removed the current page directory & table page.
|
||
//
|
||
|
||
DroppedLocks = 1;
|
||
|
||
PointerPde = MiGetPteAddress (PointerPte);
|
||
PointerPpe = MiGetPteAddress (PointerPde);
|
||
PointerPxe = MiGetPdeAddress (PointerPde);
|
||
|
||
//
|
||
// If either call below results in a PFN release and
|
||
// reacquire, then we must redo them both.
|
||
//
|
||
|
||
do {
|
||
|
||
if (MiDoesPxeExistAndMakeValid (PointerPxe,
|
||
CurrentProcess,
|
||
TRUE,
|
||
&Waited) == FALSE) {
|
||
|
||
//
|
||
// The PXE has been deleted when the PFN lock
|
||
// was released. Just bail as the PPE/PDE/PTE
|
||
// are gone now anyway.
|
||
//
|
||
|
||
return DroppedLocks;
|
||
}
|
||
|
||
#if (_MI_PAGING_LEVELS >= 4)
|
||
Waited = 0;
|
||
#endif
|
||
|
||
if (MiDoesPpeExistAndMakeValid (PointerPpe,
|
||
CurrentProcess,
|
||
TRUE,
|
||
&Waited) == FALSE) {
|
||
|
||
//
|
||
// The PPE has been deleted when the PFN lock
|
||
// was released. Just bail as the PDE/PTE are
|
||
// gone now anyway.
|
||
//
|
||
|
||
return DroppedLocks;
|
||
}
|
||
|
||
#if (_MI_PAGING_LEVELS < 4)
|
||
Waited = 0;
|
||
#endif
|
||
|
||
//
|
||
// If the call below results in a PFN release and
|
||
// reacquire, then we must redo them both. If the
|
||
// PDE was deleted when the PFN lock was released
|
||
// then we just bail as the PTE is gone anyway.
|
||
//
|
||
|
||
if (MiDoesPdeExistAndMakeValid (PointerPde,
|
||
CurrentProcess,
|
||
TRUE,
|
||
&Waited) == FALSE) {
|
||
return DroppedLocks;
|
||
}
|
||
|
||
} while (Waited != 0);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
else if (PteContents.u.Soft.Transition == 1) {
|
||
|
||
//
|
||
// This is a transition PTE. (Page is private)
|
||
//
|
||
|
||
Pfn1 = MI_PFN_ELEMENT (PteContents.u.Trans.PageFrameNumber);
|
||
|
||
MI_SET_PFN_DELETED (Pfn1);
|
||
|
||
PageTableFrameIndex = Pfn1->u4.PteFrame;
|
||
Pfn2 = MI_PFN_ELEMENT (PageTableFrameIndex);
|
||
|
||
MiDecrementShareCountInline (Pfn2, PageTableFrameIndex);
|
||
|
||
//
|
||
// Check the reference count for the page, if the reference
|
||
// count is zero, move the page to the free list, if the reference
|
||
// count is not zero, ignore this page. When the reference count
|
||
// goes to zero, it will be placed on the free list.
|
||
//
|
||
|
||
if (Pfn1->u3.e2.ReferenceCount == 0) {
|
||
MiUnlinkPageFromList (Pfn1);
|
||
MiReleasePageFileSpace (Pfn1->OriginalPte);
|
||
MiInsertPageInFreeList (MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE(&PteContents));
|
||
}
|
||
|
||
//
|
||
// Decrement the count for the number of private pages.
|
||
//
|
||
|
||
CurrentProcess->NumberOfPrivatePages -= 1;
|
||
|
||
}
|
||
else {
|
||
|
||
//
|
||
// Must be page file space.
|
||
//
|
||
|
||
if (PteContents.u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED) {
|
||
|
||
if (MiReleasePageFileSpace (*PointerPte)) {
|
||
|
||
//
|
||
// Decrement the count for the number of private pages.
|
||
//
|
||
|
||
CurrentProcess->NumberOfPrivatePages -= 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Zero the PTE contents.
|
||
//
|
||
|
||
MI_WRITE_INVALID_PTE (PointerPte, ZeroPte);
|
||
|
||
return DroppedLocks;
|
||
}
|
||
|
||
|
||
ULONG
|
||
FASTCALL
|
||
MiReleasePageFileSpace (
|
||
IN MMPTE PteContents
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine frees the paging file allocated to the specified PTE.
|
||
|
||
Arguments:
|
||
|
||
PteContents - Supplies the PTE which is in page file format.
|
||
|
||
Return Value:
|
||
|
||
Returns TRUE if any paging file space was deallocated.
|
||
|
||
Environment:
|
||
|
||
Kernel mode, APCs disabled, PFN lock held.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG FreeBit;
|
||
ULONG PageFileNumber;
|
||
PMMPAGING_FILE PageFile;
|
||
|
||
MM_PFN_LOCK_ASSERT();
|
||
|
||
if (PteContents.u.Soft.Prototype == 1) {
|
||
|
||
//
|
||
// Not in page file format.
|
||
//
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
FreeBit = GET_PAGING_FILE_OFFSET (PteContents);
|
||
|
||
if ((FreeBit == 0) || (FreeBit == MI_PTE_LOOKUP_NEEDED)) {
|
||
|
||
//
|
||
// Page is not in a paging file, just return.
|
||
//
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
PageFileNumber = GET_PAGING_FILE_NUMBER (PteContents);
|
||
|
||
ASSERT (RtlCheckBit( MmPagingFile[PageFileNumber]->Bitmap, FreeBit) == 1);
|
||
|
||
#if DBG
|
||
if ((FreeBit < 8192) && (PageFileNumber == 0)) {
|
||
ASSERT ((MmPagingFileDebug[FreeBit] & 1) != 0);
|
||
MmPagingFileDebug[FreeBit] ^= 1;
|
||
}
|
||
#endif
|
||
|
||
PageFile = MmPagingFile[PageFileNumber];
|
||
MI_CLEAR_BIT (PageFile->Bitmap->Buffer, FreeBit);
|
||
|
||
PageFile->FreeSpace += 1;
|
||
PageFile->CurrentUsage -= 1;
|
||
|
||
//
|
||
// Check to see if we should move some MDL entries for the
|
||
// modified page writer now that more free space is available.
|
||
//
|
||
|
||
if ((MmNumberOfActiveMdlEntries == 0) ||
|
||
(PageFile->FreeSpace == MM_USABLE_PAGES_FREE)) {
|
||
|
||
MiUpdateModifiedWriterMdls (PageFileNumber);
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
VOID
|
||
FASTCALL
|
||
MiReleaseConfirmedPageFileSpace (
|
||
IN MMPTE PteContents
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine frees the paging file allocated to the specified PTE.
|
||
|
||
Arguments:
|
||
|
||
PteContents - Supplies the PTE which is in page file format.
|
||
|
||
Return Value:
|
||
|
||
Returns TRUE if any paging file space was deallocated.
|
||
|
||
Environment:
|
||
|
||
Kernel mode, APCs disabled, PFN lock held.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG FreeBit;
|
||
ULONG PageFileNumber;
|
||
PMMPAGING_FILE PageFile;
|
||
|
||
MM_PFN_LOCK_ASSERT();
|
||
|
||
ASSERT (PteContents.u.Soft.Prototype == 0);
|
||
|
||
FreeBit = GET_PAGING_FILE_OFFSET (PteContents);
|
||
|
||
ASSERT ((FreeBit != 0) && (FreeBit != MI_PTE_LOOKUP_NEEDED));
|
||
|
||
PageFileNumber = GET_PAGING_FILE_NUMBER (PteContents);
|
||
|
||
PageFile = MmPagingFile[PageFileNumber];
|
||
|
||
ASSERT (RtlCheckBit( PageFile->Bitmap, FreeBit) == 1);
|
||
|
||
#if DBG
|
||
if ((FreeBit < 8192) && (PageFileNumber == 0)) {
|
||
ASSERT ((MmPagingFileDebug[FreeBit] & 1) != 0);
|
||
MmPagingFileDebug[FreeBit] ^= 1;
|
||
}
|
||
#endif
|
||
|
||
MI_CLEAR_BIT (PageFile->Bitmap->Buffer, FreeBit);
|
||
|
||
PageFile->FreeSpace += 1;
|
||
PageFile->CurrentUsage -= 1;
|
||
|
||
//
|
||
// Check to see if we should move some MDL entries for the
|
||
// modified page writer now that more free space is available.
|
||
//
|
||
|
||
if ((MmNumberOfActiveMdlEntries == 0) ||
|
||
(PageFile->FreeSpace == MM_USABLE_PAGES_FREE)) {
|
||
|
||
MiUpdateModifiedWriterMdls (PageFileNumber);
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
FASTCALL
|
||
MiUpdateModifiedWriterMdls (
|
||
IN ULONG PageFileNumber
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine ensures the MDLs for the specified paging file
|
||
are in the proper state such that paging i/o can continue.
|
||
|
||
Arguments:
|
||
|
||
PageFileNumber - Supplies the page file number to check the MDLs for.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Environment:
|
||
|
||
Kernel mode, PFN lock held.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG i;
|
||
PMMMOD_WRITER_MDL_ENTRY WriterEntry;
|
||
|
||
//
|
||
// Put the MDL entries into the active list.
|
||
//
|
||
|
||
for (i = 0; i < MM_PAGING_FILE_MDLS; i += 1) {
|
||
|
||
if ((MmPagingFile[PageFileNumber]->Entry[i]->Links.Flink !=
|
||
MM_IO_IN_PROGRESS)
|
||
&&
|
||
(MmPagingFile[PageFileNumber]->Entry[i]->CurrentList ==
|
||
&MmFreePagingSpaceLow)) {
|
||
|
||
//
|
||
// Remove this entry and put it on the active list.
|
||
//
|
||
|
||
WriterEntry = MmPagingFile[PageFileNumber]->Entry[i];
|
||
RemoveEntryList (&WriterEntry->Links);
|
||
WriterEntry->CurrentList = &MmPagingFileHeader.ListHead;
|
||
|
||
KeSetEvent (&WriterEntry->PagingListHead->Event, 0, FALSE);
|
||
|
||
InsertTailList (&WriterEntry->PagingListHead->ListHead,
|
||
&WriterEntry->Links);
|
||
MmNumberOfActiveMdlEntries += 1;
|
||
}
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
MiFlushPteList (
|
||
IN PMMPTE_FLUSH_LIST PteFlushList,
|
||
IN ULONG AllProcessors,
|
||
IN MMPTE FillPte
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine flushes all the PTEs in the PTE flush list.
|
||
If the list has overflowed, the entire TB is flushed.
|
||
|
||
Arguments:
|
||
|
||
PteFlushList - Supplies an optional pointer to the list to be flushed.
|
||
|
||
AllProcessors - Supplies TRUE if the flush occurs on all processors.
|
||
|
||
FillPte - Supplies the PTE to fill with.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Environment:
|
||
|
||
Kernel mode, PFN or a pre-process AWE lock may optionally be held.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG count;
|
||
|
||
ASSERT (ARGUMENT_PRESENT (PteFlushList));
|
||
|
||
count = PteFlushList->Count;
|
||
|
||
if (count != 0) {
|
||
if (count != 1) {
|
||
if (count < MM_MAXIMUM_FLUSH_COUNT) {
|
||
KeFlushMultipleTb (count,
|
||
&PteFlushList->FlushVa[0],
|
||
TRUE,
|
||
(BOOLEAN)AllProcessors,
|
||
&((PHARDWARE_PTE)PteFlushList->FlushPte[0]),
|
||
FillPte.u.Flush);
|
||
}
|
||
else {
|
||
|
||
//
|
||
// Array has overflowed, flush the entire TB.
|
||
//
|
||
|
||
KeFlushEntireTb (TRUE, (BOOLEAN)AllProcessors);
|
||
}
|
||
}
|
||
else {
|
||
KeFlushSingleTb (PteFlushList->FlushVa[0],
|
||
TRUE,
|
||
(BOOLEAN)AllProcessors,
|
||
(PHARDWARE_PTE)PteFlushList->FlushPte[0],
|
||
FillPte.u.Flush);
|
||
}
|
||
PteFlushList->Count = 0;
|
||
}
|
||
return;
|
||
}
|