Sync with trunk (r48545)
[reactos.git] / ntoskrnl / mm / rmap.c
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 #define NDEBUG
14 #include <debug.h>
15
16 #if defined (ALLOC_PRAGMA)
17 #pragma alloc_text(INIT, MmInitializeRmapList)
18 #endif
19
20 /* TYPES ********************************************************************/
21
22 typedef struct _MM_RMAP_ENTRY
23 {
24 struct _MM_RMAP_ENTRY* Next;
25 PEPROCESS Process;
26 PVOID Address;
27 #if DBG
28 PVOID Caller;
29 #endif
30 }
31 MM_RMAP_ENTRY, *PMM_RMAP_ENTRY;
32
33 /* GLOBALS ******************************************************************/
34
35 static FAST_MUTEX RmapListLock;
36 static NPAGED_LOOKASIDE_LIST RmapLookasideList;
37
38 /* FUNCTIONS ****************************************************************/
39
40 VOID
41 INIT_FUNCTION
42 NTAPI
43 MmInitializeRmapList(VOID)
44 {
45 ExInitializeFastMutex(&RmapListLock);
46 ExInitializeNPagedLookasideList (&RmapLookasideList,
47 NULL,
48 NULL,
49 0,
50 sizeof(MM_RMAP_ENTRY),
51 TAG_RMAP,
52 50);
53 }
54
55 NTSTATUS
56 NTAPI
57 MmWritePagePhysicalAddress(PFN_NUMBER Page)
58 {
59 PMM_RMAP_ENTRY entry;
60 PMEMORY_AREA MemoryArea;
61 PMMSUPPORT AddressSpace;
62 ULONG Type;
63 PVOID Address;
64 PEPROCESS Process;
65 PMM_PAGEOP PageOp;
66 ULONG Offset;
67 NTSTATUS Status = STATUS_SUCCESS;
68
69 /*
70 * Check that the address still has a valid rmap; then reference the
71 * process so it isn't freed while we are working.
72 */
73 ExAcquireFastMutex(&RmapListLock);
74 entry = MmGetRmapListHeadPage(Page);
75 if (entry == NULL)
76 {
77 ExReleaseFastMutex(&RmapListLock);
78 return(STATUS_UNSUCCESSFUL);
79 }
80 Process = entry->Process;
81 Address = entry->Address;
82 if ((((ULONG_PTR)Address) & 0xFFF) != 0)
83 {
84 KeBugCheck(MEMORY_MANAGEMENT);
85 }
86 if (Address < MmSystemRangeStart)
87 {
88 Status = ObReferenceObjectByPointer(Process, PROCESS_ALL_ACCESS, NULL, KernelMode);
89 ExReleaseFastMutex(&RmapListLock);
90 if (!NT_SUCCESS(Status))
91 {
92 return Status;
93 }
94 AddressSpace = &Process->Vm;
95 }
96 else
97 {
98 ExReleaseFastMutex(&RmapListLock);
99 AddressSpace = MmGetKernelAddressSpace();
100 }
101
102 /*
103 * Lock the address space; then check that the address we are using
104 * still corresponds to a valid memory area (the page might have been
105 * freed or paged out after we read the rmap entry.)
106 */
107 MmLockAddressSpace(AddressSpace);
108 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address);
109 if (MemoryArea == NULL || MemoryArea->DeleteInProgress)
110 {
111 MmUnlockAddressSpace(AddressSpace);
112 if (Address < MmSystemRangeStart)
113 {
114 ObDereferenceObject(Process);
115 }
116 return(STATUS_UNSUCCESSFUL);
117 }
118
119 Type = MemoryArea->Type;
120 if (Type == MEMORY_AREA_SECTION_VIEW)
121 {
122 Offset = (ULONG_PTR)Address - (ULONG_PTR)MemoryArea->StartingAddress
123 + MemoryArea->Data.SectionData.ViewOffset;
124 /*
125 * Get or create a pageop
126 */
127 PageOp = MmGetPageOp(MemoryArea, NULL, 0,
128 MemoryArea->Data.SectionData.Segment,
129 Offset, MM_PAGEOP_PAGEOUT, TRUE);
130
131 if (PageOp == NULL)
132 {
133 MmUnlockAddressSpace(AddressSpace);
134 if (Address < MmSystemRangeStart)
135 {
136 ObDereferenceObject(Process);
137 }
138 return(STATUS_UNSUCCESSFUL);
139 }
140
141 /*
142 * Release locks now we have a page op.
143 */
144 MmUnlockAddressSpace(AddressSpace);
145
146 /*
147 * Do the actual page out work.
148 */
149 Status = MmWritePageSectionView(AddressSpace, MemoryArea,
150 Address, PageOp);
151 }
152 else if (Type == MEMORY_AREA_VIRTUAL_MEMORY)
153 {
154 PageOp = MmGetPageOp(MemoryArea, Address < MmSystemRangeStart ? Process->UniqueProcessId : NULL,
155 Address, NULL, 0, MM_PAGEOP_PAGEOUT, TRUE);
156
157 if (PageOp == NULL)
158 {
159 MmUnlockAddressSpace(AddressSpace);
160 if (Address < MmSystemRangeStart)
161 {
162 ObDereferenceObject(Process);
163 }
164 return(STATUS_UNSUCCESSFUL);
165 }
166
167 /*
168 * Release locks now we have a page op.
169 */
170 MmUnlockAddressSpace(AddressSpace);
171
172 /*
173 * Do the actual page out work.
174 */
175 Status = MmWritePageVirtualMemory(AddressSpace, MemoryArea,
176 Address, PageOp);
177 }
178 else
179 {
180 KeBugCheck(MEMORY_MANAGEMENT);
181 }
182 if (Address < MmSystemRangeStart)
183 {
184 ObDereferenceObject(Process);
185 }
186 return(Status);
187 }
188
189 NTSTATUS
190 NTAPI
191 MmPageOutPhysicalAddress(PFN_NUMBER Page)
192 {
193 PMM_RMAP_ENTRY entry;
194 PMEMORY_AREA MemoryArea;
195 PMMSUPPORT AddressSpace;
196 ULONG Type;
197 PVOID Address;
198 PEPROCESS Process;
199 PMM_PAGEOP PageOp;
200 ULONG Offset;
201 NTSTATUS Status = STATUS_SUCCESS;
202
203 ExAcquireFastMutex(&RmapListLock);
204 entry = MmGetRmapListHeadPage(Page);
205 if (entry == NULL)
206 {
207 ExReleaseFastMutex(&RmapListLock);
208 return(STATUS_UNSUCCESSFUL);
209 }
210 Process = entry->Process;
211 Address = entry->Address;
212 if ((((ULONG_PTR)Address) & 0xFFF) != 0)
213 {
214 KeBugCheck(MEMORY_MANAGEMENT);
215 }
216
217 if (Address < MmSystemRangeStart)
218 {
219 Status = ObReferenceObjectByPointer(Process, PROCESS_ALL_ACCESS, NULL, KernelMode);
220 ExReleaseFastMutex(&RmapListLock);
221 if (!NT_SUCCESS(Status))
222 {
223 return Status;
224 }
225 AddressSpace = &Process->Vm;
226 }
227 else
228 {
229 ExReleaseFastMutex(&RmapListLock);
230 AddressSpace = MmGetKernelAddressSpace();
231 }
232
233 MmLockAddressSpace(AddressSpace);
234 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address);
235 if (MemoryArea == NULL || MemoryArea->DeleteInProgress)
236 {
237 MmUnlockAddressSpace(AddressSpace);
238 if (Address < MmSystemRangeStart)
239 {
240 ObDereferenceObject(Process);
241 }
242 return(STATUS_UNSUCCESSFUL);
243 }
244 Type = MemoryArea->Type;
245 if (Type == MEMORY_AREA_SECTION_VIEW)
246 {
247 Offset = (ULONG_PTR)Address - (ULONG_PTR)MemoryArea->StartingAddress
248 + MemoryArea->Data.SectionData.ViewOffset;
249
250 /*
251 * Get or create a pageop
252 */
253 PageOp = MmGetPageOp(MemoryArea, NULL, 0,
254 MemoryArea->Data.SectionData.Segment,
255 Offset, MM_PAGEOP_PAGEOUT, TRUE);
256 if (PageOp == NULL)
257 {
258 MmUnlockAddressSpace(AddressSpace);
259 if (Address < MmSystemRangeStart)
260 {
261 ObDereferenceObject(Process);
262 }
263 return(STATUS_UNSUCCESSFUL);
264 }
265
266 /*
267 * Release locks now we have a page op.
268 */
269 MmUnlockAddressSpace(AddressSpace);
270
271 /*
272 * Do the actual page out work.
273 */
274 Status = MmPageOutSectionView(AddressSpace, MemoryArea,
275 Address, PageOp);
276 }
277 else if (Type == MEMORY_AREA_VIRTUAL_MEMORY)
278 {
279 PageOp = MmGetPageOp(MemoryArea, Address < MmSystemRangeStart ? Process->UniqueProcessId : NULL,
280 Address, NULL, 0, MM_PAGEOP_PAGEOUT, TRUE);
281 if (PageOp == NULL)
282 {
283 MmUnlockAddressSpace(AddressSpace);
284 if (Address < MmSystemRangeStart)
285 {
286 ObDereferenceObject(Process);
287 }
288 return(STATUS_UNSUCCESSFUL);
289 }
290
291 /*
292 * Release locks now we have a page op.
293 */
294 MmUnlockAddressSpace(AddressSpace);
295
296 /*
297 * Do the actual page out work.
298 */
299 Status = MmPageOutVirtualMemory(AddressSpace, MemoryArea,
300 Address, PageOp);
301 }
302 else
303 {
304 KeBugCheck(MEMORY_MANAGEMENT);
305 }
306 if (Address < MmSystemRangeStart)
307 {
308 ObDereferenceObject(Process);
309 }
310 return(Status);
311 }
312
313 VOID
314 NTAPI
315 MmSetCleanAllRmaps(PFN_NUMBER Page)
316 {
317 PMM_RMAP_ENTRY current_entry;
318
319 ExAcquireFastMutex(&RmapListLock);
320 current_entry = MmGetRmapListHeadPage(Page);
321 if (current_entry == NULL)
322 {
323 DPRINT1("MmIsDirtyRmap: No rmaps.\n");
324 KeBugCheck(MEMORY_MANAGEMENT);
325 }
326 while (current_entry != NULL)
327 {
328 MmSetCleanPage(current_entry->Process, current_entry->Address);
329 current_entry = current_entry->Next;
330 }
331 ExReleaseFastMutex(&RmapListLock);
332 }
333
334 VOID
335 NTAPI
336 MmSetDirtyAllRmaps(PFN_NUMBER Page)
337 {
338 PMM_RMAP_ENTRY current_entry;
339
340 ExAcquireFastMutex(&RmapListLock);
341 current_entry = MmGetRmapListHeadPage(Page);
342 if (current_entry == NULL)
343 {
344 DPRINT1("MmIsDirtyRmap: No rmaps.\n");
345 KeBugCheck(MEMORY_MANAGEMENT);
346 }
347 while (current_entry != NULL)
348 {
349 MmSetDirtyPage(current_entry->Process, current_entry->Address);
350 current_entry = current_entry->Next;
351 }
352 ExReleaseFastMutex(&RmapListLock);
353 }
354
355 BOOLEAN
356 NTAPI
357 MmIsDirtyPageRmap(PFN_NUMBER Page)
358 {
359 PMM_RMAP_ENTRY current_entry;
360
361 ExAcquireFastMutex(&RmapListLock);
362 current_entry = MmGetRmapListHeadPage(Page);
363 if (current_entry == NULL)
364 {
365 ExReleaseFastMutex(&RmapListLock);
366 return(FALSE);
367 }
368 while (current_entry != NULL)
369 {
370 if (MmIsDirtyPage(current_entry->Process, current_entry->Address))
371 {
372 ExReleaseFastMutex(&RmapListLock);
373 return(TRUE);
374 }
375 current_entry = current_entry->Next;
376 }
377 ExReleaseFastMutex(&RmapListLock);
378 return(FALSE);
379 }
380
381 VOID
382 NTAPI
383 MmInsertRmap(PFN_NUMBER Page, PEPROCESS Process,
384 PVOID Address)
385 {
386 PMM_RMAP_ENTRY current_entry;
387 PMM_RMAP_ENTRY new_entry;
388 ULONG PrevSize;
389
390 Address = (PVOID)PAGE_ROUND_DOWN(Address);
391
392 new_entry = ExAllocateFromNPagedLookasideList(&RmapLookasideList);
393 if (new_entry == NULL)
394 {
395 KeBugCheck(MEMORY_MANAGEMENT);
396 }
397 new_entry->Address = Address;
398 new_entry->Process = (PEPROCESS)Process;
399 #if DBG
400 #ifdef __GNUC__
401 new_entry->Caller = __builtin_return_address(0);
402 #else
403 new_entry->Caller = _ReturnAddress();
404 #endif
405 #endif
406
407 if (MmGetPfnForProcess(Process, Address) != Page)
408 {
409 DPRINT1("Insert rmap (%d, 0x%.8X) 0x%.8X which doesn't match physical "
410 "address 0x%.8X\n", Process->UniqueProcessId, Address,
411 MmGetPfnForProcess(Process, Address) << PAGE_SHIFT,
412 Page << PAGE_SHIFT);
413 KeBugCheck(MEMORY_MANAGEMENT);
414 }
415
416 ExAcquireFastMutex(&RmapListLock);
417 current_entry = MmGetRmapListHeadPage(Page);
418 new_entry->Next = current_entry;
419 #if DBG
420 while (current_entry)
421 {
422 if (current_entry->Address == new_entry->Address && current_entry->Process == new_entry->Process)
423 {
424 DbgPrint("MmInsertRmap tries to add a second rmap entry for address %p\n current caller ",
425 current_entry->Address);
426 DbgPrint("%p", new_entry->Caller);
427 DbgPrint("\n previous caller ");
428 DbgPrint("%p", current_entry->Caller);
429 DbgPrint("\n");
430 KeBugCheck(MEMORY_MANAGEMENT);
431 }
432 current_entry = current_entry->Next;
433 }
434 #endif
435 MmSetRmapListHeadPage(Page, new_entry);
436 ExReleaseFastMutex(&RmapListLock);
437 if (Process == NULL)
438 {
439 Process = PsInitialSystemProcess;
440 }
441 if (Process)
442 {
443 PrevSize = InterlockedExchangeAddUL(&Process->Vm.WorkingSetSize, PAGE_SIZE);
444 if (PrevSize >= Process->Vm.PeakWorkingSetSize)
445 {
446 Process->Vm.PeakWorkingSetSize = PrevSize + PAGE_SIZE;
447 }
448 }
449 }
450
451 VOID
452 NTAPI
453 MmDeleteAllRmaps(PFN_NUMBER Page, PVOID Context,
454 VOID (*DeleteMapping)(PVOID Context, PEPROCESS Process,
455 PVOID Address))
456 {
457 PMM_RMAP_ENTRY current_entry;
458 PMM_RMAP_ENTRY previous_entry;
459 PEPROCESS Process;
460
461 ExAcquireFastMutex(&RmapListLock);
462 current_entry = MmGetRmapListHeadPage(Page);
463 if (current_entry == NULL)
464 {
465 DPRINT1("MmDeleteAllRmaps: No rmaps.\n");
466 KeBugCheck(MEMORY_MANAGEMENT);
467 }
468 MmSetRmapListHeadPage(Page, NULL);
469 ExReleaseFastMutex(&RmapListLock);
470 while (current_entry != NULL)
471 {
472 previous_entry = current_entry;
473 current_entry = current_entry->Next;
474 if (DeleteMapping)
475 {
476 DeleteMapping(Context, previous_entry->Process,
477 previous_entry->Address);
478 }
479 Process = previous_entry->Process;
480 ExFreeToNPagedLookasideList(&RmapLookasideList, previous_entry);
481 if (Process == NULL)
482 {
483 Process = PsInitialSystemProcess;
484 }
485 if (Process)
486 {
487 (void)InterlockedExchangeAddUL(&Process->Vm.WorkingSetSize, -PAGE_SIZE);
488 }
489 }
490 }
491
492 VOID
493 NTAPI
494 MmDeleteRmap(PFN_NUMBER Page, PEPROCESS Process,
495 PVOID Address)
496 {
497 PMM_RMAP_ENTRY current_entry, previous_entry;
498
499 ExAcquireFastMutex(&RmapListLock);
500 previous_entry = NULL;
501 current_entry = MmGetRmapListHeadPage(Page);
502 while (current_entry != NULL)
503 {
504 if (current_entry->Process == (PEPROCESS)Process &&
505 current_entry->Address == Address)
506 {
507 if (previous_entry == NULL)
508 {
509 MmSetRmapListHeadPage(Page, current_entry->Next);
510 }
511 else
512 {
513 previous_entry->Next = current_entry->Next;
514 }
515 ExReleaseFastMutex(&RmapListLock);
516 ExFreeToNPagedLookasideList(&RmapLookasideList, current_entry);
517 if (Process == NULL)
518 {
519 Process = PsInitialSystemProcess;
520 }
521 if (Process)
522 {
523 (void)InterlockedExchangeAddUL(&Process->Vm.WorkingSetSize, -PAGE_SIZE);
524 }
525 return;
526 }
527 previous_entry = current_entry;
528 current_entry = current_entry->Next;
529 }
530 KeBugCheck(MEMORY_MANAGEMENT);
531 }