aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFrederic Weisbecker <fweisbec@gmail.com>2009-12-05 03:44:31 -0500
committerFrederic Weisbecker <fweisbec@gmail.com>2009-12-06 02:27:18 -0500
commitb326e9560a28fc3e950637ef51847ed8f05c1335 (patch)
tree0804c8c4f28d4ae152d5e9205ce5a958f0d26b79
parent2f0993e0fb663c49e4d1e02654f6203246be4817 (diff)
hw-breakpoints: Use overflow handler instead of the event callback
struct perf_event::event callback was called when a breakpoint triggers. But this is a rather opaque callback, pretty tied-only to the breakpoint API and not really integrated into perf as it triggers even when we don't overflow. We prefer to use overflow_handler() as it fits into the perf events rules, being called only when we overflow. Reported-by: Peter Zijlstra <peterz@infradead.org> Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com> Cc: Paul Mackerras <paulus@samba.org> Cc: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: "K. Prasad" <prasad@linux.vnet.ibm.com>
-rw-r--r--arch/x86/kernel/hw_breakpoint.c5
-rw-r--r--arch/x86/kernel/ptrace.c9
-rw-r--r--include/linux/hw_breakpoint.h25
-rw-r--r--include/linux/perf_event.h13
-rw-r--r--kernel/hw_breakpoint.c17
-rw-r--r--kernel/perf_event.c24
-rw-r--r--kernel/trace/trace_ksym.c5
-rw-r--r--samples/hw_breakpoint/data_breakpoint.c7
8 files changed, 48 insertions, 57 deletions
diff --git a/arch/x86/kernel/hw_breakpoint.c b/arch/x86/kernel/hw_breakpoint.c
index d42f65ac4927..05d5fec64a94 100644
--- a/arch/x86/kernel/hw_breakpoint.c
+++ b/arch/x86/kernel/hw_breakpoint.c
@@ -362,8 +362,7 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp,
362 return ret; 362 return ret;
363 } 363 }
364 364
365 if (bp->callback) 365 ret = arch_store_info(bp);
366 ret = arch_store_info(bp);
367 366
368 if (ret < 0) 367 if (ret < 0)
369 return ret; 368 return ret;
@@ -519,7 +518,7 @@ static int __kprobes hw_breakpoint_handler(struct die_args *args)
519 break; 518 break;
520 } 519 }
521 520
522 (bp->callback)(bp, args->regs); 521 perf_bp_event(bp, args->regs);
523 522
524 rcu_read_unlock(); 523 rcu_read_unlock();
525 } 524 }
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index dbb395572ae2..b361d28061d0 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -555,7 +555,9 @@ static int genregs_set(struct task_struct *target,
555 return ret; 555 return ret;
556} 556}
557 557
558static void ptrace_triggered(struct perf_event *bp, void *data) 558static void ptrace_triggered(struct perf_event *bp, int nmi,
559 struct perf_sample_data *data,
560 struct pt_regs *regs)
559{ 561{
560 int i; 562 int i;
561 struct thread_struct *thread = &(current->thread); 563 struct thread_struct *thread = &(current->thread);
@@ -599,7 +601,7 @@ ptrace_modify_breakpoint(struct perf_event *bp, int len, int type,
599{ 601{
600 int err; 602 int err;
601 int gen_len, gen_type; 603 int gen_len, gen_type;
602 DEFINE_BREAKPOINT_ATTR(attr); 604 struct perf_event_attr attr;
603 605
604 /* 606 /*
605 * We shoud have at least an inactive breakpoint at this 607 * We shoud have at least an inactive breakpoint at this
@@ -721,9 +723,10 @@ static int ptrace_set_breakpoint_addr(struct task_struct *tsk, int nr,
721{ 723{
722 struct perf_event *bp; 724 struct perf_event *bp;
723 struct thread_struct *t = &tsk->thread; 725 struct thread_struct *t = &tsk->thread;
724 DEFINE_BREAKPOINT_ATTR(attr); 726 struct perf_event_attr attr;
725 727
726 if (!t->ptrace_bps[nr]) { 728 if (!t->ptrace_bps[nr]) {
729 hw_breakpoint_init(&attr);
727 /* 730 /*
728 * Put stub len and type to register (reserve) an inactive but 731 * Put stub len and type to register (reserve) an inactive but
729 * correct bp 732 * correct bp
diff --git a/include/linux/hw_breakpoint.h b/include/linux/hw_breakpoint.h
index d33096e0dbd4..4d14a384a01e 100644
--- a/include/linux/hw_breakpoint.h
+++ b/include/linux/hw_breakpoint.h
@@ -20,19 +20,16 @@ enum {
20 20
21#ifdef CONFIG_HAVE_HW_BREAKPOINT 21#ifdef CONFIG_HAVE_HW_BREAKPOINT
22 22
23/* As it's for in-kernel or ptrace use, we want it to be pinned */
24#define DEFINE_BREAKPOINT_ATTR(name) \
25struct perf_event_attr name = { \
26 .type = PERF_TYPE_BREAKPOINT, \
27 .size = sizeof(name), \
28 .pinned = 1, \
29};
30
31static inline void hw_breakpoint_init(struct perf_event_attr *attr) 23static inline void hw_breakpoint_init(struct perf_event_attr *attr)
32{ 24{
33 attr->type = PERF_TYPE_BREAKPOINT; 25 attr->type = PERF_TYPE_BREAKPOINT;
34 attr->size = sizeof(*attr); 26 attr->size = sizeof(*attr);
27 /*
28 * As it's for in-kernel or ptrace use, we want it to be pinned
29 * and to call its callback every hits.
30 */
35 attr->pinned = 1; 31 attr->pinned = 1;
32 attr->sample_period = 1;
36} 33}
37 34
38static inline unsigned long hw_breakpoint_addr(struct perf_event *bp) 35static inline unsigned long hw_breakpoint_addr(struct perf_event *bp)
@@ -52,7 +49,7 @@ static inline int hw_breakpoint_len(struct perf_event *bp)
52 49
53extern struct perf_event * 50extern struct perf_event *
54register_user_hw_breakpoint(struct perf_event_attr *attr, 51register_user_hw_breakpoint(struct perf_event_attr *attr,
55 perf_callback_t triggered, 52 perf_overflow_handler_t triggered,
56 struct task_struct *tsk); 53 struct task_struct *tsk);
57 54
58/* FIXME: only change from the attr, and don't unregister */ 55/* FIXME: only change from the attr, and don't unregister */
@@ -64,12 +61,12 @@ modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attr);
64 */ 61 */
65extern struct perf_event * 62extern struct perf_event *
66register_wide_hw_breakpoint_cpu(struct perf_event_attr *attr, 63register_wide_hw_breakpoint_cpu(struct perf_event_attr *attr,
67 perf_callback_t triggered, 64 perf_overflow_handler_t triggered,
68 int cpu); 65 int cpu);
69 66
70extern struct perf_event ** 67extern struct perf_event **
71register_wide_hw_breakpoint(struct perf_event_attr *attr, 68register_wide_hw_breakpoint(struct perf_event_attr *attr,
72 perf_callback_t triggered); 69 perf_overflow_handler_t triggered);
73 70
74extern int register_perf_hw_breakpoint(struct perf_event *bp); 71extern int register_perf_hw_breakpoint(struct perf_event *bp);
75extern int __register_perf_hw_breakpoint(struct perf_event *bp); 72extern int __register_perf_hw_breakpoint(struct perf_event *bp);
@@ -90,18 +87,18 @@ static inline struct arch_hw_breakpoint *counter_arch_bp(struct perf_event *bp)
90 87
91static inline struct perf_event * 88static inline struct perf_event *
92register_user_hw_breakpoint(struct perf_event_attr *attr, 89register_user_hw_breakpoint(struct perf_event_attr *attr,
93 perf_callback_t triggered, 90 perf_overflow_handler_t triggered,
94 struct task_struct *tsk) { return NULL; } 91 struct task_struct *tsk) { return NULL; }
95static inline struct perf_event * 92static inline struct perf_event *
96modify_user_hw_breakpoint(struct perf_event *bp, 93modify_user_hw_breakpoint(struct perf_event *bp,
97 struct perf_event_attr *attr) { return NULL; } 94 struct perf_event_attr *attr) { return NULL; }
98static inline struct perf_event * 95static inline struct perf_event *
99register_wide_hw_breakpoint_cpu(struct perf_event_attr *attr, 96register_wide_hw_breakpoint_cpu(struct perf_event_attr *attr,
100 perf_callback_t triggered, 97 perf_overflow_handler_t triggered,
101 int cpu) { return NULL; } 98 int cpu) { return NULL; }
102static inline struct perf_event ** 99static inline struct perf_event **
103register_wide_hw_breakpoint(struct perf_event_attr *attr, 100register_wide_hw_breakpoint(struct perf_event_attr *attr,
104 perf_callback_t triggered) { return NULL; } 101 perf_overflow_handler_t triggered) { return NULL; }
105static inline int 102static inline int
106register_perf_hw_breakpoint(struct perf_event *bp) { return -ENOSYS; } 103register_perf_hw_breakpoint(struct perf_event *bp) { return -ENOSYS; }
107static inline int 104static inline int
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index 84bd28a0ffab..d2f2667430da 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -565,10 +565,13 @@ struct perf_pending_entry {
565 void (*func)(struct perf_pending_entry *); 565 void (*func)(struct perf_pending_entry *);
566}; 566};
567 567
568typedef void (*perf_callback_t)(struct perf_event *, void *);
569
570struct perf_sample_data; 568struct perf_sample_data;
571 569
570typedef void (*perf_callback_t)(struct perf_event *, void *);
571typedef void (*perf_overflow_handler_t)(struct perf_event *, int,
572 struct perf_sample_data *,
573 struct pt_regs *regs);
574
572/** 575/**
573 * struct perf_event - performance event kernel representation: 576 * struct perf_event - performance event kernel representation:
574 */ 577 */
@@ -660,9 +663,7 @@ struct perf_event {
660 struct pid_namespace *ns; 663 struct pid_namespace *ns;
661 u64 id; 664 u64 id;
662 665
663 void (*overflow_handler)(struct perf_event *event, 666 perf_overflow_handler_t overflow_handler;
664 int nmi, struct perf_sample_data *data,
665 struct pt_regs *regs);
666 667
667#ifdef CONFIG_EVENT_PROFILE 668#ifdef CONFIG_EVENT_PROFILE
668 struct event_filter *filter; 669 struct event_filter *filter;
@@ -779,7 +780,7 @@ extern struct perf_event *
779perf_event_create_kernel_counter(struct perf_event_attr *attr, 780perf_event_create_kernel_counter(struct perf_event_attr *attr,
780 int cpu, 781 int cpu,
781 pid_t pid, 782 pid_t pid,
782 perf_callback_t callback); 783 perf_overflow_handler_t callback);
783extern u64 perf_event_read_value(struct perf_event *event, 784extern u64 perf_event_read_value(struct perf_event *event,
784 u64 *enabled, u64 *running); 785 u64 *enabled, u64 *running);
785 786
diff --git a/kernel/hw_breakpoint.c b/kernel/hw_breakpoint.c
index 2d10b012828f..b600fc27f161 100644
--- a/kernel/hw_breakpoint.c
+++ b/kernel/hw_breakpoint.c
@@ -259,7 +259,7 @@ void release_bp_slot(struct perf_event *bp)
259} 259}
260 260
261 261
262int __register_perf_hw_breakpoint(struct perf_event *bp) 262int register_perf_hw_breakpoint(struct perf_event *bp)
263{ 263{
264 int ret; 264 int ret;
265 265
@@ -276,19 +276,12 @@ int __register_perf_hw_breakpoint(struct perf_event *bp)
276 * This is a quick hack that will be removed soon, once we remove 276 * This is a quick hack that will be removed soon, once we remove
277 * the tmp breakpoints from ptrace 277 * the tmp breakpoints from ptrace
278 */ 278 */
279 if (!bp->attr.disabled || bp->callback == perf_bp_event) 279 if (!bp->attr.disabled || !bp->overflow_handler)
280 ret = arch_validate_hwbkpt_settings(bp, bp->ctx->task); 280 ret = arch_validate_hwbkpt_settings(bp, bp->ctx->task);
281 281
282 return ret; 282 return ret;
283} 283}
284 284
285int register_perf_hw_breakpoint(struct perf_event *bp)
286{
287 bp->callback = perf_bp_event;
288
289 return __register_perf_hw_breakpoint(bp);
290}
291
292/** 285/**
293 * register_user_hw_breakpoint - register a hardware breakpoint for user space 286 * register_user_hw_breakpoint - register a hardware breakpoint for user space
294 * @attr: breakpoint attributes 287 * @attr: breakpoint attributes
@@ -297,7 +290,7 @@ int register_perf_hw_breakpoint(struct perf_event *bp)
297 */ 290 */
298struct perf_event * 291struct perf_event *
299register_user_hw_breakpoint(struct perf_event_attr *attr, 292register_user_hw_breakpoint(struct perf_event_attr *attr,
300 perf_callback_t triggered, 293 perf_overflow_handler_t triggered,
301 struct task_struct *tsk) 294 struct task_struct *tsk)
302{ 295{
303 return perf_event_create_kernel_counter(attr, -1, tsk->pid, triggered); 296 return perf_event_create_kernel_counter(attr, -1, tsk->pid, triggered);
@@ -322,7 +315,7 @@ modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attr)
322 unregister_hw_breakpoint(bp); 315 unregister_hw_breakpoint(bp);
323 316
324 return perf_event_create_kernel_counter(attr, -1, bp->ctx->task->pid, 317 return perf_event_create_kernel_counter(attr, -1, bp->ctx->task->pid,
325 bp->callback); 318 bp->overflow_handler);
326} 319}
327EXPORT_SYMBOL_GPL(modify_user_hw_breakpoint); 320EXPORT_SYMBOL_GPL(modify_user_hw_breakpoint);
328 321
@@ -347,7 +340,7 @@ EXPORT_SYMBOL_GPL(unregister_hw_breakpoint);
347 */ 340 */
348struct perf_event ** 341struct perf_event **
349register_wide_hw_breakpoint(struct perf_event_attr *attr, 342register_wide_hw_breakpoint(struct perf_event_attr *attr,
350 perf_callback_t triggered) 343 perf_overflow_handler_t triggered)
351{ 344{
352 struct perf_event **cpu_events, **pevent, *bp; 345 struct perf_event **cpu_events, **pevent, *bp;
353 long err; 346 long err;
diff --git a/kernel/perf_event.c b/kernel/perf_event.c
index 6b7ddba1dd64..fd43ff4ac860 100644
--- a/kernel/perf_event.c
+++ b/kernel/perf_event.c
@@ -4286,15 +4286,8 @@ static void bp_perf_event_destroy(struct perf_event *event)
4286static const struct pmu *bp_perf_event_init(struct perf_event *bp) 4286static const struct pmu *bp_perf_event_init(struct perf_event *bp)
4287{ 4287{
4288 int err; 4288 int err;
4289 /* 4289
4290 * The breakpoint is already filled if we haven't created the counter 4290 err = register_perf_hw_breakpoint(bp);
4291 * through perf syscall
4292 * FIXME: manage to get trigerred to NULL if it comes from syscalls
4293 */
4294 if (!bp->callback)
4295 err = register_perf_hw_breakpoint(bp);
4296 else
4297 err = __register_perf_hw_breakpoint(bp);
4298 if (err) 4291 if (err)
4299 return ERR_PTR(err); 4292 return ERR_PTR(err);
4300 4293
@@ -4390,7 +4383,7 @@ perf_event_alloc(struct perf_event_attr *attr,
4390 struct perf_event_context *ctx, 4383 struct perf_event_context *ctx,
4391 struct perf_event *group_leader, 4384 struct perf_event *group_leader,
4392 struct perf_event *parent_event, 4385 struct perf_event *parent_event,
4393 perf_callback_t callback, 4386 perf_overflow_handler_t overflow_handler,
4394 gfp_t gfpflags) 4387 gfp_t gfpflags)
4395{ 4388{
4396 const struct pmu *pmu; 4389 const struct pmu *pmu;
@@ -4433,10 +4426,10 @@ perf_event_alloc(struct perf_event_attr *attr,
4433 4426
4434 event->state = PERF_EVENT_STATE_INACTIVE; 4427 event->state = PERF_EVENT_STATE_INACTIVE;
4435 4428
4436 if (!callback && parent_event) 4429 if (!overflow_handler && parent_event)
4437 callback = parent_event->callback; 4430 overflow_handler = parent_event->overflow_handler;
4438 4431
4439 event->callback = callback; 4432 event->overflow_handler = overflow_handler;
4440 4433
4441 if (attr->disabled) 4434 if (attr->disabled)
4442 event->state = PERF_EVENT_STATE_OFF; 4435 event->state = PERF_EVENT_STATE_OFF;
@@ -4776,7 +4769,8 @@ err_put_context:
4776 */ 4769 */
4777struct perf_event * 4770struct perf_event *
4778perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu, 4771perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu,
4779 pid_t pid, perf_callback_t callback) 4772 pid_t pid,
4773 perf_overflow_handler_t overflow_handler)
4780{ 4774{
4781 struct perf_event *event; 4775 struct perf_event *event;
4782 struct perf_event_context *ctx; 4776 struct perf_event_context *ctx;
@@ -4793,7 +4787,7 @@ perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu,
4793 } 4787 }
4794 4788
4795 event = perf_event_alloc(attr, cpu, ctx, NULL, 4789 event = perf_event_alloc(attr, cpu, ctx, NULL,
4796 NULL, callback, GFP_KERNEL); 4790 NULL, overflow_handler, GFP_KERNEL);
4797 if (IS_ERR(event)) { 4791 if (IS_ERR(event)) {
4798 err = PTR_ERR(event); 4792 err = PTR_ERR(event);
4799 goto err_put_context; 4793 goto err_put_context;
diff --git a/kernel/trace/trace_ksym.c b/kernel/trace/trace_ksym.c
index ddfa0fd43bc0..acb87d4a4ac1 100644
--- a/kernel/trace/trace_ksym.c
+++ b/kernel/trace/trace_ksym.c
@@ -79,11 +79,12 @@ void ksym_collect_stats(unsigned long hbp_hit_addr)
79} 79}
80#endif /* CONFIG_PROFILE_KSYM_TRACER */ 80#endif /* CONFIG_PROFILE_KSYM_TRACER */
81 81
82void ksym_hbp_handler(struct perf_event *hbp, void *data) 82void ksym_hbp_handler(struct perf_event *hbp, int nmi,
83 struct perf_sample_data *data,
84 struct pt_regs *regs)
83{ 85{
84 struct ring_buffer_event *event; 86 struct ring_buffer_event *event;
85 struct ksym_trace_entry *entry; 87 struct ksym_trace_entry *entry;
86 struct pt_regs *regs = data;
87 struct ring_buffer *buffer; 88 struct ring_buffer *buffer;
88 int pc; 89 int pc;
89 90
diff --git a/samples/hw_breakpoint/data_breakpoint.c b/samples/hw_breakpoint/data_breakpoint.c
index 29525500df00..c69cbe9b2426 100644
--- a/samples/hw_breakpoint/data_breakpoint.c
+++ b/samples/hw_breakpoint/data_breakpoint.c
@@ -41,7 +41,9 @@ module_param_string(ksym, ksym_name, KSYM_NAME_LEN, S_IRUGO);
41MODULE_PARM_DESC(ksym, "Kernel symbol to monitor; this module will report any" 41MODULE_PARM_DESC(ksym, "Kernel symbol to monitor; this module will report any"
42 " write operations on the kernel symbol"); 42 " write operations on the kernel symbol");
43 43
44static void sample_hbp_handler(struct perf_event *temp, void *data) 44static void sample_hbp_handler(struct perf_event *bp, int nmi,
45 struct perf_sample_data *data,
46 struct pt_regs *regs)
45{ 47{
46 printk(KERN_INFO "%s value is changed\n", ksym_name); 48 printk(KERN_INFO "%s value is changed\n", ksym_name);
47 dump_stack(); 49 dump_stack();
@@ -51,8 +53,9 @@ static void sample_hbp_handler(struct perf_event *temp, void *data)
51static int __init hw_break_module_init(void) 53static int __init hw_break_module_init(void)
52{ 54{
53 int ret; 55 int ret;
54 DEFINE_BREAKPOINT_ATTR(attr); 56 struct perf_event_attr attr;
55 57
58 hw_breakpoint_init(&attr);
56 attr.bp_addr = kallsyms_lookup_name(ksym_name); 59 attr.bp_addr = kallsyms_lookup_name(ksym_name);
57 attr.bp_len = HW_BREAKPOINT_LEN_4; 60 attr.bp_len = HW_BREAKPOINT_LEN_4;
58 attr.bp_type = HW_BREAKPOINT_W | HW_BREAKPOINT_R; 61 attr.bp_type = HW_BREAKPOINT_W | HW_BREAKPOINT_R;