Author: sir_richard
Date: Sun Oct 17 20:02:17 2010
New Revision: 49187
URL:
http://svn.reactos.org/svn/reactos?rev=49187&view=rev
Log:
[NTOS]: Use MI_SET_PFN_DELETED where we missed it.
[NTOS]: Implement support for deleting user-mode pageable VM addresses. Now when cleaning
up the process address space, MiDeleteVirtualAddresses is called for the VADs, so this
will now actually free the PEB/TEB pages that were previously getting leaked for each
thread/process (a known regression I introduced when moving to VADs for PEB/TEB).
Modified:
trunk/reactos/ntoskrnl/mm/ARM3/miarm.h
trunk/reactos/ntoskrnl/mm/ARM3/procsup.c
trunk/reactos/ntoskrnl/mm/ARM3/virtual.c
Modified: trunk/reactos/ntoskrnl/mm/ARM3/miarm.h
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/mm/ARM3/miarm.h?r…
==============================================================================
--- trunk/reactos/ntoskrnl/mm/ARM3/miarm.h [iso-8859-1] (original)
+++ trunk/reactos/ntoskrnl/mm/ARM3/miarm.h [iso-8859-1] Sun Oct 17 20:02:17 2010
@@ -1205,6 +1205,21 @@
IN ULONG Protect
);
+VOID
+NTAPI
+MiDeleteVirtualAddresses(
+ IN ULONG_PTR Va,
+ IN ULONG_PTR EndingAddress,
+ IN PMMVAD Vad
+);
+
+ULONG
+NTAPI
+MiMakeSystemAddressValid(
+ IN PVOID PageTableVirtualAddress,
+ IN PEPROCESS CurrentProcess
+);
+
//
// MiRemoveZeroPage will use inline code to zero out the page manually if only
// free pages are available. In some scenarios, we don't/can't run that piece of
Modified: trunk/reactos/ntoskrnl/mm/ARM3/procsup.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/mm/ARM3/procsup.c…
==============================================================================
--- trunk/reactos/ntoskrnl/mm/ARM3/procsup.c [iso-8859-1] (original)
+++ trunk/reactos/ntoskrnl/mm/ARM3/procsup.c [iso-8859-1] Sun Oct 17 20:02:17 2010
@@ -139,6 +139,8 @@
/* Insert the VAD */
ASSERT(Vad->EndingVpn >= Vad->StartingVpn);
Process->VadRoot.NodeHint = Vad;
+ Vad->ControlArea = NULL; // For Memory-Area hack
+ Vad->FirstPrototypePte = NULL;
MiInsertNode(&Process->VadRoot, (PVOID)Vad, Parent, Result);
/* Release the working set */
@@ -258,7 +260,7 @@
MiDecrementShareCount(Pfn2, PageTableFrameNumber);
#endif
/* Set the special pending delete marker */
- Pfn1->PteAddress = (PMMPTE)((ULONG_PTR)Pfn1->PteAddress | 1);
+ MI_SET_PFN_DELETED(Pfn1);
/* And now delete the actual stack page */
MiDecrementShareCount(Pfn1, PageFrameNumber);
@@ -1174,7 +1176,6 @@
/* Enumerate the VADs */
VadTree = &Process->VadRoot;
- DPRINT("Cleaning up VADs: %d\n", VadTree->NumberGenericTableElements);
while (VadTree->NumberGenericTableElements)
{
/* Grab the current VAD */
@@ -1186,11 +1187,15 @@
/* Remove this VAD from the tree */
ASSERT(VadTree->NumberGenericTableElements >= 1);
MiRemoveNode((PMMADDRESS_NODE)Vad, VadTree);
- DPRINT("Moving on: %d\n", VadTree->NumberGenericTableElements);
/* Only PEB/TEB VADs supported for now */
ASSERT(Vad->u.VadFlags.PrivateMemory == 1);
ASSERT(Vad->u.VadFlags.VadType == VadNone);
+
+ /* Delete the addresses */
+ MiDeleteVirtualAddresses(Vad->StartingVpn << PAGE_SHIFT,
+ (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE -
1),
+ Vad);
/* Release the working set */
MiUnlockProcessWorkingSet(Process, Thread);
Modified: trunk/reactos/ntoskrnl/mm/ARM3/virtual.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/mm/ARM3/virtual.c…
==============================================================================
--- trunk/reactos/ntoskrnl/mm/ARM3/virtual.c [iso-8859-1] (original)
+++ trunk/reactos/ntoskrnl/mm/ARM3/virtual.c [iso-8859-1] Sun Oct 17 20:02:17 2010
@@ -28,6 +28,45 @@
OUT PULONG OldAccessProtection OPTIONAL);
/* PRIVATE FUNCTIONS **********************************************************/
+
+ULONG
+NTAPI
+MiMakeSystemAddressValid(IN PVOID PageTableVirtualAddress,
+ IN PEPROCESS CurrentProcess)
+{
+ NTSTATUS Status;
+ BOOLEAN LockChange = FALSE;
+
+ /* Must be a non-pool page table, since those are double-mapped already */
+ ASSERT(PageTableVirtualAddress > MM_HIGHEST_USER_ADDRESS);
+ ASSERT((PageTableVirtualAddress < MmPagedPoolStart) ||
+ (PageTableVirtualAddress > MmPagedPoolEnd));
+
+ /* Working set lock or PFN lock should be held */
+ ASSERT(KeAreAllApcsDisabled() == TRUE);
+
+ /* Check if the page table is valid */
+ while (!MmIsAddressValid(PageTableVirtualAddress))
+ {
+ /* Fault it in */
+ Status = MmAccessFault(FALSE, PageTableVirtualAddress, KernelMode, NULL);
+ if (!NT_SUCCESS(Status))
+ {
+ /* This should not fail */
+ KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR,
+ 1,
+ Status,
+ (ULONG_PTR)CurrentProcess,
+ (ULONG_PTR)PageTableVirtualAddress);
+ }
+
+ /* This flag will be useful later when we do better locking */
+ LockChange = TRUE;
+ }
+
+ /* Let caller know what the lock state is */
+ return LockChange;
+}
PFN_NUMBER
NTAPI
@@ -126,6 +165,167 @@
return ActualPages;
}
+VOID
+NTAPI
+MiDeletePte(IN PMMPTE PointerPte,
+ IN PVOID VirtualAddress,
+ IN PEPROCESS CurrentProcess)
+{
+ PMMPFN Pfn1;
+ MMPTE PteContents;
+ PFN_NUMBER PageFrameIndex;
+
+ /* PFN lock must be held */
+ ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
+
+ /* Capture the PTE */
+ PteContents = *PointerPte;
+
+ /* We only support valid PTEs for now */
+ ASSERT(PteContents.u.Hard.Valid == 1);
+ ASSERT(PteContents.u.Soft.Prototype == 0);
+ ASSERT(PteContents.u.Soft.Transition == 0);
+
+ /* Get the PFN entry */
+ PageFrameIndex = PFN_FROM_PTE(&PteContents);
+ Pfn1 = MiGetPfnEntry(PageFrameIndex);
+
+ /* We don't support deleting prototype PTEs for now */
+ ASSERT(Pfn1->u3.e1.PrototypePte == 0);
+
+ /* Make sure the saved PTE address is valid */
+ if ((PMMPTE)((ULONG_PTR)Pfn1->PteAddress & ~0x1) != PointerPte)
+ {
+ /* The PFN entry is illegal, or invalid */
+ KeBugCheckEx(MEMORY_MANAGEMENT,
+ 0x401,
+ (ULONG_PTR)PointerPte,
+ PointerPte->u.Long,
+ (ULONG_PTR)Pfn1->PteAddress);
+ }
+
+ /* There should only be 1 shared reference count */
+ ASSERT(Pfn1->u2.ShareCount == 1);
+
+ /* FIXME: Drop the reference on the page table. For now, leak it until RosMM is gone
*/
+ //MiDecrementShareCount(MiGetPfnEntry(Pfn1->u4.PteFrame), Pfn1->u4.PteFrame);
+
+ /* Mark the PFN for deletion and dereference what should be the last ref */
+ MI_SET_PFN_DELETED(Pfn1);
+ MiDecrementShareCount(Pfn1, PageFrameIndex);
+
+ /* We should eventually do this */
+ //CurrentProcess->NumberOfPrivatePages--;
+
+ /* Destroy the PTE and flush the TLB */
+ PointerPte->u.Long = 0;
+ KeFlushCurrentTb();
+}
+
+VOID
+NTAPI
+MiDeleteVirtualAddresses(IN ULONG_PTR Va,
+ IN ULONG_PTR EndingAddress,
+ IN PMMVAD Vad)
+{
+ PMMPTE PointerPte, PointerPde;
+ MMPTE TempPte;
+ PEPROCESS CurrentProcess;
+ KIRQL OldIrql;
+
+ /* Grab the process and PTE/PDE for the address being deleted */
+ CurrentProcess = PsGetCurrentProcess();
+ PointerPde = MiAddressToPde(Va);
+ PointerPte = MiAddressToPte(Va);
+
+ /* We usually only get a VAD when it's not a VM address */
+ if (Vad)
+ {
+ /* At process deletion, we may get a VAD, but it should be a VM VAD */
+ ASSERT(Vad->u.VadFlags.PrivateMemory);
+ ASSERT(Vad->FirstPrototypePte == NULL);
+
+ /* Get out if this is a fake VAD, RosMm will free the marea pages */
+ if (Vad->u.VadFlags.Spare == 1) return;
+ }
+
+ /* In all cases, we don't support fork() yet */
+ ASSERT(CurrentProcess->CloneRoot == NULL);
+
+ /* Loop the PTE for each VA */
+ while (TRUE)
+ {
+ /* First keep going until we find a valid PDE */
+ while (PointerPde->u.Long == 0)
+ {
+ /* Still no valid PDE, try the next 4MB (or whatever) */
+ PointerPde++;
+
+ /* Update the PTE on this new boundary */
+ PointerPte = MiPteToAddress(PointerPde);
+
+ /* Check if all the PDEs are invalid, so there's nothing to free */
+ Va = (ULONG_PTR)MiPteToAddress(PointerPte);
+ if (Va > EndingAddress) return;
+ }
+
+ /* Now check if the PDE is mapped in */
+ if (PointerPde->u.Hard.Valid == 0)
+ {
+ /* It isn't, so map it in */
+ PointerPte = MiPteToAddress(PointerPde);
+ MiMakeSystemAddressValid(PointerPte, CurrentProcess);
+ }
+
+ /* Now we should have a valid PDE, mapped in, and still have some VA */
+ ASSERT(PointerPde->u.Hard.Valid == 1);
+ ASSERT(Va <= EndingAddress);
+
+ /* Lock the PFN Database while we delete the PTEs */
+ OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+ do
+ {
+ /* Capture the PDE and make sure it exists */
+ TempPte = *PointerPte;
+ if (TempPte.u.Long)
+ {
+ /* Check if the PTE is actually mapped in */
+ if (TempPte.u.Long & 0xFFFFFC01)
+ {
+ /* It is, we don't support prototype PTEs for now though */
+ ASSERT(TempPte.u.Soft.Prototype == 0);
+
+ /* Delete the PTE proper */
+ MiDeletePte(PointerPte, (PVOID)Va, CurrentProcess);
+ }
+ else
+ {
+ /* The PTE was never mapped, just nuke it here */
+ PointerPte->u.Long = 0;
+ }
+ }
+
+ /* Update the address and PTE for it */
+ Va += PAGE_SIZE;
+ PointerPte++;
+
+ /* Making sure the PDE is still valid */
+ ASSERT(PointerPde->u.Hard.Valid == 1);
+ }
+ while ((Va & (PDE_MAPPED_VA - 1)) && (Va <= EndingAddress));
+
+ /* The PDE should still be valid at this point */
+ ASSERT(PointerPde->u.Hard.Valid == 1);
+
+ /* Release the lock and get out if we're done */
+ KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+ if (Va > EndingAddress) return;
+
+ /* Otherwise, we exited because we hit a new PDE boundary, so start over */
+ PointerPde = MiAddressToPde(Va);
+ } while (TRUE);
+}
+
LONG
MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo,
OUT PBOOLEAN HaveBadAddress,