diff options
-rw-r--r-- | mm/huge_memory.c | 29 |
1 files changed, 24 insertions, 5 deletions
diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 72835cea0b0f..827d9c813051 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c | |||
@@ -769,17 +769,20 @@ static inline struct page *alloc_hugepage(int defrag) | |||
769 | } | 769 | } |
770 | #endif | 770 | #endif |
771 | 771 | ||
772 | static void set_huge_zero_page(pgtable_t pgtable, struct mm_struct *mm, | 772 | static bool set_huge_zero_page(pgtable_t pgtable, struct mm_struct *mm, |
773 | struct vm_area_struct *vma, unsigned long haddr, pmd_t *pmd, | 773 | struct vm_area_struct *vma, unsigned long haddr, pmd_t *pmd, |
774 | unsigned long zero_pfn) | 774 | unsigned long zero_pfn) |
775 | { | 775 | { |
776 | pmd_t entry; | 776 | pmd_t entry; |
777 | if (!pmd_none(*pmd)) | ||
778 | return false; | ||
777 | entry = pfn_pmd(zero_pfn, vma->vm_page_prot); | 779 | entry = pfn_pmd(zero_pfn, vma->vm_page_prot); |
778 | entry = pmd_wrprotect(entry); | 780 | entry = pmd_wrprotect(entry); |
779 | entry = pmd_mkhuge(entry); | 781 | entry = pmd_mkhuge(entry); |
780 | set_pmd_at(mm, haddr, pmd, entry); | 782 | set_pmd_at(mm, haddr, pmd, entry); |
781 | pgtable_trans_huge_deposit(mm, pgtable); | 783 | pgtable_trans_huge_deposit(mm, pgtable); |
782 | mm->nr_ptes++; | 784 | mm->nr_ptes++; |
785 | return true; | ||
783 | } | 786 | } |
784 | 787 | ||
785 | int do_huge_pmd_anonymous_page(struct mm_struct *mm, struct vm_area_struct *vma, | 788 | int do_huge_pmd_anonymous_page(struct mm_struct *mm, struct vm_area_struct *vma, |
@@ -799,6 +802,7 @@ int do_huge_pmd_anonymous_page(struct mm_struct *mm, struct vm_area_struct *vma, | |||
799 | transparent_hugepage_use_zero_page()) { | 802 | transparent_hugepage_use_zero_page()) { |
800 | pgtable_t pgtable; | 803 | pgtable_t pgtable; |
801 | unsigned long zero_pfn; | 804 | unsigned long zero_pfn; |
805 | bool set; | ||
802 | pgtable = pte_alloc_one(mm, haddr); | 806 | pgtable = pte_alloc_one(mm, haddr); |
803 | if (unlikely(!pgtable)) | 807 | if (unlikely(!pgtable)) |
804 | return VM_FAULT_OOM; | 808 | return VM_FAULT_OOM; |
@@ -809,9 +813,13 @@ int do_huge_pmd_anonymous_page(struct mm_struct *mm, struct vm_area_struct *vma, | |||
809 | goto out; | 813 | goto out; |
810 | } | 814 | } |
811 | spin_lock(&mm->page_table_lock); | 815 | spin_lock(&mm->page_table_lock); |
812 | set_huge_zero_page(pgtable, mm, vma, haddr, pmd, | 816 | set = set_huge_zero_page(pgtable, mm, vma, haddr, pmd, |
813 | zero_pfn); | 817 | zero_pfn); |
814 | spin_unlock(&mm->page_table_lock); | 818 | spin_unlock(&mm->page_table_lock); |
819 | if (!set) { | ||
820 | pte_free(mm, pgtable); | ||
821 | put_huge_zero_page(); | ||
822 | } | ||
815 | return 0; | 823 | return 0; |
816 | } | 824 | } |
817 | page = alloc_hugepage_vma(transparent_hugepage_defrag(vma), | 825 | page = alloc_hugepage_vma(transparent_hugepage_defrag(vma), |
@@ -885,14 +893,16 @@ int copy_huge_pmd(struct mm_struct *dst_mm, struct mm_struct *src_mm, | |||
885 | */ | 893 | */ |
886 | if (is_huge_zero_pmd(pmd)) { | 894 | if (is_huge_zero_pmd(pmd)) { |
887 | unsigned long zero_pfn; | 895 | unsigned long zero_pfn; |
896 | bool set; | ||
888 | /* | 897 | /* |
889 | * get_huge_zero_page() will never allocate a new page here, | 898 | * get_huge_zero_page() will never allocate a new page here, |
890 | * since we already have a zero page to copy. It just takes a | 899 | * since we already have a zero page to copy. It just takes a |
891 | * reference. | 900 | * reference. |
892 | */ | 901 | */ |
893 | zero_pfn = get_huge_zero_page(); | 902 | zero_pfn = get_huge_zero_page(); |
894 | set_huge_zero_page(pgtable, dst_mm, vma, addr, dst_pmd, | 903 | set = set_huge_zero_page(pgtable, dst_mm, vma, addr, dst_pmd, |
895 | zero_pfn); | 904 | zero_pfn); |
905 | BUG_ON(!set); /* unexpected !pmd_none(dst_pmd) */ | ||
896 | ret = 0; | 906 | ret = 0; |
897 | goto out_unlock; | 907 | goto out_unlock; |
898 | } | 908 | } |
@@ -949,7 +959,7 @@ unlock: | |||
949 | 959 | ||
950 | static int do_huge_pmd_wp_zero_page_fallback(struct mm_struct *mm, | 960 | static int do_huge_pmd_wp_zero_page_fallback(struct mm_struct *mm, |
951 | struct vm_area_struct *vma, unsigned long address, | 961 | struct vm_area_struct *vma, unsigned long address, |
952 | pmd_t *pmd, unsigned long haddr) | 962 | pmd_t *pmd, pmd_t orig_pmd, unsigned long haddr) |
953 | { | 963 | { |
954 | pgtable_t pgtable; | 964 | pgtable_t pgtable; |
955 | pmd_t _pmd; | 965 | pmd_t _pmd; |
@@ -978,6 +988,9 @@ static int do_huge_pmd_wp_zero_page_fallback(struct mm_struct *mm, | |||
978 | mmu_notifier_invalidate_range_start(mm, mmun_start, mmun_end); | 988 | mmu_notifier_invalidate_range_start(mm, mmun_start, mmun_end); |
979 | 989 | ||
980 | spin_lock(&mm->page_table_lock); | 990 | spin_lock(&mm->page_table_lock); |
991 | if (unlikely(!pmd_same(*pmd, orig_pmd))) | ||
992 | goto out_free_page; | ||
993 | |||
981 | pmdp_clear_flush(vma, haddr, pmd); | 994 | pmdp_clear_flush(vma, haddr, pmd); |
982 | /* leave pmd empty until pte is filled */ | 995 | /* leave pmd empty until pte is filled */ |
983 | 996 | ||
@@ -1010,6 +1023,12 @@ static int do_huge_pmd_wp_zero_page_fallback(struct mm_struct *mm, | |||
1010 | ret |= VM_FAULT_WRITE; | 1023 | ret |= VM_FAULT_WRITE; |
1011 | out: | 1024 | out: |
1012 | return ret; | 1025 | return ret; |
1026 | out_free_page: | ||
1027 | spin_unlock(&mm->page_table_lock); | ||
1028 | mmu_notifier_invalidate_range_end(mm, mmun_start, mmun_end); | ||
1029 | mem_cgroup_uncharge_page(page); | ||
1030 | put_page(page); | ||
1031 | goto out; | ||
1013 | } | 1032 | } |
1014 | 1033 | ||
1015 | static int do_huge_pmd_wp_page_fallback(struct mm_struct *mm, | 1034 | static int do_huge_pmd_wp_page_fallback(struct mm_struct *mm, |
@@ -1156,7 +1175,7 @@ alloc: | |||
1156 | count_vm_event(THP_FAULT_FALLBACK); | 1175 | count_vm_event(THP_FAULT_FALLBACK); |
1157 | if (is_huge_zero_pmd(orig_pmd)) { | 1176 | if (is_huge_zero_pmd(orig_pmd)) { |
1158 | ret = do_huge_pmd_wp_zero_page_fallback(mm, vma, | 1177 | ret = do_huge_pmd_wp_zero_page_fallback(mm, vma, |
1159 | address, pmd, haddr); | 1178 | address, pmd, orig_pmd, haddr); |
1160 | } else { | 1179 | } else { |
1161 | ret = do_huge_pmd_wp_page_fallback(mm, vma, address, | 1180 | ret = do_huge_pmd_wp_page_fallback(mm, vma, address, |
1162 | pmd, orig_pmd, page, haddr); | 1181 | pmd, orig_pmd, page, haddr); |