diff options
Diffstat (limited to 'kernel/perf_event.c')
| -rw-r--r-- | kernel/perf_event.c | 80 |
1 files changed, 73 insertions, 7 deletions
diff --git a/kernel/perf_event.c b/kernel/perf_event.c index afb7ef3dbc44..9ecaa45ab6b2 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 | ||
| 1662 | static void perf_event_free_filter(struct perf_event *event); | ||
| 1663 | |||
| 1661 | static void free_event_rcu(struct rcu_head *head) | 1664 | static 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 | ||
| 1977 | int perf_event_set_output(struct perf_event *event, int output_fd); | 1981 | static int perf_event_set_output(struct perf_event *event, int output_fd); |
| 1982 | static int perf_event_set_filter(struct perf_event *event, void __user *arg); | ||
| 1978 | 1983 | ||
| 1979 | static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | 1984 | static 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 | ||
| 3817 | static int perf_tp_event_match(struct perf_event *event, | ||
| 3818 | struct perf_sample_data *data); | ||
| 3819 | |||
| 3809 | static int perf_swevent_match(struct perf_event *event, | 3820 | static 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 | |||
| 4089 | void perf_tp_event(int event_id, u64 addr, u64 count, void *record, | 4107 | void 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 | } |
| 4110 | EXPORT_SYMBOL_GPL(perf_tp_event); | 4128 | EXPORT_SYMBOL_GPL(perf_tp_event); |
| 4111 | 4129 | ||
| 4112 | extern int ftrace_profile_enable(int); | 4130 | static int perf_tp_event_match(struct perf_event *event, |
| 4113 | extern 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 | ||
| 4115 | static void tp_perf_event_destroy(struct perf_event *event) | 4140 | static 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 | |||
| 4164 | static 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 | |||
| 4182 | static void perf_event_free_filter(struct perf_event *event) | ||
| 4183 | { | ||
| 4184 | ftrace_profile_free_filter(event); | ||
| 4185 | } | ||
| 4186 | |||
| 4138 | #else | 4187 | #else |
| 4188 | |||
| 4189 | static int perf_tp_event_match(struct perf_event *event, | ||
| 4190 | struct perf_sample_data *data) | ||
| 4191 | { | ||
| 4192 | return 1; | ||
| 4193 | } | ||
| 4194 | |||
| 4139 | static const struct pmu *tp_perf_event_init(struct perf_event *event) | 4195 | static const struct pmu *tp_perf_event_init(struct perf_event *event) |
| 4140 | { | 4196 | { |
| 4141 | return NULL; | 4197 | return NULL; |
| 4142 | } | 4198 | } |
| 4143 | #endif | 4199 | |
| 4200 | static int perf_event_set_filter(struct perf_event *event, void __user *arg) | ||
| 4201 | { | ||
| 4202 | return -ENOENT; | ||
| 4203 | } | ||
| 4204 | |||
| 4205 | static void perf_event_free_filter(struct perf_event *event) | ||
| 4206 | { | ||
| 4207 | } | ||
| 4208 | |||
| 4209 | #endif /* CONFIG_EVENT_PROFILE */ | ||
| 4144 | 4210 | ||
| 4145 | atomic_t perf_swevent_enabled[PERF_COUNT_SW_MAX]; | 4211 | atomic_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 | ||
| 4397 | int perf_event_set_output(struct perf_event *event, int output_fd) | 4463 | static 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; |
