- Changed all internal memory functions to use the page frame number instead of the...
[reactos.git] / reactos / ntoskrnl / mm / rmap.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /* $Id: rmap.c,v 1.29 2004/08/01 07:24:58 hbirr Exp $
20 *
21 * COPYRIGHT: See COPYING in the top directory
22 * PROJECT: ReactOS kernel
23 * FILE: ntoskrnl/mm/rmap.c
24 * PURPOSE: kernel memory managment functions
25 * PROGRAMMER: David Welch (welch@cwcom.net)
26 * UPDATE HISTORY:
27 * Created 27/12/01
28 */
29
30 /* INCLUDES *****************************************************************/
31
32 #include <ddk/ntddk.h>
33 #include <internal/mm.h>
34 #include <internal/ps.h>
35
36 #define NDEBUG
37 #include <internal/debug.h>
38
39 /* TYPES ********************************************************************/
40
41 typedef struct _MM_RMAP_ENTRY
42 {
43 struct _MM_RMAP_ENTRY* Next;
44 PEPROCESS Process;
45 PVOID Address;
46 }
47 MM_RMAP_ENTRY, *PMM_RMAP_ENTRY;
48
49 #define TAG_RMAP TAG('R', 'M', 'A', 'P')
50
51 /* GLOBALS ******************************************************************/
52
53 static FAST_MUTEX RmapListLock;
54 static NPAGED_LOOKASIDE_LIST RmapLookasideList;
55
56 /* FUNCTIONS ****************************************************************/
57
58 VOID INIT_FUNCTION
59 MmInitializeRmapList(VOID)
60 {
61 ExInitializeFastMutex(&RmapListLock);
62 ExInitializeNPagedLookasideList (&RmapLookasideList,
63 NULL,
64 NULL,
65 0,
66 sizeof(MM_RMAP_ENTRY),
67 TAG_RMAP,
68 50);
69 }
70
71 NTSTATUS
72 MmWritePagePhysicalAddress(PFN_TYPE Page)
73 {
74 PMM_RMAP_ENTRY entry;
75 PMEMORY_AREA MemoryArea;
76 PMADDRESS_SPACE AddressSpace;
77 ULONG Type;
78 PVOID Address;
79 PEPROCESS Process;
80 PMM_PAGEOP PageOp;
81 ULONG Offset;
82 NTSTATUS Status = STATUS_SUCCESS;
83
84 /*
85 * Check that the address still has a valid rmap; then reference the
86 * process so it isn't freed while we are working.
87 */
88 ExAcquireFastMutex(&RmapListLock);
89 entry = MmGetRmapListHeadPage(Page);
90 if (entry == NULL)
91 {
92 ExReleaseFastMutex(&RmapListLock);
93 return(STATUS_UNSUCCESSFUL);
94 }
95 Process = entry->Process;
96 Address = entry->Address;
97 if ((((ULONG)Address) & 0xFFF) != 0)
98 {
99 KEBUGCHECK(0);
100 }
101 if (Address < (PVOID)KERNEL_BASE)
102 {
103 Status = ObReferenceObjectByPointer(Process, PROCESS_ALL_ACCESS, NULL, KernelMode);
104 ExReleaseFastMutex(&RmapListLock);
105 if (!NT_SUCCESS(Status))
106 {
107 return Status;
108 }
109 AddressSpace = &Process->AddressSpace;
110 }
111 else
112 {
113 ExReleaseFastMutex(&RmapListLock);
114 AddressSpace = MmGetKernelAddressSpace();
115 }
116
117 /*
118 * Lock the address space; then check that the address we are using
119 * still corresponds to a valid memory area (the page might have been
120 * freed or paged out after we read the rmap entry.)
121 */
122 MmLockAddressSpace(AddressSpace);
123 MemoryArea = MmOpenMemoryAreaByAddress(AddressSpace, Address);
124 if (MemoryArea == NULL || MemoryArea->DeleteInProgress)
125 {
126 MmUnlockAddressSpace(AddressSpace);
127 if (Address < (PVOID)KERNEL_BASE)
128 {
129 ObDereferenceObject(Process);
130 }
131 return(STATUS_UNSUCCESSFUL);
132 }
133
134 Type = MemoryArea->Type;
135 if (Type == MEMORY_AREA_SECTION_VIEW)
136 {
137 Offset = (ULONG)((char*)Address - (ULONG)MemoryArea->BaseAddress);
138
139 /*
140 * Get or create a pageop
141 */
142 PageOp = MmGetPageOp(MemoryArea, 0, 0,
143 MemoryArea->Data.SectionData.Segment,
144 Offset, MM_PAGEOP_PAGEOUT, TRUE);
145
146 if (PageOp == NULL)
147 {
148 MmUnlockAddressSpace(AddressSpace);
149 if (Address < (PVOID)KERNEL_BASE)
150 {
151 ObDereferenceObject(Process);
152 }
153 return(STATUS_UNSUCCESSFUL);
154 }
155
156 /*
157 * Release locks now we have a page op.
158 */
159 MmUnlockAddressSpace(AddressSpace);
160
161 /*
162 * Do the actual page out work.
163 */
164 Status = MmWritePageSectionView(AddressSpace, MemoryArea,
165 Address, PageOp);
166 }
167 else if (Type == MEMORY_AREA_VIRTUAL_MEMORY)
168 {
169 PageOp = MmGetPageOp(MemoryArea, Address < (PVOID)KERNEL_BASE ? Process->UniqueProcessId : 0,
170 Address, NULL, 0, MM_PAGEOP_PAGEOUT, TRUE);
171
172 if (PageOp == NULL)
173 {
174 MmUnlockAddressSpace(AddressSpace);
175 if (Address < (PVOID)KERNEL_BASE)
176 {
177 ObDereferenceObject(Process);
178 }
179 return(STATUS_UNSUCCESSFUL);
180 }
181
182 /*
183 * Release locks now we have a page op.
184 */
185 MmUnlockAddressSpace(AddressSpace);
186
187 /*
188 * Do the actual page out work.
189 */
190 Status = MmWritePageVirtualMemory(AddressSpace, MemoryArea,
191 Address, PageOp);
192 }
193 else
194 {
195 KEBUGCHECK(0);
196 }
197 if (Address < (PVOID)KERNEL_BASE)
198 {
199 ObDereferenceObject(Process);
200 }
201 return(Status);
202 }
203
204 NTSTATUS
205 MmPageOutPhysicalAddress(PFN_TYPE Page)
206 {
207 PMM_RMAP_ENTRY entry;
208 PMEMORY_AREA MemoryArea;
209 PMADDRESS_SPACE AddressSpace;
210 ULONG Type;
211 PVOID Address;
212 PEPROCESS Process;
213 PMM_PAGEOP PageOp;
214 ULONG Offset;
215 NTSTATUS Status = STATUS_SUCCESS;
216
217 ExAcquireFastMutex(&RmapListLock);
218 entry = MmGetRmapListHeadPage(Page);
219 if (entry == NULL || MmGetLockCountPage(Page) != 0)
220 {
221 ExReleaseFastMutex(&RmapListLock);
222 return(STATUS_UNSUCCESSFUL);
223 }
224 Process = entry->Process;
225 Address = entry->Address;
226 if ((((ULONG)Address) & 0xFFF) != 0)
227 {
228 KEBUGCHECK(0);
229 }
230
231 if (Address < (PVOID)KERNEL_BASE)
232 {
233 Status = ObReferenceObjectByPointer(Process, PROCESS_ALL_ACCESS, NULL, KernelMode);
234 ExReleaseFastMutex(&RmapListLock);
235 if (!NT_SUCCESS(Status))
236 {
237 return Status;
238 }
239 AddressSpace = &Process->AddressSpace;
240 }
241 else
242 {
243 ExReleaseFastMutex(&RmapListLock);
244 AddressSpace = MmGetKernelAddressSpace();
245 }
246
247 MmLockAddressSpace(AddressSpace);
248 MemoryArea = MmOpenMemoryAreaByAddress(AddressSpace, Address);
249 if (MemoryArea == NULL || MemoryArea->DeleteInProgress)
250 {
251 MmUnlockAddressSpace(AddressSpace);
252 if (Address < (PVOID)KERNEL_BASE)
253 {
254 ObDereferenceObject(Process);
255 }
256 return(STATUS_UNSUCCESSFUL);
257 }
258 Type = MemoryArea->Type;
259 if (Type == MEMORY_AREA_SECTION_VIEW)
260 {
261 Offset = (ULONG)((char*)Address - (ULONG)MemoryArea->BaseAddress);
262
263 /*
264 * Get or create a pageop
265 */
266 PageOp = MmGetPageOp(MemoryArea, 0, 0,
267 MemoryArea->Data.SectionData.Segment,
268 Offset, MM_PAGEOP_PAGEOUT, TRUE);
269 if (PageOp == NULL)
270 {
271 MmUnlockAddressSpace(AddressSpace);
272 if (Address < (PVOID)KERNEL_BASE)
273 {
274 ObDereferenceObject(Process);
275 }
276 return(STATUS_UNSUCCESSFUL);
277 }
278
279 /*
280 * Release locks now we have a page op.
281 */
282 MmUnlockAddressSpace(AddressSpace);
283
284 /*
285 * Do the actual page out work.
286 */
287 Status = MmPageOutSectionView(AddressSpace, MemoryArea,
288 Address, PageOp);
289 }
290 else if (Type == MEMORY_AREA_VIRTUAL_MEMORY)
291 {
292 PageOp = MmGetPageOp(MemoryArea, Address < (PVOID)KERNEL_BASE ? Process->UniqueProcessId : 0,
293 Address, NULL, 0, MM_PAGEOP_PAGEOUT, TRUE);
294 if (PageOp == NULL)
295 {
296 MmUnlockAddressSpace(AddressSpace);
297 if (Address < (PVOID)KERNEL_BASE)
298 {
299 ObDereferenceObject(Process);
300 }
301 return(STATUS_UNSUCCESSFUL);
302 }
303
304 /*
305 * Release locks now we have a page op.
306 */
307 MmUnlockAddressSpace(AddressSpace);
308
309 /*
310 * Do the actual page out work.
311 */
312 Status = MmPageOutVirtualMemory(AddressSpace, MemoryArea,
313 Address, PageOp);
314 }
315 else
316 {
317 KEBUGCHECK(0);
318 }
319 if (Address < (PVOID)KERNEL_BASE)
320 {
321 ObDereferenceObject(Process);
322 }
323 return(Status);
324 }
325
326 VOID
327 MmSetCleanAllRmaps(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 MmSetCleanPage(current_entry->Process, current_entry->Address);
341 current_entry = current_entry->Next;
342 }
343 ExReleaseFastMutex(&RmapListLock);
344 }
345
346 VOID
347 MmSetDirtyAllRmaps(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 DPRINT1("MmIsDirtyRmap: No rmaps.\n");
356 KEBUGCHECK(0);
357 }
358 while (current_entry != NULL)
359 {
360 MmSetDirtyPage(current_entry->Process, current_entry->Address);
361 current_entry = current_entry->Next;
362 }
363 ExReleaseFastMutex(&RmapListLock);
364 }
365
366 BOOL
367 MmIsDirtyPageRmap(PFN_TYPE Page)
368 {
369 PMM_RMAP_ENTRY current_entry;
370
371 ExAcquireFastMutex(&RmapListLock);
372 current_entry = MmGetRmapListHeadPage(Page);
373 if (current_entry == NULL)
374 {
375 ExReleaseFastMutex(&RmapListLock);
376 return(FALSE);
377 }
378 while (current_entry != NULL)
379 {
380 if (MmIsDirtyPage(current_entry->Process, current_entry->Address))
381 {
382 ExReleaseFastMutex(&RmapListLock);
383 return(TRUE);
384 }
385 current_entry = current_entry->Next;
386 }
387 ExReleaseFastMutex(&RmapListLock);
388 return(FALSE);
389 }
390
391 VOID
392 MmInsertRmap(PFN_TYPE Page, PEPROCESS Process,
393 PVOID Address)
394 {
395 PMM_RMAP_ENTRY current_entry;
396 PMM_RMAP_ENTRY new_entry;
397
398 Address = (PVOID)PAGE_ROUND_DOWN(Address);
399
400 new_entry = ExAllocateFromNPagedLookasideList(&RmapLookasideList);
401 if (new_entry == NULL)
402 {
403 KEBUGCHECK(0);
404 }
405 new_entry->Address = Address;
406 new_entry->Process = Process;
407
408 if (MmGetPfnForProcess(Process, Address) != Page)
409 {
410 DPRINT1("Insert rmap (%d, 0x%.8X) 0x%.8X which doesn't match physical "
411 "address 0x%.8X\n", Process->UniqueProcessId, Address,
412 MmGetPfnForProcess(Process, Address) << PAGE_SHIFT,
413 Page << PAGE_SHIFT);
414 KEBUGCHECK(0);
415 }
416
417 ExAcquireFastMutex(&RmapListLock);
418 current_entry = MmGetRmapListHeadPage(Page);
419 new_entry->Next = current_entry;
420 MmSetRmapListHeadPage(Page, new_entry);
421 ExReleaseFastMutex(&RmapListLock);
422 }
423
424 VOID
425 MmDeleteAllRmaps(PFN_TYPE Page, PVOID Context,
426 VOID (*DeleteMapping)(PVOID Context, PEPROCESS Process,
427 PVOID Address))
428 {
429 PMM_RMAP_ENTRY current_entry;
430 PMM_RMAP_ENTRY previous_entry;
431
432 ExAcquireFastMutex(&RmapListLock);
433 current_entry = MmGetRmapListHeadPage(Page);
434 if (current_entry == NULL)
435 {
436 DPRINT1("MmDeleteAllRmaps: No rmaps.\n");
437 KEBUGCHECK(0);
438 }
439 MmSetRmapListHeadPage(Page, NULL);
440 while (current_entry != NULL)
441 {
442 previous_entry = current_entry;
443 current_entry = current_entry->Next;
444 if (DeleteMapping)
445 {
446 DeleteMapping(Context, previous_entry->Process,
447 previous_entry->Address);
448 }
449 ExFreeToNPagedLookasideList(&RmapLookasideList, previous_entry);
450 }
451 ExReleaseFastMutex(&RmapListLock);
452 }
453
454 VOID
455 MmDeleteRmap(PFN_TYPE Page, PEPROCESS Process,
456 PVOID Address)
457 {
458 PMM_RMAP_ENTRY current_entry, previous_entry;
459
460 ExAcquireFastMutex(&RmapListLock);
461 previous_entry = NULL;
462 current_entry = MmGetRmapListHeadPage(Page);
463 while (current_entry != NULL)
464 {
465 if (current_entry->Process == Process &&
466 current_entry->Address == Address)
467 {
468 if (previous_entry == NULL)
469 {
470 MmSetRmapListHeadPage(Page, current_entry->Next);
471 }
472 else
473 {
474 previous_entry->Next = current_entry->Next;
475 }
476 ExReleaseFastMutex(&RmapListLock);
477 ExFreeToNPagedLookasideList(&RmapLookasideList, current_entry);
478 return;
479 }
480 previous_entry = current_entry;
481 current_entry = current_entry->Next;
482 }
483 KEBUGCHECK(0);
484 }