2 * COPYRIGHT: See COPYING in the top directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/mm/rmap.c
5 * PURPOSE: Kernel memory managment functions
7 * PROGRAMMERS: David Welch (welch@cwcom.net)
10 /* INCLUDES *****************************************************************/
13 #include <cache/section/newmm.h>
17 /* TYPES ********************************************************************/
19 /* GLOBALS ******************************************************************/
21 static NPAGED_LOOKASIDE_LIST RmapLookasideList
;
23 /* FUNCTIONS ****************************************************************/
25 _IRQL_requires_max_(DISPATCH_LEVEL
)
30 _In_
__drv_freesMem(Mem
) PVOID P
)
32 ExFreePoolWithTag(P
, TAG_RMAP
);
38 MmInitializeRmapList(VOID
)
40 ExInitializeNPagedLookasideList (&RmapLookasideList
,
44 sizeof(MM_RMAP_ENTRY
),
51 MmPageOutPhysicalAddress(PFN_NUMBER Page
)
54 PMEMORY_AREA MemoryArea
;
55 PMMSUPPORT AddressSpace
;
57 PEPROCESS Process
= NULL
;
58 NTSTATUS Status
= STATUS_SUCCESS
;
59 PMM_SECTION_SEGMENT Segment
;
60 LARGE_INTEGER SegmentOffset
;
64 OldIrql
= MiAcquirePfnLock();
66 entry
= MmGetRmapListHeadPage(Page
);
68 while (entry
&& RMAP_IS_SEGMENT(entry
->Address
))
73 MiReleasePfnLock(OldIrql
);
77 Process
= entry
->Process
;
78 Address
= entry
->Address
;
80 if ((((ULONG_PTR
)Address
) & 0xFFF) != 0)
82 KeBugCheck(MEMORY_MANAGEMENT
);
85 /* This is for user-mode address only */
86 ASSERT(Address
< MmSystemRangeStart
);
88 if (!ExAcquireRundownProtection(&Process
->RundownProtect
))
90 MiReleasePfnLock(OldIrql
);
91 return STATUS_PROCESS_IS_TERMINATING
;
94 Status
= ObReferenceObjectByPointer(Process
, PROCESS_ALL_ACCESS
, NULL
, KernelMode
);
95 MiReleasePfnLock(OldIrql
);
96 if (!NT_SUCCESS(Status
))
98 ExReleaseRundownProtection(&Process
->RundownProtect
);
101 AddressSpace
= &Process
->Vm
;
103 MmLockAddressSpace(AddressSpace
);
105 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, Address
);
106 if (MemoryArea
== NULL
|| MemoryArea
->DeleteInProgress
)
108 MmUnlockAddressSpace(AddressSpace
);
109 ExReleaseRundownProtection(&Process
->RundownProtect
);
110 ObDereferenceObject(Process
);
115 /* Attach to it, if needed */
116 ASSERT(PsGetCurrentProcess() == PsInitialSystemProcess
);
117 if (Process
!= PsInitialSystemProcess
)
118 KeAttachProcess(&Process
->Pcb
);
120 if (MmGetPfnForProcess(Process
, Address
) != Page
)
122 /* This changed in the short window where we didn't have any locks */
123 if (Process
!= PsInitialSystemProcess
)
125 MmUnlockAddressSpace(AddressSpace
);
126 ExReleaseRundownProtection(&Process
->RundownProtect
);
127 ObDereferenceObject(Process
);
131 if (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
)
136 LARGE_INTEGER Offset
;
139 Offset
.QuadPart
= MemoryArea
->SectionData
.ViewOffset
+
140 ((ULONG_PTR
)Address
- MA_GetStartingAddress(MemoryArea
));
142 Segment
= MemoryArea
->SectionData
.Segment
;
144 MmLockSectionSegment(Segment
);
146 Entry
= MmGetPageEntrySectionSegment(Segment
, &Offset
);
147 if (Entry
&& MM_IS_WAIT_PTE(Entry
))
149 /* The segment is being read or something. Give up */
150 MmUnlockSectionSegment(Segment
);
151 if (Process
!= PsInitialSystemProcess
)
153 MmUnlockAddressSpace(AddressSpace
);
154 ExReleaseRundownProtection(&Process
->RundownProtect
);
155 ObDereferenceObject(Process
);
156 return(STATUS_UNSUCCESSFUL
);
159 /* Delete this virtual mapping in the process */
160 MmDeleteRmap(Page
, Process
, Address
);
161 MmDeleteVirtualMapping(Process
, Address
, &Dirty
, &MapPage
);
163 /* We checked this earlier */
164 ASSERT(MapPage
== Page
);
166 if (Page
!= PFN_FROM_SSE(Entry
))
170 /* This page is private to the process */
171 MmUnlockSectionSegment(Segment
);
173 /* Check if we should write it back to the page file */
174 SwapEntry
= MmGetSavedSwapEntryPage(Page
);
176 if ((SwapEntry
== 0) && Dirty
)
178 /* We don't have a Swap entry, yet the page is dirty. Get one */
179 SwapEntry
= MmAllocSwapPage();
182 PMM_REGION Region
= MmFindRegion((PVOID
)MA_GetStartingAddress(MemoryArea
),
183 &MemoryArea
->SectionData
.RegionListHead
,
186 /* We can't, so let this page in the Process VM */
187 MmCreateVirtualMapping(Process
, Address
, Region
->Protect
, Page
);
188 MmInsertRmap(Page
, Process
, Address
);
189 MmSetDirtyPage(Process
, Address
);
191 MmUnlockAddressSpace(AddressSpace
);
192 if (Process
!= PsInitialSystemProcess
)
194 ExReleaseRundownProtection(&Process
->RundownProtect
);
195 ObDereferenceObject(Process
);
197 return STATUS_UNSUCCESSFUL
;
205 /* Put a wait entry into the process and unlock */
206 MmCreatePageFileMapping(Process
, Address
, MM_WAIT_ENTRY
);
207 MmUnlockAddressSpace(AddressSpace
);
209 Status
= MmWriteToSwapPage(SwapEntry
, Page
);
211 MmLockAddressSpace(AddressSpace
);
212 MmDeletePageFileMapping(Process
, Address
, &Dummy
);
213 ASSERT(Dummy
== MM_WAIT_ENTRY
);
215 if (!NT_SUCCESS(Status
))
217 /* We failed at saving the content of this page. Keep it in */
218 PMM_REGION Region
= MmFindRegion((PVOID
)MA_GetStartingAddress(MemoryArea
),
219 &MemoryArea
->SectionData
.RegionListHead
,
222 /* This Swap Entry is useless to us */
223 MmSetSavedSwapEntryPage(Page
, 0);
224 MmFreeSwapPage(SwapEntry
);
226 /* We can't, so let this page in the Process VM */
227 MmCreateVirtualMapping(Process
, Address
, Region
->Protect
, Page
);
228 MmInsertRmap(Page
, Process
, Address
);
229 MmSetDirtyPage(Process
, Address
);
231 MmUnlockAddressSpace(AddressSpace
);
232 if (Process
!= PsInitialSystemProcess
)
234 ExReleaseRundownProtection(&Process
->RundownProtect
);
235 ObDereferenceObject(Process
);
237 return STATUS_UNSUCCESSFUL
;
243 /* Keep this in the process VM */
244 MmCreatePageFileMapping(Process
, Address
, SwapEntry
);
245 MmSetSavedSwapEntryPage(Page
, 0);
248 /* We can finally let this page go */
249 MmUnlockAddressSpace(AddressSpace
);
250 if (Process
!= PsInitialSystemProcess
)
253 OldIrql
= MiAcquirePfnLock();
254 ASSERT(MmGetRmapListHeadPage(Page
) == NULL
);
255 MiReleasePfnLock(OldIrql
);
257 MmReleasePageMemoryConsumer(MC_USER
, Page
);
259 ExReleaseRundownProtection(&Process
->RundownProtect
);
260 ObDereferenceObject(Process
);
262 return STATUS_SUCCESS
;
265 /* One less mapping referencing this segment */
266 Released
= MmUnsharePageEntrySectionSegment(MemoryArea
, Segment
, &Offset
, Dirty
, TRUE
, NULL
);
268 MmUnlockSectionSegment(Segment
);
269 if (Process
!= PsInitialSystemProcess
)
271 MmUnlockAddressSpace(AddressSpace
);
273 ExReleaseRundownProtection(&Process
->RundownProtect
);
274 ObDereferenceObject(Process
);
276 if (Released
) return STATUS_SUCCESS
;
279 else if (Type
== MEMORY_AREA_CACHE
)
281 /* NEWCC does locking itself */
282 MmUnlockAddressSpace(AddressSpace
);
283 Status
= MmpPageOutPhysicalAddress(Page
);
288 KeBugCheck(MEMORY_MANAGEMENT
);
292 /* Now write this page to file, if needed */
293 Segment
= MmGetSectionAssociation(Page
, &SegmentOffset
);
298 MmLockSectionSegment(Segment
);
300 Released
= MmCheckDirtySegment(Segment
, &SegmentOffset
, FALSE
, TRUE
);
302 MmUnlockSectionSegment(Segment
);
304 MmDereferenceSegment(Segment
);
308 return STATUS_SUCCESS
;
312 /* If we are here, then we didn't release the page */
313 return STATUS_UNSUCCESSFUL
;
318 MmInsertRmap(PFN_NUMBER Page
, PEPROCESS Process
,
321 PMM_RMAP_ENTRY current_entry
;
322 PMM_RMAP_ENTRY new_entry
;
326 if (!RMAP_IS_SEGMENT(Address
))
327 Address
= (PVOID
)PAGE_ROUND_DOWN(Address
);
329 new_entry
= ExAllocateFromNPagedLookasideList(&RmapLookasideList
);
330 if (new_entry
== NULL
)
332 KeBugCheck(MEMORY_MANAGEMENT
);
334 new_entry
->Address
= Address
;
335 new_entry
->Process
= (PEPROCESS
)Process
;
337 new_entry
->Caller
= _ReturnAddress();
341 !RMAP_IS_SEGMENT(Address
) &&
342 MmGetPfnForProcess(Process
, Address
) != Page
)
344 DPRINT1("Insert rmap (%d, 0x%.8X) 0x%.8X which doesn't match physical "
345 "address 0x%.8X\n", Process
? Process
->UniqueProcessId
: 0,
347 MmGetPfnForProcess(Process
, Address
) << PAGE_SHIFT
,
349 KeBugCheck(MEMORY_MANAGEMENT
);
352 OldIrql
= MiAcquirePfnLock();
353 current_entry
= MmGetRmapListHeadPage(Page
);
355 PMM_RMAP_ENTRY previous_entry
= NULL
;
356 /* Keep the list sorted */
357 while (current_entry
&& (current_entry
->Address
< Address
))
359 previous_entry
= current_entry
;
360 current_entry
= current_entry
->Next
;
363 /* In case of clash in the address, sort by process */
364 if (current_entry
&& (current_entry
->Address
== Address
))
366 while (current_entry
&& (current_entry
->Process
< Process
))
368 previous_entry
= current_entry
;
369 current_entry
= current_entry
->Next
;
373 if (current_entry
&& (current_entry
->Address
== Address
) && (current_entry
->Process
== Process
))
376 DbgPrint("MmInsertRmap tries to add a second rmap entry for address %p\n", current_entry
->Address
);
377 DbgPrint(" current caller %p\n", new_entry
->Caller
);
378 DbgPrint(" previous caller %p\n", current_entry
->Caller
);
380 KeBugCheck(MEMORY_MANAGEMENT
);
383 new_entry
->Next
= current_entry
;
385 previous_entry
->Next
= new_entry
;
387 MmSetRmapListHeadPage(Page
, new_entry
);
389 MiReleasePfnLock(OldIrql
);
391 if (!RMAP_IS_SEGMENT(Address
))
393 ASSERT(Process
!= NULL
);
394 PrevSize
= InterlockedExchangeAddUL(&Process
->Vm
.WorkingSetSize
, PAGE_SIZE
);
395 if (PrevSize
>= Process
->Vm
.PeakWorkingSetSize
)
397 Process
->Vm
.PeakWorkingSetSize
= PrevSize
+ PAGE_SIZE
;
404 MmDeleteRmap(PFN_NUMBER Page
, PEPROCESS Process
,
407 PMM_RMAP_ENTRY current_entry
, previous_entry
;
410 OldIrql
= MiAcquirePfnLock();
411 previous_entry
= NULL
;
412 current_entry
= MmGetRmapListHeadPage(Page
);
414 while (current_entry
!= NULL
)
416 if (current_entry
->Process
== (PEPROCESS
)Process
&&
417 current_entry
->Address
== Address
)
419 if (previous_entry
== NULL
)
421 MmSetRmapListHeadPage(Page
, current_entry
->Next
);
425 previous_entry
->Next
= current_entry
->Next
;
427 MiReleasePfnLock(OldIrql
);
429 ExFreeToNPagedLookasideList(&RmapLookasideList
, current_entry
);
430 if (!RMAP_IS_SEGMENT(Address
))
432 ASSERT(Process
!= NULL
);
433 (void)InterlockedExchangeAddUL(&Process
->Vm
.WorkingSetSize
, -PAGE_SIZE
);
437 previous_entry
= current_entry
;
438 current_entry
= current_entry
->Next
;
440 KeBugCheck(MEMORY_MANAGEMENT
);
445 Return the process pointer given when a previous call to MmInsertRmap was
446 called with a process and address pointer that conform to the segment rmap
447 schema. In short, this requires the address part to be 0xffffff00 + n
448 where n is between 0 and 255. When such an rmap exists, it specifies a
449 segment rmap in which the process part is a pointer to a slice of a section
450 page table, and the low 8 bits of the address represent a page index in the
451 page table slice. Together, this information is used by
452 MmGetSectionAssociation to determine which page entry points to this page in
453 the segment page table.
459 MmGetSegmentRmap(PFN_NUMBER Page
, PULONG RawOffset
)
461 PCACHE_SECTION_PAGE_TABLE Result
= NULL
;
462 PMM_RMAP_ENTRY current_entry
;//, previous_entry;
463 KIRQL OldIrql
= MiAcquirePfnLock();
465 //previous_entry = NULL;
466 current_entry
= MmGetRmapListHeadPage(Page
);
467 while (current_entry
!= NULL
)
469 if (RMAP_IS_SEGMENT(current_entry
->Address
))
471 Result
= (PCACHE_SECTION_PAGE_TABLE
)current_entry
->Process
;
472 *RawOffset
= (ULONG_PTR
)current_entry
->Address
& ~RMAP_SEGMENT_MASK
;
473 if (*Result
->Segment
->Flags
& MM_SEGMENT_INDELETE
)
475 MiReleasePfnLock(OldIrql
);
479 InterlockedIncrement64(Result
->Segment
->ReferenceCount
);
480 MiReleasePfnLock(OldIrql
);
483 //previous_entry = current_entry;
484 current_entry
= current_entry
->Next
;
486 MiReleasePfnLock(OldIrql
);
492 Remove the section rmap associated with the indicated page, if it exists.
498 MmDeleteSectionAssociation(PFN_NUMBER Page
)
500 PMM_RMAP_ENTRY current_entry
, previous_entry
;
501 KIRQL OldIrql
= MiAcquirePfnLock();
503 previous_entry
= NULL
;
504 current_entry
= MmGetRmapListHeadPage(Page
);
505 while (current_entry
!= NULL
)
507 if (RMAP_IS_SEGMENT(current_entry
->Address
))
509 if (previous_entry
== NULL
)
511 MmSetRmapListHeadPage(Page
, current_entry
->Next
);
515 previous_entry
->Next
= current_entry
->Next
;
517 MiReleasePfnLock(OldIrql
);
518 ExFreeToNPagedLookasideList(&RmapLookasideList
, current_entry
);
521 previous_entry
= current_entry
;
522 current_entry
= current_entry
->Next
;
524 MiReleasePfnLock(OldIrql
);