aboutsummaryrefslogtreecommitdiffstats
path: root/mm/mremap.c
diff options
context:
space:
mode:
authorJonathan Herman <hermanjl@cs.unc.edu>2013-01-17 16:15:55 -0500
committerJonathan Herman <hermanjl@cs.unc.edu>2013-01-17 16:15:55 -0500
commit8dea78da5cee153b8af9c07a2745f6c55057fe12 (patch)
treea8f4d49d63b1ecc92f2fddceba0655b2472c5bd9 /mm/mremap.c
parent406089d01562f1e2bf9f089fd7637009ebaad589 (diff)
Patched in Tegra support.
Diffstat (limited to 'mm/mremap.c')
-rw-r--r--mm/mremap.c132
1 files changed, 51 insertions, 81 deletions
diff --git a/mm/mremap.c b/mm/mremap.c
index e1031e1f6a6..506fa44403d 100644
--- a/mm/mremap.c
+++ b/mm/mremap.c
@@ -41,7 +41,8 @@ static pmd_t *get_old_pmd(struct mm_struct *mm, unsigned long addr)
41 return NULL; 41 return NULL;
42 42
43 pmd = pmd_offset(pud, addr); 43 pmd = pmd_offset(pud, addr);
44 if (pmd_none(*pmd)) 44 split_huge_page_pmd(mm, pmd);
45 if (pmd_none_or_clear_bad(pmd))
45 return NULL; 46 return NULL;
46 47
47 return pmd; 48 return pmd;
@@ -64,6 +65,8 @@ static pmd_t *alloc_new_pmd(struct mm_struct *mm, struct vm_area_struct *vma,
64 return NULL; 65 return NULL;
65 66
66 VM_BUG_ON(pmd_trans_huge(*pmd)); 67 VM_BUG_ON(pmd_trans_huge(*pmd));
68 if (pmd_none(*pmd) && __pte_alloc(mm, vma, pmd, addr))
69 return NULL;
67 70
68 return pmd; 71 return pmd;
69} 72}
@@ -71,41 +74,26 @@ static pmd_t *alloc_new_pmd(struct mm_struct *mm, struct vm_area_struct *vma,
71static void move_ptes(struct vm_area_struct *vma, pmd_t *old_pmd, 74static void move_ptes(struct vm_area_struct *vma, pmd_t *old_pmd,
72 unsigned long old_addr, unsigned long old_end, 75 unsigned long old_addr, unsigned long old_end,
73 struct vm_area_struct *new_vma, pmd_t *new_pmd, 76 struct vm_area_struct *new_vma, pmd_t *new_pmd,
74 unsigned long new_addr, bool need_rmap_locks) 77 unsigned long new_addr)
75{ 78{
76 struct address_space *mapping = NULL; 79 struct address_space *mapping = NULL;
77 struct anon_vma *anon_vma = NULL;
78 struct mm_struct *mm = vma->vm_mm; 80 struct mm_struct *mm = vma->vm_mm;
79 pte_t *old_pte, *new_pte, pte; 81 pte_t *old_pte, *new_pte, pte;
80 spinlock_t *old_ptl, *new_ptl; 82 spinlock_t *old_ptl, *new_ptl;
83 unsigned long old_start;
81 84
82 /* 85 old_start = old_addr;
83 * When need_rmap_locks is true, we take the i_mmap_mutex and anon_vma 86 mmu_notifier_invalidate_range_start(vma->vm_mm,
84 * locks to ensure that rmap will always observe either the old or the 87 old_start, old_end);
85 * new ptes. This is the easiest way to avoid races with 88 if (vma->vm_file) {
86 * truncate_pagecache(), page migration, etc... 89 /*
87 * 90 * Subtle point from Rajesh Venkatasubramanian: before
88 * When need_rmap_locks is false, we use other ways to avoid 91 * moving file-based ptes, we must lock truncate_pagecache
89 * such races: 92 * out, since it might clean the dst vma before the src vma,
90 * 93 * and we propagate stale pages into the dst afterward.
91 * - During exec() shift_arg_pages(), we use a specially tagged vma 94 */
92 * which rmap call sites look for using is_vma_temporary_stack(). 95 mapping = vma->vm_file->f_mapping;
93 * 96 mutex_lock(&mapping->i_mmap_mutex);
94 * - During mremap(), new_vma is often known to be placed after vma
95 * in rmap traversal order. This ensures rmap will always observe
96 * either the old pte, or the new pte, or both (the page table locks
97 * serialize access to individual ptes, but only rmap traversal
98 * order guarantees that we won't miss both the old and new ptes).
99 */
100 if (need_rmap_locks) {
101 if (vma->vm_file) {
102 mapping = vma->vm_file->f_mapping;
103 mutex_lock(&mapping->i_mmap_mutex);
104 }
105 if (vma->anon_vma) {
106 anon_vma = vma->anon_vma;
107 anon_vma_lock_write(anon_vma);
108 }
109 } 97 }
110 98
111 /* 99 /*
@@ -123,7 +111,7 @@ static void move_ptes(struct vm_area_struct *vma, pmd_t *old_pmd,
123 new_pte++, new_addr += PAGE_SIZE) { 111 new_pte++, new_addr += PAGE_SIZE) {
124 if (pte_none(*old_pte)) 112 if (pte_none(*old_pte))
125 continue; 113 continue;
126 pte = ptep_get_and_clear(mm, old_addr, old_pte); 114 pte = ptep_clear_flush(vma, old_addr, old_pte);
127 pte = move_pte(pte, new_vma->vm_page_prot, old_addr, new_addr); 115 pte = move_pte(pte, new_vma->vm_page_prot, old_addr, new_addr);
128 set_pte_at(mm, new_addr, new_pte, pte); 116 set_pte_at(mm, new_addr, new_pte, pte);
129 } 117 }
@@ -133,75 +121,43 @@ static void move_ptes(struct vm_area_struct *vma, pmd_t *old_pmd,
133 spin_unlock(new_ptl); 121 spin_unlock(new_ptl);
134 pte_unmap(new_pte - 1); 122 pte_unmap(new_pte - 1);
135 pte_unmap_unlock(old_pte - 1, old_ptl); 123 pte_unmap_unlock(old_pte - 1, old_ptl);
136 if (anon_vma)
137 anon_vma_unlock(anon_vma);
138 if (mapping) 124 if (mapping)
139 mutex_unlock(&mapping->i_mmap_mutex); 125 mutex_unlock(&mapping->i_mmap_mutex);
126 mmu_notifier_invalidate_range_end(vma->vm_mm, old_start, old_end);
140} 127}
141 128
142#define LATENCY_LIMIT (64 * PAGE_SIZE) 129#define LATENCY_LIMIT (64 * PAGE_SIZE)
143 130
144unsigned long move_page_tables(struct vm_area_struct *vma, 131unsigned long move_page_tables(struct vm_area_struct *vma,
145 unsigned long old_addr, struct vm_area_struct *new_vma, 132 unsigned long old_addr, struct vm_area_struct *new_vma,
146 unsigned long new_addr, unsigned long len, 133 unsigned long new_addr, unsigned long len)
147 bool need_rmap_locks)
148{ 134{
149 unsigned long extent, next, old_end; 135 unsigned long extent, next, old_end;
150 pmd_t *old_pmd, *new_pmd; 136 pmd_t *old_pmd, *new_pmd;
151 bool need_flush = false;
152 unsigned long mmun_start; /* For mmu_notifiers */
153 unsigned long mmun_end; /* For mmu_notifiers */
154 137
155 old_end = old_addr + len; 138 old_end = old_addr + len;
156 flush_cache_range(vma, old_addr, old_end); 139 flush_cache_range(vma, old_addr, old_end);
157 140
158 mmun_start = old_addr;
159 mmun_end = old_end;
160 mmu_notifier_invalidate_range_start(vma->vm_mm, mmun_start, mmun_end);
161
162 for (; old_addr < old_end; old_addr += extent, new_addr += extent) { 141 for (; old_addr < old_end; old_addr += extent, new_addr += extent) {
163 cond_resched(); 142 cond_resched();
164 next = (old_addr + PMD_SIZE) & PMD_MASK; 143 next = (old_addr + PMD_SIZE) & PMD_MASK;
165 /* even if next overflowed, extent below will be ok */ 144 if (next - 1 > old_end)
145 next = old_end;
166 extent = next - old_addr; 146 extent = next - old_addr;
167 if (extent > old_end - old_addr)
168 extent = old_end - old_addr;
169 old_pmd = get_old_pmd(vma->vm_mm, old_addr); 147 old_pmd = get_old_pmd(vma->vm_mm, old_addr);
170 if (!old_pmd) 148 if (!old_pmd)
171 continue; 149 continue;
172 new_pmd = alloc_new_pmd(vma->vm_mm, vma, new_addr); 150 new_pmd = alloc_new_pmd(vma->vm_mm, vma, new_addr);
173 if (!new_pmd) 151 if (!new_pmd)
174 break; 152 break;
175 if (pmd_trans_huge(*old_pmd)) {
176 int err = 0;
177 if (extent == HPAGE_PMD_SIZE)
178 err = move_huge_pmd(vma, new_vma, old_addr,
179 new_addr, old_end,
180 old_pmd, new_pmd);
181 if (err > 0) {
182 need_flush = true;
183 continue;
184 } else if (!err) {
185 split_huge_page_pmd(vma, old_addr, old_pmd);
186 }
187 VM_BUG_ON(pmd_trans_huge(*old_pmd));
188 }
189 if (pmd_none(*new_pmd) && __pte_alloc(new_vma->vm_mm, new_vma,
190 new_pmd, new_addr))
191 break;
192 next = (new_addr + PMD_SIZE) & PMD_MASK; 153 next = (new_addr + PMD_SIZE) & PMD_MASK;
193 if (extent > next - new_addr) 154 if (extent > next - new_addr)
194 extent = next - new_addr; 155 extent = next - new_addr;
195 if (extent > LATENCY_LIMIT) 156 if (extent > LATENCY_LIMIT)
196 extent = LATENCY_LIMIT; 157 extent = LATENCY_LIMIT;
197 move_ptes(vma, old_pmd, old_addr, old_addr + extent, 158 move_ptes(vma, old_pmd, old_addr, old_addr + extent,
198 new_vma, new_pmd, new_addr, need_rmap_locks); 159 new_vma, new_pmd, new_addr);
199 need_flush = true;
200 } 160 }
201 if (likely(need_flush))
202 flush_tlb_range(vma, old_end-len, old_addr);
203
204 mmu_notifier_invalidate_range_end(vma->vm_mm, mmun_start, mmun_end);
205 161
206 return len + old_addr - old_end; /* how much done */ 162 return len + old_addr - old_end; /* how much done */
207} 163}
@@ -219,7 +175,6 @@ static unsigned long move_vma(struct vm_area_struct *vma,
219 unsigned long hiwater_vm; 175 unsigned long hiwater_vm;
220 int split = 0; 176 int split = 0;
221 int err; 177 int err;
222 bool need_rmap_locks;
223 178
224 /* 179 /*
225 * We'd prefer to avoid failure later on in do_munmap: 180 * We'd prefer to avoid failure later on in do_munmap:
@@ -241,21 +196,18 @@ static unsigned long move_vma(struct vm_area_struct *vma,
241 return err; 196 return err;
242 197
243 new_pgoff = vma->vm_pgoff + ((old_addr - vma->vm_start) >> PAGE_SHIFT); 198 new_pgoff = vma->vm_pgoff + ((old_addr - vma->vm_start) >> PAGE_SHIFT);
244 new_vma = copy_vma(&vma, new_addr, new_len, new_pgoff, 199 new_vma = copy_vma(&vma, new_addr, new_len, new_pgoff);
245 &need_rmap_locks);
246 if (!new_vma) 200 if (!new_vma)
247 return -ENOMEM; 201 return -ENOMEM;
248 202
249 moved_len = move_page_tables(vma, old_addr, new_vma, new_addr, old_len, 203 moved_len = move_page_tables(vma, old_addr, new_vma, new_addr, old_len);
250 need_rmap_locks);
251 if (moved_len < old_len) { 204 if (moved_len < old_len) {
252 /* 205 /*
253 * On error, move entries back from new area to old, 206 * On error, move entries back from new area to old,
254 * which will succeed since page tables still there, 207 * which will succeed since page tables still there,
255 * and then proceed to unmap new area instead of old. 208 * and then proceed to unmap new area instead of old.
256 */ 209 */
257 move_page_tables(new_vma, new_addr, vma, old_addr, moved_len, 210 move_page_tables(new_vma, new_addr, vma, old_addr, moved_len);
258 true);
259 vma = new_vma; 211 vma = new_vma;
260 old_len = new_len; 212 old_len = new_len;
261 old_addr = new_addr; 213 old_addr = new_addr;
@@ -281,6 +233,7 @@ static unsigned long move_vma(struct vm_area_struct *vma,
281 * If this were a serious issue, we'd add a flag to do_munmap(). 233 * If this were a serious issue, we'd add a flag to do_munmap().
282 */ 234 */
283 hiwater_vm = mm->hiwater_vm; 235 hiwater_vm = mm->hiwater_vm;
236 mm->total_vm += new_len >> PAGE_SHIFT;
284 vm_stat_account(mm, vma->vm_flags, vma->vm_file, new_len>>PAGE_SHIFT); 237 vm_stat_account(mm, vma->vm_flags, vma->vm_file, new_len>>PAGE_SHIFT);
285 238
286 if (do_munmap(mm, old_addr, old_len) < 0) { 239 if (do_munmap(mm, old_addr, old_len) < 0) {
@@ -349,7 +302,7 @@ static struct vm_area_struct *vma_to_resize(unsigned long addr,
349 302
350 if (vma->vm_flags & VM_ACCOUNT) { 303 if (vma->vm_flags & VM_ACCOUNT) {
351 unsigned long charged = (new_len - old_len) >> PAGE_SHIFT; 304 unsigned long charged = (new_len - old_len) >> PAGE_SHIFT;
352 if (security_vm_enough_memory_mm(mm, charged)) 305 if (security_vm_enough_memory(charged))
353 goto Efault; 306 goto Efault;
354 *p = charged; 307 *p = charged;
355 } 308 }
@@ -391,6 +344,10 @@ static unsigned long mremap_to(unsigned long addr,
391 if ((addr <= new_addr) && (addr+old_len) > new_addr) 344 if ((addr <= new_addr) && (addr+old_len) > new_addr)
392 goto out; 345 goto out;
393 346
347 ret = security_file_mmap(NULL, 0, 0, 0, new_addr, 1);
348 if (ret)
349 goto out;
350
394 ret = do_munmap(mm, new_addr, new_len); 351 ret = do_munmap(mm, new_addr, new_len);
395 if (ret) 352 if (ret)
396 goto out; 353 goto out;
@@ -448,17 +405,15 @@ static int vma_expandable(struct vm_area_struct *vma, unsigned long delta)
448 * MREMAP_FIXED option added 5-Dec-1999 by Benjamin LaHaise 405 * MREMAP_FIXED option added 5-Dec-1999 by Benjamin LaHaise
449 * This option implies MREMAP_MAYMOVE. 406 * This option implies MREMAP_MAYMOVE.
450 */ 407 */
451SYSCALL_DEFINE5(mremap, unsigned long, addr, unsigned long, old_len, 408unsigned long do_mremap(unsigned long addr,
452 unsigned long, new_len, unsigned long, flags, 409 unsigned long old_len, unsigned long new_len,
453 unsigned long, new_addr) 410 unsigned long flags, unsigned long new_addr)
454{ 411{
455 struct mm_struct *mm = current->mm; 412 struct mm_struct *mm = current->mm;
456 struct vm_area_struct *vma; 413 struct vm_area_struct *vma;
457 unsigned long ret = -EINVAL; 414 unsigned long ret = -EINVAL;
458 unsigned long charged = 0; 415 unsigned long charged = 0;
459 416
460 down_write(&current->mm->mmap_sem);
461
462 if (flags & ~(MREMAP_FIXED | MREMAP_MAYMOVE)) 417 if (flags & ~(MREMAP_FIXED | MREMAP_MAYMOVE))
463 goto out; 418 goto out;
464 419
@@ -517,6 +472,7 @@ SYSCALL_DEFINE5(mremap, unsigned long, addr, unsigned long, old_len,
517 goto out; 472 goto out;
518 } 473 }
519 474
475 mm->total_vm += pages;
520 vm_stat_account(mm, vma->vm_flags, vma->vm_file, pages); 476 vm_stat_account(mm, vma->vm_flags, vma->vm_file, pages);
521 if (vma->vm_flags & VM_LOCKED) { 477 if (vma->vm_flags & VM_LOCKED) {
522 mm->locked_vm += pages; 478 mm->locked_vm += pages;
@@ -547,11 +503,25 @@ SYSCALL_DEFINE5(mremap, unsigned long, addr, unsigned long, old_len,
547 goto out; 503 goto out;
548 } 504 }
549 505
506 ret = security_file_mmap(NULL, 0, 0, 0, new_addr, 1);
507 if (ret)
508 goto out;
550 ret = move_vma(vma, addr, old_len, new_len, new_addr); 509 ret = move_vma(vma, addr, old_len, new_len, new_addr);
551 } 510 }
552out: 511out:
553 if (ret & ~PAGE_MASK) 512 if (ret & ~PAGE_MASK)
554 vm_unacct_memory(charged); 513 vm_unacct_memory(charged);
514 return ret;
515}
516
517SYSCALL_DEFINE5(mremap, unsigned long, addr, unsigned long, old_len,
518 unsigned long, new_len, unsigned long, flags,
519 unsigned long, new_addr)
520{
521 unsigned long ret;
522
523 down_write(&current->mm->mmap_sem);
524 ret = do_mremap(addr, old_len, new_len, flags, new_addr);
555 up_write(&current->mm->mmap_sem); 525 up_write(&current->mm->mmap_sem);
556 return ret; 526 return ret;
557} 527}