3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 * PROJECT: ReactOS kernel
22 * FILE: ntoskrnl/mm/balance.c
23 * PURPOSE: kernel memory managment functions
24 * PROGRAMMER: David Welch (welch@cwcom.net)
29 /* INCLUDES *****************************************************************/
31 #include <ddk/ntddk.h>
32 #include <internal/mm.h>
33 #include <ntos/minmax.h>
36 #include <internal/debug.h>
38 /* TYPES ********************************************************************/
40 typedef struct _MM_MEMORY_CONSUMER
44 NTSTATUS (*Trim)(ULONG Target, ULONG Priority, PULONG NrFreed);
45 } MM_MEMORY_CONSUMER, *PMM_MEMORY_CONSUMER;
47 typedef struct _MM_ALLOCATION_REQUEST
49 PHYSICAL_ADDRESS Page;
52 } MM_ALLOCATION_REQUEST, *PMM_ALLOCATION_REQUEST;
54 /* GLOBALS ******************************************************************/
56 static MM_MEMORY_CONSUMER MiMemoryConsumers[MC_MAXIMUM];
57 static ULONG MiMinimumAvailablePages;
58 static ULONG MiNrAvailablePages;
59 static ULONG MiNrTotalPages;
60 static LIST_ENTRY AllocationListHead;
61 static KSPIN_LOCK AllocationListLock;
62 static ULONG MiPagesRequired = 0;
63 static ULONG MiMinimumPagesPerRun = 10;
65 /* FUNCTIONS ****************************************************************/
67 VOID MmPrintMemoryStatistic(VOID)
69 DbgPrint("MC_CACHE %d, MC_USER %d, MC_PPOOL %d, MC_NPPOOL %d, MiNrAvailablePages %d\n",
70 MiMemoryConsumers[MC_CACHE].PagesUsed, MiMemoryConsumers[MC_USER].PagesUsed,
71 MiMemoryConsumers[MC_PPOOL].PagesUsed, MiMemoryConsumers[MC_NPPOOL].PagesUsed,
76 MmInitializeBalancer(ULONG NrAvailablePages)
78 memset(MiMemoryConsumers, 0, sizeof(MiMemoryConsumers));
79 InitializeListHead(&AllocationListHead);
80 KeInitializeSpinLock(&AllocationListLock);
82 MiNrAvailablePages = MiNrTotalPages = NrAvailablePages;
85 MiMinimumAvailablePages = 64;
86 MiMemoryConsumers[MC_CACHE].PagesTarget = NrAvailablePages / 2;
87 MiMemoryConsumers[MC_USER].PagesTarget =
88 NrAvailablePages - MiMinimumAvailablePages;
89 MiMemoryConsumers[MC_PPOOL].PagesTarget = NrAvailablePages / 2;
90 MiMemoryConsumers[MC_NPPOOL].PagesTarget = 0xFFFFFFFF;
94 MmInitializeMemoryConsumer(ULONG Consumer,
95 NTSTATUS (*Trim)(ULONG Target, ULONG Priority,
98 MiMemoryConsumers[Consumer].Trim = Trim;
102 MmReleasePageMemoryConsumer(ULONG Consumer, PHYSICAL_ADDRESS Page)
104 PMM_ALLOCATION_REQUEST Request;
108 if (Page.QuadPart == 0LL)
110 DPRINT1("Tried to release page zero.\n");
114 KeAcquireSpinLock(&AllocationListLock, &oldIrql);
115 if (MmGetReferenceCountPage(Page) == 1)
117 InterlockedDecrement((LONG *)&MiMemoryConsumers[Consumer].PagesUsed);
118 InterlockedIncrement((LONG *)&MiNrAvailablePages);
119 if (IsListEmpty(&AllocationListHead))
121 KeReleaseSpinLock(&AllocationListLock, oldIrql);
122 MmDereferencePage(Page);
126 Entry = RemoveHeadList(&AllocationListHead);
127 Request = CONTAINING_RECORD(Entry, MM_ALLOCATION_REQUEST, ListEntry);
128 KeReleaseSpinLock(&AllocationListLock, oldIrql);
129 Request->Page = Page;
130 KeSetEvent(&Request->Event, IO_NO_INCREMENT, FALSE);
135 KeReleaseSpinLock(&AllocationListLock, oldIrql);
136 MmDereferencePage(Page);
139 return(STATUS_SUCCESS);
143 MiTrimMemoryConsumer(ULONG Consumer)
148 Target = MiMemoryConsumers[Consumer].PagesUsed -
149 MiMemoryConsumers[Consumer].PagesTarget;
155 if (MiMemoryConsumers[Consumer].Trim != NULL)
157 MiMemoryConsumers[Consumer].Trim(Target, 0, &NrFreedPages);
162 MmRebalanceMemoryConsumers(VOID)
169 Target = (MiMinimumAvailablePages - MiNrAvailablePages) + MiPagesRequired;
170 Target = max(Target, (LONG) MiMinimumPagesPerRun);
172 for (i = 0; i < MC_MAXIMUM && Target > 0; i++)
174 if (MiMemoryConsumers[i].Trim != NULL)
176 Status = MiMemoryConsumers[i].Trim(Target, 0, &NrFreedPages);
177 if (!NT_SUCCESS(Status))
181 Target = Target - NrFreedPages;
187 MmRequestPageMemoryConsumer(ULONG Consumer, BOOLEAN CanWait,
188 PHYSICAL_ADDRESS* AllocatedPage)
192 PHYSICAL_ADDRESS Page;
196 * Make sure we don't exceed our individual target.
198 OldUsed = InterlockedIncrement((LONG *)&MiMemoryConsumers[Consumer].PagesUsed);
199 if (OldUsed >= (MiMemoryConsumers[Consumer].PagesTarget - 1) &&
204 InterlockedDecrement((LONG *)&MiMemoryConsumers[Consumer].PagesUsed);
205 return(STATUS_NO_MEMORY);
207 MiTrimMemoryConsumer(Consumer);
211 * Make sure we don't exceed global targets.
213 OldAvailable = InterlockedDecrement((LONG *)&MiNrAvailablePages);
214 if (OldAvailable < MiMinimumAvailablePages)
216 MM_ALLOCATION_REQUEST Request;
220 InterlockedIncrement((LONG *)&MiNrAvailablePages);
221 InterlockedDecrement((LONG *)&MiMemoryConsumers[Consumer].PagesUsed);
222 return(STATUS_NO_MEMORY);
225 /* Insert an allocation request. */
226 Request.Page.QuadPart = 0LL;
227 KeInitializeEvent(&Request.Event, NotificationEvent, FALSE);
228 InterlockedIncrement((LONG *)&MiPagesRequired);
230 KeAcquireSpinLock(&AllocationListLock, &oldIrql);
231 /* Always let the pager thread itself allocate memory. */
232 if (MiIsPagerThread())
234 Page = MmAllocPage(Consumer, 0);
235 KeReleaseSpinLock(&AllocationListLock, oldIrql);
236 if (Page.QuadPart == 0LL)
240 *AllocatedPage = Page;
241 InterlockedDecrement((LONG *)&MiPagesRequired);
242 return(STATUS_SUCCESS);
244 /* Otherwise start the pager thread if it isn't already working. */
245 MiStartPagerThread();
246 InsertTailList(&AllocationListHead, &Request.ListEntry);
247 KeReleaseSpinLock(&AllocationListLock, oldIrql);
249 KeWaitForSingleObject(&Request.Event,
256 if (Page.QuadPart == 0LL)
260 MmTransferOwnershipPage(Page, Consumer);
261 *AllocatedPage = Page;
262 InterlockedDecrement((LONG *)&MiPagesRequired);
264 return(STATUS_SUCCESS);
268 * Actually allocate the page.
270 Page = MmAllocPage(Consumer, 0);
271 if (Page.QuadPart == 0LL)
275 *AllocatedPage = Page;
277 return(STATUS_SUCCESS);