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.12 2002/09/07 15:12:59 chorns 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 *****************************************************************/
34 #include <internal/debug.h>
37 /* TYPES ********************************************************************/
39 typedef struct _MM_MEMORY_CONSUMER
43 NTSTATUS (*Trim
)(ULONG Target
, ULONG Priority
, PULONG NrFreed
);
44 } MM_MEMORY_CONSUMER
, *PMM_MEMORY_CONSUMER
;
46 typedef struct _MM_ALLOCATION_REQUEST
48 PHYSICAL_ADDRESS Page
;
51 } MM_ALLOCATION_REQUEST
, *PMM_ALLOCATION_REQUEST
;
53 /* GLOBALS ******************************************************************/
55 static MM_MEMORY_CONSUMER MiMemoryConsumers
[MC_MAXIMUM
];
56 static ULONG MiMinimumAvailablePages
;
57 static ULONG MiNrAvailablePages
;
58 static ULONG MiNrTotalPages
;
59 static LIST_ENTRY AllocationListHead
;
60 static KSPIN_LOCK AllocationListLock
;
61 static ULONG NrWorkingThreads
= 0;
62 static HANDLE WorkerThreadId
;
63 static ULONG MiPagesRequired
= 0;
64 static ULONG MiMinimumPagesPerRun
= 1;
66 /* FUNCTIONS ****************************************************************/
68 VOID
MmPrintMemoryStatistic(VOID
)
70 DbgPrint("MC_CACHE %d, MC_USER %d, MC_PPOOL %d, MC_NPPOOL %d\n",
71 MiMemoryConsumers
[MC_CACHE
].PagesUsed
,
72 MiMemoryConsumers
[MC_USER
].PagesUsed
,
73 MiMemoryConsumers
[MC_PPOOL
].PagesUsed
,
74 MiMemoryConsumers
[MC_NPPOOL
].PagesUsed
);
78 MmInitializeBalancer(ULONG NrAvailablePages
)
80 memset(MiMemoryConsumers
, 0, sizeof(MiMemoryConsumers
));
81 InitializeListHead(&AllocationListHead
);
82 KeInitializeSpinLock(&AllocationListLock
);
84 MiNrAvailablePages
= MiNrTotalPages
= NrAvailablePages
;
87 MiMinimumAvailablePages
= 64;
88 MiMemoryConsumers
[MC_CACHE
].PagesTarget
= NrAvailablePages
/ 2;
89 MiMemoryConsumers
[MC_USER
].PagesTarget
=
90 NrAvailablePages
- MiMinimumAvailablePages
;
91 MiMemoryConsumers
[MC_PPOOL
].PagesTarget
= NrAvailablePages
/ 2;
92 MiMemoryConsumers
[MC_NPPOOL
].PagesTarget
= 0xFFFFFFFF;
96 MmInitializeMemoryConsumer(ULONG Consumer
,
97 NTSTATUS (*Trim
)(ULONG Target
, ULONG Priority
,
100 MiMemoryConsumers
[Consumer
].Trim
= Trim
;
104 MmReleasePageMemoryConsumer(ULONG Consumer
, PHYSICAL_ADDRESS Page
)
106 PMM_ALLOCATION_REQUEST Request
;
110 if (Page
.QuadPart
== 0LL)
112 DPRINT1("Tried to release page zero.\n");
116 KeAcquireSpinLock(&AllocationListLock
, &oldIrql
);
117 if (MmGetReferenceCountPage(Page
) == 1)
119 InterlockedDecrement(&MiMemoryConsumers
[Consumer
].PagesUsed
);
120 InterlockedIncrement(&MiNrAvailablePages
);
121 InterlockedDecrement(&MiPagesRequired
);
122 if (IsListEmpty(&AllocationListHead
))
124 KeReleaseSpinLock(&AllocationListLock
, oldIrql
);
125 MmDereferencePage(Page
);
129 Entry
= RemoveHeadList(&AllocationListHead
);
130 Request
= CONTAINING_RECORD(Entry
, MM_ALLOCATION_REQUEST
, ListEntry
);
131 KeReleaseSpinLock(&AllocationListLock
, oldIrql
);
132 Request
->Page
= Page
;
133 KeSetEvent(&Request
->Event
, IO_NO_INCREMENT
, FALSE
);
138 KeReleaseSpinLock(&AllocationListLock
, oldIrql
);
139 MmDereferencePage(Page
);
142 return(STATUS_SUCCESS
);
146 MiTrimMemoryConsumer(ULONG Consumer
)
151 Target
= MiMemoryConsumers
[Consumer
].PagesUsed
-
152 MiMemoryConsumers
[Consumer
].PagesTarget
;
158 if (MiMemoryConsumers
[Consumer
].Trim
!= NULL
)
160 MiMemoryConsumers
[Consumer
].Trim(Target
, 0, &NrFreedPages
);
165 MiRebalanceMemoryConsumers(VOID
)
172 Target
= (MiMinimumAvailablePages
- MiNrAvailablePages
) + MiPagesRequired
;
173 Target
= min(Target
, MiMinimumPagesPerRun
);
175 for (i
= 0; i
< MC_MAXIMUM
&& Target
> 0; i
++)
177 if (MiMemoryConsumers
[i
].Trim
!= NULL
)
179 Status
= MiMemoryConsumers
[i
].Trim(Target
, 0, &NrFreedPages
);
180 if (!NT_SUCCESS(Status
))
184 Target
= Target
- NrFreedPages
;
194 MmRequestPageMemoryConsumer(ULONG Consumer
, BOOLEAN CanWait
,
195 PHYSICAL_ADDRESS
* AllocatedPage
)
199 PHYSICAL_ADDRESS Page
;
203 * Make sure we don't exceed our individual target.
205 OldUsed
= InterlockedIncrement(&MiMemoryConsumers
[Consumer
].PagesUsed
);
206 if (OldUsed
>= (MiMemoryConsumers
[Consumer
].PagesTarget
- 1) &&
207 WorkerThreadId
!= PsGetCurrentThreadId())
211 InterlockedDecrement(&MiMemoryConsumers
[Consumer
].PagesUsed
);
212 return(STATUS_NO_MEMORY
);
214 MiTrimMemoryConsumer(Consumer
);
218 * Make sure we don't exceed global targets.
220 OldAvailable
= InterlockedDecrement(&MiNrAvailablePages
);
221 if (OldAvailable
< MiMinimumAvailablePages
)
223 MM_ALLOCATION_REQUEST Request
;
227 InterlockedIncrement(&MiNrAvailablePages
);
228 InterlockedDecrement(&MiMemoryConsumers
[Consumer
].PagesUsed
);
229 return(STATUS_NO_MEMORY
);
232 /* Insert an allocation request. */
233 Request
.Page
.QuadPart
= 0LL;
234 KeInitializeEvent(&Request
.Event
, NotificationEvent
, FALSE
);
235 InterlockedIncrement(&MiPagesRequired
);
237 KeAcquireSpinLock(&AllocationListLock
, &oldIrql
);
238 if (NrWorkingThreads
== 0)
240 InsertTailList(&AllocationListHead
, &Request
.ListEntry
);
242 KeReleaseSpinLock(&AllocationListLock
, oldIrql
);
243 WorkerThreadId
= PsGetCurrentThreadId();
244 MiRebalanceMemoryConsumers();
245 KeAcquireSpinLock(&AllocationListLock
, &oldIrql
);
248 KeReleaseSpinLock(&AllocationListLock
, oldIrql
);
252 if (WorkerThreadId
== PsGetCurrentThreadId())
254 Page
= MmAllocPage(Consumer
, 0);
255 KeReleaseSpinLock(&AllocationListLock
, oldIrql
);
256 if (Page
.QuadPart
== 0LL)
260 *AllocatedPage
= Page
;
261 return(STATUS_SUCCESS
);
263 InsertTailList(&AllocationListHead
, &Request
.ListEntry
);
264 KeReleaseSpinLock(&AllocationListLock
, oldIrql
);
266 KeWaitForSingleObject(&Request
.Event
,
273 if (Page
.QuadPart
== 0LL)
277 MmTransferOwnershipPage(Page
, Consumer
);
278 *AllocatedPage
= Page
;
279 return(STATUS_SUCCESS
);
283 * Actually allocate the page.
285 Page
= MmAllocPage(Consumer
, 0);
286 if (Page
.QuadPart
== 0LL)
290 *AllocatedPage
= Page
;
292 return(STATUS_SUCCESS
);