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