diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/events/uprobes.c | 81 | ||||
-rw-r--r-- | kernel/resource.c | 4 | ||||
-rw-r--r-- | kernel/sched/idle.c | 1 | ||||
-rw-r--r-- | kernel/sysctl.c | 6 |
4 files changed, 68 insertions, 24 deletions
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 84fa00497c49..94d38a39d72e 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #include <linux/percpu-rwsem.h> | 26 | #include <linux/percpu-rwsem.h> |
27 | #include <linux/task_work.h> | 27 | #include <linux/task_work.h> |
28 | #include <linux/shmem_fs.h> | 28 | #include <linux/shmem_fs.h> |
29 | #include <linux/khugepaged.h> | ||
29 | 30 | ||
30 | #include <linux/uprobes.h> | 31 | #include <linux/uprobes.h> |
31 | 32 | ||
@@ -143,17 +144,19 @@ static loff_t vaddr_to_offset(struct vm_area_struct *vma, unsigned long vaddr) | |||
143 | * | 144 | * |
144 | * @vma: vma that holds the pte pointing to page | 145 | * @vma: vma that holds the pte pointing to page |
145 | * @addr: address the old @page is mapped at | 146 | * @addr: address the old @page is mapped at |
146 | * @page: the cowed page we are replacing by kpage | 147 | * @old_page: the page we are replacing by new_page |
147 | * @kpage: the modified page we replace page by | 148 | * @new_page: the modified page we replace page by |
148 | * | 149 | * |
149 | * Returns 0 on success, -EFAULT on failure. | 150 | * If @new_page is NULL, only unmap @old_page. |
151 | * | ||
152 | * Returns 0 on success, negative error code otherwise. | ||
150 | */ | 153 | */ |
151 | static int __replace_page(struct vm_area_struct *vma, unsigned long addr, | 154 | static int __replace_page(struct vm_area_struct *vma, unsigned long addr, |
152 | struct page *old_page, struct page *new_page) | 155 | struct page *old_page, struct page *new_page) |
153 | { | 156 | { |
154 | struct mm_struct *mm = vma->vm_mm; | 157 | struct mm_struct *mm = vma->vm_mm; |
155 | struct page_vma_mapped_walk pvmw = { | 158 | struct page_vma_mapped_walk pvmw = { |
156 | .page = old_page, | 159 | .page = compound_head(old_page), |
157 | .vma = vma, | 160 | .vma = vma, |
158 | .address = addr, | 161 | .address = addr, |
159 | }; | 162 | }; |
@@ -164,12 +167,12 @@ static int __replace_page(struct vm_area_struct *vma, unsigned long addr, | |||
164 | mmu_notifier_range_init(&range, MMU_NOTIFY_CLEAR, 0, vma, mm, addr, | 167 | mmu_notifier_range_init(&range, MMU_NOTIFY_CLEAR, 0, vma, mm, addr, |
165 | addr + PAGE_SIZE); | 168 | addr + PAGE_SIZE); |
166 | 169 | ||
167 | VM_BUG_ON_PAGE(PageTransHuge(old_page), old_page); | 170 | if (new_page) { |
168 | 171 | err = mem_cgroup_try_charge(new_page, vma->vm_mm, GFP_KERNEL, | |
169 | err = mem_cgroup_try_charge(new_page, vma->vm_mm, GFP_KERNEL, &memcg, | 172 | &memcg, false); |
170 | false); | 173 | if (err) |
171 | if (err) | 174 | return err; |
172 | return err; | 175 | } |
173 | 176 | ||
174 | /* For try_to_free_swap() and munlock_vma_page() below */ | 177 | /* For try_to_free_swap() and munlock_vma_page() below */ |
175 | lock_page(old_page); | 178 | lock_page(old_page); |
@@ -177,15 +180,20 @@ static int __replace_page(struct vm_area_struct *vma, unsigned long addr, | |||
177 | mmu_notifier_invalidate_range_start(&range); | 180 | mmu_notifier_invalidate_range_start(&range); |
178 | err = -EAGAIN; | 181 | err = -EAGAIN; |
179 | if (!page_vma_mapped_walk(&pvmw)) { | 182 | if (!page_vma_mapped_walk(&pvmw)) { |
180 | mem_cgroup_cancel_charge(new_page, memcg, false); | 183 | if (new_page) |
184 | mem_cgroup_cancel_charge(new_page, memcg, false); | ||
181 | goto unlock; | 185 | goto unlock; |
182 | } | 186 | } |
183 | VM_BUG_ON_PAGE(addr != pvmw.address, old_page); | 187 | VM_BUG_ON_PAGE(addr != pvmw.address, old_page); |
184 | 188 | ||
185 | get_page(new_page); | 189 | if (new_page) { |
186 | page_add_new_anon_rmap(new_page, vma, addr, false); | 190 | get_page(new_page); |
187 | mem_cgroup_commit_charge(new_page, memcg, false, false); | 191 | page_add_new_anon_rmap(new_page, vma, addr, false); |
188 | lru_cache_add_active_or_unevictable(new_page, vma); | 192 | mem_cgroup_commit_charge(new_page, memcg, false, false); |
193 | lru_cache_add_active_or_unevictable(new_page, vma); | ||
194 | } else | ||
195 | /* no new page, just dec_mm_counter for old_page */ | ||
196 | dec_mm_counter(mm, MM_ANONPAGES); | ||
189 | 197 | ||
190 | if (!PageAnon(old_page)) { | 198 | if (!PageAnon(old_page)) { |
191 | dec_mm_counter(mm, mm_counter_file(old_page)); | 199 | dec_mm_counter(mm, mm_counter_file(old_page)); |
@@ -194,8 +202,9 @@ static int __replace_page(struct vm_area_struct *vma, unsigned long addr, | |||
194 | 202 | ||
195 | flush_cache_page(vma, addr, pte_pfn(*pvmw.pte)); | 203 | flush_cache_page(vma, addr, pte_pfn(*pvmw.pte)); |
196 | ptep_clear_flush_notify(vma, addr, pvmw.pte); | 204 | ptep_clear_flush_notify(vma, addr, pvmw.pte); |
197 | set_pte_at_notify(mm, addr, pvmw.pte, | 205 | if (new_page) |
198 | mk_pte(new_page, vma->vm_page_prot)); | 206 | set_pte_at_notify(mm, addr, pvmw.pte, |
207 | mk_pte(new_page, vma->vm_page_prot)); | ||
199 | 208 | ||
200 | page_remove_rmap(old_page, false); | 209 | page_remove_rmap(old_page, false); |
201 | if (!page_mapped(old_page)) | 210 | if (!page_mapped(old_page)) |
@@ -464,6 +473,7 @@ int uprobe_write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm, | |||
464 | struct page *old_page, *new_page; | 473 | struct page *old_page, *new_page; |
465 | struct vm_area_struct *vma; | 474 | struct vm_area_struct *vma; |
466 | int ret, is_register, ref_ctr_updated = 0; | 475 | int ret, is_register, ref_ctr_updated = 0; |
476 | bool orig_page_huge = false; | ||
467 | 477 | ||
468 | is_register = is_swbp_insn(&opcode); | 478 | is_register = is_swbp_insn(&opcode); |
469 | uprobe = container_of(auprobe, struct uprobe, arch); | 479 | uprobe = container_of(auprobe, struct uprobe, arch); |
@@ -471,7 +481,7 @@ int uprobe_write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm, | |||
471 | retry: | 481 | retry: |
472 | /* Read the page with vaddr into memory */ | 482 | /* Read the page with vaddr into memory */ |
473 | ret = get_user_pages_remote(NULL, mm, vaddr, 1, | 483 | ret = get_user_pages_remote(NULL, mm, vaddr, 1, |
474 | FOLL_FORCE | FOLL_SPLIT, &old_page, &vma, NULL); | 484 | FOLL_FORCE | FOLL_SPLIT_PMD, &old_page, &vma, NULL); |
475 | if (ret <= 0) | 485 | if (ret <= 0) |
476 | return ret; | 486 | return ret; |
477 | 487 | ||
@@ -488,6 +498,10 @@ retry: | |||
488 | ref_ctr_updated = 1; | 498 | ref_ctr_updated = 1; |
489 | } | 499 | } |
490 | 500 | ||
501 | ret = 0; | ||
502 | if (!is_register && !PageAnon(old_page)) | ||
503 | goto put_old; | ||
504 | |||
491 | ret = anon_vma_prepare(vma); | 505 | ret = anon_vma_prepare(vma); |
492 | if (ret) | 506 | if (ret) |
493 | goto put_old; | 507 | goto put_old; |
@@ -501,8 +515,33 @@ retry: | |||
501 | copy_highpage(new_page, old_page); | 515 | copy_highpage(new_page, old_page); |
502 | copy_to_page(new_page, vaddr, &opcode, UPROBE_SWBP_INSN_SIZE); | 516 | copy_to_page(new_page, vaddr, &opcode, UPROBE_SWBP_INSN_SIZE); |
503 | 517 | ||
518 | if (!is_register) { | ||
519 | struct page *orig_page; | ||
520 | pgoff_t index; | ||
521 | |||
522 | VM_BUG_ON_PAGE(!PageAnon(old_page), old_page); | ||
523 | |||
524 | index = vaddr_to_offset(vma, vaddr & PAGE_MASK) >> PAGE_SHIFT; | ||
525 | orig_page = find_get_page(vma->vm_file->f_inode->i_mapping, | ||
526 | index); | ||
527 | |||
528 | if (orig_page) { | ||
529 | if (PageUptodate(orig_page) && | ||
530 | pages_identical(new_page, orig_page)) { | ||
531 | /* let go new_page */ | ||
532 | put_page(new_page); | ||
533 | new_page = NULL; | ||
534 | |||
535 | if (PageCompound(orig_page)) | ||
536 | orig_page_huge = true; | ||
537 | } | ||
538 | put_page(orig_page); | ||
539 | } | ||
540 | } | ||
541 | |||
504 | ret = __replace_page(vma, vaddr, old_page, new_page); | 542 | ret = __replace_page(vma, vaddr, old_page, new_page); |
505 | put_page(new_page); | 543 | if (new_page) |
544 | put_page(new_page); | ||
506 | put_old: | 545 | put_old: |
507 | put_page(old_page); | 546 | put_page(old_page); |
508 | 547 | ||
@@ -513,6 +552,10 @@ put_old: | |||
513 | if (ret && is_register && ref_ctr_updated) | 552 | if (ret && is_register && ref_ctr_updated) |
514 | update_ref_ctr(uprobe, mm, -1); | 553 | update_ref_ctr(uprobe, mm, -1); |
515 | 554 | ||
555 | /* try collapse pmd for compound page */ | ||
556 | if (!ret && orig_page_huge) | ||
557 | collapse_pte_mapped_thp(mm, vaddr); | ||
558 | |||
516 | return ret; | 559 | return ret; |
517 | } | 560 | } |
518 | 561 | ||
diff --git a/kernel/resource.c b/kernel/resource.c index 74877e9d90ca..76036a41143b 100644 --- a/kernel/resource.c +++ b/kernel/resource.c | |||
@@ -487,8 +487,8 @@ int walk_system_ram_range(unsigned long start_pfn, unsigned long nr_pages, | |||
487 | while (start < end && | 487 | while (start < end && |
488 | !find_next_iomem_res(start, end, flags, IORES_DESC_NONE, | 488 | !find_next_iomem_res(start, end, flags, IORES_DESC_NONE, |
489 | false, &res)) { | 489 | false, &res)) { |
490 | pfn = (res.start + PAGE_SIZE - 1) >> PAGE_SHIFT; | 490 | pfn = PFN_UP(res.start); |
491 | end_pfn = (res.end + 1) >> PAGE_SHIFT; | 491 | end_pfn = PFN_DOWN(res.end + 1); |
492 | if (end_pfn > pfn) | 492 | if (end_pfn > pfn) |
493 | ret = (*func)(pfn, end_pfn - pfn, arg); | 493 | ret = (*func)(pfn, end_pfn - pfn, arg); |
494 | if (ret) | 494 | if (ret) |
diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c index c892c6280c9f..8dad5aa600ea 100644 --- a/kernel/sched/idle.c +++ b/kernel/sched/idle.c | |||
@@ -238,7 +238,6 @@ static void do_idle(void) | |||
238 | tick_nohz_idle_enter(); | 238 | tick_nohz_idle_enter(); |
239 | 239 | ||
240 | while (!need_resched()) { | 240 | while (!need_resched()) { |
241 | check_pgt_cache(); | ||
242 | rmb(); | 241 | rmb(); |
243 | 242 | ||
244 | local_irq_disable(); | 243 | local_irq_disable(); |
diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 078950d9605b..00fcea236eba 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c | |||
@@ -264,7 +264,8 @@ extern struct ctl_table epoll_table[]; | |||
264 | extern struct ctl_table firmware_config_table[]; | 264 | extern struct ctl_table firmware_config_table[]; |
265 | #endif | 265 | #endif |
266 | 266 | ||
267 | #ifdef HAVE_ARCH_PICK_MMAP_LAYOUT | 267 | #if defined(HAVE_ARCH_PICK_MMAP_LAYOUT) || \ |
268 | defined(CONFIG_ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT) | ||
268 | int sysctl_legacy_va_layout; | 269 | int sysctl_legacy_va_layout; |
269 | #endif | 270 | #endif |
270 | 271 | ||
@@ -1573,7 +1574,8 @@ static struct ctl_table vm_table[] = { | |||
1573 | .proc_handler = proc_dointvec, | 1574 | .proc_handler = proc_dointvec, |
1574 | .extra1 = SYSCTL_ZERO, | 1575 | .extra1 = SYSCTL_ZERO, |
1575 | }, | 1576 | }, |
1576 | #ifdef HAVE_ARCH_PICK_MMAP_LAYOUT | 1577 | #if defined(HAVE_ARCH_PICK_MMAP_LAYOUT) || \ |
1578 | defined(CONFIG_ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT) | ||
1577 | { | 1579 | { |
1578 | .procname = "legacy_va_layout", | 1580 | .procname = "legacy_va_layout", |
1579 | .data = &sysctl_legacy_va_layout, | 1581 | .data = &sysctl_legacy_va_layout, |