aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFrederic Weisbecker <fweisbec@gmail.com>2009-11-26 22:55:54 -0500
committerIngo Molnar <mingo@elte.hu>2009-11-27 00:22:59 -0500
commitdd1853c3f493f6d22d9e5390b192a07b73d2ac0a (patch)
treeaf87226bcdc254ce2ab656530263e61f6552322b
parent5fa10b28e57f94a90535cfeafe89dcee9f47d540 (diff)
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: K.Prasad <prasad@linux.vnet.ibm.com> Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com> LKML-Reference: <1259294154-5197-2-git-send-regression-fweisbec@gmail.com> Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r--include/linux/hw_breakpoint.h35
-rw-r--r--kernel/hw_breakpoint.c35
-rw-r--r--kernel/trace/trace_ksym.c42
-rw-r--r--samples/hw_breakpoint/data_breakpoint.c10
4 files changed, 44 insertions, 78 deletions
diff --git a/include/linux/hw_breakpoint.h b/include/linux/hw_breakpoint.h
index 5da472e434b7..a03daed08c59 100644
--- a/include/linux/hw_breakpoint.h
+++ b/include/linux/hw_breakpoint.h
@@ -28,6 +28,13 @@ struct perf_event_attr name = { \
28 .pinned = 1, \ 28 .pinned = 1, \
29}; 29};
30 30
31static inline void hw_breakpoint_init(struct perf_event_attr *attr)
32{
33 attr->type = PERF_TYPE_BREAKPOINT;
34 attr->size = sizeof(*attr);
35 attr->pinned = 1;
36}
37
31static inline unsigned long hw_breakpoint_addr(struct perf_event *bp) 38static inline unsigned long hw_breakpoint_addr(struct perf_event *bp)
32{ 39{
33 return bp->attr.bp_addr; 40 return bp->attr.bp_addr;
@@ -59,19 +66,13 @@ modify_user_hw_breakpoint(struct perf_event *bp,
59 * Kernel breakpoints are not associated with any particular thread. 66 * Kernel breakpoints are not associated with any particular thread.
60 */ 67 */
61extern struct perf_event * 68extern struct perf_event *
62register_wide_hw_breakpoint_cpu(unsigned long addr, 69register_wide_hw_breakpoint_cpu(struct perf_event_attr *attr,
63 int len,
64 int type,
65 perf_callback_t triggered, 70 perf_callback_t triggered,
66 int cpu, 71 int cpu);
67 bool active);
68 72
69extern struct perf_event ** 73extern struct perf_event **
70register_wide_hw_breakpoint(unsigned long addr, 74register_wide_hw_breakpoint(struct perf_event_attr *attr,
71 int len, 75 perf_callback_t triggered);
72 int type,
73 perf_callback_t triggered,
74 bool active);
75 76
76extern int register_perf_hw_breakpoint(struct perf_event *bp); 77extern int register_perf_hw_breakpoint(struct perf_event *bp);
77extern int __register_perf_hw_breakpoint(struct perf_event *bp); 78extern int __register_perf_hw_breakpoint(struct perf_event *bp);
@@ -100,18 +101,12 @@ modify_user_hw_breakpoint(struct perf_event *bp,
100 perf_callback_t triggered, 101 perf_callback_t triggered,
101 struct task_struct *tsk) { return NULL; } 102 struct task_struct *tsk) { return NULL; }
102static inline struct perf_event * 103static inline struct perf_event *
103register_wide_hw_breakpoint_cpu(unsigned long addr, 104register_wide_hw_breakpoint_cpu(struct perf_event_attr *attr,
104 int len,
105 int type,
106 perf_callback_t triggered, 105 perf_callback_t triggered,
107 int cpu, 106 int cpu) { return NULL; }
108 bool active) { return NULL; }
109static inline struct perf_event ** 107static inline struct perf_event **
110register_wide_hw_breakpoint(unsigned long addr, 108register_wide_hw_breakpoint(struct perf_event_attr *attr,
111 int len, 109 perf_callback_t triggered) { return NULL; }
112 int type,
113 perf_callback_t triggered,
114 bool active) { return NULL; }
115static inline int 110static inline int
116register_perf_hw_breakpoint(struct perf_event *bp) { return -ENOSYS; } 111register_perf_hw_breakpoint(struct perf_event *bp) { return -ENOSYS; }
117static inline int 112static inline int
diff --git a/kernel/hw_breakpoint.c b/kernel/hw_breakpoint.c
index 2a47514f12fd..cf5ee1628411 100644
--- a/kernel/hw_breakpoint.c
+++ b/kernel/hw_breakpoint.c
@@ -339,42 +339,16 @@ void unregister_hw_breakpoint(struct perf_event *bp)
339} 339}
340EXPORT_SYMBOL_GPL(unregister_hw_breakpoint); 340EXPORT_SYMBOL_GPL(unregister_hw_breakpoint);
341 341
342static struct perf_event *
343register_kernel_hw_breakpoint_cpu(unsigned long addr,
344 int len,
345 int type,
346 perf_callback_t triggered,
347 int cpu,
348 bool active)
349{
350 DEFINE_BREAKPOINT_ATTR(attr);
351
352 attr.bp_addr = addr;
353 attr.bp_len = len;
354 attr.bp_type = type;
355
356 if (!active)
357 attr.disabled = 1;
358
359 return perf_event_create_kernel_counter(&attr, cpu, -1, triggered);
360}
361
362/** 342/**
363 * register_wide_hw_breakpoint - register a wide breakpoint in the kernel 343 * register_wide_hw_breakpoint - register a wide breakpoint in the kernel
364 * @addr: is the memory address that triggers the breakpoint 344 * @attr: breakpoint attributes
365 * @len: the length of the access to the memory (1 byte, 2 bytes etc...)
366 * @type: the type of the access to the memory (read/write/exec)
367 * @triggered: callback to trigger when we hit the breakpoint 345 * @triggered: callback to trigger when we hit the breakpoint
368 * @active: should we activate it while registering it
369 * 346 *
370 * @return a set of per_cpu pointers to perf events 347 * @return a set of per_cpu pointers to perf events
371 */ 348 */
372struct perf_event ** 349struct perf_event **
373register_wide_hw_breakpoint(unsigned long addr, 350register_wide_hw_breakpoint(struct perf_event_attr *attr,
374 int len, 351 perf_callback_t triggered)
375 int type,
376 perf_callback_t triggered,
377 bool active)
378{ 352{
379 struct perf_event **cpu_events, **pevent, *bp; 353 struct perf_event **cpu_events, **pevent, *bp;
380 long err; 354 long err;
@@ -386,8 +360,7 @@ register_wide_hw_breakpoint(unsigned long addr,
386 360
387 for_each_possible_cpu(cpu) { 361 for_each_possible_cpu(cpu) {
388 pevent = per_cpu_ptr(cpu_events, cpu); 362 pevent = per_cpu_ptr(cpu_events, cpu);
389 bp = register_kernel_hw_breakpoint_cpu(addr, len, type, 363 bp = perf_event_create_kernel_counter(attr, cpu, -1, triggered);
390 triggered, cpu, active);
391 364
392 *pevent = bp; 365 *pevent = bp;
393 366
diff --git a/kernel/trace/trace_ksym.c b/kernel/trace/trace_ksym.c
index c538b15b95d6..ddfa0fd43bc0 100644
--- a/kernel/trace/trace_ksym.c
+++ b/kernel/trace/trace_ksym.c
@@ -42,9 +42,7 @@
42 42
43struct trace_ksym { 43struct trace_ksym {
44 struct perf_event **ksym_hbp; 44 struct perf_event **ksym_hbp;
45 unsigned long ksym_addr; 45 struct perf_event_attr attr;
46 int type;
47 int len;
48#ifdef CONFIG_PROFILE_KSYM_TRACER 46#ifdef CONFIG_PROFILE_KSYM_TRACER
49 unsigned long counter; 47 unsigned long counter;
50#endif 48#endif
@@ -71,7 +69,7 @@ void ksym_collect_stats(unsigned long hbp_hit_addr)
71 69
72 rcu_read_lock(); 70 rcu_read_lock();
73 hlist_for_each_entry_rcu(entry, node, &ksym_filter_head, ksym_hlist) { 71 hlist_for_each_entry_rcu(entry, node, &ksym_filter_head, ksym_hlist) {
74 if ((entry->ksym_addr == hbp_hit_addr) && 72 if ((entry->attr.bp_addr == hbp_hit_addr) &&
75 (entry->counter <= MAX_UL_INT)) { 73 (entry->counter <= MAX_UL_INT)) {
76 entry->counter++; 74 entry->counter++;
77 break; 75 break;
@@ -192,14 +190,15 @@ int process_new_ksym_entry(char *ksymname, int op, unsigned long addr)
192 if (!entry) 190 if (!entry)
193 return -ENOMEM; 191 return -ENOMEM;
194 192
195 entry->type = op; 193 hw_breakpoint_init(&entry->attr);
196 entry->ksym_addr = addr; 194
197 entry->len = HW_BREAKPOINT_LEN_4; 195 entry->attr.bp_type = op;
196 entry->attr.bp_addr = addr;
197 entry->attr.bp_len = HW_BREAKPOINT_LEN_4;
198 198
199 ret = -EAGAIN; 199 ret = -EAGAIN;
200 entry->ksym_hbp = register_wide_hw_breakpoint(entry->ksym_addr, 200 entry->ksym_hbp = register_wide_hw_breakpoint(&entry->attr,
201 entry->len, entry->type, 201 ksym_hbp_handler);
202 ksym_hbp_handler, true);
203 202
204 if (IS_ERR(entry->ksym_hbp)) { 203 if (IS_ERR(entry->ksym_hbp)) {
205 ret = PTR_ERR(entry->ksym_hbp); 204 ret = PTR_ERR(entry->ksym_hbp);
@@ -236,12 +235,12 @@ static ssize_t ksym_trace_filter_read(struct file *filp, char __user *ubuf,
236 mutex_lock(&ksym_tracer_mutex); 235 mutex_lock(&ksym_tracer_mutex);
237 236
238 hlist_for_each_entry(entry, node, &ksym_filter_head, ksym_hlist) { 237 hlist_for_each_entry(entry, node, &ksym_filter_head, ksym_hlist) {
239 ret = trace_seq_printf(s, "%pS:", (void *)entry->ksym_addr); 238 ret = trace_seq_printf(s, "%pS:", (void *)entry->attr.bp_addr);
240 if (entry->type == HW_BREAKPOINT_R) 239 if (entry->attr.bp_type == HW_BREAKPOINT_R)
241 ret = trace_seq_puts(s, "r--\n"); 240 ret = trace_seq_puts(s, "r--\n");
242 else if (entry->type == HW_BREAKPOINT_W) 241 else if (entry->attr.bp_type == HW_BREAKPOINT_W)
243 ret = trace_seq_puts(s, "-w-\n"); 242 ret = trace_seq_puts(s, "-w-\n");
244 else if (entry->type == (HW_BREAKPOINT_W | HW_BREAKPOINT_R)) 243 else if (entry->attr.bp_type == (HW_BREAKPOINT_W | HW_BREAKPOINT_R))
245 ret = trace_seq_puts(s, "rw-\n"); 244 ret = trace_seq_puts(s, "rw-\n");
246 WARN_ON_ONCE(!ret); 245 WARN_ON_ONCE(!ret);
247 } 246 }
@@ -317,9 +316,9 @@ static ssize_t ksym_trace_filter_write(struct file *file,
317 316
318 ret = -EINVAL; 317 ret = -EINVAL;
319 hlist_for_each_entry(entry, node, &ksym_filter_head, ksym_hlist) { 318 hlist_for_each_entry(entry, node, &ksym_filter_head, ksym_hlist) {
320 if (entry->ksym_addr == ksym_addr) { 319 if (entry->attr.bp_addr == ksym_addr) {
321 /* Check for malformed request: (6) */ 320 /* Check for malformed request: (6) */
322 if (entry->type != op) 321 if (entry->attr.bp_type != op)
323 changed = 1; 322 changed = 1;
324 else 323 else
325 goto out; 324 goto out;
@@ -328,13 +327,12 @@ static ssize_t ksym_trace_filter_write(struct file *file,
328 } 327 }
329 if (changed) { 328 if (changed) {
330 unregister_wide_hw_breakpoint(entry->ksym_hbp); 329 unregister_wide_hw_breakpoint(entry->ksym_hbp);
331 entry->type = op; 330 entry->attr.bp_type = op;
332 ret = 0; 331 ret = 0;
333 if (op > 0) { 332 if (op > 0) {
334 entry->ksym_hbp = 333 entry->ksym_hbp =
335 register_wide_hw_breakpoint(entry->ksym_addr, 334 register_wide_hw_breakpoint(&entry->attr,
336 entry->len, entry->type, 335 ksym_hbp_handler);
337 ksym_hbp_handler, true);
338 if (IS_ERR(entry->ksym_hbp)) 336 if (IS_ERR(entry->ksym_hbp))
339 ret = PTR_ERR(entry->ksym_hbp); 337 ret = PTR_ERR(entry->ksym_hbp);
340 else 338 else
@@ -489,7 +487,7 @@ static int ksym_tracer_stat_show(struct seq_file *m, void *v)
489 487
490 entry = hlist_entry(stat, struct trace_ksym, ksym_hlist); 488 entry = hlist_entry(stat, struct trace_ksym, ksym_hlist);
491 489
492 access_type = entry->type; 490 access_type = entry->attr.bp_type;
493 491
494 switch (access_type) { 492 switch (access_type) {
495 case HW_BREAKPOINT_R: 493 case HW_BREAKPOINT_R:
@@ -505,7 +503,7 @@ static int ksym_tracer_stat_show(struct seq_file *m, void *v)
505 seq_puts(m, " NA "); 503 seq_puts(m, " NA ");
506 } 504 }
507 505
508 if (lookup_symbol_name(entry->ksym_addr, fn_name) >= 0) 506 if (lookup_symbol_name(entry->attr.bp_addr, fn_name) >= 0)
509 seq_printf(m, " %-36s", fn_name); 507 seq_printf(m, " %-36s", fn_name);
510 else 508 else
511 seq_printf(m, " %-36s", "<NA>"); 509 seq_printf(m, " %-36s", "<NA>");
diff --git a/samples/hw_breakpoint/data_breakpoint.c b/samples/hw_breakpoint/data_breakpoint.c
index ee7f9fbaffbd..29525500df00 100644
--- a/samples/hw_breakpoint/data_breakpoint.c
+++ b/samples/hw_breakpoint/data_breakpoint.c
@@ -51,13 +51,13 @@ static void sample_hbp_handler(struct perf_event *temp, void *data)
51static int __init hw_break_module_init(void) 51static int __init hw_break_module_init(void)
52{ 52{
53 int ret; 53 int ret;
54 unsigned long addr; 54 DEFINE_BREAKPOINT_ATTR(attr);
55 55
56 addr = kallsyms_lookup_name(ksym_name); 56 attr.bp_addr = kallsyms_lookup_name(ksym_name);
57 attr.bp_len = HW_BREAKPOINT_LEN_4;
58 attr.bp_type = HW_BREAKPOINT_W | HW_BREAKPOINT_R;
57 59
58 sample_hbp = register_wide_hw_breakpoint(addr, HW_BREAKPOINT_LEN_4, 60 sample_hbp = register_wide_hw_breakpoint(&attr, sample_hbp_handler);
59 HW_BREAKPOINT_W | HW_BREAKPOINT_R,
60 sample_hbp_handler, true);
61 if (IS_ERR(sample_hbp)) { 61 if (IS_ERR(sample_hbp)) {
62 ret = PTR_ERR(sample_hbp); 62 ret = PTR_ERR(sample_hbp);
63 goto fail; 63 goto fail;