Commit dd1853c3 authored by Frederic Weisbecker's avatar Frederic Weisbecker Committed by Ingo Molnar

hw-breakpoints: Use struct perf_event_attr to define kernel breakpoints

Kernel breakpoints are created using functions in which we pass
breakpoint parameters as individual variables: address, length
and type.

Although it fits well for x86, this just does not scale across
architectures that may support this api later as these may have
more or different needs. Pass in a perf_event_attr structure
instead because it is meant to evolve as much as possible into
a generic hardware breakpoint parameter structure.
Reported-by: default avatarK.Prasad <prasad@linux.vnet.ibm.com>
Signed-off-by: default avatarFrederic Weisbecker <fweisbec@gmail.com>
LKML-Reference: <1259294154-5197-2-git-send-regression-fweisbec@gmail.com>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent 5fa10b28
...@@ -28,6 +28,13 @@ struct perf_event_attr name = { \ ...@@ -28,6 +28,13 @@ struct perf_event_attr name = { \
.pinned = 1, \ .pinned = 1, \
}; };
static inline void hw_breakpoint_init(struct perf_event_attr *attr)
{
attr->type = PERF_TYPE_BREAKPOINT;
attr->size = sizeof(*attr);
attr->pinned = 1;
}
static inline unsigned long hw_breakpoint_addr(struct perf_event *bp) static inline unsigned long hw_breakpoint_addr(struct perf_event *bp)
{ {
return bp->attr.bp_addr; return bp->attr.bp_addr;
...@@ -59,19 +66,13 @@ modify_user_hw_breakpoint(struct perf_event *bp, ...@@ -59,19 +66,13 @@ modify_user_hw_breakpoint(struct perf_event *bp,
* Kernel breakpoints are not associated with any particular thread. * Kernel breakpoints are not associated with any particular thread.
*/ */
extern struct perf_event * extern struct perf_event *
register_wide_hw_breakpoint_cpu(unsigned long addr, register_wide_hw_breakpoint_cpu(struct perf_event_attr *attr,
int len,
int type,
perf_callback_t triggered, perf_callback_t triggered,
int cpu, int cpu);
bool active);
extern struct perf_event ** extern struct perf_event **
register_wide_hw_breakpoint(unsigned long addr, register_wide_hw_breakpoint(struct perf_event_attr *attr,
int len, perf_callback_t triggered);
int type,
perf_callback_t triggered,
bool active);
extern int register_perf_hw_breakpoint(struct perf_event *bp); extern int register_perf_hw_breakpoint(struct perf_event *bp);
extern int __register_perf_hw_breakpoint(struct perf_event *bp); extern int __register_perf_hw_breakpoint(struct perf_event *bp);
...@@ -100,18 +101,12 @@ modify_user_hw_breakpoint(struct perf_event *bp, ...@@ -100,18 +101,12 @@ modify_user_hw_breakpoint(struct perf_event *bp,
perf_callback_t triggered, perf_callback_t triggered,
struct task_struct *tsk) { return NULL; } struct task_struct *tsk) { return NULL; }
static inline struct perf_event * static inline struct perf_event *
register_wide_hw_breakpoint_cpu(unsigned long addr, register_wide_hw_breakpoint_cpu(struct perf_event_attr *attr,
int len,
int type,
perf_callback_t triggered, perf_callback_t triggered,
int cpu, int cpu) { return NULL; }
bool active) { return NULL; }
static inline struct perf_event ** static inline struct perf_event **
register_wide_hw_breakpoint(unsigned long addr, register_wide_hw_breakpoint(struct perf_event_attr *attr,
int len, perf_callback_t triggered) { return NULL; }
int type,
perf_callback_t triggered,
bool active) { return NULL; }
static inline int static inline int
register_perf_hw_breakpoint(struct perf_event *bp) { return -ENOSYS; } register_perf_hw_breakpoint(struct perf_event *bp) { return -ENOSYS; }
static inline int static inline int
......
...@@ -339,42 +339,16 @@ void unregister_hw_breakpoint(struct perf_event *bp) ...@@ -339,42 +339,16 @@ void unregister_hw_breakpoint(struct perf_event *bp)
} }
EXPORT_SYMBOL_GPL(unregister_hw_breakpoint); EXPORT_SYMBOL_GPL(unregister_hw_breakpoint);
static struct perf_event *
register_kernel_hw_breakpoint_cpu(unsigned long addr,
int len,
int type,
perf_callback_t triggered,
int cpu,
bool 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);
}
/** /**
* register_wide_hw_breakpoint - register a wide breakpoint in the kernel * register_wide_hw_breakpoint - register a wide breakpoint in the kernel
* @addr: is the memory address that triggers the breakpoint * @attr: breakpoint attributes
* @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)
* @triggered: callback to trigger when we hit the breakpoint * @triggered: callback to trigger when we hit the breakpoint
* @active: should we activate it while registering it
* *
* @return a set of per_cpu pointers to perf events * @return a set of per_cpu pointers to perf events
*/ */
struct perf_event ** struct perf_event **
register_wide_hw_breakpoint(unsigned long addr, register_wide_hw_breakpoint(struct perf_event_attr *attr,
int len, perf_callback_t triggered)
int type,
perf_callback_t triggered,
bool active)
{ {
struct perf_event **cpu_events, **pevent, *bp; struct perf_event **cpu_events, **pevent, *bp;
long err; long err;
...@@ -386,8 +360,7 @@ register_wide_hw_breakpoint(unsigned long addr, ...@@ -386,8 +360,7 @@ register_wide_hw_breakpoint(unsigned long addr,
for_each_possible_cpu(cpu) { for_each_possible_cpu(cpu) {
pevent = per_cpu_ptr(cpu_events, cpu); pevent = per_cpu_ptr(cpu_events, cpu);
bp = register_kernel_hw_breakpoint_cpu(addr, len, type, bp = perf_event_create_kernel_counter(attr, cpu, -1, triggered);
triggered, cpu, active);
*pevent = bp; *pevent = bp;
......
...@@ -42,9 +42,7 @@ ...@@ -42,9 +42,7 @@
struct trace_ksym { struct trace_ksym {
struct perf_event **ksym_hbp; struct perf_event **ksym_hbp;
unsigned long ksym_addr; struct perf_event_attr attr;
int type;
int len;
#ifdef CONFIG_PROFILE_KSYM_TRACER #ifdef CONFIG_PROFILE_KSYM_TRACER
unsigned long counter; unsigned long counter;
#endif #endif
...@@ -71,7 +69,7 @@ void ksym_collect_stats(unsigned long hbp_hit_addr) ...@@ -71,7 +69,7 @@ void ksym_collect_stats(unsigned long hbp_hit_addr)
rcu_read_lock(); rcu_read_lock();
hlist_for_each_entry_rcu(entry, node, &ksym_filter_head, ksym_hlist) { hlist_for_each_entry_rcu(entry, node, &ksym_filter_head, ksym_hlist) {
if ((entry->ksym_addr == hbp_hit_addr) && if ((entry->attr.bp_addr == hbp_hit_addr) &&
(entry->counter <= MAX_UL_INT)) { (entry->counter <= MAX_UL_INT)) {
entry->counter++; entry->counter++;
break; break;
...@@ -192,14 +190,15 @@ int process_new_ksym_entry(char *ksymname, int op, unsigned long addr) ...@@ -192,14 +190,15 @@ int process_new_ksym_entry(char *ksymname, int op, unsigned long addr)
if (!entry) if (!entry)
return -ENOMEM; return -ENOMEM;
entry->type = op; hw_breakpoint_init(&entry->attr);
entry->ksym_addr = addr;
entry->len = HW_BREAKPOINT_LEN_4; entry->attr.bp_type = op;
entry->attr.bp_addr = addr;
entry->attr.bp_len = HW_BREAKPOINT_LEN_4;
ret = -EAGAIN; ret = -EAGAIN;
entry->ksym_hbp = register_wide_hw_breakpoint(entry->ksym_addr, entry->ksym_hbp = register_wide_hw_breakpoint(&entry->attr,
entry->len, entry->type, ksym_hbp_handler);
ksym_hbp_handler, true);
if (IS_ERR(entry->ksym_hbp)) { if (IS_ERR(entry->ksym_hbp)) {
ret = PTR_ERR(entry->ksym_hbp); ret = PTR_ERR(entry->ksym_hbp);
...@@ -236,12 +235,12 @@ static ssize_t ksym_trace_filter_read(struct file *filp, char __user *ubuf, ...@@ -236,12 +235,12 @@ static ssize_t ksym_trace_filter_read(struct file *filp, char __user *ubuf,
mutex_lock(&ksym_tracer_mutex); mutex_lock(&ksym_tracer_mutex);
hlist_for_each_entry(entry, node, &ksym_filter_head, ksym_hlist) { hlist_for_each_entry(entry, node, &ksym_filter_head, ksym_hlist) {
ret = trace_seq_printf(s, "%pS:", (void *)entry->ksym_addr); ret = trace_seq_printf(s, "%pS:", (void *)entry->attr.bp_addr);
if (entry->type == HW_BREAKPOINT_R) if (entry->attr.bp_type == HW_BREAKPOINT_R)
ret = trace_seq_puts(s, "r--\n"); ret = trace_seq_puts(s, "r--\n");
else if (entry->type == HW_BREAKPOINT_W) else if (entry->attr.bp_type == HW_BREAKPOINT_W)
ret = trace_seq_puts(s, "-w-\n"); ret = trace_seq_puts(s, "-w-\n");
else if (entry->type == (HW_BREAKPOINT_W | HW_BREAKPOINT_R)) else if (entry->attr.bp_type == (HW_BREAKPOINT_W | HW_BREAKPOINT_R))
ret = trace_seq_puts(s, "rw-\n"); ret = trace_seq_puts(s, "rw-\n");
WARN_ON_ONCE(!ret); WARN_ON_ONCE(!ret);
} }
...@@ -317,9 +316,9 @@ static ssize_t ksym_trace_filter_write(struct file *file, ...@@ -317,9 +316,9 @@ static ssize_t ksym_trace_filter_write(struct file *file,
ret = -EINVAL; ret = -EINVAL;
hlist_for_each_entry(entry, node, &ksym_filter_head, ksym_hlist) { hlist_for_each_entry(entry, node, &ksym_filter_head, ksym_hlist) {
if (entry->ksym_addr == ksym_addr) { if (entry->attr.bp_addr == ksym_addr) {
/* Check for malformed request: (6) */ /* Check for malformed request: (6) */
if (entry->type != op) if (entry->attr.bp_type != op)
changed = 1; changed = 1;
else else
goto out; goto out;
...@@ -328,13 +327,12 @@ static ssize_t ksym_trace_filter_write(struct file *file, ...@@ -328,13 +327,12 @@ static ssize_t ksym_trace_filter_write(struct file *file,
} }
if (changed) { if (changed) {
unregister_wide_hw_breakpoint(entry->ksym_hbp); unregister_wide_hw_breakpoint(entry->ksym_hbp);
entry->type = op; entry->attr.bp_type = op;
ret = 0; ret = 0;
if (op > 0) { if (op > 0) {
entry->ksym_hbp = entry->ksym_hbp =
register_wide_hw_breakpoint(entry->ksym_addr, register_wide_hw_breakpoint(&entry->attr,
entry->len, entry->type, ksym_hbp_handler);
ksym_hbp_handler, true);
if (IS_ERR(entry->ksym_hbp)) if (IS_ERR(entry->ksym_hbp))
ret = PTR_ERR(entry->ksym_hbp); ret = PTR_ERR(entry->ksym_hbp);
else else
...@@ -489,7 +487,7 @@ static int ksym_tracer_stat_show(struct seq_file *m, void *v) ...@@ -489,7 +487,7 @@ static int ksym_tracer_stat_show(struct seq_file *m, void *v)
entry = hlist_entry(stat, struct trace_ksym, ksym_hlist); entry = hlist_entry(stat, struct trace_ksym, ksym_hlist);
access_type = entry->type; access_type = entry->attr.bp_type;
switch (access_type) { switch (access_type) {
case HW_BREAKPOINT_R: case HW_BREAKPOINT_R:
...@@ -505,7 +503,7 @@ static int ksym_tracer_stat_show(struct seq_file *m, void *v) ...@@ -505,7 +503,7 @@ static int ksym_tracer_stat_show(struct seq_file *m, void *v)
seq_puts(m, " NA "); seq_puts(m, " NA ");
} }
if (lookup_symbol_name(entry->ksym_addr, fn_name) >= 0) if (lookup_symbol_name(entry->attr.bp_addr, fn_name) >= 0)
seq_printf(m, " %-36s", fn_name); seq_printf(m, " %-36s", fn_name);
else else
seq_printf(m, " %-36s", "<NA>"); seq_printf(m, " %-36s", "<NA>");
......
...@@ -51,13 +51,13 @@ static void sample_hbp_handler(struct perf_event *temp, void *data) ...@@ -51,13 +51,13 @@ static void sample_hbp_handler(struct perf_event *temp, void *data)
static int __init hw_break_module_init(void) static int __init hw_break_module_init(void)
{ {
int ret; int ret;
unsigned long addr; DEFINE_BREAKPOINT_ATTR(attr);
addr = kallsyms_lookup_name(ksym_name); attr.bp_addr = kallsyms_lookup_name(ksym_name);
attr.bp_len = HW_BREAKPOINT_LEN_4;
attr.bp_type = HW_BREAKPOINT_W | HW_BREAKPOINT_R;
sample_hbp = register_wide_hw_breakpoint(addr, HW_BREAKPOINT_LEN_4, sample_hbp = register_wide_hw_breakpoint(&attr, sample_hbp_handler);
HW_BREAKPOINT_W | HW_BREAKPOINT_R,
sample_hbp_handler, true);
if (IS_ERR(sample_hbp)) { if (IS_ERR(sample_hbp)) {
ret = PTR_ERR(sample_hbp); ret = PTR_ERR(sample_hbp);
goto fail; goto fail;
......
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