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