Commit 7da4d641c58d201c3cc1835c05ca1a7fa26f0856
Committed by
Mel Gorman
1 parent
4fd017708c
Exists in
master
and in
20 other branches
mm: Count the number of pages affected in change_protection()
This will be used for three kinds of purposes: - to optimize mprotect() - to speed up working set scanning for working set areas that have not been touched - to more accurately scan per real working set No change in functionality from this patch. Suggested-by: Ingo Molnar <mingo@kernel.org> Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Andrea Arcangeli <aarcange@redhat.com> Cc: Rik van Riel <riel@redhat.com> Cc: Mel Gorman <mgorman@suse.de> Cc: Hugh Dickins <hughd@google.com> Cc: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Ingo Molnar <mingo@kernel.org>
Showing 4 changed files with 61 additions and 18 deletions Side-by-side Diff
include/linux/hugetlb.h
... | ... | @@ -87,7 +87,7 @@ |
87 | 87 | pud_t *pud, int write); |
88 | 88 | int pmd_huge(pmd_t pmd); |
89 | 89 | int pud_huge(pud_t pmd); |
90 | -void hugetlb_change_protection(struct vm_area_struct *vma, | |
90 | +unsigned long hugetlb_change_protection(struct vm_area_struct *vma, | |
91 | 91 | unsigned long address, unsigned long end, pgprot_t newprot); |
92 | 92 | |
93 | 93 | #else /* !CONFIG_HUGETLB_PAGE */ |
... | ... | @@ -132,7 +132,11 @@ |
132 | 132 | { |
133 | 133 | } |
134 | 134 | |
135 | -#define hugetlb_change_protection(vma, address, end, newprot) | |
135 | +static inline unsigned long hugetlb_change_protection(struct vm_area_struct *vma, | |
136 | + unsigned long address, unsigned long end, pgprot_t newprot) | |
137 | +{ | |
138 | + return 0; | |
139 | +} | |
136 | 140 | |
137 | 141 | static inline void __unmap_hugepage_range_final(struct mmu_gather *tlb, |
138 | 142 | struct vm_area_struct *vma, unsigned long start, |
include/linux/mm.h
... | ... | @@ -1078,6 +1078,9 @@ |
1078 | 1078 | extern unsigned long do_mremap(unsigned long addr, |
1079 | 1079 | unsigned long old_len, unsigned long new_len, |
1080 | 1080 | unsigned long flags, unsigned long new_addr); |
1081 | +extern unsigned long change_protection(struct vm_area_struct *vma, unsigned long start, | |
1082 | + unsigned long end, pgprot_t newprot, | |
1083 | + int dirty_accountable); | |
1081 | 1084 | extern int mprotect_fixup(struct vm_area_struct *vma, |
1082 | 1085 | struct vm_area_struct **pprev, unsigned long start, |
1083 | 1086 | unsigned long end, unsigned long newflags); |
mm/hugetlb.c
... | ... | @@ -3014,7 +3014,7 @@ |
3014 | 3014 | return i ? i : -EFAULT; |
3015 | 3015 | } |
3016 | 3016 | |
3017 | -void hugetlb_change_protection(struct vm_area_struct *vma, | |
3017 | +unsigned long hugetlb_change_protection(struct vm_area_struct *vma, | |
3018 | 3018 | unsigned long address, unsigned long end, pgprot_t newprot) |
3019 | 3019 | { |
3020 | 3020 | struct mm_struct *mm = vma->vm_mm; |
... | ... | @@ -3022,6 +3022,7 @@ |
3022 | 3022 | pte_t *ptep; |
3023 | 3023 | pte_t pte; |
3024 | 3024 | struct hstate *h = hstate_vma(vma); |
3025 | + unsigned long pages = 0; | |
3025 | 3026 | |
3026 | 3027 | BUG_ON(address >= end); |
3027 | 3028 | flush_cache_range(vma, address, end); |
3028 | 3029 | |
3029 | 3030 | |
... | ... | @@ -3032,12 +3033,15 @@ |
3032 | 3033 | ptep = huge_pte_offset(mm, address); |
3033 | 3034 | if (!ptep) |
3034 | 3035 | continue; |
3035 | - if (huge_pmd_unshare(mm, &address, ptep)) | |
3036 | + if (huge_pmd_unshare(mm, &address, ptep)) { | |
3037 | + pages++; | |
3036 | 3038 | continue; |
3039 | + } | |
3037 | 3040 | if (!huge_pte_none(huge_ptep_get(ptep))) { |
3038 | 3041 | pte = huge_ptep_get_and_clear(mm, address, ptep); |
3039 | 3042 | pte = pte_mkhuge(pte_modify(pte, newprot)); |
3040 | 3043 | set_huge_pte_at(mm, address, ptep, pte); |
3044 | + pages++; | |
3041 | 3045 | } |
3042 | 3046 | } |
3043 | 3047 | spin_unlock(&mm->page_table_lock); |
... | ... | @@ -3049,6 +3053,8 @@ |
3049 | 3053 | */ |
3050 | 3054 | flush_tlb_range(vma, start, end); |
3051 | 3055 | mutex_unlock(&vma->vm_file->f_mapping->i_mmap_mutex); |
3056 | + | |
3057 | + return pages << h->order; | |
3052 | 3058 | } |
3053 | 3059 | |
3054 | 3060 | int hugetlb_reserve_pages(struct inode *inode, |
mm/mprotect.c
... | ... | @@ -35,12 +35,13 @@ |
35 | 35 | } |
36 | 36 | #endif |
37 | 37 | |
38 | -static void change_pte_range(struct mm_struct *mm, pmd_t *pmd, | |
38 | +static unsigned long change_pte_range(struct mm_struct *mm, pmd_t *pmd, | |
39 | 39 | unsigned long addr, unsigned long end, pgprot_t newprot, |
40 | 40 | int dirty_accountable) |
41 | 41 | { |
42 | 42 | pte_t *pte, oldpte; |
43 | 43 | spinlock_t *ptl; |
44 | + unsigned long pages = 0; | |
44 | 45 | |
45 | 46 | pte = pte_offset_map_lock(mm, pmd, addr, &ptl); |
46 | 47 | arch_enter_lazy_mmu_mode(); |
... | ... | @@ -60,6 +61,7 @@ |
60 | 61 | ptent = pte_mkwrite(ptent); |
61 | 62 | |
62 | 63 | ptep_modify_prot_commit(mm, addr, pte, ptent); |
64 | + pages++; | |
63 | 65 | } else if (IS_ENABLED(CONFIG_MIGRATION) && !pte_file(oldpte)) { |
64 | 66 | swp_entry_t entry = pte_to_swp_entry(oldpte); |
65 | 67 | |
66 | 68 | |
67 | 69 | |
68 | 70 | |
... | ... | @@ -72,18 +74,22 @@ |
72 | 74 | set_pte_at(mm, addr, pte, |
73 | 75 | swp_entry_to_pte(entry)); |
74 | 76 | } |
77 | + pages++; | |
75 | 78 | } |
76 | 79 | } while (pte++, addr += PAGE_SIZE, addr != end); |
77 | 80 | arch_leave_lazy_mmu_mode(); |
78 | 81 | pte_unmap_unlock(pte - 1, ptl); |
82 | + | |
83 | + return pages; | |
79 | 84 | } |
80 | 85 | |
81 | -static inline void change_pmd_range(struct vm_area_struct *vma, pud_t *pud, | |
86 | +static inline unsigned long change_pmd_range(struct vm_area_struct *vma, pud_t *pud, | |
82 | 87 | unsigned long addr, unsigned long end, pgprot_t newprot, |
83 | 88 | int dirty_accountable) |
84 | 89 | { |
85 | 90 | pmd_t *pmd; |
86 | 91 | unsigned long next; |
92 | + unsigned long pages = 0; | |
87 | 93 | |
88 | 94 | pmd = pmd_offset(pud, addr); |
89 | 95 | do { |
90 | 96 | |
91 | 97 | |
92 | 98 | |
93 | 99 | |
94 | 100 | |
95 | 101 | |
96 | 102 | |
97 | 103 | |
... | ... | @@ -91,35 +97,42 @@ |
91 | 97 | if (pmd_trans_huge(*pmd)) { |
92 | 98 | if (next - addr != HPAGE_PMD_SIZE) |
93 | 99 | split_huge_page_pmd(vma->vm_mm, pmd); |
94 | - else if (change_huge_pmd(vma, pmd, addr, newprot)) | |
100 | + else if (change_huge_pmd(vma, pmd, addr, newprot)) { | |
101 | + pages += HPAGE_PMD_NR; | |
95 | 102 | continue; |
103 | + } | |
96 | 104 | /* fall through */ |
97 | 105 | } |
98 | 106 | if (pmd_none_or_clear_bad(pmd)) |
99 | 107 | continue; |
100 | - change_pte_range(vma->vm_mm, pmd, addr, next, newprot, | |
108 | + pages += change_pte_range(vma->vm_mm, pmd, addr, next, newprot, | |
101 | 109 | dirty_accountable); |
102 | 110 | } while (pmd++, addr = next, addr != end); |
111 | + | |
112 | + return pages; | |
103 | 113 | } |
104 | 114 | |
105 | -static inline void change_pud_range(struct vm_area_struct *vma, pgd_t *pgd, | |
115 | +static inline unsigned long change_pud_range(struct vm_area_struct *vma, pgd_t *pgd, | |
106 | 116 | unsigned long addr, unsigned long end, pgprot_t newprot, |
107 | 117 | int dirty_accountable) |
108 | 118 | { |
109 | 119 | pud_t *pud; |
110 | 120 | unsigned long next; |
121 | + unsigned long pages = 0; | |
111 | 122 | |
112 | 123 | pud = pud_offset(pgd, addr); |
113 | 124 | do { |
114 | 125 | next = pud_addr_end(addr, end); |
115 | 126 | if (pud_none_or_clear_bad(pud)) |
116 | 127 | continue; |
117 | - change_pmd_range(vma, pud, addr, next, newprot, | |
128 | + pages += change_pmd_range(vma, pud, addr, next, newprot, | |
118 | 129 | dirty_accountable); |
119 | 130 | } while (pud++, addr = next, addr != end); |
131 | + | |
132 | + return pages; | |
120 | 133 | } |
121 | 134 | |
122 | -static void change_protection(struct vm_area_struct *vma, | |
135 | +static unsigned long change_protection_range(struct vm_area_struct *vma, | |
123 | 136 | unsigned long addr, unsigned long end, pgprot_t newprot, |
124 | 137 | int dirty_accountable) |
125 | 138 | { |
... | ... | @@ -127,6 +140,7 @@ |
127 | 140 | pgd_t *pgd; |
128 | 141 | unsigned long next; |
129 | 142 | unsigned long start = addr; |
143 | + unsigned long pages = 0; | |
130 | 144 | |
131 | 145 | BUG_ON(addr >= end); |
132 | 146 | pgd = pgd_offset(mm, addr); |
133 | 147 | |
134 | 148 | |
135 | 149 | |
... | ... | @@ -135,12 +149,32 @@ |
135 | 149 | next = pgd_addr_end(addr, end); |
136 | 150 | if (pgd_none_or_clear_bad(pgd)) |
137 | 151 | continue; |
138 | - change_pud_range(vma, pgd, addr, next, newprot, | |
152 | + pages += change_pud_range(vma, pgd, addr, next, newprot, | |
139 | 153 | dirty_accountable); |
140 | 154 | } while (pgd++, addr = next, addr != end); |
155 | + | |
141 | 156 | flush_tlb_range(vma, start, end); |
157 | + | |
158 | + return pages; | |
142 | 159 | } |
143 | 160 | |
161 | +unsigned long change_protection(struct vm_area_struct *vma, unsigned long start, | |
162 | + unsigned long end, pgprot_t newprot, | |
163 | + int dirty_accountable) | |
164 | +{ | |
165 | + struct mm_struct *mm = vma->vm_mm; | |
166 | + unsigned long pages; | |
167 | + | |
168 | + mmu_notifier_invalidate_range_start(mm, start, end); | |
169 | + if (is_vm_hugetlb_page(vma)) | |
170 | + pages = hugetlb_change_protection(vma, start, end, newprot); | |
171 | + else | |
172 | + pages = change_protection_range(vma, start, end, newprot, dirty_accountable); | |
173 | + mmu_notifier_invalidate_range_end(mm, start, end); | |
174 | + | |
175 | + return pages; | |
176 | +} | |
177 | + | |
144 | 178 | int |
145 | 179 | mprotect_fixup(struct vm_area_struct *vma, struct vm_area_struct **pprev, |
146 | 180 | unsigned long start, unsigned long end, unsigned long newflags) |
... | ... | @@ -213,12 +247,8 @@ |
213 | 247 | dirty_accountable = 1; |
214 | 248 | } |
215 | 249 | |
216 | - mmu_notifier_invalidate_range_start(mm, start, end); | |
217 | - if (is_vm_hugetlb_page(vma)) | |
218 | - hugetlb_change_protection(vma, start, end, vma->vm_page_prot); | |
219 | - else | |
220 | - change_protection(vma, start, end, vma->vm_page_prot, dirty_accountable); | |
221 | - mmu_notifier_invalidate_range_end(mm, start, end); | |
250 | + change_protection(vma, start, end, vma->vm_page_prot, dirty_accountable); | |
251 | + | |
222 | 252 | vm_stat_account(mm, oldflags, vma->vm_file, -nrpages); |
223 | 253 | vm_stat_account(mm, newflags, vma->vm_file, nrpages); |
224 | 254 | perf_event_mmap(vma); |