Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 169809
b: refs/heads/master
c: 5fa10b2
h: refs/heads/master
i:
  169807: 08dc771
v: v3
  • Loading branch information
Frederic Weisbecker authored and Ingo Molnar committed Nov 27, 2009
1 parent 2e8d5d9 commit cbfb610
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 123 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: b2e74a265ded1a185f762ebaab967e9e0d008dd8
refs/heads/master: 5fa10b28e57f94a90535cfeafe89dcee9f47d540
74 changes: 41 additions & 33 deletions trunk/arch/x86/kernel/ptrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,34 @@ static unsigned long ptrace_get_dr7(struct perf_event *bp[])
return dr7;
}

static struct perf_event *
ptrace_modify_breakpoint(struct perf_event *bp, int len, int type,
struct task_struct *tsk)
{
int err;
int gen_len, gen_type;
DEFINE_BREAKPOINT_ATTR(attr);

/*
* We shoud have at least an inactive breakpoint at this
* slot. It means the user is writing dr7 without having
* written the address register first
*/
if (!bp)
return ERR_PTR(-EINVAL);

err = arch_bp_generic_fields(len, type, &gen_len, &gen_type);
if (err)
return ERR_PTR(err);

attr = bp->attr;
attr.bp_len = gen_len;
attr.bp_type = gen_type;
attr.disabled = 0;

return modify_user_hw_breakpoint(bp, &attr, bp->callback, tsk);
}

/*
* Handle ptrace writes to debug register 7.
*/
Expand All @@ -603,7 +631,6 @@ static int ptrace_write_dr7(struct task_struct *tsk, unsigned long data)
int i, orig_ret = 0, rc = 0;
int enabled, second_pass = 0;
unsigned len, type;
int gen_len, gen_type;
struct perf_event *bp;

data &= ~DR_CONTROL_RESERVED;
Expand Down Expand Up @@ -634,33 +661,12 @@ static int ptrace_write_dr7(struct task_struct *tsk, unsigned long data)
continue;
}

/*
* We shoud have at least an inactive breakpoint at this
* slot. It means the user is writing dr7 without having
* written the address register first
*/
if (!bp) {
rc = -EINVAL;
break;
}

rc = arch_bp_generic_fields(len, type, &gen_len, &gen_type);
if (rc)
break;

/*
* This is a temporary thing as bp is unregistered/registered
* to simulate modification
*/
bp = modify_user_hw_breakpoint(bp, bp->attr.bp_addr, gen_len,
gen_type, bp->callback,
tsk, true);
thread->ptrace_bps[i] = NULL;
bp = ptrace_modify_breakpoint(bp, len, type, tsk);

/* Incorrect bp, or we have a bug in bp API */
if (IS_ERR(bp)) {
rc = PTR_ERR(bp);
bp = NULL;
thread->ptrace_bps[i] = NULL;
break;
}
thread->ptrace_bps[i] = bp;
Expand Down Expand Up @@ -707,24 +713,26 @@ static int ptrace_set_breakpoint_addr(struct task_struct *tsk, int nr,
{
struct perf_event *bp;
struct thread_struct *t = &tsk->thread;
DEFINE_BREAKPOINT_ATTR(attr);

if (!t->ptrace_bps[nr]) {
/*
* Put stub len and type to register (reserve) an inactive but
* correct bp
*/
bp = register_user_hw_breakpoint(addr, HW_BREAKPOINT_LEN_1,
HW_BREAKPOINT_W,
ptrace_triggered, tsk,
false);
attr.bp_addr = addr;
attr.bp_len = HW_BREAKPOINT_LEN_1;
attr.bp_type = HW_BREAKPOINT_W;
attr.disabled = 1;

bp = register_user_hw_breakpoint(&attr, ptrace_triggered, tsk);
} else {
bp = t->ptrace_bps[nr];
t->ptrace_bps[nr] = NULL;
bp = modify_user_hw_breakpoint(bp, addr, bp->attr.bp_len,
bp->attr.bp_type,
bp->callback,
tsk,
bp->attr.disabled);

attr = bp->attr;
attr.bp_addr = addr;
bp = modify_user_hw_breakpoint(bp, &attr, bp->callback, tsk);
}
/*
* CHECKME: the previous code returned -EIO if the addr wasn't a
Expand Down
36 changes: 16 additions & 20 deletions trunk/include/linux/hw_breakpoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ enum {

#ifdef CONFIG_HAVE_HW_BREAKPOINT

/* As it's for in-kernel or ptrace use, we want it to be pinned */
#define DEFINE_BREAKPOINT_ATTR(name) \
struct perf_event_attr name = { \
.type = PERF_TYPE_BREAKPOINT, \
.size = sizeof(name), \
.pinned = 1, \
};

static inline unsigned long hw_breakpoint_addr(struct perf_event *bp)
{
return bp->attr.bp_addr;
Expand All @@ -36,22 +44,16 @@ static inline int hw_breakpoint_len(struct perf_event *bp)
}

extern struct perf_event *
register_user_hw_breakpoint(unsigned long addr,
int len,
int type,
register_user_hw_breakpoint(struct perf_event_attr *attr,
perf_callback_t triggered,
struct task_struct *tsk,
bool active);
struct task_struct *tsk);

/* FIXME: only change from the attr, and don't unregister */
extern struct perf_event *
modify_user_hw_breakpoint(struct perf_event *bp,
unsigned long addr,
int len,
int type,
struct perf_event_attr *attr,
perf_callback_t triggered,
struct task_struct *tsk,
bool active);
struct task_struct *tsk);

/*
* Kernel breakpoints are not associated with any particular thread.
Expand Down Expand Up @@ -89,20 +91,14 @@ static inline struct arch_hw_breakpoint *counter_arch_bp(struct perf_event *bp)
#else /* !CONFIG_HAVE_HW_BREAKPOINT */

static inline struct perf_event *
register_user_hw_breakpoint(unsigned long addr,
int len,
int type,
register_user_hw_breakpoint(struct perf_event_attr *attr,
perf_callback_t triggered,
struct task_struct *tsk,
bool active) { return NULL; }
struct task_struct *tsk) { return NULL; }
static inline struct perf_event *
modify_user_hw_breakpoint(struct perf_event *bp,
unsigned long addr,
int len,
int type,
struct perf_event_attr *attr,
perf_callback_t triggered,
struct task_struct *tsk,
bool active) { return NULL; }
struct task_struct *tsk) { return NULL; }
static inline struct perf_event *
register_wide_hw_breakpoint_cpu(unsigned long addr,
int len,
Expand Down
87 changes: 18 additions & 69 deletions trunk/kernel/hw_breakpoint.c
Original file line number Diff line number Diff line change
Expand Up @@ -289,90 +289,32 @@ int register_perf_hw_breakpoint(struct perf_event *bp)
return __register_perf_hw_breakpoint(bp);
}

/*
* Register a breakpoint bound to a task and a given cpu.
* If cpu is -1, the breakpoint is active for the task in every cpu
* If the task is -1, the breakpoint is active for every tasks in the given
* cpu.
*/
static struct perf_event *
register_user_hw_breakpoint_cpu(unsigned long addr,
int len,
int type,
perf_callback_t triggered,
pid_t pid,
int cpu,
bool active)
{
struct perf_event_attr *attr;
struct perf_event *bp;

attr = kzalloc(sizeof(*attr), GFP_KERNEL);
if (!attr)
return ERR_PTR(-ENOMEM);

attr->type = PERF_TYPE_BREAKPOINT;
attr->size = sizeof(*attr);
attr->bp_addr = addr;
attr->bp_len = len;
attr->bp_type = type;
/*
* Such breakpoints are used by debuggers to trigger signals when
* we hit the excepted memory op. We can't miss such events, they
* must be pinned.
*/
attr->pinned = 1;

if (!active)
attr->disabled = 1;

bp = perf_event_create_kernel_counter(attr, cpu, pid, triggered);
kfree(attr);

return bp;
}

/**
* register_user_hw_breakpoint - register a hardware breakpoint for user space
* @addr: is the memory address that triggers the breakpoint
* @len: the length of the access to the memory (1 byte, 2 bytes etc...)
* @type: the type of the access to the memory (read/write/exec)
* @attr: breakpoint attributes
* @triggered: callback to trigger when we hit the breakpoint
* @tsk: pointer to 'task_struct' of the process to which the address belongs
* @active: should we activate it while registering it
*
*/
struct perf_event *
register_user_hw_breakpoint(unsigned long addr,
int len,
int type,
register_user_hw_breakpoint(struct perf_event_attr *attr,
perf_callback_t triggered,
struct task_struct *tsk,
bool active)
struct task_struct *tsk)
{
return register_user_hw_breakpoint_cpu(addr, len, type, triggered,
tsk->pid, -1, active);
return perf_event_create_kernel_counter(attr, -1, tsk->pid, triggered);
}
EXPORT_SYMBOL_GPL(register_user_hw_breakpoint);

/**
* modify_user_hw_breakpoint - modify a user-space hardware breakpoint
* @bp: the breakpoint structure to modify
* @addr: is the memory address that triggers the breakpoint
* @len: the length of the access to the memory (1 byte, 2 bytes etc...)
* @type: the type of the access to the memory (read/write/exec)
* @attr: new breakpoint attributes
* @triggered: callback to trigger when we hit the breakpoint
* @tsk: pointer to 'task_struct' of the process to which the address belongs
* @active: should we activate it while registering it
*/
struct perf_event *
modify_user_hw_breakpoint(struct perf_event *bp,
unsigned long addr,
int len,
int type,
modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attr,
perf_callback_t triggered,
struct task_struct *tsk,
bool active)
struct task_struct *tsk)
{
/*
* FIXME: do it without unregistering
Expand All @@ -381,8 +323,7 @@ modify_user_hw_breakpoint(struct perf_event *bp,
*/
unregister_hw_breakpoint(bp);

return register_user_hw_breakpoint(addr, len, type, triggered,
tsk, active);
return perf_event_create_kernel_counter(attr, -1, tsk->pid, triggered);
}
EXPORT_SYMBOL_GPL(modify_user_hw_breakpoint);

Expand All @@ -406,8 +347,16 @@ register_kernel_hw_breakpoint_cpu(unsigned long addr,
int cpu,
bool active)
{
return register_user_hw_breakpoint_cpu(addr, len, type, triggered,
-1, cpu, active);
DEFINE_BREAKPOINT_ATTR(attr);

attr.bp_addr = addr;
attr.bp_len = len;
attr.bp_type = type;

if (!active)
attr.disabled = 1;

return perf_event_create_kernel_counter(&attr, cpu, -1, triggered);
}

/**
Expand Down

0 comments on commit cbfb610

Please sign in to comment.