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