/*++ Copyright (c) 1989 Microsoft Corporation Module Name: pfndec.c Abstract: This module contains the routines to decrement the share count and the reference counts within the Page Frame Database. Author: Lou Perazzoli (loup) 5-Apr-1989 Revision History: --*/ #include "mi.h" ULONG MmFrontOfList; VOID FASTCALL MiDecrementShareCount2 ( IN ULONG PageFrameIndex ) /*++ Routine Description: This routine decrements the share count within the PFN element for the specified physical page. If the share count becomes zero the corresponding PTE is coverted to the transition state and the reference count is decremented and the ValidPte count of the PTEframe is decremented. Arguments: PageFrameIndex - Supplies the physical page number of which to decrement the share count. Return Value: None. Environment: Must be holding the PFN database mutex with APC's disabled. --*/ { MMPTE TempPte; PMMPTE PointerPte; PMMPFN Pfn1; PMMPFN PfnX; KIRQL OldIrql; ASSERT ((PageFrameIndex <= MmHighestPhysicalPage) && (PageFrameIndex > 0)); Pfn1 = MI_PFN_ELEMENT (PageFrameIndex); Pfn1->u2.ShareCount -= 1; ASSERT (Pfn1->u2.ShareCount < 0xF000000); if (Pfn1->u2.ShareCount == 0) { // // The share count is now zero, decrement the reference count // for the PFN element and turn the referenced PTE into // the transition state if it refers to a prototype PTE. // PTEs which are not prototype PTE do not need to be placed // into transition as they are placed in transition when // they are removed from the working set (working set free routine). // // // If the PTE referenced by this PFN element is actually // a prototype PTE, it must be mapped into hyperspace and // then operated on. // if (Pfn1->u3.e1.PrototypePte == 1) { OldIrql = 99; if (MmIsAddressValid (Pfn1->PteAddress)) { PointerPte = Pfn1->PteAddress; } else { // // The address is not valid in this process, map it into // hyperspace so it can be operated upon. // PointerPte = (PMMPTE)MiMapPageInHyperSpace(Pfn1->PteFrame, &OldIrql); PointerPte = (PMMPTE)((ULONG)PointerPte + MiGetByteOffset(Pfn1->PteAddress)); } TempPte = *PointerPte; MI_MAKE_VALID_PTE_TRANSITION (TempPte, Pfn1->OriginalPte.u.Soft.Protection); *PointerPte = TempPte; if (OldIrql != 99) { MiUnmapPageInHyperSpace (OldIrql); } // // There is no need to flush the translation buffer at this // time as we only invalidated a prototytpe PTE. // } // // Change the page location to inactive (from active and valid). // Pfn1->u3.e1.PageLocation = TransitionPage; // // Decrement the reference count as the share count is now zero. // MiDecrementReferenceCount (PageFrameIndex); } return; } #if 0 VOID FASTCALL MiDecrementShareCount ( IN ULONG PageFrameIndex ) /*++ Routine Description: This routine decrements the share count within the PFN element for the specified physical page. If the share count becomes zero the corresponding PTE is coverted to the transition state and the reference count is decremented and the ValidPte count of the PTEframe is decremented. Arguments: PageFrameIndex - Supplies the physical page number of which to decrement the share count. Return Value: None. Environment: Must be holding the PFN database mutex with APC's disabled. --*/ { MMPTE TempPte; PMMPTE PointerPte; PMMPFN Pfn1; PMMPFN PfnX; KIRQL OldIrql; ASSERT ((PageFrameIndex <= MmHighestPhysicalPage) && (PageFrameIndex > 0)); Pfn1 = MI_PFN_ELEMENT (PageFrameIndex); Pfn1->u2.ShareCount -= 1; ASSERT (Pfn1->u2.ShareCount < 0xF000000); if (Pfn1->u2.ShareCount == 0) { // // The share count is now zero, decrement the reference count // for the PFN element and turn the referenced PTE into // the transition state if it refers to a prototype PTE. // PTEs which are not prototype PTE do not need to be placed // into transition as they are placed in transition when // they are removed from the working set (working set free routine). // // // If the PTE referenced by this PFN element is actually // a prototype PTE, it must be mapped into hyperspace and // then operated on. // if (Pfn1->u3.e1.PrototypePte == 1) { OldIrql = 99; if (MmIsAddressValid (Pfn1->PteAddress)) { PointerPte = Pfn1->PteAddress; } else { // // The address is not valid in this process, map it into // hyperspace so it can be operated upon. // PointerPte = (PMMPTE)MiMapPageInHyperSpace(Pfn1->PteFrame, &OldIrql); PointerPte = (PMMPTE)((ULONG)PointerPte + MiGetByteOffset(Pfn1->PteAddress)); } TempPte = *PointerPte; MI_MAKE_VALID_PTE_TRANSITION (TempPte, Pfn1->OriginalPte.u.Soft.Protection); *PointerPte = TempPte; if (OldIrql != 99) { MiUnmapPageInHyperSpace (OldIrql); } // // There is no need to flush the translation buffer at this // time as we only invalidated a prototytpe PTE. // } // // Change the page location to inactive (from active and valid). // Pfn1->u3.e1.PageLocation = TransitionPage; // // Decrement the valid pte count for the PteFrame page. // #if DBG PfnX = MI_PFN_ELEMENT (Pfn1->PteFrame); ASSERT (PfnX->u2.ShareCount != 0); #endif //DBG // // Decrement the reference count as the share count is now zero. // MiDecrementReferenceCount (PageFrameIndex); } return; } VOID FASTCALL MiDecrementShareCountOnly ( IN ULONG PageFrameIndex ) /*++ Routine Description: This routine decrements the share count within the PFN element for the specified physical page. If the share count becomes zero the corresponding PTE is coverted to the transition state and the reference count is decremented; the ValidPte count of the corresponding PTE FRAME field is not updated. Arguments: PageFrameIndex - Supplies the physical page number of which to decrement the share count. Return Value: None. Environment: Must be holding the PFN database mutex with APC's disabled. --*/ { MMPTE TempPte; PMMPTE PointerPte; PMMPFN Pfn1; KIRQL OldIrql; ASSERT ((PageFrameIndex <= MmHighestPhysicalPage) && (PageFrameIndex > 0)); Pfn1 = MI_PFN_ELEMENT (PageFrameIndex); Pfn1->u2.ShareCount -= 1; ASSERT (Pfn1->u2.ShareCount < 0xF000000); if (Pfn1->u2.ShareCount == 0) { // // The share count is now zero, decrement the reference count // for the PFN element and turn the referenced PTE into // the transition state if it refers to a prototype PTE. // PTEs which are not prototype PTE do not need to be placed // into transition as they are placed in transition when // they are removed from the working set (working set free routine). // // // If the PTE referenced by this PFN element is actually // a prototype PTE, it must be mapped into hyperspace and // then operated on. // if (Pfn1->u3.e1.PrototypePte == 1) { OldIrql = 99; if (MmIsAddressValid (Pfn1->PteAddress)) { PointerPte = Pfn1->PteAddress; } else { // // The address is not valid in this process, map it into // hyperspace so it can be operated upon. // PointerPte = (PMMPTE)MiMapPageInHyperSpace(Pfn1->PteFrame, &OldIrql); PointerPte = (PMMPTE)((ULONG)PointerPte + MiGetByteOffset(Pfn1->PteAddress)); } TempPte = *PointerPte; MI_MAKE_VALID_PTE_TRANSITION (TempPte, Pfn1->OriginalPte.u.Soft.Protection); *PointerPte = TempPte; if (OldIrql != 99) { MiUnmapPageInHyperSpace (OldIrql); } // // There is no need to flush the translation buffer at this // time as we only invalidated a prototytpe PTE. // } // // Change the page location to inactive (from active and valid). // Pfn1->u3.e1.PageLocation = TransitionPage; // // Decrement the reference count as the share count is now zero. // MiDecrementReferenceCount (PageFrameIndex); } return; } VOID FASTCALL MiDecrementShareAndValidCount ( IN ULONG PageFrameIndex ) /*++ Routine Description: This routine decrements the share count and the valid count within the PFN element for the specified physical page. If the share count becomes zero the corresponding PTE is coverted to the transition state and the reference count is decremented. Arguments: PageFrameIndex - Supplies the physical page number of which to decrement the share count. Return Value: None. Environment: Must be holding the PFN database mutex with APC's disabled. --*/ { MMPTE TempPte; PMMPTE PointerPte; PMMPFN Pfn1; KIRQL OldIrql; Pfn1 = MI_PFN_ELEMENT (PageFrameIndex); ASSERT ((PageFrameIndex <= MmHighestPhysicalPage) && (PageFrameIndex > 0)); ASSERT (Pfn1->u2.ShareCount != 0); Pfn1->u2.ShareCount -= 1; ASSERT (Pfn1->u2.ShareCount < (ULONG)0xF000000); if (Pfn1->u2.ShareCount == 0) { // // The share count is now zero, decrement the reference count // for the PFN element and turn the referenced PTE into // the transition state if it refers to a prototype PTE. // PTEs which are not prototype PTE do not need to be placed // into transition as they are placed in transition when // they are removed from the working set (working set free routine). // // // If the PTE referenced by this PFN element is actually // a prototype PTE, it must be mapped into hyperspace and // then operated on. // if (Pfn1->u3.e1.PrototypePte) { OldIrql = 99; if (MmIsAddressValid (Pfn1->PteAddress)) { PointerPte = Pfn1->PteAddress; } else { // // The address is not valid in this process, map it into // hyperspace so it can be operated upon. // PointerPte = (PMMPTE)MiMapPageInHyperSpace(Pfn1->PteFrame, &OldIrql); PointerPte = (PMMPTE)((ULONG)PointerPte + MiGetByteOffset(Pfn1->PteAddress)); } TempPte = *PointerPte; MI_MAKE_VALID_PTE_TRANSITION (TempPte, Pfn1->OriginalPte.u.Soft.Protection); *PointerPte = TempPte; if (OldIrql != 99) { MiUnmapPageInHyperSpace (OldIrql); } // // There is no need to flush the translation buffer at this // time as we only invalidated a prototytpe PTE. // } // // Change the page location to inactive (from active and valid). // Pfn1->u3.e1.PageLocation = TransitionPage; // // Decrement the reference count as the share count is now zero. // KdPrint(("MM:shareandvalid decremented share to 0 pteframe = %lx\n", Pfn1->PteFrame)); MiDecrementReferenceCount (PageFrameIndex); } return; } #endif // 0 VOID FASTCALL MiDecrementReferenceCount ( IN ULONG PageFrameIndex ) /*++ Routine Description: This routine decrements the reference count for the specified page. If the reference count becomes zero, the page is placed on the appropriate list (free, modified, standby or bad). If the page is placed on the free or standby list, the number of available pages is incremented and if it transitions from zero to one, the available page event is set. Arguments: PageFrameIndex - Supplies the physical page number of which to decrement the reference count. Return Value: none. Environment: Must be holding the PFN database mutex with APC's disabled. --*/ { PMMPFN Pfn1; MM_PFN_LOCK_ASSERT(); ASSERT ((PageFrameIndex <= MmHighestPhysicalPage) && (PageFrameIndex > 0)); Pfn1 = MI_PFN_ELEMENT (PageFrameIndex); ASSERT (Pfn1->u3.e2.ReferenceCount != 0); Pfn1->u3.e2.ReferenceCount -= 1; if (Pfn1->u3.e2.ReferenceCount != 0) { // // The reference count is not zero, return. // return; } // // The reference count is now zero, put the page on some // list. // if (Pfn1->u2.ShareCount != 0) { KeBugCheckEx (PFN_LIST_CORRUPT, 7, PageFrameIndex, Pfn1->u2.ShareCount, 0); return; } ASSERT (Pfn1->u3.e1.PageLocation != ActiveAndValid); #ifdef PARITY if (Pfn1->u3.e1.ParityError == 1) { // // This page has parity (ECC) errors, put it on the // bad page list. // MiInsertPageInList (MmPageLocationList[BadPageList], PageFrameIndex); return; } #endif if (MI_IS_PFN_DELETED (Pfn1)) { // // There is no referenced PTE for this page, delete // the page file space, if any, and place // the page on the free list. // MiReleasePageFileSpace (Pfn1->OriginalPte); MiInsertPageInList (MmPageLocationList[FreePageList], PageFrameIndex); return; } // // Place the page on the modified or standby list depending // on the state of the modify bit in the PFN element. // if (Pfn1->u3.e1.Modified == 1) { MiInsertPageInList (MmPageLocationList[ModifiedPageList], PageFrameIndex); } else { if (!MmFrontOfList) { MiInsertPageInList (MmPageLocationList[StandbyPageList], PageFrameIndex); } else { MiInsertStandbyListAtFront (PageFrameIndex); } } return; }