Merge branch 'perf/urgent' of git://git.kernel.org/pub/scm/linux/kernel/git/frederic/random-tracing into perf/urgent
This commit is contained in:
commit
98bb318864
@ -767,12 +767,20 @@ long arch_ptrace(struct task_struct *child, long request,
|
||||
|
||||
#ifdef CONFIG_HAVE_HW_BREAKPOINT
|
||||
case PTRACE_GETHBPREGS:
|
||||
if (ptrace_get_breakpoints(child) < 0)
|
||||
return -ESRCH;
|
||||
|
||||
ret = ptrace_gethbpregs(child, addr,
|
||||
(unsigned long __user *)data);
|
||||
ptrace_put_breakpoints(child);
|
||||
break;
|
||||
case PTRACE_SETHBPREGS:
|
||||
if (ptrace_get_breakpoints(child) < 0)
|
||||
return -ESRCH;
|
||||
|
||||
ret = ptrace_sethbpregs(child, addr,
|
||||
(unsigned long __user *)data);
|
||||
ptrace_put_breakpoints(child);
|
||||
break;
|
||||
#endif
|
||||
|
||||
|
@ -1591,7 +1591,10 @@ long arch_ptrace(struct task_struct *child, long request,
|
||||
}
|
||||
|
||||
case PTRACE_SET_DEBUGREG:
|
||||
if (ptrace_get_breakpoints(child) < 0)
|
||||
return -ESRCH;
|
||||
ret = ptrace_set_debugreg(child, addr, data);
|
||||
ptrace_put_breakpoints(child);
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_PPC64
|
||||
|
@ -117,7 +117,11 @@ void user_enable_single_step(struct task_struct *child)
|
||||
|
||||
set_tsk_thread_flag(child, TIF_SINGLESTEP);
|
||||
|
||||
if (ptrace_get_breakpoints(child) < 0)
|
||||
return;
|
||||
|
||||
set_single_step(child, pc);
|
||||
ptrace_put_breakpoints(child);
|
||||
}
|
||||
|
||||
void user_disable_single_step(struct task_struct *child)
|
||||
|
@ -608,6 +608,9 @@ static int ptrace_write_dr7(struct task_struct *tsk, unsigned long data)
|
||||
unsigned len, type;
|
||||
struct perf_event *bp;
|
||||
|
||||
if (ptrace_get_breakpoints(tsk) < 0)
|
||||
return -ESRCH;
|
||||
|
||||
data &= ~DR_CONTROL_RESERVED;
|
||||
old_dr7 = ptrace_get_dr7(thread->ptrace_bps);
|
||||
restore:
|
||||
@ -655,6 +658,9 @@ static int ptrace_write_dr7(struct task_struct *tsk, unsigned long data)
|
||||
}
|
||||
goto restore;
|
||||
}
|
||||
|
||||
ptrace_put_breakpoints(tsk);
|
||||
|
||||
return ((orig_ret < 0) ? orig_ret : rc);
|
||||
}
|
||||
|
||||
@ -668,10 +674,17 @@ static unsigned long ptrace_get_debugreg(struct task_struct *tsk, int n)
|
||||
|
||||
if (n < HBP_NUM) {
|
||||
struct perf_event *bp;
|
||||
|
||||
if (ptrace_get_breakpoints(tsk) < 0)
|
||||
return -ESRCH;
|
||||
|
||||
bp = thread->ptrace_bps[n];
|
||||
if (!bp)
|
||||
return 0;
|
||||
val = bp->hw.info.address;
|
||||
val = 0;
|
||||
else
|
||||
val = bp->hw.info.address;
|
||||
|
||||
ptrace_put_breakpoints(tsk);
|
||||
} else if (n == 6) {
|
||||
val = thread->debugreg6;
|
||||
} else if (n == 7) {
|
||||
@ -686,6 +699,10 @@ static int ptrace_set_breakpoint_addr(struct task_struct *tsk, int nr,
|
||||
struct perf_event *bp;
|
||||
struct thread_struct *t = &tsk->thread;
|
||||
struct perf_event_attr attr;
|
||||
int err = 0;
|
||||
|
||||
if (ptrace_get_breakpoints(tsk) < 0)
|
||||
return -ESRCH;
|
||||
|
||||
if (!t->ptrace_bps[nr]) {
|
||||
ptrace_breakpoint_init(&attr);
|
||||
@ -709,24 +726,23 @@ static int ptrace_set_breakpoint_addr(struct task_struct *tsk, int nr,
|
||||
* writing for the user. And anyway this is the previous
|
||||
* behaviour.
|
||||
*/
|
||||
if (IS_ERR(bp))
|
||||
return PTR_ERR(bp);
|
||||
if (IS_ERR(bp)) {
|
||||
err = PTR_ERR(bp);
|
||||
goto put;
|
||||
}
|
||||
|
||||
t->ptrace_bps[nr] = bp;
|
||||
} else {
|
||||
int err;
|
||||
|
||||
bp = t->ptrace_bps[nr];
|
||||
|
||||
attr = bp->attr;
|
||||
attr.bp_addr = addr;
|
||||
err = modify_user_hw_breakpoint(bp, &attr);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
put:
|
||||
ptrace_put_breakpoints(tsk);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -189,6 +189,10 @@ static inline void ptrace_init_task(struct task_struct *child, bool ptrace)
|
||||
child->ptrace = current->ptrace;
|
||||
__ptrace_link(child, current->parent);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HAVE_HW_BREAKPOINT
|
||||
atomic_set(&child->ptrace_bp_refcnt, 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
@ -350,6 +354,13 @@ extern int task_current_syscall(struct task_struct *target, long *callno,
|
||||
unsigned long args[6], unsigned int maxargs,
|
||||
unsigned long *sp, unsigned long *pc);
|
||||
|
||||
#endif
|
||||
#ifdef CONFIG_HAVE_HW_BREAKPOINT
|
||||
extern int ptrace_get_breakpoints(struct task_struct *tsk);
|
||||
extern void ptrace_put_breakpoints(struct task_struct *tsk);
|
||||
#else
|
||||
static inline void ptrace_put_breakpoints(struct task_struct *tsk) { }
|
||||
#endif /* CONFIG_HAVE_HW_BREAKPOINT */
|
||||
|
||||
#endif /* __KERNEL */
|
||||
|
||||
#endif
|
||||
|
@ -1537,6 +1537,9 @@ struct task_struct {
|
||||
unsigned long memsw_nr_pages; /* uncharged mem+swap usage */
|
||||
} memcg_batch;
|
||||
#endif
|
||||
#ifdef CONFIG_HAVE_HW_BREAKPOINT
|
||||
atomic_t ptrace_bp_refcnt;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Future-safe accessor for struct task_struct's cpus_allowed. */
|
||||
|
@ -1016,7 +1016,7 @@ NORET_TYPE void do_exit(long code)
|
||||
/*
|
||||
* FIXME: do that only when needed, using sched_exit tracepoint
|
||||
*/
|
||||
flush_ptrace_hw_breakpoint(tsk);
|
||||
ptrace_put_breakpoints(tsk);
|
||||
|
||||
exit_notify(tsk, group_dead);
|
||||
#ifdef CONFIG_NUMA
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/regset.h>
|
||||
#include <linux/hw_breakpoint.h>
|
||||
|
||||
|
||||
/*
|
||||
@ -879,3 +880,19 @@ asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid,
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_COMPAT */
|
||||
|
||||
#ifdef CONFIG_HAVE_HW_BREAKPOINT
|
||||
int ptrace_get_breakpoints(struct task_struct *tsk)
|
||||
{
|
||||
if (atomic_inc_not_zero(&tsk->ptrace_bp_refcnt))
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void ptrace_put_breakpoints(struct task_struct *tsk)
|
||||
{
|
||||
if (atomic_dec_and_test(&tsk->ptrace_bp_refcnt))
|
||||
flush_ptrace_hw_breakpoint(tsk);
|
||||
}
|
||||
#endif /* CONFIG_HAVE_HW_BREAKPOINT */
|
||||
|
Loading…
Reference in New Issue
Block a user