aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/mm/pageattr.c94
1 files changed, 93 insertions, 1 deletions
diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c
index 1cbdbbc35b47..db8ace29514f 100644
--- a/arch/x86/mm/pageattr.c
+++ b/arch/x86/mm/pageattr.c
@@ -666,7 +666,99 @@ static int split_large_page(pte_t *kpte, unsigned long address)
666 return 0; 666 return 0;
667} 667}
668 668
669#define unmap_pmd_range(pud, start, pre_end) do {} while (0) 669static bool try_to_free_pte_page(pte_t *pte)
670{
671 int i;
672
673 for (i = 0; i < PTRS_PER_PTE; i++)
674 if (!pte_none(pte[i]))
675 return false;
676
677 free_page((unsigned long)pte);
678 return true;
679}
680
681static bool try_to_free_pmd_page(pmd_t *pmd)
682{
683 int i;
684
685 for (i = 0; i < PTRS_PER_PMD; i++)
686 if (!pmd_none(pmd[i]))
687 return false;
688
689 free_page((unsigned long)pmd);
690 return true;
691}
692
693static bool unmap_pte_range(pmd_t *pmd, unsigned long start, unsigned long end)
694{
695 pte_t *pte = pte_offset_kernel(pmd, start);
696
697 while (start < end) {
698 set_pte(pte, __pte(0));
699
700 start += PAGE_SIZE;
701 pte++;
702 }
703
704 if (try_to_free_pte_page((pte_t *)pmd_page_vaddr(*pmd))) {
705 pmd_clear(pmd);
706 return true;
707 }
708 return false;
709}
710
711static void __unmap_pmd_range(pud_t *pud, pmd_t *pmd,
712 unsigned long start, unsigned long end)
713{
714 if (unmap_pte_range(pmd, start, end))
715 if (try_to_free_pmd_page((pmd_t *)pud_page_vaddr(*pud)))
716 pud_clear(pud);
717}
718
719static void unmap_pmd_range(pud_t *pud, unsigned long start, unsigned long end)
720{
721 pmd_t *pmd = pmd_offset(pud, start);
722
723 /*
724 * Not on a 2MB page boundary?
725 */
726 if (start & (PMD_SIZE - 1)) {
727 unsigned long next_page = (start + PMD_SIZE) & PMD_MASK;
728 unsigned long pre_end = min_t(unsigned long, end, next_page);
729
730 __unmap_pmd_range(pud, pmd, start, pre_end);
731
732 start = pre_end;
733 pmd++;
734 }
735
736 /*
737 * Try to unmap in 2M chunks.
738 */
739 while (end - start >= PMD_SIZE) {
740 if (pmd_large(*pmd))
741 pmd_clear(pmd);
742 else
743 __unmap_pmd_range(pud, pmd, start, start + PMD_SIZE);
744
745 start += PMD_SIZE;
746 pmd++;
747 }
748
749 /*
750 * 4K leftovers?
751 */
752 if (start < end)
753 return __unmap_pmd_range(pud, pmd, start, end);
754
755 /*
756 * Try again to free the PMD page if haven't succeeded above.
757 */
758 if (!pud_none(*pud))
759 if (try_to_free_pmd_page((pmd_t *)pud_page_vaddr(*pud)))
760 pud_clear(pud);
761}
670 762
671static void unmap_pud_range(pgd_t *pgd, unsigned long start, unsigned long end) 763static void unmap_pud_range(pgd_t *pgd, unsigned long start, unsigned long end)
672{ 764{