diff options
author | Arjan van de Ven <arjan@infradead.org> | 2009-09-25 06:25:56 -0400 |
---|---|---|
committer | Frederic Weisbecker <fweisbec@gmail.com> | 2009-11-03 12:04:17 -0500 |
commit | fb0459d75c1d0a4ba3cafdd2c754e7486968a676 (patch) | |
tree | 3e7a112cbf2feb144b3e1abbc5dfb186f77b8b50 /kernel/perf_event.c | |
parent | 0f8f86c7bdd1c954fbe153af437a0d91a6c5721a (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.c | 75 |
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 | ||
1728 | int 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 | } | ||
1746 | EXPORT_SYMBOL_GPL(perf_event_release_kernel); | ||
1747 | |||
1728 | static int perf_event_read_size(struct perf_event *event) | 1748 | static 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 | ||
1753 | static u64 perf_event_read_value(struct perf_event *event) | 1773 | u64 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 | } |
1784 | EXPORT_SYMBOL_GPL(perf_event_read_value); | ||
1764 | 1785 | ||
1765 | static int perf_event_read_entry(struct perf_event *event, | 1786 | static 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 | */ | ||
4669 | struct perf_event * | ||
4670 | perf_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(¤t->perf_event_mutex); | ||
4701 | list_add_tail(&event->owner_entry, ¤t->perf_event_list); | ||
4702 | mutex_unlock(¤t->perf_event_mutex); | ||
4703 | |||
4704 | return event; | ||
4705 | |||
4706 | err_put_context: | ||
4707 | if (err < 0) | ||
4708 | put_ctx(ctx); | ||
4709 | |||
4710 | return NULL; | ||
4711 | } | ||
4712 | EXPORT_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 | */ |