Author: jgardou
Date: Mon Sep 22 17:20:38 2014
New Revision: 64228
URL: 
http://svn.reactos.org/svn/reactos?rev=64228&view=rev
Log:
Try to repair catastrophic merge attempt from r64223, part 1 of 2
[NTOS/MM]
Commit what I got so far, including:
 - A very (very) basic balancer for the paged pool, simply sweeping all the pages of it
and ageing the pages as they're run over
 - A page writer thread writing unreferenced pages to the pagefile.
 - Support for reading from the pagefile in the ARM3 page fault handler
 - Popping a page from the standby list when running out of free pages
 - Various fixes here and there
Enjoy!
Modified:
    branches/TransitionPte/ntoskrnl/include/internal/mm.h
    branches/TransitionPte/ntoskrnl/mm/ARM3/miarm.h
    branches/TransitionPte/ntoskrnl/mm/ARM3/pagfault.c
    branches/TransitionPte/ntoskrnl/mm/ARM3/pfnlist.c
    branches/TransitionPte/ntoskrnl/mm/ARM3/section.c
    branches/TransitionPte/ntoskrnl/mm/ARM3/virtual.c
    branches/TransitionPte/ntoskrnl/mm/balance.c
    branches/TransitionPte/ntoskrnl/mm/mminit.c
    branches/TransitionPte/ntoskrnl/mm/pagefile.c
Modified: branches/TransitionPte/ntoskrnl/include/internal/mm.h
URL:
http://svn.reactos.org/svn/reactos/branches/TransitionPte/ntoskrnl/include/…
==============================================================================
--- branches/TransitionPte/ntoskrnl/include/internal/mm.h       [iso-8859-1] (original)
+++ branches/TransitionPte/ntoskrnl/include/internal/mm.h       [iso-8859-1] Mon Sep 22
17:20:38 2014
@@ -373,6 +373,7 @@
 extern MMPFNLIST MmFreePageListHead;
 extern MMPFNLIST MmStandbyPageListHead;
 extern MMPFNLIST MmModifiedPageListHead;
+extern MMPFNLIST MmModifiedPageListByColor[1];
 extern MMPFNLIST MmModifiedNoWritePageListHead;
 typedef struct _MM_MEMORY_CONSUMER
@@ -652,8 +653,27 @@
 NTAPI
 MiReadPageFile(
     _In_ PFN_NUMBER Page,
-    _In_ ULONG PageFileIndex,
-    _In_ ULONG_PTR PageFileOffset);
+    _In_ const MMPTE* PointerPte
+);
+
+NTSTATUS
+NTAPI
+MiWritePageFile(
+    _In_ PFN_NUMBER Page,
+    _In_ const MMPTE* PointerPte
+);
+
+VOID
+NTAPI
+MiFreePageFileEntry(
+    _In_ PMMPTE PointerPte
+);
+
+NTSTATUS
+NTAPI
+MiReservePageFileEntry(
+    _Out_ PMMPTE PointerPte
+);
 /* process.c ****************************************************************/
Modified: branches/TransitionPte/ntoskrnl/mm/ARM3/miarm.h
URL:
http://svn.reactos.org/svn/reactos/branches/TransitionPte/ntoskrnl/mm/ARM3/…
==============================================================================
--- branches/TransitionPte/ntoskrnl/mm/ARM3/miarm.h     [iso-8859-1] (original)
+++ branches/TransitionPte/ntoskrnl/mm/ARM3/miarm.h     [iso-8859-1] Mon Sep 22 17:20:38
2014
@@ -687,6 +687,7 @@
 extern PKEVENT MiHighPagedPoolEvent;
 extern PKEVENT MiLowNonPagedPoolEvent;
 extern PKEVENT MiHighNonPagedPoolEvent;
+extern KEVENT MpwThreadEvent;
 extern PFN_NUMBER MmLowMemoryThreshold;
 extern PFN_NUMBER MmHighMemoryThreshold;
 extern PFN_NUMBER MiLowPagedPoolThreshold;
@@ -695,6 +696,7 @@
 extern PFN_NUMBER MiHighNonPagedPoolThreshold;
 extern PFN_NUMBER MmMinimumFreePages;
 extern PFN_NUMBER MmPlentyFreePages;
+extern ULONG_PTR MmTotalPagesForPagingFile;
 extern SIZE_T MmMinimumStackCommitInBytes;
 extern PFN_COUNT MiExpansionPoolPagesInitialCharge;
 extern PFN_NUMBER MmResidentAvailablePages;
Modified: branches/TransitionPte/ntoskrnl/mm/ARM3/pagfault.c
URL:
http://svn.reactos.org/svn/reactos/branches/TransitionPte/ntoskrnl/mm/ARM3/…
==============================================================================
--- branches/TransitionPte/ntoskrnl/mm/ARM3/pagfault.c  [iso-8859-1] (original)
+++ branches/TransitionPte/ntoskrnl/mm/ARM3/pagfault.c  [iso-8859-1] Mon Sep 22 17:20:38
2014
@@ -583,7 +583,7 @@
     ASSERT(PointerPte->u.Hard.Valid == 0);
     /* Assert we have enough pages */
-    ASSERT(MmAvailablePages >= 32);
+    ASSERT(MmAvailablePages != 0);
 #if MI_TRACE_PFNS
     if (UserPdeFault) MI_SET_USAGE(MI_USAGE_PAGE_TABLE);
@@ -688,7 +688,8 @@
                         IN PMMPTE PointerPte,
                         IN PMMPTE PointerProtoPte,
                         IN KIRQL OldIrql,
-                        IN PMMPFN* LockedProtoPfn)
+                        IN PMMPFN* LockedProtoPfn,
+                        PVOID InPageBlock)
 {
     MMPTE TempPte;
     PMMPTE OriginalPte, PageTablePte;
@@ -799,6 +800,10 @@
     /* Reset the protection if needed */
     if (OriginalProtection) Protection = MM_ZERO_ACCESS;
+
+    /* Let waiters go ahead */
+    if (InPageBlock)
+        KeSetEvent(InPageBlock, IO_NO_INCREMENT, FALSE);
     /* Return success */
     ASSERT(PointerPte == MiAddressToPte(Address));
@@ -816,26 +821,30 @@
     ULONG Color;
     PFN_NUMBER Page;
     NTSTATUS Status;
-    MMPTE TempPte = *PointerPte;
-    KEVENT Event;
+    MMPTE PageFilePte = *PointerPte, TempPte;
     PMMPFN Pfn1;
-    ULONG PageFileIndex = TempPte.u.Soft.PageFileLow;
-    ULONG_PTR PageFileOffset = TempPte.u.Soft.PageFileHigh;
+
+    DPRINT1("Serving page fault for PTE %p (%x).\n", PointerPte,
PageFilePte.u.Long);
     /* Things we don't support yet */
-    ASSERT(CurrentProcess > HYDRA_PROCESS);
     ASSERT(*OldIrql != MM_NOIRQL);
     /* We must hold the PFN lock */
     ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
     /* Some sanity checks */
-    ASSERT(TempPte.u.Hard.Valid == 0);
-    ASSERT(TempPte.u.Soft.PageFileHigh != 0);
-    ASSERT(TempPte.u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED);
+    ASSERT(PageFilePte.u.Hard.Valid == 0);
+    ASSERT(PageFilePte.u.Soft.Transition == 0);
+    ASSERT(PageFilePte.u.Soft.Prototype == 0);
+    ASSERT(PageFilePte.u.Soft.PageFileHigh != 0);
+    ASSERT(PageFilePte.u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED);
     /* Get any page, it will be overwritten */
-    Color = MI_GET_NEXT_PROCESS_COLOR(CurrentProcess);
+    if (CurrentProcess > HYDRA_PROCESS)
+        Color = MI_GET_NEXT_PROCESS_COLOR(CurrentProcess);
+    else
+        Color = MI_GET_NEXT_COLOR();
+
     Page = MiRemoveAnyPage(Color);
     /* Initialize this PFN */
@@ -847,14 +856,13 @@
     ASSERT(Pfn1->u3.e1.ReadInProgress == 0);
     ASSERT(Pfn1->u3.e1.WriteInProgress == 0);
-    KeInitializeEvent(&Event, NotificationEvent, FALSE);
-    Pfn1->u1.Event = &Event;
+    /* Set NULL event */
+    Pfn1->u1.Event = NULL;
     Pfn1->u3.e1.ReadInProgress = 1;
     /* We must write the PTE now as the PFN lock will be released while performing the IO
operation */
+    TempPte.u.Long = 0;
     TempPte.u.Soft.Transition = 1;
-    TempPte.u.Soft.PageFileLow = 0;
-    TempPte.u.Soft.Prototype = 0;
     TempPte.u.Trans.PageFrameNumber = Page;
     MI_WRITE_INVALID_PTE(PointerPte, TempPte);
@@ -863,15 +871,25 @@
     KeReleaseQueuedSpinLock(LockQueuePfnLock, *OldIrql);
     /* Do the paging IO */
-    Status = MiReadPageFile(Page, PageFileIndex, PageFileOffset);
+    Status = MiReadPageFile(Page, &PageFilePte);
     /* Lock the PFN database again */
     *OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
     /* Nobody should have changed that while we were not looking */
-    ASSERT(Pfn1->u1.Event == &Event);
     ASSERT(Pfn1->u3.e1.ReadInProgress == 1);
     ASSERT(Pfn1->u3.e1.WriteInProgress == 0);
+
+    /* But maybe someone began waiting on us */
+    if (Pfn1->u1.Event != NULL)
+    {
+        DPRINT1("The page got a waiter!\n");
+        /* Waiters gonna wait */
+        KeSetEvent(Pfn1->u1.Event, IO_NO_INCREMENT, FALSE);
+        Pfn1->u1.Event = NULL;
+    }
+
+    DPRINT1("Pagefile read finished. Status: 0x%08x.\n", Status);
     if (!NT_SUCCESS(Status))
     {
@@ -879,38 +897,60 @@
         ASSERT(FALSE);
         Pfn1->u4.InPageError = 1;
         Pfn1->u1.ReadStatus = Status;
+        Status = STATUS_IN_PAGE_ERROR;
+    }
+    else
+    {
+        /* This is a success status */
+        Status = STATUS_PAGE_FAULT_PAGING_FILE;
     }
     /* This is now a nice and normal PFN */
-    Pfn1->u1.Event = NULL;
     Pfn1->u3.e1.ReadInProgress = 0;
-    /* And the PTE can finally be valid */
-    MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, TempPte.u.Trans.Protection, Page);
+    /* Check if this is a kernel or user address */
+    if (PointerPte <= MiHighestUserPte)
+    {
+        /* Build the user PTE */
+        MI_MAKE_HARDWARE_PTE_USER(&TempPte,
+            PointerPte,
+            PageFilePte.u.Soft.Protection,
+            Page);
+    }
+    else
+    {
+        /* Build the kernel PTE */
+        MI_MAKE_HARDWARE_PTE(&TempPte,
+            PointerPte,
+            PageFilePte.u.Soft.Protection,
+            Page);
+    }
+
+    /* Write it */
     MI_WRITE_VALID_PTE(PointerPte, TempPte);
-
-    /* Waiters gonna wait */
-    KeSetEvent(&Event, IO_NO_INCREMENT, FALSE);
     return Status;
 }
 NTSTATUS
 NTAPI
-MiResolveTransitionFault(IN PVOID FaultingAddress,
-                         IN PMMPTE PointerPte,
-                         IN PEPROCESS CurrentProcess,
-                         IN KIRQL OldIrql,
-                         OUT PVOID *InPageBlock)
+MiResolveTransitionFault(
+    _In_ BOOLEAN StoreInstruction,
+    _In_ PVOID FaultingAddress,
+    _In_ PMMPTE PointerPte,
+    _In_ PEPROCESS CurrentProcess,
+    _In_ KIRQL OldIrql,
+    _In_ PVOID *InPageBlock)
 {
     PFN_NUMBER PageFrameIndex;
     PMMPFN Pfn1;
     MMPTE TempPte;
     PMMPTE PointerToPteForProtoPage;
-    DPRINT1("Transition fault on 0x%p with PTE 0x%p in process %s\n",
-            FaultingAddress, PointerPte, CurrentProcess->ImageFileName);
-
-    /* Windowss does this check */
+
+    DPRINT("Transition fault on 0x%p with PTE 0x%p in process %s\n",
+            FaultingAddress, PointerPte, CurrentProcess ?
CurrentProcess->ImageFileName : "Kernel");
+
+    /* Windows does this check */
     ASSERT(*InPageBlock == NULL);
     /* ARM3 doesn't support this path */
@@ -934,25 +974,52 @@
     ASSERT(Pfn1->u4.InPageError == 0);
     /* See if we should wait before terminating the fault */
-    if (Pfn1->u3.e1.ReadInProgress == 1)
-    {
-       DPRINT1("The page is currently being read!\n");
-       ASSERT(Pfn1->u1.Event != NULL);
-       *InPageBlock = Pfn1->u1.Event;
-       if (PointerPte == Pfn1->PteAddress)
-       {
-           DPRINT1("And this if for this particular PTE.\n");
-           /* The PTE will be made valid by the thread serving the fault */
-           return STATUS_SUCCESS; // FIXME: Maybe something more descriptive
-       }
+    if ((Pfn1->u3.e1.WriteInProgress == 1) || (Pfn1->u3.e1.ReadInProgress == 1))
+    {
+        KEVENT IoEvent;
+        DPRINT1("The page is currently being written to or read!\n");
+
+        /* Both of them, it's not possible */
+        ASSERT(((Pfn1->u3.e1.ReadInProgress == 0) &&
(Pfn1->u3.e1.WriteInProgress == 1)) ||
+                ((Pfn1->u3.e1.ReadInProgress == 1) &&
(Pfn1->u3.e1.WriteInProgress == 0)));
+
+        KeInitializeEvent(&IoEvent, NotificationEvent, FALSE);
+        *InPageBlock = Pfn1->u1.Event;
+        Pfn1->u1.Event = &IoEvent;
+
+        /*
+         * Mark it as unmodified.
+         * The page-out thread will put it back in the standby list
+         * and we will be able to start anew for this PTE
+         */
+        if ((Pfn1->u3.e1.WriteInProgress) && (!StoreInstruction))
+            Pfn1->u3.e1.Modified = 0;
+
+        /* Release the lock so the writer can finish */
+        KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+
+        /* Wait for it to really finish */
+        KeWaitForSingleObject(&IoEvent, WrPageIn, KernelMode, FALSE, NULL);
+
+        /* Get it again */
+        OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+
+        /* Read the PTE again */
+        TempPte = *PointerPte;
+        if (TempPte.u.Hard.Valid == 1)
+        {
+            /* Someone did it while we were not looking. */
+            return STATUS_PAGE_FAULT_TRANSITION;
+        }
+        /* Otherwise the state of this PTE must still be the same */
+        ASSERT(TempPte.u.Soft.Transition == 1);
+        ASSERT(TempPte.u.Soft.Prototype == 0);
+        ASSERT(TempPte.u.Trans.PageFrameNumber == PageFrameIndex);
     }
     /* Windows checks there's some free pages and this isn't an in-page error */
     ASSERT(MmAvailablePages > 0);
     ASSERT(Pfn1->u4.InPageError == 0);
-
-    /* ReactOS checks for this */
-    ASSERT(MmAvailablePages > 32);
     /* Was this a transition page in the valid list, or free/zero list? */
     if (Pfn1->u3.e1.PageLocation == ActiveAndValid)
@@ -1066,7 +1133,8 @@
                                        PointerPte,
                                        PointerProtoPte,
                                        OldIrql,
-                                       OutPfn);
+                                       OutPfn,
+                                       NULL);
     }
     /* Make sure there's some protection mask */
@@ -1125,18 +1193,25 @@
     {
         /* Resolve the transition fault */
         ASSERT(OldIrql != MM_NOIRQL);
-        Status = MiResolveTransitionFault(Address,
+        Status = MiResolveTransitionFault(StoreInstruction,
+                                          Address,
                                           PointerProtoPte,
                                           Process,
                                           OldIrql,
                                           &InPageBlock);
         ASSERT(NT_SUCCESS(Status));
     }
+    else if (TempPte.u.Soft.PageFileHigh != 0)
+    {
+        Status = MiResolvePageFileFault(StoreInstruction,
+                                        Address,
+                                        PointerProtoPte,
+                                        Process,
+                                        &OldIrql);
+        ASSERT(NT_SUCCESS(Status));
+    }
     else
     {
-        /* We also don't support paged out pages */
-        ASSERT(TempPte.u.Soft.PageFileHigh == 0);
-
         /* Resolve the demand zero fault */
         Status = MiResolveDemandZeroFault(Address,
                                           PointerProtoPte,
@@ -1152,7 +1227,8 @@
                                    PointerPte,
                                    PointerProtoPte,
                                    OldIrql,
-                                   OutPfn);
+                                   OutPfn,
+                                   InPageBlock);
 }
 NTSTATUS
@@ -1173,9 +1249,8 @@
     PMMPFN Pfn1, OutPfn = NULL;
     PFN_NUMBER PageFrameIndex;
     PFN_COUNT PteCount, ProcessedPtes;
-    DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n",
-             Address,
-             Process);
+    DPRINT("ARM3 Page Fault Dispatcher for address: %p (PTE %p) in process:
%p\n",
+        Address, PointerPte, Process);
     /* Make sure the addresses are ok */
     ASSERT(PointerPte == MiAddressToPte(Address));
@@ -1283,8 +1358,49 @@
                     ASSERT(Pfn1->u3.e1.PageLocation != ActiveAndValid);
                     /* Should not yet happen in ReactOS */
-                    ASSERT(Pfn1->u3.e1.ReadInProgress == 0);
                     ASSERT(Pfn1->u4.InPageError == 0);
+
+                    if ((Pfn1->u3.e1.ReadInProgress == 1) ||
(Pfn1->u3.e1.WriteInProgress == 1))
+                    {
+                        KEVENT IoEvent;
+                        PKEVENT PageEvent;
+                        DPRINT1("The page is currently being written to or
read!\n");
+
+                        /* Both of them, it's not possible */
+                        ASSERT(((Pfn1->u3.e1.ReadInProgress == 0) &&
(Pfn1->u3.e1.WriteInProgress == 1)) ||
+                                ((Pfn1->u3.e1.ReadInProgress == 1) &&
(Pfn1->u3.e1.WriteInProgress == 0)));
+
+                        KeInitializeEvent(&IoEvent, NotificationEvent, FALSE);
+                        PageEvent = Pfn1->u1.Event;
+                        Pfn1->u1.Event = &IoEvent;
+
+                        /*
+                         * Mark it as unmodified.
+                         * The page-out thread will put it back in the standby list
+                         * and we will be able to start anew for this PTE
+                         */
+                        if (Pfn1->u3.e1.WriteInProgress)
+                            Pfn1->u3.e1.Modified = 0;
+
+                        /* Release the lock so the writer can finish */
+                        KeReleaseQueuedSpinLock(LockQueuePfnLock, LockIrql);
+
+                        /* Wait for it to really finish */
+                        KeWaitForSingleObject(&IoEvent, WrPageIn, KernelMode, FALSE,
NULL);
+
+                        /* Get it again */
+                        LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+
+                        /* Read the PTE again */
+                        TempPte = *PointerProtoPte;
+
+                        /* Maybe someone else was also waiting */
+                        if (PageEvent)
+                            KeSetEvent(PageEvent, IO_NO_INCREMENT, FALSE);
+
+                        /* And re-loop */
+                        continue;
+                    }
                     /* Get the page */
                     MiUnlinkPageFromList(Pfn1);
@@ -1337,7 +1453,8 @@
                                             PointerPte,
                                             PointerProtoPte,
                                             LockIrql,
-                                            &OutPfn);
+                                            &OutPfn,
+                                            NULL);
                     /* THIS RELEASES THE PFN LOCK! */
                     break;
@@ -1390,14 +1507,14 @@
             {
                 /* We had a locked PFN, so acquire the PFN lock to dereference it */
                 ASSERT(PointerProtoPte != NULL);
-                OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+                LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
                 /* Dereference the locked PFN */
                 MiDereferencePfnAndDropLockCount(OutPfn);
                 ASSERT(OutPfn->u3.e2.ReferenceCount >= 1);
                 /* And now release the lock */
-                KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+                KeReleaseQueuedSpinLock(LockQueuePfnLock, LockIrql);
             }
             /* Complete this as a transition fault */
@@ -1416,7 +1533,12 @@
         LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
         /* Resolve */
-        Status = MiResolveTransitionFault(Address, PointerPte, Process, LockIrql,
&InPageBlock);
+        Status = MiResolveTransitionFault(StoreInstruction,
+            Address,
+            PointerPte,
+            Process,
+            LockIrql,
+            &InPageBlock);
         NT_ASSERT(NT_SUCCESS(Status));
@@ -1425,8 +1547,8 @@
         if (InPageBlock != NULL)
         {
-               /* The page is being paged in by another process */
-               KeWaitForSingleObject(InPageBlock, WrPageIn, KernelMode, FALSE, NULL);
+            /* We must wake the blocked threads */
+            KeSetEvent(InPageBlock, IO_NO_INCREMENT, FALSE);
         }
         ASSERT(OldIrql == KeGetCurrentIrql());
@@ -1821,9 +1943,6 @@
         }
         else
         {
-            /* We don't implement transition PTEs */
-            ASSERT(TempPte.u.Soft.Transition == 0);
-
             /* Check for no-access PTE */
             if (TempPte.u.Soft.Protection == MM_NOACCESS)
             {
@@ -2090,7 +2209,7 @@
             OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
             /* Make sure we have enough pages */
-            ASSERT(MmAvailablePages >= 32);
+            ASSERT(MmAvailablePages != 0);
             /* Try to get a zero page */
             MI_SET_USAGE(MI_USAGE_PEB_TEB);
Modified: branches/TransitionPte/ntoskrnl/mm/ARM3/pfnlist.c
URL:
http://svn.reactos.org/svn/reactos/branches/TransitionPte/ntoskrnl/mm/ARM3/…
==============================================================================
--- branches/TransitionPte/ntoskrnl/mm/ARM3/pfnlist.c   [iso-8859-1] (original)
+++ branches/TransitionPte/ntoskrnl/mm/ARM3/pfnlist.c   [iso-8859-1] Mon Sep 22 17:20:38
2014
@@ -36,7 +36,7 @@
 ULONG MmSystemPageColor;
 ULONG MmTransitionSharedPages;
-ULONG MmTotalPagesForPagingFile;
+ULONG_PTR MmTotalPagesForPagingFile;
 MMPFNLIST MmZeroedPageListHead = {0, ZeroedPageList, LIST_HEAD, LIST_HEAD};
 MMPFNLIST MmFreePageListHead = {0, FreePageList, LIST_HEAD, LIST_HEAD};
@@ -266,7 +266,6 @@
         }
         /* Decrease transition page counter */
-        ASSERT(Pfn->u3.e1.PrototypePte == 1); /* Only supported ARM3 case */
         MmTransitionSharedPages--;
         /* One less page */
@@ -287,6 +286,9 @@
         /* Decrement the counters */
         ListHead->Total--;
         MmTotalPagesForPagingFile--;
+
+        if (MmTotalPagesForPagingFile == 0)
+            KeClearEvent(&MpwThreadEvent);
         /* Pick the correct colored list */
         ListHead = &MmModifiedPageListByColor[0];
@@ -477,12 +479,89 @@
     return PageIndex;
 }
+static
+PFN_NUMBER
+NTAPI
+MiPopPageFromStandbyList(void)
+{
+    unsigned i;
+    PFN_NUMBER PageFrameIndex;
+    PMMPFN Pfn1;
+    PMMPTE PointerPte, SysPte, PteTable;
+    MMPTE PdePte;
+
+    /* Make sure PFN lock is held and we have pages */
+    ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
+    ASSERT(MmAvailablePages != 0);
+
+    /* This should be called in last resort if there are no more free pages */
+    ASSERT(MmZeroedPageListHead.Total == 0);
+    ASSERT(MmFreePageListHead.Total == 0);
+
+    /* Go by priority */
+    for (i = 0; i < 8; i++)
+    {
+        /* Get the oldest one */
+        PageFrameIndex = MmStandbyPageListByPriority[i].Blink;
+        if (PageFrameIndex != LIST_HEAD)
+            break;
+    }
+
+    /* There are no free pages but still available ones, so this must be here */
+    ASSERT(PageFrameIndex != LIST_HEAD);
+
+    /* Get the PFN */
+    Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
+
+    DPRINT1("Popping page %x from standbylist (PTE %p).\n",
+        PageFrameIndex, Pfn1->PteAddress);
+
+    /* Sanity checks */
+    ASSERT(Pfn1->u3.e1.PageLocation == StandbyPageList);
+    ASSERT(Pfn1->u3.e1.Modified == 0);
+
+    /* Get a PTE to map the page directory */
+    SysPte = MiReserveSystemPtes(1, SystemPteSpace);
+    ASSERT(SysPte != NULL);
+
+    /* Build it */
+    MI_MAKE_HARDWARE_PTE_KERNEL(&PdePte,
+                                SysPte,
+                                MM_READWRITE,
+                                Pfn1->u4.PteFrame);
+    /* And map it */
+    MI_WRITE_VALID_PTE(SysPte, PdePte);
+    PteTable = MiPteToAddress(SysPte);
+
+    /* Finally get a pointer to the PTE this page represents */
+    PointerPte =
&PteTable[MiAddressToPteOffset(MiPteToAddress(Pfn1->PteAddress))];
+
+    /* Other sanity checks */
+    ASSERT(PointerPte->u.Hard.Valid == 0);
+    ASSERT(PointerPte->u.Soft.Transition == 1);
+
+    /* Restore the PTE to its original value */
+    *PointerPte = Pfn1->OriginalPte;
+
+    /* We can get rid of this */
+    MiReleaseSystemPtes(SysPte, 1, SystemPteSpace);
+
+    /* And dereference the page table frame */
+    MiDecrementShareCount(MI_PFN_ELEMENT(Pfn1->u4.PteFrame), Pfn1->u4.PteFrame);
+
+    /* Unlink */
+    MiUnlinkPageFromList(Pfn1);
+
+    return PageFrameIndex;
+}
+
 PFN_NUMBER
 NTAPI
 MiRemoveAnyPage(IN ULONG Color)
 {
     PFN_NUMBER PageIndex;
     PMMPFN Pfn1;
+    BOOLEAN FromStandbyList = FALSE;
     /* Make sure PFN lock is held and we have pages */
     ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
@@ -507,23 +586,27 @@
                 ASSERT_LIST_INVARIANT(&MmZeroedPageListHead);
                 PageIndex = MmZeroedPageListHead.Flink;
                 Color = PageIndex & MmSecondaryColorMask;
-                ASSERT(PageIndex != LIST_HEAD);
                 if (PageIndex == LIST_HEAD)
                 {
-                    /* FIXME: Should check the standby list */
                     ASSERT(MmZeroedPageListHead.Total == 0);
+                    /* Pop one page from the standby list and try again */
+                    PageIndex = MiPopPageFromStandbyList();
+                    FromStandbyList = TRUE;
+                    ASSERT(PageIndex != LIST_HEAD);
                 }
             }
         }
     }
     /* Remove the page from its list */
-    PageIndex = MiRemovePageByColor(PageIndex, Color);
+    if (!FromStandbyList)
+        PageIndex = MiRemovePageByColor(PageIndex, Color);
     /* Sanity checks */
     Pfn1 = MI_PFN_ELEMENT(PageIndex);
     ASSERT((Pfn1->u3.e1.PageLocation == FreePageList) ||
-           (Pfn1->u3.e1.PageLocation == ZeroedPageList));
+           (Pfn1->u3.e1.PageLocation == ZeroedPageList) ||
+           (Pfn1->u3.e1.PageLocation == StandbyPageList));
     ASSERT(Pfn1->u3.e2.ReferenceCount == 0);
     ASSERT(Pfn1->u2.ShareCount == 0);
     ASSERT_LIST_INVARIANT(&MmFreePageListHead);
@@ -540,6 +623,7 @@
     PFN_NUMBER PageIndex;
     PMMPFN Pfn1;
     BOOLEAN Zero = FALSE;
+    BOOLEAN FromStandbyList = FALSE;
     /* Make sure PFN lock is held and we have pages */
     ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
@@ -567,11 +651,13 @@
                 ASSERT_LIST_INVARIANT(&MmFreePageListHead);
                 PageIndex = MmFreePageListHead.Flink;
                 Color = PageIndex & MmSecondaryColorMask;
-                ASSERT(PageIndex != LIST_HEAD);
                 if (PageIndex == LIST_HEAD)
                 {
-                    /* FIXME: Should check the standby list */
                     ASSERT(MmZeroedPageListHead.Total == 0);
+                    /* Pop one page from the standby list and try again */
+                    PageIndex = MiPopPageFromStandbyList();
+                    FromStandbyList = TRUE;
+                    ASSERT(PageIndex != LIST_HEAD);
                 }
             }
         }
@@ -584,10 +670,12 @@
     /* Sanity checks */
     Pfn1 = MI_PFN_ELEMENT(PageIndex);
     ASSERT((Pfn1->u3.e1.PageLocation == FreePageList) ||
-           (Pfn1->u3.e1.PageLocation == ZeroedPageList));
+           (Pfn1->u3.e1.PageLocation == ZeroedPageList)||
+           (Pfn1->u3.e1.PageLocation == StandbyPageList));
     /* Remove the page from its list */
-    PageIndex = MiRemovePageByColor(PageIndex, Color);
+    if (!FromStandbyList)
+        PageIndex = MiRemovePageByColor(PageIndex, Color);
     ASSERT(Pfn1 == MI_PFN_ELEMENT(PageIndex));
     /* Zero it, if needed */
@@ -744,7 +832,6 @@
     Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
     ASSERT(Pfn1->u4.MustBeCached == 0);
     ASSERT(Pfn1->u3.e2.ReferenceCount == 0);
-    ASSERT(Pfn1->u3.e1.PrototypePte == 1);
     ASSERT(Pfn1->u3.e1.Rom != 1);
     /* One more transition page on a list */
@@ -979,9 +1066,8 @@
     }
     else if (ListName == ModifiedPageList)
     {
-        /* In ARM3, page must be destined for page file, and not yet written out */
+        /* In ARM3, page must be destined for page file */
         ASSERT(Pfn1->OriginalPte.u.Soft.Prototype == 0);
-        ASSERT(Pfn1->OriginalPte.u.Soft.PageFileHigh == 0);
         /* One more transition page */
         MmTransitionSharedPages++;
@@ -989,7 +1075,8 @@
         /* Increment the number of per-process modified pages */
         PsGetCurrentProcess()->ModifiedPageCount++;
-        /* FIXME: Wake up modified page writer if there are not enough free pages */
+        /* Wake the page writer thread */
+        KeSetEvent(&MpwThreadEvent, IO_NO_INCREMENT, FALSE);
     }
     else if (ListName == ModifiedNoWritePageList)
     {
@@ -1059,6 +1146,9 @@
     PageFrameIndex = PFN_FROM_PTE(PointerPtePte);
     ASSERT(PageFrameIndex != 0);
     Pfn1->u4.PteFrame = PageFrameIndex;
+
+    /* HACK until we get working sets rolling */
+    Pfn1->Wsle.u1.e1.Age = 0;
     /* Increase its share count so we don't get rid of it */
     Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
@@ -1270,32 +1360,8 @@
         /* PFN lock must be held */
         ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
-        if (Pfn1->u3.e2.ReferenceCount == 1)
-        {
-            /* Is there still a PFN for this page? */
-            if (MI_IS_PFN_DELETED(Pfn1) == TRUE)
-            {
-                /* Clear the last reference */
-                Pfn1->u3.e2.ReferenceCount = 0;
-                ASSERT(Pfn1->OriginalPte.u.Soft.Prototype == 0);
-
-                /* Mark the page temporarily as valid, we're going to make it free
soon */
-                Pfn1->u3.e1.PageLocation = ActiveAndValid;
-
-                /* Bring it back into the free list */
-                MiInsertPageInFreeList(PageFrameIndex);
-            }
-            else
-            {
-                /* PFN not yet deleted, drop a ref count */
-                MiDecrementReferenceCount(Pfn1, PageFrameIndex);
-            }
-        }
-        else
-        {
-            /* Otherwise, just drop the reference count */
-            InterlockedDecrement16((PSHORT)&Pfn1->u3.e2.ReferenceCount);
-        }
+        /* Drop a ref count */
+        MiDecrementReferenceCount(Pfn1, PageFrameIndex);
     }
 }
@@ -1338,6 +1404,13 @@
     /* Did someone set the delete flag? */
     if (MI_IS_PFN_DELETED(Pfn1))
     {
+        /* Did we have a Pagefile entry for it */
+        if ((Pfn1->OriginalPte.u.Soft.Prototype == 0)
+                && (Pfn1->OriginalPte.u.Soft.PageFileHigh != 0))
+        {
+            /* Yes. Set it free */
+            MiFreePageFileEntry(&Pfn1->OriginalPte);
+        }
         /* Insert it into the free list, there's nothing left to do */
         MiInsertPageInFreeList(PageFrameIndex);
         return;
Modified: branches/TransitionPte/ntoskrnl/mm/ARM3/section.c
URL:
http://svn.reactos.org/svn/reactos/branches/TransitionPte/ntoskrnl/mm/ARM3/…
==============================================================================
--- branches/TransitionPte/ntoskrnl/mm/ARM3/section.c   [iso-8859-1] (original)
+++ branches/TransitionPte/ntoskrnl/mm/ARM3/section.c   [iso-8859-1] Mon Sep 22 17:20:38
2014
@@ -680,25 +680,55 @@
                 PageFrameIndex = PFN_FROM_PTE(&TempPte);
                 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
-                /* As this is a paged-backed section, nobody should reference it anymore
(no cache or whatever) */
-                ASSERT(Pfn1->u3.ReferenceCount == 0);
-
-                /* And it should be in standby or modified list */
-                ASSERT((Pfn1->u3.e1.PageLocation == ModifiedPageList) ||
(Pfn1->u3.e1.PageLocation == StandbyPageList));
-
-                /* Unlink it and put it back in free list */
-                MiUnlinkPageFromList(Pfn1);
-
-                /* Temporarily mark this as active and make it free again */
-                Pfn1->u3.e1.PageLocation = ActiveAndValid;
-                MI_SET_PFN_DELETED(Pfn1);
-
-                MiInsertPageInFreeList(PageFrameIndex);
+                if (Pfn1->u3.e1.WriteInProgress == 1)
+                {
+                    KEVENT WriteEvent;
+
+                    DPRINT1("Setting the page free while it is being paged
out!\n");
+
+                    /* Only the page writer thread should hold a reference to it */
+                    ASSERT(Pfn1->u3.ReferenceCount == 1);
+
+                    /* Set the page event */
+                    KeInitializeEvent(&WriteEvent, NotificationEvent, FALSE);
+                    /* We must be the only ones waiting */
+                    ASSERT(Pfn1->u1.Event == NULL);
+                    Pfn1->u1.Event = &WriteEvent;
+
+                    /* Tell the page-out thread to abort */
+                    Pfn1->u3.e1.WriteInProgress = 0;
+                    /* We canceled the write operation, so the page-write-thread will
simply dereference
+                     * the Pfn. It has no way to know whether the page will be re-used
again (page fault case)
+                     * or if it should be made free. So mark it as deleted here */
+                    MI_SET_PFN_DELETED(Pfn1);
+
+                    /* Let the writer thread finish and go along */
+                    KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+                    KeWaitForSingleObject(&WriteEvent, FreePage, KernelMode, FALSE,
NULL);
+                    OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+                }
+                else
+                {
+                    /* As this is a paged-backed section, nobody should reference it
anymore (no cache or whatever) */
+                    ASSERT(Pfn1->u3.ReferenceCount == 0);
+
+                    /* And it should be in standby or modified list */
+                    ASSERT((Pfn1->u3.e1.PageLocation == ModifiedPageList) ||
+                        (Pfn1->u3.e1.PageLocation == StandbyPageList));
+
+                    /* Unlink it and temporarily take a reference */
+                    MiUnlinkPageFromList(Pfn1);
+                    Pfn1->u3.e2.ReferenceCount++;
+
+                    /* This will put it back in free list and clean up properly */
+                    MI_SET_PFN_DELETED(Pfn1);
+                    MiDecrementReferenceCount(Pfn1, PageFrameIndex);
+                }
             }
             else if (TempPte.u.Soft.PageFileHigh != 0)
             {
-                /* Should not happen for now */
-                ASSERT(FALSE);
+                /* There isn't much to do */
+                MiFreePageFileEntry(&TempPte);
             }
         }
         else
Modified: branches/TransitionPte/ntoskrnl/mm/ARM3/virtual.c
URL:
http://svn.reactos.org/svn/reactos/branches/TransitionPte/ntoskrnl/mm/ARM3/…
==============================================================================
--- branches/TransitionPte/ntoskrnl/mm/ARM3/virtual.c   [iso-8859-1] (original)
+++ branches/TransitionPte/ntoskrnl/mm/ARM3/virtual.c   [iso-8859-1] Mon Sep 22 17:20:38
2014
@@ -317,7 +317,6 @@
         {
             /* As always, only handle current ARM3 scenarios */
             ASSERT(PointerPte->u.Soft.Prototype == 0);
-            ASSERT(PointerPte->u.Soft.Transition == 0);
             /* Normally this is one possibility -- freeing a valid page */
             if (PointerPte->u.Hard.Valid)
@@ -345,6 +344,57 @@
                 /* Decrement the page table too */
                 MiDecrementShareCount(Pfn2, PageTableIndex);
+
+                /* Release the PFN database */
+                KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+
+                /* Destroy the PTE */
+                MI_ERASE_PTE(PointerPte);
+            }
+            else if (PointerPte->u.Soft.Transition == 1)
+            {
+                /* Get the page PFN */
+                PageFrameIndex = PFN_FROM_PTE(PointerPte);
+                Pfn1 = MiGetPfnEntry(PageFrameIndex);
+
+                /* The page should be out of any working set */
+                ASSERT(Pfn1->u1.WsIndex == 0);
+
+                /* Get the page table entry */
+                PageTableIndex = Pfn1->u4.PteFrame;
+                Pfn2 = MiGetPfnEntry(PageTableIndex);
+
+                /* Lock the PFN database */
+                OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+
+                /* Delete the page */
+                MI_SET_PFN_DELETED(Pfn1);
+
+                /* Decrement the page table */
+                MiDecrementShareCount(Pfn2, PageTableIndex);
+
+                ASSERT(Pfn1->u3.e2.ReferenceCount == 0);
+                /* Unlink it and temporarily take a reference */
+                MiUnlinkPageFromList(Pfn1);
+                Pfn1->u3.e2.ReferenceCount++;
+
+                /* This will put it back in free list and clean properly up */
+                MI_SET_PFN_DELETED(Pfn1);
+                MiDecrementReferenceCount(Pfn1, PageFrameIndex);
+
+                /* Release the PFN database */
+                KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+
+                /* Destroy the PTE */
+                MI_ERASE_PTE(PointerPte);
+            }
+            else if (PointerPte->u.Soft.PageFileHigh != 0)
+            {
+                /* The PFN lock also protects the page files */
+                OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+
+                /* Pretty easy: free the page file slot and go look elsewhere */
+                MiFreePageFileEntry(PointerPte);
                 /* Release the PFN database */
                 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
@@ -358,10 +408,7 @@
                  * The only other ARM3 possibility is a demand zero page, which would
                  * mean freeing some of the paged pool pages that haven't even been
                  * touched yet, as part of a larger allocation.
-                 *
-                 * Right now, we shouldn't expect any page file information in the
PTE
                  */
-                ASSERT(PointerPte->u.Soft.PageFileHigh == 0);
                 /* Destroy the PTE */
                 MI_ERASE_PTE(PointerPte);
@@ -407,9 +454,8 @@
     /* See if the PTE is valid */
     if (TempPte.u.Hard.Valid == 0)
     {
-        /* Prototype and paged out PTEs not supported yet */
+        /* Prototype PTEs not supported yet */
         ASSERT(TempPte.u.Soft.Prototype == 0);
-        ASSERT((TempPte.u.Soft.PageFileHigh == 0) || (TempPte.u.Soft.Transition == 1));
         if (TempPte.u.Soft.Transition)
         {
@@ -435,15 +481,24 @@
                 /* And it should be in standby or modified list */
                 ASSERT((Pfn1->u3.e1.PageLocation == ModifiedPageList) ||
(Pfn1->u3.e1.PageLocation == StandbyPageList));
-                /* Unlink it and temporarily mark it as active */
+                /* Unlink it and temporarily take a reference */
                 MiUnlinkPageFromList(Pfn1);
                 Pfn1->u3.e2.ReferenceCount++;
-                Pfn1->u3.e1.PageLocation = ActiveAndValid;
                 /* This will put it back in free list and clean properly up */
                 MI_SET_PFN_DELETED(Pfn1);
                 MiDecrementReferenceCount(Pfn1, PageFrameIndex);
             }
+            return;
+        }
+
+        if (TempPte.u.Soft.PageFileHigh != 0)
+        {
+            /* Release the page file entry */
+            MiFreePageFileEntry(&TempPte);
+
+            /* And that's pretty much it */
+            MI_ERASE_PTE(PointerPte);
             return;
         }
     }
@@ -475,7 +530,6 @@
         }
 #endif
         /* Drop the share count on the page table */
-        PointerPde = MiPteToPde(PointerPte);
         MiDecrementShareCount(MiGetPfnEntry(PointerPde->u.Hard.PageFrameNumber),
             PointerPde->u.Hard.PageFrameNumber);
Modified: branches/TransitionPte/ntoskrnl/mm/balance.c
URL:
http://svn.reactos.org/svn/reactos/branches/TransitionPte/ntoskrnl/mm/balan…
==============================================================================
--- branches/TransitionPte/ntoskrnl/mm/balance.c        [iso-8859-1] (original)
+++ branches/TransitionPte/ntoskrnl/mm/balance.c        [iso-8859-1] Mon Sep 22 17:20:38
2014
@@ -22,22 +22,14 @@
 #pragma alloc_text(INIT, MiInitBalancerThread)
 #endif
-
-/* TYPES ********************************************************************/
-typedef struct _MM_ALLOCATION_REQUEST
-{
-   PFN_NUMBER Page;
-   LIST_ENTRY ListEntry;
-   KEVENT Event;
-}
-MM_ALLOCATION_REQUEST, *PMM_ALLOCATION_REQUEST;
+#define TEST_PAGING
+
+
 /* GLOBALS ******************************************************************/
 MM_MEMORY_CONSUMER MiMemoryConsumers[MC_MAXIMUM];
 static ULONG MiMinimumAvailablePages;
 static ULONG MiNrTotalPages;
-static LIST_ENTRY AllocationListHead;
-static KSPIN_LOCK AllocationListLock;
 static ULONG MiMinimumPagesPerRun;
 static CLIENT_ID MiBalancerThreadId;
@@ -53,8 +45,6 @@
 MmInitializeBalancer(ULONG NrAvailablePages, ULONG NrSystemPages)
 {
    memset(MiMemoryConsumers, 0, sizeof(MiMemoryConsumers));
-   InitializeListHead(&AllocationListHead);
-   KeInitializeSpinLock(&AllocationListLock);
    MiNrTotalPages = NrAvailablePages;
@@ -86,18 +76,10 @@
    MiMemoryConsumers[Consumer].Trim = Trim;
 }
-VOID
-NTAPI
-MiZeroPhysicalPage(
-    IN PFN_NUMBER PageFrameIndex
-);
-
 NTSTATUS
 NTAPI
 MmReleasePageMemoryConsumer(ULONG Consumer, PFN_NUMBER Page)
 {
-   PMM_ALLOCATION_REQUEST Request;
-   PLIST_ENTRY Entry;
    KIRQL OldIrql;
    if (Page == 0)
@@ -110,26 +92,11 @@
    {
       if(Consumer == MC_USER) MmRemoveLRUUserPage(Page);
       (void)InterlockedDecrementUL(&MiMemoryConsumers[Consumer].PagesUsed);
-      if ((Entry = ExInterlockedRemoveHeadList(&AllocationListHead,
&AllocationListLock)) == NULL)
-      {
-         OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
-         MmDereferencePage(Page);
-         KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
-      }
-      else
-      {
-         Request = CONTAINING_RECORD(Entry, MM_ALLOCATION_REQUEST, ListEntry);
-         MiZeroPhysicalPage(Page);
-         Request->Page = Page;
-         KeSetEvent(&Request->Event, IO_NO_INCREMENT, FALSE);
-      }
    }
-   else
-   {
-      OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
-      MmDereferencePage(Page);
-      KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
-   }
+
+   OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+   MmDereferencePage(Page);
+   KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
    return(STATUS_SUCCESS);
 }
@@ -149,10 +116,13 @@
         return InitialTarget;
     }
-    if (MiMemoryConsumers[Consumer].PagesUsed >
MiMemoryConsumers[Consumer].PagesTarget)
-    {
-        /* Consumer page limit exceeded */
-        Target = max(Target, MiMemoryConsumers[Consumer].PagesUsed -
MiMemoryConsumers[Consumer].PagesTarget);
+    // if (MiFreeSwapPages == 0)
+    {
+        if (MiMemoryConsumers[Consumer].PagesUsed >
MiMemoryConsumers[Consumer].PagesTarget)
+        {
+            /* Consumer page limit exceeded */
+            Target = max(Target, MiMemoryConsumers[Consumer].PagesUsed -
MiMemoryConsumers[Consumer].PagesTarget);
+        }
     }
     if (MmAvailablePages < MiMinimumAvailablePages)
     {
@@ -162,13 +132,6 @@
     if (Target)
     {
-        if (!InitialTarget)
-        {
-            /* If there was no initial target,
-             * swap at least MiMinimumPagesPerRun */
-            Target = max(Target, MiMinimumPagesPerRun);
-        }
-
         /* Now swap the pages out */
         Status = MiMemoryConsumers[Consumer].Trim(Target, 0, &NrFreedPages);
@@ -265,8 +228,9 @@
     * Make sure we don't exceed our individual target.
     */
    PagesUsed = InterlockedIncrementUL(&MiMemoryConsumers[Consumer].PagesUsed);
-   if (PagesUsed > MiMemoryConsumers[Consumer].PagesTarget &&
-       !MiIsBalancerThread())
+   if ((PagesUsed > MiMemoryConsumers[Consumer].PagesTarget) &&
+       !MiIsBalancerThread() &&
+       (MmNumberOfPagingFiles == 0))
    {
       MmRebalanceMemoryConsumers();
    }
@@ -285,52 +249,6 @@
       }
       if (Consumer == MC_USER) MmInsertLRULastUserPage(Page);
       *AllocatedPage = Page;
-      if (MmAvailablePages < MiMinimumAvailablePages)
-          MmRebalanceMemoryConsumers();
-      return(STATUS_SUCCESS);
-   }
-
-   /*
-    * Make sure we don't exceed global targets.
-    */
-   if (MmAvailablePages < MiMinimumAvailablePages)
-   {
-      MM_ALLOCATION_REQUEST Request;
-
-      if (!CanWait)
-      {
-         (void)InterlockedDecrementUL(&MiMemoryConsumers[Consumer].PagesUsed);
-         MmRebalanceMemoryConsumers();
-         return(STATUS_NO_MEMORY);
-      }
-
-      /* Insert an allocation request. */
-      Request.Page = 0;
-      KeInitializeEvent(&Request.Event, NotificationEvent, FALSE);
-
-      ExInterlockedInsertTailList(&AllocationListHead, &Request.ListEntry,
&AllocationListLock);
-      MmRebalanceMemoryConsumers();
-
-      KeWaitForSingleObject(&Request.Event,
-                            0,
-                            KernelMode,
-                            FALSE,
-                            NULL);
-
-      Page = Request.Page;
-      if (Page == 0)
-      {
-         KeBugCheck(NO_PAGES_AVAILABLE);
-      }
-
-      if(Consumer == MC_USER) MmInsertLRULastUserPage(Page);
-      *AllocatedPage = Page;
-
-      if (MmAvailablePages < MiMinimumAvailablePages)
-      {
-          MmRebalanceMemoryConsumers();
-      }
-
       return(STATUS_SUCCESS);
    }
@@ -347,91 +265,246 @@
    if(Consumer == MC_USER) MmInsertLRULastUserPage(Page);
    *AllocatedPage = Page;
-   if (MmAvailablePages < MiMinimumAvailablePages)
-   {
-       MmRebalanceMemoryConsumers();
-   }
-
    return(STATUS_SUCCESS);
 }
+/* Old implementation of the balancer thread */
+static
+VOID
+NTAPI
+MiRosBalancerThread(VOID)
+{
+    ULONG InitialTarget = 0;
+
+    DPRINT1("Legacy MM balancer in action!\n");
+
+#if (_MI_PAGING_LEVELS == 2)
+    if (!MiIsBalancerThread())
+    {
+        /* Clean up the unused PDEs */
+        ULONG_PTR Address;
+        PEPROCESS Process = PsGetCurrentProcess();
+
+        /* Acquire PFN lock */
+        KIRQL OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+        PMMPDE pointerPde;
+        for (Address = (ULONG_PTR) MI_LOWEST_VAD_ADDRESS ;
+                Address < (ULONG_PTR) MM_HIGHEST_VAD_ADDRESS; Address += (PAGE_SIZE *
PTE_COUNT))
+        {
+            if (MiQueryPageTableReferences((PVOID) Address) == 0)
+            {
+                pointerPde = MiAddressToPde(Address);
+                if (pointerPde->u.Hard.Valid)
+                    MiDeletePte(pointerPde, MiPdeToPte(pointerPde), Process, NULL);
+                ASSERT(pointerPde->u.Hard.Valid == 0);
+            }
+        }
+        /* Release lock */
+        KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+    }
+#endif
+    do
+    {
+        ULONG OldTarget = InitialTarget;
+        ULONG i;
+
+        /* Trim each consumer */
+        for (i = 0; i < MC_MAXIMUM; i++)
+        {
+            InitialTarget = MiTrimMemoryConsumer(i, InitialTarget);
+        }
+
+        /* No pages left to swap! */
+        if (InitialTarget != 0 && InitialTarget == OldTarget)
+        {
+            /* Game over */
+            KeBugCheck(NO_PAGES_AVAILABLE);
+        }
+    } while (InitialTarget != 0);
+}
 VOID NTAPI
 MiBalancerThread(PVOID Unused)
 {
-   PVOID WaitObjects[2];
-   NTSTATUS Status;
-   ULONG i;
-
-   WaitObjects[0] = &MiBalancerEvent;
-   WaitObjects[1] = &MiBalancerTimer;
-
-   while (1)
-   {
-      Status = KeWaitForMultipleObjects(2,
-                                        WaitObjects,
-                                        WaitAny,
-                                        Executive,
-                                        KernelMode,
-                                        FALSE,
-                                        NULL,
-                                        NULL);
-
-      if (Status == STATUS_WAIT_0 || Status == STATUS_WAIT_1)
-      {
-        ULONG InitialTarget = 0;
-
-#if (_MI_PAGING_LEVELS == 2)
-        if (!MiIsBalancerThread())
-        {
-            /* Clean up the unused PDEs */
-            ULONG_PTR Address;
-            PEPROCESS Process = PsGetCurrentProcess();
-
-            /* Acquire PFN lock */
-            KIRQL OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
-            PMMPDE pointerPde;
-            for (Address = (ULONG_PTR)MI_LOWEST_VAD_ADDRESS;
-                 Address < (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS;
-                 Address += (PAGE_SIZE * PTE_COUNT))
-            {
-                if (MiQueryPageTableReferences((PVOID)Address) == 0)
+    PVOID WaitObjects[2];
+    NTSTATUS Status;
+    ULONG i;
+    PVOID LastPagedPoolAddress = MmPagedPoolStart;
+#ifdef TEST_PAGING
+    PULONG PagingTestBuffer;
+    BOOLEAN TestPage = FALSE;
+#endif
+
+    WaitObjects[0] = &MiBalancerTimer;
+    WaitObjects[1] = &MiBalancerEvent;
+
+#ifdef TEST_PAGING
+    PagingTestBuffer = ExAllocatePoolWithTag(PagedPool, PAGE_SIZE, 'tseT');
+    DPRINT1("PagingTestBuffer: %p, PTE %p\n", PagingTestBuffer,
MiAddressToPte(PagingTestBuffer));
+    for (i = 0; i < (PAGE_SIZE / sizeof(ULONG)); i++)
+        PagingTestBuffer[i] = i;
+#endif
+
+    while (1)
+    {
+        /* For now, we only age the paged pool, with 100 pages per run. */
+        LONG Count = 100;
+        PVOID Address = LastPagedPoolAddress;
+        KIRQL OldIrql;
+        PMMPTE PointerPte;
+        PMMPDE PointerPde;
+        ULONG PdeIndex;
+        MMPTE TempPte;
+        PMMPFN Pfn1;
+        BOOLEAN Flush;
+
+        Status = KeWaitForMultipleObjects(
+            2,
+            WaitObjects,
+            WaitAny,
+            Executive,
+            KernelMode,
+            FALSE,
+            NULL,
+            NULL);
+        if (!NT_SUCCESS(Status))
+        {
+            DPRINT1("KeWaitForMultipleObjects failed, status = %x\n", Status);
+            KeBugCheck(MEMORY_MANAGEMENT);
+        }
+
+        DPRINT("MM Balancer: Starting to loop.\n");
+
+        while (Count-- > 0)
+        {
+            Address = (PVOID)((ULONG_PTR) Address + PAGE_SIZE);
+
+            /* Acquire PFN lock while we are cooking */
+            OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+
+            if (Address > MmPagedPoolEnd)
+            {
+                Address = MmPagedPoolStart;
+            }
+
+            PointerPde = MiAddressToPde(Address);
+            PointerPte = MiAddressToPte(Address);
+
+            PdeIndex = ((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) /
sizeof(MMPTE);
+
+            if (MmSystemPagePtes[PdeIndex].u.Hard.Valid == 0)
+                TempPte.u.Long = 0;
+            else
+                TempPte = *PointerPte;
+
+            if (TempPte.u.Hard.Valid == 0)
+            {
+                /* Bad luck, not a paged-in address */
+#ifdef TEST_PAGING
+                if (PointerPte == MiAddressToPte(PagingTestBuffer))
                 {
-                    pointerPde = MiAddressToPde(Address);
-                    if (pointerPde->u.Hard.Valid)
-                        MiDeletePte(pointerPde, MiPdeToPte(pointerPde), Process, NULL);
-                    ASSERT(pointerPde->u.Hard.Valid == 0);
+                    /* Of course it should not magically become a prototype pte */
+                    ASSERT(TempPte.u.Soft.Prototype == 0);
+                    if (TempPte.u.Soft.Transition == 0)
+                    {
+                        /* It was paged out! */
+                        ASSERT(TempPte.u.Soft.PageFileHigh != 0);
+                        TestPage = TRUE;
+                    }
                 }
-            }
-            /* Release lock */
+#endif
+                KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+                continue;
+            }
+
+            Flush = FALSE;
+
+            /* Get the Pfn */
+            Pfn1 = MI_PFN_ELEMENT(PFN_FROM_PTE(&TempPte));
+            ASSERT(Pfn1->PteAddress == PointerPte);
+            ASSERT(Pfn1->u3.e1.PrototypePte == 0);
+
+            /* First check if it was written to */
+            if (TempPte.u.Hard.Dirty)
+                Pfn1->u3.e1.Modified = 1;
+
+            /* See if it was accessed since the last time we looked */
+            if (TempPte.u.Hard.Accessed)
+            {
+                /* Yes! Mark the page as young and fresh again */
+                Pfn1->Wsle.u1.e1.Age = 0;
+                /* Tell the CPU we want to know for the next time */
+                TempPte.u.Hard.Accessed = 0;
+                MI_UPDATE_VALID_PTE(PointerPte, TempPte);
+                Flush = TRUE;
+            }
+            else if (Pfn1->Wsle.u1.e1.Age == 3)
+            {
+                /* Page is getting old: Mark the PTE as transition */
+                DPRINT1("MM Balancer: putting %p (page %x) as transition.\n",
PointerPte,
+                    PFN_FROM_PTE(&TempPte));
+                TempPte.u.Hard.Valid = 0;
+                TempPte.u.Soft.Transition = 1;
+                TempPte.u.Trans.Protection = MM_READWRITE;
+                MiDecrementShareCount(Pfn1, PFN_FROM_PTE(&TempPte));
+                MI_WRITE_INVALID_PTE(PointerPte, TempPte);
+                Flush = TRUE;
+            }
+            else
+            {
+                /* The page is just getting older */
+                Pfn1->Wsle.u1.e1.Age++;
+            }
+
+            /* Flush the TLB entry if we have to */
+            if (Flush)
+            {
+#ifdef CONFIG_SMP
+                // FIXME: Should invalidate entry in every CPU TLB
+                ASSERT(FALSE);
+#endif
+                KeInvalidateTlbEntry(Address);
+            }
+
+            /* Unlock, we're done for this address */
             KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
         }
-#endif
-          do
-          {
-              ULONG OldTarget = InitialTarget;
-
-              /* Trim each consumer */
-              for (i = 0; i < MC_MAXIMUM; i++)
-              {
-                  InitialTarget = MiTrimMemoryConsumer(i, InitialTarget);
-              }
-
-              /* No pages left to swap! */
-              if (InitialTarget != 0 &&
-                  InitialTarget == OldTarget)
-              {
-                  /* Game over */
-                  KeBugCheck(NO_PAGES_AVAILABLE);
-              }
-          } while (InitialTarget != 0);
-      }
-      else
-      {
-         DPRINT1("KeWaitForMultipleObjects failed, status = %x\n", Status);
-         KeBugCheck(MEMORY_MANAGEMENT);
-      }
-   }
+
+        DPRINT("MM Balancer: End of the loop.\n");
+
+#ifdef TEST_PAGING
+        if (TestPage)
+        {
+            BOOLEAN Passed = TRUE;
+            DPRINT1("Verifying data integrity of paged out data!\n");
+            for (i = 0; i < (PAGE_SIZE / sizeof(ULONG)); i++)
+            {
+                if (PagingTestBuffer[i] != i)
+                    Passed = FALSE;
+            }
+            if (Passed)
+                DPRINT1("PASSED! \\o/\n");
+            else
+                DPRINT1("FAILED /o\\\n");
+            TestPage = FALSE;
+        }
+#endif
+        /* Remember this for the next run */
+        LastPagedPoolAddress = Address;
+
+        if (Status == STATUS_WAIT_1)
+        {
+            /* Balancer thread was woken up by legacy MM
+             * This means we are really starving. Let's see if this still a problem
*/
+            if ((MmAvailablePages >= MmMinimumFreePages) &&
(MmNumberOfPagingFiles != 0))
+            {
+                /* Yay! No need to trust the old balancer */
+                continue;
+            }
+
+            MiRosBalancerThread();
+        }
+    }
 }
 VOID
Modified: branches/TransitionPte/ntoskrnl/mm/mminit.c
URL:
http://svn.reactos.org/svn/reactos/branches/TransitionPte/ntoskrnl/mm/mmini…
==============================================================================
--- branches/TransitionPte/ntoskrnl/mm/mminit.c [iso-8859-1] (original)
+++ branches/TransitionPte/ntoskrnl/mm/mminit.c [iso-8859-1] Mon Sep 22 17:20:38 2014
@@ -299,36 +299,179 @@
 NTAPI
 MmMpwThreadMain(PVOID Parameter)
 {
-   NTSTATUS Status;
-   ULONG PagesWritten;
-   LARGE_INTEGER Timeout;
-
-   UNREFERENCED_PARAMETER(Parameter);
-
-   Timeout.QuadPart = -50000000;
-
-   for(;;)
-   {
-      Status = KeWaitForSingleObject(&MpwThreadEvent,
-                                     0,
-                                     KernelMode,
-                                     FALSE,
-                                     &Timeout);
-      if (!NT_SUCCESS(Status))
-      {
-         DbgPrint("MpwThread: Wait failed\n");
-         KeBugCheck(MEMORY_MANAGEMENT);
-         return;
-      }
-
-      PagesWritten = 0;
-
-#ifndef NEWCC
-      // XXX arty -- we flush when evicting pages or destorying cache
-      // sections.
-      CcRosFlushDirtyPages(128, &PagesWritten, FALSE);
-#endif
-   }
+    NTSTATUS Status;
+    PMMPTE SysPte, PteTable;
+
+    UNREFERENCED_PARAMETER(Parameter);
+
+    /* Reserve a PTE for the page table */
+    SysPte = MiReserveSystemPtes(1, SystemPteSpace);
+    ASSERT(SysPte != NULL);
+    PteTable = MiPteToAddress(SysPte);
+
+    for(;;)
+    {
+        KIRQL OldIrql;
+        PFN_NUMBER PageFrameIndex;
+        PMMPFN Pfn;
+        MMPTE TempPte, PdePte;
+        PMMPTE PointerPte;
+        BOOLEAN PageFileEntryFromPage = FALSE;
+        /*
+         * To start working, we wait for two conditions:
+         *  - there are pages to be paged out.
+         *  - We are in a low memory situation.
+         */
+        Status = KeWaitForSingleObject(
+            &MpwThreadEvent,
+            WrPageOut,
+            KernelMode,
+            FALSE,
+            NULL);
+
+        DPRINT("THE KRAKEN WAS RELEASED AND WILL PAGE YOUR ASS OUT!\n");
+
+        if (!NT_SUCCESS(Status))
+        {
+            DPRINT1("MpwThread: Wait failed\n");
+            KeBugCheck(MEMORY_MANAGEMENT);
+            return;
+        }
+
+        /* Lock the PFN database */
+        OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+
+        /* The true main loop */
+        while ((MmTotalPagesForPagingFile != 0) && (MiFreeSwapPages != 0))
+        {
+            /* Get the first page from the list */
+            PageFrameIndex = MmModifiedPageListByColor[0].Flink;
+
+            /* Get The Pfn */
+            Pfn = MI_PFN_ELEMENT(PageFrameIndex);
+
+            /* Some things which must always hold */
+            ASSERT(Pfn->OriginalPte.u.Soft.Transition == 0);
+            ASSERT(Pfn->u3.ReferenceCount == 0);
+
+            /* And some that are not yet supported */
+            ASSERT(Pfn->OriginalPte.u.Soft.Prototype == 0);
+
+            /* Maybe this is not the first time */
+            if ((Pfn->OriginalPte.u.Soft.Prototype == 0) &&
+                    (Pfn->OriginalPte.u.Soft.PageFileHigh != 0))
+            {
+                /* Use that */
+                TempPte = Pfn->OriginalPte;
+                PageFileEntryFromPage = TRUE;
+            }
+            else
+            {
+                MiReservePageFileEntry(&TempPte);
+            }
+
+            /* Get it out of the list and reference it */
+            MiUnlinkPageFromList(Pfn);
+            MiReferenceUnusedPageAndBumpLockCount(Pfn);
+
+            ASSERT(Pfn->u3.e1.PageLocation == ModifiedPageList);
+
+            /* Mark it as write in progress */
+            Pfn->u3.e1.WriteInProgress = 1;
+            Pfn->u1.Event = NULL;
+
+            /* Release the PFN lock while we are writing */
+            KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+
+            /* Do the actual write */
+            Status = MiWritePageFile(PageFrameIndex, &TempPte);
+
+            /* Get the lock again */
+            OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+
+            /* Get a mapping to the page table */
+            MI_MAKE_HARDWARE_PTE_KERNEL(&PdePte,
+                                        SysPte,
+                                        MM_READWRITE,
+                                        Pfn->u4.PteFrame);
+            MI_WRITE_VALID_PTE(SysPte, PdePte);
+
+            /* Finally get a pointer to the PTE this page represents */
+            PointerPte =
&PteTable[MiAddressToPteOffset(MiPteToAddress(Pfn->PteAddress))];
+
+            /* Get relevant values from the original PTE */
+            TempPte.u.Soft.Protection = PointerPte->u.Soft.Protection;
+
+            /* Maybe someone aborted the operation */
+            if (Pfn->u3.e1.WriteInProgress == 0)
+            {
+                DPRINT1("Someone aborted the page-out operation!\n");
+                /* Just set the event and let the waiter go along */
+                ASSERT(Pfn->u1.Event != NULL);
+                KeSetEvent(Pfn->u1.Event, IO_NO_INCREMENT, FALSE);
+                Pfn->u1.Event = NULL;
+                /* This is now useless */
+                if (!PageFileEntryFromPage)
+                    MiFreePageFileEntry(&TempPte);
+            }
+            /* Maybe someone tried to access the page while we were not looking */
+            else if (Pfn->u1.Event != NULL)
+            {
+                DPRINT1("Page fault occured while we were paging out!\n");
+
+                /* So the page fault handler marked the page as unmodified */
+                ASSERT(Pfn->u3.e1.Modified == 0);
+
+                /* Save the pagefile entry for this page */
+                Pfn->OriginalPte = TempPte;
+                KeSetEvent(Pfn->u1.Event, IO_NO_INCREMENT, FALSE);
+                Pfn->u1.Event = NULL;
+            }
+            else if (!NT_SUCCESS(Status))
+            {
+                DPRINT1("Failed to write page to pagefile!\n");
+                /* This is now useless */
+                if (!PageFileEntryFromPage)
+                    MiFreePageFileEntry(&TempPte);
+                /* MiDereferencePfnAndDropLockCount will put it back on the tail of the
list */
+            }
+            else
+            {
+                DPRINT1("Page %x successfully paged out. PTE pointer %p (->
%x)\n",
+                    PageFrameIndex, Pfn->PteAddress, TempPte.u.Long);
+
+                /* Of course it must already be invalid */
+                ASSERT(PointerPte->u.Hard.Valid == 0);
+
+                /* And be in transition */
+                ASSERT(PointerPte->u.Soft.Transition == 1);
+
+                /* So the PTE is now officially paged out */
+                MI_WRITE_INVALID_PTE(PointerPte, TempPte);
+
+                /* And the pagefile entry belongs to the PTE, not to the page! */
+                Pfn->OriginalPte.u.Long = 0;
+
+                /* And dereference the page table frame */
+                MiDecrementShareCount(MI_PFN_ELEMENT(Pfn->u4.PteFrame),
Pfn->u4.PteFrame);
+                /* We can finally make it available for real */
+                MI_SET_PFN_DELETED(Pfn);
+            }
+
+            /* We're done with this */
+            Pfn->u3.e1.WriteInProgress = 0;
+
+            /* This will put it back in the free list */
+            MiDereferencePfnAndDropLockCount(Pfn);
+
+            /* Unmap the Page Table */
+            MI_ERASE_PTE(SysPte);
+            KeInvalidateTlbEntry(PteTable);
+        }
+
+        /* We're done for this run */
+        KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+    }
 }
 NTSTATUS
Modified: branches/TransitionPte/ntoskrnl/mm/pagefile.c
URL:
http://svn.reactos.org/svn/reactos/branches/TransitionPte/ntoskrnl/mm/pagef…
==============================================================================
--- branches/TransitionPte/ntoskrnl/mm/pagefile.c       [iso-8859-1] (original)
+++ branches/TransitionPte/ntoskrnl/mm/pagefile.c       [iso-8859-1] Mon Sep 22 17:20:38
2014
@@ -46,13 +46,14 @@
 {
    LIST_ENTRY PagingFileListEntry;
    PFILE_OBJECT FileObject;
-   LARGE_INTEGER MaximumSize;
-   LARGE_INTEGER CurrentSize;
+   ULONG_PTR MaximumSize;
+   ULONG_PTR CurrentSize;
    PFN_NUMBER FreePages;
    PFN_NUMBER UsedPages;
-   PULONG AllocMap;
+   PVOID AllocMap;
+   RTL_BITMAP AllocBitMap;
+   ULONG_PTR AllocBitMapHint;
    KSPIN_LOCK AllocMapLock;
-   ULONG AllocMapSize;
    PRETRIEVAL_POINTERS_BUFFER RetrievalPointers;
 }
 PAGINGFILE, *PPAGINGFILE;
@@ -72,9 +73,6 @@
 /* List of paging files, both used and free */
 static PPAGINGFILE PagingFileList[MAX_PAGING_FILES];
-
-/* Lock for examining the list of paging files */
-static KSPIN_LOCK PagingFileListLock;
 /* Number of paging files */
 ULONG MmNumberOfPagingFiles;
@@ -221,30 +219,62 @@
 NTAPI
 MmWriteToSwapPage(SWAPENTRY SwapEntry, PFN_NUMBER Page)
 {
-   ULONG i;
-   ULONG_PTR offset;
-   LARGE_INTEGER file_offset;
+    MMPTE TempPte;
+
+    TempPte.u.Long = 0;
+    TempPte.u.Soft.PageFileHigh = OFFSET_FROM_ENTRY(SwapEntry);
+    TempPte.u.Soft.PageFileLow = FILE_FROM_ENTRY(SwapEntry);
+    return MiWritePageFile(Page, &TempPte);
+}
+
+
+NTSTATUS
+NTAPI
+MmReadFromSwapPage(SWAPENTRY SwapEntry, PFN_NUMBER Page)
+{
+    MMPTE TempPte;
+
+    TempPte.u.Long = 0;
+    TempPte.u.Soft.PageFileHigh = OFFSET_FROM_ENTRY(SwapEntry);
+    TempPte.u.Soft.PageFileLow = FILE_FROM_ENTRY(SwapEntry);
+    return MiReadPageFile(Page, &TempPte);
+}
+
+NTSTATUS
+NTAPI
+MiReadPageFile(
+       _In_ PFN_NUMBER Page,
+       _In_ const MMPTE* PointerPte
+)
+{
+   LARGE_INTEGER FileOffset;
    IO_STATUS_BLOCK Iosb;
+   ULONG PageFileIndex = PointerPte->u.Soft.PageFileLow;
+   ULONG_PTR PageFileOffset = PointerPte->u.Soft.PageFileHigh;
    NTSTATUS Status;
    KEVENT Event;
-   UCHAR MdlBase[sizeof(MDL) + sizeof(ULONG)];
+   UCHAR MdlBase[sizeof(MDL) + sizeof(PFN_NUMBER)];
    PMDL Mdl = (PMDL)MdlBase;
-
-   DPRINT("MmWriteToSwapPage\n");
-
-   if (SwapEntry == 0)
+   PPAGINGFILE PagingFile;
+
+   DPRINT("MiReadPageFile\n");
+
+   if (PageFileOffset == 0)
    {
       KeBugCheck(MEMORY_MANAGEMENT);
       return(STATUS_UNSUCCESSFUL);
    }
-   i = FILE_FROM_ENTRY(SwapEntry);
-   offset = OFFSET_FROM_ENTRY(SwapEntry);
-
-   if (PagingFileList[i]->FileObject == NULL ||
-         PagingFileList[i]->FileObject->DeviceObject == NULL)
-   {
-      DPRINT1("Bad paging file 0x%.8X\n", SwapEntry);
+   /* Normalize it */
+   PageFileOffset--;
+
+   ASSERT(PageFileIndex < MAX_PAGING_FILES);
+
+   PagingFile = PagingFileList[PageFileIndex];
+
+   if (PagingFile->FileObject == NULL || PagingFile->FileObject->DeviceObject ==
NULL)
+   {
+      DPRINT1("Bad paging file %u\n", PageFileIndex);
       KeBugCheck(MEMORY_MANAGEMENT);
    }
@@ -252,80 +282,13 @@
    MmBuildMdlFromPages(Mdl, &Page);
    Mdl->MdlFlags |= MDL_PAGES_LOCKED;
-   file_offset.QuadPart = offset * PAGE_SIZE;
-   file_offset = MmGetOffsetPageFile(PagingFileList[i]->RetrievalPointers,
file_offset);
-
-   KeInitializeEvent(&Event, NotificationEvent, FALSE);
-   Status = IoSynchronousPageWrite(PagingFileList[i]->FileObject,
-                                   Mdl,
-                                   &file_offset,
-                                   &Event,
-                                   &Iosb);
-   if (Status == STATUS_PENDING)
-   {
-      KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
-      Status = Iosb.Status;
-   }
-
-   if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
-   {
-      MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
-   }
-   return(Status);
-}
-
-
-NTSTATUS
-NTAPI
-MmReadFromSwapPage(SWAPENTRY SwapEntry, PFN_NUMBER Page)
-{
-       return MiReadPageFile(Page, FILE_FROM_ENTRY(SwapEntry),
OFFSET_FROM_ENTRY(SwapEntry));
-}
-
-NTSTATUS
-NTAPI
-MiReadPageFile(
-       _In_ PFN_NUMBER Page,
-       _In_ ULONG PageFileIndex,
-       _In_ ULONG_PTR PageFileOffset)
-{
-   LARGE_INTEGER file_offset;
-   IO_STATUS_BLOCK Iosb;
-   NTSTATUS Status;
-   KEVENT Event;
-   UCHAR MdlBase[sizeof(MDL) + sizeof(ULONG)];
-   PMDL Mdl = (PMDL)MdlBase;
-   PPAGINGFILE PagingFile;
-
-   DPRINT("MiReadSwapFile\n");
-
-   if (PageFileOffset == 0)
-   {
-      KeBugCheck(MEMORY_MANAGEMENT);
-      return(STATUS_UNSUCCESSFUL);
-   }
-
-   ASSERT(PageFileIndex < MAX_PAGING_FILES);
-
-   PagingFile = PagingFileList[PageFileIndex];
-
-   if (PagingFile->FileObject == NULL || PagingFile->FileObject->DeviceObject ==
NULL)
-   {
-      DPRINT1("Bad paging file %u\n", PageFileIndex);
-      KeBugCheck(MEMORY_MANAGEMENT);
-   }
-
-   MmInitializeMdl(Mdl, NULL, PAGE_SIZE);
-   MmBuildMdlFromPages(Mdl, &Page);
-   Mdl->MdlFlags |= MDL_PAGES_LOCKED;
-
-   file_offset.QuadPart = PageFileOffset * PAGE_SIZE;
-   file_offset = MmGetOffsetPageFile(PagingFile->RetrievalPointers, file_offset);
+   FileOffset.QuadPart = PageFileOffset * PAGE_SIZE;
+   FileOffset = MmGetOffsetPageFile(PagingFile->RetrievalPointers, FileOffset);
    KeInitializeEvent(&Event, NotificationEvent, FALSE);
    Status = IoPageRead(PagingFile->FileObject,
                        Mdl,
-                       &file_offset,
+                       &FileOffset,
                        &Event,
                        &Iosb);
    if (Status == STATUS_PENDING)
@@ -340,14 +303,182 @@
    return(Status);
 }
+NTSTATUS
+NTAPI
+MiWritePageFile(
+    _In_ PFN_NUMBER Page,
+    _In_ const MMPTE* PointerPte
+)
+{
+    ULONG PageFileIndex = PointerPte->u.Soft.PageFileLow;
+    ULONG_PTR PageFileOffset = PointerPte->u.Soft.PageFileHigh;
+    LARGE_INTEGER FileOffset;
+    IO_STATUS_BLOCK Iosb;
+    NTSTATUS Status;
+    KEVENT Event;
+    UCHAR MdlBase[sizeof(MDL) + sizeof(PFN_NUMBER)];
+    PMDL Mdl = (PMDL) MdlBase;
+    PPAGINGFILE PagingFile;
+
+    /* The PTE must already be setup to point to a pagefile entry */
+    ASSERT(PointerPte->u.Hard.Valid == 0);
+    ASSERT(PointerPte->u.Soft.Transition == 0);
+    ASSERT(PointerPte->u.Soft.Prototype == 0);
+
+    DPRINT("MiWriteSwapFile\n");
+
+    if (PageFileOffset == 0)
+    {
+        KeBugCheck(MEMORY_MANAGEMENT);
+        return (STATUS_UNSUCCESSFUL);
+    }
+
+    /* Normalize it */
+    PageFileOffset--;
+
+    ASSERT(PageFileIndex < MAX_PAGING_FILES);
+
+    PagingFile = PagingFileList[PageFileIndex];
+
+    if (PagingFile->FileObject == NULL || PagingFile->FileObject->DeviceObject
== NULL)
+    {
+        DPRINT1("Bad paging file %u\n", PageFileIndex);
+        KeBugCheck(MEMORY_MANAGEMENT);
+    }
+
+    MmInitializeMdl(Mdl, NULL, PAGE_SIZE);
+    MmBuildMdlFromPages(Mdl, &Page);
+    Mdl->MdlFlags |= MDL_PAGES_LOCKED;
+
+    FileOffset.QuadPart = PageFileOffset * PAGE_SIZE;
+    FileOffset = MmGetOffsetPageFile(PagingFile->RetrievalPointers, FileOffset);
+
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+    Status = IoSynchronousPageWrite(PagingFile->FileObject,
+                                    Mdl,
+                                    &FileOffset,
+                                    &Event,
+                                    &Iosb);
+    if (Status == STATUS_PENDING)
+    {
+        KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+        Status = Iosb.Status;
+    }
+    if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
+    {
+        MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl);
+    }
+    return Status;
+}
+
+VOID
+NTAPI
+MiFreePageFileEntry(
+    _In_ PMMPTE PointerPte
+)
+{
+    ULONG PageFileIndex;
+    ULONG_PTR PageFileOffset;
+    PPAGINGFILE PageFile;
+
+    /* Some sanity checks */
+    ASSERT(PointerPte->u.Hard.Valid == 0);
+    ASSERT(PointerPte->u.Soft.Transition == 0);
+    ASSERT(PointerPte->u.Soft.Prototype == 0);
+
+    /* We must hold the PFN lock */
+    ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
+
+    PageFileIndex = PointerPte->u.Soft.PageFileLow;
+    PageFileOffset = PointerPte->u.Soft.PageFileHigh;
+
+    /* Fix up the value */
+    ASSERT(PageFileOffset != 0);
+    PageFileOffset--;
+
+    DPRINT1("Releasing page file entry %u, %lu\n", PageFileIndex,
PageFileOffset);
+
+    ASSERT(PageFileIndex < MAX_PAGING_FILES);
+
+    PageFile = PagingFileList[PageFileIndex];
+
+    /* And also lock this pagefile */
+    KeAcquireSpinLockAtDpcLevel(&PageFile->AllocMapLock);
+
+    /* It must be already in use */
+    ASSERT(RtlTestBit(&PageFile->AllocBitMap, PageFileOffset));
+    RtlClearBit(&PageFile->AllocBitMap, PageFileOffset);
+
+    /* One more */
+    PageFile->FreePages++;
+    PageFile->UsedPages--;
+
+    MiFreeSwapPages++;
+    MiUsedSwapPages--;
+
+    /* Done */
+    KeReleaseSpinLockFromDpcLevel(&PageFile->AllocMapLock);
+}
+
+NTSTATUS
+NTAPI
+MiReservePageFileEntry(
+    _Out_ PMMPTE PointerPte
+)
+{
+    ULONG PageFileIndex;
+    ULONG_PTR PageFileOffset;
+    PPAGINGFILE PageFile;
+
+    /* We must hold the PFN lock */
+    ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
+
+    for (PageFileIndex = 0; PageFileIndex < MAX_PAGING_FILES; PageFileIndex++)
+    {
+        if (PagingFileList[PageFileIndex] == NULL)
+            continue;
+        if (PagingFileList[PageFileIndex]->FreePages != 0)
+            break;
+    }
+
+    if (PageFileIndex == MAX_PAGING_FILES)
+    {
+        DPRINT1("Pagefiles are full!\n");
+        return STATUS_PAGEFILE_QUOTA;
+    }
+
+    PointerPte->u.Long = 0;
+
+    PageFile = PagingFileList[PageFileIndex];
+
+    /* And also lock this page file */
+    KeAcquireSpinLockAtDpcLevel(&PageFile->AllocMapLock);
+
+    /* Find a free entry in this page file */
+    PageFileOffset = RtlFindClearBitsAndSet(&PageFile->AllocBitMap, 1,
PageFile->AllocBitMapHint);
+
+    /* We must have found something as the file was marked as having free entries */
+    ASSERT(PageFileOffset != ~((ULONG_PTR)0));
+    PageFile->AllocBitMapHint = PageFileOffset & ~((PageFile->MaximumSize /
PAGE_SIZE) - 1);
+
+    DPRINT1("Reserving page file entry %lu, %Iu\n", PageFileIndex,
PageFileOffset);
+
+    /* Done */
+    KeReleaseSpinLockFromDpcLevel(&PageFile->AllocMapLock);
+
+    /* We rely on PageFileHigh being != 0 */
+    PointerPte->u.Soft.PageFileHigh = PageFileOffset + 1;
+    PointerPte->u.Soft.PageFileLow = PageFileIndex;
+
+    return STATUS_SUCCESS;
+}
+
 VOID
 INIT_FUNCTION
 NTAPI
 MmInitPagingFile(VOID)
 {
    ULONG i;
-
-   KeInitializeSpinLock(&PagingFileListLock);
    MiFreeSwapPages = 0;
    MiUsedSwapPages = 0;
@@ -360,104 +491,41 @@
    MmNumberOfPagingFiles = 0;
 }
-static ULONG
-MiAllocPageFromPagingFile(PPAGINGFILE PagingFile)
-{
-   KIRQL oldIrql;
-   ULONG i, j;
-
-   KeAcquireSpinLock(&PagingFile->AllocMapLock, &oldIrql);
-
-   for (i = 0; i < PagingFile->AllocMapSize; i++)
-   {
-      for (j = 0; j < 32; j++)
-      {
-         if (!(PagingFile->AllocMap[i] & (1 << j)))
-         {
-            PagingFile->AllocMap[i] |= (1 << j);
-            PagingFile->UsedPages++;
-            PagingFile->FreePages--;
-            KeReleaseSpinLock(&PagingFile->AllocMapLock, oldIrql);
-            return((i * 32) + j);
-         }
-      }
-   }
-
-   KeReleaseSpinLock(&PagingFile->AllocMapLock, oldIrql);
-   return(0xFFFFFFFF);
-}
-
 VOID
 NTAPI
 MmFreeSwapPage(SWAPENTRY Entry)
 {
-   ULONG i;
-   ULONG_PTR off;
-   KIRQL oldIrql;
-
-   i = FILE_FROM_ENTRY(Entry);
-   off = OFFSET_FROM_ENTRY(Entry);
-
-   KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
-   if (PagingFileList[i] == NULL)
-   {
-      KeBugCheck(MEMORY_MANAGEMENT);
-   }
-   KeAcquireSpinLockAtDpcLevel(&PagingFileList[i]->AllocMapLock);
-
-   PagingFileList[i]->AllocMap[off >> 5] &= (~(1 << (off % 32)));
-
-   PagingFileList[i]->FreePages++;
-   PagingFileList[i]->UsedPages--;
-
-   MiFreeSwapPages++;
-   MiUsedSwapPages--;
-
-   KeReleaseSpinLockFromDpcLevel(&PagingFileList[i]->AllocMapLock);
-   KeReleaseSpinLock(&PagingFileListLock, oldIrql);
+    MMPTE TempPte;
+    KIRQL OldIrql;
+
+    TempPte.u.Long = 0;
+    TempPte.u.Soft.PageFileLow = FILE_FROM_ENTRY(Entry);
+    TempPte.u.Soft.PageFileHigh = OFFSET_FROM_ENTRY(Entry);
+
+    OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+    MiFreePageFileEntry(&TempPte);
+    KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
 }
 SWAPENTRY
 NTAPI
 MmAllocSwapPage(VOID)
 {
-   KIRQL oldIrql;
-   ULONG i;
-   ULONG off;
-   SWAPENTRY entry;
-
-   KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
-
-   if (MiFreeSwapPages == 0)
-   {
-      KeReleaseSpinLock(&PagingFileListLock, oldIrql);
-      return(0);
-   }
-
-   for (i = 0; i < MAX_PAGING_FILES; i++)
-   {
-      if (PagingFileList[i] != NULL &&
-            PagingFileList[i]->FreePages >= 1)
-      {
-         off = MiAllocPageFromPagingFile(PagingFileList[i]);
-         if (off == 0xFFFFFFFF)
-         {
-            KeBugCheck(MEMORY_MANAGEMENT);
-            KeReleaseSpinLock(&PagingFileListLock, oldIrql);
-            return(STATUS_UNSUCCESSFUL);
-         }
-         MiUsedSwapPages++;
-         MiFreeSwapPages--;
-         KeReleaseSpinLock(&PagingFileListLock, oldIrql);
-
-         entry = ENTRY_FROM_FILE_OFFSET(i, off);
-         return(entry);
-      }
-   }
-
-   KeReleaseSpinLock(&PagingFileListLock, oldIrql);
-   KeBugCheck(MEMORY_MANAGEMENT);
-   return(0);
+    MMPTE TempPte;
+    SWAPENTRY entry;
+    NTSTATUS Status;
+    KIRQL OldIrql;
+
+    OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+    Status = MiReservePageFileEntry(&TempPte);
+    KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+
+    if (!NT_SUCCESS(Status))
+        return 0;
+
+    entry = ENTRY_FROM_FILE_OFFSET(TempPte.u.Soft.PageFileLow,
TempPte.u.Soft.PageFileHigh);
+
+    return entry;
 }
 static PRETRIEVEL_DESCRIPTOR_LIST FASTCALL
@@ -488,7 +556,7 @@
    IO_STATUS_BLOCK IoStatus;
    PFILE_OBJECT FileObject;
    PPAGINGFILE PagingFile;
-   KIRQL oldIrql;
+   KIRQL OldIrql;
    ULONG AllocMapSize;
    FILE_FS_SIZE_INFORMATION FsSizeInformation;
    PRETRIEVEL_DESCRIPTOR_LIST RetDescList;
@@ -646,7 +714,6 @@
 #if defined(__GNUC__)
    Vcn.QuadPart = 0LL;
 #else
-
    Vcn.QuadPart = 0;
 #endif
@@ -718,16 +785,15 @@
    RtlZeroMemory(PagingFile, sizeof(*PagingFile));
    PagingFile->FileObject = FileObject;
-   PagingFile->MaximumSize.QuadPart = SafeMaximumSize.QuadPart;
-   PagingFile->CurrentSize.QuadPart = SafeInitialSize.QuadPart;
-   PagingFile->FreePages = (ULONG)(SafeInitialSize.QuadPart / PAGE_SIZE);
+   PagingFile->MaximumSize = SafeMaximumSize.LowPart;
+   PagingFile->CurrentSize = SafeInitialSize.LowPart;
+   PagingFile->FreePages = (ULONG)(SafeMaximumSize.LowPart / PAGE_SIZE);
    PagingFile->UsedPages = 0;
    KeInitializeSpinLock(&PagingFile->AllocMapLock);
    AllocMapSize = (PagingFile->FreePages / 32) + 1;
    PagingFile->AllocMap = ExAllocatePool(NonPagedPool,
                                          AllocMapSize * sizeof(ULONG));
-   PagingFile->AllocMapSize = AllocMapSize;
    if (PagingFile->AllocMap == NULL)
    {
@@ -760,8 +826,10 @@
       return(STATUS_NO_MEMORY);
    }
-   RtlZeroMemory(PagingFile->AllocMap, AllocMapSize * sizeof(ULONG));
    RtlZeroMemory(PagingFile->RetrievalPointers, Size);
+
+   RtlInitializeBitMap(&PagingFile->AllocBitMap, PagingFile->AllocMap,
AllocMapSize * 32);
+   RtlClearAllBits(&PagingFile->AllocBitMap);
    Count = 0;
    PagingFile->RetrievalPointers->ExtentCount = ExtentCount;
@@ -799,7 +867,7 @@
       PagingFile->RetrievalPointers->Extents[i].NextVcn.QuadPart *=
BytesPerAllocationUnit;
    }
-   KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
+   OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
    for (i = 0; i < MAX_PAGING_FILES; i++)
    {
       if (PagingFileList[i] == NULL)
@@ -810,7 +878,7 @@
    }
    MiFreeSwapPages = MiFreeSwapPages + PagingFile->FreePages;
    MmNumberOfPagingFiles++;
-   KeReleaseSpinLock(&PagingFileListLock, oldIrql);
+   KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
    ZwClose(FileHandle);