ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

balance.c
Go to the documentation of this file.
00001 /*
00002  * COPYRIGHT:       See COPYING in the top level directory
00003  * PROJECT:         ReactOS kernel
00004  * FILE:            ntoskrnl/mm/balance.c
00005  * PURPOSE:         kernel memory managment functions
00006  *
00007  * PROGRAMMERS:     David Welch (welch@cwcom.net)
00008  *                  Cameron Gutman (cameron.gutman@reactos.org)
00009  */
00010 
00011 /* INCLUDES *****************************************************************/
00012 
00013 #include <ntoskrnl.h>
00014 #define NDEBUG
00015 #include <debug.h>
00016 
00017 #include "ARM3/miarm.h"
00018 
00019 #if defined (ALLOC_PRAGMA)
00020 #pragma alloc_text(INIT, MmInitializeBalancer)
00021 #pragma alloc_text(INIT, MmInitializeMemoryConsumer)
00022 #pragma alloc_text(INIT, MiInitBalancerThread)
00023 #endif
00024 
00025 
00026 /* TYPES ********************************************************************/
00027 typedef struct _MM_ALLOCATION_REQUEST
00028 {
00029    PFN_NUMBER Page;
00030    LIST_ENTRY ListEntry;
00031    KEVENT Event;
00032 }
00033 MM_ALLOCATION_REQUEST, *PMM_ALLOCATION_REQUEST;
00034 /* GLOBALS ******************************************************************/
00035 
00036 MM_MEMORY_CONSUMER MiMemoryConsumers[MC_MAXIMUM];
00037 static ULONG MiMinimumAvailablePages;
00038 static ULONG MiNrTotalPages;
00039 static LIST_ENTRY AllocationListHead;
00040 static KSPIN_LOCK AllocationListLock;
00041 static ULONG MiMinimumPagesPerRun;
00042 
00043 static CLIENT_ID MiBalancerThreadId;
00044 static HANDLE MiBalancerThreadHandle = NULL;
00045 static KEVENT MiBalancerEvent;
00046 static KTIMER MiBalancerTimer;
00047 
00048 /* FUNCTIONS ****************************************************************/
00049 
00050 VOID
00051 INIT_FUNCTION
00052 NTAPI
00053 MmInitializeBalancer(ULONG NrAvailablePages, ULONG NrSystemPages)
00054 {
00055    memset(MiMemoryConsumers, 0, sizeof(MiMemoryConsumers));
00056    InitializeListHead(&AllocationListHead);
00057    KeInitializeSpinLock(&AllocationListLock);
00058 
00059    MiNrTotalPages = NrAvailablePages;
00060 
00061    /* Set up targets. */
00062    MiMinimumAvailablePages = 128;
00063    MiMinimumPagesPerRun = 256;
00064     if ((NrAvailablePages + NrSystemPages) >= 8192)
00065     {
00066         MiMemoryConsumers[MC_CACHE].PagesTarget = NrAvailablePages / 4 * 3;
00067     }
00068     else if ((NrAvailablePages + NrSystemPages) >= 4096)
00069     {
00070         MiMemoryConsumers[MC_CACHE].PagesTarget = NrAvailablePages / 3 * 2;
00071     }
00072     else
00073     {
00074         MiMemoryConsumers[MC_CACHE].PagesTarget = NrAvailablePages / 8;
00075     }
00076    MiMemoryConsumers[MC_USER].PagesTarget = NrAvailablePages - MiMinimumAvailablePages;
00077 }
00078 
00079 VOID
00080 INIT_FUNCTION
00081 NTAPI
00082 MmInitializeMemoryConsumer(ULONG Consumer,
00083                            NTSTATUS (*Trim)(ULONG Target, ULONG Priority,
00084                                             PULONG NrFreed))
00085 {
00086    MiMemoryConsumers[Consumer].Trim = Trim;
00087 }
00088 
00089 VOID
00090 NTAPI
00091 MiZeroPhysicalPage(
00092     IN PFN_NUMBER PageFrameIndex
00093 );
00094 
00095 NTSTATUS
00096 NTAPI
00097 MmReleasePageMemoryConsumer(ULONG Consumer, PFN_NUMBER Page)
00098 {
00099    PMM_ALLOCATION_REQUEST Request;
00100    PLIST_ENTRY Entry;
00101    KIRQL OldIrql;
00102 
00103    if (Page == 0)
00104    {
00105       DPRINT1("Tried to release page zero.\n");
00106       KeBugCheck(MEMORY_MANAGEMENT);
00107    }
00108 
00109    if (MmGetReferenceCountPage(Page) == 1)
00110    {
00111       if(Consumer == MC_USER) MmRemoveLRUUserPage(Page);
00112       (void)InterlockedDecrementUL(&MiMemoryConsumers[Consumer].PagesUsed);
00113       if ((Entry = ExInterlockedRemoveHeadList(&AllocationListHead, &AllocationListLock)) == NULL)
00114       {
00115          OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
00116          MmDereferencePage(Page);
00117          KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
00118       }
00119       else
00120       {
00121          Request = CONTAINING_RECORD(Entry, MM_ALLOCATION_REQUEST, ListEntry);
00122          MiZeroPhysicalPage(Page);
00123          Request->Page = Page;
00124          KeSetEvent(&Request->Event, IO_NO_INCREMENT, FALSE);
00125       }
00126    }
00127    else
00128    {
00129       OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
00130       MmDereferencePage(Page);
00131       KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
00132    }
00133 
00134    return(STATUS_SUCCESS);
00135 }
00136 
00137 ULONG
00138 NTAPI
00139 MiTrimMemoryConsumer(ULONG Consumer, ULONG InitialTarget)
00140 {
00141     ULONG Target = InitialTarget;
00142     ULONG NrFreedPages = 0;
00143     NTSTATUS Status;
00144 
00145     /* Make sure we can trim this consumer */
00146     if (!MiMemoryConsumers[Consumer].Trim)
00147     {
00148         /* Return the unmodified initial target */
00149         return InitialTarget;
00150     }
00151 
00152     if (MiMemoryConsumers[Consumer].PagesUsed > MiMemoryConsumers[Consumer].PagesTarget)
00153     {
00154         /* Consumer page limit exceeded */
00155         Target = max(Target, MiMemoryConsumers[Consumer].PagesUsed - MiMemoryConsumers[Consumer].PagesTarget);
00156     }
00157     if (MmAvailablePages < MiMinimumAvailablePages)
00158     {
00159         /* Global page limit exceeded */
00160         Target = (ULONG)max(Target, MiMinimumAvailablePages - MmAvailablePages);
00161     }
00162 
00163     if (Target)
00164     {
00165         if (!InitialTarget)
00166         {
00167             /* If there was no initial target,
00168              * swap at least MiMinimumPagesPerRun */
00169             Target = max(Target, MiMinimumPagesPerRun);
00170         }
00171 
00172         /* Now swap the pages out */
00173         Status = MiMemoryConsumers[Consumer].Trim(Target, 0, &NrFreedPages);
00174 
00175         DPRINT("Trimming consumer %d: Freed %d pages with a target of %d pages\n", Consumer, NrFreedPages, Target);
00176 
00177         if (!NT_SUCCESS(Status))
00178         {
00179             KeBugCheck(MEMORY_MANAGEMENT);
00180         }
00181 
00182         /* Update the target */
00183         if (NrFreedPages < Target)
00184             Target -= NrFreedPages;
00185         else
00186             Target = 0;
00187 
00188         /* Return the remaining pages needed to meet the target */
00189         return Target;
00190     }
00191     else
00192     {
00193         /* Initial target is zero and we don't have anything else to add */
00194         return 0;
00195     }
00196 }
00197 
00198 NTSTATUS
00199 MmTrimUserMemory(ULONG Target, ULONG Priority, PULONG NrFreedPages)
00200 {
00201     PFN_NUMBER CurrentPage;
00202     PFN_NUMBER NextPage;
00203     NTSTATUS Status;
00204 
00205     (*NrFreedPages) = 0;
00206 
00207     CurrentPage = MmGetLRUFirstUserPage();
00208     while (CurrentPage != 0 && Target > 0)
00209     {
00210         Status = MmPageOutPhysicalAddress(CurrentPage);
00211         if (NT_SUCCESS(Status))
00212         {
00213             DPRINT("Succeeded\n");
00214             Target--;
00215             (*NrFreedPages)++;
00216         }
00217 
00218         NextPage = MmGetLRUNextUserPage(CurrentPage);
00219         if (NextPage <= CurrentPage)
00220         {
00221             /* We wrapped around, so we're done */
00222             break;
00223         }
00224         CurrentPage = NextPage;
00225     }
00226 
00227     return STATUS_SUCCESS;
00228 }
00229 
00230 static BOOLEAN
00231 MiIsBalancerThread(VOID)
00232 {
00233    return (MiBalancerThreadHandle != NULL) &&
00234           (PsGetCurrentThreadId() == MiBalancerThreadId.UniqueThread);
00235 }
00236 
00237 VOID
00238 NTAPI
00239 MiDeletePte(IN PMMPTE PointerPte,
00240             IN PVOID VirtualAddress,
00241             IN PEPROCESS CurrentProcess,
00242             IN PMMPTE PrototypePte);
00243 
00244 VOID
00245 NTAPI
00246 MmRebalanceMemoryConsumers(VOID)
00247 {
00248     if (MiBalancerThreadHandle != NULL &&
00249         !MiIsBalancerThread())
00250     {
00251         KeSetEvent(&MiBalancerEvent, IO_NO_INCREMENT, FALSE);
00252     }
00253 }
00254 
00255 NTSTATUS
00256 NTAPI
00257 MmRequestPageMemoryConsumer(ULONG Consumer, BOOLEAN CanWait,
00258                             PPFN_NUMBER AllocatedPage)
00259 {
00260    ULONG PagesUsed;
00261    PFN_NUMBER Page;
00262    KIRQL OldIrql;
00263 
00264    /*
00265     * Make sure we don't exceed our individual target.
00266     */
00267    PagesUsed = InterlockedIncrementUL(&MiMemoryConsumers[Consumer].PagesUsed);
00268    if (PagesUsed > MiMemoryConsumers[Consumer].PagesTarget &&
00269        !MiIsBalancerThread())
00270    {
00271       MmRebalanceMemoryConsumers();
00272    }
00273 
00274    /*
00275     * Allocate always memory for the non paged pool and for the pager thread.
00276     */
00277    if ((Consumer == MC_SYSTEM) || MiIsBalancerThread())
00278    {
00279       OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
00280       Page = MmAllocPage(Consumer);
00281       KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
00282       if (Page == 0)
00283       {
00284          KeBugCheck(NO_PAGES_AVAILABLE);
00285       }
00286       if (Consumer == MC_USER) MmInsertLRULastUserPage(Page);
00287       *AllocatedPage = Page;
00288       if (MmAvailablePages < MiMinimumAvailablePages)
00289           MmRebalanceMemoryConsumers();
00290       return(STATUS_SUCCESS);
00291    }
00292 
00293    /*
00294     * Make sure we don't exceed global targets.
00295     */
00296    if (MmAvailablePages < MiMinimumAvailablePages)
00297    {
00298       MM_ALLOCATION_REQUEST Request;
00299 
00300       if (!CanWait)
00301       {
00302          (void)InterlockedDecrementUL(&MiMemoryConsumers[Consumer].PagesUsed);
00303          MmRebalanceMemoryConsumers();
00304          return(STATUS_NO_MEMORY);
00305       }
00306 
00307       /* Insert an allocation request. */
00308       Request.Page = 0;
00309       KeInitializeEvent(&Request.Event, NotificationEvent, FALSE);
00310 
00311       ExInterlockedInsertTailList(&AllocationListHead, &Request.ListEntry, &AllocationListLock);
00312       MmRebalanceMemoryConsumers();
00313 
00314       KeWaitForSingleObject(&Request.Event,
00315                             0,
00316                             KernelMode,
00317                             FALSE,
00318                             NULL);
00319 
00320       Page = Request.Page;
00321       if (Page == 0)
00322       {
00323          KeBugCheck(NO_PAGES_AVAILABLE);
00324       }
00325 
00326       if(Consumer == MC_USER) MmInsertLRULastUserPage(Page);
00327       *AllocatedPage = Page;
00328 
00329       if (MmAvailablePages < MiMinimumAvailablePages)
00330       {
00331           MmRebalanceMemoryConsumers();
00332       }
00333 
00334       return(STATUS_SUCCESS);
00335    }
00336 
00337    /*
00338     * Actually allocate the page.
00339     */
00340    OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
00341    Page = MmAllocPage(Consumer);
00342    KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
00343    if (Page == 0)
00344    {
00345       KeBugCheck(NO_PAGES_AVAILABLE);
00346    }
00347    if(Consumer == MC_USER) MmInsertLRULastUserPage(Page);
00348    *AllocatedPage = Page;
00349 
00350    if (MmAvailablePages < MiMinimumAvailablePages)
00351    {
00352        MmRebalanceMemoryConsumers();
00353    }
00354 
00355    return(STATUS_SUCCESS);
00356 }
00357 
00358 VOID NTAPI
00359 MiBalancerThread(PVOID Unused)
00360 {
00361    PVOID WaitObjects[2];
00362    NTSTATUS Status;
00363    ULONG i;
00364 
00365    WaitObjects[0] = &MiBalancerEvent;
00366    WaitObjects[1] = &MiBalancerTimer;
00367 
00368    while (1)
00369    {
00370       Status = KeWaitForMultipleObjects(2,
00371                                         WaitObjects,
00372                                         WaitAny,
00373                                         Executive,
00374                                         KernelMode,
00375                                         FALSE,
00376                                         NULL,
00377                                         NULL);
00378 
00379       if (Status == STATUS_WAIT_0 || Status == STATUS_WAIT_1)
00380       {
00381         ULONG InitialTarget = 0;
00382 
00383 #if (_MI_PAGING_LEVELS == 2)
00384         if(!MiIsBalancerThread())
00385         {
00386             /* Clean up the unused PDEs */
00387             ULONG_PTR Address;
00388             PEPROCESS Process = PsGetCurrentProcess();
00389 
00390             /* Acquire PFN lock */
00391             KIRQL OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
00392             PMMPDE pointerPde;
00393             for(Address = (ULONG_PTR)MI_LOWEST_VAD_ADDRESS;
00394                 Address < (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS;
00395                 Address += (PAGE_SIZE * PTE_COUNT))
00396             {
00397                 if(MmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)] == 0)
00398                 {
00399                     pointerPde = MiAddressToPde(Address);
00400                     if(pointerPde->u.Hard.Valid)
00401                         MiDeletePte(pointerPde, MiPdeToPte(pointerPde), Process, NULL);
00402                     ASSERT(pointerPde->u.Hard.Valid == 0);
00403                 }
00404             }
00405             /* Release lock */
00406             KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
00407         }
00408 #endif
00409           do
00410           {
00411               ULONG OldTarget = InitialTarget;
00412 
00413               /* Trim each consumer */
00414               for (i = 0; i < MC_MAXIMUM; i++)
00415               {
00416                   InitialTarget = MiTrimMemoryConsumer(i, InitialTarget);
00417               }
00418 
00419               /* No pages left to swap! */
00420               if (InitialTarget != 0 &&
00421                   InitialTarget == OldTarget)
00422               {
00423                   /* Game over */
00424                   KeBugCheck(NO_PAGES_AVAILABLE);
00425               }
00426           } while (InitialTarget != 0);
00427       }
00428       else
00429       {
00430          DPRINT1("KeWaitForMultipleObjects failed, status = %x\n", Status);
00431          KeBugCheck(MEMORY_MANAGEMENT);
00432       }
00433    }
00434 }
00435 
00436 VOID
00437 INIT_FUNCTION
00438 NTAPI
00439 MiInitBalancerThread(VOID)
00440 {
00441    KPRIORITY Priority;
00442    NTSTATUS Status;
00443 #if !defined(__GNUC__)
00444 
00445    LARGE_INTEGER dummyJunkNeeded;
00446    dummyJunkNeeded.QuadPart = -20000000; /* 2 sec */
00447    ;
00448 #endif
00449 
00450 
00451    KeInitializeEvent(&MiBalancerEvent, SynchronizationEvent, FALSE);
00452    KeInitializeTimerEx(&MiBalancerTimer, SynchronizationTimer);
00453    KeSetTimerEx(&MiBalancerTimer,
00454 #if defined(__GNUC__)
00455                 (LARGE_INTEGER)(LONGLONG)-20000000LL,     /* 2 sec */
00456 #else
00457                 dummyJunkNeeded,
00458 #endif
00459                 2000,         /* 2 sec */
00460                 NULL);
00461 
00462    Status = PsCreateSystemThread(&MiBalancerThreadHandle,
00463                                  THREAD_ALL_ACCESS,
00464                                  NULL,
00465                                  NULL,
00466                                  &MiBalancerThreadId,
00467                                  (PKSTART_ROUTINE) MiBalancerThread,
00468                                  NULL);
00469    if (!NT_SUCCESS(Status))
00470    {
00471       KeBugCheck(MEMORY_MANAGEMENT);
00472    }
00473 
00474    Priority = LOW_REALTIME_PRIORITY + 1;
00475    NtSetInformationThread(MiBalancerThreadHandle,
00476                           ThreadPriority,
00477                           &Priority,
00478                           sizeof(Priority));
00479 
00480 }
00481 
00482 
00483 /* EOF */

Generated on Fri May 25 2012 04:36:03 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.