https://git.reactos.org/?p=reactos.git;a=commitdiff;h=4c5351bf55527a35bce09…
commit 4c5351bf55527a35bce09446420dd7296eedbe99
Author:     Jérôme Gardou <jerome.gardou(a)reactos.org>
AuthorDate: Fri Oct 16 15:27:07 2020 +0200
Commit:     Jérôme Gardou <jerome.gardou(a)reactos.org>
CommitDate: Tue Oct 20 15:20:59 2020 +0200
    [NTOS/MM]
     - Fix PFNs tracing
     - Add private pages to the process working set
---
 ntoskrnl/cc/view.c             |  20 ----
 ntoskrnl/include/internal/mm.h |  44 ++++++++-
 ntoskrnl/mm/ARM3/miarm.h       |   4 +
 ntoskrnl/mm/ARM3/mminit.c      |   4 +-
 ntoskrnl/mm/ARM3/pagfault.c    |  69 +++++++++++---
 ntoskrnl/mm/ARM3/pfnlist.c     |  13 ++-
 ntoskrnl/mm/ARM3/procsup.c     | 212 ++++++++++++++++++++++++++++++++++++++++-
 ntoskrnl/mm/ARM3/sysldr.c      |   9 +-
 ntoskrnl/mm/ARM3/virtual.c     |  40 ++++----
 ntoskrnl/mm/freelist.c         |  15 +++
 ntoskrnl/mm/i386/page.c        |   6 ++
 ntoskrnl/mm/marea.c            |   5 +-
 ntoskrnl/mm/section.c          |  11 ---
 13 files changed, 364 insertions(+), 88 deletions(-)
diff --git a/ntoskrnl/cc/view.c b/ntoskrnl/cc/view.c
index fde2bbc926a..c576997f943 100644
--- a/ntoskrnl/cc/view.c
+++ b/ntoskrnl/cc/view.c
@@ -665,7 +665,6 @@ CcRosMapVacbInKernelSpace(
     {
         PFN_NUMBER PageFrameNumber;
-        MI_SET_USAGE(MI_USAGE_CACHE);
         Status = MmRequestPageMemoryConsumer(MC_CACHE, TRUE, &PageFrameNumber);
         if (PageFrameNumber == 0)
         {
@@ -907,25 +906,6 @@ Retry:
     InsertTailList(&VacbLruListHead, ¤t->VacbLruListEntry);
     KeReleaseQueuedSpinLock(LockQueueMasterLock, oldIrql);
-    MI_SET_USAGE(MI_USAGE_CACHE);
-#if MI_TRACE_PFNS
-    if ((SharedCacheMap->FileObject) &&
(SharedCacheMap->FileObject->FileName.Buffer))
-    {
-        PWCHAR pos;
-        ULONG len = 0;
-        pos = wcsrchr(SharedCacheMap->FileObject->FileName.Buffer, '\\');
-        if (pos)
-        {
-            len = wcslen(pos) * sizeof(WCHAR);
-            snprintf(MI_PFN_CURRENT_PROCESS_NAME, min(16, len), "%S", pos);
-        }
-        else
-        {
-            snprintf(MI_PFN_CURRENT_PROCESS_NAME, min(16, len), "%wZ",
&SharedCacheMap->FileObject->FileName);
-        }
-    }
-#endif
-
     /* Reference it to allow release */
     CcRosVacbIncRefCount(current);
diff --git a/ntoskrnl/include/internal/mm.h b/ntoskrnl/include/internal/mm.h
index 32fd7b1146d..d2ead92df1f 100644
--- a/ntoskrnl/include/internal/mm.h
+++ b/ntoskrnl/include/internal/mm.h
@@ -2,6 +2,8 @@
 #include <internal/arch/mm.h>
+#define MI_TRACE_PFNS 1
+
 /* TYPES *********************************************************************/
 struct _EPROCESS;
@@ -248,9 +250,45 @@ MM_RMAP_ENTRY, *PMM_RMAP_ENTRY;
 extern ULONG MI_PFN_CURRENT_USAGE;
 extern CHAR MI_PFN_CURRENT_PROCESS_NAME[16];
 #define MI_SET_USAGE(x)     MI_PFN_CURRENT_USAGE = x
-#define MI_SET_PROCESS2(x)  memcpy(MI_PFN_CURRENT_PROCESS_NAME, x, 16)
+#define MI_SET_PROCESS2(x)  memcpy(MI_PFN_CURRENT_PROCESS_NAME, x, min(sizeof(x),
sizeof(MI_PFN_CURRENT_PROCESS_NAME)))
+FORCEINLINE
+void
+MI_SET_PROCESS(PEPROCESS Process)
+{
+    if (!Process)
+        MI_SET_PROCESS2("Kernel");
+    else if (Process == (PEPROCESS)1)
+        MI_SET_PROCESS2("Hydra");
+    else
+        MI_SET_PROCESS2(Process->ImageFileName);
+}
+
+FORCEINLINE
+void
+MI_SET_PROCESS_USTR(PUNICODE_STRING ustr)
+{
+    PWSTR pos, strEnd;
+    int i;
+
+    if (!ustr->Buffer || ustr->Length == 0)
+    {
+        MI_PFN_CURRENT_PROCESS_NAME[0] = 0;
+        return;
+    }
+
+    pos = strEnd = &ustr->Buffer[ustr->Length / sizeof(WCHAR)];
+    while ((*pos != L'\\') && (pos >  ustr->Buffer))
+        pos--;
+
+    if (*pos == L'\\')
+        pos++;
+
+    for (i = 0; i < sizeof(MI_PFN_CURRENT_PROCESS_NAME) && pos <= strEnd;
i++, pos++)
+        MI_PFN_CURRENT_PROCESS_NAME[i] = (CHAR)*pos;
+}
 #else
 #define MI_SET_USAGE(x)
+#define MI_SET_PROCESS(x)
 #define MI_SET_PROCESS2(x)
 #endif
@@ -278,6 +316,9 @@ typedef enum _MI_PFN_USAGES
     MI_USAGE_PFN_DATABASE,
     MI_USAGE_BOOT_DRIVER,
     MI_USAGE_INIT_MEMORY,
+    MI_USAGE_PAGE_FILE,
+    MI_USAGE_COW,
+    MI_USAGE_WSLE,
     MI_USAGE_FREE_PAGE
 } MI_PFN_USAGES;
@@ -358,6 +399,7 @@ typedef struct _MMPFN
 #if MI_TRACE_PFNS
     MI_PFN_USAGES PfnUsage;
     CHAR ProcessName[16];
+#define MI_SET_PFN_PROCESS_NAME(pfn, x) memcpy(pfn->ProcessName, x, min(sizeof(x),
sizeof(pfn->ProcessName)))
 #endif
     // HACK until WS lists are supported
diff --git a/ntoskrnl/mm/ARM3/miarm.h b/ntoskrnl/mm/ARM3/miarm.h
index 6df6b7e5385..a30e9957874 100644
--- a/ntoskrnl/mm/ARM3/miarm.h
+++ b/ntoskrnl/mm/ARM3/miarm.h
@@ -1058,6 +1058,10 @@ VOID
 NTAPI
 MiInsertInWorkingSetList(_Inout_ PMMSUPPORT Vm, _In_ PVOID Address, _In_ ULONG
Protection);
+VOID
+NTAPI
+MiRemoveFromWorkingSetList(_Inout_ PMMSUPPORT Vm, _In_ PVOID Address);
+
 //
 // New ARM3<->RosMM PAGE Architecture
 //
diff --git a/ntoskrnl/mm/ARM3/mminit.c b/ntoskrnl/mm/ARM3/mminit.c
index 0bae38e3e02..3c724d8ea3e 100644
--- a/ntoskrnl/mm/ARM3/mminit.c
+++ b/ntoskrnl/mm/ARM3/mminit.c
@@ -801,7 +801,7 @@ MiBuildPfnDatabaseFromPages(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
                 Pfn1->u3.e1.CacheAttribute = MiNonCached;
 #if MI_TRACE_PFNS
                 Pfn1->PfnUsage = MI_USAGE_INIT_MEMORY;
-                memcpy(Pfn1->ProcessName, "Initial PDE", 16);
+                MI_SET_PFN_PROCESS_NAME(Pfn1, "Initial PDE");
 #endif
             }
             else
@@ -848,7 +848,7 @@ MiBuildPfnDatabaseFromPages(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
                                 Pfn2->u3.e1.CacheAttribute = MiNonCached;
 #if MI_TRACE_PFNS
                                 Pfn2->PfnUsage = MI_USAGE_INIT_MEMORY;
-                                memcpy(Pfn1->ProcessName, "Initial PTE",
16);
+                                MI_SET_PFN_PROCESS_NAME(Pfn2, "Initial PTE");
 #endif
                             }
                         }
diff --git a/ntoskrnl/mm/ARM3/pagfault.c b/ntoskrnl/mm/ARM3/pagfault.c
index 1ab109e9f65..a487d3a1ba2 100644
--- a/ntoskrnl/mm/ARM3/pagfault.c
+++ b/ntoskrnl/mm/ARM3/pagfault.c
@@ -697,16 +697,6 @@ MiResolveDemandZeroFault(IN PVOID Address,
     /* Increment demand zero faults */
     KeGetCurrentPrcb()->MmDemandZeroCount++;
-    /* Do we have the lock? */
-    if (HaveLock)
-    {
-        /* Release it */
-        MiReleasePfnLock(OldIrql);
-
-        /* Update performance counters */
-        if (Process > HYDRA_PROCESS) Process->NumberOfPrivatePages++;
-    }
-
     /* Zero the page if need be */
     if (NeedZero) MiZeroPfn(PageFrameNumber);
@@ -745,6 +735,23 @@ MiResolveDemandZeroFault(IN PVOID Address,
         ASSERT(Pfn1->u3.e1.PrototypePte == 0);
     }
+    /* Add the page to our working set, if it's not a proto PTE */
+    if ((Process > HYDRA_PROCESS) && (PointerPte == MiAddressToPte(Address)))
+    {
+        /* FIXME: Also support session VM scenario */
+        MiInsertInWorkingSetList(&Process->Vm, Address, Protection);
+    }
+
+    /* Do we have the lock? */
+    if (HaveLock)
+    {
+        /* Release it */
+        MiReleasePfnLock(OldIrql);
+
+        /* Update performance counters */
+        if (Process > HYDRA_PROCESS) Process->NumberOfPrivatePages++;
+    }
+
     //
     // It's all good now
     //
@@ -899,6 +906,9 @@ MiResolvePageFileFault(_In_ BOOLEAN StoreInstruction,
     ASSERT(CurrentProcess > HYDRA_PROCESS);
     ASSERT(*OldIrql != MM_NOIRQL);
+    MI_SET_USAGE(MI_USAGE_PAGE_FILE);
+    MI_SET_PROCESS(CurrentProcess);
+
     /* We must hold the PFN lock */
     MI_ASSERT_PFN_LOCK_HELD();
@@ -959,6 +969,9 @@ MiResolvePageFileFault(_In_ BOOLEAN StoreInstruction,
         KeSetEvent(Pfn1->u1.Event, IO_NO_INCREMENT, FALSE);
     }
+    /* And we can insert this into the working set */
+    MiInsertInWorkingSetList(&CurrentProcess->Vm, FaultingAddress, Protection);
+
     return Status;
 }
@@ -976,6 +989,8 @@ MiResolveTransitionFault(IN BOOLEAN StoreInstruction,
     PMMPFN Pfn1;
     MMPTE TempPte;
     PMMPTE PointerToPteForProtoPage;
+    ULONG Protection;
+
     DPRINT("Transition fault on 0x%p with PTE 0x%p in process %s\n",
             FaultingAddress, PointerPte, CurrentProcess->ImageFileName);
@@ -1069,8 +1084,9 @@ MiResolveTransitionFault(IN BOOLEAN StoreInstruction,
     ASSERT(PointerPte->u.Hard.Valid == 0);
     ASSERT(PointerPte->u.Trans.Prototype == 0);
     ASSERT(PointerPte->u.Trans.Transition == 1);
+    Protection = TempPte.u.Trans.Protection;
     TempPte.u.Long = (PointerPte->u.Long & ~0xFFF) |
-                     (MmProtectToPteMask[PointerPte->u.Trans.Protection]) |
+                     (MmProtectToPteMask[Protection]) |
                      MiDetermineUserGlobalPteMask(PointerPte);
     /* Is the PTE writeable? */
@@ -1090,6 +1106,10 @@ MiResolveTransitionFault(IN BOOLEAN StoreInstruction,
     /* Write the valid PTE */
     MI_WRITE_VALID_PTE(PointerPte, TempPte);
+    /* If this was a user fault, add it to the working set */
+    if (CurrentProcess > HYDRA_PROCESS)
+        MiInsertInWorkingSetList(&CurrentProcess->Vm, FaultingAddress,
Protection);
+
     /* Return success */
     return STATUS_PAGE_FAULT_TRANSITION;
 }
@@ -1210,6 +1230,9 @@ MiResolveProtoPteFault(IN BOOLEAN StoreInstruction,
         ASSERT(TempPte.u.Hard.Valid == 1);
         ProtoPageFrameIndex = PFN_FROM_PTE(&TempPte);
+        MI_SET_USAGE(MI_USAGE_COW);
+        MI_SET_PROCESS(Process);
+
         /* Get a new page for the private copy */
         if (Process > HYDRA_PROCESS)
             Color = MI_GET_NEXT_PROCESS_COLOR(Process);
@@ -1245,6 +1268,13 @@ MiResolveProtoPteFault(IN BOOLEAN StoreInstruction,
         /* And finally, write the valid PTE */
         MI_WRITE_VALID_PTE(PointerPte, PteContents);
+        /* Add the page to our working set */
+        if (Process > HYDRA_PROCESS)
+        {
+            /* FIXME: Also support session VM scenario */
+            MiInsertInWorkingSetList(&Process->Vm, Address, Protection);
+        }
+
         /* The caller expects us to release the PFN lock */
         MiReleasePfnLock(OldIrql);
         return Status;
@@ -2205,11 +2235,15 @@ UserFault:
             {
                 PFN_NUMBER PageFrameIndex, OldPageFrameIndex;
                 PMMPFN Pfn1;
+                ProtectionCode = TempPte.u.Soft.Protection;
                 LockIrql = MiAcquirePfnLock();
                 ASSERT(MmAvailablePages > 0);
+                MI_SET_USAGE(MI_USAGE_COW);
+                MI_SET_PROCESS(CurrentProcess);
+
                 /* Allocate a new page and copy it */
                 PageFrameIndex =
MiRemoveAnyPage(MI_GET_NEXT_PROCESS_COLOR(CurrentProcess));
                 OldPageFrameIndex = PFN_FROM_PTE(&TempPte);
@@ -2231,6 +2265,9 @@ UserFault:
                 MI_WRITE_VALID_PTE(PointerPte, TempPte);
+                /* We can now add it to our working set */
+                MiInsertInWorkingSetList(&CurrentProcess->Vm, Address,
ProtectionCode);
+
                 MiReleasePfnLock(LockIrql);
                 /* Return the status */
@@ -2347,6 +2384,7 @@ UserFault:
                 TempPte.u.Soft.Protection = ProtectionCode;
                 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
             }
+            ProtectionCode = PointerPte->u.Soft.Protection;
             /* Lock the PFN database since we're going to grab a page */
             OldIrql = MiAcquirePfnLock();
@@ -2384,9 +2422,6 @@ UserFault:
             /* One more demand-zero fault */
             KeGetCurrentPrcb()->MmDemandZeroCount++;
-            /* And we're done with the lock */
-            MiReleasePfnLock(OldIrql);
-
             /* Fault on user PDE, or fault on user PTE? */
             if (PointerPte <= MiHighestUserPte)
             {
@@ -2413,6 +2448,12 @@ UserFault:
             Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
             ASSERT(Pfn1->u1.Event == NULL);
+            /* We can now insert it into the working set */
+            MiInsertInWorkingSetList(&CurrentProcess->Vm, Address,
ProtectionCode);
+
+            /* And we're done with the lock */
+            MiReleasePfnLock(OldIrql);
+
             /* Demand zero */
             ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
             MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
diff --git a/ntoskrnl/mm/ARM3/pfnlist.c b/ntoskrnl/mm/ARM3/pfnlist.c
index b039a5181a2..866a29cd15f 100644
--- a/ntoskrnl/mm/ARM3/pfnlist.c
+++ b/ntoskrnl/mm/ARM3/pfnlist.c
@@ -254,8 +254,8 @@ MiUnlinkFreeOrZeroedPage(IN PMMPFN Entry)
     ASSERT(MI_PFN_CURRENT_USAGE != MI_USAGE_NOT_SET);
     Entry->PfnUsage = MI_PFN_CURRENT_USAGE;
     memcpy(Entry->ProcessName, MI_PFN_CURRENT_PROCESS_NAME, 16);
-//    MI_PFN_CURRENT_USAGE = MI_USAGE_NOT_SET;
-//    memcpy(MI_PFN_CURRENT_PROCESS_NAME, "Not Set", 16);
+    MI_PFN_CURRENT_USAGE = MI_USAGE_NOT_SET;
+    MI_SET_PROCESS2("Not Set");
 #endif
 }
@@ -459,11 +459,11 @@ MiRemovePageByColor(IN PFN_NUMBER PageIndex,
     MiDecrementAvailablePages();
 #if MI_TRACE_PFNS
-    //ASSERT(MI_PFN_CURRENT_USAGE != MI_USAGE_NOT_SET);
+    ASSERT(MI_PFN_CURRENT_USAGE != MI_USAGE_NOT_SET);
     Pfn1->PfnUsage = MI_PFN_CURRENT_USAGE;
     memcpy(Pfn1->ProcessName, MI_PFN_CURRENT_PROCESS_NAME, 16);
-    //MI_PFN_CURRENT_USAGE = MI_USAGE_NOT_SET;
-    //memcpy(MI_PFN_CURRENT_PROCESS_NAME, "Not Set", 16);
+    MI_PFN_CURRENT_USAGE = MI_USAGE_NOT_SET;
+    MI_SET_PROCESS2("Not Set");
 #endif
     /* Return the page */
@@ -937,9 +937,8 @@ MiInsertPageInList(IN PMMPFNLIST ListHead,
         ColorHead->Count++;
 #if MI_TRACE_PFNS
-            //ASSERT(MI_PFN_CURRENT_USAGE == MI_USAGE_NOT_SET);
+            ASSERT(MI_PFN_CURRENT_USAGE == MI_USAGE_NOT_SET);
             Pfn1->PfnUsage = MI_USAGE_FREE_PAGE;
-            MI_PFN_CURRENT_USAGE = MI_USAGE_NOT_SET;
             RtlZeroMemory(Pfn1->ProcessName, 16);
 #endif
     }
diff --git a/ntoskrnl/mm/ARM3/procsup.c b/ntoskrnl/mm/ARM3/procsup.c
index ab01d902686..2e4d9029d36 100644
--- a/ntoskrnl/mm/ARM3/procsup.c
+++ b/ntoskrnl/mm/ARM3/procsup.c
@@ -6,6 +6,8 @@
  * PROGRAMMERS:     ReactOS Portable Systems Group
  */
+#define GROW_WSLE 1
+
 /* INCLUDES *******************************************************************/
 #include <ntoskrnl.h>
@@ -862,18 +864,34 @@ MiGetFirstFreeWsleIndex(_Inout_ PMMSUPPORT Vm)
         if (WsList->LastEntry == WsList->LastInitializedWsle)
         {
             /* We must grow our array. Allocate a new page */
-            PMMPTE PointerPte =
MiAddressToPte(&WsList->Wsle[WsList->LastInitializedWsle + 1]);
+            PVOID Address = &WsList->Wsle[WsList->LastInitializedWsle + 1];
+            PMMPTE PointerPte = MiAddressToPte(Address);
             MMPTE TempPte;
+
+            MI_SET_USAGE(MI_USAGE_WSLE);
+            MI_SET_PROCESS(PsGetCurrentProcess());
+
+            /* We must be at page boundary */
+            ASSERT(Address == ALIGN_DOWN_POINTER_BY(Address, PAGE_SIZE));
+
             PFN_NUMBER PageFrameIndex = MiRemoveAnyPage(MI_GET_NEXT_COLOR());
             MiInitializePfn(PageFrameIndex, PointerPte, TRUE);
-            MI_MAKE_HARDWARE_PTE_KERNEL(&TempPte, PointerPte, MM_READWRITE,
PageFrameIndex);
+
+            TempPte = ValidKernelPteLocal;
+            TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
             MI_WRITE_VALID_PTE(PointerPte, TempPte);
             WsList->LastInitializedWsle += PAGE_SIZE / sizeof(MMWSLE);
+            /* Make sure we are staying on the same page */
+            ASSERT(Address ==
ALIGN_DOWN_POINTER_BY(&WsList->Wsle[WsList->LastInitializedWsle], PAGE_SIZE));
+
             /* We must insert this page in our working set ! */
             MiInsertInWorkingSetList(Vm,
&WsList->Wsle[WsList->LastInitializedWsle], MM_READWRITE);
+
+            /* Now the last entry is the tail of our WSLE array */
+            ASSERT(WsList->Wsle[WsList->LastEntry].u1.e1.VirtualPageNumber ==
((ULONG_PTR)&WsList->Wsle[WsList->LastInitializedWsle]) >> PAGE_SHIFT);
         }
         /* At this point we must be good to go */
@@ -909,18 +927,26 @@ MiGetFirstFreeWsleIndex(_Inout_ PMMSUPPORT Vm)
 VOID
 NTAPI
-MiInsertInWorkingSetList(_Inout_ PMMSUPPORT Vm, _In_ PVOID Address, _In_ ULONG
Protection)
+MiInsertInWorkingSetList(
+    _Inout_ PMMSUPPORT Vm,
+    _In_ PVOID Address,
+    _In_ ULONG Protection)
 {
     ULONG WsIndex = MiGetFirstFreeWsleIndex(Vm);
     PMMWSLE WsleEntry = &Vm->VmWorkingSetList->Wsle[WsIndex];
     PMMPTE PointerPte = MiAddressToPte(Address);
-    PMMPFN Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
+    PMMPFN Pfn1;
     /* Make sure we got a rounded address */
     Address = ALIGN_DOWN_POINTER_BY(Address, PAGE_SIZE);
-    /* Make sure we are locking the right thing */
+    /* Make sure we are locking the right things */
     ASSERT(MM_ANY_WS_LOCK_HELD(PsGetCurrentThread()));
+    MI_ASSERT_PFN_LOCK_HELD();
+
+    /* Make sure we are adding a paged-in address */
+    ASSERT(PointerPte->u.Hard.Valid == 1);
+    Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
     /* The Pfn must be an active one */
     ASSERT(Pfn1->u3.e1.PageLocation == ActiveAndValid);
@@ -931,6 +957,11 @@ MiInsertInWorkingSetList(_Inout_ PMMSUPPORT Vm, _In_ PVOID Address,
_In_ ULONG P
     /* Shared pages not supported yet */
     ASSERT(Pfn1->u1.WsIndex == 0);
+    ASSERT(Pfn1->u3.e1.PrototypePte == 0);
+
+    /* Nor are "ROS PFN" */
+    ASSERT(MI_IS_ROS_PFN(Pfn1) == FALSE);
+
     WsleEntry->u1.e1.Direct = 1;
     Pfn1->u1.WsIndex = WsIndex;
@@ -941,6 +972,177 @@ MiInsertInWorkingSetList(_Inout_ PMMSUPPORT Vm, _In_ PVOID Address,
_In_ ULONG P
         Vm->PeakWorkingSetSize = Vm->WorkingSetSize;
 }
+static
+void
+MiShrinkWorkingSet(_Inout_ PMMSUPPORT Vm)
+{
+    PMMWSL WsList = Vm->VmWorkingSetList;
+    ULONG LastValid = WsList->LastEntry;
+
+    while(WsList->Wsle[LastValid].u1.e1.Valid == 0)
+    {
+        LastValid--;
+    }
+
+    if (LastValid != WsList->LastEntry)
+    {
+        /* There was a hole behind us. Handle this */
+        PMMWSLE NextFree = &WsList->Wsle[LastValid + 1];
+        if (NextFree->u1.Free.PreviousFree == MMWSLE_PREVIOUS_FREE_INVALID)
+        {
+            /* This was actually our first free entry. */
+            ASSERT(WsList->FirstFree == LastValid + 1);
+            WsList->FirstFree = MMWSLE_NEXT_FREE_INVALID;
+        }
+        else
+        {
+            /* The previous one is now the last in the queue */
+            PMMWSLE PreviousFree =
&WsList->Wsle[NextFree->u1.Free.PreviousFree];
+
+            ASSERT(PreviousFree->u1.Free.MustBeZero == 0);
+            PreviousFree->u1.Free.NextFree = MMWSLE_NEXT_FREE_INVALID;
+        }
+
+        /* Nuke everyone */
+        RtlZeroMemory(&WsList->Wsle[LastValid + 1], (WsList->LastEntry -
LastValid) * sizeof(MMWSLE));
+        WsList->LastEntry = LastValid;
+    }
+
+    if (LastValid < WsList->FirstDynamic)
+    {
+        /* Do not mess around with the protected ones */
+        return;
+    }
+
+    /* See if we should shrink our array */
+    if (LastValid == (WsList->LastInitializedWsle - (PAGE_SIZE / sizeof(MMWSLE)) + 1))
+    {
+        PVOID WsleArrayQueue = ALIGN_DOWN_POINTER_BY(&WsList->Wsle[LastValid],
PAGE_SIZE);
+        PEPROCESS Process = MmGetAddressSpaceOwner(Vm);
+
+        ASSERT(WsList->Wsle[WsList->LastEntry].u1.e1.VirtualPageNumber ==
((ULONG_PTR)WsleArrayQueue) >> PAGE_SHIFT);
+
+        /* Kernel address space not supported yet */
+        ASSERT(Process != NULL);
+
+        /* Nuke the PTE. This will remove the virtual address from the working set */
+        MiDeletePte(MiAddressToPte(WsleArrayQueue), WsleArrayQueue, Process, NULL);
+    }
+}
+
+VOID
+NTAPI
+MiRemoveFromWorkingSetList(
+    _Inout_ PMMSUPPORT Vm,
+    _In_ PVOID Address)
+{
+    PMMWSL WsList = Vm->VmWorkingSetList;
+    ULONG WsIndex;
+    PMMWSLE WsleEntry;
+    PMMPTE PointerPte = MiAddressToPte(Address);
+    PMMPFN Pfn1;
+
+    /* Make sure we got a rounded address */
+    Address = ALIGN_DOWN_POINTER_BY(Address, PAGE_SIZE);
+
+    /* Make sure we are locking the right things */
+    ASSERT(MM_ANY_WS_LOCK_HELD(PsGetCurrentThread()));
+    MI_ASSERT_PFN_LOCK_HELD();
+
+    /* Make sure we are removing a paged-in address */
+    ASSERT(PointerPte->u.Hard.Valid == 1);
+    Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
+
+    /* The Pfn must be an active one */
+    ASSERT(Pfn1->u3.e1.PageLocation == ActiveAndValid);
+
+    WsIndex = Pfn1->u1.WsIndex;
+    WsleEntry = &Vm->VmWorkingSetList->Wsle[WsIndex];
+
+    /* Shared page not handled yet */
+    ASSERT(Pfn1->u3.e1.PrototypePte == 0);
+    ASSERT(WsleEntry->u1.e1.Direct == 1);
+    /* Nor are "ROS PFN" */
+    ASSERT(MI_IS_ROS_PFN(Pfn1) == FALSE);
+
+    /* Some sanity checks */
+    ASSERT(WsIndex >= WsList->FirstDynamic);
+    ASSERT(WsIndex <= WsList->LastEntry);
+    ASSERT(WsIndex <= WsList->LastInitializedWsle);
+    ASSERT(WsleEntry->u1.e1.Valid == 1);
+    ASSERT(WsleEntry->u1.e1.VirtualPageNumber == ((ULONG_PTR)Address) >>
PAGE_SHIFT);
+
+    /* Let this go */
+    Pfn1->u1.WsIndex = 0;
+
+    /* Nuke it */
+    WsleEntry->u1.Long = 0;
+
+    /* Insert our entry into the free list */
+    if (WsIndex == WsList->LastEntry)
+    {
+        /* Let's shrink the active list */
+        WsList->LastEntry--;
+        MiShrinkWorkingSet(Vm);
+    }
+    else if (WsList->FirstFree > WsList->LastEntry)
+    {
+        /* We are the first free entry to be inserted */
+        WsList->FirstFree = WsIndex;
+        WsleEntry->u1.Free.PreviousFree = MMWSLE_PREVIOUS_FREE_INVALID;
+        WsleEntry->u1.Free.NextFree = MMWSLE_NEXT_FREE_INVALID;
+    }
+    else
+    {
+        /* Keep this sorted */
+        PMMWSLE NextFree = &WsList->Wsle[WsList->FirstFree];
+        PMMWSLE PreviousFree = NULL;
+
+        ASSERT(NextFree->u1.Free.MustBeZero == 0);
+
+        while (NextFree < WsleEntry)
+        {
+            PreviousFree = NextFree;
+            if (NextFree->u1.Free.NextFree != MMWSLE_NEXT_FREE_INVALID)
+            {
+                NextFree = &WsList->Wsle[NextFree->u1.Free.NextFree];
+                ASSERT(NextFree->u1.Free.MustBeZero == 0);
+            }
+            else
+            {
+                NextFree = NULL;
+                break;
+            }
+        }
+
+        ASSERT(PreviousFree || NextFree);
+
+        if (PreviousFree)
+        {
+            ASSERT((NextFree != NULL) || (PreviousFree->u1.Free.NextFree ==
MMWSLE_NEXT_FREE_INVALID));
+            PreviousFree->u1.Free.NextFree = WsIndex;
+            WsleEntry->u1.Free.PreviousFree = PreviousFree - WsList->Wsle;
+        }
+        else
+        {
+            WsleEntry->u1.Free.PreviousFree = MMWSLE_PREVIOUS_FREE_INVALID;
+            ASSERT(NextFree->u1.Free.PreviousFree == MMWSLE_PREVIOUS_FREE_INVALID);
+            WsList->FirstFree = WsIndex;
+        }
+
+        if (NextFree)
+        {
+            NextFree->u1.Free.PreviousFree = WsIndex;
+            WsleEntry->u1.Free.NextFree = NextFree - WsList->Wsle;
+        }
+        else
+        {
+            WsleEntry->u1.Free.NextFree = MMWSLE_NEXT_FREE_INVALID;
+        }
+    }
+
+    Vm->WorkingSetSize -= PAGE_SIZE;
+}
 VOID
 NTAPI
diff --git a/ntoskrnl/mm/ARM3/sysldr.c b/ntoskrnl/mm/ARM3/sysldr.c
index d414b1f6b43..58e4c3057b2 100644
--- a/ntoskrnl/mm/ARM3/sysldr.c
+++ b/ntoskrnl/mm/ARM3/sysldr.c
@@ -188,14 +188,7 @@ MiLoadImageSection(IN OUT PVOID *SectionPtr,
         /* Some debug stuff */
         MI_SET_USAGE(MI_USAGE_DRIVER_PAGE);
 #if MI_TRACE_PFNS
-        if (FileName->Buffer)
-        {
-            PWCHAR pos = NULL;
-            ULONG len = 0;
-            pos = wcsrchr(FileName->Buffer, '\\');
-            len = wcslen(pos) * sizeof(WCHAR);
-            if (pos) snprintf(MI_PFN_CURRENT_PROCESS_NAME, min(16, len), "%S",
pos);
-        }
+        MI_SET_PROCESS_USTR(FileName);
 #endif
         /* Grab a page */
diff --git a/ntoskrnl/mm/ARM3/virtual.c b/ntoskrnl/mm/ARM3/virtual.c
index f0ab5d08f7b..630afc5c990 100644
--- a/ntoskrnl/mm/ARM3/virtual.c
+++ b/ntoskrnl/mm/ARM3/virtual.c
@@ -503,6 +503,9 @@ MiDeletePte(IN PMMPTE PointerPte,
     }
     else
     {
+        /* Remove this address from the WS list */
+        MiRemoveFromWorkingSetList(&CurrentProcess->Vm, VirtualAddress);
+
         /* Make sure the saved PTE address is valid */
         if ((PMMPTE)((ULONG_PTR)Pfn1->PteAddress & ~0x1) != PointerPte)
         {
@@ -2303,13 +2306,15 @@ MiProtectVirtualMemory(IN PEPROCESS Process,
                 {
                     KIRQL OldIrql = MiAcquirePfnLock();
+                    /* Remove this from the working set */
+                    MiRemoveFromWorkingSetList(AddressSpace, MiPteToAddress(PointerPte));
+
                     /* Mark the PTE as transition and change its protection */
                     PteContents.u.Hard.Valid = 0;
                     PteContents.u.Soft.Transition = 1;
                     PteContents.u.Trans.Protection = ProtectionMask;
                     /* Decrease PFN share count and write the PTE */
                     MiDecrementShareCount(Pfn1, PFN_FROM_PTE(&PteContents));
-                    // FIXME: remove the page from the WS
                     MI_WRITE_INVALID_PTE(PointerPte, PteContents);
 #ifdef CONFIG_SMP
                     // FIXME: Should invalidate entry in every CPU TLB
@@ -2447,8 +2452,10 @@ MiMakePdeExistAndMakeValid(IN PMMPDE PointerPde,
 VOID
 NTAPI
-MiProcessValidPteList(IN PMMPTE *ValidPteList,
-                      IN ULONG Count)
+MiProcessValidPteList(
+    _Inout_ PMMSUPPORT Vm,
+    _Inout_ PMMPTE *ValidPteList,
+    _In_ ULONG Count)
 {
     KIRQL OldIrql;
     ULONG i;
@@ -2468,6 +2475,11 @@ MiProcessValidPteList(IN PMMPTE *ValidPteList,
         TempPte = *ValidPteList[i];
         ASSERT(TempPte.u.Hard.Valid == 1);
+        //
+        // We can now remove this addres from the working set
+        //
+        MiRemoveFromWorkingSetList(Vm, MiPteToAddress(ValidPteList[i]));
+
         //
         // Get the PFN entry for the page itself, and then for its page table
         //
@@ -2509,7 +2521,6 @@ MiDecommitPages(IN PVOID StartingAddress,
     ULONG CommitReduction = 0;
     PMMPTE ValidPteList[256];
     ULONG PteCount = 0;
-    PMMPFN Pfn1;
     MMPTE PteContents;
     PETHREAD CurrentThread = PsGetCurrentThread();
@@ -2541,10 +2552,10 @@ MiDecommitPages(IN PVOID StartingAddress,
             // such, and does not flush the entire TLB all the time, but right
             // now we have bigger problems to worry about than TLB flushing.
             //
-            PointerPde = MiAddressToPde(StartingAddress);
+            PointerPde = MiPteToPde(PointerPte);
             if (PteCount)
             {
-                MiProcessValidPteList(ValidPteList, PteCount);
+                MiProcessValidPteList(&Process->Vm, ValidPteList, PteCount);
                 PteCount = 0;
             }
@@ -2579,21 +2590,13 @@ MiDecommitPages(IN PVOID StartingAddress,
                 //Process->NumberOfPrivatePages--;
                 if (PteContents.u.Hard.Valid)
                 {
-                    //
-                    // It's valid. At this point make sure that it is not a ROS
-                    // PFN. Also, we don't support ProtoPTEs in this code path.
-                    //
-                    Pfn1 = MiGetPfnEntry(PteContents.u.Hard.PageFrameNumber);
-                    ASSERT(MI_IS_ROS_PFN(Pfn1) == FALSE);
-                    ASSERT(Pfn1->u3.e1.PrototypePte == FALSE);
-
                     //
                     // Flush any pending PTEs that we had not yet flushed, if our
                     // list has gotten too big, then add this PTE to the flush list.
                     //
                     if (PteCount == 256)
                     {
-                        MiProcessValidPteList(ValidPteList, PteCount);
+                        MiProcessValidPteList(&Process->Vm, ValidPteList,
PteCount);
                         PteCount = 0;
                     }
                     ValidPteList[PteCount++] = PointerPte;
@@ -2623,7 +2626,7 @@ MiDecommitPages(IN PVOID StartingAddress,
             // This used to be a zero PTE and it no longer is, so we must add a
             // reference to the pagetable.
             //
-            MiIncrementPageTableReferences(StartingAddress);
+            MiIncrementPageTableReferences(MiPteToAddress(PointerPte));
             //
             // Next, we account for decommitted PTEs and make the PTE as such
@@ -2633,17 +2636,16 @@ MiDecommitPages(IN PVOID StartingAddress,
         }
         //
-        // Move to the next PTE and the next address
+        // Move to the next PTE
         //
         PointerPte++;
-        StartingAddress = (PVOID)((ULONG_PTR)StartingAddress + PAGE_SIZE);
     }
     //
     // Flush any dangling PTEs from the loop in the last page table, and then
     // release the working set and return the commit reduction accounting.
     //
-    if (PteCount) MiProcessValidPteList(ValidPteList, PteCount);
+    if (PteCount) MiProcessValidPteList(&Process->Vm, ValidPteList, PteCount);
     MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread);
     return CommitReduction;
 }
diff --git a/ntoskrnl/mm/freelist.c b/ntoskrnl/mm/freelist.c
index 93d9bbd9f59..80447593ecd 100644
--- a/ntoskrnl/mm/freelist.c
+++ b/ntoskrnl/mm/freelist.c
@@ -574,6 +574,21 @@ MmAllocPage(ULONG Type)
     OldIrql = MiAcquirePfnLock();
+#if MI_TRACE_PFNS
+    switch(Type)
+    {
+    case MC_CACHE:
+    case MC_SYSTEM:
+        MI_SET_USAGE(MI_USAGE_CACHE);
+        break;
+    case MC_USER:
+        MI_SET_USAGE(MI_USAGE_SECTION);
+        break;
+    default:
+        ASSERT(FALSE);
+    }
+#endif
+
     PfnOffset = MiRemoveZeroPage(MI_GET_NEXT_COLOR());
     if (!PfnOffset)
     {
diff --git a/ntoskrnl/mm/i386/page.c b/ntoskrnl/mm/i386/page.c
index 6b5ac1e8353..f8337dadc26 100644
--- a/ntoskrnl/mm/i386/page.c
+++ b/ntoskrnl/mm/i386/page.c
@@ -264,6 +264,10 @@ MmGetPageTableForProcess(PEPROCESS Process, PVOID Address, BOOLEAN
Create)
             MI_WRITE_INVALID_PTE(PointerPde, DemandZeroPde);
             // Tiny HACK: Parameter 1 is the architecture specific FaultCode for an
access violation (i.e. page is present)
+
+            /* Lock the working set, as this will add this address to it */
+            MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
+
             Status = MiDispatchFault(0x1,
                                      Pt,
                                      PointerPde,
@@ -275,6 +279,8 @@ MmGetPageTableForProcess(PEPROCESS Process, PVOID Address, BOOLEAN
Create)
             DBG_UNREFERENCED_LOCAL_VARIABLE(Status);
             ASSERT(KeAreAllApcsDisabled() == TRUE);
             ASSERT(PointerPde->u.Hard.Valid == 1);
+
+            MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
         }
         return (PULONG)MiAddressToPte(Address);
     }
diff --git a/ntoskrnl/mm/marea.c b/ntoskrnl/mm/marea.c
index d953f422f96..a68adecd9be 100644
--- a/ntoskrnl/mm/marea.c
+++ b/ntoskrnl/mm/marea.c
@@ -331,13 +331,16 @@ MmFreeMemoryArea(
                 ASSERT(AddressSpace != MmGetKernelAddressSpace());
                 if (MiQueryPageTableReferences((PVOID)Address) == 0)
                 {
+                    KIRQL OldIrql;
                     /* No PTE relies on this PDE. Release it */
-                    KIRQL OldIrql = MiAcquirePfnLock();
+                    MiLockProcessWorkingSet(Process, PsGetCurrentThread());
+                    OldIrql = MiAcquirePfnLock();
                     PMMPDE PointerPde = MiAddressToPde(Address);
                     ASSERT(PointerPde->u.Hard.Valid == 1);
                     MiDeletePte(PointerPde, MiPdeToPte(PointerPde), Process, NULL);
                     ASSERT(PointerPde->u.Hard.Valid == 0);
                     MiReleasePfnLock(OldIrql);
+                    MiUnlockProcessWorkingSet(Process, PsGetCurrentThread());
                 }
             }
 #endif
diff --git a/ntoskrnl/mm/section.c b/ntoskrnl/mm/section.c
index ce63e796a3d..0825d4ecb49 100644
--- a/ntoskrnl/mm/section.c
+++ b/ntoskrnl/mm/section.c
@@ -1163,8 +1163,6 @@ MiReadPage(PMEMORY_AREA MemoryArea,
          * Allocate a page, this is rather complicated by the possibility
          * we might have to move other things out of memory
          */
-        MI_SET_USAGE(MI_USAGE_SECTION);
-        MI_SET_PROCESS2(PsGetCurrentProcess()->ImageFileName);
         Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, Page);
         if (!NT_SUCCESS(Status))
         {
@@ -1624,9 +1622,6 @@ MmNotPresentFaultSectionView(PMMSUPPORT AddressSpace,
                 ((Offset.QuadPart >=
(LONGLONG)PAGE_ROUND_UP(Segment->RawLength.QuadPart) &&
                   (Section->AllocationAttributes & SEC_IMAGE))))
         {
-            MI_SET_USAGE(MI_USAGE_SECTION);
-            if (Process) MI_SET_PROCESS2(Process->ImageFileName);
-            if (!Process) MI_SET_PROCESS2("Kernel Section");
             Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
             if (!NT_SUCCESS(Status))
             {
@@ -1707,9 +1702,6 @@ MmNotPresentFaultSectionView(PMMSUPPORT AddressSpace,
         MmUnlockSectionSegment(Segment);
         MmUnlockAddressSpace(AddressSpace);
-        MI_SET_USAGE(MI_USAGE_SECTION);
-        if (Process) MI_SET_PROCESS2(Process->ImageFileName);
-        if (!Process) MI_SET_PROCESS2("Kernel Section");
         Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
         if (!NT_SUCCESS(Status))
         {
@@ -1878,9 +1870,6 @@ MmAccessFaultSectionView(PMMSUPPORT AddressSpace,
     /*
      * Allocate a page
      */
-    MI_SET_USAGE(MI_USAGE_SECTION);
-    if (Process) MI_SET_PROCESS2(Process->ImageFileName);
-    if (!Process) MI_SET_PROCESS2("Kernel Section");
     Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &NewPage);
     if (!NT_SUCCESS(Status))
     {