aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/perf_event.c
diff options
context:
space:
mode:
authorLi Zefan <lizf@cn.fujitsu.com>2009-10-14 23:21:42 -0400
committerIngo Molnar <mingo@elte.hu>2009-10-15 05:35:23 -0400
commit6fb2915df7f0747d9044da9dbff5b46dc2e20830 (patch)
treeb4c5d7d913362ae6161c2859a7a385263330e965 /kernel/perf_event.c
parentb0f1a59a98d7ac2102e7e4f22904c26d564a5628 (diff)
tracing/profile: Add filter support
- Add an ioctl to allocate a filter for a perf event. - Free the filter when the associated perf event is to be freed. - Do the filtering in perf_swevent_match(). Signed-off-by: Li Zefan <lizf@cn.fujitsu.com> Acked-by: Peter Zijlstra <peterz@infradead.org> Acked-by: Frederic Weisbecker <fweisbec@gmail.com> Cc: Steven Rostedt <rostedt@goodmis.org> Cc: Tom Zanussi <tzanussi@gmail.com> LKML-Reference: <4AD69546.8050401@cn.fujitsu.com> Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel/perf_event.c')
-rw-r--r--kernel/perf_event.c80
1 files changed, 73 insertions, 7 deletions
diff --git a/kernel/perf_event.c b/kernel/perf_event.c
index 9d0b5c665883..12b5ec39bf97 100644
--- a/kernel/perf_event.c
+++ b/kernel/perf_event.c
@@ -28,6 +28,7 @@
28#include <linux/anon_inodes.h> 28#include <linux/anon_inodes.h>
29#include <linux/kernel_stat.h> 29#include <linux/kernel_stat.h>
30#include <linux/perf_event.h> 30#include <linux/perf_event.h>
31#include <linux/ftrace_event.h>
31 32
32#include <asm/irq_regs.h> 33#include <asm/irq_regs.h>
33 34
@@ -1658,6 +1659,8 @@ static struct perf_event_context *find_get_context(pid_t pid, int cpu)
1658 return ERR_PTR(err); 1659 return ERR_PTR(err);
1659} 1660}
1660 1661
1662static void perf_event_free_filter(struct perf_event *event);
1663
1661static void free_event_rcu(struct rcu_head *head) 1664static void free_event_rcu(struct rcu_head *head)
1662{ 1665{
1663 struct perf_event *event; 1666 struct perf_event *event;
@@ -1665,6 +1668,7 @@ static void free_event_rcu(struct rcu_head *head)
1665 event = container_of(head, struct perf_event, rcu_head); 1668 event = container_of(head, struct perf_event, rcu_head);
1666 if (event->ns) 1669 if (event->ns)
1667 put_pid_ns(event->ns); 1670 put_pid_ns(event->ns);
1671 perf_event_free_filter(event);
1668 kfree(event); 1672 kfree(event);
1669} 1673}
1670 1674
@@ -1974,7 +1978,8 @@ unlock:
1974 return ret; 1978 return ret;
1975} 1979}
1976 1980
1977int perf_event_set_output(struct perf_event *event, int output_fd); 1981static int perf_event_set_output(struct perf_event *event, int output_fd);
1982static int perf_event_set_filter(struct perf_event *event, void __user *arg);
1978 1983
1979static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 1984static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
1980{ 1985{
@@ -2002,6 +2007,9 @@ static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
2002 case PERF_EVENT_IOC_SET_OUTPUT: 2007 case PERF_EVENT_IOC_SET_OUTPUT:
2003 return perf_event_set_output(event, arg); 2008 return perf_event_set_output(event, arg);
2004 2009
2010 case PERF_EVENT_IOC_SET_FILTER:
2011 return perf_event_set_filter(event, (void __user *)arg);
2012
2005 default: 2013 default:
2006 return -ENOTTY; 2014 return -ENOTTY;
2007 } 2015 }
@@ -3806,9 +3814,14 @@ static int perf_swevent_is_counting(struct perf_event *event)
3806 return 1; 3814 return 1;
3807} 3815}
3808 3816
3817static int perf_tp_event_match(struct perf_event *event,
3818 struct perf_sample_data *data);
3819
3809static int perf_swevent_match(struct perf_event *event, 3820static int perf_swevent_match(struct perf_event *event,
3810 enum perf_type_id type, 3821 enum perf_type_id type,
3811 u32 event_id, struct pt_regs *regs) 3822 u32 event_id,
3823 struct perf_sample_data *data,
3824 struct pt_regs *regs)
3812{ 3825{
3813 if (!perf_swevent_is_counting(event)) 3826 if (!perf_swevent_is_counting(event))
3814 return 0; 3827 return 0;
@@ -3826,6 +3839,10 @@ static int perf_swevent_match(struct perf_event *event,
3826 return 0; 3839 return 0;
3827 } 3840 }
3828 3841
3842 if (event->attr.type == PERF_TYPE_TRACEPOINT &&
3843 !perf_tp_event_match(event, data))
3844 return 0;
3845
3829 return 1; 3846 return 1;
3830} 3847}
3831 3848
@@ -3842,7 +3859,7 @@ static void perf_swevent_ctx_event(struct perf_event_context *ctx,
3842 3859
3843 rcu_read_lock(); 3860 rcu_read_lock();
3844 list_for_each_entry_rcu(event, &ctx->event_list, event_entry) { 3861 list_for_each_entry_rcu(event, &ctx->event_list, event_entry) {
3845 if (perf_swevent_match(event, type, event_id, regs)) 3862 if (perf_swevent_match(event, type, event_id, data, regs))
3846 perf_swevent_add(event, nr, nmi, data, regs); 3863 perf_swevent_add(event, nr, nmi, data, regs);
3847 } 3864 }
3848 rcu_read_unlock(); 3865 rcu_read_unlock();
@@ -4086,6 +4103,7 @@ static const struct pmu perf_ops_task_clock = {
4086}; 4103};
4087 4104
4088#ifdef CONFIG_EVENT_PROFILE 4105#ifdef CONFIG_EVENT_PROFILE
4106
4089void perf_tp_event(int event_id, u64 addr, u64 count, void *record, 4107void perf_tp_event(int event_id, u64 addr, u64 count, void *record,
4090 int entry_size) 4108 int entry_size)
4091{ 4109{
@@ -4109,8 +4127,15 @@ void perf_tp_event(int event_id, u64 addr, u64 count, void *record,
4109} 4127}
4110EXPORT_SYMBOL_GPL(perf_tp_event); 4128EXPORT_SYMBOL_GPL(perf_tp_event);
4111 4129
4112extern int ftrace_profile_enable(int); 4130static int perf_tp_event_match(struct perf_event *event,
4113extern void ftrace_profile_disable(int); 4131 struct perf_sample_data *data)
4132{
4133 void *record = data->raw->data;
4134
4135 if (likely(!event->filter) || filter_match_preds(event->filter, record))
4136 return 1;
4137 return 0;
4138}
4114 4139
4115static void tp_perf_event_destroy(struct perf_event *event) 4140static void tp_perf_event_destroy(struct perf_event *event)
4116{ 4141{
@@ -4135,12 +4160,53 @@ static const struct pmu *tp_perf_event_init(struct perf_event *event)
4135 4160
4136 return &perf_ops_generic; 4161 return &perf_ops_generic;
4137} 4162}
4163
4164static int perf_event_set_filter(struct perf_event *event, void __user *arg)
4165{
4166 char *filter_str;
4167 int ret;
4168
4169 if (event->attr.type != PERF_TYPE_TRACEPOINT)
4170 return -EINVAL;
4171
4172 filter_str = strndup_user(arg, PAGE_SIZE);
4173 if (IS_ERR(filter_str))
4174 return PTR_ERR(filter_str);
4175
4176 ret = ftrace_profile_set_filter(event, event->attr.config, filter_str);
4177
4178 kfree(filter_str);
4179 return ret;
4180}
4181
4182static void perf_event_free_filter(struct perf_event *event)
4183{
4184 ftrace_profile_free_filter(event);
4185}
4186
4138#else 4187#else
4188
4189static int perf_tp_event_match(struct perf_event *event,
4190 struct perf_sample_data *data)
4191{
4192 return 1;
4193}
4194
4139static const struct pmu *tp_perf_event_init(struct perf_event *event) 4195static const struct pmu *tp_perf_event_init(struct perf_event *event)
4140{ 4196{
4141 return NULL; 4197 return NULL;
4142} 4198}
4143#endif 4199
4200static int perf_event_set_filter(struct perf_event *event, void __user *arg)
4201{
4202 return -ENOENT;
4203}
4204
4205static void perf_event_free_filter(struct perf_event *event)
4206{
4207}
4208
4209#endif /* CONFIG_EVENT_PROFILE */
4144 4210
4145atomic_t perf_swevent_enabled[PERF_COUNT_SW_MAX]; 4211atomic_t perf_swevent_enabled[PERF_COUNT_SW_MAX];
4146 4212
@@ -4394,7 +4460,7 @@ err_size:
4394 goto out; 4460 goto out;
4395} 4461}
4396 4462
4397int perf_event_set_output(struct perf_event *event, int output_fd) 4463static int perf_event_set_output(struct perf_event *event, int output_fd)
4398{ 4464{
4399 struct perf_event *output_event = NULL; 4465 struct perf_event *output_event = NULL;
4400 struct file *output_file = NULL; 4466 struct file *output_file = NULL;