diff options
-rw-r--r-- | arch/x86/kernel/hw_breakpoint.c | 5 | ||||
-rw-r--r-- | arch/x86/kernel/ptrace.c | 9 | ||||
-rw-r--r-- | include/linux/hw_breakpoint.h | 25 | ||||
-rw-r--r-- | include/linux/perf_event.h | 13 | ||||
-rw-r--r-- | kernel/hw_breakpoint.c | 17 | ||||
-rw-r--r-- | kernel/perf_event.c | 24 | ||||
-rw-r--r-- | kernel/trace/trace_ksym.c | 5 | ||||
-rw-r--r-- | samples/hw_breakpoint/data_breakpoint.c | 7 |
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 | ||
558 | static void ptrace_triggered(struct perf_event *bp, void *data) | 558 | static 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) \ | ||
25 | struct perf_event_attr name = { \ | ||
26 | .type = PERF_TYPE_BREAKPOINT, \ | ||
27 | .size = sizeof(name), \ | ||
28 | .pinned = 1, \ | ||
29 | }; | ||
30 | |||
31 | static inline void hw_breakpoint_init(struct perf_event_attr *attr) | 23 | static 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 | ||
38 | static inline unsigned long hw_breakpoint_addr(struct perf_event *bp) | 35 | static 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 | ||
53 | extern struct perf_event * | 50 | extern struct perf_event * |
54 | register_user_hw_breakpoint(struct perf_event_attr *attr, | 51 | register_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 | */ |
65 | extern struct perf_event * | 62 | extern struct perf_event * |
66 | register_wide_hw_breakpoint_cpu(struct perf_event_attr *attr, | 63 | register_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 | ||
70 | extern struct perf_event ** | 67 | extern struct perf_event ** |
71 | register_wide_hw_breakpoint(struct perf_event_attr *attr, | 68 | register_wide_hw_breakpoint(struct perf_event_attr *attr, |
72 | perf_callback_t triggered); | 69 | perf_overflow_handler_t triggered); |
73 | 70 | ||
74 | extern int register_perf_hw_breakpoint(struct perf_event *bp); | 71 | extern int register_perf_hw_breakpoint(struct perf_event *bp); |
75 | extern int __register_perf_hw_breakpoint(struct perf_event *bp); | 72 | extern 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 | ||
91 | static inline struct perf_event * | 88 | static inline struct perf_event * |
92 | register_user_hw_breakpoint(struct perf_event_attr *attr, | 89 | register_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; } |
95 | static inline struct perf_event * | 92 | static inline struct perf_event * |
96 | modify_user_hw_breakpoint(struct perf_event *bp, | 93 | modify_user_hw_breakpoint(struct perf_event *bp, |
97 | struct perf_event_attr *attr) { return NULL; } | 94 | struct perf_event_attr *attr) { return NULL; } |
98 | static inline struct perf_event * | 95 | static inline struct perf_event * |
99 | register_wide_hw_breakpoint_cpu(struct perf_event_attr *attr, | 96 | register_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; } |
102 | static inline struct perf_event ** | 99 | static inline struct perf_event ** |
103 | register_wide_hw_breakpoint(struct perf_event_attr *attr, | 100 | register_wide_hw_breakpoint(struct perf_event_attr *attr, |
104 | perf_callback_t triggered) { return NULL; } | 101 | perf_overflow_handler_t triggered) { return NULL; } |
105 | static inline int | 102 | static inline int |
106 | register_perf_hw_breakpoint(struct perf_event *bp) { return -ENOSYS; } | 103 | register_perf_hw_breakpoint(struct perf_event *bp) { return -ENOSYS; } |
107 | static inline int | 104 | static 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 | ||
568 | typedef void (*perf_callback_t)(struct perf_event *, void *); | ||
569 | |||
570 | struct perf_sample_data; | 568 | struct perf_sample_data; |
571 | 569 | ||
570 | typedef void (*perf_callback_t)(struct perf_event *, void *); | ||
571 | typedef 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 * | |||
779 | perf_event_create_kernel_counter(struct perf_event_attr *attr, | 780 | perf_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); |
783 | extern u64 perf_event_read_value(struct perf_event *event, | 784 | extern 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 | ||
262 | int __register_perf_hw_breakpoint(struct perf_event *bp) | 262 | int 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 | ||
285 | int 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 | */ |
298 | struct perf_event * | 291 | struct perf_event * |
299 | register_user_hw_breakpoint(struct perf_event_attr *attr, | 292 | register_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 | } |
327 | EXPORT_SYMBOL_GPL(modify_user_hw_breakpoint); | 320 | EXPORT_SYMBOL_GPL(modify_user_hw_breakpoint); |
328 | 321 | ||
@@ -347,7 +340,7 @@ EXPORT_SYMBOL_GPL(unregister_hw_breakpoint); | |||
347 | */ | 340 | */ |
348 | struct perf_event ** | 341 | struct perf_event ** |
349 | register_wide_hw_breakpoint(struct perf_event_attr *attr, | 342 | register_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) | |||
4286 | static const struct pmu *bp_perf_event_init(struct perf_event *bp) | 4286 | static 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 | */ |
4777 | struct perf_event * | 4770 | struct perf_event * |
4778 | perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu, | 4771 | perf_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 | ||
82 | void ksym_hbp_handler(struct perf_event *hbp, void *data) | 82 | void 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); | |||
41 | MODULE_PARM_DESC(ksym, "Kernel symbol to monitor; this module will report any" | 41 | MODULE_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 | ||
44 | static void sample_hbp_handler(struct perf_event *temp, void *data) | 44 | static 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) | |||
51 | static int __init hw_break_module_init(void) | 53 | static 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; |