ReactOS  0.4.15-dev-5131-g311fcc6
balance.c
Go to the documentation of this file.
1 /*
2  * COPYRIGHT: See COPYING in the top level directory
3  * PROJECT: ReactOS kernel
4  * FILE: ntoskrnl/mm/balance.c
5  * PURPOSE: kernel memory managment functions
6  *
7  * PROGRAMMERS: David Welch (welch@cwcom.net)
8  * Cameron Gutman (cameron.gutman@reactos.org)
9  */
10 
11 /* INCLUDES *****************************************************************/
12 
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <debug.h>
16 
17 #include "ARM3/miarm.h"
18 
19 
20 /* TYPES ********************************************************************/
21 typedef struct _MM_ALLOCATION_REQUEST
22 {
26 }
28 /* GLOBALS ******************************************************************/
29 
37 
39 
40 /* FUNCTIONS ****************************************************************/
41 
42 CODE_SEG("INIT")
43 VOID
44 NTAPI
45 MmInitializeBalancer(ULONG NrAvailablePages, ULONG NrSystemPages)
46 {
48 
49  /* Set up targets. */
52  MiMemoryConsumers[MC_USER].PagesTarget = NrAvailablePages / 2;
53 }
54 
55 CODE_SEG("INIT")
56 VOID
57 NTAPI
59  ULONG Consumer,
60  NTSTATUS (*Trim)(ULONG Target, ULONG Priority, PULONG NrFreed))
61 {
62  MiMemoryConsumers[Consumer].Trim = Trim;
63 }
64 
65 VOID
66 NTAPI
68  IN PFN_NUMBER PageFrameIndex
69 );
70 
72 NTAPI
74 {
75  KIRQL OldIrql;
76 
77  if (Page == 0)
78  {
79  DPRINT1("Tried to release page zero.\n");
80  KeBugCheck(MEMORY_MANAGEMENT);
81  }
82 
83  (void)InterlockedDecrementUL(&MiMemoryConsumers[Consumer].PagesUsed);
85 
86  OldIrql = MiAcquirePfnLock();
87 
89 
90  MiReleasePfnLock(OldIrql);
91 
92  return(STATUS_SUCCESS);
93 }
94 
95 ULONG
96 NTAPI
97 MiTrimMemoryConsumer(ULONG Consumer, ULONG InitialTarget)
98 {
99  ULONG Target = InitialTarget;
100  ULONG NrFreedPages = 0;
102 
103  /* Make sure we can trim this consumer */
104  if (!MiMemoryConsumers[Consumer].Trim)
105  {
106  /* Return the unmodified initial target */
107  return InitialTarget;
108  }
109 
111  {
112  /* Global page limit exceeded */
114  }
115  else if (MiMemoryConsumers[Consumer].PagesUsed > MiMemoryConsumers[Consumer].PagesTarget)
116  {
117  /* Consumer page limit exceeded */
118  Target = max(Target, MiMemoryConsumers[Consumer].PagesUsed - MiMemoryConsumers[Consumer].PagesTarget);
119  }
120 
121  if (Target)
122  {
123  /* Now swap the pages out */
125 
126  DPRINT("Trimming consumer %lu: Freed %lu pages with a target of %lu pages\n", Consumer, NrFreedPages, Target);
127 
128  if (!NT_SUCCESS(Status))
129  {
130  KeBugCheck(MEMORY_MANAGEMENT);
131  }
132  }
133 
134  /* Return the page count needed to be freed to meet the initial target */
135  return (InitialTarget > NrFreedPages) ? (InitialTarget - NrFreedPages) : 0;
136 }
137 
138 NTSTATUS
140 {
141  PFN_NUMBER CurrentPage;
143 
144  (*NrFreedPages) = 0;
145 
146  DPRINT1("MM BALANCER: %s\n", Priority ? "Paging out!" : "Removing access bit!");
147 
148  CurrentPage = MmGetLRUFirstUserPage();
149  while (CurrentPage != 0 && Target > 0)
150  {
151  if (Priority)
152  {
153  Status = MmPageOutPhysicalAddress(CurrentPage);
154  if (NT_SUCCESS(Status))
155  {
156  DPRINT("Succeeded\n");
157  Target--;
158  (*NrFreedPages)++;
159  }
160  }
161  else
162  {
163  /* When not paging-out agressively, just reset the accessed bit */
165  PVOID Address = NULL;
166  BOOLEAN Accessed = FALSE;
167 
168  /*
169  * We have a lock-ordering problem here. We cant lock the PFN DB before the Process address space.
170  * So we must use circonvoluted loops.
171  * Well...
172  */
173  while (TRUE)
174  {
176  KIRQL OldIrql = MiAcquirePfnLock();
178  while (Entry)
179  {
180  if (RMAP_IS_SEGMENT(Entry->Address))
181  {
182  Entry = Entry->Next;
183  continue;
184  }
185 
186  /* Check that we didn't treat this entry before */
187  if (Entry->Address < Address)
188  {
189  Entry = Entry->Next;
190  continue;
191  }
192 
193  if ((Entry->Address == Address) && (Entry->Process <= Process))
194  {
195  Entry = Entry->Next;
196  continue;
197  }
198 
199  break;
200  }
201 
202  if (!Entry)
203  {
204  MiReleasePfnLock(OldIrql);
205  break;
206  }
207 
208  Process = Entry->Process;
209  Address = Entry->Address;
210 
212 
213  if (!ExAcquireRundownProtection(&Process->RundownProtect))
214  {
216  MiReleasePfnLock(OldIrql);
217  continue;
218  }
219 
220  MiReleasePfnLock(OldIrql);
221 
224 
225  /* Be sure this is still valid. */
227  {
229  Accessed = Accessed || Pte->u.Hard.Accessed;
230  Pte->u.Hard.Accessed = 0;
231 
232  /* There is no need to invalidate, the balancer thread is never on a user process */
233  //KeInvalidateTlbEntry(Address);
234  }
235 
237 
239  ExReleaseRundownProtection(&Process->RundownProtect);
241  }
242 
243  if (!Accessed)
244  {
245  /* Nobody accessed this page since the last time we check. Time to clean up */
246 
247  Status = MmPageOutPhysicalAddress(CurrentPage);
248  // DPRINT1("Paged-out one page: %s\n", NT_SUCCESS(Status) ? "Yes" : "No");
249  (void)Status;
250  }
251 
252  /* Done for this page. */
253  Target--;
254  }
255 
256  CurrentPage = MmGetLRUNextUserPage(CurrentPage, TRUE);
257  }
258 
259  if (CurrentPage)
260  {
261  KIRQL OldIrql = MiAcquirePfnLock();
262  MmDereferencePage(CurrentPage);
263  MiReleasePfnLock(OldIrql);
264  }
265 
266  return STATUS_SUCCESS;
267 }
268 
269 VOID
270 NTAPI
272 {
273  // if (InterlockedCompareExchange(&PageOutThreadActive, 0, 1) == 0)
274  {
276  }
277 }
278 
279 NTSTATUS
280 NTAPI
282  PPFN_NUMBER AllocatedPage)
283 {
285 
286  /* Update the target */
287  InterlockedIncrementUL(&MiMemoryConsumers[Consumer].PagesUsed);
289 
290  /*
291  * Actually allocate the page.
292  */
293  Page = MmAllocPage(Consumer);
294  if (Page == 0)
295  {
296  KeBugCheck(NO_PAGES_AVAILABLE);
297  }
298  *AllocatedPage = Page;
299 
300  return(STATUS_SUCCESS);
301 }
302 
303 
304 VOID NTAPI
306 {
307  PVOID WaitObjects[2];
309  ULONG i;
310 
311  WaitObjects[0] = &MiBalancerEvent;
312  WaitObjects[1] = &MiBalancerTimer;
313 
314  while (1)
315  {
317  WaitObjects,
318  WaitAny,
319  Executive,
320  KernelMode,
321  FALSE,
322  NULL,
323  NULL);
324 
326  {
327  ULONG InitialTarget = 0;
328 
329  do
330  {
331  ULONG OldTarget = InitialTarget;
332 
333  /* Trim each consumer */
334  for (i = 0; i < MC_MAXIMUM; i++)
335  {
336  InitialTarget = MiTrimMemoryConsumer(i, InitialTarget);
337  }
338 
339  /* No pages left to swap! */
340  if (InitialTarget != 0 &&
341  InitialTarget == OldTarget)
342  {
343  /* Game over */
344  KeBugCheck(NO_PAGES_AVAILABLE);
345  }
346  }
347  while (InitialTarget != 0);
348 
349  if (Status == STATUS_WAIT_0)
351  }
352  else
353  {
354  DPRINT1("KeWaitForMultipleObjects failed, status = %x\n", Status);
355  KeBugCheck(MEMORY_MANAGEMENT);
356  }
357  }
358 }
359 
360 CODE_SEG("INIT")
361 VOID
362 NTAPI
364 {
368 
371 
372  Timeout.QuadPart = -20000000; /* 2 sec */
374  Timeout,
375  2000, /* 2 sec */
376  NULL);
377 
380  NULL,
381  NULL,
384  NULL);
385  if (!NT_SUCCESS(Status))
386  {
387  KeBugCheck(MEMORY_MANAGEMENT);
388  }
389 
393  &Priority,
394  sizeof(Priority));
395 
396 }
397 
398 
399 /* EOF */
VOID NTAPI MmDereferencePage(PFN_NUMBER Page)
Definition: freelist.c:565
VOID NTAPI MmRebalanceMemoryConsumers(VOID)
Definition: balance.c:271
PFN_NUMBER Page
Definition: balance.c:23
VOID NTAPI MmInitializeMemoryConsumer(ULONG Consumer, NTSTATUS(*Trim)(ULONG Target, ULONG Priority, PULONG NrFreed))
Definition: balance.c:58
_Must_inspect_result_ typedef _In_ PVOID Unused
Definition: iotypes.h:1166
#define IN
Definition: typedefs.h:39
#define max(a, b)
Definition: svc.c:63
#define THREAD_ALL_ACCESS
Definition: nt_native.h:1339
union _MMPTE::@2285 u
KAPC_STATE
Definition: ketypes.h:1285
struct png_info_def **typedef void(__cdecl typeof(png_destroy_read_struct))(struct png_struct_def **
Definition: typeof.h:49
static ULONG MiMinimumPagesPerRun
Definition: balance.c:32
#define PsGetCurrentThread()
Definition: env_spec_w32.h:81
VOID NTAPI MiBalancerThread(PVOID Unused)
Definition: balance.c:305
NTKERNELAPI VOID FASTCALL ExReleaseRundownProtection(_Inout_ PEX_RUNDOWN_REF RunRef)
#define TRUE
Definition: types.h:120
#define MC_USER
Definition: mm.h:114
struct _MM_RMAP_ENTRY *NTAPI MmGetRmapListHeadPage(PFN_NUMBER Page)
Definition: freelist.c:458
ULONG PagesTarget
Definition: mm.h:458
LIST_ENTRY ListEntry
Definition: balance.c:24
LONG NTSTATUS
Definition: precomp.h:26
struct _MM_ALLOCATION_REQUEST MM_ALLOCATION_REQUEST
#define ExAcquireRundownProtection
Definition: ex.h:133
FORCEINLINE VOID MiLockProcessWorkingSet(IN PEPROCESS Process, IN PETHREAD Thread)
Definition: miarm.h:1129
#define LOW_REALTIME_PRIORITY
VOID NTAPI MmInitializeBalancer(ULONG NrAvailablePages, ULONG NrSystemPages)
Definition: balance.c:45
BOOLEAN NTAPI KeSetTimerEx(IN OUT PKTIMER Timer, IN LARGE_INTEGER DueTime, IN LONG Period, IN PKDPC Dpc OPTIONAL)
Definition: timerobj.c:294
PFN_NUMBER MmAvailablePages
Definition: freelist.c:26
LONG NTAPI KeSetEvent(IN PKEVENT Event, IN KPRIORITY Increment, IN BOOLEAN Wait)
Definition: eventobj.c:159
LONG KPRIORITY
Definition: compat.h:662
static KEVENT MiBalancerEvent
Definition: balance.c:35
static LONG PageOutThreadActive
Definition: balance.c:38
UCHAR KIRQL
Definition: env_spec_w32.h:591
#define STATUS_WAIT_1
Definition: ntstatus.h:71
ULONG * PPFN_NUMBER
Definition: ke.h:9
#define MiAddressToPte(x)
Definition: mmx86.c:19
#define STATUS_WAIT_0
Definition: ntstatus.h:237
#define MC_MAXIMUM
Definition: mm.h:116
ULONG PFN_NUMBER
Definition: ke.h:9
PFN_NUMBER NTAPI MmGetLRUNextUserPage(PFN_NUMBER PreviousPage, BOOLEAN MoveToLast)
Definition: freelist.c:125
NTSTATUS(* NTAPI)(IN PFILE_FULL_EA_INFORMATION EaBuffer, IN ULONG EaLength, OUT PULONG ErrorOffset)
Definition: IoEaTest.cpp:117
_In_ PVOID _Out_opt_ BOOLEAN _Out_opt_ PPFN_NUMBER Page
Definition: mm.h:1295
#define FALSE
Definition: types.h:117
VOID NTAPI KeStackAttachProcess(IN PKPROCESS Process, OUT PRKAPC_STATE ApcState)
Definition: procobj.c:704
DECLSPEC_NORETURN VOID NTAPI KeBugCheck(ULONG BugCheckCode)
Definition: bug.c:1427
long LONG
Definition: pedump.c:60
static CLIENT_ID MiBalancerThreadId
Definition: balance.c:33
unsigned char BOOLEAN
struct _MM_ALLOCATION_REQUEST * PMM_ALLOCATION_REQUEST
static WCHAR Address[46]
Definition: ping.c:68
NTSTATUS NTAPI MmPageOutPhysicalAddress(PFN_NUMBER Page)
Definition: rmap.c:51
NTSTATUS NTAPI NtSetInformationThread(IN HANDLE ThreadHandle, IN THREADINFOCLASS ThreadInformationClass, IN PVOID ThreadInformation, IN ULONG ThreadInformationLength)
Definition: query.c:2018
Status
Definition: gdiplustypes.h:24
ULONG NTAPI MiTrimMemoryConsumer(ULONG Consumer, ULONG InitialTarget)
Definition: balance.c:97
#define NT_SUCCESS(StatCode)
Definition: apphelp.c:32
NTSTATUS(* Trim)(ULONG Target, ULONG Priority, PULONG NrFreed)
Definition: mm.h:459
#define ObDereferenceObject
Definition: obfuncs.h:203
NTSTATUS NTAPI MmRequestPageMemoryConsumer(ULONG Consumer, BOOLEAN CanWait, PPFN_NUMBER AllocatedPage)
Definition: balance.c:281
#define RMAP_IS_SEGMENT(x)
Definition: mm.h:940
PFN_NUMBER NTAPI MmAllocPage(ULONG Consumer)
Definition: freelist.c:601
NTSTATUS NTAPI KeWaitForMultipleObjects(IN ULONG Count, IN PVOID Object[], IN WAIT_TYPE WaitType, IN KWAIT_REASON WaitReason, IN KPROCESSOR_MODE WaitMode, IN BOOLEAN Alertable, IN PLARGE_INTEGER Timeout OPTIONAL, OUT PKWAIT_BLOCK WaitBlockArray OPTIONAL)
Definition: wait.c:586
BOOLEAN NTAPI MmIsAddressValid(IN PVOID VirtualAddress)
Definition: mmsup.c:174
_In_ WDFINTERRUPT _In_ WDF_INTERRUPT_POLICY _In_ WDF_INTERRUPT_PRIORITY Priority
Definition: wdfinterrupt.h:651
#define InterlockedDecrement
Definition: armddk.h:52
_Requires_lock_held_ Interrupt _Releases_lock_ Interrupt _In_ _IRQL_restores_ KIRQL OldIrql
Definition: kefuncs.h:792
MM_MEMORY_CONSUMER MiMemoryConsumers[MC_MAXIMUM]
Definition: balance.c:30
#define InterlockedDecrementUL(Addend)
Definition: ex.h:1522
Definition: typedefs.h:119
VOID UpdateTotalCommittedPages(LONG Delta)
Definition: mm.h:871
PFN_NUMBER NTAPI MmGetLRUFirstUserPage(VOID)
Definition: freelist.c:45
NTSTATUS NTAPI MmReleasePageMemoryConsumer(ULONG Consumer, PFN_NUMBER Page)
Definition: balance.c:73
MMPTE_HARDWARE Hard
Definition: mmtypes.h:217
static ULONG Timeout
Definition: ping.c:61
#define KeInitializeEvent(pEvt, foo, foo2)
Definition: env_spec_w32.h:477
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint i
Definition: glfuncs.h:248
VOID NTAPI KeUnstackDetachProcess(IN PRKAPC_STATE ApcState)
Definition: procobj.c:756
#define InterlockedIncrementUL(Addend)
Definition: ex.h:1525
NTSTATUS NTAPI PsCreateSystemThread(OUT PHANDLE ThreadHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN HANDLE ProcessHandle, IN PCLIENT_ID ClientId, IN PKSTART_ROUTINE StartRoutine, IN PVOID StartContext)
Definition: thread.c:602
_Out_ PKAPC_STATE ApcState
Definition: mm.h:1727
unsigned int * PULONG
Definition: retypes.h:1
#define NULL
Definition: types.h:112
_In_ WDFIOTARGET Target
Definition: wdfrequest.h:306
#define DPRINT1
Definition: precomp.h:8
VOID NTAPI MiZeroPhysicalPage(IN PFN_NUMBER PageFrameIndex)
Definition: pfnlist.c:122
_Must_inspect_result_ _In_ PLARGE_INTEGER _In_ PLARGE_INTEGER _In_ ULONG _In_ PFILE_OBJECT _In_ PVOID Process
Definition: fsrtlfuncs.h:219
VOID NTAPI MiInitBalancerThread(VOID)
Definition: balance.c:363
#define ObReferenceObject
Definition: obfuncs.h:204
unsigned int ULONG
Definition: retypes.h:1
#define IO_NO_INCREMENT
Definition: iotypes.h:598
Definition: mm.h:265
static HANDLE MiBalancerThreadHandle
Definition: balance.c:34
#define STATUS_SUCCESS
Definition: shellext.h:65
#define DPRINT
Definition: sndvol32.h:71
static ULONG MiMinimumAvailablePages
Definition: balance.c:31
#define memset(x, y, z)
Definition: compat.h:39
static CODE_SEG("PAGE")
Definition: isapnp.c:1482
NTSTATUS MmTrimUserMemory(ULONG Target, ULONG Priority, PULONG NrFreedPages)
Definition: balance.c:139
ULONG64 Accessed
Definition: mmtypes.h:163
base of all file and directory entries
Definition: entries.h:82
VOID NTAPI KeInitializeTimerEx(OUT PKTIMER Timer, IN TIMER_TYPE Type)
Definition: timerobj.c:244
FORCEINLINE VOID MiUnlockProcessWorkingSet(IN PEPROCESS Process, IN PETHREAD Thread)
Definition: miarm.h:1199
static KTIMER MiBalancerTimer
Definition: balance.c:36