perf: Add context field to perf_event
Avi Kivity [Wed, 29 Jun 2011 15:42:35 +0000 (18:42 +0300)]
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>

12 files changed:
arch/arm/kernel/ptrace.c
arch/powerpc/kernel/ptrace.c
arch/sh/kernel/ptrace_32.c
arch/x86/kernel/kgdb.c
arch/x86/kernel/ptrace.c
drivers/oprofile/oprofile_perf.c
include/linux/hw_breakpoint.h
include/linux/perf_event.h
kernel/events/core.c
kernel/events/hw_breakpoint.c
kernel/watchdog.c
samples/hw_breakpoint/data_breakpoint.c

index 0c9b105..5c19961 100644 (file)
@@ -479,7 +479,8 @@ static struct perf_event *ptrace_hbp_create(struct task_struct *tsk, int type)
        attr.bp_type    = type;
        attr.disabled   = 1;
 
-       return register_user_hw_breakpoint(&attr, ptrace_hbptriggered, tsk);
+       return register_user_hw_breakpoint(&attr, ptrace_hbptriggered, NULL,
+                                          tsk);
 }
 
 static int ptrace_gethbpregs(struct task_struct *tsk, long num,
index 3177617..05b7dd2 100644 (file)
@@ -973,7 +973,7 @@ int ptrace_set_debugreg(struct task_struct *task, unsigned long addr,
                                                                &attr.bp_type);
 
        thread->ptrace_bps[0] = bp = register_user_hw_breakpoint(&attr,
-                                                       ptrace_triggered, task);
+                                              ptrace_triggered, NULL, task);
        if (IS_ERR(bp)) {
                thread->ptrace_bps[0] = NULL;
                ptrace_put_breakpoints(task);
index 8051976..92b3c27 100644 (file)
@@ -91,7 +91,8 @@ static int set_single_step(struct task_struct *tsk, unsigned long addr)
                attr.bp_len = HW_BREAKPOINT_LEN_2;
                attr.bp_type = HW_BREAKPOINT_R;
 
-               bp = register_user_hw_breakpoint(&attr, ptrace_triggered, tsk);
+               bp = register_user_hw_breakpoint(&attr, ptrace_triggered,
+                                                NULL, tsk);
                if (IS_ERR(bp))
                        return PTR_ERR(bp);
 
index 98da6a7..00354d4 100644 (file)
@@ -638,7 +638,7 @@ void kgdb_arch_late(void)
        for (i = 0; i < HBP_NUM; i++) {
                if (breakinfo[i].pev)
                        continue;
-               breakinfo[i].pev = register_wide_hw_breakpoint(&attr, NULL);
+               breakinfo[i].pev = register_wide_hw_breakpoint(&attr, NULL, NULL);
                if (IS_ERR((void * __force)breakinfo[i].pev)) {
                        printk(KERN_ERR "kgdb: Could not allocate hw"
                               "breakpoints\nDisabling the kernel debugger\n");
index 11db2e9..8252879 100644 (file)
@@ -715,7 +715,8 @@ static int ptrace_set_breakpoint_addr(struct task_struct *tsk, int nr,
                attr.bp_type = HW_BREAKPOINT_W;
                attr.disabled = 1;
 
-               bp = register_user_hw_breakpoint(&attr, ptrace_triggered, tsk);
+               bp = register_user_hw_breakpoint(&attr, ptrace_triggered,
+                                                NULL, tsk);
 
                /*
                 * CHECKME: the previous code returned -EIO if the addr wasn't
index 9046f7b..59acf9e 100644 (file)
@@ -79,7 +79,7 @@ static int op_create_counter(int cpu, int event)
 
        pevent = perf_event_create_kernel_counter(&counter_config[event].attr,
                                                  cpu, NULL,
-                                                 op_overflow_handler);
+                                                 op_overflow_handler, NULL);
 
        if (IS_ERR(pevent))
                return PTR_ERR(pevent);
index d1e55fe..6ae9c63 100644 (file)
@@ -73,6 +73,7 @@ static inline unsigned long hw_breakpoint_len(struct perf_event *bp)
 extern struct perf_event *
 register_user_hw_breakpoint(struct perf_event_attr *attr,
                            perf_overflow_handler_t triggered,
+                           void *context,
                            struct task_struct *tsk);
 
 /* FIXME: only change from the attr, and don't unregister */
@@ -85,11 +86,13 @@ modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attr);
 extern struct perf_event *
 register_wide_hw_breakpoint_cpu(struct perf_event_attr *attr,
                                perf_overflow_handler_t triggered,
+                               void *context,
                                int cpu);
 
 extern struct perf_event * __percpu *
 register_wide_hw_breakpoint(struct perf_event_attr *attr,
-                           perf_overflow_handler_t triggered);
+                           perf_overflow_handler_t triggered,
+                           void *context);
 
 extern int register_perf_hw_breakpoint(struct perf_event *bp);
 extern int __register_perf_hw_breakpoint(struct perf_event *bp);
@@ -115,6 +118,7 @@ static inline int __init init_hw_breakpoint(void) { return 0; }
 static inline struct perf_event *
 register_user_hw_breakpoint(struct perf_event_attr *attr,
                            perf_overflow_handler_t triggered,
+                           void *context,
                            struct task_struct *tsk)    { return NULL; }
 static inline int
 modify_user_hw_breakpoint(struct perf_event *bp,
@@ -122,10 +126,12 @@ modify_user_hw_breakpoint(struct perf_event *bp,
 static inline struct perf_event *
 register_wide_hw_breakpoint_cpu(struct perf_event_attr *attr,
                                perf_overflow_handler_t  triggered,
+                               void *context,
                                int cpu)                { return NULL; }
 static inline struct perf_event * __percpu *
 register_wide_hw_breakpoint(struct perf_event_attr *attr,
-                           perf_overflow_handler_t triggered)  { return NULL; }
+                           perf_overflow_handler_t triggered,
+                           void *context)              { return NULL; }
 static inline int
 register_perf_hw_breakpoint(struct perf_event *bp)     { return -ENOSYS; }
 static inline int
index a5f54b9..2a08cac 100644 (file)
@@ -839,6 +839,7 @@ struct perf_event {
        u64                             id;
 
        perf_overflow_handler_t         overflow_handler;
+       void                            *overflow_handler_context;
 
 #ifdef CONFIG_EVENT_TRACING
        struct ftrace_event_call        *tp_event;
@@ -960,7 +961,8 @@ extern struct perf_event *
 perf_event_create_kernel_counter(struct perf_event_attr *attr,
                                int cpu,
                                struct task_struct *task,
-                               perf_overflow_handler_t callback);
+                               perf_overflow_handler_t callback,
+                               void *context);
 extern u64 perf_event_read_value(struct perf_event *event,
                                 u64 *enabled, u64 *running);
 
index 81de28d..ba8e0f4 100644 (file)
@@ -5745,7 +5745,8 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu,
                 struct task_struct *task,
                 struct perf_event *group_leader,
                 struct perf_event *parent_event,
-                perf_overflow_handler_t overflow_handler)
+                perf_overflow_handler_t overflow_handler,
+                void *context)
 {
        struct pmu *pmu;
        struct perf_event *event;
@@ -5803,10 +5804,13 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu,
 #endif
        }
 
-       if (!overflow_handler && parent_event)
+       if (!overflow_handler && parent_event) {
                overflow_handler = parent_event->overflow_handler;
+               context = parent_event->overflow_handler_context;
+       }
 
        event->overflow_handler = overflow_handler;
+       event->overflow_handler_context = context;
 
        if (attr->disabled)
                event->state = PERF_EVENT_STATE_OFF;
@@ -6073,7 +6077,8 @@ SYSCALL_DEFINE5(perf_event_open,
                }
        }
 
-       event = perf_event_alloc(&attr, cpu, task, group_leader, NULL, NULL);
+       event = perf_event_alloc(&attr, cpu, task, group_leader, NULL,
+                                NULL, NULL);
        if (IS_ERR(event)) {
                err = PTR_ERR(event);
                goto err_task;
@@ -6258,7 +6263,8 @@ err_fd:
 struct perf_event *
 perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu,
                                 struct task_struct *task,
-                                perf_overflow_handler_t overflow_handler)
+                                perf_overflow_handler_t overflow_handler,
+                                void *context)
 {
        struct perf_event_context *ctx;
        struct perf_event *event;
@@ -6268,7 +6274,8 @@ perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu,
         * Get the target context (task or percpu):
         */
 
-       event = perf_event_alloc(attr, cpu, task, NULL, NULL, overflow_handler);
+       event = perf_event_alloc(attr, cpu, task, NULL, NULL,
+                                overflow_handler, context);
        if (IS_ERR(event)) {
                err = PTR_ERR(event);
                goto err;
@@ -6552,7 +6559,7 @@ inherit_event(struct perf_event *parent_event,
                                           parent_event->cpu,
                                           child,
                                           group_leader, parent_event,
-                                          NULL);
+                                          NULL, NULL);
        if (IS_ERR(child_event))
                return child_event;
        get_ctx(child_ctx);
@@ -6579,6 +6586,8 @@ inherit_event(struct perf_event *parent_event,
 
        child_event->ctx = child_ctx;
        child_event->overflow_handler = parent_event->overflow_handler;
+       child_event->overflow_handler_context
+               = parent_event->overflow_handler_context;
 
        /*
         * Precalculate sample_data sizes
index 086adf2..b7971d6 100644 (file)
@@ -431,9 +431,11 @@ int register_perf_hw_breakpoint(struct perf_event *bp)
 struct perf_event *
 register_user_hw_breakpoint(struct perf_event_attr *attr,
                            perf_overflow_handler_t triggered,
+                           void *context,
                            struct task_struct *tsk)
 {
-       return perf_event_create_kernel_counter(attr, -1, tsk, triggered);
+       return perf_event_create_kernel_counter(attr, -1, tsk, triggered,
+                                               context);
 }
 EXPORT_SYMBOL_GPL(register_user_hw_breakpoint);
 
@@ -502,7 +504,8 @@ EXPORT_SYMBOL_GPL(unregister_hw_breakpoint);
  */
 struct perf_event * __percpu *
 register_wide_hw_breakpoint(struct perf_event_attr *attr,
-                           perf_overflow_handler_t triggered)
+                           perf_overflow_handler_t triggered,
+                           void *context)
 {
        struct perf_event * __percpu *cpu_events, **pevent, *bp;
        long err;
@@ -515,7 +518,8 @@ register_wide_hw_breakpoint(struct perf_event_attr *attr,
        get_online_cpus();
        for_each_online_cpu(cpu) {
                pevent = per_cpu_ptr(cpu_events, cpu);
-               bp = perf_event_create_kernel_counter(attr, cpu, NULL, triggered);
+               bp = perf_event_create_kernel_counter(attr, cpu, NULL,
+                                                     triggered, context);
 
                *pevent = bp;
 
index a6708e6..a933e3a 100644 (file)
@@ -375,7 +375,7 @@ static int watchdog_nmi_enable(int cpu)
        hw_nmi_watchdog_set_attr(wd_attr);
 
        /* Try to register using hardware perf events */
-       event = perf_event_create_kernel_counter(wd_attr, cpu, NULL, watchdog_overflow_callback);
+       event = perf_event_create_kernel_counter(wd_attr, cpu, NULL, watchdog_overflow_callback, NULL);
        if (!IS_ERR(event)) {
                printk(KERN_INFO "NMI watchdog enabled, takes one hw-pmu counter.\n");
                goto out_save;
index 7b164d3..ef7f322 100644 (file)
@@ -60,7 +60,7 @@ static int __init hw_break_module_init(void)
        attr.bp_len = HW_BREAKPOINT_LEN_4;
        attr.bp_type = HW_BREAKPOINT_W | HW_BREAKPOINT_R;
 
-       sample_hbp = register_wide_hw_breakpoint(&attr, sample_hbp_handler);
+       sample_hbp = register_wide_hw_breakpoint(&attr, sample_hbp_handler, NULL);
        if (IS_ERR((void __force *)sample_hbp)) {
                ret = PTR_ERR((void __force *)sample_hbp);
                goto fail;