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.
19 /* $Id: balance.c,v 1.19 2003/07/13 14:36:32 dwelch Exp $
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
);