Merge branch 'uprobes/core' of git://git.kernel.org/pub/scm/linux/kernel/git/oleg/misc into perf/core
Pull uprobes fixes, cleanups and preparation for the ARM port from Oleg Nesterov. Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
7e0dd574cd
7 changed files with 65 additions and 56 deletions
|
@ -158,10 +158,8 @@ static int do_signal(struct pt_regs *regs)
|
||||||
|
|
||||||
void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags)
|
void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags)
|
||||||
{
|
{
|
||||||
if (thread_info_flags & _TIF_UPROBE) {
|
if (thread_info_flags & _TIF_UPROBE)
|
||||||
clear_thread_flag(TIF_UPROBE);
|
|
||||||
uprobe_notify_resume(regs);
|
uprobe_notify_resume(regs);
|
||||||
}
|
|
||||||
|
|
||||||
if (thread_info_flags & _TIF_SIGPENDING)
|
if (thread_info_flags & _TIF_SIGPENDING)
|
||||||
do_signal(regs);
|
do_signal(regs);
|
||||||
|
|
|
@ -64,6 +64,8 @@ int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
|
||||||
autask->saved_trap_nr = current->thread.trap_nr;
|
autask->saved_trap_nr = current->thread.trap_nr;
|
||||||
current->thread.trap_nr = UPROBE_TRAP_NR;
|
current->thread.trap_nr = UPROBE_TRAP_NR;
|
||||||
regs->nip = current->utask->xol_vaddr;
|
regs->nip = current->utask->xol_vaddr;
|
||||||
|
|
||||||
|
user_enable_single_step(current);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,6 +121,8 @@ int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
|
||||||
* to be executed.
|
* to be executed.
|
||||||
*/
|
*/
|
||||||
regs->nip = utask->vaddr + MAX_UINSN_BYTES;
|
regs->nip = utask->vaddr + MAX_UINSN_BYTES;
|
||||||
|
|
||||||
|
user_disable_single_step(current);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,6 +166,8 @@ void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
|
||||||
|
|
||||||
current->thread.trap_nr = utask->autask.saved_trap_nr;
|
current->thread.trap_nr = utask->autask.saved_trap_nr;
|
||||||
instruction_pointer_set(regs, utask->vaddr);
|
instruction_pointer_set(regs, utask->vaddr);
|
||||||
|
|
||||||
|
user_disable_single_step(current);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -478,6 +478,11 @@ int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
|
||||||
regs->ip = current->utask->xol_vaddr;
|
regs->ip = current->utask->xol_vaddr;
|
||||||
pre_xol_rip_insn(auprobe, regs, autask);
|
pre_xol_rip_insn(auprobe, regs, autask);
|
||||||
|
|
||||||
|
autask->saved_tf = !!(regs->flags & X86_EFLAGS_TF);
|
||||||
|
regs->flags |= X86_EFLAGS_TF;
|
||||||
|
if (test_tsk_thread_flag(current, TIF_BLOCKSTEP))
|
||||||
|
set_task_blockstep(current, false);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -603,6 +608,16 @@ int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
|
||||||
if (auprobe->fixups & UPROBE_FIX_CALL)
|
if (auprobe->fixups & UPROBE_FIX_CALL)
|
||||||
result = adjust_ret_addr(regs->sp, correction);
|
result = adjust_ret_addr(regs->sp, correction);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* arch_uprobe_pre_xol() doesn't save the state of TIF_BLOCKSTEP
|
||||||
|
* so we can get an extra SIGTRAP if we do not clear TF. We need
|
||||||
|
* to examine the opcode to make it right.
|
||||||
|
*/
|
||||||
|
if (utask->autask.saved_tf)
|
||||||
|
send_sig(SIGTRAP, current, 0);
|
||||||
|
else if (!(auprobe->fixups & UPROBE_FIX_SETF))
|
||||||
|
regs->flags &= ~X86_EFLAGS_TF;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -647,6 +662,10 @@ void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
|
||||||
current->thread.trap_nr = utask->autask.saved_trap_nr;
|
current->thread.trap_nr = utask->autask.saved_trap_nr;
|
||||||
handle_riprel_post_xol(auprobe, regs, NULL);
|
handle_riprel_post_xol(auprobe, regs, NULL);
|
||||||
instruction_pointer_set(regs, utask->vaddr);
|
instruction_pointer_set(regs, utask->vaddr);
|
||||||
|
|
||||||
|
/* clear TF if it was set by us in arch_uprobe_pre_xol() */
|
||||||
|
if (!utask->autask.saved_tf)
|
||||||
|
regs->flags &= ~X86_EFLAGS_TF;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -676,38 +695,3 @@ bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
|
||||||
send_sig(SIGTRAP, current, 0);
|
send_sig(SIGTRAP, current, 0);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void arch_uprobe_enable_step(struct arch_uprobe *auprobe)
|
|
||||||
{
|
|
||||||
struct task_struct *task = current;
|
|
||||||
struct arch_uprobe_task *autask = &task->utask->autask;
|
|
||||||
struct pt_regs *regs = task_pt_regs(task);
|
|
||||||
|
|
||||||
autask->saved_tf = !!(regs->flags & X86_EFLAGS_TF);
|
|
||||||
|
|
||||||
regs->flags |= X86_EFLAGS_TF;
|
|
||||||
if (test_tsk_thread_flag(task, TIF_BLOCKSTEP))
|
|
||||||
set_task_blockstep(task, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void arch_uprobe_disable_step(struct arch_uprobe *auprobe)
|
|
||||||
{
|
|
||||||
struct task_struct *task = current;
|
|
||||||
struct arch_uprobe_task *autask = &task->utask->autask;
|
|
||||||
bool trapped = (task->utask->state == UTASK_SSTEP_TRAPPED);
|
|
||||||
struct pt_regs *regs = task_pt_regs(task);
|
|
||||||
/*
|
|
||||||
* The state of TIF_BLOCKSTEP was not saved so we can get an extra
|
|
||||||
* SIGTRAP if we do not clear TF. We need to examine the opcode to
|
|
||||||
* make it right.
|
|
||||||
*/
|
|
||||||
if (unlikely(trapped)) {
|
|
||||||
if (!autask->saved_tf)
|
|
||||||
regs->flags &= ~X86_EFLAGS_TF;
|
|
||||||
} else {
|
|
||||||
if (autask->saved_tf)
|
|
||||||
send_sig(SIGTRAP, task, 0);
|
|
||||||
else if (!(auprobe->fixups & UPROBE_FIX_SETF))
|
|
||||||
regs->flags &= ~X86_EFLAGS_TF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -97,12 +97,12 @@ extern int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_con
|
||||||
extern void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc);
|
extern void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc);
|
||||||
extern int uprobe_mmap(struct vm_area_struct *vma);
|
extern int uprobe_mmap(struct vm_area_struct *vma);
|
||||||
extern void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned long end);
|
extern void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned long end);
|
||||||
|
extern void uprobe_start_dup_mmap(void);
|
||||||
|
extern void uprobe_end_dup_mmap(void);
|
||||||
extern void uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm);
|
extern void uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm);
|
||||||
extern void uprobe_free_utask(struct task_struct *t);
|
extern void uprobe_free_utask(struct task_struct *t);
|
||||||
extern void uprobe_copy_process(struct task_struct *t);
|
extern void uprobe_copy_process(struct task_struct *t);
|
||||||
extern unsigned long __weak uprobe_get_swbp_addr(struct pt_regs *regs);
|
extern unsigned long __weak uprobe_get_swbp_addr(struct pt_regs *regs);
|
||||||
extern void __weak arch_uprobe_enable_step(struct arch_uprobe *arch);
|
|
||||||
extern void __weak arch_uprobe_disable_step(struct arch_uprobe *arch);
|
|
||||||
extern int uprobe_post_sstep_notifier(struct pt_regs *regs);
|
extern int uprobe_post_sstep_notifier(struct pt_regs *regs);
|
||||||
extern int uprobe_pre_sstep_notifier(struct pt_regs *regs);
|
extern int uprobe_pre_sstep_notifier(struct pt_regs *regs);
|
||||||
extern void uprobe_notify_resume(struct pt_regs *regs);
|
extern void uprobe_notify_resume(struct pt_regs *regs);
|
||||||
|
@ -129,6 +129,12 @@ static inline void
|
||||||
uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned long end)
|
uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned long end)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
static inline void uprobe_start_dup_mmap(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
static inline void uprobe_end_dup_mmap(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
static inline void
|
static inline void
|
||||||
uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm)
|
uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm)
|
||||||
{
|
{
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#include <linux/ptrace.h> /* user_enable_single_step */
|
#include <linux/ptrace.h> /* user_enable_single_step */
|
||||||
#include <linux/kdebug.h> /* notifier mechanism */
|
#include <linux/kdebug.h> /* notifier mechanism */
|
||||||
#include "../../mm/internal.h" /* munlock_vma_page */
|
#include "../../mm/internal.h" /* munlock_vma_page */
|
||||||
|
#include <linux/percpu-rwsem.h>
|
||||||
|
|
||||||
#include <linux/uprobes.h>
|
#include <linux/uprobes.h>
|
||||||
|
|
||||||
|
@ -71,6 +72,8 @@ static struct mutex uprobes_mutex[UPROBES_HASH_SZ];
|
||||||
static struct mutex uprobes_mmap_mutex[UPROBES_HASH_SZ];
|
static struct mutex uprobes_mmap_mutex[UPROBES_HASH_SZ];
|
||||||
#define uprobes_mmap_hash(v) (&uprobes_mmap_mutex[((unsigned long)(v)) % UPROBES_HASH_SZ])
|
#define uprobes_mmap_hash(v) (&uprobes_mmap_mutex[((unsigned long)(v)) % UPROBES_HASH_SZ])
|
||||||
|
|
||||||
|
static struct percpu_rw_semaphore dup_mmap_sem;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* uprobe_events allows us to skip the uprobe_mmap if there are no uprobe
|
* uprobe_events allows us to skip the uprobe_mmap if there are no uprobe
|
||||||
* events active at this time. Probably a fine grained per inode count is
|
* events active at this time. Probably a fine grained per inode count is
|
||||||
|
@ -766,10 +769,13 @@ static int register_for_each_vma(struct uprobe *uprobe, bool is_register)
|
||||||
struct map_info *info;
|
struct map_info *info;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
|
percpu_down_write(&dup_mmap_sem);
|
||||||
info = build_map_info(uprobe->inode->i_mapping,
|
info = build_map_info(uprobe->inode->i_mapping,
|
||||||
uprobe->offset, is_register);
|
uprobe->offset, is_register);
|
||||||
if (IS_ERR(info))
|
if (IS_ERR(info)) {
|
||||||
return PTR_ERR(info);
|
err = PTR_ERR(info);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
while (info) {
|
while (info) {
|
||||||
struct mm_struct *mm = info->mm;
|
struct mm_struct *mm = info->mm;
|
||||||
|
@ -799,7 +805,8 @@ static int register_for_each_vma(struct uprobe *uprobe, bool is_register)
|
||||||
mmput(mm);
|
mmput(mm);
|
||||||
info = free_map_info(info);
|
info = free_map_info(info);
|
||||||
}
|
}
|
||||||
|
out:
|
||||||
|
percpu_up_write(&dup_mmap_sem);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1131,6 +1138,16 @@ void uprobe_clear_state(struct mm_struct *mm)
|
||||||
kfree(area);
|
kfree(area);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void uprobe_start_dup_mmap(void)
|
||||||
|
{
|
||||||
|
percpu_down_read(&dup_mmap_sem);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uprobe_end_dup_mmap(void)
|
||||||
|
{
|
||||||
|
percpu_up_read(&dup_mmap_sem);
|
||||||
|
}
|
||||||
|
|
||||||
void uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm)
|
void uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm)
|
||||||
{
|
{
|
||||||
newmm->uprobes_state.xol_area = NULL;
|
newmm->uprobes_state.xol_area = NULL;
|
||||||
|
@ -1199,6 +1216,11 @@ static unsigned long xol_get_insn_slot(struct uprobe *uprobe, unsigned long slot
|
||||||
vaddr = kmap_atomic(area->page);
|
vaddr = kmap_atomic(area->page);
|
||||||
memcpy(vaddr + offset, uprobe->arch.insn, MAX_UINSN_BYTES);
|
memcpy(vaddr + offset, uprobe->arch.insn, MAX_UINSN_BYTES);
|
||||||
kunmap_atomic(vaddr);
|
kunmap_atomic(vaddr);
|
||||||
|
/*
|
||||||
|
* We probably need flush_icache_user_range() but it needs vma.
|
||||||
|
* This should work on supported architectures too.
|
||||||
|
*/
|
||||||
|
flush_dcache_page(area->page);
|
||||||
|
|
||||||
return current->utask->xol_vaddr;
|
return current->utask->xol_vaddr;
|
||||||
}
|
}
|
||||||
|
@ -1430,16 +1452,6 @@ static struct uprobe *find_active_uprobe(unsigned long bp_vaddr, int *is_swbp)
|
||||||
return uprobe;
|
return uprobe;
|
||||||
}
|
}
|
||||||
|
|
||||||
void __weak arch_uprobe_enable_step(struct arch_uprobe *arch)
|
|
||||||
{
|
|
||||||
user_enable_single_step(current);
|
|
||||||
}
|
|
||||||
|
|
||||||
void __weak arch_uprobe_disable_step(struct arch_uprobe *arch)
|
|
||||||
{
|
|
||||||
user_disable_single_step(current);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Run handler and ask thread to singlestep.
|
* Run handler and ask thread to singlestep.
|
||||||
* Ensure all non-fatal signals cannot interrupt thread while it singlesteps.
|
* Ensure all non-fatal signals cannot interrupt thread while it singlesteps.
|
||||||
|
@ -1493,7 +1505,6 @@ static void handle_swbp(struct pt_regs *regs)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (!pre_ssout(uprobe, regs, bp_vaddr)) {
|
if (!pre_ssout(uprobe, regs, bp_vaddr)) {
|
||||||
arch_uprobe_enable_step(&uprobe->arch);
|
|
||||||
utask->active_uprobe = uprobe;
|
utask->active_uprobe = uprobe;
|
||||||
utask->state = UTASK_SSTEP;
|
utask->state = UTASK_SSTEP;
|
||||||
return;
|
return;
|
||||||
|
@ -1525,7 +1536,6 @@ static void handle_singlestep(struct uprobe_task *utask, struct pt_regs *regs)
|
||||||
else
|
else
|
||||||
WARN_ON_ONCE(1);
|
WARN_ON_ONCE(1);
|
||||||
|
|
||||||
arch_uprobe_disable_step(&uprobe->arch);
|
|
||||||
put_uprobe(uprobe);
|
put_uprobe(uprobe);
|
||||||
utask->active_uprobe = NULL;
|
utask->active_uprobe = NULL;
|
||||||
utask->state = UTASK_RUNNING;
|
utask->state = UTASK_RUNNING;
|
||||||
|
@ -1604,6 +1614,9 @@ static int __init init_uprobes(void)
|
||||||
mutex_init(&uprobes_mmap_mutex[i]);
|
mutex_init(&uprobes_mmap_mutex[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (percpu_init_rwsem(&dup_mmap_sem))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
return register_die_notifier(&uprobe_exception_nb);
|
return register_die_notifier(&uprobe_exception_nb);
|
||||||
}
|
}
|
||||||
module_init(init_uprobes);
|
module_init(init_uprobes);
|
||||||
|
|
|
@ -352,6 +352,7 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
|
||||||
unsigned long charge;
|
unsigned long charge;
|
||||||
struct mempolicy *pol;
|
struct mempolicy *pol;
|
||||||
|
|
||||||
|
uprobe_start_dup_mmap();
|
||||||
down_write(&oldmm->mmap_sem);
|
down_write(&oldmm->mmap_sem);
|
||||||
flush_cache_dup_mm(oldmm);
|
flush_cache_dup_mm(oldmm);
|
||||||
uprobe_dup_mmap(oldmm, mm);
|
uprobe_dup_mmap(oldmm, mm);
|
||||||
|
@ -469,6 +470,7 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
|
||||||
up_write(&mm->mmap_sem);
|
up_write(&mm->mmap_sem);
|
||||||
flush_tlb_mm(oldmm);
|
flush_tlb_mm(oldmm);
|
||||||
up_write(&oldmm->mmap_sem);
|
up_write(&oldmm->mmap_sem);
|
||||||
|
uprobe_end_dup_mmap();
|
||||||
return retval;
|
return retval;
|
||||||
fail_nomem_anon_vma_fork:
|
fail_nomem_anon_vma_fork:
|
||||||
mpol_put(pol);
|
mpol_put(pol);
|
||||||
|
|
|
@ -189,7 +189,7 @@ static int create_trace_uprobe(int argc, char **argv)
|
||||||
if (argv[0][0] == '-')
|
if (argv[0][0] == '-')
|
||||||
is_delete = true;
|
is_delete = true;
|
||||||
else if (argv[0][0] != 'p') {
|
else if (argv[0][0] != 'p') {
|
||||||
pr_info("Probe definition must be started with 'p', 'r' or" " '-'.\n");
|
pr_info("Probe definition must be started with 'p' or '-'.\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue