Skip to content

Commit

Permalink
perf: Add context field to perf_event
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: Peter Zijlstra <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Ingo Molnar <[email protected]>
  • Loading branch information
avikivity authored and Ingo Molnar committed Jul 1, 2011
1 parent 89d6c0b commit 4dc0da8
Show file tree
Hide file tree
Showing 12 changed files with 44 additions and 20 deletions.
3 changes: 2 additions & 1 deletion arch/arm/kernel/ptrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion arch/powerpc/kernel/ptrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
3 changes: 2 additions & 1 deletion arch/sh/kernel/ptrace_32.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
2 changes: 1 addition & 1 deletion arch/x86/kernel/kgdb.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
3 changes: 2 additions & 1 deletion arch/x86/kernel/ptrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion drivers/oprofile/oprofile_perf.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
10 changes: 8 additions & 2 deletions include/linux/hw_breakpoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -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);
Expand All @@ -115,17 +118,20 @@ 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,
struct perf_event_attr *attr) { return -ENOSYS; }
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
Expand Down
4 changes: 3 additions & 1 deletion include/linux/perf_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);

Expand Down
21 changes: 15 additions & 6 deletions kernel/events/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -6258,7 +6263,8 @@ SYSCALL_DEFINE5(perf_event_open,
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;
Expand All @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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
Expand Down
10 changes: 7 additions & 3 deletions kernel/events/hw_breakpoint.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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;
Expand All @@ -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;

Expand Down
2 changes: 1 addition & 1 deletion kernel/watchdog.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion samples/hw_breakpoint/data_breakpoint.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 4dc0da8

Please sign in to comment.