Commit 98bb3188 authored by Ingo Molnar's avatar Ingo Molnar

Merge branch 'perf/urgent' of...

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