[NTOSKRNL]
[reactos.git] / reactos / ntoskrnl / mm / balance.c
index cc7b59d..5ddf0a5 100644 (file)
@@ -5,6 +5,7 @@
  * PURPOSE:         kernel memory managment functions
  *
  * PROGRAMMERS:     David Welch (welch@cwcom.net)
+ *                  Cameron Gutman (cameron.gutman@reactos.org)
  */
 
 /* INCLUDES *****************************************************************/
 /* TYPES ********************************************************************/
 typedef struct _MM_ALLOCATION_REQUEST
 {
-   PFN_TYPE Page;
+   PFN_NUMBER Page;
    LIST_ENTRY ListEntry;
    KEVENT Event;
 }
 MM_ALLOCATION_REQUEST, *PMM_ALLOCATION_REQUEST;
-
 /* GLOBALS ******************************************************************/
 
 MM_MEMORY_CONSUMER MiMemoryConsumers[MC_MAXIMUM];
@@ -36,25 +36,15 @@ static ULONG MiMinimumAvailablePages;
 static ULONG MiNrTotalPages;
 static LIST_ENTRY AllocationListHead;
 static KSPIN_LOCK AllocationListLock;
-static ULONG MiPagesRequired = 0;
-static ULONG MiMinimumPagesPerRun = 10;
+static ULONG MiMinimumPagesPerRun;
 
 static CLIENT_ID MiBalancerThreadId;
 static HANDLE MiBalancerThreadHandle = NULL;
 static KEVENT MiBalancerEvent;
 static KTIMER MiBalancerTimer;
-static LONG MiBalancerWork = 0;
 
 /* FUNCTIONS ****************************************************************/
 
-VOID MmPrintMemoryStatistic(VOID)
-{
-   DbgPrint("MC_CACHE %d, MC_USER %d, MC_PPOOL %d, MC_NPPOOL %d, MmAvailablePages %d\n",
-            MiMemoryConsumers[MC_CACHE].PagesUsed, MiMemoryConsumers[MC_USER].PagesUsed,
-            MiMemoryConsumers[MC_PPOOL].PagesUsed, MiMemoryConsumers[MC_NPPOOL].PagesUsed,
-            MmAvailablePages);
-}
-
 VOID
 INIT_FUNCTION
 NTAPI
@@ -67,10 +57,11 @@ MmInitializeBalancer(ULONG NrAvailablePages, ULONG NrSystemPages)
    MiNrTotalPages = NrAvailablePages;
 
    /* Set up targets. */
-   MiMinimumAvailablePages = 64;
+   MiMinimumAvailablePages = 128;
+   MiMinimumPagesPerRun = 256;
     if ((NrAvailablePages + NrSystemPages) >= 8192)
     {
-        MiMemoryConsumers[MC_CACHE].PagesTarget = NrAvailablePages / 4 * 3;   
+        MiMemoryConsumers[MC_CACHE].PagesTarget = NrAvailablePages / 4 * 3;
     }
     else if ((NrAvailablePages + NrSystemPages) >= 4096)
     {
@@ -78,15 +69,9 @@ MmInitializeBalancer(ULONG NrAvailablePages, ULONG NrSystemPages)
     }
     else
     {
-        MiMemoryConsumers[MC_CACHE].PagesTarget = NrAvailablePages / 8;        
+        MiMemoryConsumers[MC_CACHE].PagesTarget = NrAvailablePages / 8;
     }
-   MiMemoryConsumers[MC_USER].PagesTarget =
-      NrAvailablePages - MiMinimumAvailablePages;
-   MiMemoryConsumers[MC_PPOOL].PagesTarget = NrAvailablePages / 2;
-   MiMemoryConsumers[MC_NPPOOL].PagesTarget = 0xFFFFFFFF;
-   MiMemoryConsumers[MC_NPPOOL].PagesUsed = NrSystemPages;
-   MiMemoryConsumers[MC_SYSTEM].PagesTarget = 0xFFFFFFFF;
-   MiMemoryConsumers[MC_SYSTEM].PagesUsed = 0;
+   MiMemoryConsumers[MC_USER].PagesTarget = NrAvailablePages - MiMinimumAvailablePages;
 }
 
 VOID
@@ -99,9 +84,15 @@ MmInitializeMemoryConsumer(ULONG Consumer,
    MiMemoryConsumers[Consumer].Trim = Trim;
 }
 
+VOID
+NTAPI
+MiZeroPhysicalPage(
+    IN PFN_NUMBER PageFrameIndex
+);
+
 NTSTATUS
 NTAPI
-MmReleasePageMemoryConsumer(ULONG Consumer, PFN_TYPE Page)
+MmReleasePageMemoryConsumer(ULONG Consumer, PFN_NUMBER Page)
 {
    PMM_ALLOCATION_REQUEST Request;
    PLIST_ENTRY Entry;
@@ -113,33 +104,27 @@ MmReleasePageMemoryConsumer(ULONG Consumer, PFN_TYPE Page)
       KeBugCheck(MEMORY_MANAGEMENT);
    }
 
-   KeAcquireSpinLock(&AllocationListLock, &OldIrql);
    if (MmGetReferenceCountPage(Page) == 1)
    {
+      if(Consumer == MC_USER) MmRemoveLRUUserPage(Page);
       (void)InterlockedDecrementUL(&MiMemoryConsumers[Consumer].PagesUsed);
-      if (IsListEmpty(&AllocationListHead) || MmAvailablePages < MiMinimumAvailablePages)
+      if (MmAvailablePages < MiMinimumAvailablePages ||
+            (Entry = ExInterlockedRemoveHeadList(&AllocationListHead, &AllocationListLock)) == NULL)
       {
-         KeReleaseSpinLock(&AllocationListLock, OldIrql);
-         if(Consumer == MC_USER) MmRemoveLRUUserPage(Page);
          OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
          MmDereferencePage(Page);
          KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
       }
       else
       {
-         Entry = RemoveHeadList(&AllocationListHead);
          Request = CONTAINING_RECORD(Entry, MM_ALLOCATION_REQUEST, ListEntry);
-         KeReleaseSpinLock(&AllocationListLock, OldIrql);
-         if(Consumer == MC_USER) MmRemoveLRUUserPage(Page);
-         MiZeroPage(Page);
+         MiZeroPhysicalPage(Page);
          Request->Page = Page;
          KeSetEvent(&Request->Event, IO_NO_INCREMENT, FALSE);
       }
    }
    else
    {
-      KeReleaseSpinLock(&AllocationListLock, OldIrql);
-      if(Consumer == MC_USER) MmRemoveLRUUserPage(Page);
       OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
       MmDereferencePage(Page);
       KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
@@ -152,36 +137,57 @@ VOID
 NTAPI
 MiTrimMemoryConsumer(ULONG Consumer)
 {
-   LONG Target;
-   ULONG NrFreedPages;
+    LONG Target = 0;
+    ULONG NrFreedPages = 0;
+    NTSTATUS Status;
 
-   Target = MiMemoryConsumers[Consumer].PagesUsed -
-            MiMemoryConsumers[Consumer].PagesTarget;
-   if (Target < 1)
-   {
-      Target = 1;
-   }
+    /* Make sure we can trim this consumer */
+    if (!MiMemoryConsumers[Consumer].Trim)
+        return;
 
-   if (MiMemoryConsumers[Consumer].Trim != NULL)
-   {
-      MiMemoryConsumers[Consumer].Trim(Target, 0, &NrFreedPages);
-   }
+    if (MiMemoryConsumers[Consumer].PagesUsed > MiMemoryConsumers[Consumer].PagesTarget)
+    {
+        /* Consumer page limit exceeded */
+        Target = max(Target, MiMemoryConsumers[Consumer].PagesUsed - MiMemoryConsumers[Consumer].PagesTarget);
+    }
+    if (MmAvailablePages < MiMinimumAvailablePages)
+    {
+        /* Global page limit exceeded */
+        Target = max(Target, MiMinimumAvailablePages - MmAvailablePages);
+    }
+
+    if (Target)
+    {
+        /* Swap at least MiMinimumPagesPerRun */
+        Target = max(Target, MiMinimumPagesPerRun);
+
+        /* Now swap the pages out */
+        Status = MiMemoryConsumers[Consumer].Trim(Target, 0, &NrFreedPages);
+
+        DPRINT1("Trimming consumer %d: Freed %d pages with a target of %d pages\n", Consumer, NrFreedPages, Target);
+
+               if (NrFreedPages == 0)
+                   DPRINT1("Ran out of pages to swap! Complete memory exhaustion is imminent!\n");
+
+        if (!NT_SUCCESS(Status))
+        {
+            KeBugCheck(MEMORY_MANAGEMENT);
+        }
+    }
 }
 
 NTSTATUS
 MmTrimUserMemory(ULONG Target, ULONG Priority, PULONG NrFreedPages)
 {
-    PFN_TYPE CurrentPage;
-    PFN_TYPE NextPage;
+    PFN_NUMBER CurrentPage;
+    PFN_NUMBER NextPage;
     NTSTATUS Status;
-    
+
     (*NrFreedPages) = 0;
-    
+
     CurrentPage = MmGetLRUFirstUserPage();
     while (CurrentPage != 0 && Target > 0)
     {
-        NextPage = MmGetLRUNextUserPage(CurrentPage);
-        
         Status = MmPageOutPhysicalAddress(CurrentPage);
         if (NT_SUCCESS(Status))
         {
@@ -189,75 +195,60 @@ MmTrimUserMemory(ULONG Target, ULONG Priority, PULONG NrFreedPages)
             Target--;
             (*NrFreedPages)++;
         }
-        
+
+        NextPage = MmGetLRUNextUserPage(CurrentPage);
+        if (NextPage <= CurrentPage)
+        {
+            /* We wrapped around, so we're done */
+            break;
+        }
         CurrentPage = NextPage;
     }
-    return(STATUS_SUCCESS);
-}
-
-VOID
-NTAPI
-MmRebalanceMemoryConsumers(VOID)
-{
-   LONG Target;
-   ULONG i;
-   ULONG NrFreedPages;
-   NTSTATUS Status;
-
-   Target = (MiMinimumAvailablePages - MmAvailablePages) + MiPagesRequired;
-   Target = max(Target, (LONG) MiMinimumPagesPerRun);
 
-   for (i = 0; i < MC_MAXIMUM && Target > 0; i++)
-   {
-      if (MiMemoryConsumers[i].Trim != NULL)
-      {
-         Status = MiMemoryConsumers[i].Trim(Target, 0, &NrFreedPages);
-         if (!NT_SUCCESS(Status))
-         {
-            KeBugCheck(MEMORY_MANAGEMENT);
-         }
-         Target = Target - NrFreedPages;
-      }
-   }
+    return STATUS_SUCCESS;
 }
 
 static BOOLEAN
 MiIsBalancerThread(VOID)
 {
-   return MiBalancerThreadHandle != NULL &&
-          PsGetCurrentThread() == MiBalancerThreadId.UniqueThread;
+   return (MiBalancerThreadHandle != NULL) &&
+          (PsGetCurrentThreadId() == MiBalancerThreadId.UniqueThread);
 }
 
-VOID NTAPI MiSetConsumer(IN PFN_TYPE Pfn, IN ULONG Consumer);
+VOID
+NTAPI
+MmRebalanceMemoryConsumers(VOID)
+{
+    if (MiBalancerThreadHandle != NULL &&
+        !MiIsBalancerThread())
+    {
+        KeSetEvent(&MiBalancerEvent, IO_NO_INCREMENT, FALSE);
+    }
+}
 
 NTSTATUS
 NTAPI
 MmRequestPageMemoryConsumer(ULONG Consumer, BOOLEAN CanWait,
-                            PPFN_TYPE AllocatedPage)
+                            PPFN_NUMBER AllocatedPage)
 {
-   ULONG OldUsed;
-   PFN_TYPE Page;
+   ULONG PagesUsed;
+   PFN_NUMBER Page;
    KIRQL OldIrql;
 
    /*
     * Make sure we don't exceed our individual target.
     */
-   OldUsed = InterlockedIncrementUL(&MiMemoryConsumers[Consumer].PagesUsed);
-   if (OldUsed >= (MiMemoryConsumers[Consumer].PagesTarget - 1) &&
-         !MiIsBalancerThread())
+   PagesUsed = InterlockedIncrementUL(&MiMemoryConsumers[Consumer].PagesUsed);
+   if (PagesUsed > MiMemoryConsumers[Consumer].PagesTarget &&
+       !MiIsBalancerThread())
    {
-      if (!CanWait)
-      {
-         (void)InterlockedDecrementUL(&MiMemoryConsumers[Consumer].PagesUsed);
-         return(STATUS_NO_MEMORY);
-      }
-      MiTrimMemoryConsumer(Consumer);
+      MmRebalanceMemoryConsumers();
    }
 
    /*
     * Allocate always memory for the non paged pool and for the pager thread.
     */
-   if ((Consumer == MC_NPPOOL) || (Consumer == MC_SYSTEM) || MiIsBalancerThread())
+   if ((Consumer == MC_SYSTEM) || MiIsBalancerThread())
    {
       OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
       Page = MmAllocPage(Consumer);
@@ -266,42 +257,33 @@ MmRequestPageMemoryConsumer(ULONG Consumer, BOOLEAN CanWait,
       {
          KeBugCheck(NO_PAGES_AVAILABLE);
       }
+      if (Consumer == MC_USER) MmInsertLRULastUserPage(Page);
       *AllocatedPage = Page;
-      if (MmAvailablePages <= MiMinimumAvailablePages &&
-            MiBalancerThreadHandle != NULL)
-      {
-         KeSetEvent(&MiBalancerEvent, IO_NO_INCREMENT, FALSE);
-      }
+      if (MmAvailablePages < MiMinimumAvailablePages)
+          MmRebalanceMemoryConsumers();
       return(STATUS_SUCCESS);
    }
 
    /*
     * Make sure we don't exceed global targets.
     */
-   if (MmAvailablePages <= MiMinimumAvailablePages)
+   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);
-      (void)InterlockedIncrementUL(&MiPagesRequired);
 
-      KeAcquireSpinLock(&AllocationListLock, &OldIrql);
-
-      if (MiBalancerThreadHandle != NULL)
-      {
-         KeSetEvent(&MiBalancerEvent, IO_NO_INCREMENT, FALSE);
-      }
-      InsertTailList(&AllocationListHead, &Request.ListEntry);
-      KeReleaseSpinLock(&AllocationListLock, OldIrql);
+      ExInterlockedInsertTailList(&AllocationListHead, &Request.ListEntry, &AllocationListLock);
+      MmRebalanceMemoryConsumers();
 
       KeWaitForSingleObject(&Request.Event,
                             0,
@@ -314,11 +296,15 @@ MmRequestPageMemoryConsumer(ULONG Consumer, BOOLEAN CanWait,
       {
          KeBugCheck(NO_PAGES_AVAILABLE);
       }
-      /* Update the Consumer and make the page active */
-      MiSetConsumer(Page, Consumer);
+
       if(Consumer == MC_USER) MmInsertLRULastUserPage(Page);
       *AllocatedPage = Page;
-      (void)InterlockedDecrementUL(&MiPagesRequired);
+
+      if (MmAvailablePages < MiMinimumAvailablePages)
+      {
+          MmRebalanceMemoryConsumers();
+      }
+
       return(STATUS_SUCCESS);
    }
 
@@ -335,6 +321,11 @@ MmRequestPageMemoryConsumer(ULONG Consumer, BOOLEAN CanWait,
    if(Consumer == MC_USER) MmInsertLRULastUserPage(Page);
    *AllocatedPage = Page;
 
+   if (MmAvailablePages < MiMinimumAvailablePages)
+   {
+       MmRebalanceMemoryConsumers();
+   }
+
    return(STATUS_SUCCESS);
 }
 
@@ -344,11 +335,6 @@ MiBalancerThread(PVOID Unused)
    PVOID WaitObjects[2];
    NTSTATUS Status;
    ULONG i;
-   ULONG NrFreedPages;
-   ULONG NrPagesUsed;
-   ULONG Target;
-   BOOLEAN ShouldRun;
-
 
    WaitObjects[0] = &MiBalancerEvent;
    WaitObjects[1] = &MiBalancerTimer;
@@ -364,55 +350,12 @@ MiBalancerThread(PVOID Unused)
                                         NULL,
                                         NULL);
 
-      if (Status == STATUS_SUCCESS)
-      {
-         /* MiBalancerEvent */
-         while (MmAvailablePages < MiMinimumAvailablePages + 5)
-         {
-            for (i = 0; i < MC_MAXIMUM; i++)
-            {
-               if (MiMemoryConsumers[i].Trim != NULL)
-               {
-                  NrFreedPages = 0;
-                  Status = MiMemoryConsumers[i].Trim(MiMinimumPagesPerRun, 0, &NrFreedPages);
-                  if (!NT_SUCCESS(Status))
-                  {
-                     KeBugCheck(MEMORY_MANAGEMENT);
-                  }
-               }
-            }
-         }
-         InterlockedExchange(&MiBalancerWork, 0);
-      }
-      else if (Status == STATUS_SUCCESS + 1)
+      if (Status == STATUS_WAIT_0 || Status == STATUS_WAIT_1)
       {
-         /* MiBalancerTimer */
-         ShouldRun = MmAvailablePages < MiMinimumAvailablePages + 5 ? TRUE : FALSE;
-         for (i = 0; i < MC_MAXIMUM; i++)
-         {
-            if (MiMemoryConsumers[i].Trim != NULL)
-            {
-               NrPagesUsed = MiMemoryConsumers[i].PagesUsed;
-               if (NrPagesUsed > MiMemoryConsumers[i].PagesTarget || ShouldRun)
-               {
-                  if (NrPagesUsed > MiMemoryConsumers[i].PagesTarget)
-                  {
-                     Target = max (NrPagesUsed - MiMemoryConsumers[i].PagesTarget,
-                                   MiMinimumPagesPerRun);
-                  }
-                  else
-                  {
-                     Target = MiMinimumPagesPerRun;
-                  }
-                  NrFreedPages = 0;
-                  Status = MiMemoryConsumers[i].Trim(Target, 0, &NrFreedPages);
-                  if (!NT_SUCCESS(Status))
-                  {
-                     KeBugCheck(MEMORY_MANAGEMENT);
-                  }
-               }
-            }
-         }
+          for (i = 0; i < MC_MAXIMUM; i++)
+          {
+              MiTrimMemoryConsumer(i);
+          }
       }
       else
       {