ULONG MiDecommitPages ( IN PVOID StartingAddress, IN PMMPTE EndingPte, IN PEPROCESS Process, IN PMMVAD_SHORT Vad ) /*++ Routine Description: This routine decommits the specified range of pages. Arguments: StartingAddress - Supplies the starting address of the range. EndingPte - Supplies the ending PTE of the range. Process - Supplies the current process. Vad - Supplies the virtual address descriptor which describes the range. Return Value: Value to reduce commitment by for the VAD. Environment: Kernel mode, APCs disabled, AddressCreation mutex held. --*/ { PMMPTE PointerPde; PMMPTE PointerPte; PVOID Va; ULONG CommitReduction; PMMPTE CommitLimitPte; KIRQL OldIrql; PMMPTE ValidPteList[MM_VALID_PTE_SIZE]; ULONG count; WSLE_NUMBER WorkingSetIndex; PMMPFN Pfn1; PMMPFN Pfn2; WSLE_NUMBER Entry; MMWSLENTRY Locked; MMPTE PteContents; PFN_NUMBER PageTableFrameIndex; PVOID UsedPageTableHandle; PETHREAD CurrentThread; count = 0; CommitReduction = 0; if (Vad->u.VadFlags.MemCommit) { CommitLimitPte = MiGetPteAddress (MI_VPN_TO_VA (Vad->EndingVpn)); } else { CommitLimitPte = NULL; } // // Decommit each page by setting the PTE to be explicitly // decommitted. The PTEs cannot be deleted all at once as // this would set the PTEs to zero which would auto-evaluate // as committed if referenced by another thread when a page // table page is being in-paged. // PointerPde = MiGetPdeAddress (StartingAddress); PointerPte = MiGetPteAddress (StartingAddress); Va = StartingAddress; // // Loop through all the PDEs which map this region and ensure that // they exist. If they don't exist create them by touching a // PTE mapped by the PDE. // CurrentThread = PsGetCurrentThread (); LOCK_WS_UNSAFE (CurrentThread, Process); MiMakePdeExistAndMakeValid (PointerPde, Process, MM_NOIRQL); while (PointerPte <= EndingPte) { if (MiIsPteOnPdeBoundary (PointerPte)) { PointerPde = MiGetPdeAddress (Va); if (count != 0) { MiProcessValidPteList (&ValidPteList[0], count); count = 0; } MiMakePdeExistAndMakeValid (PointerPde, Process, MM_NOIRQL); } // // The working set lock is held. No PTEs can go from // invalid to valid or valid to invalid. Transition // PTEs can go from transition to pagefile. // PteContents = *PointerPte; if (PteContents.u.Long != 0) { if (PointerPte->u.Long == MmDecommittedPte.u.Long) { // // This PTE is already decommitted. // CommitReduction += 1; } else { Process->NumberOfPrivatePages -= 1; if (PteContents.u.Hard.Valid == 1) { // // Make sure this is not a forked PTE. // Pfn1 = MI_PFN_ELEMENT (PteContents.u.Hard.PageFrameNumber); if (Pfn1->u3.e1.PrototypePte) { // // MiDeletePte may release both the working set pushlock // and the PFN lock so the valid PTE list must be // processed now. // if (count != 0) { MiProcessValidPteList (&ValidPteList[0], count); count = 0; } LOCK_PFN (OldIrql); MiDeletePte (PointerPte, Va, FALSE, Process, NULL, NULL, OldIrql); UNLOCK_PFN (OldIrql); Process->NumberOfPrivatePages += 1; MI_WRITE_INVALID_PTE (PointerPte, MmDecommittedPte); } else { // // PTE is valid, process later when PFN lock is held. // if (count == MM_VALID_PTE_SIZE) { MiProcessValidPteList (&ValidPteList[0], count); count = 0; } ValidPteList[count] = PointerPte; count += 1; // // Remove address from working set list. // WorkingSetIndex = Pfn1->u1.WsIndex; ASSERT (PAGE_ALIGN(MmWsle[WorkingSetIndex].u1.Long) == Va); // // Check to see if this entry is locked in the // working set or locked in memory. // Locked = MmWsle[WorkingSetIndex].u1.e1; MiRemoveWsle (WorkingSetIndex, MmWorkingSetList); // // Add this entry to the list of free working set // entries and adjust the working set count. // MiReleaseWsle (WorkingSetIndex, &Process->Vm); if ((Locked.LockedInWs == 1) || (Locked.LockedInMemory == 1)) { // // This entry is locked. // MmWorkingSetList->FirstDynamic -= 1; if (WorkingSetIndex != MmWorkingSetList->FirstDynamic) { Entry = MmWorkingSetList->FirstDynamic; ASSERT (MmWsle[Entry].u1.e1.Valid); MiSwapWslEntries (Entry, WorkingSetIndex, &Process->Vm, FALSE); } } MI_SET_PTE_IN_WORKING_SET (PointerPte, 0); } } else if (PteContents.u.Soft.Prototype) { // // This is a forked PTE, just delete it. // // MiDeletePte may release both the working set pushlock // and the PFN lock so the valid PTE list must be // processed now. // if (count != 0) { MiProcessValidPteList (&ValidPteList[0], count); count = 0; } LOCK_PFN (OldIrql); MiDeletePte (PointerPte, Va, FALSE, Process, NULL, NULL, OldIrql); UNLOCK_PFN (OldIrql); Process->NumberOfPrivatePages += 1; MI_WRITE_INVALID_PTE (PointerPte, MmDecommittedPte); } else if (PteContents.u.Soft.Transition == 1) { // // Transition PTE, get the PFN database lock // and reprocess this one. // LOCK_PFN (OldIrql); PteContents = *PointerPte; if (PteContents.u.Soft.Transition == 1) { // // PTE is still in transition, delete it. // Pfn1 = MI_PFN_ELEMENT (PteContents.u.Trans.PageFrameNumber); MI_SET_PFN_DELETED (Pfn1); PageTableFrameIndex = Pfn1->u4.PteFrame; Pfn2 = MI_PFN_ELEMENT (PageTableFrameIndex); MiDecrementShareCountInline (Pfn2, PageTableFrameIndex); // // Check the reference count for the page, if the // reference count is zero, move the page to the // free list, if the reference count is not zero, // ignore this page. When the reference count // goes to zero, it will be placed on the free list. // if (Pfn1->u3.e2.ReferenceCount == 0) { MiUnlinkPageFromList (Pfn1); MiReleasePageFileSpace (Pfn1->OriginalPte); MiInsertPageInFreeList (MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE(&PteContents)); } } else { // // Page MUST be in page file format! // ASSERT (PteContents.u.Soft.Valid == 0); ASSERT (PteContents.u.Soft.Prototype == 0); ASSERT (PteContents.u.Soft.PageFileHigh != 0); MiReleasePageFileSpace (PteContents); } MI_WRITE_INVALID_PTE (PointerPte, MmDecommittedPte); UNLOCK_PFN (OldIrql); } else { // // Must be demand zero or paging file format. // if (PteContents.u.Soft.PageFileHigh != 0) { LOCK_PFN (OldIrql); MiReleasePageFileSpace (PteContents); UNLOCK_PFN (OldIrql); } else { // // Don't subtract out the private page count for // a demand zero page. // Process->NumberOfPrivatePages += 1; } MI_WRITE_INVALID_PTE (PointerPte, MmDecommittedPte); } } } else { // // The PTE is already zero. // // // Increment the count of non-zero page table entries for this // page table and the number of private pages for the process. // UsedPageTableHandle = MI_GET_USED_PTES_HANDLE (Va); MI_INCREMENT_USED_PTES_BY_HANDLE (UsedPageTableHandle); if (PointerPte > CommitLimitPte) { // // PTE is not committed. // CommitReduction += 1; } MI_WRITE_INVALID_PTE (PointerPte, MmDecommittedPte); } PointerPte += 1; Va = (PVOID)((PCHAR)Va + PAGE_SIZE); } if (count != 0) { MiProcessValidPteList (&ValidPteList[0], count); } UNLOCK_WS_UNSAFE (CurrentThread, Process); return CommitReduction; }
ULONG MiDecommitPages ( IN PVOID StartingAddress, IN PMMPTE EndingPte, IN PEPROCESS Process, IN PMMVAD_SHORT Vad ) /*++ Routine Description: This routine decommits the specficed range of pages. Arguments: StartingAddress - Supplies the starting address of the range. EndingPte - Supplies the ending PTE of the range. Process - Supplies the current process. Vad - Supplies the virtual address descriptor which describes the range. Return Value: Value to reduce commitment by for the VAD. Environment: Kernel mode, APCs disable, WorkingSetMutex and AddressCreation mutexes held. --*/ { PMMPTE PointerPde; PMMPTE PointerPte; PVOID Va; ULONG PdeOffset; ULONG CommitReduction = 0; PMMPTE CommitLimitPte; KIRQL OldIrql; PMMPTE ValidPteList[MM_VALID_PTE_SIZE]; ULONG count = 0; ULONG WorkingSetIndex; PMMPFN Pfn1; PMMPFN Pfn2; PVOID SwapVa; ULONG Entry; MMWSLENTRY Locked; MMPTE PteContents; if (Vad->u.VadFlags.MemCommit) { CommitLimitPte = MiGetPteAddress (Vad->EndingVa); } else { CommitLimitPte = NULL; } // // Decommit each page by setting the PTE to be explicitly // decommitted. The PTEs cannot be deleted all at once as // this would set the PTEs to zero which would auto-evaluate // as committed if referenced by another thread when a page // table page is being in-paged. // PointerPde = MiGetPdeAddress (StartingAddress); PointerPte = MiGetPteAddress (StartingAddress); Va = StartingAddress; PdeOffset = MiGetPdeOffset (Va); // // Loop through all the PDEs which map this region and ensure that // they exist. If they don't exist create them by touching a // PTE mapped by the PDE. // // // Get the PFN mutex so the MiDeletePte can be called. // MiMakePdeExistAndMakeValid(PointerPde, Process, FALSE); while (PointerPte <= EndingPte) { if (((ULONG)PointerPte & (PAGE_SIZE - 1)) == 0) { PdeOffset = MiGetPdeOffset (Va); PointerPde = MiGetPdeAddress (Va); if (count != 0) { MiProcessValidPteList (&ValidPteList[0], count); count = 0; } MiMakePdeExistAndMakeValid(PointerPde, Process, FALSE); } // // The working set lock is held. No PTEs can go from // invalid to valid or valid to invalid. Transition // PTEs can go from transition to pagefile. // PteContents = *PointerPte; if (PteContents.u.Long != 0) { if (PointerPte->u.Long == MmDecommittedPte.u.Long) { // // This PTE is already decommitted. // CommitReduction += 1; } else { Process->NumberOfPrivatePages -= 1; if (PteContents.u.Hard.Valid == 1) { // // Make sure this is not a forked PTE. // Pfn1 = MI_PFN_ELEMENT (PteContents.u.Hard.PageFrameNumber); if (Pfn1->u3.e1.PrototypePte) { LOCK_PFN (OldIrql); MiDeletePte (PointerPte, Va, FALSE, Process, NULL, NULL); UNLOCK_PFN (OldIrql); Process->NumberOfPrivatePages += 1; *PointerPte = MmDecommittedPte; } else { // // Pte is valid, process later when PFN lock is held. // if (count == MM_VALID_PTE_SIZE) { MiProcessValidPteList (&ValidPteList[0], count); count = 0; } ValidPteList[count] = PointerPte; count += 1; // // Remove address from working set list. // WorkingSetIndex = Pfn1->u1.WsIndex; ASSERT (PAGE_ALIGN(MmWsle[WorkingSetIndex].u1.Long) == Va); // // Check to see if this entry is locked in the working set // or locked in memory. // Locked = MmWsle[WorkingSetIndex].u1.e1; MiRemoveWsle (WorkingSetIndex, MmWorkingSetList); // // Add this entry to the list of free working set entries // and adjust the working set count. // MiReleaseWsle (WorkingSetIndex, &Process->Vm); if ((Locked.LockedInWs == 1) || (Locked.LockedInMemory == 1)) { // // This entry is locked. // MmWorkingSetList->FirstDynamic -= 1; if (WorkingSetIndex != MmWorkingSetList->FirstDynamic) { SwapVa = MmWsle[MmWorkingSetList->FirstDynamic].u1.VirtualAddress; SwapVa = PAGE_ALIGN (SwapVa); Pfn2 = MI_PFN_ELEMENT ( MiGetPteAddress (SwapVa)->u.Hard.PageFrameNumber); Entry = MiLocateWsle (SwapVa, MmWorkingSetList, Pfn2->u1.WsIndex); MiSwapWslEntries (Entry, WorkingSetIndex, &Process->Vm); } } } } else if (PteContents.u.Soft.Prototype) { // // This is a forked PTE, just delete it. // LOCK_PFN (OldIrql); MiDeletePte (PointerPte, Va, FALSE, Process, NULL, NULL); UNLOCK_PFN (OldIrql); Process->NumberOfPrivatePages += 1; *PointerPte = MmDecommittedPte; } else if (PteContents.u.Soft.Transition == 1) { // // Transition PTE, get the PFN database lock // and reprocess this one. // LOCK_PFN (OldIrql); PteContents = *PointerPte; if (PteContents.u.Soft.Transition == 1) { // // PTE is still in transition, delete it. // Pfn1 = MI_PFN_ELEMENT (PteContents.u.Trans.PageFrameNumber); MI_SET_PFN_DELETED (Pfn1); MiDecrementShareCount (Pfn1->PteFrame); // // Check the reference count for the page, if the // reference count is zero, move the page to the // free list, if the reference count is not zero, // ignore this page. When the refernce count // goes to zero, it will be placed on the free list. // if (Pfn1->u3.e2.ReferenceCount == 0) { MiUnlinkPageFromList (Pfn1); MiReleasePageFileSpace (Pfn1->OriginalPte); MiInsertPageInList (MmPageLocationList[FreePageList], PteContents.u.Trans.PageFrameNumber); } *PointerPte = MmDecommittedPte; } else { // // Page MUST be in page file format! // ASSERT (PteContents.u.Soft.Valid == 0); ASSERT (PteContents.u.Soft.Prototype == 0); ASSERT (PteContents.u.Soft.PageFileHigh != 0); MiReleasePageFileSpace (PteContents); *PointerPte = MmDecommittedPte; } UNLOCK_PFN (OldIrql); } else { // // Must be demand zero or paging file format. // if (PteContents.u.Soft.PageFileHigh != 0) { LOCK_PFN (OldIrql); MiReleasePageFileSpace (PteContents); UNLOCK_PFN (OldIrql); } else { // // Don't subtract out the private page count for // a demand zero page. // Process->NumberOfPrivatePages += 1; } *PointerPte = MmDecommittedPte; } } } else { // // The PTE is already zero. // // // Increment the count of non-zero page table entires for this // page table and the number of private pages for the process. // MmWorkingSetList->UsedPageTableEntries[PdeOffset] += 1; if (PointerPte > CommitLimitPte) { // // Pte is not committed. // CommitReduction += 1; } *PointerPte = MmDecommittedPte; } PointerPte += 1; Va = (PVOID)((ULONG)Va + PAGE_SIZE); } if (count != 0) { MiProcessValidPteList (&ValidPteList[0], count); } return CommitReduction; }