diff options
author | Frederic Weisbecker <fweisbec@gmail.com> | 2009-12-05 03:44:31 -0500 |
---|---|---|
committer | Frederic Weisbecker <fweisbec@gmail.com> | 2009-12-06 02:27:18 -0500 |
commit | b326e9560a28fc3e950637ef51847ed8f05c1335 (patch) | |
tree | 0804c8c4f28d4ae152d5e9205ce5a958f0d26b79 /kernel | |
parent | 2f0993e0fb663c49e4d1e02654f6203246be4817 (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>
Diffstat (limited to 'kernel')
-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 |
3 files changed, 17 insertions, 29 deletions
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 | ||