mirror of
https://github.com/adulau/aha.git
synced 2024-12-28 03:36:19 +00:00
perf/core: Provide a kernel-internal interface to get to performance 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>
This commit is contained in:
parent
0f8f86c7bd
commit
fb0459d75c
2 changed files with 80 additions and 1 deletions
|
@ -744,6 +744,12 @@ extern int hw_perf_group_sched_in(struct perf_event *group_leader,
|
||||||
struct perf_cpu_context *cpuctx,
|
struct perf_cpu_context *cpuctx,
|
||||||
struct perf_event_context *ctx, int cpu);
|
struct perf_event_context *ctx, int cpu);
|
||||||
extern void perf_event_update_userpage(struct perf_event *event);
|
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 {
|
struct perf_sample_data {
|
||||||
u64 type;
|
u64 type;
|
||||||
|
|
|
@ -1725,6 +1725,26 @@ static int perf_release(struct inode *inode, struct file *file)
|
||||||
return 0;
|
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)
|
static int perf_event_read_size(struct perf_event *event)
|
||||||
{
|
{
|
||||||
int entry = sizeof(u64); /* value */
|
int entry = sizeof(u64); /* value */
|
||||||
|
@ -1750,7 +1770,7 @@ static int perf_event_read_size(struct perf_event *event)
|
||||||
return size;
|
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;
|
struct perf_event *child;
|
||||||
u64 total = 0;
|
u64 total = 0;
|
||||||
|
@ -1761,6 +1781,7 @@ static u64 perf_event_read_value(struct perf_event *event)
|
||||||
|
|
||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(perf_event_read_value);
|
||||||
|
|
||||||
static int perf_event_read_entry(struct perf_event *event,
|
static int perf_event_read_entry(struct perf_event *event,
|
||||||
u64 read_format, char __user *buf)
|
u64 read_format, char __user *buf)
|
||||||
|
@ -4638,6 +4659,58 @@ err_put_context:
|
||||||
return err;
|
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(¤t->perf_event_mutex);
|
||||||
|
list_add_tail(&event->owner_entry, ¤t->perf_event_list);
|
||||||
|
mutex_unlock(¤t->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:
|
* inherit a event from parent task to child task:
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in a new issue