aboutsummaryrefslogtreecommitdiffstats
path: root/mm
diff options
context:
space:
mode:
authorPeter Zijlstra <a.p.zijlstra@chello.nl>2012-11-18 21:14:23 -0500
committerMel Gorman <mgorman@suse.de>2012-12-11 09:28:34 -0500
commit7da4d641c58d201c3cc1835c05ca1a7fa26f0856 (patch)
treebf40cc40433383ee94559ea2ef19a282dead358f /mm
parent4fd017708c4a067da51a2b5cf8aedddf4e840b1f (diff)
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>
Diffstat (limited to 'mm')
-rw-r--r--mm/hugetlb.c10
-rw-r--r--mm/mprotect.c58
2 files changed, 52 insertions, 16 deletions
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 59a0059b39e2..712895eda0d0 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -3014,7 +3014,7 @@ same_page:
3014 return i ? i : -EFAULT; 3014 return i ? i : -EFAULT;
3015} 3015}
3016 3016
3017void hugetlb_change_protection(struct vm_area_struct *vma, 3017unsigned long hugetlb_change_protection(struct vm_area_struct *vma,
3018 unsigned long address, unsigned long end, pgprot_t newprot) 3018 unsigned long address, unsigned long end, pgprot_t newprot)
3019{ 3019{
3020 struct mm_struct *mm = vma->vm_mm; 3020 struct mm_struct *mm = vma->vm_mm;
@@ -3022,6 +3022,7 @@ void hugetlb_change_protection(struct vm_area_struct *vma,
3022 pte_t *ptep; 3022 pte_t *ptep;
3023 pte_t pte; 3023 pte_t pte;
3024 struct hstate *h = hstate_vma(vma); 3024 struct hstate *h = hstate_vma(vma);
3025 unsigned long pages = 0;
3025 3026
3026 BUG_ON(address >= end); 3027 BUG_ON(address >= end);
3027 flush_cache_range(vma, address, end); 3028 flush_cache_range(vma, address, end);
@@ -3032,12 +3033,15 @@ void hugetlb_change_protection(struct vm_area_struct *vma,
3032 ptep = huge_pte_offset(mm, address); 3033 ptep = huge_pte_offset(mm, address);
3033 if (!ptep) 3034 if (!ptep)
3034 continue; 3035 continue;
3035 if (huge_pmd_unshare(mm, &address, ptep)) 3036 if (huge_pmd_unshare(mm, &address, ptep)) {
3037 pages++;
3036 continue; 3038 continue;
3039 }
3037 if (!huge_pte_none(huge_ptep_get(ptep))) { 3040 if (!huge_pte_none(huge_ptep_get(ptep))) {
3038 pte = huge_ptep_get_and_clear(mm, address, ptep); 3041 pte = huge_ptep_get_and_clear(mm, address, ptep);
3039 pte = pte_mkhuge(pte_modify(pte, newprot)); 3042 pte = pte_mkhuge(pte_modify(pte, newprot));
3040 set_huge_pte_at(mm, address, ptep, pte); 3043 set_huge_pte_at(mm, address, ptep, pte);
3044 pages++;
3041 } 3045 }
3042 } 3046 }
3043 spin_unlock(&mm->page_table_lock); 3047 spin_unlock(&mm->page_table_lock);
@@ -3049,6 +3053,8 @@ void hugetlb_change_protection(struct vm_area_struct *vma,
3049 */ 3053 */
3050 flush_tlb_range(vma, start, end); 3054 flush_tlb_range(vma, start, end);
3051 mutex_unlock(&vma->vm_file->f_mapping->i_mmap_mutex); 3055 mutex_unlock(&vma->vm_file->f_mapping->i_mmap_mutex);
3056
3057 return pages << h->order;
3052} 3058}
3053 3059
3054int hugetlb_reserve_pages(struct inode *inode, 3060int hugetlb_reserve_pages(struct inode *inode,
diff --git a/mm/mprotect.c b/mm/mprotect.c
index a40992610ab6..1e265be25f85 100644
--- a/mm/mprotect.c
+++ b/mm/mprotect.c
@@ -35,12 +35,13 @@ static inline pgprot_t pgprot_modify(pgprot_t oldprot, pgprot_t newprot)
35} 35}
36#endif 36#endif
37 37
38static void change_pte_range(struct mm_struct *mm, pmd_t *pmd, 38static unsigned long change_pte_range(struct mm_struct *mm, pmd_t *pmd,
39 unsigned long addr, unsigned long end, pgprot_t newprot, 39 unsigned long addr, unsigned long end, pgprot_t newprot,
40 int dirty_accountable) 40 int dirty_accountable)
41{ 41{
42 pte_t *pte, oldpte; 42 pte_t *pte, oldpte;
43 spinlock_t *ptl; 43 spinlock_t *ptl;
44 unsigned long pages = 0;
44 45
45 pte = pte_offset_map_lock(mm, pmd, addr, &ptl); 46 pte = pte_offset_map_lock(mm, pmd, addr, &ptl);
46 arch_enter_lazy_mmu_mode(); 47 arch_enter_lazy_mmu_mode();
@@ -60,6 +61,7 @@ static void change_pte_range(struct mm_struct *mm, pmd_t *pmd,
60 ptent = pte_mkwrite(ptent); 61 ptent = pte_mkwrite(ptent);
61 62
62 ptep_modify_prot_commit(mm, addr, pte, ptent); 63 ptep_modify_prot_commit(mm, addr, pte, ptent);
64 pages++;
63 } else if (IS_ENABLED(CONFIG_MIGRATION) && !pte_file(oldpte)) { 65 } else if (IS_ENABLED(CONFIG_MIGRATION) && !pte_file(oldpte)) {
64 swp_entry_t entry = pte_to_swp_entry(oldpte); 66 swp_entry_t entry = pte_to_swp_entry(oldpte);
65 67
@@ -72,18 +74,22 @@ static void change_pte_range(struct mm_struct *mm, pmd_t *pmd,
72 set_pte_at(mm, addr, pte, 74 set_pte_at(mm, addr, pte,
73 swp_entry_to_pte(entry)); 75 swp_entry_to_pte(entry));
74 } 76 }
77 pages++;
75 } 78 }
76 } while (pte++, addr += PAGE_SIZE, addr != end); 79 } while (pte++, addr += PAGE_SIZE, addr != end);
77 arch_leave_lazy_mmu_mode(); 80 arch_leave_lazy_mmu_mode();
78 pte_unmap_unlock(pte - 1, ptl); 81 pte_unmap_unlock(pte - 1, ptl);
82
83 return pages;
79} 84}
80 85
81static inline void change_pmd_range(struct vm_area_struct *vma, pud_t *pud, 86static inline unsigned long change_pmd_range(struct vm_area_struct *vma, pud_t *pud,
82 unsigned long addr, unsigned long end, pgprot_t newprot, 87 unsigned long addr, unsigned long end, pgprot_t newprot,
83 int dirty_accountable) 88 int dirty_accountable)
84{ 89{
85 pmd_t *pmd; 90 pmd_t *pmd;
86 unsigned long next; 91 unsigned long next;
92 unsigned long pages = 0;
87 93
88 pmd = pmd_offset(pud, addr); 94 pmd = pmd_offset(pud, addr);
89 do { 95 do {
@@ -91,35 +97,42 @@ static inline void change_pmd_range(struct vm_area_struct *vma, pud_t *pud,
91 if (pmd_trans_huge(*pmd)) { 97 if (pmd_trans_huge(*pmd)) {
92 if (next - addr != HPAGE_PMD_SIZE) 98 if (next - addr != HPAGE_PMD_SIZE)
93 split_huge_page_pmd(vma->vm_mm, pmd); 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 continue; 102 continue;
103 }
96 /* fall through */ 104 /* fall through */
97 } 105 }
98 if (pmd_none_or_clear_bad(pmd)) 106 if (pmd_none_or_clear_bad(pmd))
99 continue; 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 dirty_accountable); 109 dirty_accountable);
102 } while (pmd++, addr = next, addr != end); 110 } while (pmd++, addr = next, addr != end);
111
112 return pages;
103} 113}
104 114
105static inline void change_pud_range(struct vm_area_struct *vma, pgd_t *pgd, 115static inline unsigned long change_pud_range(struct vm_area_struct *vma, pgd_t *pgd,
106 unsigned long addr, unsigned long end, pgprot_t newprot, 116 unsigned long addr, unsigned long end, pgprot_t newprot,
107 int dirty_accountable) 117 int dirty_accountable)
108{ 118{
109 pud_t *pud; 119 pud_t *pud;
110 unsigned long next; 120 unsigned long next;
121 unsigned long pages = 0;
111 122
112 pud = pud_offset(pgd, addr); 123 pud = pud_offset(pgd, addr);
113 do { 124 do {
114 next = pud_addr_end(addr, end); 125 next = pud_addr_end(addr, end);
115 if (pud_none_or_clear_bad(pud)) 126 if (pud_none_or_clear_bad(pud))
116 continue; 127 continue;
117 change_pmd_range(vma, pud, addr, next, newprot, 128 pages += change_pmd_range(vma, pud, addr, next, newprot,
118 dirty_accountable); 129 dirty_accountable);
119 } while (pud++, addr = next, addr != end); 130 } while (pud++, addr = next, addr != end);
131
132 return pages;
120} 133}
121 134
122static void change_protection(struct vm_area_struct *vma, 135static unsigned long change_protection_range(struct vm_area_struct *vma,
123 unsigned long addr, unsigned long end, pgprot_t newprot, 136 unsigned long addr, unsigned long end, pgprot_t newprot,
124 int dirty_accountable) 137 int dirty_accountable)
125{ 138{
@@ -127,6 +140,7 @@ static void change_protection(struct vm_area_struct *vma,
127 pgd_t *pgd; 140 pgd_t *pgd;
128 unsigned long next; 141 unsigned long next;
129 unsigned long start = addr; 142 unsigned long start = addr;
143 unsigned long pages = 0;
130 144
131 BUG_ON(addr >= end); 145 BUG_ON(addr >= end);
132 pgd = pgd_offset(mm, addr); 146 pgd = pgd_offset(mm, addr);
@@ -135,10 +149,30 @@ static void change_protection(struct vm_area_struct *vma,
135 next = pgd_addr_end(addr, end); 149 next = pgd_addr_end(addr, end);
136 if (pgd_none_or_clear_bad(pgd)) 150 if (pgd_none_or_clear_bad(pgd))
137 continue; 151 continue;
138 change_pud_range(vma, pgd, addr, next, newprot, 152 pages += change_pud_range(vma, pgd, addr, next, newprot,
139 dirty_accountable); 153 dirty_accountable);
140 } while (pgd++, addr = next, addr != end); 154 } while (pgd++, addr = next, addr != end);
155
141 flush_tlb_range(vma, start, end); 156 flush_tlb_range(vma, start, end);
157
158 return pages;
159}
160
161unsigned 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;
142} 176}
143 177
144int 178int
@@ -213,12 +247,8 @@ success:
213 dirty_accountable = 1; 247 dirty_accountable = 1;
214 } 248 }
215 249
216 mmu_notifier_invalidate_range_start(mm, start, end); 250 change_protection(vma, start, end, vma->vm_page_prot, dirty_accountable);
217 if (is_vm_hugetlb_page(vma)) 251
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);
222 vm_stat_account(mm, oldflags, vma->vm_file, -nrpages); 252 vm_stat_account(mm, oldflags, vma->vm_file, -nrpages);
223 vm_stat_account(mm, newflags, vma->vm_file, nrpages); 253 vm_stat_account(mm, newflags, vma->vm_file, nrpages);
224 perf_event_mmap(vma); 254 perf_event_mmap(vma);