2 * PROJECT: ReactOS Kernel
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: ntoskrnl/mm/ARM3/pfnlist.c
5 * PURPOSE: ARM Memory Manager PFN List Manipulation
6 * PROGRAMMERS: ReactOS Portable Systems Group
9 /* INCLUDES *******************************************************************/
15 #define MODULE_INVOLVED_IN_ARM3
16 #include <mm/ARM3/miarm.h>
19 #define ASSERT_LIST_INVARIANT(x) \
21 ASSERT(((x)->Total == 0 && \
22 (x)->Flink == LIST_HEAD && \
23 (x)->Blink == LIST_HEAD) || \
25 (x)->Flink != LIST_HEAD && \
26 (x)->Blink != LIST_HEAD)); \
29 #define ASSERT_LIST_INVARIANT(x)
32 /* GLOBALS ********************************************************************/
36 ULONG MmSystemPageColor
;
38 ULONG MmTransitionSharedPages
;
39 ULONG MmTotalPagesForPagingFile
;
41 MMPFNLIST MmZeroedPageListHead
= {0, ZeroedPageList
, LIST_HEAD
, LIST_HEAD
};
42 MMPFNLIST MmFreePageListHead
= {0, FreePageList
, LIST_HEAD
, LIST_HEAD
};
43 MMPFNLIST MmStandbyPageListHead
= {0, StandbyPageList
, LIST_HEAD
, LIST_HEAD
};
44 MMPFNLIST MmStandbyPageListByPriority
[8];
45 MMPFNLIST MmModifiedPageListHead
= {0, ModifiedPageList
, LIST_HEAD
, LIST_HEAD
};
46 MMPFNLIST MmModifiedPageListByColor
[1] = {{0, ModifiedPageList
, LIST_HEAD
, LIST_HEAD
}};
47 MMPFNLIST MmModifiedNoWritePageListHead
= {0, ModifiedNoWritePageList
, LIST_HEAD
, LIST_HEAD
};
48 MMPFNLIST MmBadPageListHead
= {0, BadPageList
, LIST_HEAD
, LIST_HEAD
};
49 MMPFNLIST MmRomPageListHead
= {0, StandbyPageList
, LIST_HEAD
, LIST_HEAD
};
51 PMMPFNLIST MmPageLocationList
[] =
53 &MmZeroedPageListHead
,
55 &MmStandbyPageListHead
,
56 &MmModifiedPageListHead
,
57 &MmModifiedNoWritePageListHead
,
63 ULONG MI_PFN_CURRENT_USAGE
;
64 CHAR MI_PFN_CURRENT_PROCESS_NAME
[16] = "None yet";
66 /* FUNCTIONS ******************************************************************/
70 MiIncrementAvailablePages(
73 /* Increment available pages */
76 /* Check if we've reached the configured low memory threshold */
77 if (MmAvailablePages
== MmLowMemoryThreshold
)
79 /* Clear the event, because now we're ABOVE the threshold */
80 KeClearEvent(MiLowMemoryEvent
);
82 else if (MmAvailablePages
== MmHighMemoryThreshold
)
84 /* Otherwise check if we reached the high threshold and signal the event */
85 KeSetEvent(MiHighMemoryEvent
, 0, FALSE
);
91 MiDecrementAvailablePages(
94 ASSERT(MmAvailablePages
> 0);
96 /* See if we hit any thresholds */
97 if (MmAvailablePages
== MmHighMemoryThreshold
)
99 /* Clear the high memory event */
100 KeClearEvent(MiHighMemoryEvent
);
102 else if (MmAvailablePages
== MmLowMemoryThreshold
)
104 /* Signal the low memory event */
105 KeSetEvent(MiLowMemoryEvent
, 0, FALSE
);
110 if (MmAvailablePages
< MmMinimumFreePages
)
112 /* FIXME: Should wake up the MPW and working set manager, if we had one */
114 DPRINT1("Running low on pages: %lu remaining\n", MmAvailablePages
);
116 /* Call RosMm and see if it can release any pages for us */
117 MmRebalanceMemoryConsumers();
123 MiZeroPhysicalPage(IN PFN_NUMBER PageFrameIndex
)
126 PVOID VirtualAddress
;
127 PEPROCESS Process
= PsGetCurrentProcess();
129 /* Map in hyperspace, then wipe it using XMMI or MEMSET */
130 VirtualAddress
= MiMapPageInHyperSpace(Process
, PageFrameIndex
, &OldIrql
);
131 ASSERT(VirtualAddress
);
132 KeZeroPages(VirtualAddress
, PAGE_SIZE
);
133 MiUnmapPageInHyperSpace(Process
, VirtualAddress
, OldIrql
);
138 MiUnlinkFreeOrZeroedPage(IN PMMPFN Entry
)
140 PFN_NUMBER OldFlink
, OldBlink
;
144 PMMCOLOR_TABLES ColorTable
;
147 /* Make sure the PFN lock is held */
148 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
150 /* Make sure the PFN entry isn't in-use */
151 ASSERT(Entry
->u3
.e1
.WriteInProgress
== 0);
152 ASSERT(Entry
->u3
.e1
.ReadInProgress
== 0);
154 /* Find the list for this entry, make sure it's the free or zero list */
155 ListHead
= MmPageLocationList
[Entry
->u3
.e1
.PageLocation
];
156 ListName
= ListHead
->ListName
;
157 ASSERT(ListHead
!= NULL
);
158 ASSERT(ListName
<= FreePageList
);
159 ASSERT_LIST_INVARIANT(ListHead
);
161 /* Remove one count */
162 ASSERT(ListHead
->Total
!= 0);
165 /* Get the forward and back pointers */
166 OldFlink
= Entry
->u1
.Flink
;
167 OldBlink
= Entry
->u2
.Blink
;
169 /* Check if the next entry is the list head */
170 if (OldFlink
!= LIST_HEAD
)
172 /* It is not, so set the backlink of the actual entry, to our backlink */
173 MI_PFN_ELEMENT(OldFlink
)->u2
.Blink
= OldBlink
;
177 /* Set the list head's backlink instead */
178 ListHead
->Blink
= OldBlink
;
181 /* Check if the back entry is the list head */
182 if (OldBlink
!= LIST_HEAD
)
184 /* It is not, so set the backlink of the actual entry, to our backlink */
185 MI_PFN_ELEMENT(OldBlink
)->u1
.Flink
= OldFlink
;
189 /* Set the list head's backlink instead */
190 ListHead
->Flink
= OldFlink
;
193 /* Get the page color */
194 OldBlink
= MiGetPfnEntryIndex(Entry
);
195 Color
= OldBlink
& MmSecondaryColorMask
;
197 /* Get the first page on the color list */
198 ColorTable
= &MmFreePagesByColor
[ListName
][Color
];
200 /* Check if this was was actually the head */
201 OldFlink
= ColorTable
->Flink
;
202 if (OldFlink
== OldBlink
)
204 /* Make the table point to the next page this page was linking to */
205 ColorTable
->Flink
= Entry
->OriginalPte
.u
.Long
;
206 if (ColorTable
->Flink
!= LIST_HEAD
)
208 /* And make the previous link point to the head now */
209 MI_PFN_ELEMENT(ColorTable
->Flink
)->u4
.PteFrame
= COLORED_LIST_HEAD
;
213 /* And if that page was the head, loop the list back around */
214 ColorTable
->Blink
= (PVOID
)LIST_HEAD
;
219 /* This page shouldn't be pointing back to the head */
220 ASSERT(Entry
->u4
.PteFrame
!= COLORED_LIST_HEAD
);
222 /* Make the back link point to whoever the next page is */
223 Pfn1
= MI_PFN_ELEMENT(Entry
->u4
.PteFrame
);
224 Pfn1
->OriginalPte
.u
.Long
= Entry
->OriginalPte
.u
.Long
;
226 /* Check if this page was pointing to the head */
227 if (Entry
->OriginalPte
.u
.Long
!= LIST_HEAD
)
229 /* Make the back link point to the head */
230 Pfn1
= MI_PFN_ELEMENT(Entry
->OriginalPte
.u
.Long
);
231 Pfn1
->u4
.PteFrame
= Entry
->u4
.PteFrame
;
235 /* Then the table is directly back pointing to this page now */
236 ColorTable
->Blink
= Pfn1
;
240 /* One less colored page */
241 ASSERT(ColorTable
->Count
>= 1);
245 Entry
->OriginalPte
.u
.Long
= 0;
247 /* We are not on a list anymore */
248 Entry
->u1
.Flink
= Entry
->u2
.Blink
= 0;
249 ASSERT_LIST_INVARIANT(ListHead
);
251 /* Decrement number of available pages */
252 MiDecrementAvailablePages();
255 ASSERT(MI_PFN_CURRENT_USAGE
!= MI_USAGE_NOT_SET
);
256 Entry
->PfnUsage
= MI_PFN_CURRENT_USAGE
;
257 memcpy(Entry
->ProcessName
, MI_PFN_CURRENT_PROCESS_NAME
, 16);
258 // MI_PFN_CURRENT_USAGE = MI_USAGE_NOT_SET;
259 // memcpy(MI_PFN_CURRENT_PROCESS_NAME, "Not Set", 16);
265 MiUnlinkPageFromList(IN PMMPFN Pfn
)
268 PFN_NUMBER OldFlink
, OldBlink
;
270 /* Make sure the PFN lock is held */
271 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
273 /* ARM3 should only call this for dead pages */
274 ASSERT(Pfn
->u3
.e2
.ReferenceCount
== 0);
276 /* Transition pages are supposed to be standby/modified/nowrite */
277 ListHead
= MmPageLocationList
[Pfn
->u3
.e1
.PageLocation
];
278 ASSERT(ListHead
->ListName
>= StandbyPageList
);
280 /* Check if this was standby, or modified */
281 if (ListHead
== &MmStandbyPageListHead
)
283 /* Should not be a ROM page */
284 ASSERT(Pfn
->u3
.e1
.Rom
== 0);
286 /* Get the exact list */
287 ListHead
= &MmStandbyPageListByPriority
[Pfn
->u4
.Priority
];
289 /* Decrement number of available pages */
290 MiDecrementAvailablePages();
292 /* Decrease transition page counter */
293 ASSERT(Pfn
->u3
.e1
.PrototypePte
== 1); /* Only supported ARM3 case */
294 MmTransitionSharedPages
--;
296 else if (ListHead
== &MmModifiedPageListHead
)
298 /* Only shared memory (page-file backed) modified pages are supported */
299 ASSERT(Pfn
->OriginalPte
.u
.Soft
.Prototype
== 0);
301 /* Decrement the counters */
303 MmTotalPagesForPagingFile
--;
305 /* Pick the correct colored list */
306 ListHead
= &MmModifiedPageListByColor
[0];
308 /* Decrease transition page counter */
309 MmTransitionSharedPages
--;
311 else if (ListHead
== &MmModifiedNoWritePageListHead
)
313 /* List not yet supported */
317 /* Nothing should be in progress and the list should not be empty */
318 ASSERT(Pfn
->u3
.e1
.WriteInProgress
== 0);
319 ASSERT(Pfn
->u3
.e1
.ReadInProgress
== 0);
320 ASSERT(ListHead
->Total
!= 0);
322 /* Get the forward and back pointers */
323 OldFlink
= Pfn
->u1
.Flink
;
324 OldBlink
= Pfn
->u2
.Blink
;
326 /* Check if the next entry is the list head */
327 if (OldFlink
!= LIST_HEAD
)
329 /* It is not, so set the backlink of the actual entry, to our backlink */
330 MI_PFN_ELEMENT(OldFlink
)->u2
.Blink
= OldBlink
;
334 /* Set the list head's backlink instead */
335 ListHead
->Blink
= OldBlink
;
338 /* Check if the back entry is the list head */
339 if (OldBlink
!= LIST_HEAD
)
341 /* It is not, so set the backlink of the actual entry, to our backlink */
342 MI_PFN_ELEMENT(OldBlink
)->u1
.Flink
= OldFlink
;
346 /* Set the list head's backlink instead */
347 ListHead
->Flink
= OldFlink
;
351 Pfn
->OriginalPte
.u
.Long
= 0;
353 /* We are not on a list anymore */
354 Pfn
->u1
.Flink
= Pfn
->u2
.Blink
= 0;
356 /* Remove one entry from the list */
359 ASSERT_LIST_INVARIANT(ListHead
);
364 MiRemovePageByColor(IN PFN_NUMBER PageIndex
,
370 PFN_NUMBER OldFlink
, OldBlink
;
371 USHORT OldColor
, OldCache
;
372 PMMCOLOR_TABLES ColorTable
;
374 /* Make sure PFN lock is held */
375 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
376 ASSERT(Color
< MmSecondaryColors
);
378 /* Get the PFN entry */
379 Pfn1
= MI_PFN_ELEMENT(PageIndex
);
380 ASSERT(Pfn1
->u3
.e1
.RemovalRequested
== 0);
381 ASSERT(Pfn1
->u3
.e1
.Rom
== 0);
383 /* Capture data for later */
384 OldColor
= Pfn1
->u3
.e1
.PageColor
;
385 OldCache
= Pfn1
->u3
.e1
.CacheAttribute
;
387 /* Could be either on free or zero list */
388 ListHead
= MmPageLocationList
[Pfn1
->u3
.e1
.PageLocation
];
389 ASSERT_LIST_INVARIANT(ListHead
);
390 ListName
= ListHead
->ListName
;
391 ASSERT(ListName
<= FreePageList
);
396 /* Get the forward and back pointers */
397 OldFlink
= Pfn1
->u1
.Flink
;
398 OldBlink
= Pfn1
->u2
.Blink
;
400 /* Check if the next entry is the list head */
401 if (OldFlink
!= LIST_HEAD
)
403 /* It is not, so set the backlink of the actual entry, to our backlink */
404 MI_PFN_ELEMENT(OldFlink
)->u2
.Blink
= OldBlink
;
408 /* Set the list head's backlink instead */
409 ListHead
->Blink
= OldBlink
;
412 /* Check if the back entry is the list head */
413 if (OldBlink
!= LIST_HEAD
)
415 /* It is not, so set the backlink of the actual entry, to our backlink */
416 MI_PFN_ELEMENT(OldBlink
)->u1
.Flink
= OldFlink
;
420 /* Set the list head's backlink instead */
421 ListHead
->Flink
= OldFlink
;
424 /* We are not on a list anymore */
425 ASSERT_LIST_INVARIANT(ListHead
);
426 Pfn1
->u1
.Flink
= Pfn1
->u2
.Blink
= 0;
428 /* Zero flags but restore color and cache */
429 Pfn1
->u3
.e2
.ShortFlags
= 0;
430 Pfn1
->u3
.e1
.PageColor
= OldColor
;
431 Pfn1
->u3
.e1
.CacheAttribute
= OldCache
;
433 /* Get the first page on the color list */
434 ASSERT(Color
< MmSecondaryColors
);
435 ColorTable
= &MmFreePagesByColor
[ListName
][Color
];
436 ASSERT(ColorTable
->Count
>= 1);
438 /* Set the forward link to whoever we were pointing to */
439 ColorTable
->Flink
= Pfn1
->OriginalPte
.u
.Long
;
441 /* Get the first page on the color list */
442 if (ColorTable
->Flink
== LIST_HEAD
)
444 /* This is the beginning of the list, so set the sentinel value */
445 ColorTable
->Blink
= (PVOID
)LIST_HEAD
;
449 /* The list is empty, so we are the first page */
450 MI_PFN_ELEMENT(ColorTable
->Flink
)->u4
.PteFrame
= COLORED_LIST_HEAD
;
457 Pfn1
->OriginalPte
.u
.Long
= 0;
459 /* Decrement number of available pages */
460 MiDecrementAvailablePages();
463 //ASSERT(MI_PFN_CURRENT_USAGE != MI_USAGE_NOT_SET);
464 Pfn1
->PfnUsage
= MI_PFN_CURRENT_USAGE
;
465 memcpy(Pfn1
->ProcessName
, MI_PFN_CURRENT_PROCESS_NAME
, 16);
466 //MI_PFN_CURRENT_USAGE = MI_USAGE_NOT_SET;
467 //memcpy(MI_PFN_CURRENT_PROCESS_NAME, "Not Set", 16);
470 /* Return the page */
476 MiRemoveAnyPage(IN ULONG Color
)
478 PFN_NUMBER PageIndex
;
481 /* Make sure PFN lock is held and we have pages */
482 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
483 ASSERT(MmAvailablePages
!= 0);
484 ASSERT(Color
< MmSecondaryColors
);
486 /* Check the colored free list */
487 PageIndex
= MmFreePagesByColor
[FreePageList
][Color
].Flink
;
488 if (PageIndex
== LIST_HEAD
)
490 /* Check the colored zero list */
491 PageIndex
= MmFreePagesByColor
[ZeroedPageList
][Color
].Flink
;
492 if (PageIndex
== LIST_HEAD
)
494 /* Check the free list */
495 ASSERT_LIST_INVARIANT(&MmFreePageListHead
);
496 PageIndex
= MmFreePageListHead
.Flink
;
497 Color
= PageIndex
& MmSecondaryColorMask
;
498 if (PageIndex
== LIST_HEAD
)
500 /* Check the zero list */
501 ASSERT_LIST_INVARIANT(&MmZeroedPageListHead
);
502 PageIndex
= MmZeroedPageListHead
.Flink
;
503 Color
= PageIndex
& MmSecondaryColorMask
;
504 ASSERT(PageIndex
!= LIST_HEAD
);
505 if (PageIndex
== LIST_HEAD
)
507 /* FIXME: Should check the standby list */
508 ASSERT(MmZeroedPageListHead
.Total
== 0);
514 /* Remove the page from its list */
515 PageIndex
= MiRemovePageByColor(PageIndex
, Color
);
518 Pfn1
= MI_PFN_ELEMENT(PageIndex
);
519 ASSERT((Pfn1
->u3
.e1
.PageLocation
== FreePageList
) ||
520 (Pfn1
->u3
.e1
.PageLocation
== ZeroedPageList
));
521 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
== 0);
522 ASSERT(Pfn1
->u2
.ShareCount
== 0);
523 ASSERT_LIST_INVARIANT(&MmFreePageListHead
);
524 ASSERT_LIST_INVARIANT(&MmZeroedPageListHead
);
526 /* Return the page */
532 MiRemoveZeroPage(IN ULONG Color
)
534 PFN_NUMBER PageIndex
;
536 BOOLEAN Zero
= FALSE
;
538 /* Make sure PFN lock is held and we have pages */
539 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
540 ASSERT(MmAvailablePages
!= 0);
541 ASSERT(Color
< MmSecondaryColors
);
543 /* Check the colored zero list */
544 PageIndex
= MmFreePagesByColor
[ZeroedPageList
][Color
].Flink
;
545 if (PageIndex
== LIST_HEAD
)
547 /* Check the zero list */
548 ASSERT_LIST_INVARIANT(&MmZeroedPageListHead
);
549 PageIndex
= MmZeroedPageListHead
.Flink
;
550 if (PageIndex
== LIST_HEAD
)
552 /* This means there's no zero pages, we have to look for free ones */
553 ASSERT(MmZeroedPageListHead
.Total
== 0);
556 /* Check the colored free list */
557 PageIndex
= MmFreePagesByColor
[FreePageList
][Color
].Flink
;
558 if (PageIndex
== LIST_HEAD
)
560 /* Check the free list */
561 ASSERT_LIST_INVARIANT(&MmFreePageListHead
);
562 PageIndex
= MmFreePageListHead
.Flink
;
563 Color
= PageIndex
& MmSecondaryColorMask
;
564 ASSERT(PageIndex
!= LIST_HEAD
);
565 if (PageIndex
== LIST_HEAD
)
567 /* FIXME: Should check the standby list */
568 ASSERT(MmZeroedPageListHead
.Total
== 0);
574 Color
= PageIndex
& MmSecondaryColorMask
;
579 Pfn1
= MI_PFN_ELEMENT(PageIndex
);
580 ASSERT((Pfn1
->u3
.e1
.PageLocation
== FreePageList
) ||
581 (Pfn1
->u3
.e1
.PageLocation
== ZeroedPageList
));
583 /* Remove the page from its list */
584 PageIndex
= MiRemovePageByColor(PageIndex
, Color
);
585 ASSERT(Pfn1
== MI_PFN_ELEMENT(PageIndex
));
587 /* Zero it, if needed */
588 if (Zero
) MiZeroPhysicalPage(PageIndex
);
591 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
== 0);
592 ASSERT(Pfn1
->u2
.ShareCount
== 0);
593 ASSERT_LIST_INVARIANT(&MmFreePageListHead
);
594 ASSERT_LIST_INVARIANT(&MmZeroedPageListHead
);
596 /* Return the page */
602 MiInsertPageInFreeList(IN PFN_NUMBER PageFrameIndex
)
609 PMMCOLOR_TABLES ColorTable
;
611 /* Make sure the page index is valid */
612 ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL
);
613 ASSERT((PageFrameIndex
!= 0) &&
614 (PageFrameIndex
<= MmHighestPhysicalPage
) &&
615 (PageFrameIndex
>= MmLowestPhysicalPage
));
617 /* Get the PFN entry */
618 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
620 /* Sanity checks that a right kind of page is being inserted here */
621 ASSERT(Pfn1
->u4
.MustBeCached
== 0);
622 ASSERT(Pfn1
->u3
.e1
.Rom
!= 1);
623 ASSERT(Pfn1
->u3
.e1
.RemovalRequested
== 0);
624 ASSERT(Pfn1
->u4
.VerifierAllocation
== 0);
625 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
== 0);
627 /* Get the free page list and increment its count */
628 ListHead
= &MmFreePageListHead
;
629 ASSERT_LIST_INVARIANT(ListHead
);
632 /* Get the last page on the list */
633 LastPage
= ListHead
->Blink
;
634 if (LastPage
!= LIST_HEAD
)
636 /* Link us with the previous page, so we're at the end now */
637 MI_PFN_ELEMENT(LastPage
)->u1
.Flink
= PageFrameIndex
;
641 /* The list is empty, so we are the first page */
642 ListHead
->Flink
= PageFrameIndex
;
645 /* Now make the list head point back to us (since we go at the end) */
646 ListHead
->Blink
= PageFrameIndex
;
647 ASSERT_LIST_INVARIANT(ListHead
);
649 /* And initialize our own list pointers */
650 Pfn1
->u1
.Flink
= LIST_HEAD
;
651 Pfn1
->u2
.Blink
= LastPage
;
653 /* Set the list name and default priority */
654 Pfn1
->u3
.e1
.PageLocation
= FreePageList
;
655 Pfn1
->u4
.Priority
= 3;
657 /* Clear some status fields */
658 Pfn1
->u4
.InPageError
= 0;
659 Pfn1
->u4
.AweAllocation
= 0;
661 /* Increment number of available pages */
662 MiIncrementAvailablePages();
664 /* Get the page color */
665 Color
= PageFrameIndex
& MmSecondaryColorMask
;
667 /* Get the first page on the color list */
668 ColorTable
= &MmFreePagesByColor
[FreePageList
][Color
];
669 if (ColorTable
->Flink
== LIST_HEAD
)
671 /* The list is empty, so we are the first page */
672 Pfn1
->u4
.PteFrame
= COLORED_LIST_HEAD
;
673 ColorTable
->Flink
= PageFrameIndex
;
677 /* Get the previous page */
678 Blink
= (PMMPFN
)ColorTable
->Blink
;
680 /* Make it link to us, and link back to it */
681 Blink
->OriginalPte
.u
.Long
= PageFrameIndex
;
682 Pfn1
->u4
.PteFrame
= MiGetPfnEntryIndex(Blink
);
685 /* Now initialize our own list pointers */
686 ColorTable
->Blink
= Pfn1
;
688 /* This page is now the last */
689 Pfn1
->OriginalPte
.u
.Long
= LIST_HEAD
;
691 /* And increase the count in the colored list */
694 /* Notify zero page thread if enough pages are on the free list now */
695 if ((ListHead
->Total
>= 8) && !(MmZeroingPageThreadActive
))
698 MmZeroingPageThreadActive
= TRUE
;
699 KeSetEvent(&MmZeroingPageEvent
, IO_NO_INCREMENT
, FALSE
);
703 Pfn1
->PfnUsage
= MI_USAGE_FREE_PAGE
;
704 RtlZeroMemory(Pfn1
->ProcessName
, 16);
710 MiInsertStandbyListAtFront(IN PFN_NUMBER PageFrameIndex
)
716 /* Make sure the lock is held */
717 DPRINT("Inserting page: %lx into standby list !\n", PageFrameIndex
);
718 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
720 /* Make sure the PFN is valid */
721 ASSERT((PageFrameIndex
!= 0) &&
722 (PageFrameIndex
<= MmHighestPhysicalPage
) &&
723 (PageFrameIndex
>= MmLowestPhysicalPage
));
725 /* Grab the PFN and validate it is the right kind of PFN being inserted */
726 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
727 ASSERT(Pfn1
->u4
.MustBeCached
== 0);
728 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
== 0);
729 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== 1);
730 ASSERT(Pfn1
->u3
.e1
.Rom
!= 1);
732 /* One more transition page on a list */
733 MmTransitionSharedPages
++;
735 /* Get the standby page list and increment its count */
736 ListHead
= &MmStandbyPageListByPriority
[Pfn1
->u4
.Priority
];
737 ASSERT_LIST_INVARIANT(ListHead
);
740 /* Make the head of the list point to this page now */
741 Flink
= ListHead
->Flink
;
742 ListHead
->Flink
= PageFrameIndex
;
744 /* Make the page point to the previous head, and back to the list */
745 Pfn1
->u1
.Flink
= Flink
;
746 Pfn1
->u2
.Blink
= LIST_HEAD
;
748 /* Was the list empty? */
749 if (Flink
!= LIST_HEAD
)
751 /* It wasn't, so update the backlink of the previous head page */
752 Pfn2
= MI_PFN_ELEMENT(Flink
);
753 Pfn2
->u2
.Blink
= PageFrameIndex
;
757 /* It was empty, so have it loop back around to this new page */
758 ListHead
->Blink
= PageFrameIndex
;
761 /* Move the page onto its new location */
762 Pfn1
->u3
.e1
.PageLocation
= StandbyPageList
;
764 /* Increment number of available pages */
765 MiIncrementAvailablePages();
770 MiInsertPageInList(IN PMMPFNLIST ListHead
,
771 IN PFN_NUMBER PageFrameIndex
)
773 PFN_NUMBER Flink
, LastPage
;
776 PMMCOLOR_TABLES ColorHead
;
779 /* For free pages, use MiInsertPageInFreeList */
780 ASSERT(ListHead
!= &MmFreePageListHead
);
782 /* Make sure the lock is held */
783 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
785 /* Make sure the PFN is valid */
786 ASSERT((PageFrameIndex
) &&
787 (PageFrameIndex
<= MmHighestPhysicalPage
) &&
788 (PageFrameIndex
>= MmLowestPhysicalPage
));
790 /* Page should be unused */
791 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
792 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
== 0);
793 ASSERT(Pfn1
->u3
.e1
.Rom
!= 1);
795 /* Is a standby or modified page being inserted? */
796 ListName
= ListHead
->ListName
;
797 if ((ListName
== StandbyPageList
) || (ListName
== ModifiedPageList
))
799 /* If the page is in transition, it must also be a prototype page */
800 if ((Pfn1
->OriginalPte
.u
.Soft
.Prototype
== 0) &&
801 (Pfn1
->OriginalPte
.u
.Soft
.Transition
== 1))
803 /* Crash the system on inconsistency */
804 KeBugCheckEx(MEMORY_MANAGEMENT
, 0x8888, 0, 0, 0);
808 /* Standby pages are prioritized, so we need to get the real head */
809 if (ListHead
== &MmStandbyPageListHead
)
811 /* Obviously the prioritized list should still have the same name */
812 ListHead
= &MmStandbyPageListByPriority
[Pfn1
->u4
.Priority
];
813 ASSERT(ListHead
->ListName
== ListName
);
816 /* Increment the list count */
819 /* Is a modified page being inserted? */
820 if (ListHead
== &MmModifiedPageListHead
)
822 /* For now, only single-prototype pages should end up in this path */
823 DPRINT("Modified page being added: %lx\n", PageFrameIndex
);
824 ASSERT(Pfn1
->OriginalPte
.u
.Soft
.Prototype
== 0);
826 /* Modified pages are colored when they are selected for page file */
827 ListHead
= &MmModifiedPageListByColor
[0];
828 ASSERT (ListHead
->ListName
== ListName
);
831 /* Increment the number of paging file modified pages */
832 MmTotalPagesForPagingFile
++;
835 /* Don't handle bad pages yet yet */
836 ASSERT(Pfn1
->u3
.e1
.RemovalRequested
== 0);
838 /* Zero pages go to the head, all other pages go to the end */
839 if (ListName
== ZeroedPageList
)
841 /* Make the head of the list point to this page now */
842 Flink
= ListHead
->Flink
;
843 ListHead
->Flink
= PageFrameIndex
;
845 /* Make the page point to the previous head, and back to the list */
846 Pfn1
->u1
.Flink
= Flink
;
847 Pfn1
->u2
.Blink
= LIST_HEAD
;
849 /* Was the list empty? */
850 if (Flink
!= LIST_HEAD
)
852 /* It wasn't, so update the backlink of the previous head page */
853 Pfn2
= MI_PFN_ELEMENT(Flink
);
854 Pfn2
->u2
.Blink
= PageFrameIndex
;
858 /* It was empty, so have it loop back around to this new page */
859 ListHead
->Blink
= PageFrameIndex
;
864 /* Get the last page on the list */
865 LastPage
= ListHead
->Blink
;
866 if (LastPage
!= LIST_HEAD
)
868 /* Link us with the previous page, so we're at the end now */
869 MI_PFN_ELEMENT(LastPage
)->u1
.Flink
= PageFrameIndex
;
873 /* The list is empty, so we are the first page */
874 ListHead
->Flink
= PageFrameIndex
;
877 /* Now make the list head point back to us (since we go at the end) */
878 ListHead
->Blink
= PageFrameIndex
;
879 ASSERT_LIST_INVARIANT(ListHead
);
881 /* And initialize our own list pointers */
882 Pfn1
->u1
.Flink
= LIST_HEAD
;
883 Pfn1
->u2
.Blink
= LastPage
;
886 /* Move the page onto its new location */
887 Pfn1
->u3
.e1
.PageLocation
= ListName
;
889 /* For zero/free pages, we also have to handle the colored lists */
890 if (ListName
<= StandbyPageList
)
892 /* Increment number of available pages */
893 MiIncrementAvailablePages();
896 ASSERT(ListName
== ZeroedPageList
);
897 ASSERT(Pfn1
->u4
.InPageError
== 0);
899 /* Get the page color */
900 Color
= PageFrameIndex
& MmSecondaryColorMask
;
902 /* Get the list for this color */
903 ColorHead
= &MmFreePagesByColor
[ZeroedPageList
][Color
];
905 /* Get the old head */
906 Flink
= ColorHead
->Flink
;
908 /* Make this page point back to the list, and point forwards to the old head */
909 Pfn1
->OriginalPte
.u
.Long
= Flink
;
910 Pfn1
->u4
.PteFrame
= COLORED_LIST_HEAD
;
912 /* Set the new head */
913 ColorHead
->Flink
= PageFrameIndex
;
915 /* Was the head empty? */
916 if (Flink
!= LIST_HEAD
)
918 /* No, so make the old head point to this page */
919 Pfn2
= MI_PFN_ELEMENT(Flink
);
920 Pfn2
->u4
.PteFrame
= PageFrameIndex
;
924 /* Yes, make it loop back to this page */
925 ColorHead
->Blink
= (PVOID
)Pfn1
;
928 /* One more paged on the colored list */
932 //ASSERT(MI_PFN_CURRENT_USAGE == MI_USAGE_NOT_SET);
933 Pfn1
->PfnUsage
= MI_USAGE_FREE_PAGE
;
934 MI_PFN_CURRENT_USAGE
= MI_USAGE_NOT_SET
;
935 RtlZeroMemory(Pfn1
->ProcessName
, 16);
938 else if (ListName
== ModifiedPageList
)
940 /* In ARM3, page must be destined for page file, and not yet written out */
941 ASSERT(Pfn1
->OriginalPte
.u
.Soft
.Prototype
== 0);
942 ASSERT(Pfn1
->OriginalPte
.u
.Soft
.PageFileHigh
== 0);
944 /* One more transition page */
945 MmTransitionSharedPages
++;
947 /* Increment the number of per-process modified pages */
948 PsGetCurrentProcess()->ModifiedPageCount
++;
950 /* FIXME: Wake up modified page writer if there are not enough free pages */
952 else if (ListName
== ModifiedNoWritePageList
)
954 /* This list is not yet implemented */
961 MiInitializePfn(IN PFN_NUMBER PageFrameIndex
,
962 IN PMMPTE PointerPte
,
967 PMMPTE PointerPtePte
;
968 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
971 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
972 Pfn1
->PteAddress
= PointerPte
;
974 /* Check if this PFN is part of a valid address space */
975 if (PointerPte
->u
.Hard
.Valid
== 1)
977 /* Only valid from MmCreateProcessAddressSpace path */
978 ASSERT(PsGetCurrentProcess()->Vm
.WorkingSetSize
== 0);
980 /* Make this a demand zero PTE */
981 MI_MAKE_SOFTWARE_PTE(&Pfn1
->OriginalPte
, MM_READWRITE
);
985 /* Copy the PTE data */
986 Pfn1
->OriginalPte
= *PointerPte
;
987 ASSERT(!((Pfn1
->OriginalPte
.u
.Soft
.Prototype
== 0) &&
988 (Pfn1
->OriginalPte
.u
.Soft
.Transition
== 1)));
991 /* Otherwise this is a fresh page -- set it up */
992 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
== 0);
993 Pfn1
->u3
.e2
.ReferenceCount
= 1;
994 Pfn1
->u2
.ShareCount
= 1;
995 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
996 ASSERT(Pfn1
->u3
.e1
.Rom
== 0);
997 Pfn1
->u3
.e1
.Modified
= Modified
;
999 /* Get the page table for the PTE */
1000 PointerPtePte
= MiAddressToPte(PointerPte
);
1001 if (PointerPtePte
->u
.Hard
.Valid
== 0)
1003 /* Make sure the PDE gets paged in properly */
1004 Status
= MiCheckPdeForPagedPool(PointerPte
);
1005 if (!NT_SUCCESS(Status
))
1008 KeBugCheckEx(MEMORY_MANAGEMENT
,
1010 (ULONG_PTR
)PointerPte
,
1011 (ULONG_PTR
)PointerPtePte
->u
.Long
,
1012 (ULONG_PTR
)MiPteToAddress(PointerPte
));
1016 /* Get the PFN for the page table */
1017 PageFrameIndex
= PFN_FROM_PTE(PointerPtePte
);
1018 ASSERT(PageFrameIndex
!= 0);
1019 Pfn1
->u4
.PteFrame
= PageFrameIndex
;
1021 /* Increase its share count so we don't get rid of it */
1022 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
1023 Pfn1
->u2
.ShareCount
++;
1028 MiInitializePfnAndMakePteValid(IN PFN_NUMBER PageFrameIndex
,
1029 IN PMMPTE PointerPte
,
1034 PMMPTE PointerPtePte
;
1035 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
1037 /* PTE must be invalid */
1038 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
1041 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
1042 Pfn1
->PteAddress
= PointerPte
;
1043 Pfn1
->OriginalPte
= DemandZeroPte
;
1045 /* Otherwise this is a fresh page -- set it up */
1046 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
== 0);
1047 Pfn1
->u3
.e2
.ReferenceCount
++;
1048 Pfn1
->u2
.ShareCount
++;
1049 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
1050 ASSERT(Pfn1
->u3
.e1
.Rom
== 0);
1051 Pfn1
->u3
.e1
.Modified
= 1;
1053 /* Get the page table for the PTE */
1054 PointerPtePte
= MiAddressToPte(PointerPte
);
1055 if (PointerPtePte
->u
.Hard
.Valid
== 0)
1057 /* Make sure the PDE gets paged in properly */
1058 Status
= MiCheckPdeForPagedPool(PointerPte
);
1059 if (!NT_SUCCESS(Status
))
1062 KeBugCheckEx(MEMORY_MANAGEMENT
,
1064 (ULONG_PTR
)PointerPte
,
1065 (ULONG_PTR
)PointerPtePte
->u
.Long
,
1066 (ULONG_PTR
)MiPteToAddress(PointerPte
));
1070 /* Get the PFN for the page table */
1071 PageFrameIndex
= PFN_FROM_PTE(PointerPtePte
);
1072 ASSERT(PageFrameIndex
!= 0);
1073 Pfn1
->u4
.PteFrame
= PageFrameIndex
;
1075 /* Increase its share count so we don't get rid of it */
1076 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
1077 Pfn1
->u2
.ShareCount
++;
1079 /* Write valid PTE */
1080 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
1085 MiInitializeAndChargePfn(OUT PPFN_NUMBER PageFrameIndex
,
1086 IN PMMPDE PointerPde
,
1087 IN PFN_NUMBER ContainingPageFrame
,
1088 IN BOOLEAN SessionAllocation
)
1093 /* Use either a global or local PDE */
1094 TempPde
= SessionAllocation
? ValidKernelPdeLocal
: ValidKernelPde
;
1096 /* Lock the PFN database */
1097 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1099 /* Make sure nobody is racing us */
1100 if (PointerPde
->u
.Hard
.Valid
== 1)
1102 /* Return special error if that was the case */
1103 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1104 return STATUS_RETRY
;
1107 /* Grab a zero page and set the PFN, then make it valid */
1108 *PageFrameIndex
= MiRemoveZeroPage(MI_GET_NEXT_COLOR());
1109 TempPde
.u
.Hard
.PageFrameNumber
= *PageFrameIndex
;
1110 MI_WRITE_VALID_PDE(PointerPde
, TempPde
);
1112 /* Initialize the PFN */
1113 MiInitializePfnForOtherProcess(*PageFrameIndex
,
1115 ContainingPageFrame
);
1116 ASSERT(MI_PFN_ELEMENT(*PageFrameIndex
)->u1
.WsIndex
== 0);
1118 /* Release the lock and return success */
1119 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1120 return STATUS_SUCCESS
;
1125 MiDecrementShareCount(IN PMMPFN Pfn1
,
1126 IN PFN_NUMBER PageFrameIndex
)
1131 ASSERT(PageFrameIndex
> 0);
1132 ASSERT(MI_PFN_ELEMENT(PageFrameIndex
) != NULL
);
1133 ASSERT(Pfn1
== MI_PFN_ELEMENT(PageFrameIndex
));
1134 ASSERT(MI_IS_ROS_PFN(Pfn1
) == FALSE
);
1136 /* Page must be in-use */
1137 if ((Pfn1
->u3
.e1
.PageLocation
!= ActiveAndValid
) &&
1138 (Pfn1
->u3
.e1
.PageLocation
!= StandbyPageList
))
1140 /* Otherwise we have PFN corruption */
1141 KeBugCheckEx(PFN_LIST_CORRUPT
,
1144 Pfn1
->u3
.e1
.PageLocation
,
1148 /* Page should at least have one reference */
1149 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
!= 0);
1151 /* Check if the share count is now 0 */
1152 ASSERT(Pfn1
->u2
.ShareCount
< 0xF000000);
1153 if (!--Pfn1
->u2
.ShareCount
)
1155 /* Was this a prototype PTE? */
1156 if (Pfn1
->u3
.e1
.PrototypePte
)
1158 /* Grab the PTE address and make sure it's in prototype pool */
1159 PointerPte
= Pfn1
->PteAddress
;
1160 ASSERT((PointerPte
>= (PMMPTE
)MmPagedPoolStart
) && (PointerPte
<= (PMMPTE
)MmPagedPoolEnd
));
1162 /* The PTE that backs it should also be valdi */
1163 PointerPte
= MiAddressToPte(PointerPte
);
1164 ASSERT(PointerPte
->u
.Hard
.Valid
== 1);
1166 /* Get the original prototype PTE and turn it into a transition PTE */
1167 PointerPte
= Pfn1
->PteAddress
;
1168 TempPte
= *PointerPte
;
1169 TempPte
.u
.Soft
.Transition
= 1;
1170 TempPte
.u
.Soft
.Valid
= 0;
1171 TempPte
.u
.Soft
.Prototype
= 0;
1172 TempPte
.u
.Soft
.Protection
= Pfn1
->OriginalPte
.u
.Soft
.Protection
;
1173 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
1174 DPRINT("Marking PTE: %p as transition (%p - %lx)\n", PointerPte
, Pfn1
, MiGetPfnEntryIndex(Pfn1
));
1177 /* Put the page in transition */
1178 Pfn1
->u3
.e1
.PageLocation
= TransitionPage
;
1180 /* PFN lock must be held */
1181 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
1183 if (Pfn1
->u3
.e2
.ReferenceCount
== 1)
1185 /* Is there still a PFN for this page? */
1186 if (MI_IS_PFN_DELETED(Pfn1
))
1188 /* Clear the last reference */
1189 Pfn1
->u3
.e2
.ReferenceCount
= 0;
1190 ASSERT(Pfn1
->OriginalPte
.u
.Soft
.Prototype
== 0);
1192 /* Mark the page temporarily as valid, we're going to make it free soon */
1193 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
1195 /* Bring it back into the free list */
1196 MiInsertPageInFreeList(PageFrameIndex
);
1200 /* PFN not yet deleted, drop a ref count */
1201 MiDecrementReferenceCount(Pfn1
, PageFrameIndex
);
1206 /* Otherwise, just drop the reference count */
1207 InterlockedDecrement16((PSHORT
)&Pfn1
->u3
.e2
.ReferenceCount
);
1214 MiDecrementReferenceCount(IN PMMPFN Pfn1
,
1215 IN PFN_NUMBER PageFrameIndex
)
1217 /* PFN lock must be held */
1218 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
1220 /* Sanity checks on the page */
1221 if (PageFrameIndex
> MmHighestPhysicalPage
||
1222 Pfn1
!= MI_PFN_ELEMENT(PageFrameIndex
) ||
1223 Pfn1
->u3
.e2
.ReferenceCount
== 0 ||
1224 Pfn1
->u3
.e2
.ReferenceCount
>= 2500)
1226 DPRINT1("PageFrameIndex=0x%lx, MmHighestPhysicalPage=0x%lx\n", PageFrameIndex
, MmHighestPhysicalPage
);
1227 DPRINT1("Pfn1=%p, Element=%p, RefCount=%u\n", Pfn1
, MI_PFN_ELEMENT(PageFrameIndex
), Pfn1
->u3
.e2
.ReferenceCount
);
1228 ASSERT(PageFrameIndex
<= MmHighestPhysicalPage
);
1229 ASSERT(Pfn1
== MI_PFN_ELEMENT(PageFrameIndex
));
1230 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
!= 0);
1231 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
< 2500);
1234 /* Dereference the page, bail out if it's still alive */
1235 InterlockedDecrement16((PSHORT
)&Pfn1
->u3
.e2
.ReferenceCount
);
1236 if (Pfn1
->u3
.e2
.ReferenceCount
) return;
1238 /* Nobody should still have reference to this page */
1239 if (Pfn1
->u2
.ShareCount
!= 0)
1241 /* Otherwise something's really wrong */
1242 KeBugCheckEx(PFN_LIST_CORRUPT
, 7, PageFrameIndex
, Pfn1
->u2
.ShareCount
, 0);
1245 /* And it should be lying on some page list */
1246 ASSERT(Pfn1
->u3
.e1
.PageLocation
!= ActiveAndValid
);
1248 /* Did someone set the delete flag? */
1249 if (MI_IS_PFN_DELETED(Pfn1
))
1251 /* Insert it into the free list, there's nothing left to do */
1252 MiInsertPageInFreeList(PageFrameIndex
);
1256 /* Check to see which list this page should go into */
1257 if (Pfn1
->u3
.e1
.Modified
== 1)
1259 /* Push it into the modified page list */
1260 MiInsertPageInList(&MmModifiedPageListHead
, PageFrameIndex
);
1264 /* Otherwise, insert this page into the standby list */
1265 ASSERT(Pfn1
->u3
.e1
.RemovalRequested
== 0);
1266 MiInsertStandbyListAtFront(PageFrameIndex
);
1272 MiInitializePfnForOtherProcess(IN PFN_NUMBER PageFrameIndex
,
1273 IN PVOID PteAddress
,
1274 IN PFN_NUMBER PteFrame
)
1279 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
1280 Pfn1
->PteAddress
= PteAddress
;
1282 /* Make this a software PTE */
1283 MI_MAKE_SOFTWARE_PTE(&Pfn1
->OriginalPte
, MM_READWRITE
);
1285 /* Setup the page */
1286 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
== 0);
1287 Pfn1
->u3
.e2
.ReferenceCount
= 1;
1288 Pfn1
->u2
.ShareCount
= 1;
1289 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
1290 Pfn1
->u3
.e1
.Modified
= TRUE
;
1291 Pfn1
->u4
.InPageError
= FALSE
;
1293 /* Did we get a PFN for the page table */
1297 Pfn1
->u4
.PteFrame
= PteFrame
;
1299 /* Increase its share count so we don't get rid of it */
1300 Pfn1
= MI_PFN_ELEMENT(PteFrame
);
1301 Pfn1
->u2
.ShareCount
++;