- Initialize the used page count for the non paged pool in MmInitializeBalancer.
[reactos.git] / reactos / ntoskrnl / mm / balance.c
index 6e5bde3..1543fd5 100644 (file)
@@ -16,9 +16,8 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
-/* $Id: balance.c,v 1.3 2001/12/31 01:53:45 dwelch Exp $
+/* $Id: balance.c,v 1.22 2003/10/23 20:28:08 hbirr Exp $
  *
- * COPYRIGHT:   See COPYING in the top directory
  * PROJECT:     ReactOS kernel 
  * FILE:        ntoskrnl/mm/balance.c
  * PURPOSE:     kernel memory managment functions
@@ -31,6 +30,7 @@
 
 #include <ddk/ntddk.h>
 #include <internal/mm.h>
+#include <ntos/minmax.h>
 
 #define NDEBUG
 #include <internal/debug.h>
@@ -46,7 +46,7 @@ typedef struct _MM_MEMORY_CONSUMER
 
 typedef struct _MM_ALLOCATION_REQUEST
 {
-  PVOID Page;
+  PHYSICAL_ADDRESS Page;
   LIST_ENTRY ListEntry;
   KEVENT Event;
 } MM_ALLOCATION_REQUEST, *PMM_ALLOCATION_REQUEST;
@@ -59,11 +59,21 @@ static ULONG MiNrAvailablePages;
 static ULONG MiNrTotalPages;
 static LIST_ENTRY AllocationListHead;
 static KSPIN_LOCK AllocationListLock;
+static ULONG MiPagesRequired = 0;
+static ULONG MiMinimumPagesPerRun = 10;
 
 /* FUNCTIONS ****************************************************************/
 
-VOID
-MmInitializeBalancer(ULONG NrAvailablePages)
+VOID MmPrintMemoryStatistic(VOID)
+{
+  DbgPrint("MC_CACHE %d, MC_USER %d, MC_PPOOL %d, MC_NPPOOL %d, MiNrAvailablePages %d\n",
+           MiMemoryConsumers[MC_CACHE].PagesUsed, MiMemoryConsumers[MC_USER].PagesUsed, 
+           MiMemoryConsumers[MC_PPOOL].PagesUsed, MiMemoryConsumers[MC_NPPOOL].PagesUsed,
+          MiNrAvailablePages); 
+}
+
+VOID INIT_FUNCTION
+MmInitializeBalancer(ULONG NrAvailablePages, ULONG NrSystemPages)
 {
   memset(MiMemoryConsumers, 0, sizeof(MiMemoryConsumers));
   InitializeListHead(&AllocationListHead);
@@ -74,12 +84,14 @@ MmInitializeBalancer(ULONG NrAvailablePages)
   /* Set up targets. */
   MiMinimumAvailablePages = 64;
   MiMemoryConsumers[MC_CACHE].PagesTarget = NrAvailablePages / 2;
-  MiMemoryConsumers[MC_USER].PagesTarget = NrAvailablePages - MiMinimumAvailablePages;
+  MiMemoryConsumers[MC_USER].PagesTarget = 
+    NrAvailablePages - MiMinimumAvailablePages;
   MiMemoryConsumers[MC_PPOOL].PagesTarget = NrAvailablePages / 2;
   MiMemoryConsumers[MC_NPPOOL].PagesTarget = 0xFFFFFFFF;
+  MiMemoryConsumers[MC_NPPOOL].PagesUsed = NrSystemPages;
 }
 
-VOID
+VOID INIT_FUNCTION
 MmInitializeMemoryConsumer(ULONG Consumer, 
                           NTSTATUS (*Trim)(ULONG Target, ULONG Priority, 
                                            PULONG NrFreed))
@@ -88,28 +100,43 @@ MmInitializeMemoryConsumer(ULONG Consumer,
 }
 
 NTSTATUS
-MmReleasePageMemoryConsumer(ULONG Consumer, PVOID Page)
+MmReleasePageMemoryConsumer(ULONG Consumer, PHYSICAL_ADDRESS Page)
 {
   PMM_ALLOCATION_REQUEST Request;
   PLIST_ENTRY Entry;
   KIRQL oldIrql;
 
-  InterlockedDecrement(&MiMemoryConsumers[Consumer].PagesUsed);
-  InterlockedIncrement(&MiNrAvailablePages);
+  if (Page.QuadPart == 0LL)
+    {
+      DPRINT1("Tried to release page zero.\n");
+      KEBUGCHECK(0);
+    }
+
   KeAcquireSpinLock(&AllocationListLock, &oldIrql);
-  if (IsListEmpty(&AllocationListHead))
+  if (MmGetReferenceCountPage(Page) == 1)
     {
-      KeReleaseSpinLock(&AllocationListLock, oldIrql);
-      MmDereferencePage(Page);
+      InterlockedDecrement((LONG *)&MiMemoryConsumers[Consumer].PagesUsed);
+      InterlockedIncrement((LONG *)&MiNrAvailablePages);
+      if (IsListEmpty(&AllocationListHead))
+       {
+         KeReleaseSpinLock(&AllocationListLock, oldIrql);
+         MmDereferencePage(Page);
+       }
+      else
+       {
+         Entry = RemoveHeadList(&AllocationListHead);
+         Request = CONTAINING_RECORD(Entry, MM_ALLOCATION_REQUEST, ListEntry);
+         KeReleaseSpinLock(&AllocationListLock, oldIrql);
+         Request->Page = Page;
+         KeSetEvent(&Request->Event, IO_NO_INCREMENT, FALSE);
+       }
     }
   else
     {
-      Entry = RemoveHeadList(&AllocationListHead);
-      Request = CONTAINING_RECORD(Entry, MM_ALLOCATION_REQUEST, ListEntry);
       KeReleaseSpinLock(&AllocationListLock, oldIrql);
-      Request->Page = Page;
-      KeSetEvent(&Request->Event, IO_NO_INCREMENT, FALSE);
+      MmDereferencePage(Page);
     }
+
   return(STATUS_SUCCESS);
 }
 
@@ -117,32 +144,31 @@ VOID
 MiTrimMemoryConsumer(ULONG Consumer)
 {
   LONG Target;
+  ULONG NrFreedPages;
 
-  Target = MiMemoryConsumers[Consumer].PagesUsed - MiMemoryConsumers[Consumer].PagesTarget;
-  if (Target < 0)
+  Target = MiMemoryConsumers[Consumer].PagesUsed - 
+    MiMemoryConsumers[Consumer].PagesTarget;
+  if (Target < 1)
     {
       Target = 1;
     }
 
   if (MiMemoryConsumers[Consumer].Trim != NULL)
     {
-      MiMemoryConsumers[Consumer].Trim(Target, 0, NULL);
+      MiMemoryConsumers[Consumer].Trim(Target, 0, &NrFreedPages);
     }
 }
 
 VOID
-MiRebalanceMemoryConsumers(VOID)
+MmRebalanceMemoryConsumers(VOID)
 {
   LONG Target;
   ULONG i;
   ULONG NrFreedPages;
   NTSTATUS Status;
 
-  Target = MiMinimumAvailablePages - MiNrAvailablePages;
-  if (Target < 0)
-    {
-      Target = 1;
-    }
+  Target = (MiMinimumAvailablePages - MiNrAvailablePages) + MiPagesRequired;
+  Target = max(Target, (LONG) MiMinimumPagesPerRun);
 
   for (i = 0; i < MC_MAXIMUM && Target > 0; i++)
     {
@@ -151,33 +177,32 @@ MiRebalanceMemoryConsumers(VOID)
          Status = MiMemoryConsumers[i].Trim(Target, 0, &NrFreedPages);
          if (!NT_SUCCESS(Status))
            {
-             KeBugCheck(0);
+             KEBUGCHECK(0);
            }
          Target = Target - NrFreedPages;
        }
     }
-  if (Target > 0)
-    {
-      KeBugCheck(0);
-    }
 }
 
 NTSTATUS
-MmRequestPageMemoryConsumer(ULONG Consumer, BOOLEAN CanWait, PVOID* AllocatedPage)
+MmRequestPageMemoryConsumer(ULONG Consumer, BOOLEAN CanWait, 
+                           PHYSICAL_ADDRESS* AllocatedPage)
 {
   ULONG OldUsed;
   ULONG OldAvailable;
-  PVOID Page;
+  PHYSICAL_ADDRESS Page;
+  KIRQL oldIrql;
   
   /*
    * Make sure we don't exceed our individual target.
    */
-  OldUsed = InterlockedIncrement(&MiMemoryConsumers[Consumer].PagesUsed);
-  if (OldUsed >= (MiMemoryConsumers[Consumer].PagesTarget - 1))
+  OldUsed = InterlockedIncrement((LONG *)&MiMemoryConsumers[Consumer].PagesUsed);
+  if (OldUsed >= (MiMemoryConsumers[Consumer].PagesTarget - 1) &&
+      !MiIsPagerThread())
     {
       if (!CanWait)
        {
-         InterlockedDecrement(&MiMemoryConsumers[Consumer].PagesUsed);
+         InterlockedDecrement((LONG *)&MiMemoryConsumers[Consumer].PagesUsed);
          return(STATUS_NO_MEMORY);
        }
       MiTrimMemoryConsumer(Consumer);
@@ -186,49 +211,71 @@ MmRequestPageMemoryConsumer(ULONG Consumer, BOOLEAN CanWait, PVOID* AllocatedPag
   /*
    * Make sure we don't exceed global targets.
    */
-  OldAvailable = InterlockedDecrement(&MiNrAvailablePages);
+  OldAvailable = InterlockedDecrement((LONG *)&MiNrAvailablePages);
   if (OldAvailable < MiMinimumAvailablePages)
-    {
-      if (!CanWait)
-       {
-         InterlockedIncrement(&MiNrAvailablePages);
-         InterlockedDecrement(&MiMemoryConsumers[Consumer].PagesUsed);
-         return(STATUS_NO_MEMORY);
-       }
-      MiRebalanceMemoryConsumers();
-    }
-
-  /*
-   * Actually allocate the page.
-   */
-  Page = MmAllocPage(Consumer, 0);
-  if (Page == NULL)
     {
       MM_ALLOCATION_REQUEST Request;
-      KIRQL oldIrql;
 
-      /* Still not trimmed enough. */
       if (!CanWait)
        {
-         InterlockedIncrement(&MiNrAvailablePages);
-         InterlockedDecrement(&MiMemoryConsumers[Consumer].PagesUsed);
+         InterlockedIncrement((LONG *)&MiNrAvailablePages);
+         InterlockedDecrement((LONG *)&MiMemoryConsumers[Consumer].PagesUsed);
          return(STATUS_NO_MEMORY);
        }
 
       /* Insert an allocation request. */
-      Request.Page = NULL;
+      Request.Page.QuadPart = 0LL;
       KeInitializeEvent(&Request.Event, NotificationEvent, FALSE);
-      KeAcquireSpinLock(&AllocationListLock, &oldIrql);
+      InterlockedIncrement((LONG *)&MiPagesRequired);
+
+      KeAcquireSpinLock(&AllocationListLock, &oldIrql);     
+      /* Always let the pager thread itself allocate memory. */
+      if (MiIsPagerThread())
+       {
+         Page = MmAllocPage(Consumer, 0);
+         KeReleaseSpinLock(&AllocationListLock, oldIrql);
+         if (Page.QuadPart == 0LL)
+           {
+             KEBUGCHECK(0);
+           }
+         *AllocatedPage = Page;
+         InterlockedDecrement((LONG *)&MiPagesRequired);
+         return(STATUS_SUCCESS);
+       }
+      /* Otherwise start the pager thread if it isn't already working. */
+      MiStartPagerThread();
       InsertTailList(&AllocationListHead, &Request.ListEntry);
       KeReleaseSpinLock(&AllocationListLock, oldIrql);
-      MiRebalanceMemoryConsumers();
+
+      KeWaitForSingleObject(&Request.Event,
+                           0,
+                           KernelMode,
+                           FALSE,
+                           NULL);
+      
       Page = Request.Page;
-      if (Page == NULL)
+      if (Page.QuadPart == 0LL)
        {
-         KeBugCheck(0);
+         KEBUGCHECK(0);
        }
+      MmTransferOwnershipPage(Page, Consumer);
+      *AllocatedPage = Page;
+      InterlockedDecrement((LONG *)&MiPagesRequired);
+      MiStopPagerThread();
+      return(STATUS_SUCCESS);
+    }
+  
+  /*
+   * Actually allocate the page.
+   */
+  Page = MmAllocPage(Consumer, 0);
+  if (Page.QuadPart == 0LL)
+    {
+      KEBUGCHECK(0);
     }
   *AllocatedPage = Page;
 
   return(STATUS_SUCCESS);
 }
+
+/* EOF */