mirror of
https://github.com/reactos/reactos.git
synced 2025-05-25 04:03:56 +00:00
[NTOS:MM] Implement proper refcounting of page tables on amd64
CORE-17552
This commit is contained in:
parent
3aa346c21f
commit
c8fb3f7514
8 changed files with 217 additions and 68 deletions
|
@ -248,6 +248,7 @@ MiMapLockedPagesInUserSpace(
|
||||||
|
|
||||||
/* Acquire a share count */
|
/* Acquire a share count */
|
||||||
Pfn1 = MI_PFN_ELEMENT(PointerPde->u.Hard.PageFrameNumber);
|
Pfn1 = MI_PFN_ELEMENT(PointerPde->u.Hard.PageFrameNumber);
|
||||||
|
DPRINT("Incrementing %p from %p\n", Pfn1, _ReturnAddress());
|
||||||
OldIrql = MiAcquirePfnLock();
|
OldIrql = MiAcquirePfnLock();
|
||||||
Pfn1->u2.ShareCount++;
|
Pfn1->u2.ShareCount++;
|
||||||
MiReleasePfnLock(OldIrql);
|
MiReleasePfnLock(OldIrql);
|
||||||
|
@ -330,9 +331,6 @@ MiUnmapLockedPagesInUserSpace(
|
||||||
ASSERT(MiAddressToPte(PointerPte)->u.Hard.Valid == 1);
|
ASSERT(MiAddressToPte(PointerPte)->u.Hard.Valid == 1);
|
||||||
ASSERT(PointerPte->u.Hard.Valid == 1);
|
ASSERT(PointerPte->u.Hard.Valid == 1);
|
||||||
|
|
||||||
/* Dereference the page */
|
|
||||||
MiDecrementPageTableReferences(BaseAddress);
|
|
||||||
|
|
||||||
/* Invalidate it */
|
/* Invalidate it */
|
||||||
MI_ERASE_PTE(PointerPte);
|
MI_ERASE_PTE(PointerPte);
|
||||||
|
|
||||||
|
@ -341,28 +339,17 @@ MiUnmapLockedPagesInUserSpace(
|
||||||
PageTablePage = PointerPde->u.Hard.PageFrameNumber;
|
PageTablePage = PointerPde->u.Hard.PageFrameNumber;
|
||||||
MiDecrementShareCount(MiGetPfnEntry(PageTablePage), PageTablePage);
|
MiDecrementShareCount(MiGetPfnEntry(PageTablePage), PageTablePage);
|
||||||
|
|
||||||
|
if (MiDecrementPageTableReferences(BaseAddress) == 0)
|
||||||
|
{
|
||||||
|
ASSERT(MiIsPteOnPdeBoundary(PointerPte + 1) || (NumberOfPages == 1));
|
||||||
|
MiDeletePde(PointerPde, Process);
|
||||||
|
}
|
||||||
|
|
||||||
/* Next page */
|
/* Next page */
|
||||||
PointerPte++;
|
PointerPte++;
|
||||||
NumberOfPages--;
|
NumberOfPages--;
|
||||||
BaseAddress = (PVOID)((ULONG_PTR)BaseAddress + PAGE_SIZE);
|
BaseAddress = (PVOID)((ULONG_PTR)BaseAddress + PAGE_SIZE);
|
||||||
MdlPages++;
|
MdlPages++;
|
||||||
|
|
||||||
/* Moving to a new PDE? */
|
|
||||||
if (PointerPde != MiAddressToPde(BaseAddress))
|
|
||||||
{
|
|
||||||
/* See if we should delete it */
|
|
||||||
KeFlushProcessTb();
|
|
||||||
PointerPde = MiPteToPde(PointerPte - 1);
|
|
||||||
ASSERT(PointerPde->u.Hard.Valid == 1);
|
|
||||||
if (MiQueryPageTableReferences(BaseAddress) == 0)
|
|
||||||
{
|
|
||||||
ASSERT(PointerPde->u.Long != 0);
|
|
||||||
MiDeletePte(PointerPde,
|
|
||||||
MiPteToAddress(PointerPde),
|
|
||||||
Process,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
KeFlushProcessTb();
|
KeFlushProcessTb();
|
||||||
|
|
|
@ -1823,40 +1823,7 @@ MiReferenceUnusedPageAndBumpLockCount(IN PMMPFN Pfn1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FORCEINLINE
|
|
||||||
VOID
|
|
||||||
MiIncrementPageTableReferences(IN PVOID Address)
|
|
||||||
{
|
|
||||||
PUSHORT RefCount;
|
|
||||||
|
|
||||||
RefCount = &MmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)];
|
|
||||||
|
|
||||||
*RefCount += 1;
|
|
||||||
ASSERT(*RefCount <= PTE_PER_PAGE);
|
|
||||||
}
|
|
||||||
|
|
||||||
FORCEINLINE
|
|
||||||
VOID
|
|
||||||
MiDecrementPageTableReferences(IN PVOID Address)
|
|
||||||
{
|
|
||||||
PUSHORT RefCount;
|
|
||||||
|
|
||||||
RefCount = &MmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)];
|
|
||||||
|
|
||||||
*RefCount -= 1;
|
|
||||||
ASSERT(*RefCount < PTE_PER_PAGE);
|
|
||||||
}
|
|
||||||
|
|
||||||
FORCEINLINE
|
|
||||||
USHORT
|
|
||||||
MiQueryPageTableReferences(IN PVOID Address)
|
|
||||||
{
|
|
||||||
PUSHORT RefCount;
|
|
||||||
|
|
||||||
RefCount = &MmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)];
|
|
||||||
|
|
||||||
return *RefCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
CODE_SEG("INIT")
|
CODE_SEG("INIT")
|
||||||
BOOLEAN
|
BOOLEAN
|
||||||
|
@ -2484,8 +2451,190 @@ MiSynchronizeSystemPde(PMMPDE PointerPde)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if _MI_PAGING_LEVELS == 2
|
||||||
|
FORCEINLINE
|
||||||
|
USHORT
|
||||||
|
MiIncrementPageTableReferences(IN PVOID Address)
|
||||||
|
{
|
||||||
|
PUSHORT RefCount;
|
||||||
|
|
||||||
|
RefCount = &MmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)];
|
||||||
|
|
||||||
|
*RefCount += 1;
|
||||||
|
ASSERT(*RefCount <= PTE_PER_PAGE);
|
||||||
|
return *RefCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
FORCEINLINE
|
||||||
|
USHORT
|
||||||
|
MiDecrementPageTableReferences(IN PVOID Address)
|
||||||
|
{
|
||||||
|
PUSHORT RefCount;
|
||||||
|
|
||||||
|
RefCount = &MmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)];
|
||||||
|
|
||||||
|
*RefCount -= 1;
|
||||||
|
ASSERT(*RefCount < PTE_PER_PAGE);
|
||||||
|
return *RefCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
FORCEINLINE
|
||||||
|
USHORT
|
||||||
|
MiQueryPageTableReferences(IN PVOID Address)
|
||||||
|
{
|
||||||
|
PUSHORT RefCount;
|
||||||
|
|
||||||
|
RefCount = &MmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)];
|
||||||
|
|
||||||
|
return *RefCount;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
FORCEINLINE
|
||||||
|
USHORT
|
||||||
|
MiIncrementPageTableReferences(IN PVOID Address)
|
||||||
|
{
|
||||||
|
PMMPDE PointerPde = MiAddressToPde(Address);
|
||||||
|
PMMPFN Pfn;
|
||||||
|
|
||||||
|
/* We should not tinker with this one. */
|
||||||
|
ASSERT(PointerPde != (PMMPDE)PXE_SELFMAP);
|
||||||
|
DPRINT("Incrementing %p from %p\n", Address, _ReturnAddress());
|
||||||
|
|
||||||
|
/* Make sure we're locked */
|
||||||
|
ASSERT(PsGetCurrentThread()->OwnsProcessWorkingSetExclusive);
|
||||||
|
|
||||||
|
/* If we're bumping refcount, then it must be valid! */
|
||||||
|
ASSERT(PointerPde->u.Hard.Valid == 1);
|
||||||
|
|
||||||
|
/* This lies on the PFN */
|
||||||
|
Pfn = MiGetPfnEntry(PFN_FROM_PDE(PointerPde));
|
||||||
|
Pfn->OriginalPte.u.Soft.UsedPageTableEntries++;
|
||||||
|
|
||||||
|
ASSERT(Pfn->OriginalPte.u.Soft.UsedPageTableEntries <= PTE_PER_PAGE);
|
||||||
|
|
||||||
|
return Pfn->OriginalPte.u.Soft.UsedPageTableEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
FORCEINLINE
|
||||||
|
USHORT
|
||||||
|
MiDecrementPageTableReferences(IN PVOID Address)
|
||||||
|
{
|
||||||
|
PMMPDE PointerPde = MiAddressToPde(Address);
|
||||||
|
PMMPFN Pfn;
|
||||||
|
|
||||||
|
/* We should not tinker with this one. */
|
||||||
|
ASSERT(PointerPde != (PMMPDE)PXE_SELFMAP);
|
||||||
|
|
||||||
|
DPRINT("Decrementing %p from %p\n", PointerPde, _ReturnAddress());
|
||||||
|
|
||||||
|
/* Make sure we're locked */
|
||||||
|
ASSERT(PsGetCurrentThread()->OwnsProcessWorkingSetExclusive);
|
||||||
|
|
||||||
|
/* If we're decreasing refcount, then it must be valid! */
|
||||||
|
ASSERT(PointerPde->u.Hard.Valid == 1);
|
||||||
|
|
||||||
|
/* This lies on the PFN */
|
||||||
|
Pfn = MiGetPfnEntry(PFN_FROM_PDE(PointerPde));
|
||||||
|
|
||||||
|
ASSERT(Pfn->OriginalPte.u.Soft.UsedPageTableEntries != 0);
|
||||||
|
Pfn->OriginalPte.u.Soft.UsedPageTableEntries--;
|
||||||
|
|
||||||
|
ASSERT(Pfn->OriginalPte.u.Soft.UsedPageTableEntries < PTE_PER_PAGE);
|
||||||
|
|
||||||
|
return Pfn->OriginalPte.u.Soft.UsedPageTableEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
FORCEINLINE
|
||||||
|
USHORT
|
||||||
|
MiQueryPageTableReferences(IN PVOID Address)
|
||||||
|
{
|
||||||
|
PMMPDE PointerPde;
|
||||||
|
PMMPPE PointerPpe;
|
||||||
|
#if _MI_PAGING_LEVELS == 4
|
||||||
|
PMMPXE PointerPxe;
|
||||||
|
#endif
|
||||||
|
PMMPFN Pfn;
|
||||||
|
|
||||||
|
/* Make sure we're locked */
|
||||||
|
ASSERT((PsGetCurrentThread()->OwnsProcessWorkingSetExclusive) || (PsGetCurrentThread()->OwnsProcessWorkingSetShared));
|
||||||
|
|
||||||
|
/* Check if PXE or PPE have references first. */
|
||||||
|
#if _MI_PAGING_LEVELS == 4
|
||||||
|
PointerPxe = MiAddressToPxe(Address);
|
||||||
|
if ((PointerPxe->u.Hard.Valid == 1) || (PointerPxe->u.Soft.Transition == 1))
|
||||||
|
{
|
||||||
|
Pfn = MiGetPfnEntry(PFN_FROM_PXE(PointerPxe));
|
||||||
|
if (Pfn->OriginalPte.u.Soft.UsedPageTableEntries == 0)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if (PointerPxe->u.Soft.UsedPageTableEntries == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PointerPxe->u.Hard.Valid == 0)
|
||||||
|
{
|
||||||
|
MiMakeSystemAddressValid(MiPteToAddress(PointerPxe), PsGetCurrentProcess());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PointerPpe = MiAddressToPpe(Address);
|
||||||
|
if ((PointerPpe->u.Hard.Valid == 1) || (PointerPpe->u.Soft.Transition == 1))
|
||||||
|
{
|
||||||
|
Pfn = MiGetPfnEntry(PFN_FROM_PPE(PointerPpe));
|
||||||
|
if (Pfn->OriginalPte.u.Soft.UsedPageTableEntries == 0)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if (PointerPpe->u.Soft.UsedPageTableEntries == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PointerPpe->u.Hard.Valid == 0)
|
||||||
|
{
|
||||||
|
MiMakeSystemAddressValid(MiPteToAddress(PointerPpe), PsGetCurrentProcess());
|
||||||
|
}
|
||||||
|
|
||||||
|
PointerPde = MiAddressToPde(Address);
|
||||||
|
if ((PointerPde->u.Hard.Valid == 0) && (PointerPde->u.Soft.Transition == 0))
|
||||||
|
{
|
||||||
|
return PointerPde->u.Soft.UsedPageTableEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This lies on the PFN */
|
||||||
|
Pfn = MiGetPfnEntry(PFN_FROM_PDE(PointerPde));
|
||||||
|
return Pfn->OriginalPte.u.Soft.UsedPageTableEntries;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
FORCEINLINE
|
||||||
|
VOID
|
||||||
|
MiDeletePde(
|
||||||
|
_In_ PMMPDE PointerPde,
|
||||||
|
_In_ PEPROCESS CurrentProcess)
|
||||||
|
{
|
||||||
|
/* Only for user-mode ones */
|
||||||
|
ASSERT(MiIsUserPde(PointerPde));
|
||||||
|
|
||||||
|
/* Kill this one as a PTE */
|
||||||
|
MiDeletePte((PMMPTE)PointerPde, MiPdeToPte(PointerPde), CurrentProcess, NULL);
|
||||||
|
#if _MI_PAGING_LEVELS >= 3
|
||||||
|
/* Cascade down */
|
||||||
|
if (MiDecrementPageTableReferences(MiPdeToPte(PointerPde)) == 0)
|
||||||
|
{
|
||||||
|
MiDeletePte(MiPdeToPpe(PointerPde), PointerPde, CurrentProcess, NULL);
|
||||||
|
#if _MI_PAGING_LEVELS == 4
|
||||||
|
if (MiDecrementPageTableReferences(PointerPde) == 0)
|
||||||
|
{
|
||||||
|
MiDeletePte(MiPdeToPxe(PointerPde), MiPdeToPpe(PointerPde), CurrentProcess, NULL);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/* EOF */
|
/* EOF */
|
||||||
|
|
|
@ -2145,6 +2145,7 @@ UserFault:
|
||||||
|
|
||||||
/* We should come back with a valid PPE */
|
/* We should come back with a valid PPE */
|
||||||
ASSERT(PointerPpe->u.Hard.Valid == 1);
|
ASSERT(PointerPpe->u.Hard.Valid == 1);
|
||||||
|
MiIncrementPageTableReferences(PointerPde);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -2184,6 +2185,10 @@ UserFault:
|
||||||
MM_EXECUTE_READWRITE,
|
MM_EXECUTE_READWRITE,
|
||||||
CurrentProcess,
|
CurrentProcess,
|
||||||
MM_NOIRQL);
|
MM_NOIRQL);
|
||||||
|
#if _MI_PAGING_LEVELS >= 3
|
||||||
|
MiIncrementPageTableReferences(PointerPte);
|
||||||
|
#endif
|
||||||
|
|
||||||
#if MI_TRACE_PFNS
|
#if MI_TRACE_PFNS
|
||||||
UserPdeFault = FALSE;
|
UserPdeFault = FALSE;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1027,6 +1027,8 @@ MiInitializePfn(IN PFN_NUMBER PageFrameIndex,
|
||||||
ASSERT(PageFrameIndex != 0);
|
ASSERT(PageFrameIndex != 0);
|
||||||
Pfn1->u4.PteFrame = PageFrameIndex;
|
Pfn1->u4.PteFrame = PageFrameIndex;
|
||||||
|
|
||||||
|
DPRINT("Incrementing share count of %lp from %p\n", PageFrameIndex, _ReturnAddress());
|
||||||
|
|
||||||
/* Increase its share count so we don't get rid of it */
|
/* Increase its share count so we don't get rid of it */
|
||||||
Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
|
Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
|
||||||
Pfn1->u2.ShareCount++;
|
Pfn1->u2.ShareCount++;
|
||||||
|
|
|
@ -477,7 +477,7 @@ MiSessionInitializeWorkingSetList(VOID)
|
||||||
|
|
||||||
/* Fill out the two pointers */
|
/* Fill out the two pointers */
|
||||||
MmSessionSpace->Vm.VmWorkingSetList = WorkingSetList;
|
MmSessionSpace->Vm.VmWorkingSetList = WorkingSetList;
|
||||||
MmSessionSpace->Wsle = (PMMWSLE)WorkingSetList->UsedPageTableEntries;
|
MmSessionSpace->Wsle = (PMMWSLE)((&WorkingSetList->VadBitMapHint) + 1);
|
||||||
|
|
||||||
/* Get the PDE for the working set, and check if it's already allocated */
|
/* Get the PDE for the working set, and check if it's already allocated */
|
||||||
PointerPde = MiAddressToPde(WorkingSetList);
|
PointerPde = MiAddressToPde(WorkingSetList);
|
||||||
|
|
|
@ -727,18 +727,15 @@ MiDeleteVirtualAddresses(IN ULONG_PTR Va,
|
||||||
/* Check remaining PTE count (go back 1 page due to above loop) */
|
/* Check remaining PTE count (go back 1 page due to above loop) */
|
||||||
if (MiQueryPageTableReferences((PVOID)(Va - PAGE_SIZE)) == 0)
|
if (MiQueryPageTableReferences((PVOID)(Va - PAGE_SIZE)) == 0)
|
||||||
{
|
{
|
||||||
if (PointerPde->u.Long != 0)
|
ASSERT(PointerPde->u.Long != 0);
|
||||||
{
|
|
||||||
/* Delete the PTE proper */
|
/* Delete the PDE proper */
|
||||||
MiDeletePte(PointerPde,
|
MiDeletePde(PointerPde, CurrentProcess);
|
||||||
MiPteToAddress(PointerPde),
|
|
||||||
CurrentProcess,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Release the lock and get out if we're done */
|
/* Release the lock */
|
||||||
MiReleasePfnLock(OldIrql);
|
MiReleasePfnLock(OldIrql);
|
||||||
|
|
||||||
if (Va > EndingAddress) return;
|
if (Va > EndingAddress) return;
|
||||||
|
|
||||||
/* Otherwise, we exited because we hit a new PDE boundary, so start over */
|
/* Otherwise, we exited because we hit a new PDE boundary, so start over */
|
||||||
|
|
|
@ -238,11 +238,10 @@ MmDeleteVirtualMapping(PEPROCESS Process, PVOID Address,
|
||||||
if (Address < MmSystemRangeStart)
|
if (Address < MmSystemRangeStart)
|
||||||
{
|
{
|
||||||
/* Remove PDE reference */
|
/* Remove PDE reference */
|
||||||
MiDecrementPageTableReferences(Address);
|
if (MiDecrementPageTableReferences(Address) == 0)
|
||||||
if (MiQueryPageTableReferences(Address) == 0)
|
|
||||||
{
|
{
|
||||||
KIRQL OldIrql = MiAcquirePfnLock();
|
KIRQL OldIrql = MiAcquirePfnLock();
|
||||||
MiDeletePte(MiAddressToPte(PointerPte), PointerPte, Process, NULL);
|
MiDeletePde(MiAddressToPde(Address), Process);
|
||||||
MiReleasePfnLock(OldIrql);
|
MiReleasePfnLock(OldIrql);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,8 +292,7 @@ MmDeletePageFileMapping(
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This used to be a non-zero PTE, now we can let the PDE go. */
|
/* This used to be a non-zero PTE, now we can let the PDE go. */
|
||||||
MiDecrementPageTableReferences(Address);
|
if (MiDecrementPageTableReferences(Address) == 0)
|
||||||
if (MiQueryPageTableReferences(Address) == 0)
|
|
||||||
{
|
{
|
||||||
/* We can let it go */
|
/* We can let it go */
|
||||||
KIRQL OldIrql = MiAcquirePfnLock();
|
KIRQL OldIrql = MiAcquirePfnLock();
|
||||||
|
|
|
@ -879,8 +879,19 @@ typedef struct _MMWSL
|
||||||
PVOID HighestPermittedHashAddress;
|
PVOID HighestPermittedHashAddress;
|
||||||
ULONG NumberOfImageWaiters;
|
ULONG NumberOfImageWaiters;
|
||||||
ULONG VadBitMapHint;
|
ULONG VadBitMapHint;
|
||||||
|
#ifndef _M_AMD64
|
||||||
USHORT UsedPageTableEntries[768];
|
USHORT UsedPageTableEntries[768];
|
||||||
ULONG CommittedPageTables[24];
|
ULONG CommittedPageTables[24];
|
||||||
|
#else
|
||||||
|
VOID* HighestUserAddress;
|
||||||
|
ULONG MaximumUserPageTablePages;
|
||||||
|
ULONG MaximumUserPageDirectoryPages;
|
||||||
|
ULONG* CommittedPageTables;
|
||||||
|
ULONG NumberOfCommittedPageDirectories;
|
||||||
|
ULONG* CommittedPageDirectories;
|
||||||
|
ULONG NumberOfCommittedPageDirectoryParents;
|
||||||
|
ULONGLONG CommittedPageDirectoryParents[1];
|
||||||
|
#endif
|
||||||
} MMWSL, *PMMWSL;
|
} MMWSL, *PMMWSL;
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
Loading…
Reference in a new issue