Memory balancer
[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.1 2001/12/28 00:04:45 dwelch Exp $
20 *
21 * COPYRIGHT: See COPYING in the top directory
22 * PROJECT: ReactOS kernel
23 * FILE: ntoskrnl/mm/balance.c
24 * PURPOSE: kernel memory managment functions
25 * PROGRAMMER: David Welch (welch@cwcom.net)
26 * UPDATE HISTORY:
27 * Created 27/12/01
28 */
29
30 /* INCLUDES *****************************************************************/
31
32 #include <ddk/ntddk.h>
33 #include <internal/mm.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, PVOID* FreedPages);
45 } MM_MEMORY_CONSUMER, *PMM_MEMORY_CONSUMER;
46
47 #define MC_CACHE (0)
48 #define MC_USER (1)
49 #define MC_PPOOL (2)
50 #define MC_NPPOOL (3)
51 #define MC_MAXIMUM (4)
52
53 /* GLOBALS ******************************************************************/
54
55 static MM_MEMORY_CONSUMER MiMemoryConsumers[MC_MAXIMUM];
56 static ULONG MiMinimumAvailablePages;
57 static ULONG MiNrAvailablePages;
58 static ULONG MiNrTotalPages;
59
60 /* FUNCTIONS ****************************************************************/
61
62 VOID
63 MmInitializeBalancer(ULONG NrAvailablePages)
64 {
65 memset(MiMemoryConsumers, 0, sizeof(MiMemoryConsumers));
66
67 MiNrAvailablePages = MiNrTotalPages = NrAvailablePages;
68
69 /* Set up targets. */
70 MiMinimumAvailablePages = 64;
71 MiMemoryConsumers[MC_CACHE].PagesTarget = NrAvailablePages / 2;
72 MiMemoryConsumers[MC_USER].PagesTarget = NrAvailablePages - MiMinimumAvailablePages;
73 MiMemoryConsumers[MC_PPOOL].PagesTarget = NrAvailablePages / 2;
74 MiMemoryConsumers[MC_NPPOOL].PagesTarget = 0xFFFFFFFF;
75 }
76
77 VOID
78 MmInitializeMemoryConsumer(ULONG Consumer,
79 NTSTATUS (*Trim)(ULONG Target, ULONG Priority,
80 PULONG NrFreed, PVOID* FreedPages))
81 {
82 MiMemoryConsumers[Consumer].Trim = Trim;
83 }
84
85 NTSTATUS
86 MmReleasePageMemoryConsumer(ULONG Consumer, PVOID Page)
87 {
88 InterlockedDecrement(&MiMemoryConsumers[Consumer].PagesUsed);
89 InterlockedIncrement(&MiNrAvailablePages);
90 MmDereferencePage(Page);
91 return(STATUS_SUCCESS);
92 }
93
94 VOID
95 MiTrimMemoryConsumer(ULONG Consumer)
96 {
97 LONG Target;
98
99 Target = MiMemoryConsumers[Consumer].PagesUsed - MiMemoryConsumers[Consumer].PagesTarget;
100 if (Target < 0)
101 {
102 Target = 1;
103 }
104
105 if (MiMemoryConsumers[Consumer].Trim != NULL)
106 {
107 MiMemoryConsumers[Consumer].Trim(Target, 0, NULL, NULL);
108 }
109 }
110
111 VOID
112 MiRebalanceMemoryConsumers(PVOID* Page)
113 {
114 LONG Target;
115 ULONG i;
116 PVOID* FreedPages;
117 ULONG NrFreedPages;
118 ULONG TotalFreedPages;
119
120 Target = MiMinimumAvailablePages - MiNrAvailablePages;
121 if (Target < 0)
122 {
123 Target = 1;
124 }
125
126 FreedPages = alloca(sizeof(PVOID) * Target);
127 TotalFreedPages = 0;
128
129 for (i = 0; i < MC_MAXIMUM && Target > 0; i++)
130 {
131 if (MiMemoryConsumers[i].Trim != NULL)
132 {
133 MiMemoryConsumers[i].Trim(Target, 0, &NrFreedPages, FreedPages);
134 Target = Target - NrFreedPages;
135 FreedPages = FreedPages + NrFreedPages;
136 TotalFreedPages = TotalFreedPages + NrFreedPages;
137 }
138 }
139 if (Target > 0)
140 {
141 KeBugCheck(0);
142 }
143 if (Page != NULL)
144 {
145 *Page = FreedPages[0];
146 i = 1;
147 }
148 else
149 {
150 i = 0;
151 }
152 for (; i < TotalFreedPages; i++)
153 {
154 MmDereferencePage(FreedPages[i]);
155 }
156 }
157
158 NTSTATUS
159 MmRequestPageMemoryConsumer(ULONG Consumer, BOOLEAN CanWait, PVOID* AllocatedPage)
160 {
161 ULONG OldUsed;
162 ULONG OldAvailable;
163 PVOID Page;
164
165 /*
166 * Make sure we don't exceed our individual target.
167 */
168 OldUsed = InterlockedIncrement(&MiMemoryConsumers[Consumer].PagesUsed);
169 if (OldUsed >= (MiMemoryConsumers[Consumer].PagesTarget - 1))
170 {
171 if (!CanWait)
172 {
173 InterlockedDecrement(&MiMemoryConsumers[Consumer].PagesUsed);
174 return(STATUS_NO_MEMORY);
175 }
176 MiTrimMemoryConsumer(Consumer);
177 }
178
179 /*
180 * Make sure we don't exceed global targets.
181 */
182 OldAvailable = InterlockedDecrement(&MiNrAvailablePages);
183 if (OldAvailable < MiMinimumAvailablePages)
184 {
185 if (!CanWait)
186 {
187 InterlockedIncrement(&MiNrAvailablePages);
188 InterlockedDecrement(&MiMemoryConsumers[Consumer].PagesUsed);
189 return(STATUS_NO_MEMORY);
190 }
191 MiRebalanceMemoryConsumers(NULL);
192 }
193
194 /*
195 * Actually allocate the page.
196 */
197 Page = MmAllocPage(0);
198 if (Page == NULL)
199 {
200 /* Still not trimmed enough. */
201 if (!CanWait)
202 {
203 InterlockedIncrement(&MiNrAvailablePages);
204 InterlockedDecrement(&MiMemoryConsumers[Consumer].PagesUsed);
205 return(STATUS_NO_MEMORY);
206 }
207 MiRebalanceMemoryConsumers(&Page);
208 }
209 *AllocatedPage = Page;
210
211 return(STATUS_SUCCESS);
212 }