update for HEAD-2003091401
[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   PMADDRESS_SPACE AddressSpace;
76   ULONG Type;
77   PVOID Address;
78   PEPROCESS Process;
79   PMM_PAGEOP PageOp;
80   ULONG Offset;
81   NTSTATUS Status = STATUS_SUCCESS;
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 || MemoryArea->DeleteInProgress)
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 = (ULONG)(Address - (ULONG)MemoryArea->BaseAddress);
137
138       /*
139        * Get or create a pageop
140        */
141       PageOp = MmGetPageOp(MemoryArea, 0, 0, 
142                            MemoryArea->Data.SectionData.Segment, 
143                            Offset, MM_PAGEOP_PAGEOUT);
144       if (PageOp == NULL)
145         {
146           DPRINT1("MmGetPageOp failed\n");
147           KEBUGCHECK(0);
148         }
149
150
151       if (PageOp->Thread != PsGetCurrentThread())
152         {
153           MmUnlockAddressSpace(AddressSpace);
154           Status = KeWaitForSingleObject(&PageOp->CompletionEvent,
155                                          0,
156                                          KernelMode,
157                                          FALSE,
158                                          NULL);
159           KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
160           MmReleasePageOp(PageOp);
161           if (Address < (PVOID)KERNEL_BASE)
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 = MmWritePageSectionView(AddressSpace, MemoryArea, 
177                                       Address, PageOp);
178     }
179   else if (Type == MEMORY_AREA_VIRTUAL_MEMORY)
180     {
181       PageOp = MmGetPageOp(MemoryArea, Address < (PVOID)KERNEL_BASE ? Process->UniqueProcessId : 0,
182                            Address, NULL, 0, MM_PAGEOP_PAGEOUT);
183       
184       if (PageOp->Thread != PsGetCurrentThread())
185         {
186           MmReleasePageOp(PageOp);
187           MmUnlockAddressSpace(AddressSpace);
188           if (Address < (PVOID)KERNEL_BASE)
189             {
190               ObDereferenceObject(Process);
191             }
192           return(STATUS_UNSUCCESSFUL);
193         }
194
195       /*
196        * Release locks now we have a page op.
197        */
198       MmUnlockAddressSpace(AddressSpace);
199
200       /*
201        * Do the actual page out work.
202        */
203       Status = MmWritePageVirtualMemory(AddressSpace, MemoryArea, 
204                                         Address, PageOp);
205     }
206   else
207     {
208       KEBUGCHECK(0);
209     }  
210   if (Address < (PVOID)KERNEL_BASE)
211     {
212       ObDereferenceObject(Process);
213     }
214   return(Status);
215 }
216
217 NTSTATUS
218 MmPageOutPhysicalAddress(PHYSICAL_ADDRESS PhysicalAddress)
219 {
220   PMM_RMAP_ENTRY entry;
221   PMEMORY_AREA MemoryArea;
222   PMADDRESS_SPACE AddressSpace;
223   ULONG Type;
224   PVOID Address;
225   PEPROCESS Process;
226   PMM_PAGEOP PageOp;
227   ULONG Offset;
228   NTSTATUS Status = STATUS_SUCCESS;
229
230   ExAcquireFastMutex(&RmapListLock);
231   entry = MmGetRmapListHeadPage(PhysicalAddress);
232   if (entry == NULL)
233     {
234       ExReleaseFastMutex(&RmapListLock);
235       return(STATUS_UNSUCCESSFUL);
236     }
237   Process = entry->Process;
238   Address = entry->Address;
239   if ((((ULONG)Address) & 0xFFF) != 0)
240     {
241       KEBUGCHECK(0);
242     }
243
244   if (Address < (PVOID)KERNEL_BASE)
245     {
246       Status = ObReferenceObjectByPointer(Process, PROCESS_ALL_ACCESS, NULL, KernelMode);
247       ExReleaseFastMutex(&RmapListLock);
248       if (!NT_SUCCESS(Status))
249         {
250           return Status;
251         }
252       AddressSpace = &Process->AddressSpace;
253     }
254   else
255     {
256       ExReleaseFastMutex(&RmapListLock);
257       AddressSpace = MmGetKernelAddressSpace();
258     }
259
260   MmLockAddressSpace(AddressSpace);
261   MemoryArea = MmOpenMemoryAreaByAddress(AddressSpace, Address);
262   if (MemoryArea == NULL || MemoryArea->DeleteInProgress)
263     {
264       MmUnlockAddressSpace(AddressSpace);
265       if (Address < (PVOID)KERNEL_BASE)
266         {
267           ObDereferenceObject(Process);
268         }
269       return(STATUS_UNSUCCESSFUL);
270     }
271   Type = MemoryArea->Type;
272   if (Type == MEMORY_AREA_SECTION_VIEW)
273     {
274       Offset = (ULONG)(Address - (ULONG)MemoryArea->BaseAddress);
275
276       /*
277        * Get or create a pageop
278        */
279       PageOp = MmGetPageOp(MemoryArea, 0, 0, 
280                            MemoryArea->Data.SectionData.Segment, 
281                            Offset, MM_PAGEOP_PAGEOUT);
282       if (PageOp == NULL)
283         {
284           DPRINT1("MmGetPageOp failed\n");
285           KEBUGCHECK(0);
286         }
287
288       if (PageOp->Thread != PsGetCurrentThread())
289         {
290           MmReleasePageOp(PageOp);
291           MmUnlockAddressSpace(AddressSpace);
292           if (Address < (PVOID)KERNEL_BASE)
293             {
294               ObDereferenceObject(Process);
295             }
296           return(STATUS_UNSUCCESSFUL);
297         }
298       
299       /*
300        * Release locks now we have a page op.
301        */
302       MmUnlockAddressSpace(AddressSpace);
303
304       /*
305        * Do the actual page out work.
306        */
307       Status = MmPageOutSectionView(AddressSpace, MemoryArea, 
308                                     Address, PageOp);
309     }
310   else if (Type == MEMORY_AREA_VIRTUAL_MEMORY)
311     {
312       PageOp = MmGetPageOp(MemoryArea, Address < (PVOID)KERNEL_BASE ? Process->UniqueProcessId : 0,
313                            Address, NULL, 0, MM_PAGEOP_PAGEOUT);
314       if (PageOp->Thread != PsGetCurrentThread())
315         {
316           MmUnlockAddressSpace(AddressSpace);
317           Status = KeWaitForSingleObject(&PageOp->CompletionEvent,
318                                          0,
319                                          KernelMode,
320                                          FALSE,
321                                          NULL);
322           KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
323           MmReleasePageOp(PageOp);
324           if (Address < (PVOID)KERNEL_BASE)
325             {
326               ObDereferenceObject(Process);
327             }
328           return(STATUS_UNSUCCESSFUL);
329         }
330
331       /*
332        * Release locks now we have a page op.
333        */
334       MmUnlockAddressSpace(AddressSpace);
335
336       /*
337        * Do the actual page out work.
338        */
339       Status = MmPageOutVirtualMemory(AddressSpace, MemoryArea, 
340                                       Address, PageOp);
341     }
342   else
343     {
344       KEBUGCHECK(0);
345     }
346   if (Address < (PVOID)KERNEL_BASE)
347     {
348       ObDereferenceObject(Process);
349     }
350   return(Status);
351 }
352
353 VOID
354 MmSetCleanAllRmaps(PHYSICAL_ADDRESS PhysicalAddress)
355 {
356   PMM_RMAP_ENTRY current_entry;
357
358   ExAcquireFastMutex(&RmapListLock);
359   current_entry = MmGetRmapListHeadPage(PhysicalAddress);
360   if (current_entry == NULL)
361     {
362       DPRINT1("MmIsDirtyRmap: No rmaps.\n");
363       KEBUGCHECK(0);
364     }
365   while (current_entry != NULL)
366     {      
367       MmSetCleanPage(current_entry->Process, current_entry->Address);
368       current_entry = current_entry->Next;
369     }
370   ExReleaseFastMutex(&RmapListLock);
371 }
372
373 VOID
374 MmSetDirtyAllRmaps(PHYSICAL_ADDRESS PhysicalAddress)
375 {
376   PMM_RMAP_ENTRY current_entry;
377
378   ExAcquireFastMutex(&RmapListLock);
379   current_entry = MmGetRmapListHeadPage(PhysicalAddress);
380   if (current_entry == NULL)
381     {
382       DPRINT1("MmIsDirtyRmap: No rmaps.\n");
383       KEBUGCHECK(0);
384     }
385   while (current_entry != NULL)
386     {      
387       MmSetDirtyPage(current_entry->Process, current_entry->Address);
388       current_entry = current_entry->Next;
389     }
390   ExReleaseFastMutex(&RmapListLock);
391 }
392
393 BOOL
394 MmIsDirtyPageRmap(PHYSICAL_ADDRESS PhysicalAddress)
395 {
396   PMM_RMAP_ENTRY current_entry;
397
398   ExAcquireFastMutex(&RmapListLock);
399   current_entry = MmGetRmapListHeadPage(PhysicalAddress);
400   if (current_entry == NULL)
401     {
402       ExReleaseFastMutex(&RmapListLock);
403       return(FALSE);
404     }
405   while (current_entry != NULL)
406     {      
407       if (MmIsDirtyPage(current_entry->Process, current_entry->Address))
408         {         
409           ExReleaseFastMutex(&RmapListLock);
410           return(TRUE);
411         }
412       current_entry = current_entry->Next;
413     }
414   ExReleaseFastMutex(&RmapListLock);
415   return(FALSE);
416 }
417
418 VOID
419 MmInsertRmap(PHYSICAL_ADDRESS PhysicalAddress, PEPROCESS Process, 
420              PVOID Address)
421 {
422   PMM_RMAP_ENTRY current_entry;
423   PMM_RMAP_ENTRY new_entry;
424
425   Address = (PVOID)PAGE_ROUND_DOWN(Address);
426
427   new_entry = ExAllocateFromNPagedLookasideList(&RmapLookasideList);
428   if (new_entry == NULL)
429     {
430       KEBUGCHECK(0);
431     }
432   new_entry->Address = Address;
433   new_entry->Process = Process;
434
435   if (MmGetPhysicalAddressForProcess(Process, Address).QuadPart != 
436       PhysicalAddress.QuadPart)
437     {
438       DPRINT1("Insert rmap (%d, 0x%.8X) 0x%.8X which doesn't match physical "
439               "address 0x%.8X\n", Process->UniqueProcessId, Address, 
440               MmGetPhysicalAddressForProcess(Process, Address), 
441               PhysicalAddress)
442       KEBUGCHECK(0);
443     }
444
445   ExAcquireFastMutex(&RmapListLock);
446   current_entry = MmGetRmapListHeadPage(PhysicalAddress);
447   new_entry->Next = current_entry;
448   MmSetRmapListHeadPage(PhysicalAddress, new_entry);
449   ExReleaseFastMutex(&RmapListLock);
450 }
451
452 VOID
453 MmDeleteAllRmaps(PHYSICAL_ADDRESS PhysicalAddress, PVOID Context, 
454                  VOID (*DeleteMapping)(PVOID Context, PEPROCESS Process, 
455                                        PVOID Address))
456 {
457   PMM_RMAP_ENTRY current_entry;
458   PMM_RMAP_ENTRY previous_entry;
459
460   ExAcquireFastMutex(&RmapListLock);
461   current_entry = MmGetRmapListHeadPage(PhysicalAddress);
462   if (current_entry == NULL)
463     {
464       DPRINT1("MmDeleteAllRmaps: No rmaps.\n");
465       KEBUGCHECK(0);
466     }
467   MmSetRmapListHeadPage(PhysicalAddress, NULL);
468   while (current_entry != NULL)
469     {
470       previous_entry = current_entry;
471       current_entry = current_entry->Next;
472       if (DeleteMapping)
473         {
474           DeleteMapping(Context, previous_entry->Process, 
475                         previous_entry->Address);
476         }
477       ExFreeToNPagedLookasideList(&RmapLookasideList, previous_entry);
478     }
479   ExReleaseFastMutex(&RmapListLock);
480 }
481
482 VOID
483 MmDeleteRmap(PHYSICAL_ADDRESS PhysicalAddress, PEPROCESS Process, 
484              PVOID Address)
485 {
486   PMM_RMAP_ENTRY current_entry, previous_entry;
487
488   ExAcquireFastMutex(&RmapListLock);
489   previous_entry = NULL;
490   current_entry = MmGetRmapListHeadPage(PhysicalAddress);
491   while (current_entry != NULL)
492     {
493       if (current_entry->Process == Process && 
494           current_entry->Address == Address)
495         {
496           if (previous_entry == NULL)
497             {
498               MmSetRmapListHeadPage(PhysicalAddress, current_entry->Next);
499             }
500           else
501             {
502               previous_entry->Next = current_entry->Next;
503             }
504           ExReleaseFastMutex(&RmapListLock);
505           ExFreeToNPagedLookasideList(&RmapLookasideList, current_entry);
506           return;
507         }
508       previous_entry = current_entry;
509       current_entry = current_entry->Next;
510     }
511   KEBUGCHECK(0);
512 }