From 4993239ac6abaab04e8ffd6cadf1457ca18df9c7 Mon Sep 17 00:00:00 2001 From: Sir Richard Date: Sat, 31 Mar 2012 20:08:34 +0000 Subject: [PATCH] [NTOS]: Add initial support for standby page list and modified page list in the current page list routines. Add support for prototype PTEs in a few more cases, including handling of transition pages. Should not affect any of the current code as those lists/transition pages aren't yet used. svn path=/trunk/; revision=56291 --- reactos/ntoskrnl/mm/ARM3/pfnlist.c | 315 +++++++++++++++++++++++------ 1 file changed, 258 insertions(+), 57 deletions(-) diff --git a/reactos/ntoskrnl/mm/ARM3/pfnlist.c b/reactos/ntoskrnl/mm/ARM3/pfnlist.c index 30631433703..55c41e239ac 100644 --- a/reactos/ntoskrnl/mm/ARM3/pfnlist.c +++ b/reactos/ntoskrnl/mm/ARM3/pfnlist.c @@ -723,41 +723,37 @@ MiInsertPageInFreeList(IN PFN_NUMBER PageFrameIndex) #endif } -/* Note: This function is hardcoded only for the zeroed page list, for now */ VOID -NTAPI -MiInsertPageInList(IN PMMPFNLIST ListHead, - IN PFN_NUMBER PageFrameIndex) +FASTCALL +MiInsertStandbyListAtFront(IN PFN_NUMBER PageFrameIndex) { + PMMPFNLIST ListHead; PFN_NUMBER Flink; PMMPFN Pfn1, Pfn2; - MMLISTS ListName; - PMMCOLOR_TABLES ColorHead; - ULONG Color; - - /* For free pages, use MiInsertPageInFreeList */ - ASSERT(ListHead != &MmFreePageListHead); /* Make sure the lock is held */ + DPRINT1("Inserting page: %lx into standby list !\n", PageFrameIndex); ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); /* Make sure the PFN is valid */ - ASSERT((PageFrameIndex) && + ASSERT((PageFrameIndex != 0) && (PageFrameIndex <= MmHighestPhysicalPage) && (PageFrameIndex >= MmLowestPhysicalPage)); - /* Page should be unused */ + /* Grab the PFN and validate it is the right kind of PFN being inserted */ Pfn1 = MI_PFN_ELEMENT(PageFrameIndex); + ASSERT(Pfn1->u4.MustBeCached == 0); ASSERT(Pfn1->u3.e2.ReferenceCount == 0); + ASSERT(Pfn1->u3.e1.PrototypePte == 1); ASSERT(Pfn1->u3.e1.Rom != 1); - /* Only used for zero pages in ReactOS */ - ListName = ListHead->ListName; - ASSERT(ListName == ZeroedPageList); - ListHead->Total++; + /* One more transition page on a list */ + MmTransitionSharedPages++; - /* Don't handle bad pages yet yet */ - ASSERT(Pfn1->u3.e1.RemovalRequested == 0); + /* Get the standby page list and increment its count */ + ListHead = &MmStandbyPageListByPriority [Pfn1->u4.Priority]; + ASSERT_LIST_INVARIANT(ListHead); + ListHead->Total++; /* Make the head of the list point to this page now */ Flink = ListHead->Flink; @@ -781,7 +777,7 @@ MiInsertPageInList(IN PMMPFNLIST ListHead, } /* Move the page onto its new location */ - Pfn1->u3.e1.PageLocation = ListName; + Pfn1->u3.e1.PageLocation = StandbyPageList; /* One more page on the system */ MmAvailablePages++; @@ -797,49 +793,210 @@ MiInsertPageInList(IN PMMPFNLIST ListHead, /* Otherwise check if we reached the high threshold and signal the event */ KeSetEvent(MiHighMemoryEvent, 0, FALSE); } +} - /* Sanity checks */ - ASSERT(ListName == ZeroedPageList); - ASSERT(Pfn1->u4.InPageError == 0); +VOID +NTAPI +MiInsertPageInList(IN PMMPFNLIST ListHead, + IN PFN_NUMBER PageFrameIndex) +{ + PFN_NUMBER Flink, LastPage; + PMMPFN Pfn1, Pfn2; + MMLISTS ListName; + PMMCOLOR_TABLES ColorHead; + ULONG Color; - /* Get the page color */ - Color = PageFrameIndex & MmSecondaryColorMask; + /* For free pages, use MiInsertPageInFreeList */ + ASSERT(ListHead != &MmFreePageListHead); - /* Get the list for this color */ - ColorHead = &MmFreePagesByColor[ZeroedPageList][Color]; + /* Make sure the lock is held */ + ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); - /* Get the old head */ - Flink = ColorHead->Flink; + /* Make sure the PFN is valid */ + ASSERT((PageFrameIndex) && + (PageFrameIndex <= MmHighestPhysicalPage) && + (PageFrameIndex >= MmLowestPhysicalPage)); - /* Make this page point back to the list, and point forwards to the old head */ - Pfn1->OriginalPte.u.Long = Flink; - Pfn1->u4.PteFrame = COLORED_LIST_HEAD; + /* Page should be unused */ + Pfn1 = MI_PFN_ELEMENT(PageFrameIndex); + ASSERT(Pfn1->u3.e2.ReferenceCount == 0); + ASSERT(Pfn1->u3.e1.Rom != 1); - /* Set the new head */ - ColorHead->Flink = PageFrameIndex; - - /* Was the head empty? */ - if (Flink != LIST_HEAD) + /* Is a standby or modified page being inserted? */ + ListName = ListHead->ListName; + if ((ListName == StandbyPageList) || (ListName == ModifiedPageList)) { - /* No, so make the old head point to this page */ - Pfn2 = MI_PFN_ELEMENT(Flink); - Pfn2->u4.PteFrame = PageFrameIndex; + /* If the page is in transition, it must also be a prototype page */ + if ((Pfn1->OriginalPte.u.Soft.Prototype == 0) && + (Pfn1->OriginalPte.u.Soft.Transition == 1)) + { + /* Crash the system on inconsistency */ + KeBugCheckEx(MEMORY_MANAGEMENT, 0x8888, 0, 0, 0); + } + } + + /* Standby pages are prioritized, so we need to get the real head */ + if (ListHead == &MmStandbyPageListHead) + { + /* Obviously the prioritized list should still have the same name */ + ListHead = &MmStandbyPageListByPriority[Pfn1->u4.Priority]; + ASSERT(ListHead->ListName == ListName); + } + + /* Increment the list count */ + ListHead->Total++; + + /* Is a modified page being inserted? */ + if (ListHead == &MmModifiedPageListHead) + { + /* For now, only single-prototype pages should end up in this path */ + DPRINT1("Modified page being added: %lx\n", PageFrameIndex); + ASSERT(Pfn1->OriginalPte.u.Soft.Prototype == 0); + + /* Modified pages are colored when they are selected for page file */ + ListHead = &MmModifiedPageListByColor[0]; + ASSERT (ListHead->ListName == ListName); + ListHead->Total++; + + /* Increment the number of paging file modified pages */ + MmTotalPagesForPagingFile++; + } + + /* Don't handle bad pages yet yet */ + ASSERT(Pfn1->u3.e1.RemovalRequested == 0); + + /* Zero pages go to the head, all other pages go to the end */ + if (ListName == ZeroedPageList) + { + /* Make the head of the list point to this page now */ + Flink = ListHead->Flink; + ListHead->Flink = PageFrameIndex; + + /* Make the page point to the previous head, and back to the list */ + Pfn1->u1.Flink = Flink; + Pfn1->u2.Blink = LIST_HEAD; + + /* Was the list empty? */ + if (Flink != LIST_HEAD) + { + /* It wasn't, so update the backlink of the previous head page */ + Pfn2 = MI_PFN_ELEMENT(Flink); + Pfn2->u2.Blink = PageFrameIndex; + } + else + { + /* It was empty, so have it loop back around to this new page */ + ListHead->Blink = PageFrameIndex; + } } else { - /* Yes, make it loop back to this page */ - ColorHead->Blink = (PVOID)Pfn1; + /* Get the last page on the list */ + LastPage = ListHead->Blink; + if (LastPage != LIST_HEAD) + { + /* Link us with the previous page, so we're at the end now */ + MI_PFN_ELEMENT(LastPage)->u1.Flink = PageFrameIndex; + } + else + { + /* The list is empty, so we are the first page */ + ListHead->Flink = PageFrameIndex; + } + + /* Now make the list head point back to us (since we go at the end) */ + ListHead->Blink = PageFrameIndex; + ASSERT_LIST_INVARIANT(ListHead); + + /* And initialize our own list pointers */ + Pfn1->u1.Flink = LIST_HEAD; + Pfn1->u2.Blink = LastPage; } - /* One more paged on the colored list */ - ColorHead->Count++; + /* Move the page onto its new location */ + Pfn1->u3.e1.PageLocation = ListName; + + /* For zero/free pages, we also have to handle the colored lists */ + if (ListName <= StandbyPageList) + { + /* One more page on the system */ + MmAvailablePages++; + + /* Check if we've reached the configured low memory threshold */ + if (MmAvailablePages == MmLowMemoryThreshold) + { + /* Clear the event, because now we're ABOVE the threshold */ + KeClearEvent(MiLowMemoryEvent); + } + else if (MmAvailablePages == MmHighMemoryThreshold) + { + /* Otherwise check if we reached the high threshold and signal the event */ + KeSetEvent(MiHighMemoryEvent, 0, FALSE); + } + + /* Sanity checks */ + ASSERT(ListName == ZeroedPageList); + ASSERT(Pfn1->u4.InPageError == 0); + + /* Get the page color */ + Color = PageFrameIndex & MmSecondaryColorMask; + + /* Get the list for this color */ + ColorHead = &MmFreePagesByColor[ZeroedPageList][Color]; + + /* Get the old head */ + Flink = ColorHead->Flink; + + /* Make this page point back to the list, and point forwards to the old head */ + Pfn1->OriginalPte.u.Long = Flink; + Pfn1->u4.PteFrame = COLORED_LIST_HEAD; + + /* Set the new head */ + ColorHead->Flink = PageFrameIndex; + + /* Was the head empty? */ + if (Flink != LIST_HEAD) + { + /* No, so make the old head point to this page */ + Pfn2 = MI_PFN_ELEMENT(Flink); + Pfn2->u4.PteFrame = PageFrameIndex; + } + else + { + /* Yes, make it loop back to this page */ + ColorHead->Blink = (PVOID)Pfn1; + } + + /* One more paged on the colored list */ + ColorHead->Count++; #if MI_TRACE_PFNS - //ASSERT(MI_PFN_CURRENT_USAGE == MI_USAGE_NOT_SET); - Pfn1->PfnUsage = MI_USAGE_FREE_PAGE; - MI_PFN_CURRENT_USAGE = MI_USAGE_NOT_SET; - RtlZeroMemory(Pfn1->ProcessName, 16); + //ASSERT(MI_PFN_CURRENT_USAGE == MI_USAGE_NOT_SET); + Pfn1->PfnUsage = MI_USAGE_FREE_PAGE; + MI_PFN_CURRENT_USAGE = MI_USAGE_NOT_SET; + RtlZeroMemory(Pfn1->ProcessName, 16); #endif + } + else if (ListName == ModifiedPageList) + { + /* In ARM3, page must be destined for page file, and not yet written out */ + ASSERT(Pfn1->OriginalPte.u.Soft.Prototype == 0); + ASSERT(Pfn1->OriginalPte.u.Soft.PageFileHigh == 0); + + /* One more transition page */ + ASSERT(Pfn1->u3.e1.PrototypePte == 1); + MmTransitionSharedPages++; + + /* Increment the number of per-process modified pages */ + PsGetCurrentProcess()->ModifiedPageCount++; + + /* FIXME: Wake up modified page writer if there are not enough free pages */ + } + else if (ListName == ModifiedNoWritePageList) + { + /* This list is not yet implemented */ + ASSERT(FALSE); + } } VOID @@ -1020,6 +1177,9 @@ NTAPI MiDecrementShareCount(IN PMMPFN Pfn1, IN PFN_NUMBER PageFrameIndex) { + PMMPTE PointerPte; + MMPTE TempPte; + ASSERT(PageFrameIndex > 0); ASSERT(MI_PFN_ELEMENT(PageFrameIndex) != NULL); ASSERT(Pfn1 == MI_PFN_ELEMENT(PageFrameIndex)); @@ -1041,8 +1201,27 @@ MiDecrementShareCount(IN PMMPFN Pfn1, ASSERT(Pfn1->u2.ShareCount < 0xF000000); if (!--Pfn1->u2.ShareCount) { - /* ReactOS does not handle these */ - ASSERT(Pfn1->u3.e1.PrototypePte == 0); + /* Was this a prototype PTE? */ + if (Pfn1->u3.e1.PrototypePte) + { + /* Grab the PTE address and make sure it's in prototype pool */ + PointerPte = Pfn1->PteAddress; + ASSERT((PointerPte >= (PMMPTE)MmPagedPoolStart) && (PointerPte <= (PMMPTE)MmPagedPoolEnd)); + + /* The PTE that backs it should also be valdi */ + PointerPte = MiAddressToPte(PointerPte); + ASSERT(PointerPte->u.Hard.Valid == 1); + + /* Get the original prototype PTE and turn it into a transition PTE */ + PointerPte = Pfn1->PteAddress; + TempPte = *PointerPte; + TempPte.u.Soft.Transition = 1; + TempPte.u.Soft.Valid = 0; + TempPte.u.Soft.Prototype = 0; + TempPte.u.Soft.Protection = Pfn1->OriginalPte.u.Soft.Protection; + MI_WRITE_INVALID_PTE(PointerPte, TempPte); + DPRINT1("Marking PTE: %p as transition (%p - %lx)\n", PointerPte, Pfn1, MiGetPfnEntryIndex(Pfn1)); + } /* Put the page in transition */ Pfn1->u3.e1.PageLocation = TransitionPage; @@ -1054,8 +1233,24 @@ MiDecrementShareCount(IN PMMPFN Pfn1, ASSERT(Pfn1->u3.e2.ReferenceCount != 0); if (Pfn1->u3.e2.ReferenceCount == 1) { - /* In ReactOS, this path should always be hit with a deleted PFN */ - ASSERT((MI_IS_PFN_DELETED(Pfn1) == TRUE) || (Pfn1->u3.e1.PrototypePte == 1)); + /* Is there still a PFN for this page? */ + if (MI_IS_PFN_DELETED(Pfn1) == TRUE) + { + /* Clear the last reference */ + Pfn1->u3.e2.ReferenceCount = 0; + ASSERT(Pfn1->OriginalPte.u.Soft.Prototype == 0); + + /* Mark the page temporarily as valid, we're going to make it free soon */ + Pfn1->u3.e1.PageLocation = ActiveAndValid; + + /* Bring it back into the free list */ + MiInsertPageInFreeList(PageFrameIndex); + } + else + { + /* PFN not yet deleted, drop a ref count */ + MiDecrementReferenceCount(Pfn1, PageFrameIndex); + } /* Clear the last reference */ Pfn1->u3.e2.ReferenceCount = 0; @@ -1110,12 +1305,18 @@ MiDecrementReferenceCount(IN PMMPFN Pfn1, return; } - /* We don't have a modified list yet */ - ASSERT(Pfn1->u3.e1.Modified == 0); - ASSERT(Pfn1->u3.e1.RemovalRequested == 0); - - /* FIXME: Normally it would go on the standby list, but we're pushing it on the free list */ - MiInsertPageInFreeList(PageFrameIndex); + /* Check to see which list this page should go into */ + if (Pfn1->u3.e1.Modified == 1) + { + /* Push it into the modified page list */ + MiInsertPageInList(&MmModifiedPageListHead, PageFrameIndex); + } + else + { + /* Otherwise, insert this page into the standby list */ + ASSERT(Pfn1->u3.e1.RemovalRequested == 0); + MiInsertStandbyListAtFront(PageFrameIndex); + } } VOID