Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf: Add context field to perf_event

The perf_event overflow handler does not receive any caller-derived
argument, so many callers need to resort to looking up the perf_event
in their local data structure. This is ugly and doesn't scale if a
single callback services many perf_events.

Fix by adding a context parameter to perf_event_create_kernel_counter()
(and derived hardware breakpoints APIs) and storing it in the perf_event.
The field can be accessed from the callback as event->overflow_handler_context.
All callers are updated.

Signed-off-by: Avi Kivity <avi@redhat.com>
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1309362157-6596-2-git-send-email-avi@redhat.com
Signed-off-by: Ingo Molnar <mingo@elte.hu>

authored by

Avi Kivity and committed by
Ingo Molnar
4dc0da86 89d6c0b5

+44 -20
+2 -1
arch/arm/kernel/ptrace.c
··· 479 479 attr.bp_type = type; 480 480 attr.disabled = 1; 481 481 482 - return register_user_hw_breakpoint(&attr, ptrace_hbptriggered, tsk); 482 + return register_user_hw_breakpoint(&attr, ptrace_hbptriggered, NULL, 483 + tsk); 483 484 } 484 485 485 486 static int ptrace_gethbpregs(struct task_struct *tsk, long num,
+1 -1
arch/powerpc/kernel/ptrace.c
··· 973 973 &attr.bp_type); 974 974 975 975 thread->ptrace_bps[0] = bp = register_user_hw_breakpoint(&attr, 976 - ptrace_triggered, task); 976 + ptrace_triggered, NULL, task); 977 977 if (IS_ERR(bp)) { 978 978 thread->ptrace_bps[0] = NULL; 979 979 ptrace_put_breakpoints(task);
+2 -1
arch/sh/kernel/ptrace_32.c
··· 91 91 attr.bp_len = HW_BREAKPOINT_LEN_2; 92 92 attr.bp_type = HW_BREAKPOINT_R; 93 93 94 - bp = register_user_hw_breakpoint(&attr, ptrace_triggered, tsk); 94 + bp = register_user_hw_breakpoint(&attr, ptrace_triggered, 95 + NULL, tsk); 95 96 if (IS_ERR(bp)) 96 97 return PTR_ERR(bp); 97 98
+1 -1
arch/x86/kernel/kgdb.c
··· 638 638 for (i = 0; i < HBP_NUM; i++) { 639 639 if (breakinfo[i].pev) 640 640 continue; 641 - breakinfo[i].pev = register_wide_hw_breakpoint(&attr, NULL); 641 + breakinfo[i].pev = register_wide_hw_breakpoint(&attr, NULL, NULL); 642 642 if (IS_ERR((void * __force)breakinfo[i].pev)) { 643 643 printk(KERN_ERR "kgdb: Could not allocate hw" 644 644 "breakpoints\nDisabling the kernel debugger\n");
+2 -1
arch/x86/kernel/ptrace.c
··· 715 715 attr.bp_type = HW_BREAKPOINT_W; 716 716 attr.disabled = 1; 717 717 718 - bp = register_user_hw_breakpoint(&attr, ptrace_triggered, tsk); 718 + bp = register_user_hw_breakpoint(&attr, ptrace_triggered, 719 + NULL, tsk); 719 720 720 721 /* 721 722 * CHECKME: the previous code returned -EIO if the addr wasn't
+1 -1
drivers/oprofile/oprofile_perf.c
··· 79 79 80 80 pevent = perf_event_create_kernel_counter(&counter_config[event].attr, 81 81 cpu, NULL, 82 - op_overflow_handler); 82 + op_overflow_handler, NULL); 83 83 84 84 if (IS_ERR(pevent)) 85 85 return PTR_ERR(pevent);
+8 -2
include/linux/hw_breakpoint.h
··· 73 73 extern struct perf_event * 74 74 register_user_hw_breakpoint(struct perf_event_attr *attr, 75 75 perf_overflow_handler_t triggered, 76 + void *context, 76 77 struct task_struct *tsk); 77 78 78 79 /* FIXME: only change from the attr, and don't unregister */ ··· 86 85 extern struct perf_event * 87 86 register_wide_hw_breakpoint_cpu(struct perf_event_attr *attr, 88 87 perf_overflow_handler_t triggered, 88 + void *context, 89 89 int cpu); 90 90 91 91 extern struct perf_event * __percpu * 92 92 register_wide_hw_breakpoint(struct perf_event_attr *attr, 93 - perf_overflow_handler_t triggered); 93 + perf_overflow_handler_t triggered, 94 + void *context); 94 95 95 96 extern int register_perf_hw_breakpoint(struct perf_event *bp); 96 97 extern int __register_perf_hw_breakpoint(struct perf_event *bp); ··· 118 115 static inline struct perf_event * 119 116 register_user_hw_breakpoint(struct perf_event_attr *attr, 120 117 perf_overflow_handler_t triggered, 118 + void *context, 121 119 struct task_struct *tsk) { return NULL; } 122 120 static inline int 123 121 modify_user_hw_breakpoint(struct perf_event *bp, ··· 126 122 static inline struct perf_event * 127 123 register_wide_hw_breakpoint_cpu(struct perf_event_attr *attr, 128 124 perf_overflow_handler_t triggered, 125 + void *context, 129 126 int cpu) { return NULL; } 130 127 static inline struct perf_event * __percpu * 131 128 register_wide_hw_breakpoint(struct perf_event_attr *attr, 132 - perf_overflow_handler_t triggered) { return NULL; } 129 + perf_overflow_handler_t triggered, 130 + void *context) { return NULL; } 133 131 static inline int 134 132 register_perf_hw_breakpoint(struct perf_event *bp) { return -ENOSYS; } 135 133 static inline int
+3 -1
include/linux/perf_event.h
··· 839 839 u64 id; 840 840 841 841 perf_overflow_handler_t overflow_handler; 842 + void *overflow_handler_context; 842 843 843 844 #ifdef CONFIG_EVENT_TRACING 844 845 struct ftrace_event_call *tp_event; ··· 961 960 perf_event_create_kernel_counter(struct perf_event_attr *attr, 962 961 int cpu, 963 962 struct task_struct *task, 964 - perf_overflow_handler_t callback); 963 + perf_overflow_handler_t callback, 964 + void *context); 965 965 extern u64 perf_event_read_value(struct perf_event *event, 966 966 u64 *enabled, u64 *running); 967 967
+15 -6
kernel/events/core.c
··· 5745 5745 struct task_struct *task, 5746 5746 struct perf_event *group_leader, 5747 5747 struct perf_event *parent_event, 5748 - perf_overflow_handler_t overflow_handler) 5748 + perf_overflow_handler_t overflow_handler, 5749 + void *context) 5749 5750 { 5750 5751 struct pmu *pmu; 5751 5752 struct perf_event *event; ··· 5804 5803 #endif 5805 5804 } 5806 5805 5807 - if (!overflow_handler && parent_event) 5806 + if (!overflow_handler && parent_event) { 5808 5807 overflow_handler = parent_event->overflow_handler; 5808 + context = parent_event->overflow_handler_context; 5809 + } 5809 5810 5810 5811 event->overflow_handler = overflow_handler; 5812 + event->overflow_handler_context = context; 5811 5813 5812 5814 if (attr->disabled) 5813 5815 event->state = PERF_EVENT_STATE_OFF; ··· 6077 6073 } 6078 6074 } 6079 6075 6080 - event = perf_event_alloc(&attr, cpu, task, group_leader, NULL, NULL); 6076 + event = perf_event_alloc(&attr, cpu, task, group_leader, NULL, 6077 + NULL, NULL); 6081 6078 if (IS_ERR(event)) { 6082 6079 err = PTR_ERR(event); 6083 6080 goto err_task; ··· 6263 6258 struct perf_event * 6264 6259 perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu, 6265 6260 struct task_struct *task, 6266 - perf_overflow_handler_t overflow_handler) 6261 + perf_overflow_handler_t overflow_handler, 6262 + void *context) 6267 6263 { 6268 6264 struct perf_event_context *ctx; 6269 6265 struct perf_event *event; ··· 6274 6268 * Get the target context (task or percpu): 6275 6269 */ 6276 6270 6277 - event = perf_event_alloc(attr, cpu, task, NULL, NULL, overflow_handler); 6271 + event = perf_event_alloc(attr, cpu, task, NULL, NULL, 6272 + overflow_handler, context); 6278 6273 if (IS_ERR(event)) { 6279 6274 err = PTR_ERR(event); 6280 6275 goto err; ··· 6559 6552 parent_event->cpu, 6560 6553 child, 6561 6554 group_leader, parent_event, 6562 - NULL); 6555 + NULL, NULL); 6563 6556 if (IS_ERR(child_event)) 6564 6557 return child_event; 6565 6558 get_ctx(child_ctx); ··· 6586 6579 6587 6580 child_event->ctx = child_ctx; 6588 6581 child_event->overflow_handler = parent_event->overflow_handler; 6582 + child_event->overflow_handler_context 6583 + = parent_event->overflow_handler_context; 6589 6584 6590 6585 /* 6591 6586 * Precalculate sample_data sizes
+7 -3
kernel/events/hw_breakpoint.c
··· 431 431 struct perf_event * 432 432 register_user_hw_breakpoint(struct perf_event_attr *attr, 433 433 perf_overflow_handler_t triggered, 434 + void *context, 434 435 struct task_struct *tsk) 435 436 { 436 - return perf_event_create_kernel_counter(attr, -1, tsk, triggered); 437 + return perf_event_create_kernel_counter(attr, -1, tsk, triggered, 438 + context); 437 439 } 438 440 EXPORT_SYMBOL_GPL(register_user_hw_breakpoint); 439 441 ··· 504 502 */ 505 503 struct perf_event * __percpu * 506 504 register_wide_hw_breakpoint(struct perf_event_attr *attr, 507 - perf_overflow_handler_t triggered) 505 + perf_overflow_handler_t triggered, 506 + void *context) 508 507 { 509 508 struct perf_event * __percpu *cpu_events, **pevent, *bp; 510 509 long err; ··· 518 515 get_online_cpus(); 519 516 for_each_online_cpu(cpu) { 520 517 pevent = per_cpu_ptr(cpu_events, cpu); 521 - bp = perf_event_create_kernel_counter(attr, cpu, NULL, triggered); 518 + bp = perf_event_create_kernel_counter(attr, cpu, NULL, 519 + triggered, context); 522 520 523 521 *pevent = bp; 524 522
+1 -1
kernel/watchdog.c
··· 375 375 hw_nmi_watchdog_set_attr(wd_attr); 376 376 377 377 /* Try to register using hardware perf events */ 378 - event = perf_event_create_kernel_counter(wd_attr, cpu, NULL, watchdog_overflow_callback); 378 + event = perf_event_create_kernel_counter(wd_attr, cpu, NULL, watchdog_overflow_callback, NULL); 379 379 if (!IS_ERR(event)) { 380 380 printk(KERN_INFO "NMI watchdog enabled, takes one hw-pmu counter.\n"); 381 381 goto out_save;
+1 -1
samples/hw_breakpoint/data_breakpoint.c
··· 60 60 attr.bp_len = HW_BREAKPOINT_LEN_4; 61 61 attr.bp_type = HW_BREAKPOINT_W | HW_BREAKPOINT_R; 62 62 63 - sample_hbp = register_wide_hw_breakpoint(&attr, sample_hbp_handler); 63 + sample_hbp = register_wide_hw_breakpoint(&attr, sample_hbp_handler, NULL); 64 64 if (IS_ERR((void __force *)sample_hbp)) { 65 65 ret = PTR_ERR((void __force *)sample_hbp); 66 66 goto fail;