ReactOS  0.4.15-dev-3440-g915569a
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);
84 
85  OldIrql = MiAcquirePfnLock();
86 
88 
89  MiReleasePfnLock(OldIrql);
90 
91  return(STATUS_SUCCESS);
92 }
93 
94 ULONG
95 NTAPI
96 MiTrimMemoryConsumer(ULONG Consumer, ULONG InitialTarget)
97 {
98  ULONG Target = InitialTarget;
99  ULONG NrFreedPages = 0;
101 
102  /* Make sure we can trim this consumer */
103  if (!MiMemoryConsumers[Consumer].Trim)
104  {
105  /* Return the unmodified initial target */
106  return InitialTarget;
107  }
108 
110  {
111  /* Global page limit exceeded */
113  }
114  else if (MiMemoryConsumers[Consumer].PagesUsed > MiMemoryConsumers[Consumer].PagesTarget)
115  {
116  /* Consumer page limit exceeded */
117  Target = max(Target, MiMemoryConsumers[Consumer].PagesUsed - MiMemoryConsumers[Consumer].PagesTarget);
118  }
119 
120  if (Target)
121  {
122  /* Now swap the pages out */
124 
125  DPRINT("Trimming consumer %lu: Freed %lu pages with a target of %lu pages\n", Consumer, NrFreedPages, Target);
126 
127  if (!NT_SUCCESS(Status))
128  {
129  KeBugCheck(MEMORY_MANAGEMENT);
130  }
131  }
132 
133  /* Return the page count needed to be freed to meet the initial target */
134  return (InitialTarget > NrFreedPages) ? (InitialTarget - NrFreedPages) : 0;
135 }
136 
137 NTSTATUS
139 {
140  PFN_NUMBER CurrentPage;
142 
143  (*NrFreedPages) = 0;
144 
145  DPRINT1("MM BALANCER: %s\n", Priority ? "Paging out!" : "Removing access bit!");
146 
147  CurrentPage = MmGetLRUFirstUserPage();
148  while (CurrentPage != 0 && Target > 0)
149  {
150  if (Priority)
151  {
152  Status = MmPageOutPhysicalAddress(CurrentPage);
153  if (NT_SUCCESS(Status))
154  {
155  DPRINT("Succeeded\n");
156  Target--;
157  (*NrFreedPages)++;
158  }
159  }
160  else
161  {
162  /* When not paging-out agressively, just reset the accessed bit */
164  PVOID Address = NULL;
165  BOOLEAN Accessed = FALSE;
166 
167  /*
168  * We have a lock-ordering problem here. We cant lock the PFN DB before the Process address space.
169  * So we must use circonvoluted loops.
170  * Well...
171  */
172  while (TRUE)
173  {
175  KIRQL OldIrql = MiAcquirePfnLock();
177  while (Entry)
178  {
179  if (RMAP_IS_SEGMENT(Entry->Address))
180  {
181  Entry = Entry->Next;
182  continue;
183  }
184 
185  /* Check that we didn't treat this entry before */
186  if (Entry->Address < Address)
187  {
188  Entry = Entry->Next;
189  continue;
190  }
191 
192  if ((Entry->Address == Address) && (Entry->Process <= Process))
193  {
194  Entry = Entry->Next;
195  continue;
196  }
197 
198  break;
199  }
200 
201  if (!Entry)
202  {
203  MiReleasePfnLock(OldIrql);
204  break;
205  }
206 
207  Process = Entry->Process;
208  Address = Entry->Address;
209 
211 
212  if (!ExAcquireRundownProtection(&Process->RundownProtect))
213  {
215  MiReleasePfnLock(OldIrql);
216  continue;
217  }
218 
219  MiReleasePfnLock(OldIrql);
220 
223 
224  /* Be sure this is still valid. */
226  {
228  Accessed = Accessed || Pte->u.Hard.Accessed;
229  Pte->u.Hard.Accessed = 0;
230 
231  /* There is no need to invalidate, the balancer thread is never on a user process */
232  //KeInvalidateTlbEntry(Address);
233  }
234 
236 
238  ExReleaseRundownProtection(&Process->RundownProtect);
240  }
241 
242  if (!Accessed)
243  {
244  /* Nobody accessed this page since the last time we check. Time to clean up */
245 
246  Status = MmPageOutPhysicalAddress(CurrentPage);
247  // DPRINT1("Paged-out one page: %s\n", NT_SUCCESS(Status) ? "Yes" : "No");
248  (void)Status;
249  }
250 
251  /* Done for this page. */
252  Target--;
253  }
254 
255  CurrentPage = MmGetLRUNextUserPage(CurrentPage, TRUE);
256  }
257 
258  if (CurrentPage)
259  {
260  KIRQL OldIrql = MiAcquirePfnLock();
261  MmDereferencePage(CurrentPage);
262  MiReleasePfnLock(OldIrql);
263  }
264 
265  return STATUS_SUCCESS;
266 }
267 
268 VOID
269 NTAPI
271 {
272  // if (InterlockedCompareExchange(&PageOutThreadActive, 0, 1) == 0)
273  {
275  }
276 }
277 
278 NTSTATUS
279 NTAPI
281  PPFN_NUMBER AllocatedPage)
282 {
284 
285  /* Update the target */
286  InterlockedIncrementUL(&MiMemoryConsumers[Consumer].PagesUsed);
287 
288  /*
289  * Actually allocate the page.
290  */
291  Page = MmAllocPage(Consumer);
292  if (Page == 0)
293  {
294  KeBugCheck(NO_PAGES_AVAILABLE);
295  }
296  *AllocatedPage = Page;
297 
298  return(STATUS_SUCCESS);
299 }
300 
301 
302 VOID NTAPI
304 {
305  PVOID WaitObjects[2];
307  ULONG i;
308 
309  WaitObjects[0] = &MiBalancerEvent;
310  WaitObjects[1] = &MiBalancerTimer;
311 
312  while (1)
313  {
315  WaitObjects,
316  WaitAny,
317  Executive,
318  KernelMode,
319  FALSE,
320  NULL,
321  NULL);
322 
324  {
325  ULONG InitialTarget = 0;
326 
327  do
328  {
329  ULONG OldTarget = InitialTarget;
330 
331  /* Trim each consumer */
332  for (i = 0; i < MC_MAXIMUM; i++)
333  {
334  InitialTarget = MiTrimMemoryConsumer(i, InitialTarget);
335  }
336 
337  /* No pages left to swap! */
338  if (InitialTarget != 0 &&
339  InitialTarget == OldTarget)
340  {
341  /* Game over */
342  KeBugCheck(NO_PAGES_AVAILABLE);
343  }
344  }
345  while (InitialTarget != 0);
346 
347  if (Status == STATUS_WAIT_0)
349  }
350  else
351  {
352  DPRINT1("KeWaitForMultipleObjects failed, status = %x\n", Status);
353  KeBugCheck(MEMORY_MANAGEMENT);
354  }
355  }
356 }
357 
358 CODE_SEG("INIT")
359 VOID
360 NTAPI
362 {
366 
369 
370  Timeout.QuadPart = -20000000; /* 2 sec */
372  Timeout,
373  2000, /* 2 sec */
374  NULL);
375 
378  NULL,
379  NULL,
382  NULL);
383  if (!NT_SUCCESS(Status))
384  {
385  KeBugCheck(MEMORY_MANAGEMENT);
386  }
387 
391  &Priority,
392  sizeof(Priority));
393 
394 }
395 
396 
397 /* EOF */
VOID NTAPI MmDereferencePage(PFN_NUMBER Page)
Definition: freelist.c:565
VOID NTAPI MmRebalanceMemoryConsumers(VOID)
Definition: balance.c:270
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
KAPC_STATE
Definition: ketypes.h:1280
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:303
NTKERNELAPI VOID FASTCALL ExReleaseRundownProtection(_Inout_ PEX_RUNDOWN_REF RunRef)
#define TRUE
Definition: types.h:120
#define MC_USER
Definition: mm.h:103
struct _MM_RMAP_ENTRY *NTAPI MmGetRmapListHeadPage(PFN_NUMBER Page)
Definition: freelist.c:458
ULONG PagesTarget
Definition: mm.h:447
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:105
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
#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
PFN_NUMBER Page
Definition: section.c:4924
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:96
#define NT_SUCCESS(StatCode)
Definition: apphelp.c:32
NTSTATUS(* Trim)(ULONG Target, ULONG Priority, PULONG NrFreed)
Definition: mm.h:448
#define ObDereferenceObject
Definition: obfuncs.h:203
NTSTATUS NTAPI MmRequestPageMemoryConsumer(ULONG Consumer, BOOLEAN CanWait, PPFN_NUMBER AllocatedPage)
Definition: balance.c:280
#define RMAP_IS_SEGMENT(x)
Definition: mm.h:893
union _MMPTE::@2275 u
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:790
MM_MEMORY_CONSUMER MiMemoryConsumers[MC_MAXIMUM]
Definition: balance.c:30
#define InterlockedDecrementUL(Addend)
Definition: ex.h:1522
Definition: typedefs.h:119
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:1680
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:361
#define ObReferenceObject
Definition: obfuncs.h:204
unsigned int ULONG
Definition: retypes.h:1
#define IO_NO_INCREMENT
Definition: iotypes.h:598
Definition: mm.h:254
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:138
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