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 "../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 MMPFNLIST MmZeroedPageListHead
= {0, ZeroedPageList
, LIST_HEAD
, LIST_HEAD
};
39 MMPFNLIST MmFreePageListHead
= {0, FreePageList
, LIST_HEAD
, LIST_HEAD
};
40 MMPFNLIST MmStandbyPageListHead
= {0, StandbyPageList
, LIST_HEAD
, LIST_HEAD
};
41 MMPFNLIST MmModifiedPageListHead
= {0, ModifiedPageList
, LIST_HEAD
, LIST_HEAD
};
42 MMPFNLIST MmModifiedNoWritePageListHead
= {0, ModifiedNoWritePageList
, LIST_HEAD
, LIST_HEAD
};
43 MMPFNLIST MmBadPageListHead
= {0, BadPageList
, LIST_HEAD
, LIST_HEAD
};
44 MMPFNLIST MmRomPageListHead
= {0, StandbyPageList
, LIST_HEAD
, LIST_HEAD
};
46 PMMPFNLIST MmPageLocationList
[] =
48 &MmZeroedPageListHead
,
50 &MmStandbyPageListHead
,
51 &MmModifiedPageListHead
,
52 &MmModifiedNoWritePageListHead
,
58 ULONG MI_PFN_CURRENT_USAGE
;
59 CHAR MI_PFN_CURRENT_PROCESS_NAME
[16] = "None yet";
61 /* FUNCTIONS ******************************************************************/
65 MiZeroPhysicalPage(IN PFN_NUMBER PageFrameIndex
)
69 PEPROCESS Process
= PsGetCurrentProcess();
71 /* Map in hyperspace, then wipe it using XMMI or MEMSET */
72 VirtualAddress
= MiMapPageInHyperSpace(Process
, PageFrameIndex
, &OldIrql
);
73 ASSERT(VirtualAddress
);
74 KeZeroPages(VirtualAddress
, PAGE_SIZE
);
75 MiUnmapPageInHyperSpace(Process
, VirtualAddress
, OldIrql
);
80 MiUnlinkFreeOrZeroedPage(IN PMMPFN Entry
)
82 PFN_NUMBER OldFlink
, OldBlink
;
86 PMMCOLOR_TABLES ColorTable
;
89 /* Make sure the PFN lock is held */
90 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
92 /* Make sure the PFN entry isn't in-use */
93 ASSERT(Entry
->u3
.e1
.WriteInProgress
== 0);
94 ASSERT(Entry
->u3
.e1
.ReadInProgress
== 0);
96 /* Find the list for this entry, make sure it's the free or zero list */
97 ListHead
= MmPageLocationList
[Entry
->u3
.e1
.PageLocation
];
98 ListName
= ListHead
->ListName
;
99 ASSERT(ListHead
!= NULL
);
100 ASSERT(ListName
<= FreePageList
);
101 ASSERT_LIST_INVARIANT(ListHead
);
103 /* Remove one count */
104 ASSERT(ListHead
->Total
!= 0);
107 /* Get the forward and back pointers */
108 OldFlink
= Entry
->u1
.Flink
;
109 OldBlink
= Entry
->u2
.Blink
;
111 /* Check if the next entry is the list head */
112 if (OldFlink
!= LIST_HEAD
)
114 /* It is not, so set the backlink of the actual entry, to our backlink */
115 MI_PFN_ELEMENT(OldFlink
)->u2
.Blink
= OldBlink
;
119 /* Set the list head's backlink instead */
120 ListHead
->Blink
= OldBlink
;
123 /* Check if the back entry is the list head */
124 if (OldBlink
!= LIST_HEAD
)
126 /* It is not, so set the backlink of the actual entry, to our backlink */
127 MI_PFN_ELEMENT(OldBlink
)->u1
.Flink
= OldFlink
;
131 /* Set the list head's backlink instead */
132 ListHead
->Flink
= OldFlink
;
135 /* Get the page color */
136 OldBlink
= MiGetPfnEntryIndex(Entry
);
137 Color
= OldBlink
& MmSecondaryColorMask
;
139 /* Get the first page on the color list */
140 ColorTable
= &MmFreePagesByColor
[ListName
][Color
];
142 /* Check if this was was actually the head */
143 OldFlink
= ColorTable
->Flink
;
144 if (OldFlink
== OldBlink
)
146 /* Make the table point to the next page this page was linking to */
147 ColorTable
->Flink
= Entry
->OriginalPte
.u
.Long
;
148 if (ColorTable
->Flink
!= LIST_HEAD
)
150 /* And make the previous link point to the head now */
151 MI_PFN_ELEMENT(ColorTable
->Flink
)->u4
.PteFrame
= COLORED_LIST_HEAD
;
155 /* And if that page was the head, loop the list back around */
156 ColorTable
->Blink
= (PVOID
)LIST_HEAD
;
161 /* This page shouldn't be pointing back to the head */
162 ASSERT(Entry
->u4
.PteFrame
!= COLORED_LIST_HEAD
);
164 /* Make the back link point to whoever the next page is */
165 Pfn1
= MI_PFN_ELEMENT(Entry
->u4
.PteFrame
);
166 Pfn1
->OriginalPte
.u
.Long
= Entry
->OriginalPte
.u
.Long
;
168 /* Check if this page was pointing to the head */
169 if (Entry
->OriginalPte
.u
.Long
!= LIST_HEAD
)
171 /* Make the back link point to the head */
172 Pfn1
= MI_PFN_ELEMENT(Entry
->OriginalPte
.u
.Long
);
173 Pfn1
->u4
.PteFrame
= Entry
->u4
.PteFrame
;
177 /* Then the table is directly back pointing to this page now */
178 ColorTable
->Blink
= Pfn1
;
182 /* One less colored page */
183 ASSERT(ColorTable
->Count
>= 1);
187 Entry
->OriginalPte
.u
.Long
= 0;
189 /* We are not on a list anymore */
190 Entry
->u1
.Flink
= Entry
->u2
.Blink
= 0;
191 ASSERT_LIST_INVARIANT(ListHead
);
193 /* See if we hit any thresholds */
194 if (MmAvailablePages
== MmHighMemoryThreshold
)
196 /* Clear the high memory event */
197 KeClearEvent(MiHighMemoryEvent
);
199 else if (MmAvailablePages
== MmLowMemoryThreshold
)
201 /* Signal the low memory event */
202 KeSetEvent(MiLowMemoryEvent
, 0, FALSE
);
206 if (--MmAvailablePages
< MmMinimumFreePages
)
208 /* FIXME: Should wake up the MPW and working set manager, if we had one */
210 DPRINT1("Running low on pages: %d remaining\n", MmAvailablePages
);
212 /* Call RosMm and see if it can release any pages for us */
213 MmRebalanceMemoryConsumers();
217 ASSERT(MI_PFN_CURRENT_USAGE
!= MI_USAGE_NOT_SET
);
218 Entry
->PfnUsage
= MI_PFN_CURRENT_USAGE
;
219 memcpy(Entry
->ProcessName
, MI_PFN_CURRENT_PROCESS_NAME
, 16);
220 // MI_PFN_CURRENT_USAGE = MI_USAGE_NOT_SET;
221 // memcpy(MI_PFN_CURRENT_PROCESS_NAME, "Not Set", 16);
227 MiRemovePageByColor(IN PFN_NUMBER PageIndex
,
233 PFN_NUMBER OldFlink
, OldBlink
;
234 USHORT OldColor
, OldCache
;
235 PMMCOLOR_TABLES ColorTable
;
237 /* Make sure PFN lock is held */
238 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
239 ASSERT(Color
< MmSecondaryColors
);
241 /* Get the PFN entry */
242 Pfn1
= MI_PFN_ELEMENT(PageIndex
);
243 ASSERT(Pfn1
->u3
.e1
.RemovalRequested
== 0);
244 ASSERT(Pfn1
->u3
.e1
.Rom
== 0);
246 /* Capture data for later */
247 OldColor
= Pfn1
->u3
.e1
.PageColor
;
248 OldCache
= Pfn1
->u3
.e1
.CacheAttribute
;
250 /* Could be either on free or zero list */
251 ListHead
= MmPageLocationList
[Pfn1
->u3
.e1
.PageLocation
];
252 ASSERT_LIST_INVARIANT(ListHead
);
253 ListName
= ListHead
->ListName
;
254 ASSERT(ListName
<= FreePageList
);
259 /* Get the forward and back pointers */
260 OldFlink
= Pfn1
->u1
.Flink
;
261 OldBlink
= Pfn1
->u2
.Blink
;
263 /* Check if the next entry is the list head */
264 if (OldFlink
!= LIST_HEAD
)
266 /* It is not, so set the backlink of the actual entry, to our backlink */
267 MI_PFN_ELEMENT(OldFlink
)->u2
.Blink
= OldBlink
;
271 /* Set the list head's backlink instead */
272 ListHead
->Blink
= OldBlink
;
275 /* Check if the back entry is the list head */
276 if (OldBlink
!= LIST_HEAD
)
278 /* It is not, so set the backlink of the actual entry, to our backlink */
279 MI_PFN_ELEMENT(OldBlink
)->u1
.Flink
= OldFlink
;
283 /* Set the list head's backlink instead */
284 ListHead
->Flink
= OldFlink
;
287 /* We are not on a list anymore */
288 ASSERT_LIST_INVARIANT(ListHead
);
289 Pfn1
->u1
.Flink
= Pfn1
->u2
.Blink
= 0;
291 /* Zero flags but restore color and cache */
292 Pfn1
->u3
.e2
.ShortFlags
= 0;
293 Pfn1
->u3
.e1
.PageColor
= OldColor
;
294 Pfn1
->u3
.e1
.CacheAttribute
= OldCache
;
296 /* Get the first page on the color list */
297 ASSERT(Color
< MmSecondaryColors
);
298 ColorTable
= &MmFreePagesByColor
[ListName
][Color
];
299 ASSERT(ColorTable
->Count
>= 1);
301 /* Set the forward link to whoever we were pointing to */
302 ColorTable
->Flink
= Pfn1
->OriginalPte
.u
.Long
;
304 /* Get the first page on the color list */
305 if (ColorTable
->Flink
== LIST_HEAD
)
307 /* This is the beginning of the list, so set the sentinel value */
308 ColorTable
->Blink
= (PVOID
)LIST_HEAD
;
312 /* The list is empty, so we are the first page */
313 MI_PFN_ELEMENT(ColorTable
->Flink
)->u4
.PteFrame
= COLORED_LIST_HEAD
;
320 Pfn1
->OriginalPte
.u
.Long
= 0;
322 /* See if we hit any thresholds */
323 if (MmAvailablePages
== MmHighMemoryThreshold
)
325 /* Clear the high memory event */
326 KeClearEvent(MiHighMemoryEvent
);
328 else if (MmAvailablePages
== MmLowMemoryThreshold
)
330 /* Signal the low memory event */
331 KeSetEvent(MiLowMemoryEvent
, 0, FALSE
);
335 if (--MmAvailablePages
< MmMinimumFreePages
)
337 /* FIXME: Should wake up the MPW and working set manager, if we had one */
339 DPRINT1("Running low on pages: %d remaining\n", MmAvailablePages
);
341 /* Call RosMm and see if it can release any pages for us */
342 MmRebalanceMemoryConsumers();
346 //ASSERT(MI_PFN_CURRENT_USAGE != MI_USAGE_NOT_SET);
347 Pfn1
->PfnUsage
= MI_PFN_CURRENT_USAGE
;
348 memcpy(Pfn1
->ProcessName
, MI_PFN_CURRENT_PROCESS_NAME
, 16);
349 //MI_PFN_CURRENT_USAGE = MI_USAGE_NOT_SET;
350 //memcpy(MI_PFN_CURRENT_PROCESS_NAME, "Not Set", 16);
353 /* Return the page */
359 MiRemoveAnyPage(IN ULONG Color
)
361 PFN_NUMBER PageIndex
;
364 /* Make sure PFN lock is held and we have pages */
365 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
366 ASSERT(MmAvailablePages
!= 0);
367 ASSERT(Color
< MmSecondaryColors
);
369 /* Check the colored free list */
370 PageIndex
= MmFreePagesByColor
[FreePageList
][Color
].Flink
;
371 if (PageIndex
== LIST_HEAD
)
373 /* Check the colored zero list */
374 PageIndex
= MmFreePagesByColor
[ZeroedPageList
][Color
].Flink
;
375 if (PageIndex
== LIST_HEAD
)
377 /* Check the free list */
378 ASSERT_LIST_INVARIANT(&MmFreePageListHead
);
379 PageIndex
= MmFreePageListHead
.Flink
;
380 Color
= PageIndex
& MmSecondaryColorMask
;
381 if (PageIndex
== LIST_HEAD
)
383 /* Check the zero list */
384 ASSERT_LIST_INVARIANT(&MmZeroedPageListHead
);
385 PageIndex
= MmZeroedPageListHead
.Flink
;
386 Color
= PageIndex
& MmSecondaryColorMask
;
387 ASSERT(PageIndex
!= LIST_HEAD
);
388 if (PageIndex
== LIST_HEAD
)
390 /* FIXME: Should check the standby list */
391 ASSERT(MmZeroedPageListHead
.Total
== 0);
397 /* Remove the page from its list */
398 PageIndex
= MiRemovePageByColor(PageIndex
, Color
);
401 Pfn1
= MI_PFN_ELEMENT(PageIndex
);
402 ASSERT((Pfn1
->u3
.e1
.PageLocation
== FreePageList
) ||
403 (Pfn1
->u3
.e1
.PageLocation
== ZeroedPageList
));
404 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
== 0);
405 ASSERT(Pfn1
->u2
.ShareCount
== 0);
406 ASSERT_LIST_INVARIANT(&MmFreePageListHead
);
407 ASSERT_LIST_INVARIANT(&MmZeroedPageListHead
);
409 /* Return the page */
415 MiRemoveZeroPage(IN ULONG Color
)
417 PFN_NUMBER PageIndex
;
419 BOOLEAN Zero
= FALSE
;
421 /* Make sure PFN lock is held and we have pages */
422 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
423 ASSERT(MmAvailablePages
!= 0);
424 ASSERT(Color
< MmSecondaryColors
);
426 /* Check the colored zero list */
427 PageIndex
= MmFreePagesByColor
[ZeroedPageList
][Color
].Flink
;
428 if (PageIndex
== LIST_HEAD
)
430 /* Check the zero list */
431 ASSERT_LIST_INVARIANT(&MmZeroedPageListHead
);
432 PageIndex
= MmZeroedPageListHead
.Flink
;
433 if (PageIndex
== LIST_HEAD
)
435 /* This means there's no zero pages, we have to look for free ones */
436 ASSERT(MmZeroedPageListHead
.Total
== 0);
439 /* Check the colored free list */
440 PageIndex
= MmFreePagesByColor
[FreePageList
][Color
].Flink
;
441 if (PageIndex
== LIST_HEAD
)
443 /* Check the free list */
444 ASSERT_LIST_INVARIANT(&MmFreePageListHead
);
445 PageIndex
= MmFreePageListHead
.Flink
;
446 Color
= PageIndex
& MmSecondaryColorMask
;
447 ASSERT(PageIndex
!= LIST_HEAD
);
448 if (PageIndex
== LIST_HEAD
)
450 /* FIXME: Should check the standby list */
451 ASSERT(MmZeroedPageListHead
.Total
== 0);
457 Color
= PageIndex
& MmSecondaryColorMask
;
462 Pfn1
= MI_PFN_ELEMENT(PageIndex
);
463 ASSERT((Pfn1
->u3
.e1
.PageLocation
== FreePageList
) ||
464 (Pfn1
->u3
.e1
.PageLocation
== ZeroedPageList
));
466 /* Remove the page from its list */
467 PageIndex
= MiRemovePageByColor(PageIndex
, Color
);
468 ASSERT(Pfn1
== MI_PFN_ELEMENT(PageIndex
));
470 /* Zero it, if needed */
471 if (Zero
) MiZeroPhysicalPage(PageIndex
);
474 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
== 0);
475 ASSERT(Pfn1
->u2
.ShareCount
== 0);
476 ASSERT_LIST_INVARIANT(&MmFreePageListHead
);
477 ASSERT_LIST_INVARIANT(&MmZeroedPageListHead
);
479 /* Return the page */
485 MiInsertPageInFreeList(IN PFN_NUMBER PageFrameIndex
)
492 PMMCOLOR_TABLES ColorTable
;
494 /* Make sure the page index is valid */
495 ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL
);
496 ASSERT((PageFrameIndex
!= 0) &&
497 (PageFrameIndex
<= MmHighestPhysicalPage
) &&
498 (PageFrameIndex
>= MmLowestPhysicalPage
));
500 /* Get the PFN entry */
501 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
503 /* Sanity checks that a right kind of page is being inserted here */
504 ASSERT(Pfn1
->u4
.MustBeCached
== 0);
505 ASSERT(Pfn1
->u3
.e1
.Rom
!= 1);
506 ASSERT(Pfn1
->u3
.e1
.RemovalRequested
== 0);
507 ASSERT(Pfn1
->u4
.VerifierAllocation
== 0);
508 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
== 0);
510 /* Get the free page list and increment its count */
511 ListHead
= &MmFreePageListHead
;
512 ASSERT_LIST_INVARIANT(ListHead
);
515 /* Get the last page on the list */
516 LastPage
= ListHead
->Blink
;
517 if (LastPage
!= LIST_HEAD
)
519 /* Link us with the previous page, so we're at the end now */
520 MI_PFN_ELEMENT(LastPage
)->u1
.Flink
= PageFrameIndex
;
524 /* The list is empty, so we are the first page */
525 ListHead
->Flink
= PageFrameIndex
;
528 /* Now make the list head point back to us (since we go at the end) */
529 ListHead
->Blink
= PageFrameIndex
;
530 ASSERT_LIST_INVARIANT(ListHead
);
532 /* And initialize our own list pointers */
533 Pfn1
->u1
.Flink
= LIST_HEAD
;
534 Pfn1
->u2
.Blink
= LastPage
;
536 /* Set the list name and default priority */
537 Pfn1
->u3
.e1
.PageLocation
= FreePageList
;
538 Pfn1
->u4
.Priority
= 3;
540 /* Clear some status fields */
541 Pfn1
->u4
.InPageError
= 0;
542 Pfn1
->u4
.AweAllocation
= 0;
544 /* Increase available pages */
547 /* Check if we've reached the configured low memory threshold */
548 if (MmAvailablePages
== MmLowMemoryThreshold
)
550 /* Clear the event, because now we're ABOVE the threshold */
551 KeClearEvent(MiLowMemoryEvent
);
553 else if (MmAvailablePages
== MmHighMemoryThreshold
)
555 /* Otherwise check if we reached the high threshold and signal the event */
556 KeSetEvent(MiHighMemoryEvent
, 0, FALSE
);
559 /* Get the page color */
560 Color
= PageFrameIndex
& MmSecondaryColorMask
;
562 /* Get the first page on the color list */
563 ColorTable
= &MmFreePagesByColor
[FreePageList
][Color
];
564 if (ColorTable
->Flink
== LIST_HEAD
)
566 /* The list is empty, so we are the first page */
567 Pfn1
->u4
.PteFrame
= COLORED_LIST_HEAD
;
568 ColorTable
->Flink
= PageFrameIndex
;
572 /* Get the previous page */
573 Blink
= (PMMPFN
)ColorTable
->Blink
;
575 /* Make it link to us, and link back to it */
576 Blink
->OriginalPte
.u
.Long
= PageFrameIndex
;
577 Pfn1
->u4
.PteFrame
= MiGetPfnEntryIndex(Blink
);
580 /* Now initialize our own list pointers */
581 ColorTable
->Blink
= Pfn1
;
583 /* This page is now the last */
584 Pfn1
->OriginalPte
.u
.Long
= LIST_HEAD
;
586 /* And increase the count in the colored list */
589 /* Notify zero page thread if enough pages are on the free list now */
590 if ((ListHead
->Total
>= 8) && !(MmZeroingPageThreadActive
))
593 MmZeroingPageThreadActive
= TRUE
;
594 KeSetEvent(&MmZeroingPageEvent
, IO_NO_INCREMENT
, FALSE
);
598 Pfn1
->PfnUsage
= MI_USAGE_FREE_PAGE
;
599 RtlZeroMemory(Pfn1
->ProcessName
, 16);
603 /* Note: This function is hardcoded only for the zeroed page list, for now */
606 MiInsertPageInList(IN PMMPFNLIST ListHead
,
607 IN PFN_NUMBER PageFrameIndex
)
612 PMMCOLOR_TABLES ColorHead
;
615 /* For free pages, use MiInsertPageInFreeList */
616 ASSERT(ListHead
!= &MmFreePageListHead
);
618 /* Make sure the lock is held */
619 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
621 /* Make sure the PFN is valid */
622 ASSERT((PageFrameIndex
) &&
623 (PageFrameIndex
<= MmHighestPhysicalPage
) &&
624 (PageFrameIndex
>= MmLowestPhysicalPage
));
626 /* Page should be unused */
627 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
628 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
== 0);
629 ASSERT(Pfn1
->u3
.e1
.Rom
!= 1);
631 /* Only used for zero pages in ReactOS */
632 ListName
= ListHead
->ListName
;
633 ASSERT(ListName
== ZeroedPageList
);
636 /* Don't handle bad pages yet yet */
637 ASSERT(Pfn1
->u3
.e1
.RemovalRequested
== 0);
639 /* Make the head of the list point to this page now */
640 Flink
= ListHead
->Flink
;
641 ListHead
->Flink
= PageFrameIndex
;
643 /* Make the page point to the previous head, and back to the list */
644 Pfn1
->u1
.Flink
= Flink
;
645 Pfn1
->u2
.Blink
= LIST_HEAD
;
647 /* Was the list empty? */
648 if (Flink
!= LIST_HEAD
)
650 /* It wasn't, so update the backlink of the previous head page */
651 Pfn2
= MI_PFN_ELEMENT(Flink
);
652 Pfn2
->u2
.Blink
= PageFrameIndex
;
656 /* It was empty, so have it loop back around to this new page */
657 ListHead
->Blink
= PageFrameIndex
;
660 /* Move the page onto its new location */
661 Pfn1
->u3
.e1
.PageLocation
= ListName
;
663 /* One more page on the system */
666 /* Check if we've reached the configured low memory threshold */
667 if (MmAvailablePages
== MmLowMemoryThreshold
)
669 /* Clear the event, because now we're ABOVE the threshold */
670 KeClearEvent(MiLowMemoryEvent
);
672 else if (MmAvailablePages
== MmHighMemoryThreshold
)
674 /* Otherwise check if we reached the high threshold and signal the event */
675 KeSetEvent(MiHighMemoryEvent
, 0, FALSE
);
679 ASSERT(ListName
== ZeroedPageList
);
680 ASSERT(Pfn1
->u4
.InPageError
== 0);
682 /* Get the page color */
683 Color
= PageFrameIndex
& MmSecondaryColorMask
;
685 /* Get the list for this color */
686 ColorHead
= &MmFreePagesByColor
[ZeroedPageList
][Color
];
688 /* Get the old head */
689 Flink
= ColorHead
->Flink
;
691 /* Make this page point back to the list, and point forwards to the old head */
692 Pfn1
->OriginalPte
.u
.Long
= Flink
;
693 Pfn1
->u4
.PteFrame
= COLORED_LIST_HEAD
;
695 /* Set the new head */
696 ColorHead
->Flink
= PageFrameIndex
;
698 /* Was the head empty? */
699 if (Flink
!= LIST_HEAD
)
701 /* No, so make the old head point to this page */
702 Pfn2
= MI_PFN_ELEMENT(Flink
);
703 Pfn2
->u4
.PteFrame
= PageFrameIndex
;
707 /* Yes, make it loop back to this page */
708 ColorHead
->Blink
= (PVOID
)Pfn1
;
711 /* One more paged on the colored list */
715 //ASSERT(MI_PFN_CURRENT_USAGE == MI_USAGE_NOT_SET);
716 Pfn1
->PfnUsage
= MI_USAGE_FREE_PAGE
;
717 MI_PFN_CURRENT_USAGE
= MI_USAGE_NOT_SET
;
718 RtlZeroMemory(Pfn1
->ProcessName
, 16);
724 MiInitializePfn(IN PFN_NUMBER PageFrameIndex
,
725 IN PMMPTE PointerPte
,
730 PMMPTE PointerPtePte
;
731 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
734 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
735 Pfn1
->PteAddress
= PointerPte
;
737 /* Check if this PFN is part of a valid address space */
738 if (PointerPte
->u
.Hard
.Valid
== 1)
740 /* Only valid from MmCreateProcessAddressSpace path */
741 ASSERT(PsGetCurrentProcess()->Vm
.WorkingSetSize
== 0);
743 /* Make this a demand zero PTE */
744 MI_MAKE_SOFTWARE_PTE(&Pfn1
->OriginalPte
, MM_READWRITE
);
748 /* Copy the PTE data */
749 Pfn1
->OriginalPte
= *PointerPte
;
750 ASSERT(!((Pfn1
->OriginalPte
.u
.Soft
.Prototype
== 0) &&
751 (Pfn1
->OriginalPte
.u
.Soft
.Transition
== 1)));
754 /* Otherwise this is a fresh page -- set it up */
755 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
== 0);
756 Pfn1
->u3
.e2
.ReferenceCount
= 1;
757 Pfn1
->u2
.ShareCount
= 1;
758 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
759 ASSERT(Pfn1
->u3
.e1
.Rom
== 0);
760 Pfn1
->u3
.e1
.Modified
= Modified
;
762 /* Get the page table for the PTE */
763 PointerPtePte
= MiAddressToPte(PointerPte
);
764 if (PointerPtePte
->u
.Hard
.Valid
== 0)
766 /* Make sure the PDE gets paged in properly */
767 Status
= MiCheckPdeForPagedPool(PointerPte
);
768 if (!NT_SUCCESS(Status
))
771 KeBugCheckEx(MEMORY_MANAGEMENT
,
773 (ULONG_PTR
)PointerPte
,
774 (ULONG_PTR
)PointerPtePte
->u
.Long
,
775 (ULONG_PTR
)MiPteToAddress(PointerPte
));
779 /* Get the PFN for the page table */
780 PageFrameIndex
= PFN_FROM_PTE(PointerPtePte
);
781 ASSERT(PageFrameIndex
!= 0);
782 Pfn1
->u4
.PteFrame
= PageFrameIndex
;
784 /* Increase its share count so we don't get rid of it */
785 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
786 Pfn1
->u2
.ShareCount
++;
791 MiInitializePfnAndMakePteValid(IN PFN_NUMBER PageFrameIndex
,
792 IN PMMPTE PointerPte
,
797 PMMPTE PointerPtePte
;
798 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
800 /* PTE must be invalid */
801 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
804 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
805 Pfn1
->PteAddress
= PointerPte
;
806 Pfn1
->OriginalPte
= DemandZeroPte
;
808 /* Otherwise this is a fresh page -- set it up */
809 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
== 0);
810 Pfn1
->u3
.e2
.ReferenceCount
++;
811 Pfn1
->u2
.ShareCount
++;
812 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
813 ASSERT(Pfn1
->u3
.e1
.Rom
== 0);
814 Pfn1
->u3
.e1
.Modified
= 1;
816 /* Get the page table for the PTE */
817 PointerPtePte
= MiAddressToPte(PointerPte
);
818 if (PointerPtePte
->u
.Hard
.Valid
== 0)
820 /* Make sure the PDE gets paged in properly */
821 Status
= MiCheckPdeForPagedPool(PointerPte
);
822 if (!NT_SUCCESS(Status
))
825 KeBugCheckEx(MEMORY_MANAGEMENT
,
827 (ULONG_PTR
)PointerPte
,
828 (ULONG_PTR
)PointerPtePte
->u
.Long
,
829 (ULONG_PTR
)MiPteToAddress(PointerPte
));
833 /* Get the PFN for the page table */
834 PageFrameIndex
= PFN_FROM_PTE(PointerPtePte
);
835 ASSERT(PageFrameIndex
!= 0);
836 Pfn1
->u4
.PteFrame
= PageFrameIndex
;
838 /* Increase its share count so we don't get rid of it */
839 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
840 Pfn1
->u2
.ShareCount
++;
842 /* Write valid PTE */
843 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
849 MiAllocatePfn(IN PMMPTE PointerPte
,
853 PFN_NUMBER PageFrameIndex
;
856 /* Sanity check that we aren't passed a valid PTE */
857 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
859 /* Make an empty software PTE */
860 MI_MAKE_SOFTWARE_PTE(&TempPte
, MM_READWRITE
);
862 /* Lock the PFN database */
863 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
865 /* Check if we're running low on pages */
866 if (MmAvailablePages
< 128)
868 DPRINT1("Warning, running low on memory: %d pages left\n", MmAvailablePages
);
870 //MiEnsureAvailablePageOrWait(NULL, OldIrql);
872 /* Call RosMm and see if it can release any pages for us */
873 MmRebalanceMemoryConsumers();
877 ASSERT_LIST_INVARIANT(&MmFreePageListHead
);
878 ASSERT_LIST_INVARIANT(&MmZeroedPageListHead
);
879 PageFrameIndex
= MiRemoveAnyPage(MI_GET_NEXT_COLOR());
881 /* Write the software PTE */
882 MI_WRITE_INVALID_PTE(PointerPte
, TempPte
);
883 PointerPte
->u
.Soft
.Protection
|= Protection
;
885 /* Initialize its PFN entry */
886 MiInitializePfn(PageFrameIndex
, PointerPte
, TRUE
);
888 /* Release the PFN lock and return the page */
889 ASSERT_LIST_INVARIANT(&MmFreePageListHead
);
890 ASSERT_LIST_INVARIANT(&MmZeroedPageListHead
);
891 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
892 return PageFrameIndex
;
897 MiDecrementShareCount(IN PMMPFN Pfn1
,
898 IN PFN_NUMBER PageFrameIndex
)
900 ASSERT(PageFrameIndex
> 0);
901 ASSERT(MI_PFN_ELEMENT(PageFrameIndex
) != NULL
);
902 ASSERT(Pfn1
== MI_PFN_ELEMENT(PageFrameIndex
));
903 ASSERT(MI_IS_ROS_PFN(Pfn1
) == FALSE
);
905 /* Page must be in-use */
906 if ((Pfn1
->u3
.e1
.PageLocation
!= ActiveAndValid
) &&
907 (Pfn1
->u3
.e1
.PageLocation
!= StandbyPageList
))
909 /* Otherwise we have PFN corruption */
910 KeBugCheckEx(PFN_LIST_CORRUPT
,
913 Pfn1
->u3
.e1
.PageLocation
,
917 /* Check if the share count is now 0 */
918 ASSERT(Pfn1
->u2
.ShareCount
< 0xF000000);
919 if (!--Pfn1
->u2
.ShareCount
)
921 /* ReactOS does not handle these */
922 ASSERT(Pfn1
->u3
.e1
.PrototypePte
== 0);
924 /* Put the page in transition */
925 Pfn1
->u3
.e1
.PageLocation
= TransitionPage
;
927 /* PFN lock must be held */
928 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
930 /* Page should at least have one reference */
931 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
!= 0);
932 if (Pfn1
->u3
.e2
.ReferenceCount
== 1)
934 if(Pfn1
->u3
.e1
.PrototypePte
== 0)
936 /* In ReactOS, this path should always be hit with a deleted PFN */
937 ASSERT(MI_IS_PFN_DELETED(Pfn1
) == TRUE
);
940 /* Clear the last reference */
941 Pfn1
->u3
.e2
.ReferenceCount
= 0;
942 ASSERT(Pfn1
->OriginalPte
.u
.Soft
.Prototype
== 0);
944 /* Mark the page temporarily as valid, we're going to make it free soon */
945 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
947 /* Bring it back into the free list */
948 MiInsertPageInFreeList(PageFrameIndex
);
952 /* Otherwise, just drop the reference count */
953 InterlockedDecrement16((PSHORT
)&Pfn1
->u3
.e2
.ReferenceCount
);
960 MiDecrementReferenceCount(IN PMMPFN Pfn1
,
961 IN PFN_NUMBER PageFrameIndex
)
963 /* PFN lock must be held */
964 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
966 /* Sanity checks on the page */
967 ASSERT(PageFrameIndex
< MmHighestPhysicalPage
);
968 ASSERT(Pfn1
== MI_PFN_ELEMENT(PageFrameIndex
));
969 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
!= 0);
971 /* Dereference the page, bail out if it's still alive */
972 InterlockedDecrement16((PSHORT
)&Pfn1
->u3
.e2
.ReferenceCount
);
973 if (Pfn1
->u3
.e2
.ReferenceCount
) return;
975 /* Nobody should still have reference to this page */
976 if (Pfn1
->u2
.ShareCount
!= 0)
978 /* Otherwise something's really wrong */
979 KeBugCheckEx(PFN_LIST_CORRUPT
, 7, PageFrameIndex
, Pfn1
->u2
.ShareCount
, 0);
982 /* And it should be lying on some page list */
983 ASSERT(Pfn1
->u3
.e1
.PageLocation
!= ActiveAndValid
);
985 /* Did someone set the delete flag? */
986 if (MI_IS_PFN_DELETED(Pfn1
))
988 /* Insert it into the free list, there's nothing left to do */
989 MiInsertPageInFreeList(PageFrameIndex
);
993 /* We don't have a modified list yet */
994 ASSERT(Pfn1
->u3
.e1
.Modified
== 0);
995 ASSERT(Pfn1
->u3
.e1
.RemovalRequested
== 0);
997 /* FIXME: Normally it would go on the standby list, but we're pushing it on the free list */
998 MiInsertPageInFreeList(PageFrameIndex
);
1003 MiInitializePfnForOtherProcess(IN PFN_NUMBER PageFrameIndex
,
1004 IN PMMPTE PointerPte
,
1005 IN PFN_NUMBER PteFrame
)
1010 Pfn1
= MI_PFN_ELEMENT(PageFrameIndex
);
1011 Pfn1
->PteAddress
= PointerPte
;
1013 /* Make this a software PTE */
1014 MI_MAKE_SOFTWARE_PTE(&Pfn1
->OriginalPte
, MM_READWRITE
);
1016 /* Setup the page */
1017 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
== 0);
1018 Pfn1
->u3
.e2
.ReferenceCount
= 1;
1019 Pfn1
->u2
.ShareCount
= 1;
1020 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
1021 Pfn1
->u3
.e1
.Modified
= TRUE
;
1022 Pfn1
->u4
.InPageError
= FALSE
;
1024 /* Did we get a PFN for the page table */
1028 Pfn1
->u4
.PteFrame
= PteFrame
;
1030 /* Increase its share count so we don't get rid of it */
1031 Pfn1
= MI_PFN_ELEMENT(PteFrame
);
1032 Pfn1
->u2
.ShareCount
++;