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