diff options
Diffstat (limited to 'arch/x86/mm/pageattr.c')
-rw-r--r-- | arch/x86/mm/pageattr.c | 63 |
1 files changed, 47 insertions, 16 deletions
diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index 4540e8880cd9..ac9c7797b632 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c | |||
@@ -52,6 +52,7 @@ static DEFINE_SPINLOCK(cpa_lock); | |||
52 | #define CPA_FLUSHTLB 1 | 52 | #define CPA_FLUSHTLB 1 |
53 | #define CPA_ARRAY 2 | 53 | #define CPA_ARRAY 2 |
54 | #define CPA_PAGES_ARRAY 4 | 54 | #define CPA_PAGES_ARRAY 4 |
55 | #define CPA_FREE_PAGETABLES 8 | ||
55 | 56 | ||
56 | #ifdef CONFIG_PROC_FS | 57 | #ifdef CONFIG_PROC_FS |
57 | static unsigned long direct_pages_count[PG_LEVEL_NUM]; | 58 | static unsigned long direct_pages_count[PG_LEVEL_NUM]; |
@@ -723,10 +724,13 @@ static int split_large_page(struct cpa_data *cpa, pte_t *kpte, | |||
723 | return 0; | 724 | return 0; |
724 | } | 725 | } |
725 | 726 | ||
726 | static bool try_to_free_pte_page(pte_t *pte) | 727 | static bool try_to_free_pte_page(struct cpa_data *cpa, pte_t *pte) |
727 | { | 728 | { |
728 | int i; | 729 | int i; |
729 | 730 | ||
731 | if (!(cpa->flags & CPA_FREE_PAGETABLES)) | ||
732 | return false; | ||
733 | |||
730 | for (i = 0; i < PTRS_PER_PTE; i++) | 734 | for (i = 0; i < PTRS_PER_PTE; i++) |
731 | if (!pte_none(pte[i])) | 735 | if (!pte_none(pte[i])) |
732 | return false; | 736 | return false; |
@@ -735,10 +739,13 @@ static bool try_to_free_pte_page(pte_t *pte) | |||
735 | return true; | 739 | return true; |
736 | } | 740 | } |
737 | 741 | ||
738 | static bool try_to_free_pmd_page(pmd_t *pmd) | 742 | static bool try_to_free_pmd_page(struct cpa_data *cpa, pmd_t *pmd) |
739 | { | 743 | { |
740 | int i; | 744 | int i; |
741 | 745 | ||
746 | if (!(cpa->flags & CPA_FREE_PAGETABLES)) | ||
747 | return false; | ||
748 | |||
742 | for (i = 0; i < PTRS_PER_PMD; i++) | 749 | for (i = 0; i < PTRS_PER_PMD; i++) |
743 | if (!pmd_none(pmd[i])) | 750 | if (!pmd_none(pmd[i])) |
744 | return false; | 751 | return false; |
@@ -759,7 +766,9 @@ static bool try_to_free_pud_page(pud_t *pud) | |||
759 | return true; | 766 | return true; |
760 | } | 767 | } |
761 | 768 | ||
762 | static bool unmap_pte_range(pmd_t *pmd, unsigned long start, unsigned long end) | 769 | static bool unmap_pte_range(struct cpa_data *cpa, pmd_t *pmd, |
770 | unsigned long start, | ||
771 | unsigned long end) | ||
763 | { | 772 | { |
764 | pte_t *pte = pte_offset_kernel(pmd, start); | 773 | pte_t *pte = pte_offset_kernel(pmd, start); |
765 | 774 | ||
@@ -770,22 +779,23 @@ static bool unmap_pte_range(pmd_t *pmd, unsigned long start, unsigned long end) | |||
770 | pte++; | 779 | pte++; |
771 | } | 780 | } |
772 | 781 | ||
773 | if (try_to_free_pte_page((pte_t *)pmd_page_vaddr(*pmd))) { | 782 | if (try_to_free_pte_page(cpa, (pte_t *)pmd_page_vaddr(*pmd))) { |
774 | pmd_clear(pmd); | 783 | pmd_clear(pmd); |
775 | return true; | 784 | return true; |
776 | } | 785 | } |
777 | return false; | 786 | return false; |
778 | } | 787 | } |
779 | 788 | ||
780 | static void __unmap_pmd_range(pud_t *pud, pmd_t *pmd, | 789 | static void __unmap_pmd_range(struct cpa_data *cpa, pud_t *pud, pmd_t *pmd, |
781 | unsigned long start, unsigned long end) | 790 | unsigned long start, unsigned long end) |
782 | { | 791 | { |
783 | if (unmap_pte_range(pmd, start, end)) | 792 | if (unmap_pte_range(cpa, pmd, start, end)) |
784 | if (try_to_free_pmd_page((pmd_t *)pud_page_vaddr(*pud))) | 793 | if (try_to_free_pmd_page(cpa, (pmd_t *)pud_page_vaddr(*pud))) |
785 | pud_clear(pud); | 794 | pud_clear(pud); |
786 | } | 795 | } |
787 | 796 | ||
788 | static void unmap_pmd_range(pud_t *pud, unsigned long start, unsigned long end) | 797 | static void unmap_pmd_range(struct cpa_data *cpa, pud_t *pud, |
798 | unsigned long start, unsigned long end) | ||
789 | { | 799 | { |
790 | pmd_t *pmd = pmd_offset(pud, start); | 800 | pmd_t *pmd = pmd_offset(pud, start); |
791 | 801 | ||
@@ -796,7 +806,7 @@ static void unmap_pmd_range(pud_t *pud, unsigned long start, unsigned long end) | |||
796 | unsigned long next_page = (start + PMD_SIZE) & PMD_MASK; | 806 | unsigned long next_page = (start + PMD_SIZE) & PMD_MASK; |
797 | unsigned long pre_end = min_t(unsigned long, end, next_page); | 807 | unsigned long pre_end = min_t(unsigned long, end, next_page); |
798 | 808 | ||
799 | __unmap_pmd_range(pud, pmd, start, pre_end); | 809 | __unmap_pmd_range(cpa, pud, pmd, start, pre_end); |
800 | 810 | ||
801 | start = pre_end; | 811 | start = pre_end; |
802 | pmd++; | 812 | pmd++; |
@@ -809,7 +819,8 @@ static void unmap_pmd_range(pud_t *pud, unsigned long start, unsigned long end) | |||
809 | if (pmd_large(*pmd)) | 819 | if (pmd_large(*pmd)) |
810 | pmd_clear(pmd); | 820 | pmd_clear(pmd); |
811 | else | 821 | else |
812 | __unmap_pmd_range(pud, pmd, start, start + PMD_SIZE); | 822 | __unmap_pmd_range(cpa, pud, pmd, |
823 | start, start + PMD_SIZE); | ||
813 | 824 | ||
814 | start += PMD_SIZE; | 825 | start += PMD_SIZE; |
815 | pmd++; | 826 | pmd++; |
@@ -819,17 +830,19 @@ static void unmap_pmd_range(pud_t *pud, unsigned long start, unsigned long end) | |||
819 | * 4K leftovers? | 830 | * 4K leftovers? |
820 | */ | 831 | */ |
821 | if (start < end) | 832 | if (start < end) |
822 | return __unmap_pmd_range(pud, pmd, start, end); | 833 | return __unmap_pmd_range(cpa, pud, pmd, start, end); |
823 | 834 | ||
824 | /* | 835 | /* |
825 | * Try again to free the PMD page if haven't succeeded above. | 836 | * Try again to free the PMD page if haven't succeeded above. |
826 | */ | 837 | */ |
827 | if (!pud_none(*pud)) | 838 | if (!pud_none(*pud)) |
828 | if (try_to_free_pmd_page((pmd_t *)pud_page_vaddr(*pud))) | 839 | if (try_to_free_pmd_page(cpa, (pmd_t *)pud_page_vaddr(*pud))) |
829 | pud_clear(pud); | 840 | pud_clear(pud); |
830 | } | 841 | } |
831 | 842 | ||
832 | static void unmap_pud_range(pgd_t *pgd, unsigned long start, unsigned long end) | 843 | static void __unmap_pud_range(struct cpa_data *cpa, pgd_t *pgd, |
844 | unsigned long start, | ||
845 | unsigned long end) | ||
833 | { | 846 | { |
834 | pud_t *pud = pud_offset(pgd, start); | 847 | pud_t *pud = pud_offset(pgd, start); |
835 | 848 | ||
@@ -840,7 +853,7 @@ static void unmap_pud_range(pgd_t *pgd, unsigned long start, unsigned long end) | |||
840 | unsigned long next_page = (start + PUD_SIZE) & PUD_MASK; | 853 | unsigned long next_page = (start + PUD_SIZE) & PUD_MASK; |
841 | unsigned long pre_end = min_t(unsigned long, end, next_page); | 854 | unsigned long pre_end = min_t(unsigned long, end, next_page); |
842 | 855 | ||
843 | unmap_pmd_range(pud, start, pre_end); | 856 | unmap_pmd_range(cpa, pud, start, pre_end); |
844 | 857 | ||
845 | start = pre_end; | 858 | start = pre_end; |
846 | pud++; | 859 | pud++; |
@@ -854,7 +867,7 @@ static void unmap_pud_range(pgd_t *pgd, unsigned long start, unsigned long end) | |||
854 | if (pud_large(*pud)) | 867 | if (pud_large(*pud)) |
855 | pud_clear(pud); | 868 | pud_clear(pud); |
856 | else | 869 | else |
857 | unmap_pmd_range(pud, start, start + PUD_SIZE); | 870 | unmap_pmd_range(cpa, pud, start, start + PUD_SIZE); |
858 | 871 | ||
859 | start += PUD_SIZE; | 872 | start += PUD_SIZE; |
860 | pud++; | 873 | pud++; |
@@ -864,7 +877,7 @@ static void unmap_pud_range(pgd_t *pgd, unsigned long start, unsigned long end) | |||
864 | * 2M leftovers? | 877 | * 2M leftovers? |
865 | */ | 878 | */ |
866 | if (start < end) | 879 | if (start < end) |
867 | unmap_pmd_range(pud, start, end); | 880 | unmap_pmd_range(cpa, pud, start, end); |
868 | 881 | ||
869 | /* | 882 | /* |
870 | * No need to try to free the PUD page because we'll free it in | 883 | * No need to try to free the PUD page because we'll free it in |
@@ -872,6 +885,24 @@ static void unmap_pud_range(pgd_t *pgd, unsigned long start, unsigned long end) | |||
872 | */ | 885 | */ |
873 | } | 886 | } |
874 | 887 | ||
888 | static void unmap_pud_range(pgd_t *pgd, unsigned long start, unsigned long end) | ||
889 | { | ||
890 | struct cpa_data cpa = { | ||
891 | .flags = CPA_FREE_PAGETABLES, | ||
892 | }; | ||
893 | |||
894 | __unmap_pud_range(&cpa, pgd, start, end); | ||
895 | } | ||
896 | |||
897 | void unmap_pud_range_nofree(pgd_t *pgd, unsigned long start, unsigned long end) | ||
898 | { | ||
899 | struct cpa_data cpa = { | ||
900 | .flags = 0, | ||
901 | }; | ||
902 | |||
903 | __unmap_pud_range(&cpa, pgd, start, end); | ||
904 | } | ||
905 | |||
875 | static void unmap_pgd_range(pgd_t *root, unsigned long addr, unsigned long end) | 906 | static void unmap_pgd_range(pgd_t *root, unsigned long addr, unsigned long end) |
876 | { | 907 | { |
877 | pgd_t *pgd_entry = root + pgd_index(addr); | 908 | pgd_t *pgd_entry = root + pgd_index(addr); |