xref: /reactos/ntoskrnl/mm/rmap.c (revision 40462c92)
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 
21 static NPAGED_LOOKASIDE_LIST RmapLookasideList;
22 FAST_MUTEX RmapListLock;
23 
24 /* FUNCTIONS ****************************************************************/
25 
26 _IRQL_requires_max_(DISPATCH_LEVEL)
27 static
28 VOID
29 NTAPI
30 RmapListFree(
31     _In_ __drv_freesMem(Mem) PVOID P)
32 {
33     ExFreePoolWithTag(P, TAG_RMAP);
34 }
35 
36 CODE_SEG("INIT")
37 VOID
38 NTAPI
39 MmInitializeRmapList(VOID)
40 {
41     ExInitializeFastMutex(&RmapListLock);
42     ExInitializeNPagedLookasideList (&RmapLookasideList,
43                                      NULL,
44                                      RmapListFree,
45                                      0,
46                                      sizeof(MM_RMAP_ENTRY),
47                                      TAG_RMAP,
48                                      50);
49 }
50 
51 NTSTATUS
52 NTAPI
53 MmPageOutPhysicalAddress(PFN_NUMBER Page)
54 {
55     PMM_RMAP_ENTRY entry;
56     PMEMORY_AREA MemoryArea;
57     PMMSUPPORT AddressSpace;
58     ULONG Type;
59     PVOID Address;
60     PEPROCESS Process;
61     ULONGLONG Offset;
62     NTSTATUS Status = STATUS_SUCCESS;
63 
64     ExAcquireFastMutex(&RmapListLock);
65     entry = MmGetRmapListHeadPage(Page);
66 
67 #ifdef NEWCC
68     // Special case for NEWCC: we can have a page that's only in a segment
69     // page table
70     if (entry && RMAP_IS_SEGMENT(entry->Address) && entry->Next == NULL)
71     {
72         /* NEWCC does locking itself */
73         ExReleaseFastMutex(&RmapListLock);
74         return MmpPageOutPhysicalAddress(Page);
75     }
76 #endif
77 
78     while (entry && RMAP_IS_SEGMENT(entry->Address))
79         entry = entry->Next;
80 
81     if (entry == NULL)
82     {
83         ExReleaseFastMutex(&RmapListLock);
84         return(STATUS_UNSUCCESSFUL);
85     }
86 
87     Process = entry->Process;
88 
89     Address = entry->Address;
90 
91     if ((((ULONG_PTR)Address) & 0xFFF) != 0)
92     {
93         KeBugCheck(MEMORY_MANAGEMENT);
94     }
95 
96     if (Address < MmSystemRangeStart)
97     {
98         if (!ExAcquireRundownProtection(&Process->RundownProtect))
99         {
100             ExReleaseFastMutex(&RmapListLock);
101             return STATUS_PROCESS_IS_TERMINATING;
102         }
103 
104         Status = ObReferenceObjectByPointer(Process, PROCESS_ALL_ACCESS, NULL, KernelMode);
105         ExReleaseFastMutex(&RmapListLock);
106         if (!NT_SUCCESS(Status))
107         {
108             ExReleaseRundownProtection(&Process->RundownProtect);
109             return Status;
110         }
111         AddressSpace = &Process->Vm;
112     }
113     else
114     {
115         ExReleaseFastMutex(&RmapListLock);
116         AddressSpace = MmGetKernelAddressSpace();
117     }
118 
119     MmLockAddressSpace(AddressSpace);
120     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address);
121     if (MemoryArea == NULL || MemoryArea->DeleteInProgress)
122     {
123         MmUnlockAddressSpace(AddressSpace);
124         if (Address < MmSystemRangeStart)
125         {
126             ExReleaseRundownProtection(&Process->RundownProtect);
127             ObDereferenceObject(Process);
128         }
129         return(STATUS_UNSUCCESSFUL);
130     }
131     Type = MemoryArea->Type;
132     if (Type == MEMORY_AREA_SECTION_VIEW)
133     {
134         ULONG_PTR Entry;
135         Offset = MemoryArea->Data.SectionData.ViewOffset.QuadPart +
136                  ((ULONG_PTR)Address - MA_GetStartingAddress(MemoryArea));
137 
138         MmLockSectionSegment(MemoryArea->Data.SectionData.Segment);
139 
140         /*
141          * Get or create a pageop
142          */
143         Entry = MmGetPageEntrySectionSegment(MemoryArea->Data.SectionData.Segment,
144                                              (PLARGE_INTEGER)&Offset);
145         if (Entry && MM_IS_WAIT_PTE(Entry))
146         {
147             MmUnlockSectionSegment(MemoryArea->Data.SectionData.Segment);
148             MmUnlockAddressSpace(AddressSpace);
149             if (Address < MmSystemRangeStart)
150             {
151                 ExReleaseRundownProtection(&Process->RundownProtect);
152                 ObDereferenceObject(Process);
153             }
154             return(STATUS_UNSUCCESSFUL);
155         }
156 
157         MmSetPageEntrySectionSegment(MemoryArea->Data.SectionData.Segment, (PLARGE_INTEGER)&Offset, MAKE_SWAP_SSE(MM_WAIT_ENTRY));
158 
159         /*
160          * Release locks now we have a page op.
161          */
162         MmUnlockSectionSegment(MemoryArea->Data.SectionData.Segment);
163         MmUnlockAddressSpace(AddressSpace);
164 
165         /*
166          * Do the actual page out work.
167          */
168         Status = MmPageOutSectionView(AddressSpace, MemoryArea, Address, Entry);
169     }
170     else if (Type == MEMORY_AREA_CACHE)
171     {
172         /* NEWCC does locking itself */
173         MmUnlockAddressSpace(AddressSpace);
174         Status = MmpPageOutPhysicalAddress(Page);
175     }
176     else
177     {
178         KeBugCheck(MEMORY_MANAGEMENT);
179     }
180 
181     if (Address < MmSystemRangeStart)
182     {
183         ExReleaseRundownProtection(&Process->RundownProtect);
184         ObDereferenceObject(Process);
185     }
186     return(Status);
187 }
188 
189 VOID
190 NTAPI
191 MmSetCleanAllRmaps(PFN_NUMBER Page)
192 {
193     PMM_RMAP_ENTRY current_entry;
194 
195     ExAcquireFastMutex(&RmapListLock);
196     current_entry = MmGetRmapListHeadPage(Page);
197     if (current_entry == NULL)
198     {
199         DPRINT1("MmIsDirtyRmap: No rmaps.\n");
200         KeBugCheck(MEMORY_MANAGEMENT);
201     }
202     while (current_entry != NULL)
203     {
204         if (!RMAP_IS_SEGMENT(current_entry->Address))
205             MmSetCleanPage(current_entry->Process, current_entry->Address);
206         current_entry = current_entry->Next;
207     }
208     ExReleaseFastMutex(&RmapListLock);
209 }
210 
211 VOID
212 NTAPI
213 MmSetDirtyAllRmaps(PFN_NUMBER Page)
214 {
215     PMM_RMAP_ENTRY current_entry;
216 
217     ExAcquireFastMutex(&RmapListLock);
218     current_entry = MmGetRmapListHeadPage(Page);
219     if (current_entry == NULL)
220     {
221         DPRINT1("MmIsDirtyRmap: No rmaps.\n");
222         KeBugCheck(MEMORY_MANAGEMENT);
223     }
224     while (current_entry != NULL)
225     {
226         if (!RMAP_IS_SEGMENT(current_entry->Address))
227             MmSetDirtyPage(current_entry->Process, current_entry->Address);
228         current_entry = current_entry->Next;
229     }
230     ExReleaseFastMutex(&RmapListLock);
231 }
232 
233 BOOLEAN
234 NTAPI
235 MmIsDirtyPageRmap(PFN_NUMBER Page)
236 {
237     PMM_RMAP_ENTRY current_entry;
238 
239     ExAcquireFastMutex(&RmapListLock);
240     current_entry = MmGetRmapListHeadPage(Page);
241     if (current_entry == NULL)
242     {
243         ExReleaseFastMutex(&RmapListLock);
244         return(FALSE);
245     }
246     while (current_entry != NULL)
247     {
248         if (
249             !RMAP_IS_SEGMENT(current_entry->Address) &&
250             MmIsDirtyPage(current_entry->Process, current_entry->Address))
251         {
252             ExReleaseFastMutex(&RmapListLock);
253             return(TRUE);
254         }
255         current_entry = current_entry->Next;
256     }
257     ExReleaseFastMutex(&RmapListLock);
258     return(FALSE);
259 }
260 
261 VOID
262 NTAPI
263 MmInsertRmap(PFN_NUMBER Page, PEPROCESS Process,
264              PVOID Address)
265 {
266     PMM_RMAP_ENTRY current_entry;
267     PMM_RMAP_ENTRY new_entry;
268     ULONG PrevSize;
269     if (!RMAP_IS_SEGMENT(Address))
270         Address = (PVOID)PAGE_ROUND_DOWN(Address);
271 
272     new_entry = ExAllocateFromNPagedLookasideList(&RmapLookasideList);
273     if (new_entry == NULL)
274     {
275         KeBugCheck(MEMORY_MANAGEMENT);
276     }
277     new_entry->Address = Address;
278     new_entry->Process = (PEPROCESS)Process;
279 #if DBG
280 #ifdef __GNUC__
281     new_entry->Caller = __builtin_return_address(0);
282 #else
283     new_entry->Caller = _ReturnAddress();
284 #endif
285 #endif
286 
287     if (
288         !RMAP_IS_SEGMENT(Address) &&
289         MmGetPfnForProcess(Process, Address) != Page)
290     {
291         DPRINT1("Insert rmap (%d, 0x%.8X) 0x%.8X which doesn't match physical "
292                 "address 0x%.8X\n", Process ? Process->UniqueProcessId : 0,
293                 Address,
294                 MmGetPfnForProcess(Process, Address) << PAGE_SHIFT,
295                 Page << PAGE_SHIFT);
296         KeBugCheck(MEMORY_MANAGEMENT);
297     }
298 
299     ExAcquireFastMutex(&RmapListLock);
300     current_entry = MmGetRmapListHeadPage(Page);
301     new_entry->Next = current_entry;
302 #if DBG
303     while (current_entry)
304     {
305         if (current_entry->Address == new_entry->Address && current_entry->Process == new_entry->Process)
306         {
307             DbgPrint("MmInsertRmap tries to add a second rmap entry for address %p\n    current caller ",
308                      current_entry->Address);
309             DbgPrint("%p", new_entry->Caller);
310             DbgPrint("\n    previous caller ");
311             DbgPrint("%p", current_entry->Caller);
312             DbgPrint("\n");
313             KeBugCheck(MEMORY_MANAGEMENT);
314         }
315         current_entry = current_entry->Next;
316     }
317 #endif
318     MmSetRmapListHeadPage(Page, new_entry);
319     ExReleaseFastMutex(&RmapListLock);
320     if (!RMAP_IS_SEGMENT(Address))
321     {
322         if (Process == NULL)
323         {
324             Process = PsInitialSystemProcess;
325         }
326         if (Process)
327         {
328             PrevSize = InterlockedExchangeAddUL(&Process->Vm.WorkingSetSize, PAGE_SIZE);
329             if (PrevSize >= Process->Vm.PeakWorkingSetSize)
330             {
331                 Process->Vm.PeakWorkingSetSize = PrevSize + PAGE_SIZE;
332             }
333         }
334     }
335 }
336 
337 VOID
338 NTAPI
339 MmDeleteAllRmaps(PFN_NUMBER Page, PVOID Context,
340                  VOID (*DeleteMapping)(PVOID Context, PEPROCESS Process,
341                                        PVOID Address))
342 {
343     PMM_RMAP_ENTRY current_entry;
344     PMM_RMAP_ENTRY previous_entry;
345     PEPROCESS Process;
346 
347     ExAcquireFastMutex(&RmapListLock);
348     current_entry = MmGetRmapListHeadPage(Page);
349     if (current_entry == NULL)
350     {
351         DPRINT1("MmDeleteAllRmaps: No rmaps.\n");
352         KeBugCheck(MEMORY_MANAGEMENT);
353     }
354     MmSetRmapListHeadPage(Page, NULL);
355     ExReleaseFastMutex(&RmapListLock);
356 
357     while (current_entry != NULL)
358     {
359         previous_entry = current_entry;
360         current_entry = current_entry->Next;
361         if (!RMAP_IS_SEGMENT(previous_entry->Address))
362         {
363             if (DeleteMapping)
364             {
365                 DeleteMapping(Context, previous_entry->Process,
366                               previous_entry->Address);
367             }
368             Process = previous_entry->Process;
369             ExFreeToNPagedLookasideList(&RmapLookasideList, previous_entry);
370             if (Process == NULL)
371             {
372                 Process = PsInitialSystemProcess;
373             }
374             if (Process)
375             {
376                 (void)InterlockedExchangeAddUL(&Process->Vm.WorkingSetSize, -PAGE_SIZE);
377             }
378         }
379         else
380         {
381             ExFreeToNPagedLookasideList(&RmapLookasideList, previous_entry);
382         }
383     }
384 }
385 
386 VOID
387 NTAPI
388 MmDeleteRmap(PFN_NUMBER Page, PEPROCESS Process,
389              PVOID Address)
390 {
391     PMM_RMAP_ENTRY current_entry, previous_entry;
392 
393     ExAcquireFastMutex(&RmapListLock);
394     previous_entry = NULL;
395     current_entry = MmGetRmapListHeadPage(Page);
396 
397     while (current_entry != NULL)
398     {
399         if (current_entry->Process == (PEPROCESS)Process &&
400                 current_entry->Address == Address)
401         {
402             if (previous_entry == NULL)
403             {
404                 MmSetRmapListHeadPage(Page, current_entry->Next);
405             }
406             else
407             {
408                 previous_entry->Next = current_entry->Next;
409             }
410             ExReleaseFastMutex(&RmapListLock);
411             ExFreeToNPagedLookasideList(&RmapLookasideList, current_entry);
412             if (!RMAP_IS_SEGMENT(Address))
413             {
414                 if (Process == NULL)
415                 {
416                     Process = PsInitialSystemProcess;
417                 }
418                 if (Process)
419                 {
420                     (void)InterlockedExchangeAddUL(&Process->Vm.WorkingSetSize, -PAGE_SIZE);
421                 }
422             }
423             return;
424         }
425         previous_entry = current_entry;
426         current_entry = current_entry->Next;
427     }
428     KeBugCheck(MEMORY_MANAGEMENT);
429 }
430 
431 /*
432 
433 Return the process pointer given when a previous call to MmInsertRmap was
434 called with a process and address pointer that conform to the segment rmap
435 schema.  In short, this requires the address part to be 0xffffff00 + n
436 where n is between 0 and 255.  When such an rmap exists, it specifies a
437 segment rmap in which the process part is a pointer to a slice of a section
438 page table, and the low 8 bits of the address represent a page index in the
439 page table slice.  Together, this information is used by
440 MmGetSectionAssociation to determine which page entry points to this page in
441 the segment page table.
442 
443 */
444 
445 PVOID
446 NTAPI
447 MmGetSegmentRmap(PFN_NUMBER Page, PULONG RawOffset)
448 {
449     PCACHE_SECTION_PAGE_TABLE Result = NULL;
450     PMM_RMAP_ENTRY current_entry;//, previous_entry;
451 
452     ExAcquireFastMutex(&RmapListLock);
453     //previous_entry = NULL;
454     current_entry = MmGetRmapListHeadPage(Page);
455     while (current_entry != NULL)
456     {
457         if (RMAP_IS_SEGMENT(current_entry->Address))
458         {
459             Result = (PCACHE_SECTION_PAGE_TABLE)current_entry->Process;
460             *RawOffset = (ULONG_PTR)current_entry->Address & ~RMAP_SEGMENT_MASK;
461             InterlockedIncrementUL(&Result->Segment->ReferenceCount);
462             ExReleaseFastMutex(&RmapListLock);
463             return Result;
464         }
465         //previous_entry = current_entry;
466         current_entry = current_entry->Next;
467     }
468     ExReleaseFastMutex(&RmapListLock);
469     return NULL;
470 }
471 
472 /*
473 
474 Remove the section rmap associated with the indicated page, if it exists.
475 
476 */
477 
478 VOID
479 NTAPI
480 MmDeleteSectionAssociation(PFN_NUMBER Page)
481 {
482     PMM_RMAP_ENTRY current_entry, previous_entry;
483 
484     ExAcquireFastMutex(&RmapListLock);
485     previous_entry = NULL;
486     current_entry = MmGetRmapListHeadPage(Page);
487     while (current_entry != NULL)
488     {
489         if (RMAP_IS_SEGMENT(current_entry->Address))
490         {
491             if (previous_entry == NULL)
492             {
493                 MmSetRmapListHeadPage(Page, current_entry->Next);
494             }
495             else
496             {
497                 previous_entry->Next = current_entry->Next;
498             }
499             ExReleaseFastMutex(&RmapListLock);
500             ExFreeToNPagedLookasideList(&RmapLookasideList, current_entry);
501             return;
502         }
503         previous_entry = current_entry;
504         current_entry = current_entry->Next;
505     }
506     ExReleaseFastMutex(&RmapListLock);
507 }
508