Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenbalance.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
1.7.6.1
|