diff options
| author | Ingo Molnar <mingo@kernel.org> | 2014-06-05 05:18:03 -0400 |
|---|---|---|
| committer | Ingo Molnar <mingo@kernel.org> | 2014-06-05 06:26:27 -0400 |
| commit | c56d34064b6eb9f9cde9e35bbfe16eedf3d81f94 (patch) | |
| tree | 9877ef9e1b238e14a1878f10d51ea55fbca5f619 /kernel/events | |
| parent | b13fa91421213a8d1fd05086050f05e994f3b72d (diff) | |
| parent | a03b1e1c372b60183b8141cdd161316429fab5ac (diff) | |
Merge branch 'perf/uprobes' into perf/core
These bits from Oleg are fully cooked, ship them to Linus.
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'kernel/events')
| -rw-r--r-- | kernel/events/uprobes.c | 35 |
1 files changed, 22 insertions, 13 deletions
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index d1edc5e6fd03..3b02c72938a8 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c | |||
| @@ -127,7 +127,7 @@ struct xol_area { | |||
| 127 | */ | 127 | */ |
| 128 | static bool valid_vma(struct vm_area_struct *vma, bool is_register) | 128 | static bool valid_vma(struct vm_area_struct *vma, bool is_register) |
| 129 | { | 129 | { |
| 130 | vm_flags_t flags = VM_HUGETLB | VM_MAYEXEC | VM_SHARED; | 130 | vm_flags_t flags = VM_HUGETLB | VM_MAYEXEC | VM_MAYSHARE; |
| 131 | 131 | ||
| 132 | if (is_register) | 132 | if (is_register) |
| 133 | flags |= VM_WRITE; | 133 | flags |= VM_WRITE; |
| @@ -279,18 +279,13 @@ static int verify_opcode(struct page *page, unsigned long vaddr, uprobe_opcode_t | |||
| 279 | * supported by that architecture then we need to modify is_trap_at_addr and | 279 | * supported by that architecture then we need to modify is_trap_at_addr and |
| 280 | * uprobe_write_opcode accordingly. This would never be a problem for archs | 280 | * uprobe_write_opcode accordingly. This would never be a problem for archs |
| 281 | * that have fixed length instructions. | 281 | * that have fixed length instructions. |
| 282 | */ | 282 | * |
| 283 | |||
| 284 | /* | ||
| 285 | * uprobe_write_opcode - write the opcode at a given virtual address. | 283 | * uprobe_write_opcode - write the opcode at a given virtual address. |
| 286 | * @mm: the probed process address space. | 284 | * @mm: the probed process address space. |
| 287 | * @vaddr: the virtual address to store the opcode. | 285 | * @vaddr: the virtual address to store the opcode. |
| 288 | * @opcode: opcode to be written at @vaddr. | 286 | * @opcode: opcode to be written at @vaddr. |
| 289 | * | 287 | * |
| 290 | * Called with mm->mmap_sem held (for read and with a reference to | 288 | * Called with mm->mmap_sem held for write. |
| 291 | * mm). | ||
| 292 | * | ||
| 293 | * For mm @mm, write the opcode at @vaddr. | ||
| 294 | * Return 0 (success) or a negative errno. | 289 | * Return 0 (success) or a negative errno. |
| 295 | */ | 290 | */ |
| 296 | int uprobe_write_opcode(struct mm_struct *mm, unsigned long vaddr, | 291 | int uprobe_write_opcode(struct mm_struct *mm, unsigned long vaddr, |
| @@ -310,21 +305,25 @@ retry: | |||
| 310 | if (ret <= 0) | 305 | if (ret <= 0) |
| 311 | goto put_old; | 306 | goto put_old; |
| 312 | 307 | ||
| 308 | ret = anon_vma_prepare(vma); | ||
| 309 | if (ret) | ||
| 310 | goto put_old; | ||
| 311 | |||
| 313 | ret = -ENOMEM; | 312 | ret = -ENOMEM; |
| 314 | new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, vaddr); | 313 | new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, vaddr); |
| 315 | if (!new_page) | 314 | if (!new_page) |
| 316 | goto put_old; | 315 | goto put_old; |
| 317 | 316 | ||
| 318 | __SetPageUptodate(new_page); | 317 | if (mem_cgroup_charge_anon(new_page, mm, GFP_KERNEL)) |
| 318 | goto put_new; | ||
| 319 | 319 | ||
| 320 | __SetPageUptodate(new_page); | ||
| 320 | copy_highpage(new_page, old_page); | 321 | copy_highpage(new_page, old_page); |
| 321 | copy_to_page(new_page, vaddr, &opcode, UPROBE_SWBP_INSN_SIZE); | 322 | copy_to_page(new_page, vaddr, &opcode, UPROBE_SWBP_INSN_SIZE); |
| 322 | 323 | ||
| 323 | ret = anon_vma_prepare(vma); | ||
| 324 | if (ret) | ||
| 325 | goto put_new; | ||
| 326 | |||
| 327 | ret = __replace_page(vma, vaddr, old_page, new_page); | 324 | ret = __replace_page(vma, vaddr, old_page, new_page); |
| 325 | if (ret) | ||
| 326 | mem_cgroup_uncharge_page(new_page); | ||
| 328 | 327 | ||
| 329 | put_new: | 328 | put_new: |
| 330 | page_cache_release(new_page); | 329 | page_cache_release(new_page); |
| @@ -1352,6 +1351,16 @@ unsigned long __weak uprobe_get_swbp_addr(struct pt_regs *regs) | |||
| 1352 | return instruction_pointer(regs) - UPROBE_SWBP_INSN_SIZE; | 1351 | return instruction_pointer(regs) - UPROBE_SWBP_INSN_SIZE; |
| 1353 | } | 1352 | } |
| 1354 | 1353 | ||
| 1354 | unsigned long uprobe_get_trap_addr(struct pt_regs *regs) | ||
| 1355 | { | ||
| 1356 | struct uprobe_task *utask = current->utask; | ||
| 1357 | |||
| 1358 | if (unlikely(utask && utask->active_uprobe)) | ||
| 1359 | return utask->vaddr; | ||
| 1360 | |||
| 1361 | return instruction_pointer(regs); | ||
| 1362 | } | ||
| 1363 | |||
| 1355 | /* | 1364 | /* |
| 1356 | * Called with no locks held. | 1365 | * Called with no locks held. |
| 1357 | * Called in context of a exiting or a exec-ing thread. | 1366 | * Called in context of a exiting or a exec-ing thread. |
