diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-17 16:15:55 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-17 16:15:55 -0500 |
commit | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (patch) | |
tree | a8f4d49d63b1ecc92f2fddceba0655b2472c5bd9 /mm/mprotect.c | |
parent | 406089d01562f1e2bf9f089fd7637009ebaad589 (diff) |
Patched in Tegra support.
Diffstat (limited to 'mm/mprotect.c')
-rw-r--r-- | mm/mprotect.c | 158 |
1 files changed, 34 insertions, 124 deletions
diff --git a/mm/mprotect.c b/mm/mprotect.c index 94722a4d6b4..5a688a2756b 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c | |||
@@ -35,16 +35,12 @@ static inline pgprot_t pgprot_modify(pgprot_t oldprot, pgprot_t newprot) | |||
35 | } | 35 | } |
36 | #endif | 36 | #endif |
37 | 37 | ||
38 | static unsigned long change_pte_range(struct vm_area_struct *vma, pmd_t *pmd, | 38 | static void 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, int prot_numa, bool *ret_all_same_node) | 40 | int dirty_accountable) |
41 | { | 41 | { |
42 | struct mm_struct *mm = vma->vm_mm; | ||
43 | pte_t *pte, oldpte; | 42 | pte_t *pte, oldpte; |
44 | spinlock_t *ptl; | 43 | spinlock_t *ptl; |
45 | unsigned long pages = 0; | ||
46 | bool all_same_node = true; | ||
47 | int last_nid = -1; | ||
48 | 44 | ||
49 | pte = pte_offset_map_lock(mm, pmd, addr, &ptl); | 45 | pte = pte_offset_map_lock(mm, pmd, addr, &ptl); |
50 | arch_enter_lazy_mmu_mode(); | 46 | arch_enter_lazy_mmu_mode(); |
@@ -52,45 +48,19 @@ static unsigned long change_pte_range(struct vm_area_struct *vma, pmd_t *pmd, | |||
52 | oldpte = *pte; | 48 | oldpte = *pte; |
53 | if (pte_present(oldpte)) { | 49 | if (pte_present(oldpte)) { |
54 | pte_t ptent; | 50 | pte_t ptent; |
55 | bool updated = false; | ||
56 | 51 | ||
57 | ptent = ptep_modify_prot_start(mm, addr, pte); | 52 | ptent = ptep_modify_prot_start(mm, addr, pte); |
58 | if (!prot_numa) { | 53 | ptent = pte_modify(ptent, newprot); |
59 | ptent = pte_modify(ptent, newprot); | ||
60 | updated = true; | ||
61 | } else { | ||
62 | struct page *page; | ||
63 | |||
64 | page = vm_normal_page(vma, addr, oldpte); | ||
65 | if (page) { | ||
66 | int this_nid = page_to_nid(page); | ||
67 | if (last_nid == -1) | ||
68 | last_nid = this_nid; | ||
69 | if (last_nid != this_nid) | ||
70 | all_same_node = false; | ||
71 | |||
72 | /* only check non-shared pages */ | ||
73 | if (!pte_numa(oldpte) && | ||
74 | page_mapcount(page) == 1) { | ||
75 | ptent = pte_mknuma(ptent); | ||
76 | updated = true; | ||
77 | } | ||
78 | } | ||
79 | } | ||
80 | 54 | ||
81 | /* | 55 | /* |
82 | * Avoid taking write faults for pages we know to be | 56 | * Avoid taking write faults for pages we know to be |
83 | * dirty. | 57 | * dirty. |
84 | */ | 58 | */ |
85 | if (dirty_accountable && pte_dirty(ptent)) { | 59 | if (dirty_accountable && pte_dirty(ptent)) |
86 | ptent = pte_mkwrite(ptent); | 60 | ptent = pte_mkwrite(ptent); |
87 | updated = true; | ||
88 | } | ||
89 | 61 | ||
90 | if (updated) | ||
91 | pages++; | ||
92 | ptep_modify_prot_commit(mm, addr, pte, ptent); | 62 | ptep_modify_prot_commit(mm, addr, pte, ptent); |
93 | } else if (IS_ENABLED(CONFIG_MIGRATION) && !pte_file(oldpte)) { | 63 | } else if (PAGE_MIGRATION && !pte_file(oldpte)) { |
94 | swp_entry_t entry = pte_to_swp_entry(oldpte); | 64 | swp_entry_t entry = pte_to_swp_entry(oldpte); |
95 | 65 | ||
96 | if (is_write_migration_entry(entry)) { | 66 | if (is_write_migration_entry(entry)) { |
@@ -102,101 +72,61 @@ static unsigned long change_pte_range(struct vm_area_struct *vma, pmd_t *pmd, | |||
102 | set_pte_at(mm, addr, pte, | 72 | set_pte_at(mm, addr, pte, |
103 | swp_entry_to_pte(entry)); | 73 | swp_entry_to_pte(entry)); |
104 | } | 74 | } |
105 | pages++; | ||
106 | } | 75 | } |
107 | } while (pte++, addr += PAGE_SIZE, addr != end); | 76 | } while (pte++, addr += PAGE_SIZE, addr != end); |
108 | arch_leave_lazy_mmu_mode(); | 77 | arch_leave_lazy_mmu_mode(); |
109 | pte_unmap_unlock(pte - 1, ptl); | 78 | pte_unmap_unlock(pte - 1, ptl); |
110 | |||
111 | *ret_all_same_node = all_same_node; | ||
112 | return pages; | ||
113 | } | ||
114 | |||
115 | #ifdef CONFIG_NUMA_BALANCING | ||
116 | static inline void change_pmd_protnuma(struct mm_struct *mm, unsigned long addr, | ||
117 | pmd_t *pmd) | ||
118 | { | ||
119 | spin_lock(&mm->page_table_lock); | ||
120 | set_pmd_at(mm, addr & PMD_MASK, pmd, pmd_mknuma(*pmd)); | ||
121 | spin_unlock(&mm->page_table_lock); | ||
122 | } | ||
123 | #else | ||
124 | static inline void change_pmd_protnuma(struct mm_struct *mm, unsigned long addr, | ||
125 | pmd_t *pmd) | ||
126 | { | ||
127 | BUG(); | ||
128 | } | 79 | } |
129 | #endif /* CONFIG_NUMA_BALANCING */ | ||
130 | 80 | ||
131 | static inline unsigned long change_pmd_range(struct vm_area_struct *vma, | 81 | static inline void change_pmd_range(struct vm_area_struct *vma, pud_t *pud, |
132 | pud_t *pud, unsigned long addr, unsigned long end, | 82 | unsigned long addr, unsigned long end, pgprot_t newprot, |
133 | pgprot_t newprot, int dirty_accountable, int prot_numa) | 83 | int dirty_accountable) |
134 | { | 84 | { |
135 | pmd_t *pmd; | 85 | pmd_t *pmd; |
136 | unsigned long next; | 86 | unsigned long next; |
137 | unsigned long pages = 0; | ||
138 | bool all_same_node; | ||
139 | 87 | ||
140 | pmd = pmd_offset(pud, addr); | 88 | pmd = pmd_offset(pud, addr); |
141 | do { | 89 | do { |
142 | next = pmd_addr_end(addr, end); | 90 | next = pmd_addr_end(addr, end); |
143 | if (pmd_trans_huge(*pmd)) { | 91 | if (pmd_trans_huge(*pmd)) { |
144 | if (next - addr != HPAGE_PMD_SIZE) | 92 | if (next - addr != HPAGE_PMD_SIZE) |
145 | split_huge_page_pmd(vma, addr, pmd); | 93 | split_huge_page_pmd(vma->vm_mm, pmd); |
146 | else if (change_huge_pmd(vma, pmd, addr, newprot, | 94 | else if (change_huge_pmd(vma, pmd, addr, newprot)) |
147 | prot_numa)) { | ||
148 | pages += HPAGE_PMD_NR; | ||
149 | continue; | 95 | continue; |
150 | } | ||
151 | /* fall through */ | 96 | /* fall through */ |
152 | } | 97 | } |
153 | if (pmd_none_or_clear_bad(pmd)) | 98 | if (pmd_none_or_clear_bad(pmd)) |
154 | continue; | 99 | continue; |
155 | pages += change_pte_range(vma, pmd, addr, next, newprot, | 100 | change_pte_range(vma->vm_mm, pmd, addr, next, newprot, |
156 | dirty_accountable, prot_numa, &all_same_node); | 101 | dirty_accountable); |
157 | |||
158 | /* | ||
159 | * If we are changing protections for NUMA hinting faults then | ||
160 | * set pmd_numa if the examined pages were all on the same | ||
161 | * node. This allows a regular PMD to be handled as one fault | ||
162 | * and effectively batches the taking of the PTL | ||
163 | */ | ||
164 | if (prot_numa && all_same_node) | ||
165 | change_pmd_protnuma(vma->vm_mm, addr, pmd); | ||
166 | } while (pmd++, addr = next, addr != end); | 102 | } while (pmd++, addr = next, addr != end); |
167 | |||
168 | return pages; | ||
169 | } | 103 | } |
170 | 104 | ||
171 | static inline unsigned long change_pud_range(struct vm_area_struct *vma, | 105 | static inline void change_pud_range(struct vm_area_struct *vma, pgd_t *pgd, |
172 | pgd_t *pgd, unsigned long addr, unsigned long end, | 106 | unsigned long addr, unsigned long end, pgprot_t newprot, |
173 | pgprot_t newprot, int dirty_accountable, int prot_numa) | 107 | int dirty_accountable) |
174 | { | 108 | { |
175 | pud_t *pud; | 109 | pud_t *pud; |
176 | unsigned long next; | 110 | unsigned long next; |
177 | unsigned long pages = 0; | ||
178 | 111 | ||
179 | pud = pud_offset(pgd, addr); | 112 | pud = pud_offset(pgd, addr); |
180 | do { | 113 | do { |
181 | next = pud_addr_end(addr, end); | 114 | next = pud_addr_end(addr, end); |
182 | if (pud_none_or_clear_bad(pud)) | 115 | if (pud_none_or_clear_bad(pud)) |
183 | continue; | 116 | continue; |
184 | pages += change_pmd_range(vma, pud, addr, next, newprot, | 117 | change_pmd_range(vma, pud, addr, next, newprot, |
185 | dirty_accountable, prot_numa); | 118 | dirty_accountable); |
186 | } while (pud++, addr = next, addr != end); | 119 | } while (pud++, addr = next, addr != end); |
187 | |||
188 | return pages; | ||
189 | } | 120 | } |
190 | 121 | ||
191 | static unsigned long change_protection_range(struct vm_area_struct *vma, | 122 | static void change_protection(struct vm_area_struct *vma, |
192 | unsigned long addr, unsigned long end, pgprot_t newprot, | 123 | unsigned long addr, unsigned long end, pgprot_t newprot, |
193 | int dirty_accountable, int prot_numa) | 124 | int dirty_accountable) |
194 | { | 125 | { |
195 | struct mm_struct *mm = vma->vm_mm; | 126 | struct mm_struct *mm = vma->vm_mm; |
196 | pgd_t *pgd; | 127 | pgd_t *pgd; |
197 | unsigned long next; | 128 | unsigned long next; |
198 | unsigned long start = addr; | 129 | unsigned long start = addr; |
199 | unsigned long pages = 0; | ||
200 | 130 | ||
201 | BUG_ON(addr >= end); | 131 | BUG_ON(addr >= end); |
202 | pgd = pgd_offset(mm, addr); | 132 | pgd = pgd_offset(mm, addr); |
@@ -205,32 +135,10 @@ static unsigned long change_protection_range(struct vm_area_struct *vma, | |||
205 | next = pgd_addr_end(addr, end); | 135 | next = pgd_addr_end(addr, end); |
206 | if (pgd_none_or_clear_bad(pgd)) | 136 | if (pgd_none_or_clear_bad(pgd)) |
207 | continue; | 137 | continue; |
208 | pages += change_pud_range(vma, pgd, addr, next, newprot, | 138 | change_pud_range(vma, pgd, addr, next, newprot, |
209 | dirty_accountable, prot_numa); | 139 | dirty_accountable); |
210 | } while (pgd++, addr = next, addr != end); | 140 | } while (pgd++, addr = next, addr != end); |
211 | 141 | flush_tlb_range(vma, start, end); | |
212 | /* Only flush the TLB if we actually modified any entries: */ | ||
213 | if (pages) | ||
214 | flush_tlb_range(vma, start, end); | ||
215 | |||
216 | return pages; | ||
217 | } | ||
218 | |||
219 | unsigned long change_protection(struct vm_area_struct *vma, unsigned long start, | ||
220 | unsigned long end, pgprot_t newprot, | ||
221 | int dirty_accountable, int prot_numa) | ||
222 | { | ||
223 | struct mm_struct *mm = vma->vm_mm; | ||
224 | unsigned long pages; | ||
225 | |||
226 | mmu_notifier_invalidate_range_start(mm, start, end); | ||
227 | if (is_vm_hugetlb_page(vma)) | ||
228 | pages = hugetlb_change_protection(vma, start, end, newprot); | ||
229 | else | ||
230 | pages = change_protection_range(vma, start, end, newprot, dirty_accountable, prot_numa); | ||
231 | mmu_notifier_invalidate_range_end(mm, start, end); | ||
232 | |||
233 | return pages; | ||
234 | } | 142 | } |
235 | 143 | ||
236 | int | 144 | int |
@@ -260,7 +168,7 @@ mprotect_fixup(struct vm_area_struct *vma, struct vm_area_struct **pprev, | |||
260 | if (!(oldflags & (VM_ACCOUNT|VM_WRITE|VM_HUGETLB| | 168 | if (!(oldflags & (VM_ACCOUNT|VM_WRITE|VM_HUGETLB| |
261 | VM_SHARED|VM_NORESERVE))) { | 169 | VM_SHARED|VM_NORESERVE))) { |
262 | charged = nrpages; | 170 | charged = nrpages; |
263 | if (security_vm_enough_memory_mm(mm, charged)) | 171 | if (security_vm_enough_memory(charged)) |
264 | return -ENOMEM; | 172 | return -ENOMEM; |
265 | newflags |= VM_ACCOUNT; | 173 | newflags |= VM_ACCOUNT; |
266 | } | 174 | } |
@@ -305,9 +213,12 @@ success: | |||
305 | dirty_accountable = 1; | 213 | dirty_accountable = 1; |
306 | } | 214 | } |
307 | 215 | ||
308 | change_protection(vma, start, end, vma->vm_page_prot, | 216 | mmu_notifier_invalidate_range_start(mm, start, end); |
309 | dirty_accountable, 0); | 217 | if (is_vm_hugetlb_page(vma)) |
310 | 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); | ||
311 | vm_stat_account(mm, oldflags, vma->vm_file, -nrpages); | 222 | vm_stat_account(mm, oldflags, vma->vm_file, -nrpages); |
312 | vm_stat_account(mm, newflags, vma->vm_file, nrpages); | 223 | vm_stat_account(mm, newflags, vma->vm_file, nrpages); |
313 | perf_event_mmap(vma); | 224 | perf_event_mmap(vma); |
@@ -351,11 +262,10 @@ SYSCALL_DEFINE3(mprotect, unsigned long, start, size_t, len, | |||
351 | 262 | ||
352 | down_write(¤t->mm->mmap_sem); | 263 | down_write(¤t->mm->mmap_sem); |
353 | 264 | ||
354 | vma = find_vma(current->mm, start); | 265 | vma = find_vma_prev(current->mm, start, &prev); |
355 | error = -ENOMEM; | 266 | error = -ENOMEM; |
356 | if (!vma) | 267 | if (!vma) |
357 | goto out; | 268 | goto out; |
358 | prev = vma->vm_prev; | ||
359 | if (unlikely(grows & PROT_GROWSDOWN)) { | 269 | if (unlikely(grows & PROT_GROWSDOWN)) { |
360 | if (vma->vm_start >= end) | 270 | if (vma->vm_start >= end) |
361 | goto out; | 271 | goto out; |
@@ -363,7 +273,8 @@ SYSCALL_DEFINE3(mprotect, unsigned long, start, size_t, len, | |||
363 | error = -EINVAL; | 273 | error = -EINVAL; |
364 | if (!(vma->vm_flags & VM_GROWSDOWN)) | 274 | if (!(vma->vm_flags & VM_GROWSDOWN)) |
365 | goto out; | 275 | goto out; |
366 | } else { | 276 | } |
277 | else { | ||
367 | if (vma->vm_start > start) | 278 | if (vma->vm_start > start) |
368 | goto out; | 279 | goto out; |
369 | if (unlikely(grows & PROT_GROWSUP)) { | 280 | if (unlikely(grows & PROT_GROWSUP)) { |
@@ -379,10 +290,9 @@ SYSCALL_DEFINE3(mprotect, unsigned long, start, size_t, len, | |||
379 | for (nstart = start ; ; ) { | 290 | for (nstart = start ; ; ) { |
380 | unsigned long newflags; | 291 | unsigned long newflags; |
381 | 292 | ||
382 | /* Here we know that vma->vm_start <= nstart < vma->vm_end. */ | 293 | /* Here we know that vma->vm_start <= nstart < vma->vm_end. */ |
383 | 294 | ||
384 | newflags = vm_flags; | 295 | newflags = vm_flags | (vma->vm_flags & ~(VM_READ | VM_WRITE | VM_EXEC)); |
385 | newflags |= (vma->vm_flags & ~(VM_READ | VM_WRITE | VM_EXEC)); | ||
386 | 296 | ||
387 | /* newflags >> 4 shift VM_MAY% in place of VM_% */ | 297 | /* newflags >> 4 shift VM_MAY% in place of VM_% */ |
388 | if ((newflags & ~(newflags >> 4)) & (VM_READ | VM_WRITE | VM_EXEC)) { | 298 | if ((newflags & ~(newflags >> 4)) & (VM_READ | VM_WRITE | VM_EXEC)) { |