- Initialize the used page count for the non paged pool in MmInitializeBalancer.
[reactos.git] / reactos / ntoskrnl / mm / balance.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
4 *
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.
9 *
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.
14 *
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.
18 */
19 /* $Id: balance.c,v 1.22 2003/10/23 20:28:08 hbirr Exp $
20 *
21 * PROJECT: ReactOS kernel
22 * FILE: ntoskrnl/mm/balance.c
23 * PURPOSE: kernel memory managment functions
24 * PROGRAMMER: David Welch (welch@cwcom.net)
25 * UPDATE HISTORY:
26 * Created 27/12/01
27 */
28
29 /* INCLUDES *****************************************************************/
30
31 #include <ddk/ntddk.h>
32 #include <internal/mm.h>
33 #include <ntos/minmax.h>
34
35 #define NDEBUG
36 #include <internal/debug.h>
37
38 /* TYPES ********************************************************************/
39
40 typedef struct _MM_MEMORY_CONSUMER
41 {
42 ULONG PagesUsed;
43 ULONG PagesTarget;
44 NTSTATUS (*Trim)(ULONG Target, ULONG Priority, PULONG NrFreed);
45 } MM_MEMORY_CONSUMER, *PMM_MEMORY_CONSUMER;
46
47 typedef struct _MM_ALLOCATION_REQUEST
48 {
49 PHYSICAL_ADDRESS Page;
50 LIST_ENTRY ListEntry;
51 KEVENT Event;
52 } MM_ALLOCATION_REQUEST, *PMM_ALLOCATION_REQUEST;
53
54 /* GLOBALS ******************************************************************/
55
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;
64
65 /* FUNCTIONS ****************************************************************/
66
67 VOID MmPrintMemoryStatistic(VOID)
68 {
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,
72 MiNrAvailablePages);
73 }
74
75 VOID INIT_FUNCTION
76 MmInitializeBalancer(ULONG NrAvailablePages, ULONG NrSystemPages)
77 {
78 memset(MiMemoryConsumers, 0, sizeof(MiMemoryConsumers));
79 InitializeListHead(&AllocationListHead);
80 KeInitializeSpinLock(&AllocationListLock);
81
82 MiNrAvailablePages = MiNrTotalPages = NrAvailablePages;
83
84 /* Set up targets. */
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;
91 MiMemoryConsumers[MC_NPPOOL].PagesUsed = NrSystemPages;
92 }
93
94 VOID INIT_FUNCTION
95 MmInitializeMemoryConsumer(ULONG Consumer,
96 NTSTATUS (*Trim)(ULONG Target, ULONG Priority,
97 PULONG NrFreed))
98 {
99 MiMemoryConsumers[Consumer].Trim = Trim;
100 }
101
102 NTSTATUS
103 MmReleasePageMemoryConsumer(ULONG Consumer, PHYSICAL_ADDRESS Page)
104 {
105 PMM_ALLOCATION_REQUEST Request;
106 PLIST_ENTRY Entry;
107 KIRQL oldIrql;
108
109 if (Page.QuadPart == 0LL)
110 {
111 DPRINT1("Tried to release page zero.\n");
112 KEBUGCHECK(0);
113 }
114
115 KeAcquireSpinLock(&AllocationListLock, &oldIrql);
116 if (MmGetReferenceCountPage(Page) == 1)
117 {
118 InterlockedDecrement((LONG *)&MiMemoryConsumers[Consumer].PagesUsed);
119 InterlockedIncrement((LONG *)&MiNrAvailablePages);
120 if (IsListEmpty(&AllocationListHead))
121 {
122 KeReleaseSpinLock(&AllocationListLock, oldIrql);
123 MmDereferencePage(Page);
124 }
125 else
126 {
127 Entry = RemoveHeadList(&AllocationListHead);
128 Request = CONTAINING_RECORD(Entry, MM_ALLOCATION_REQUEST, ListEntry);
129 KeReleaseSpinLock(&AllocationListLock, oldIrql);
130 Request->Page = Page;
131 KeSetEvent(&Request->Event, IO_NO_INCREMENT, FALSE);
132 }
133 }
134 else
135 {
136 KeReleaseSpinLock(&AllocationListLock, oldIrql);
137 MmDereferencePage(Page);
138 }
139
140 return(STATUS_SUCCESS);
141 }
142
143 VOID
144 MiTrimMemoryConsumer(ULONG Consumer)
145 {
146 LONG Target;
147 ULONG NrFreedPages;
148
149 Target = MiMemoryConsumers[Consumer].PagesUsed -
150 MiMemoryConsumers[Consumer].PagesTarget;
151 if (Target < 1)
152 {
153 Target = 1;
154 }
155
156 if (MiMemoryConsumers[Consumer].Trim != NULL)
157 {
158 MiMemoryConsumers[Consumer].Trim(Target, 0, &NrFreedPages);
159 }
160 }
161
162 VOID
163 MmRebalanceMemoryConsumers(VOID)
164 {
165 LONG Target;
166 ULONG i;
167 ULONG NrFreedPages;
168 NTSTATUS Status;
169
170 Target = (MiMinimumAvailablePages - MiNrAvailablePages) + MiPagesRequired;
171 Target = max(Target, (LONG) MiMinimumPagesPerRun);
172
173 for (i = 0; i < MC_MAXIMUM && Target > 0; i++)
174 {
175 if (MiMemoryConsumers[i].Trim != NULL)
176 {
177 Status = MiMemoryConsumers[i].Trim(Target, 0, &NrFreedPages);
178 if (!NT_SUCCESS(Status))
179 {
180 KEBUGCHECK(0);
181 }
182 Target = Target - NrFreedPages;
183 }
184 }
185 }
186
187 NTSTATUS
188 MmRequestPageMemoryConsumer(ULONG Consumer, BOOLEAN CanWait,
189 PHYSICAL_ADDRESS* AllocatedPage)
190 {
191 ULONG OldUsed;
192 ULONG OldAvailable;
193 PHYSICAL_ADDRESS Page;
194 KIRQL oldIrql;
195
196 /*
197 * Make sure we don't exceed our individual target.
198 */
199 OldUsed = InterlockedIncrement((LONG *)&MiMemoryConsumers[Consumer].PagesUsed);
200 if (OldUsed >= (MiMemoryConsumers[Consumer].PagesTarget - 1) &&
201 !MiIsPagerThread())
202 {
203 if (!CanWait)
204 {
205 InterlockedDecrement((LONG *)&MiMemoryConsumers[Consumer].PagesUsed);
206 return(STATUS_NO_MEMORY);
207 }
208 MiTrimMemoryConsumer(Consumer);
209 }
210
211 /*
212 * Make sure we don't exceed global targets.
213 */
214 OldAvailable = InterlockedDecrement((LONG *)&MiNrAvailablePages);
215 if (OldAvailable < MiMinimumAvailablePages)
216 {
217 MM_ALLOCATION_REQUEST Request;
218
219 if (!CanWait)
220 {
221 InterlockedIncrement((LONG *)&MiNrAvailablePages);
222 InterlockedDecrement((LONG *)&MiMemoryConsumers[Consumer].PagesUsed);
223 return(STATUS_NO_MEMORY);
224 }
225
226 /* Insert an allocation request. */
227 Request.Page.QuadPart = 0LL;
228 KeInitializeEvent(&Request.Event, NotificationEvent, FALSE);
229 InterlockedIncrement((LONG *)&MiPagesRequired);
230
231 KeAcquireSpinLock(&AllocationListLock, &oldIrql);
232 /* Always let the pager thread itself allocate memory. */
233 if (MiIsPagerThread())
234 {
235 Page = MmAllocPage(Consumer, 0);
236 KeReleaseSpinLock(&AllocationListLock, oldIrql);
237 if (Page.QuadPart == 0LL)
238 {
239 KEBUGCHECK(0);
240 }
241 *AllocatedPage = Page;
242 InterlockedDecrement((LONG *)&MiPagesRequired);
243 return(STATUS_SUCCESS);
244 }
245 /* Otherwise start the pager thread if it isn't already working. */
246 MiStartPagerThread();
247 InsertTailList(&AllocationListHead, &Request.ListEntry);
248 KeReleaseSpinLock(&AllocationListLock, oldIrql);
249
250 KeWaitForSingleObject(&Request.Event,
251 0,
252 KernelMode,
253 FALSE,
254 NULL);
255
256 Page = Request.Page;
257 if (Page.QuadPart == 0LL)
258 {
259 KEBUGCHECK(0);
260 }
261 MmTransferOwnershipPage(Page, Consumer);
262 *AllocatedPage = Page;
263 InterlockedDecrement((LONG *)&MiPagesRequired);
264 MiStopPagerThread();
265 return(STATUS_SUCCESS);
266 }
267
268 /*
269 * Actually allocate the page.
270 */
271 Page = MmAllocPage(Consumer, 0);
272 if (Page.QuadPart == 0LL)
273 {
274 KEBUGCHECK(0);
275 }
276 *AllocatedPage = Page;
277
278 return(STATUS_SUCCESS);
279 }
280
281 /* EOF */