Skip to content

Commit

Permalink
perf/core: Provide a kernel-internal interface to get to performance …
Browse files Browse the repository at this point in the history
…counters

There are reasons for kernel code to ask for, and use, performance
counters.
For example, in CPU freq governors this tends to be a good idea, but
there are other examples possible as well of course.

This patch adds the needed bits to do enable this functionality; they
have been tested in an experimental cpufreq driver that I'm working on,
and the changes are all that I needed to access counters properly.

[fweisbec@gmail.com: added pid to perf_event_create_kernel_counter so
that we can profile a particular task too

TODO: Have a better error reporting, don't just return NULL in fail
case.]

v2: Remove the wrong comment about the fact
    perf_event_create_kernel_counter must be called from a kernel
    thread.

Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Acked-by: Peter Zijlstra <peterz@infradead.org>
Cc: "K.Prasad" <prasad@linux.vnet.ibm.com>
Cc: Alan Stern <stern@rowland.harvard.edu>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Jan Kiszka <jan.kiszka@siemens.com>
Cc: Jiri Slaby <jirislaby@gmail.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Avi Kivity <avi@redhat.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Masami Hiramatsu <mhiramat@redhat.com>
Cc: Paul Mundt <lethal@linux-sh.org>
Cc: Jan Kiszka <jan.kiszka@web.de>
Cc: Avi Kivity <avi@redhat.com>
LKML-Reference: <20090925122556.2f8bd939@infradead.org>
Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com>
  • Loading branch information
Arjan van de Ven authored and Frederic Weisbecker committed Nov 3, 2009
1 parent 0f8f86c commit fb0459d
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 1 deletion.
6 changes: 6 additions & 0 deletions include/linux/perf_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,12 @@ extern int hw_perf_group_sched_in(struct perf_event *group_leader,
struct perf_cpu_context *cpuctx,
struct perf_event_context *ctx, int cpu);
extern void perf_event_update_userpage(struct perf_event *event);
extern int perf_event_release_kernel(struct perf_event *event);
extern struct perf_event *
perf_event_create_kernel_counter(struct perf_event_attr *attr,
int cpu,
pid_t pid);
extern u64 perf_event_read_value(struct perf_event *event);

struct perf_sample_data {
u64 type;
Expand Down
75 changes: 74 additions & 1 deletion kernel/perf_event.c
Original file line number Diff line number Diff line change
Expand Up @@ -1725,6 +1725,26 @@ static int perf_release(struct inode *inode, struct file *file)
return 0;
}

int perf_event_release_kernel(struct perf_event *event)
{
struct perf_event_context *ctx = event->ctx;

WARN_ON_ONCE(ctx->parent_ctx);
mutex_lock(&ctx->mutex);
perf_event_remove_from_context(event);
mutex_unlock(&ctx->mutex);

mutex_lock(&event->owner->perf_event_mutex);
list_del_init(&event->owner_entry);
mutex_unlock(&event->owner->perf_event_mutex);
put_task_struct(event->owner);

free_event(event);

return 0;
}
EXPORT_SYMBOL_GPL(perf_event_release_kernel);

static int perf_event_read_size(struct perf_event *event)
{
int entry = sizeof(u64); /* value */
Expand All @@ -1750,7 +1770,7 @@ static int perf_event_read_size(struct perf_event *event)
return size;
}

static u64 perf_event_read_value(struct perf_event *event)
u64 perf_event_read_value(struct perf_event *event)
{
struct perf_event *child;
u64 total = 0;
Expand All @@ -1761,6 +1781,7 @@ static u64 perf_event_read_value(struct perf_event *event)

return total;
}
EXPORT_SYMBOL_GPL(perf_event_read_value);

static int perf_event_read_entry(struct perf_event *event,
u64 read_format, char __user *buf)
Expand Down Expand Up @@ -4638,6 +4659,58 @@ SYSCALL_DEFINE5(perf_event_open,
return err;
}

/**
* perf_event_create_kernel_counter
*
* @attr: attributes of the counter to create
* @cpu: cpu in which the counter is bound
* @pid: task to profile
*/
struct perf_event *
perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu,
pid_t pid)
{
struct perf_event *event;
struct perf_event_context *ctx;
int err;

/*
* Get the target context (task or percpu):
*/

ctx = find_get_context(pid, cpu);
if (IS_ERR(ctx))
return NULL ;

event = perf_event_alloc(attr, cpu, ctx, NULL,
NULL, GFP_KERNEL);
err = PTR_ERR(event);
if (IS_ERR(event))
goto err_put_context;

event->filp = NULL;
WARN_ON_ONCE(ctx->parent_ctx);
mutex_lock(&ctx->mutex);
perf_install_in_context(ctx, event, cpu);
++ctx->generation;
mutex_unlock(&ctx->mutex);

event->owner = current;
get_task_struct(current);
mutex_lock(&current->perf_event_mutex);
list_add_tail(&event->owner_entry, &current->perf_event_list);
mutex_unlock(&current->perf_event_mutex);

return event;

err_put_context:
if (err < 0)
put_ctx(ctx);

return NULL;
}
EXPORT_SYMBOL_GPL(perf_event_create_kernel_counter);

/*
* inherit a event from parent task to child task:
*/
Expand Down

0 comments on commit fb0459d

Please sign in to comment.