ReactOS  0.4.15-dev-3207-ga415bd4
rmap.c
Go to the documentation of this file.
1 /*
2  * COPYRIGHT: See COPYING in the top directory
3  * PROJECT: ReactOS kernel
4  * FILE: ntoskrnl/mm/rmap.c
5  * PURPOSE: Kernel memory managment functions
6  *
7  * PROGRAMMERS: David Welch (welch@cwcom.net)
8  */
9 
10 /* INCLUDES *****************************************************************/
11 
12 #include <ntoskrnl.h>
13 #include <cache/section/newmm.h>
14 #define NDEBUG
15 #include <debug.h>
16 
17 /* TYPES ********************************************************************/
18 
19 /* GLOBALS ******************************************************************/
20 
22 
23 /* FUNCTIONS ****************************************************************/
24 
26 static
27 VOID
28 NTAPI
29 RmapListFree(
31 {
33 }
34 
35 CODE_SEG("INIT")
36 VOID
37 NTAPI
39 {
41  NULL,
42  RmapListFree,
43  0,
44  sizeof(MM_RMAP_ENTRY),
45  TAG_RMAP,
46  50);
47 }
48 
50 NTAPI
52 {
56  PVOID Address = NULL;
60  LARGE_INTEGER SegmentOffset;
61  KIRQL OldIrql;
62 
63 GetEntry:
64  OldIrql = MiAcquirePfnLock();
65 
67 
68  while (entry && RMAP_IS_SEGMENT(entry->Address))
69  entry = entry->Next;
70 
71  if (entry == NULL)
72  {
73  MiReleasePfnLock(OldIrql);
74  goto WriteSegment;
75  }
76 
77  Process = entry->Process;
78  Address = entry->Address;
79 
80  if ((((ULONG_PTR)Address) & 0xFFF) != 0)
81  {
82  KeBugCheck(MEMORY_MANAGEMENT);
83  }
84 
85  /* This is for user-mode address only */
87 
88  if (!ExAcquireRundownProtection(&Process->RundownProtect))
89  {
90  MiReleasePfnLock(OldIrql);
92  }
93 
95  MiReleasePfnLock(OldIrql);
96  if (!NT_SUCCESS(Status))
97  {
98  ExReleaseRundownProtection(&Process->RundownProtect);
99  return Status;
100  }
101  AddressSpace = &Process->Vm;
102 
104 
107  {
109  ExReleaseRundownProtection(&Process->RundownProtect);
111  goto GetEntry;
112  }
113 
114 
115  /* Attach to it, if needed */
118  KeAttachProcess(&Process->Pcb);
119 
121  {
122  /* This changed in the short window where we didn't have any locks */
124  KeDetachProcess();
126  ExReleaseRundownProtection(&Process->RundownProtect);
128  goto GetEntry;
129  }
130 
132  {
134  BOOLEAN Dirty;
135  PFN_NUMBER MapPage;
137  BOOLEAN Released;
138 
139  Offset.QuadPart = MemoryArea->SectionData.ViewOffset +
141 
142  Segment = MemoryArea->SectionData.Segment;
143 
145 
147  if (Entry && MM_IS_WAIT_PTE(Entry))
148  {
149  /* The segment is being read or something. Give up */
152  KeDetachProcess();
154  ExReleaseRundownProtection(&Process->RundownProtect);
156  return(STATUS_UNSUCCESSFUL);
157  }
158 
159  /* Delete this virtual mapping in the process */
161  MmDeleteVirtualMapping(Process, Address, &Dirty, &MapPage);
162 
163  /* We checked this earlier */
164  ASSERT(MapPage == Page);
165 
166  if (Page != PFN_FROM_SSE(Entry))
167  {
168  SWAPENTRY SwapEntry;
169 
170  /* This page is private to the process */
172 
173  /* Check if we should write it back to the page file */
174  SwapEntry = MmGetSavedSwapEntryPage(Page);
175 
176  if ((SwapEntry == 0) && Dirty)
177  {
178  /* We don't have a Swap entry, yet the page is dirty. Get one */
179  SwapEntry = MmAllocSwapPage();
180  if (!SwapEntry)
181  {
183  &MemoryArea->SectionData.RegionListHead,
184  Address, NULL);
185 
186  /* We can't, so let this page in the Process VM */
190 
193  KeDetachProcess();
194  ExReleaseRundownProtection(&Process->RundownProtect);
196 
197  return STATUS_UNSUCCESSFUL;
198  }
199  }
200 
201  if (Dirty)
202  {
203  SWAPENTRY Dummy;
204 
205  /* Put a wait entry into the process and unlock */
206  MmCreatePageFileMapping(Process, Address, MM_WAIT_ENTRY);
208 
209  Status = MmWriteToSwapPage(SwapEntry, Page);
210 
213  ASSERT(Dummy == MM_WAIT_ENTRY);
214 
215  if (!NT_SUCCESS(Status))
216  {
217  /* We failed at saving the content of this page. Keep it in */
219  &MemoryArea->SectionData.RegionListHead,
220  Address, NULL);
221 
222  /* This Swap Entry is useless to us */
224  MmFreeSwapPage(SwapEntry);
225 
226  /* We can't, so let this page in the Process VM */
230 
233  KeDetachProcess();
234  ExReleaseRundownProtection(&Process->RundownProtect);
236 
237  return STATUS_UNSUCCESSFUL;
238  }
239  }
240 
241  if (SwapEntry)
242  {
243  /* Keep this in the process VM */
246  }
247 
248  /* We can finally let this page go */
251  KeDetachProcess();
252 #if DBG
253  OldIrql = MiAcquirePfnLock();
255  MiReleasePfnLock(OldIrql);
256 #endif
258 
259  ExReleaseRundownProtection(&Process->RundownProtect);
261 
262  return STATUS_SUCCESS;
263  }
264 
265  /* One less mapping referencing this segment */
267 
270  KeDetachProcess();
272 
273  ExReleaseRundownProtection(&Process->RundownProtect);
275 
276  if (Released) return STATUS_SUCCESS;
277  }
278 #ifdef NEWCC
279  else if (Type == MEMORY_AREA_CACHE)
280  {
281  /* NEWCC does locking itself */
284  }
285 #endif
286  else
287  {
288  KeBugCheck(MEMORY_MANAGEMENT);
289  }
290 
291 WriteSegment:
292  /* Now write this page to file, if needed */
293  Segment = MmGetSectionAssociation(Page, &SegmentOffset);
294  if (Segment)
295  {
296  BOOLEAN Released;
297 
299 
300  Released = MmCheckDirtySegment(Segment, &SegmentOffset, FALSE, TRUE);
301 
303  MmDereferenceSegment(Segment);
304 
305  if (Released)
306  {
307  return STATUS_SUCCESS;
308  }
309  }
310 
311  /* If we are here, then we didn't release the page */
312  return STATUS_UNSUCCESSFUL;
313 }
314 
315 VOID
316 NTAPI
318  PVOID Address)
319 {
320  PMM_RMAP_ENTRY current_entry;
321  PMM_RMAP_ENTRY new_entry;
322  ULONG PrevSize;
323  KIRQL OldIrql;
324 
325  if (!RMAP_IS_SEGMENT(Address))
327 
328  new_entry = ExAllocateFromNPagedLookasideList(&RmapLookasideList);
329  if (new_entry == NULL)
330  {
331  KeBugCheck(MEMORY_MANAGEMENT);
332  }
333  new_entry->Address = Address;
334  new_entry->Process = (PEPROCESS)Process;
335 #if DBG
336  new_entry->Caller = _ReturnAddress();
337 #endif
338 
339  if (
342  {
343  DPRINT1("Insert rmap (%d, 0x%.8X) 0x%.8X which doesn't match physical "
344  "address 0x%.8X\n", Process ? Process->UniqueProcessId : 0,
345  Address,
347  Page << PAGE_SHIFT);
348  KeBugCheck(MEMORY_MANAGEMENT);
349  }
350 
351  OldIrql = MiAcquirePfnLock();
352  current_entry = MmGetRmapListHeadPage(Page);
353 
354  PMM_RMAP_ENTRY previous_entry = NULL;
355  /* Keep the list sorted */
356  while (current_entry && (current_entry->Address < Address))
357  {
358  previous_entry = current_entry;
359  current_entry = current_entry->Next;
360  }
361 
362  /* In case of clash in the address, sort by process */
363  if (current_entry && (current_entry->Address == Address))
364  {
365  while (current_entry && (current_entry->Process < Process))
366  {
367  previous_entry = current_entry;
368  current_entry = current_entry->Next;
369  }
370  }
371 
372  if (current_entry && (current_entry->Address == Address) && (current_entry->Process == Process))
373  {
374 #if DBG
375  DbgPrint("MmInsertRmap tries to add a second rmap entry for address %p\n", current_entry->Address);
376  DbgPrint(" current caller %p\n", new_entry->Caller);
377  DbgPrint(" previous caller %p\n", current_entry->Caller);
378 #endif
379  KeBugCheck(MEMORY_MANAGEMENT);
380  }
381 
382  new_entry->Next = current_entry;
383  if (previous_entry)
384  previous_entry->Next = new_entry;
385  else
386  MmSetRmapListHeadPage(Page, new_entry);
387 
388  MiReleasePfnLock(OldIrql);
389 
390  if (!RMAP_IS_SEGMENT(Address))
391  {
392  ASSERT(Process != NULL);
393  PrevSize = InterlockedExchangeAddUL(&Process->Vm.WorkingSetSize, PAGE_SIZE);
394  if (PrevSize >= Process->Vm.PeakWorkingSetSize)
395  {
396  Process->Vm.PeakWorkingSetSize = PrevSize + PAGE_SIZE;
397  }
398  }
399 }
400 
401 VOID
402 NTAPI
404  PVOID Address)
405 {
406  PMM_RMAP_ENTRY current_entry, previous_entry;
407  KIRQL OldIrql;
408 
409  OldIrql = MiAcquirePfnLock();
410  previous_entry = NULL;
411  current_entry = MmGetRmapListHeadPage(Page);
412 
413  while (current_entry != NULL)
414  {
415  if (current_entry->Process == (PEPROCESS)Process &&
416  current_entry->Address == Address)
417  {
418  if (previous_entry == NULL)
419  {
420  MmSetRmapListHeadPage(Page, current_entry->Next);
421  }
422  else
423  {
424  previous_entry->Next = current_entry->Next;
425  }
426  MiReleasePfnLock(OldIrql);
427 
428  ExFreeToNPagedLookasideList(&RmapLookasideList, current_entry);
429  if (!RMAP_IS_SEGMENT(Address))
430  {
431  ASSERT(Process != NULL);
432  (void)InterlockedExchangeAddUL(&Process->Vm.WorkingSetSize, -PAGE_SIZE);
433  }
434  return;
435  }
436  previous_entry = current_entry;
437  current_entry = current_entry->Next;
438  }
439  KeBugCheck(MEMORY_MANAGEMENT);
440 }
441 
442 /*
443 
444 Return the process pointer given when a previous call to MmInsertRmap was
445 called with a process and address pointer that conform to the segment rmap
446 schema. In short, this requires the address part to be 0xffffff00 + n
447 where n is between 0 and 255. When such an rmap exists, it specifies a
448 segment rmap in which the process part is a pointer to a slice of a section
449 page table, and the low 8 bits of the address represent a page index in the
450 page table slice. Together, this information is used by
451 MmGetSectionAssociation to determine which page entry points to this page in
452 the segment page table.
453 
454 */
455 
456 PVOID
457 NTAPI
459 {
461  PMM_RMAP_ENTRY current_entry;//, previous_entry;
462  KIRQL OldIrql = MiAcquirePfnLock();
463 
464  //previous_entry = NULL;
465  current_entry = MmGetRmapListHeadPage(Page);
466  while (current_entry != NULL)
467  {
468  if (RMAP_IS_SEGMENT(current_entry->Address))
469  {
470  Result = (PCACHE_SECTION_PAGE_TABLE)current_entry->Process;
471  *RawOffset = (ULONG_PTR)current_entry->Address & ~RMAP_SEGMENT_MASK;
472  if (*Result->Segment->Flags & MM_SEGMENT_INDELETE)
473  {
474  MiReleasePfnLock(OldIrql);
475  return NULL;
476  }
477  MiReleasePfnLock(OldIrql);
478  return Result;
479  }
480  //previous_entry = current_entry;
481  current_entry = current_entry->Next;
482  }
483  MiReleasePfnLock(OldIrql);
484  return NULL;
485 }
486 
487 /*
488 
489 Remove the section rmap associated with the indicated page, if it exists.
490 
491 */
492 
493 VOID
494 NTAPI
496 {
497  PMM_RMAP_ENTRY current_entry, previous_entry;
498  KIRQL OldIrql = MiAcquirePfnLock();
499 
500  previous_entry = NULL;
501  current_entry = MmGetRmapListHeadPage(Page);
502  while (current_entry != NULL)
503  {
504  if (RMAP_IS_SEGMENT(current_entry->Address))
505  {
506  if (previous_entry == NULL)
507  {
508  MmSetRmapListHeadPage(Page, current_entry->Next);
509  }
510  else
511  {
512  previous_entry->Next = current_entry->Next;
513  }
514  MiReleasePfnLock(OldIrql);
515  ExFreeToNPagedLookasideList(&RmapLookasideList, current_entry);
516  return;
517  }
518  previous_entry = current_entry;
519  current_entry = current_entry->Next;
520  }
521  MiReleasePfnLock(OldIrql);
522 }
PMM_REGION NTAPI MmFindRegion(PVOID BaseAddress, PLIST_ENTRY RegionListHead, PVOID Address, PVOID *RegionBaseAddress)
Definition: region.c:257
#define PAGE_SHIFT
Definition: env_spec_w32.h:45
VOID NTAPI MmDeleteRmap(PFN_NUMBER Page, PEPROCESS Process, PVOID Address)
Definition: rmap.c:403
#define MmGetPageEntrySectionSegment(S, O)
Definition: mm.h:1538
PEPROCESS Process
Definition: mm.h:257
struct LOOKASIDE_ALIGN _NPAGED_LOOKASIDE_LIST NPAGED_LOOKASIDE_LIST
struct png_info_def **typedef void(__cdecl typeof(png_destroy_read_struct))(struct png_struct_def **
Definition: typeof.h:49
ULONG Type
Definition: mm.h:240
#define PROCESS_ALL_ACCESS
Definition: nt_native.h:1324
#define DbgPrint
Definition: hal.h:12
struct _Entry Entry
Definition: kefuncs.h:627
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
#define PFN_FROM_SSE(E)
Definition: mm.h:1304
_In_ ULONG _In_ PHYSICAL_ADDRESS _Inout_ PULONG AddressSpace
Definition: iofuncs.h:2272
FORCEINLINE VOID MmUnlockAddressSpace(PMMSUPPORT AddressSpace)
Definition: mm.h:1618
Definition: mm.h:451
LONG NTSTATUS
Definition: precomp.h:26
struct _EPROCESS * PEPROCESS
Definition: nt_native.h:30
#define ExAcquireRundownProtection
Definition: ex.h:133
NTSTATUS NTAPI MmReleasePageMemoryConsumer(ULONG Consumer, PFN_NUMBER Page)
Definition: balance.c:73
#define PAGE_ROUND_DOWN(x)
Definition: mmtypes.h:36
NTSTATUS NTAPI MmCreateVirtualMapping(struct _EPROCESS *Process, PVOID Address, ULONG flProtect, PFN_NUMBER Page)
NTSTATUS NTAPI MmCreatePageFileMapping(struct _EPROCESS *Process, PVOID Address, SWAPENTRY SwapEntry)
BOOLEAN NTAPI MmUnsharePageEntrySectionSegment(PMEMORY_AREA MemoryArea, PMM_SECTION_SEGMENT Segment, PLARGE_INTEGER Offset, BOOLEAN Dirty, BOOLEAN PageOut, ULONG_PTR *InEntry)
Definition: section.c:1090
struct _CACHE_SECTION_PAGE_TABLE * PCACHE_SECTION_PAGE_TABLE
VOID NTAPI MmDeleteSectionAssociation(PFN_NUMBER Page)
Definition: rmap.c:495
VOID NTAPI MmDeletePageFileMapping(struct _EPROCESS *Process, PVOID Address, SWAPENTRY *SwapEntry)
BOOLEAN NTAPI MmCheckDirtySegment(PMM_SECTION_SEGMENT Segment, PLARGE_INTEGER Offset, BOOLEAN ForceDirty, BOOLEAN PageOut)
PMEMORY_AREA NTAPI MmLocateMemoryAreaByAddress(PMMSUPPORT AddressSpace, PVOID Address)
Definition: marea.c:60
#define MmLockSectionSegment(x)
Definition: mm.h:1333
uint32_t ULONG_PTR
Definition: typedefs.h:65
#define MmSetDirtyPage(__P, __A)
Definition: mm.h:1197
PVOID NTAPI MmGetSegmentRmap(PFN_NUMBER Page, PULONG RawOffset)
Definition: rmap.c:458
UCHAR KIRQL
Definition: env_spec_w32.h:591
ULONG PFN_NUMBER
Definition: ke.h:9
NTSTATUS(* NTAPI)(IN PFILE_FULL_EA_INFORMATION EaBuffer, IN ULONG EaLength, OUT PULONG ErrorOffset)
Definition: IoEaTest.cpp:117
#define FALSE
Definition: types.h:117
PEPROCESS PsInitialSystemProcess
Definition: psmgr.c:50
_IRQL_requires_max_(DISPATCH_LEVEL)
Definition: rmap.c:25
DECLSPEC_NORETURN VOID NTAPI KeBugCheck(ULONG BugCheckCode)
Definition: bug.c:1427
NTSTATUS NTAPI MmpPageOutPhysicalAddress(PFN_NUMBER Page)
Definition: swapout.c:345
#define PsGetCurrentProcess
Definition: psfuncs.h:17
VOID NTAPI MmInitializeRmapList(VOID)
Definition: rmap.c:38
unsigned char BOOLEAN
static WCHAR Address[46]
Definition: ping.c:68
VOID NTAPI MmDeleteVirtualMapping(struct _EPROCESS *Process, PVOID Address, BOOLEAN *WasDirty, PPFN_NUMBER Page)
_At_(*)(_In_ PWSK_CLIENT Client, _In_opt_ PUNICODE_STRING NodeName, _In_opt_ PUNICODE_STRING ServiceName, _In_opt_ ULONG NameSpace, _In_opt_ GUID *Provider, _In_opt_ PADDRINFOEXW Hints, _Outptr_ PADDRINFOEXW *Result, _In_opt_ PEPROCESS OwningProcess, _In_opt_ PETHREAD OwningThread, _Inout_ PIRP Irp Result)(Mem)) NTSTATUS(WSKAPI *PFN_WSK_GET_ADDRESS_INFO
Definition: wsk.h:426
#define _In_
Definition: ms_sal.h:308
NTSTATUS NTAPI ObReferenceObjectByPointer(IN PVOID Object, IN ACCESS_MASK DesiredAccess, IN POBJECT_TYPE ObjectType, IN KPROCESSOR_MODE AccessMode)
Definition: obref.c:381
PFN_NUMBER Page
Definition: section.c:4923
void * PVOID
Definition: retypes.h:9
#define MM_IS_WAIT_PTE(E)
Definition: mm.h:1306
#define MEMORY_AREA_SECTION_VIEW
Definition: mm.h:82
#define MmUnlockSectionSegment(x)
Definition: mm.h:1341
VOID NTAPI MmSetRmapListHeadPage(PFN_NUMBER Page, struct _MM_RMAP_ENTRY *ListHead)
_Inout_ PVOID Segment
Definition: exfuncs.h:1101
KIRQL OldIrql
Definition: mm.h:1502
Status
Definition: gdiplustypes.h:24
VOID NTAPI ExInitializeNPagedLookasideList(IN PNPAGED_LOOKASIDE_LIST Lookaside, IN PALLOCATE_FUNCTION Allocate OPTIONAL, IN PFREE_FUNCTION Free OPTIONAL, IN ULONG Flags, IN SIZE_T Size, IN ULONG Tag, IN USHORT Depth)
Definition: lookas.c:218
#define ASSERT(a)
Definition: mode.c:44
PMM_SECTION_SEGMENT NTAPI MmGetSectionAssociation(PFN_NUMBER Page, PLARGE_INTEGER Offset)
Definition: sptab.c:360
#define TAG_RMAP
Definition: tag.h:133
#define NT_SUCCESS(StatCode)
Definition: apphelp.c:32
SWAPENTRY NTAPI MmAllocSwapPage(VOID)
Definition: pagefile.c:304
#define InterlockedExchangeAddUL(Addend, Value)
Definition: ex.h:1531
#define STATUS_PROCESS_IS_TERMINATING
Definition: ntstatus.h:502
#define ObDereferenceObject
Definition: obfuncs.h:203
BOOLEAN DeleteInProgress
Definition: mm.h:242
#define RMAP_SEGMENT_MASK
Definition: mm.h:892
Type
Definition: Type.h:6
#define RMAP_IS_SEGMENT(x)
Definition: mm.h:893
#define STATUS_UNSUCCESSFUL
Definition: udferr_usr.h:132
VOID NTAPI KeDetachProcess(VOID)
Definition: procobj.c:621
uint32_t entry
Definition: isohybrid.c:63
#define PAGE_SIZE
Definition: env_spec_w32.h:49
#define P(row, col)
#define MA_GetStartingAddress(_MemoryArea)
Definition: mm.h:233
#define DISPATCH_LEVEL
Definition: env_spec_w32.h:696
_In_ ULONG _In_ ULONG Offset
Definition: ntddpcm.h:101
PFN_NUMBER NTAPI MmGetPfnForProcess(struct _EPROCESS *Process, PVOID Address)
VOID NTAPI KeAttachProcess(IN PKPROCESS Process)
Definition: procobj.c:582
ULONG_PTR SWAPENTRY
Definition: mm.h:53
#define MM_SEGMENT_INDELETE
Definition: mm.h:228
unsigned int * PULONG
Definition: retypes.h:1
VOID NTAPI MmInsertRmap(PFN_NUMBER Page, PEPROCESS Process, PVOID Address)
Definition: rmap.c:317
#define NULL
Definition: types.h:112
#define __drv_freesMem(kind)
Definition: driverspecs.h:271
struct _MM_RMAP_ENTRY * Next
Definition: mm.h:256
#define DPRINT1
Definition: precomp.h:8
struct _MEMORY_AREA::@1745 SectionData
VOID NTAPI MmSetSavedSwapEntryPage(PFN_NUMBER Page, SWAPENTRY SavedSwapEntry)
Definition: freelist.c:483
_Must_inspect_result_ _In_ PLARGE_INTEGER _In_ PLARGE_INTEGER _In_ ULONG _In_ PFILE_OBJECT _In_ PVOID Process
Definition: fsrtlfuncs.h:219
void * _ReturnAddress(void)
unsigned int ULONG
Definition: retypes.h:1
Definition: mm.h:254
#define ULONG_PTR
Definition: config.h:101
NTSTATUS NTAPI MmWriteToSwapPage(SWAPENTRY SwapEntry, PFN_NUMBER Page)
Definition: pagefile.c:130
static NPAGED_LOOKASIDE_LIST RmapLookasideList
Definition: rmap.c:21
PVOID Address
Definition: mm.h:258
#define STATUS_SUCCESS
Definition: shellext.h:65
NTSTATUS NTAPI MmPageOutPhysicalAddress(PFN_NUMBER Page)
Definition: rmap.c:51
#define ExFreePoolWithTag(_P, _T)
Definition: module.h:1099
struct _MEMORY_AREA * MemoryArea
Definition: newmm.h:37
static CODE_SEG("PAGE")
Definition: isapnp.c:1482
FORCEINLINE VOID MmLockAddressSpace(PMMSUPPORT AddressSpace)
Definition: mm.h:1611
#define MmSystemRangeStart
Definition: mm.h:32
base of all file and directory entries
Definition: entries.h:82
VOID NTAPI MmFreeSwapPage(SWAPENTRY Entry)
Definition: pagefile.c:274
SWAPENTRY NTAPI MmGetSavedSwapEntryPage(PFN_NUMBER Page)
Definition: freelist.c:499