fixed uninitialized variable warning
[reactos.git] / reactos / ntoskrnl / mm / balance.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/mm/balance.c
6 * PURPOSE: kernel memory managment functions
7 *
8 * PROGRAMMERS: David Welch (welch@cwcom.net)
9 */
10
11 /* INCLUDES *****************************************************************/
12
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <internal/debug.h>
16
17 /* TYPES ********************************************************************/
18 typedef struct _MM_ALLOCATION_REQUEST
19 {
20 PFN_TYPE Page;
21 LIST_ENTRY ListEntry;
22 KEVENT Event;
23 }
24 MM_ALLOCATION_REQUEST, *PMM_ALLOCATION_REQUEST;
25
26 /* GLOBALS ******************************************************************/
27
28 MM_MEMORY_CONSUMER MiMemoryConsumers[MC_MAXIMUM];
29 static ULONG MiMinimumAvailablePages;
30 static ULONG MiNrTotalPages;
31 static LIST_ENTRY AllocationListHead;
32 static KSPIN_LOCK AllocationListLock;
33 static ULONG MiPagesRequired = 0;
34 static ULONG MiMinimumPagesPerRun = 10;
35
36 static CLIENT_ID MiBalancerThreadId;
37 static HANDLE MiBalancerThreadHandle = NULL;
38 static KEVENT MiBalancerEvent;
39 static KTIMER MiBalancerTimer;
40 static LONG MiBalancerWork = 0;
41
42 /* FUNCTIONS ****************************************************************/
43
44 VOID MmPrintMemoryStatistic(VOID)
45 {
46 DbgPrint("MC_CACHE %d, MC_USER %d, MC_PPOOL %d, MC_NPPOOL %d, MmStats.NrFreePages %d\n",
47 MiMemoryConsumers[MC_CACHE].PagesUsed, MiMemoryConsumers[MC_USER].PagesUsed,
48 MiMemoryConsumers[MC_PPOOL].PagesUsed, MiMemoryConsumers[MC_NPPOOL].PagesUsed,
49 MmStats.NrFreePages);
50 }
51
52 VOID
53 INIT_FUNCTION
54 NTAPI
55 MmInitializeBalancer(ULONG NrAvailablePages, ULONG NrSystemPages)
56 {
57 memset(MiMemoryConsumers, 0, sizeof(MiMemoryConsumers));
58 InitializeListHead(&AllocationListHead);
59 KeInitializeSpinLock(&AllocationListLock);
60
61 MiNrTotalPages = NrAvailablePages;
62
63 /* Set up targets. */
64 MiMinimumAvailablePages = 64;
65 MiMemoryConsumers[MC_CACHE].PagesTarget = NrAvailablePages / 2;
66 MiMemoryConsumers[MC_USER].PagesTarget =
67 NrAvailablePages - MiMinimumAvailablePages;
68 MiMemoryConsumers[MC_PPOOL].PagesTarget = NrAvailablePages / 2;
69 MiMemoryConsumers[MC_NPPOOL].PagesTarget = 0xFFFFFFFF;
70 MiMemoryConsumers[MC_NPPOOL].PagesUsed = NrSystemPages;
71 }
72
73 VOID
74 INIT_FUNCTION
75 NTAPI
76 MmInitializeMemoryConsumer(ULONG Consumer,
77 NTSTATUS (*Trim)(ULONG Target, ULONG Priority,
78 PULONG NrFreed))
79 {
80 MiMemoryConsumers[Consumer].Trim = Trim;
81 }
82
83 NTSTATUS
84 NTAPI
85 MmReleasePageMemoryConsumer(ULONG Consumer, PFN_TYPE Page)
86 {
87 PMM_ALLOCATION_REQUEST Request;
88 PLIST_ENTRY Entry;
89 KIRQL oldIrql;
90
91 if (Page == 0)
92 {
93 DPRINT1("Tried to release page zero.\n");
94 KEBUGCHECK(0);
95 }
96
97 KeAcquireSpinLock(&AllocationListLock, &oldIrql);
98 if (MmGetReferenceCountPage(Page) == 1)
99 {
100 InterlockedDecrementUL(&MiMemoryConsumers[Consumer].PagesUsed);
101 if (IsListEmpty(&AllocationListHead) || MmStats.NrFreePages < MiMinimumAvailablePages)
102 {
103 KeReleaseSpinLock(&AllocationListLock, oldIrql);
104 MmDereferencePage(Page);
105 }
106 else
107 {
108 Entry = RemoveHeadList(&AllocationListHead);
109 Request = CONTAINING_RECORD(Entry, MM_ALLOCATION_REQUEST, ListEntry);
110 KeReleaseSpinLock(&AllocationListLock, oldIrql);
111 Request->Page = Page;
112 KeSetEvent(&Request->Event, IO_NO_INCREMENT, FALSE);
113 }
114 }
115 else
116 {
117 KeReleaseSpinLock(&AllocationListLock, oldIrql);
118 MmDereferencePage(Page);
119 }
120
121 return(STATUS_SUCCESS);
122 }
123
124 VOID
125 NTAPI
126 MiTrimMemoryConsumer(ULONG Consumer)
127 {
128 LONG Target;
129 ULONG NrFreedPages;
130
131 Target = MiMemoryConsumers[Consumer].PagesUsed -
132 MiMemoryConsumers[Consumer].PagesTarget;
133 if (Target < 1)
134 {
135 Target = 1;
136 }
137
138 if (MiMemoryConsumers[Consumer].Trim != NULL)
139 {
140 MiMemoryConsumers[Consumer].Trim(Target, 0, &NrFreedPages);
141 }
142 }
143
144 VOID
145 NTAPI
146 MmRebalanceMemoryConsumers(VOID)
147 {
148 LONG Target;
149 ULONG i;
150 ULONG NrFreedPages;
151 NTSTATUS Status;
152
153 Target = (MiMinimumAvailablePages - MmStats.NrFreePages) + MiPagesRequired;
154 Target = max(Target, (LONG) MiMinimumPagesPerRun);
155
156 for (i = 0; i < MC_MAXIMUM && Target > 0; i++)
157 {
158 if (MiMemoryConsumers[i].Trim != NULL)
159 {
160 Status = MiMemoryConsumers[i].Trim(Target, 0, &NrFreedPages);
161 if (!NT_SUCCESS(Status))
162 {
163 KEBUGCHECK(0);
164 }
165 Target = Target - NrFreedPages;
166 }
167 }
168 }
169
170 static BOOLEAN
171 MiIsBalancerThread(VOID)
172 {
173 return MiBalancerThreadHandle != NULL &&
174 PsGetCurrentThread() == MiBalancerThreadId.UniqueThread;
175 }
176
177 NTSTATUS
178 NTAPI
179 MmRequestPageMemoryConsumer(ULONG Consumer, BOOLEAN CanWait,
180 PPFN_TYPE AllocatedPage)
181 {
182 ULONG OldUsed;
183 PFN_TYPE Page;
184 KIRQL oldIrql;
185
186 /*
187 * Make sure we don't exceed our individual target.
188 */
189 OldUsed = InterlockedIncrementUL(&MiMemoryConsumers[Consumer].PagesUsed);
190 if (OldUsed >= (MiMemoryConsumers[Consumer].PagesTarget - 1) &&
191 !MiIsBalancerThread())
192 {
193 if (!CanWait)
194 {
195 InterlockedDecrementUL(&MiMemoryConsumers[Consumer].PagesUsed);
196 return(STATUS_NO_MEMORY);
197 }
198 MiTrimMemoryConsumer(Consumer);
199 }
200
201 /*
202 * Allocate always memory for the non paged pool and for the pager thread.
203 */
204 if (Consumer == MC_NPPOOL || MiIsBalancerThread())
205 {
206 Page = MmAllocPage(Consumer, 0);
207 if (Page == 0)
208 {
209 KEBUGCHECK(NO_PAGES_AVAILABLE);
210 }
211 *AllocatedPage = Page;
212 if (MmStats.NrFreePages <= MiMinimumAvailablePages &&
213 MiBalancerThreadHandle != NULL)
214 {
215 KeSetEvent(&MiBalancerEvent, IO_NO_INCREMENT, FALSE);
216 }
217 return(STATUS_SUCCESS);
218 }
219
220 /*
221 * Make sure we don't exceed global targets.
222 */
223 if (MmStats.NrFreePages <= MiMinimumAvailablePages)
224 {
225 MM_ALLOCATION_REQUEST Request;
226
227 if (!CanWait)
228 {
229 InterlockedDecrementUL(&MiMemoryConsumers[Consumer].PagesUsed);
230 return(STATUS_NO_MEMORY);
231 }
232
233 /* Insert an allocation request. */
234 Request.Page = 0;
235
236 KeInitializeEvent(&Request.Event, NotificationEvent, FALSE);
237 InterlockedIncrementUL(&MiPagesRequired);
238
239 KeAcquireSpinLock(&AllocationListLock, &oldIrql);
240
241 if (MiBalancerThreadHandle != NULL)
242 {
243 KeSetEvent(&MiBalancerEvent, IO_NO_INCREMENT, FALSE);
244 }
245 InsertTailList(&AllocationListHead, &Request.ListEntry);
246 KeReleaseSpinLock(&AllocationListLock, oldIrql);
247
248 KeWaitForSingleObject(&Request.Event,
249 0,
250 KernelMode,
251 FALSE,
252 NULL);
253
254 Page = Request.Page;
255 if (Page == 0)
256 {
257 KEBUGCHECK(NO_PAGES_AVAILABLE);
258 }
259 MmTransferOwnershipPage(Page, Consumer);
260 *AllocatedPage = Page;
261 InterlockedDecrementUL(&MiPagesRequired);
262 return(STATUS_SUCCESS);
263 }
264
265 /*
266 * Actually allocate the page.
267 */
268 Page = MmAllocPage(Consumer, 0);
269 if (Page == 0)
270 {
271 KEBUGCHECK(NO_PAGES_AVAILABLE);
272 }
273 *AllocatedPage = Page;
274
275 return(STATUS_SUCCESS);
276 }
277
278 VOID STDCALL
279 MiBalancerThread(PVOID Unused)
280 {
281 PVOID WaitObjects[2];
282 NTSTATUS Status;
283 ULONG i;
284 ULONG NrFreedPages;
285 ULONG NrPagesUsed;
286 ULONG Target;
287 BOOLEAN ShouldRun;
288
289
290 WaitObjects[0] = &MiBalancerEvent;
291 WaitObjects[1] = &MiBalancerTimer;
292
293 while (1)
294 {
295 Status = KeWaitForMultipleObjects(2,
296 WaitObjects,
297 WaitAny,
298 Executive,
299 KernelMode,
300 FALSE,
301 NULL,
302 NULL);
303
304 if (Status == STATUS_SUCCESS)
305 {
306 /* MiBalancerEvent */
307 CHECKPOINT;
308 while (MmStats.NrFreePages < MiMinimumAvailablePages + 5)
309 {
310 for (i = 0; i < MC_MAXIMUM; i++)
311 {
312 if (MiMemoryConsumers[i].Trim != NULL)
313 {
314 NrFreedPages = 0;
315 Status = MiMemoryConsumers[i].Trim(MiMinimumPagesPerRun, 0, &NrFreedPages);
316 if (!NT_SUCCESS(Status))
317 {
318 KEBUGCHECK(0);
319 }
320 }
321 }
322 }
323 InterlockedExchange(&MiBalancerWork, 0);
324 CHECKPOINT;
325 }
326 else if (Status == STATUS_SUCCESS + 1)
327 {
328 /* MiBalancerTimer */
329 ShouldRun = MmStats.NrFreePages < MiMinimumAvailablePages + 5 ? TRUE : FALSE;
330 for (i = 0; i < MC_MAXIMUM; i++)
331 {
332 if (MiMemoryConsumers[i].Trim != NULL)
333 {
334 NrPagesUsed = MiMemoryConsumers[i].PagesUsed;
335 if (NrPagesUsed > MiMemoryConsumers[i].PagesTarget || ShouldRun)
336 {
337 if (NrPagesUsed > MiMemoryConsumers[i].PagesTarget)
338 {
339 Target = max (NrPagesUsed - MiMemoryConsumers[i].PagesTarget,
340 MiMinimumPagesPerRun);
341 }
342 else
343 {
344 Target = MiMinimumPagesPerRun;
345 }
346 NrFreedPages = 0;
347 Status = MiMemoryConsumers[i].Trim(Target, 0, &NrFreedPages);
348 if (!NT_SUCCESS(Status))
349 {
350 KEBUGCHECK(0);
351 }
352 }
353 }
354 }
355 }
356 else
357 {
358 DPRINT1("KeWaitForMultipleObjects failed, status = %x\n", Status);
359 KEBUGCHECK(0);
360 }
361 }
362 }
363
364 VOID
365 INIT_FUNCTION
366 NTAPI
367 MiInitBalancerThread(VOID)
368 {
369 KPRIORITY Priority;
370 NTSTATUS Status;
371 #if !defined(__GNUC__)
372
373 LARGE_INTEGER dummyJunkNeeded;
374 dummyJunkNeeded.QuadPart = -20000000; /* 2 sec */
375 ;
376 #endif
377
378 CHECKPOINT;
379
380 KeInitializeEvent(&MiBalancerEvent, SynchronizationEvent, FALSE);
381 KeInitializeTimerEx(&MiBalancerTimer, SynchronizationTimer);
382 KeSetTimerEx(&MiBalancerTimer,
383 #if defined(__GNUC__)
384 (LARGE_INTEGER)(LONGLONG)-20000000LL, /* 2 sec */
385 #else
386 dummyJunkNeeded,
387 #endif
388 2000, /* 2 sec */
389 NULL);
390
391 Status = PsCreateSystemThread(&MiBalancerThreadHandle,
392 THREAD_ALL_ACCESS,
393 NULL,
394 NULL,
395 &MiBalancerThreadId,
396 (PKSTART_ROUTINE) MiBalancerThread,
397 NULL);
398 if (!NT_SUCCESS(Status))
399 {
400 KEBUGCHECK(0);
401 }
402
403 Priority = LOW_REALTIME_PRIORITY + 1;
404 NtSetInformationThread(MiBalancerThreadHandle,
405 ThreadPriority,
406 &Priority,
407 sizeof(Priority));
408
409 }
410
411
412 /* EOF */