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