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