ReactOS  0.4.15-dev-2996-gf777e6b
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 
35 
40 
42 
43 /* FUNCTIONS ****************************************************************/
44 
45 CODE_SEG("INIT")
46 VOID
47 NTAPI
48 MmInitializeBalancer(ULONG NrAvailablePages, ULONG NrSystemPages)
49 {
53 
54  /* Set up targets. */
57  MiMemoryConsumers[MC_USER].PagesTarget = NrAvailablePages / 2;
58 }
59 
60 CODE_SEG("INIT")
61 VOID
62 NTAPI
64  ULONG Consumer,
65  NTSTATUS (*Trim)(ULONG Target, ULONG Priority, PULONG NrFreed))
66 {
67  MiMemoryConsumers[Consumer].Trim = Trim;
68 }
69 
70 VOID
71 NTAPI
73  IN PFN_NUMBER PageFrameIndex
74 );
75 
77 NTAPI
79 {
80  KIRQL OldIrql;
81 
82  if (Page == 0)
83  {
84  DPRINT1("Tried to release page zero.\n");
85  KeBugCheck(MEMORY_MANAGEMENT);
86  }
87 
88  (void)InterlockedDecrementUL(&MiMemoryConsumers[Consumer].PagesUsed);
89 
90  OldIrql = MiAcquirePfnLock();
91 
93 
94  MiReleasePfnLock(OldIrql);
95 
96  return(STATUS_SUCCESS);
97 }
98 
99 ULONG
100 NTAPI
101 MiTrimMemoryConsumer(ULONG Consumer, ULONG InitialTarget)
102 {
103  ULONG Target = InitialTarget;
104  ULONG NrFreedPages = 0;
106 
107  /* Make sure we can trim this consumer */
108  if (!MiMemoryConsumers[Consumer].Trim)
109  {
110  /* Return the unmodified initial target */
111  return InitialTarget;
112  }
113 
115  {
116  /* Global page limit exceeded */
118  }
119  else if (MiMemoryConsumers[Consumer].PagesUsed > MiMemoryConsumers[Consumer].PagesTarget)
120  {
121  /* Consumer page limit exceeded */
122  Target = max(Target, MiMemoryConsumers[Consumer].PagesUsed - MiMemoryConsumers[Consumer].PagesTarget);
123  }
124 
125  if (Target)
126  {
127  /* Now swap the pages out */
129 
130  DPRINT("Trimming consumer %lu: Freed %lu pages with a target of %lu pages\n", Consumer, NrFreedPages, Target);
131 
132  if (!NT_SUCCESS(Status))
133  {
134  KeBugCheck(MEMORY_MANAGEMENT);
135  }
136  }
137 
138  /* Return the page count needed to be freed to meet the initial target */
139  return (InitialTarget > NrFreedPages) ? (InitialTarget - NrFreedPages) : 0;
140 }
141 
142 NTSTATUS
144 {
145  PFN_NUMBER CurrentPage;
147 
148  (*NrFreedPages) = 0;
149 
150  DPRINT1("MM BALANCER: %s\n", Priority ? "Paging out!" : "Removing access bit!");
151 
152  CurrentPage = MmGetLRUFirstUserPage();
153  while (CurrentPage != 0 && Target > 0)
154  {
155  if (Priority)
156  {
157  Status = MmPageOutPhysicalAddress(CurrentPage);
158  if (NT_SUCCESS(Status))
159  {
160  DPRINT("Succeeded\n");
161  Target--;
162  (*NrFreedPages)++;
163  }
164  }
165  else
166  {
167  /* When not paging-out agressively, just reset the accessed bit */
169  PVOID Address = NULL;
170  BOOLEAN Accessed = FALSE;
171 
172  /*
173  * We have a lock-ordering problem here. We cant lock the PFN DB before the Process address space.
174  * So we must use circonvoluted loops.
175  * Well...
176  */
177  while (TRUE)
178  {
180  KIRQL OldIrql = MiAcquirePfnLock();
182  while (Entry)
183  {
184  if (RMAP_IS_SEGMENT(Entry->Address))
185  {
186  Entry = Entry->Next;
187  continue;
188  }
189 
190  /* Check that we didn't treat this entry before */
191  if (Entry->Address < Address)
192  {
193  Entry = Entry->Next;
194  continue;
195  }
196 
197  if ((Entry->Address == Address) && (Entry->Process <= Process))
198  {
199  Entry = Entry->Next;
200  continue;
201  }
202 
203  break;
204  }
205 
206  if (!Entry)
207  {
208  MiReleasePfnLock(OldIrql);
209  break;
210  }
211 
212  Process = Entry->Process;
213  Address = Entry->Address;
214 
215  MiReleasePfnLock(OldIrql);
216 
218 
220 
221  /* Be sure this is still valid. */
223  if (Pte->u.Hard.Valid)
224  {
225  Accessed = Accessed || Pte->u.Hard.Accessed;
226  Pte->u.Hard.Accessed = 0;
227 
228  /* There is no need to invalidate, the balancer thread is never on a user process */
229  //KeInvalidateTlbEntry(Address);
230  }
231 
233 
235  }
236 
237  if (!Accessed)
238  {
239  /* Nobody accessed this page since the last time we check. Time to clean up */
240 
241  Status = MmPageOutPhysicalAddress(CurrentPage);
242  // DPRINT1("Paged-out one page: %s\n", NT_SUCCESS(Status) ? "Yes" : "No");
243  (void)Status;
244  }
245 
246  /* Done for this page. */
247  Target--;
248  }
249 
250  CurrentPage = MmGetLRUNextUserPage(CurrentPage, TRUE);
251  }
252 
253  if (CurrentPage)
254  {
255  KIRQL OldIrql = MiAcquirePfnLock();
256  MmDereferencePage(CurrentPage);
257  MiReleasePfnLock(OldIrql);
258  }
259 
260  return STATUS_SUCCESS;
261 }
262 
263 VOID
264 NTAPI
266 {
267  // if (InterlockedCompareExchange(&PageOutThreadActive, 0, 1) == 0)
268  {
270  }
271 }
272 
273 NTSTATUS
274 NTAPI
276  PPFN_NUMBER AllocatedPage)
277 {
279 
280  /* Update the target */
281  InterlockedIncrementUL(&MiMemoryConsumers[Consumer].PagesUsed);
282 
283  /*
284  * Actually allocate the page.
285  */
286  Page = MmAllocPage(Consumer);
287  if (Page == 0)
288  {
289  KeBugCheck(NO_PAGES_AVAILABLE);
290  }
291  *AllocatedPage = Page;
292 
293  return(STATUS_SUCCESS);
294 }
295 
296 
297 VOID NTAPI
299 {
300  PVOID WaitObjects[2];
302  ULONG i;
303 
304  WaitObjects[0] = &MiBalancerEvent;
305  WaitObjects[1] = &MiBalancerTimer;
306 
307  while (1)
308  {
310  WaitObjects,
311  WaitAny,
312  Executive,
313  KernelMode,
314  FALSE,
315  NULL,
316  NULL);
317 
319  {
320  ULONG InitialTarget = 0;
321 
322  do
323  {
324  ULONG OldTarget = InitialTarget;
325 
326  /* Trim each consumer */
327  for (i = 0; i < MC_MAXIMUM; i++)
328  {
329  InitialTarget = MiTrimMemoryConsumer(i, InitialTarget);
330  }
331 
332  /* No pages left to swap! */
333  if (InitialTarget != 0 &&
334  InitialTarget == OldTarget)
335  {
336  /* Game over */
337  KeBugCheck(NO_PAGES_AVAILABLE);
338  }
339  }
340  while (InitialTarget != 0);
341 
342  if (Status == STATUS_WAIT_0)
344  }
345  else
346  {
347  DPRINT1("KeWaitForMultipleObjects failed, status = %x\n", Status);
348  KeBugCheck(MEMORY_MANAGEMENT);
349  }
350  }
351 }
352 
354 {
357  PMMPFN Pfn1;
358 
359  /* Make sure the PFN lock is held */
361 
363  {
364  /* Dirty way to know if we were initialized. */
365  return FALSE;
366  }
367 
369  if (!Entry)
370  return FALSE;
371 
374  Request->Page = Page;
375 
376  Pfn1 = MiGetPfnEntry(Page);
377  ASSERT(Pfn1->u3.e2.ReferenceCount == 0);
378  Pfn1->u3.e2.ReferenceCount = 1;
380 
381  /* This marks the PFN as a ReactOS PFN */
382  Pfn1->u4.AweAllocation = TRUE;
383 
384  /* Allocate the extra ReactOS Data and zero it out */
385  Pfn1->u1.SwapEntry = 0;
386  Pfn1->RmapListHead = NULL;
387 
389 
390  return TRUE;
391 }
392 
393 CODE_SEG("INIT")
394 VOID
395 NTAPI
397 {
401 
404 
405  Timeout.QuadPart = -20000000; /* 2 sec */
407  Timeout,
408  2000, /* 2 sec */
409  NULL);
410 
413  NULL,
414  NULL,
417  NULL);
418  if (!NT_SUCCESS(Status))
419  {
420  KeBugCheck(MEMORY_MANAGEMENT);
421  }
422 
426  &Priority,
427  sizeof(Priority));
428 
429 }
430 
431 
432 /* EOF */
VOID NTAPI MmDereferencePage(PFN_NUMBER Page)
Definition: freelist.c:565
VOID NTAPI MmRebalanceMemoryConsumers(VOID)
Definition: balance.c:265
PFN_NUMBER Page
Definition: balance.c:23
static LIST_ENTRY AllocationListHead
Definition: balance.c:32
VOID NTAPI MmInitializeMemoryConsumer(ULONG Consumer, NTSTATUS(*Trim)(ULONG Target, ULONG Priority, PULONG NrFreed))
Definition: balance.c:63
_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
#define MI_ASSERT_PFN_LOCK_HELD()
Definition: mm.h:996
static ULONG MiMinimumPagesPerRun
Definition: balance.c:34
struct _Entry Entry
Definition: kefuncs.h:627
VOID NTAPI MiBalancerThread(PVOID Unused)
Definition: balance.c:298
#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_PTR AweAllocation
Definition: mm.h:410
ULONG PagesTarget
Definition: mm.h:447
LIST_ENTRY ListEntry
Definition: balance.c:24
FORCEINLINE VOID MmUnlockAddressSpace(PMMSUPPORT AddressSpace)
Definition: mm.h:1618
PMM_RMAP_ENTRY RmapListHead
Definition: mm.h:400
LONG NTSTATUS
Definition: precomp.h:26
BOOLEAN MmRosNotifyAvailablePage(PFN_NUMBER Page)
Definition: balance.c:353
struct _MM_ALLOCATION_REQUEST MM_ALLOCATION_REQUEST
union _MMPFN::@1761 u3
#define LOW_REALTIME_PRIORITY
_In_ WDFREQUEST Request
Definition: wdfdevice.h:547
VOID NTAPI MmInitializeBalancer(ULONG NrAvailablePages, ULONG NrSystemPages)
Definition: balance.c:48
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
USHORT PageLocation
Definition: mm.h:354
static KEVENT MiBalancerEvent
Definition: balance.c:38
static LONG PageOutThreadActive
Definition: balance.c:41
UCHAR KIRQL
Definition: env_spec_w32.h:591
MMPFNENTRY e1
Definition: mm.h:386
#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:36
FORCEINLINE VOID KeInitializeSpinLock(_Out_ PKSPIN_LOCK SpinLock)
Definition: kefuncs.h:238
PLIST_ENTRY NTAPI ExInterlockedRemoveHeadList(IN OUT PLIST_ENTRY ListHead, IN OUT PKSPIN_LOCK Lock)
Definition: interlocked.c:166
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:4888
NTSTATUS NTAPI NtSetInformationThread(IN HANDLE ThreadHandle, IN THREADINFOCLASS ThreadInformationClass, IN PVOID ThreadInformation, IN ULONG ThreadInformationLength)
Definition: query.c:2018
PFLT_MESSAGE_WAITER_QUEUE CONTAINING_RECORD(Csq, DEVICE_EXTENSION, IrpQueue)) -> WaiterQ.mLock) _IRQL_raises_(DISPATCH_LEVEL) VOID NTAPI FltpAcquireMessageWaiterLock(_In_ PIO_CSQ Csq, _Out_ PKIRQL Irql)
Definition: Messaging.c:560
KIRQL OldIrql
Definition: mm.h:1502
Status
Definition: gdiplustypes.h:24
ULONG NTAPI MiTrimMemoryConsumer(ULONG Consumer, ULONG InitialTarget)
Definition: balance.c:101
#define ASSERT(a)
Definition: mode.c:44
#define NT_SUCCESS(StatCode)
Definition: apphelp.c:32
NTSTATUS(* Trim)(ULONG Target, ULONG Priority, PULONG NrFreed)
Definition: mm.h:448
ULONG64 Valid
Definition: mmtypes.h:150
NTSTATUS NTAPI MmRequestPageMemoryConsumer(ULONG Consumer, BOOLEAN CanWait, PPFN_NUMBER AllocatedPage)
Definition: balance.c:275
#define RMAP_IS_SEGMENT(x)
Definition: mm.h:893
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
_In_ WDFINTERRUPT _In_ WDF_INTERRUPT_POLICY _In_ WDF_INTERRUPT_PRIORITY Priority
Definition: wdfinterrupt.h:651
static KSPIN_LOCK AllocationListLock
Definition: balance.c:33
#define InterlockedDecrement
Definition: armddk.h:52
MM_MEMORY_CONSUMER MiMemoryConsumers[MC_MAXIMUM]
Definition: balance.c:30
Definition: mm.h:362
#define InterlockedDecrementUL(Addend)
Definition: ex.h:1522
Definition: typedefs.h:119
PFN_NUMBER NTAPI MmGetLRUFirstUserPage(VOID)
Definition: freelist.c:45
FORCEINLINE PMMPFN MiGetPfnEntry(IN PFN_NUMBER Pfn)
Definition: mm.h:1000
NTSTATUS NTAPI MmReleasePageMemoryConsumer(ULONG Consumer, PFN_NUMBER Page)
Definition: balance.c:78
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 InitializeListHead(ListHead)
Definition: env_spec_w32.h:944
#define InterlockedIncrementUL(Addend)
Definition: ex.h:1525
ULONG KSPIN_LOCK
Definition: env_spec_w32.h:72
struct _MMPFN::@1761::@1767 e2
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:1679
unsigned int * PULONG
Definition: retypes.h:1
#define NULL
Definition: types.h:112
SWAPENTRY SwapEntry
Definition: mm.h:373
_In_ WDFIOTARGET Target
Definition: wdfrequest.h:306
#define DPRINT1
Definition: precomp.h:8
VOID NTAPI MiZeroPhysicalPage(IN PFN_NUMBER PageFrameIndex)
Definition: pfnlist.c:122
union _MMPFN::@1759 u1
_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:396
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:37
#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
union _MMPTE::@2287 u
static CODE_SEG("PAGE")
Definition: isapnp.c:1482
NTSTATUS MmTrimUserMemory(ULONG Target, ULONG Priority, PULONG NrFreedPages)
Definition: balance.c:143
FORCEINLINE VOID MmLockAddressSpace(PMMSUPPORT AddressSpace)
Definition: mm.h:1611
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
static KTIMER MiBalancerTimer
Definition: balance.c:39
union _MMPFN::@1764 u4