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>
This commit is contained in:
parent
89d6c0b5bd
commit
4dc0da8696
12 changed files with 44 additions and 20 deletions
|
@ -479,7 +479,8 @@ static struct perf_event *ptrace_hbp_create(struct task_struct *tsk, int type)
|
||||||
attr.bp_type = type;
|
attr.bp_type = type;
|
||||||
attr.disabled = 1;
|
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,
|
static int ptrace_gethbpregs(struct task_struct *tsk, long num,
|
||||||
|
|
|
@ -973,7 +973,7 @@ int ptrace_set_debugreg(struct task_struct *task, unsigned long addr,
|
||||||
&attr.bp_type);
|
&attr.bp_type);
|
||||||
|
|
||||||
thread->ptrace_bps[0] = bp = register_user_hw_breakpoint(&attr,
|
thread->ptrace_bps[0] = bp = register_user_hw_breakpoint(&attr,
|
||||||
ptrace_triggered, task);
|
ptrace_triggered, NULL, task);
|
||||||
if (IS_ERR(bp)) {
|
if (IS_ERR(bp)) {
|
||||||
thread->ptrace_bps[0] = NULL;
|
thread->ptrace_bps[0] = NULL;
|
||||||
ptrace_put_breakpoints(task);
|
ptrace_put_breakpoints(task);
|
||||||
|
|
|
@ -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_len = HW_BREAKPOINT_LEN_2;
|
||||||
attr.bp_type = HW_BREAKPOINT_R;
|
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))
|
if (IS_ERR(bp))
|
||||||
return PTR_ERR(bp);
|
return PTR_ERR(bp);
|
||||||
|
|
||||||
|
|
|
@ -638,7 +638,7 @@ void kgdb_arch_late(void)
|
||||||
for (i = 0; i < HBP_NUM; i++) {
|
for (i = 0; i < HBP_NUM; i++) {
|
||||||
if (breakinfo[i].pev)
|
if (breakinfo[i].pev)
|
||||||
continue;
|
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)) {
|
if (IS_ERR((void * __force)breakinfo[i].pev)) {
|
||||||
printk(KERN_ERR "kgdb: Could not allocate hw"
|
printk(KERN_ERR "kgdb: Could not allocate hw"
|
||||||
"breakpoints\nDisabling the kernel debugger\n");
|
"breakpoints\nDisabling the kernel debugger\n");
|
||||||
|
|
|
@ -715,7 +715,8 @@ static int ptrace_set_breakpoint_addr(struct task_struct *tsk, int nr,
|
||||||
attr.bp_type = HW_BREAKPOINT_W;
|
attr.bp_type = HW_BREAKPOINT_W;
|
||||||
attr.disabled = 1;
|
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
|
* CHECKME: the previous code returned -EIO if the addr wasn't
|
||||||
|
|
|
@ -79,7 +79,7 @@ static int op_create_counter(int cpu, int event)
|
||||||
|
|
||||||
pevent = perf_event_create_kernel_counter(&counter_config[event].attr,
|
pevent = perf_event_create_kernel_counter(&counter_config[event].attr,
|
||||||
cpu, NULL,
|
cpu, NULL,
|
||||||
op_overflow_handler);
|
op_overflow_handler, NULL);
|
||||||
|
|
||||||
if (IS_ERR(pevent))
|
if (IS_ERR(pevent))
|
||||||
return PTR_ERR(pevent);
|
return PTR_ERR(pevent);
|
||||||
|
|
|
@ -73,6 +73,7 @@ static inline unsigned long hw_breakpoint_len(struct perf_event *bp)
|
||||||
extern struct perf_event *
|
extern struct perf_event *
|
||||||
register_user_hw_breakpoint(struct perf_event_attr *attr,
|
register_user_hw_breakpoint(struct perf_event_attr *attr,
|
||||||
perf_overflow_handler_t triggered,
|
perf_overflow_handler_t triggered,
|
||||||
|
void *context,
|
||||||
struct task_struct *tsk);
|
struct task_struct *tsk);
|
||||||
|
|
||||||
/* FIXME: only change from the attr, and don't unregister */
|
/* 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 *
|
extern struct perf_event *
|
||||||
register_wide_hw_breakpoint_cpu(struct perf_event_attr *attr,
|
register_wide_hw_breakpoint_cpu(struct perf_event_attr *attr,
|
||||||
perf_overflow_handler_t triggered,
|
perf_overflow_handler_t triggered,
|
||||||
|
void *context,
|
||||||
int cpu);
|
int cpu);
|
||||||
|
|
||||||
extern struct perf_event * __percpu *
|
extern struct perf_event * __percpu *
|
||||||
register_wide_hw_breakpoint(struct perf_event_attr *attr,
|
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);
|
||||||
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 *
|
static inline struct perf_event *
|
||||||
register_user_hw_breakpoint(struct perf_event_attr *attr,
|
register_user_hw_breakpoint(struct perf_event_attr *attr,
|
||||||
perf_overflow_handler_t triggered,
|
perf_overflow_handler_t triggered,
|
||||||
|
void *context,
|
||||||
struct task_struct *tsk) { return NULL; }
|
struct task_struct *tsk) { return NULL; }
|
||||||
static inline int
|
static inline int
|
||||||
modify_user_hw_breakpoint(struct perf_event *bp,
|
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 *
|
static inline struct perf_event *
|
||||||
register_wide_hw_breakpoint_cpu(struct perf_event_attr *attr,
|
register_wide_hw_breakpoint_cpu(struct perf_event_attr *attr,
|
||||||
perf_overflow_handler_t triggered,
|
perf_overflow_handler_t triggered,
|
||||||
|
void *context,
|
||||||
int cpu) { return NULL; }
|
int cpu) { return NULL; }
|
||||||
static inline struct perf_event * __percpu *
|
static inline struct perf_event * __percpu *
|
||||||
register_wide_hw_breakpoint(struct perf_event_attr *attr,
|
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
|
static inline int
|
||||||
register_perf_hw_breakpoint(struct perf_event *bp) { return -ENOSYS; }
|
register_perf_hw_breakpoint(struct perf_event *bp) { return -ENOSYS; }
|
||||||
static inline int
|
static inline int
|
||||||
|
|
|
@ -839,6 +839,7 @@ struct perf_event {
|
||||||
u64 id;
|
u64 id;
|
||||||
|
|
||||||
perf_overflow_handler_t overflow_handler;
|
perf_overflow_handler_t overflow_handler;
|
||||||
|
void *overflow_handler_context;
|
||||||
|
|
||||||
#ifdef CONFIG_EVENT_TRACING
|
#ifdef CONFIG_EVENT_TRACING
|
||||||
struct ftrace_event_call *tp_event;
|
struct ftrace_event_call *tp_event;
|
||||||
|
@ -960,7 +961,8 @@ extern struct perf_event *
|
||||||
perf_event_create_kernel_counter(struct perf_event_attr *attr,
|
perf_event_create_kernel_counter(struct perf_event_attr *attr,
|
||||||
int cpu,
|
int cpu,
|
||||||
struct task_struct *task,
|
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,
|
extern u64 perf_event_read_value(struct perf_event *event,
|
||||||
u64 *enabled, u64 *running);
|
u64 *enabled, u64 *running);
|
||||||
|
|
||||||
|
|
|
@ -5745,7 +5745,8 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu,
|
||||||
struct task_struct *task,
|
struct task_struct *task,
|
||||||
struct perf_event *group_leader,
|
struct perf_event *group_leader,
|
||||||
struct perf_event *parent_event,
|
struct perf_event *parent_event,
|
||||||
perf_overflow_handler_t overflow_handler)
|
perf_overflow_handler_t overflow_handler,
|
||||||
|
void *context)
|
||||||
{
|
{
|
||||||
struct pmu *pmu;
|
struct pmu *pmu;
|
||||||
struct perf_event *event;
|
struct perf_event *event;
|
||||||
|
@ -5803,10 +5804,13 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu,
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!overflow_handler && parent_event)
|
if (!overflow_handler && parent_event) {
|
||||||
overflow_handler = parent_event->overflow_handler;
|
overflow_handler = parent_event->overflow_handler;
|
||||||
|
context = parent_event->overflow_handler_context;
|
||||||
|
}
|
||||||
|
|
||||||
event->overflow_handler = overflow_handler;
|
event->overflow_handler = overflow_handler;
|
||||||
|
event->overflow_handler_context = context;
|
||||||
|
|
||||||
if (attr->disabled)
|
if (attr->disabled)
|
||||||
event->state = PERF_EVENT_STATE_OFF;
|
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)) {
|
if (IS_ERR(event)) {
|
||||||
err = PTR_ERR(event);
|
err = PTR_ERR(event);
|
||||||
goto err_task;
|
goto err_task;
|
||||||
|
@ -6258,7 +6263,8 @@ SYSCALL_DEFINE5(perf_event_open,
|
||||||
struct perf_event *
|
struct perf_event *
|
||||||
perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu,
|
perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu,
|
||||||
struct task_struct *task,
|
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_context *ctx;
|
||||||
struct perf_event *event;
|
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):
|
* 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)) {
|
if (IS_ERR(event)) {
|
||||||
err = PTR_ERR(event);
|
err = PTR_ERR(event);
|
||||||
goto err;
|
goto err;
|
||||||
|
@ -6552,7 +6559,7 @@ inherit_event(struct perf_event *parent_event,
|
||||||
parent_event->cpu,
|
parent_event->cpu,
|
||||||
child,
|
child,
|
||||||
group_leader, parent_event,
|
group_leader, parent_event,
|
||||||
NULL);
|
NULL, NULL);
|
||||||
if (IS_ERR(child_event))
|
if (IS_ERR(child_event))
|
||||||
return child_event;
|
return child_event;
|
||||||
get_ctx(child_ctx);
|
get_ctx(child_ctx);
|
||||||
|
@ -6579,6 +6586,8 @@ inherit_event(struct perf_event *parent_event,
|
||||||
|
|
||||||
child_event->ctx = child_ctx;
|
child_event->ctx = child_ctx;
|
||||||
child_event->overflow_handler = parent_event->overflow_handler;
|
child_event->overflow_handler = parent_event->overflow_handler;
|
||||||
|
child_event->overflow_handler_context
|
||||||
|
= parent_event->overflow_handler_context;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Precalculate sample_data sizes
|
* Precalculate sample_data sizes
|
||||||
|
|
|
@ -431,9 +431,11 @@ int register_perf_hw_breakpoint(struct perf_event *bp)
|
||||||
struct perf_event *
|
struct perf_event *
|
||||||
register_user_hw_breakpoint(struct perf_event_attr *attr,
|
register_user_hw_breakpoint(struct perf_event_attr *attr,
|
||||||
perf_overflow_handler_t triggered,
|
perf_overflow_handler_t triggered,
|
||||||
|
void *context,
|
||||||
struct task_struct *tsk)
|
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);
|
EXPORT_SYMBOL_GPL(register_user_hw_breakpoint);
|
||||||
|
|
||||||
|
@ -502,7 +504,8 @@ EXPORT_SYMBOL_GPL(unregister_hw_breakpoint);
|
||||||
*/
|
*/
|
||||||
struct perf_event * __percpu *
|
struct perf_event * __percpu *
|
||||||
register_wide_hw_breakpoint(struct perf_event_attr *attr,
|
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;
|
struct perf_event * __percpu *cpu_events, **pevent, *bp;
|
||||||
long err;
|
long err;
|
||||||
|
@ -515,7 +518,8 @@ register_wide_hw_breakpoint(struct perf_event_attr *attr,
|
||||||
get_online_cpus();
|
get_online_cpus();
|
||||||
for_each_online_cpu(cpu) {
|
for_each_online_cpu(cpu) {
|
||||||
pevent = per_cpu_ptr(cpu_events, 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;
|
*pevent = bp;
|
||||||
|
|
||||||
|
|
|
@ -375,7 +375,7 @@ static int watchdog_nmi_enable(int cpu)
|
||||||
hw_nmi_watchdog_set_attr(wd_attr);
|
hw_nmi_watchdog_set_attr(wd_attr);
|
||||||
|
|
||||||
/* Try to register using hardware perf events */
|
/* 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)) {
|
if (!IS_ERR(event)) {
|
||||||
printk(KERN_INFO "NMI watchdog enabled, takes one hw-pmu counter.\n");
|
printk(KERN_INFO "NMI watchdog enabled, takes one hw-pmu counter.\n");
|
||||||
goto out_save;
|
goto out_save;
|
||||||
|
|
|
@ -60,7 +60,7 @@ static int __init hw_break_module_init(void)
|
||||||
attr.bp_len = HW_BREAKPOINT_LEN_4;
|
attr.bp_len = HW_BREAKPOINT_LEN_4;
|
||||||
attr.bp_type = HW_BREAKPOINT_W | HW_BREAKPOINT_R;
|
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)) {
|
if (IS_ERR((void __force *)sample_hbp)) {
|
||||||
ret = PTR_ERR((void __force *)sample_hbp);
|
ret = PTR_ERR((void __force *)sample_hbp);
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
Loading…
Reference in a new issue