VOID NTAPI MmRebalanceMemoryConsumers(VOID) { if (MiBalancerThreadHandle != NULL && !MiIsBalancerThread()) { KeSetEvent(&MiBalancerEvent, IO_NO_INCREMENT, FALSE); } }
NTSTATUS NTAPI MmRequestPageMemoryConsumer(ULONG Consumer, BOOLEAN CanWait, PPFN_NUMBER AllocatedPage) { ULONG OldUsed; 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()) { if (!CanWait) { (void)InterlockedDecrementUL(&MiMemoryConsumers[Consumer].PagesUsed); return(STATUS_NO_MEMORY); } MiTrimMemoryConsumer(Consumer); } /* * Allocate always memory for the non paged pool and for the pager thread. */ if ((Consumer == MC_SYSTEM) || MiIsBalancerThread()) { OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); Page = MmAllocPage(Consumer); KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); if (Page == 0) { KeBugCheck(NO_PAGES_AVAILABLE); } *AllocatedPage = Page; if (MmAvailablePages <= MiMinimumAvailablePages && MiBalancerThreadHandle != NULL) { KeSetEvent(&MiBalancerEvent, IO_NO_INCREMENT, FALSE); } 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); 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); KeWaitForSingleObject(&Request.Event, 0, KernelMode, FALSE, NULL); Page = Request.Page; if (Page == 0) { KeBugCheck(NO_PAGES_AVAILABLE); } /* Update the Consumer and make the page active */ if(Consumer == MC_USER) MmInsertLRULastUserPage(Page); *AllocatedPage = Page; (void)InterlockedDecrementUL(&MiPagesRequired); return(STATUS_SUCCESS); } /* * Actually allocate the page. */ OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); Page = MmAllocPage(Consumer); KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); if (Page == 0) { KeBugCheck(NO_PAGES_AVAILABLE); } if(Consumer == MC_USER) MmInsertLRULastUserPage(Page); *AllocatedPage = Page; return(STATUS_SUCCESS); }
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) { 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; /* 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); } } }