aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/perf_event.c
diff options
context:
space:
mode:
authorArjan van de Ven <arjan@infradead.org>2009-09-25 06:25:56 -0400
committerFrederic Weisbecker <fweisbec@gmail.com>2009-11-03 12:04:17 -0500
commitfb0459d75c1d0a4ba3cafdd2c754e7486968a676 (patch)
tree3e7a112cbf2feb144b3e1abbc5dfb186f77b8b50 /kernel/perf_event.c
parent0f8f86c7bdd1c954fbe153af437a0d91a6c5721a (diff)
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>
Diffstat (limited to 'kernel/perf_event.c')
-rw-r--r--kernel/perf_event.c75
1 files changed, 74 insertions, 1 deletions
diff --git a/kernel/perf_event.c b/kernel/perf_event.c
index 12b5ec39bf9..02d4ff041b0 100644
--- a/kernel/perf_event.c
+++ b/kernel/perf_event.c
@@ -1725,6 +1725,26 @@ static int perf_release(struct inode *inode, struct file *file)
1725 return 0; 1725 return 0;
1726} 1726}
1727 1727
1728int perf_event_release_kernel(struct perf_event *event)
1729{
1730 struct perf_event_context *ctx = event->ctx;
1731
1732 WARN_ON_ONCE(ctx->parent_ctx);
1733 mutex_lock(&ctx->mutex);
1734 perf_event_remove_from_context(event);
1735 mutex_unlock(&ctx->mutex);
1736
1737 mutex_lock(&event->owner->perf_event_mutex);
1738 list_del_init(&event->owner_entry);
1739 mutex_unlock(&event->owner->perf_event_mutex);
1740 put_task_struct(event->owner);
1741
1742 free_event(event);
1743
1744 return 0;
1745}
1746EXPORT_SYMBOL_GPL(perf_event_release_kernel);
1747
1728static int perf_event_read_size(struct perf_event *event) 1748static int perf_event_read_size(struct perf_event *event)
1729{ 1749{
1730 int entry = sizeof(u64); /* value */ 1750 int entry = sizeof(u64); /* value */
@@ -1750,7 +1770,7 @@ static int perf_event_read_size(struct perf_event *event)
1750 return size; 1770 return size;
1751} 1771}
1752 1772
1753static u64 perf_event_read_value(struct perf_event *event) 1773u64 perf_event_read_value(struct perf_event *event)
1754{ 1774{
1755 struct perf_event *child; 1775 struct perf_event *child;
1756 u64 total = 0; 1776 u64 total = 0;
@@ -1761,6 +1781,7 @@ static u64 perf_event_read_value(struct perf_event *event)
1761 1781
1762 return total; 1782 return total;
1763} 1783}
1784EXPORT_SYMBOL_GPL(perf_event_read_value);
1764 1785
1765static int perf_event_read_entry(struct perf_event *event, 1786static int perf_event_read_entry(struct perf_event *event,
1766 u64 read_format, char __user *buf) 1787 u64 read_format, char __user *buf)
@@ -4638,6 +4659,58 @@ err_put_context:
4638 return err; 4659 return err;
4639} 4660}
4640 4661
4662/**
4663 * perf_event_create_kernel_counter
4664 *
4665 * @attr: attributes of the counter to create
4666 * @cpu: cpu in which the counter is bound
4667 * @pid: task to profile
4668 */
4669struct perf_event *
4670perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu,
4671 pid_t pid)
4672{
4673 struct perf_event *event;
4674 struct perf_event_context *ctx;
4675 int err;
4676
4677 /*
4678 * Get the target context (task or percpu):
4679 */
4680
4681 ctx = find_get_context(pid, cpu);
4682 if (IS_ERR(ctx))
4683 return NULL ;
4684
4685 event = perf_event_alloc(attr, cpu, ctx, NULL,
4686 NULL, GFP_KERNEL);
4687 err = PTR_ERR(event);
4688 if (IS_ERR(event))
4689 goto err_put_context;
4690
4691 event->filp = NULL;
4692 WARN_ON_ONCE(ctx->parent_ctx);
4693 mutex_lock(&ctx->mutex);
4694 perf_install_in_context(ctx, event, cpu);
4695 ++ctx->generation;
4696 mutex_unlock(&ctx->mutex);
4697
4698 event->owner = current;
4699 get_task_struct(current);
4700 mutex_lock(&current->perf_event_mutex);
4701 list_add_tail(&event->owner_entry, &current->perf_event_list);
4702 mutex_unlock(&current->perf_event_mutex);
4703
4704 return event;
4705
4706err_put_context:
4707 if (err < 0)
4708 put_ctx(ctx);
4709
4710 return NULL;
4711}
4712EXPORT_SYMBOL_GPL(perf_event_create_kernel_counter);
4713
4641/* 4714/*
4642 * inherit a event from parent task to child task: 4715 * inherit a event from parent task to child task:
4643 */ 4716 */