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.8 2002/05/14 21:19:18 dwelch Exp $
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)
30 /* INCLUDES *****************************************************************/
32 #include <ddk/ntddk.h>
33 #include <internal/mm.h>
34 #include <ntos/minmax.h>
37 #include <internal/debug.h>
39 /* TYPES ********************************************************************/
41 typedef struct _MM_MEMORY_CONSUMER
45 NTSTATUS (*Trim
)(ULONG Target
, ULONG Priority
, PULONG NrFreed
);
46 } MM_MEMORY_CONSUMER
, *PMM_MEMORY_CONSUMER
;
48 typedef struct _MM_ALLOCATION_REQUEST
53 } MM_ALLOCATION_REQUEST
, *PMM_ALLOCATION_REQUEST
;
55 /* GLOBALS ******************************************************************/
57 static MM_MEMORY_CONSUMER MiMemoryConsumers
[MC_MAXIMUM
];
58 static ULONG MiMinimumAvailablePages
;
59 static ULONG MiNrAvailablePages
;
60 static ULONG MiNrTotalPages
;
61 static LIST_ENTRY AllocationListHead
;
62 static KSPIN_LOCK AllocationListLock
;
63 static ULONG NrWorkingThreads
= 0;
64 static HANDLE WorkerThreadId
;
65 static ULONG MiPagesRequired
= 0;
66 static ULONG MiMinimumPagesPerRun
= 1;
68 /* FUNCTIONS ****************************************************************/
71 MmInitializeBalancer(ULONG NrAvailablePages
)
73 memset(MiMemoryConsumers
, 0, sizeof(MiMemoryConsumers
));
74 InitializeListHead(&AllocationListHead
);
75 KeInitializeSpinLock(&AllocationListLock
);
77 MiNrAvailablePages
= MiNrTotalPages
= NrAvailablePages
;
80 MiMinimumAvailablePages
= 64;
81 MiMemoryConsumers
[MC_CACHE
].PagesTarget
= NrAvailablePages
/ 2;
82 MiMemoryConsumers
[MC_USER
].PagesTarget
= NrAvailablePages
- MiMinimumAvailablePages
;
83 MiMemoryConsumers
[MC_PPOOL
].PagesTarget
= NrAvailablePages
/ 2;
84 MiMemoryConsumers
[MC_NPPOOL
].PagesTarget
= 0xFFFFFFFF;
88 MmInitializeMemoryConsumer(ULONG Consumer
,
89 NTSTATUS (*Trim
)(ULONG Target
, ULONG Priority
,
92 MiMemoryConsumers
[Consumer
].Trim
= Trim
;
96 MmReleasePageMemoryConsumer(ULONG Consumer
, PVOID Page
)
98 PMM_ALLOCATION_REQUEST Request
;
104 DPRINT1("Tried to release page zero.\n");
108 InterlockedDecrement(&MiMemoryConsumers
[Consumer
].PagesUsed
);
109 InterlockedIncrement(&MiNrAvailablePages
);
110 InterlockedDecrement(&MiPagesRequired
);
111 KeAcquireSpinLock(&AllocationListLock
, &oldIrql
);
112 if (IsListEmpty(&AllocationListHead
))
114 KeReleaseSpinLock(&AllocationListLock
, oldIrql
);
115 MmDereferencePage(Page
);
119 Entry
= RemoveHeadList(&AllocationListHead
);
120 Request
= CONTAINING_RECORD(Entry
, MM_ALLOCATION_REQUEST
, ListEntry
);
121 KeReleaseSpinLock(&AllocationListLock
, oldIrql
);
122 Request
->Page
= Page
;
123 KeSetEvent(&Request
->Event
, IO_NO_INCREMENT
, FALSE
);
125 return(STATUS_SUCCESS
);
129 MiTrimMemoryConsumer(ULONG Consumer
)
133 Target
= MiMemoryConsumers
[Consumer
].PagesUsed
-
134 MiMemoryConsumers
[Consumer
].PagesTarget
;
140 if (MiMemoryConsumers
[Consumer
].Trim
!= NULL
)
142 MiMemoryConsumers
[Consumer
].Trim(Target
, 0, NULL
);
147 MiRebalanceMemoryConsumers(VOID
)
154 Target
= (MiMinimumAvailablePages
- MiNrAvailablePages
) + MiPagesRequired
;
155 Target
= min(Target
, MiMinimumPagesPerRun
);
157 for (i
= 0; i
< MC_MAXIMUM
&& Target
> 0; i
++)
159 if (MiMemoryConsumers
[i
].Trim
!= NULL
)
161 Status
= MiMemoryConsumers
[i
].Trim(Target
, 0, &NrFreedPages
);
162 if (!NT_SUCCESS(Status
))
166 Target
= Target
- NrFreedPages
;
176 MmRequestPageMemoryConsumer(ULONG Consumer
, BOOLEAN CanWait
, PVOID
* AllocatedPage
)
184 * Make sure we don't exceed our individual target.
186 OldUsed
= InterlockedIncrement(&MiMemoryConsumers
[Consumer
].PagesUsed
);
187 if (OldUsed
>= (MiMemoryConsumers
[Consumer
].PagesTarget
- 1) &&
188 WorkerThreadId
!= PsGetCurrentThreadId())
192 InterlockedDecrement(&MiMemoryConsumers
[Consumer
].PagesUsed
);
193 return(STATUS_NO_MEMORY
);
195 MiTrimMemoryConsumer(Consumer
);
199 * Make sure we don't exceed global targets.
201 OldAvailable
= InterlockedDecrement(&MiNrAvailablePages
);
202 if (OldAvailable
< MiMinimumAvailablePages
)
204 MM_ALLOCATION_REQUEST Request
;
208 InterlockedIncrement(&MiNrAvailablePages
);
209 InterlockedDecrement(&MiMemoryConsumers
[Consumer
].PagesUsed
);
210 return(STATUS_NO_MEMORY
);
213 /* Insert an allocation request. */
215 KeInitializeEvent(&Request
.Event
, NotificationEvent
, FALSE
);
216 InterlockedIncrement(&MiPagesRequired
);
218 KeAcquireSpinLock(&AllocationListLock
, &oldIrql
);
219 if (NrWorkingThreads
== 0)
221 InsertTailList(&AllocationListHead
, &Request
.ListEntry
);
223 KeReleaseSpinLock(&AllocationListLock
, oldIrql
);
224 WorkerThreadId
= PsGetCurrentThreadId();
225 MiRebalanceMemoryConsumers();
226 KeAcquireSpinLock(&AllocationListLock
, &oldIrql
);
229 KeReleaseSpinLock(&AllocationListLock
, oldIrql
);
233 if (WorkerThreadId
== PsGetCurrentThreadId())
235 Page
= MmAllocPage(Consumer
, 0);
236 KeReleaseSpinLock(&AllocationListLock
, oldIrql
);
241 *AllocatedPage
= Page
;
242 return(STATUS_SUCCESS
);
244 InsertTailList(&AllocationListHead
, &Request
.ListEntry
);
245 KeReleaseSpinLock(&AllocationListLock
, oldIrql
);
247 KeWaitForSingleObject(&Request
.Event
,
258 MmTransferOwnershipPage(Page
, Consumer
);
259 *AllocatedPage
= Page
;
260 return(STATUS_SUCCESS
);
264 * Actually allocate the page.
266 Page
= MmAllocPage(Consumer
, 0);
271 *AllocatedPage
= Page
;
273 return(STATUS_SUCCESS
);