Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fb0459d7 authored by Arjan van de Ven's avatar Arjan van de Ven Committed by Frederic Weisbecker
Browse files

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: default avatarArjan van de Ven <arjan@linux.intel.com>
Acked-by: default avatarPeter 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: default avatarFrederic Weisbecker <fweisbec@gmail.com>
parent 0f8f86c7
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -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;
+74 −1
Original line number Diff line number Diff line
@@ -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 */
@@ -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;
@@ -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)
@@ -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:
 */