aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/perf_counter.c
diff options
context:
space:
mode:
authorPeter Zijlstra <a.p.zijlstra@chello.nl>2009-08-19 05:18:27 -0400
committerIngo Molnar <mingo@elte.hu>2009-08-25 03:36:13 -0400
commita4be7c2778d1fd8e0a8a5607bec91fa221ab2c91 (patch)
treee7aca50900a98d68173e6a45ead2815d75e2e197 /kernel/perf_counter.c
parentfa289beca9de9119c7760bd984f3640da21bc94c (diff)
perf_counter: Allow sharing of output channels
Provide the ability to configure a counter to send its output to another (already existing) counter's output stream. Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Frederic Weisbecker <fweisbec@gmail.com> Cc: Mike Galbraith <efault@gmx.de> Cc: stephane eranian <eranian@googlemail.com> Cc: Paul Mackerras <paulus@samba.org> LKML-Reference: <20090819092023.980284148@chello.nl> Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel/perf_counter.c')
-rw-r--r--kernel/perf_counter.c83
1 files changed, 80 insertions, 3 deletions
diff --git a/kernel/perf_counter.c b/kernel/perf_counter.c
index 06bf6a4f2608..53abcbefa0bf 100644
--- a/kernel/perf_counter.c
+++ b/kernel/perf_counter.c
@@ -1692,6 +1692,11 @@ static void free_counter(struct perf_counter *counter)
1692 atomic_dec(&nr_task_counters); 1692 atomic_dec(&nr_task_counters);
1693 } 1693 }
1694 1694
1695 if (counter->output) {
1696 fput(counter->output->filp);
1697 counter->output = NULL;
1698 }
1699
1695 if (counter->destroy) 1700 if (counter->destroy)
1696 counter->destroy(counter); 1701 counter->destroy(counter);
1697 1702
@@ -1977,6 +1982,8 @@ unlock:
1977 return ret; 1982 return ret;
1978} 1983}
1979 1984
1985int perf_counter_set_output(struct perf_counter *counter, int output_fd);
1986
1980static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 1987static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
1981{ 1988{
1982 struct perf_counter *counter = file->private_data; 1989 struct perf_counter *counter = file->private_data;
@@ -2000,6 +2007,9 @@ static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
2000 case PERF_COUNTER_IOC_PERIOD: 2007 case PERF_COUNTER_IOC_PERIOD:
2001 return perf_counter_period(counter, (u64 __user *)arg); 2008 return perf_counter_period(counter, (u64 __user *)arg);
2002 2009
2010 case PERF_COUNTER_IOC_SET_OUTPUT:
2011 return perf_counter_set_output(counter, arg);
2012
2003 default: 2013 default:
2004 return -ENOTTY; 2014 return -ENOTTY;
2005 } 2015 }
@@ -2270,6 +2280,11 @@ static int perf_mmap(struct file *file, struct vm_area_struct *vma)
2270 2280
2271 WARN_ON_ONCE(counter->ctx->parent_ctx); 2281 WARN_ON_ONCE(counter->ctx->parent_ctx);
2272 mutex_lock(&counter->mmap_mutex); 2282 mutex_lock(&counter->mmap_mutex);
2283 if (counter->output) {
2284 ret = -EINVAL;
2285 goto unlock;
2286 }
2287
2273 if (atomic_inc_not_zero(&counter->mmap_count)) { 2288 if (atomic_inc_not_zero(&counter->mmap_count)) {
2274 if (nr_pages != counter->data->nr_pages) 2289 if (nr_pages != counter->data->nr_pages)
2275 ret = -EINVAL; 2290 ret = -EINVAL;
@@ -2655,6 +2670,7 @@ static int perf_output_begin(struct perf_output_handle *handle,
2655 struct perf_counter *counter, unsigned int size, 2670 struct perf_counter *counter, unsigned int size,
2656 int nmi, int sample) 2671 int nmi, int sample)
2657{ 2672{
2673 struct perf_counter *output_counter;
2658 struct perf_mmap_data *data; 2674 struct perf_mmap_data *data;
2659 unsigned int offset, head; 2675 unsigned int offset, head;
2660 int have_lost; 2676 int have_lost;
@@ -2664,13 +2680,17 @@ static int perf_output_begin(struct perf_output_handle *handle,
2664 u64 lost; 2680 u64 lost;
2665 } lost_event; 2681 } lost_event;
2666 2682
2683 rcu_read_lock();
2667 /* 2684 /*
2668 * For inherited counters we send all the output towards the parent. 2685 * For inherited counters we send all the output towards the parent.
2669 */ 2686 */
2670 if (counter->parent) 2687 if (counter->parent)
2671 counter = counter->parent; 2688 counter = counter->parent;
2672 2689
2673 rcu_read_lock(); 2690 output_counter = rcu_dereference(counter->output);
2691 if (output_counter)
2692 counter = output_counter;
2693
2674 data = rcu_dereference(counter->data); 2694 data = rcu_dereference(counter->data);
2675 if (!data) 2695 if (!data)
2676 goto out; 2696 goto out;
@@ -4218,6 +4238,57 @@ err_size:
4218 goto out; 4238 goto out;
4219} 4239}
4220 4240
4241int perf_counter_set_output(struct perf_counter *counter, int output_fd)
4242{
4243 struct perf_counter *output_counter = NULL;
4244 struct file *output_file = NULL;
4245 struct perf_counter *old_output;
4246 int fput_needed = 0;
4247 int ret = -EINVAL;
4248
4249 if (!output_fd)
4250 goto set;
4251
4252 output_file = fget_light(output_fd, &fput_needed);
4253 if (!output_file)
4254 return -EBADF;
4255
4256 if (output_file->f_op != &perf_fops)
4257 goto out;
4258
4259 output_counter = output_file->private_data;
4260
4261 /* Don't chain output fds */
4262 if (output_counter->output)
4263 goto out;
4264
4265 /* Don't set an output fd when we already have an output channel */
4266 if (counter->data)
4267 goto out;
4268
4269 atomic_long_inc(&output_file->f_count);
4270
4271set:
4272 mutex_lock(&counter->mmap_mutex);
4273 old_output = counter->output;
4274 rcu_assign_pointer(counter->output, output_counter);
4275 mutex_unlock(&counter->mmap_mutex);
4276
4277 if (old_output) {
4278 /*
4279 * we need to make sure no existing perf_output_*()
4280 * is still referencing this counter.
4281 */
4282 synchronize_rcu();
4283 fput(old_output->filp);
4284 }
4285
4286 ret = 0;
4287out:
4288 fput_light(output_file, fput_needed);
4289 return ret;
4290}
4291
4221/** 4292/**
4222 * sys_perf_counter_open - open a performance counter, associate it to a task/cpu 4293 * sys_perf_counter_open - open a performance counter, associate it to a task/cpu
4223 * 4294 *
@@ -4240,7 +4311,7 @@ SYSCALL_DEFINE5(perf_counter_open,
4240 int ret; 4311 int ret;
4241 4312
4242 /* for future expandability... */ 4313 /* for future expandability... */
4243 if (flags) 4314 if (flags & ~(PERF_FLAG_FD_NO_GROUP | PERF_FLAG_FD_OUTPUT))
4244 return -EINVAL; 4315 return -EINVAL;
4245 4316
4246 ret = perf_copy_attr(attr_uptr, &attr); 4317 ret = perf_copy_attr(attr_uptr, &attr);
@@ -4268,7 +4339,7 @@ SYSCALL_DEFINE5(perf_counter_open,
4268 * Look up the group leader (we will attach this counter to it): 4339 * Look up the group leader (we will attach this counter to it):
4269 */ 4340 */
4270 group_leader = NULL; 4341 group_leader = NULL;
4271 if (group_fd != -1) { 4342 if (group_fd != -1 && !(flags & PERF_FLAG_FD_NO_GROUP)) {
4272 ret = -EINVAL; 4343 ret = -EINVAL;
4273 group_file = fget_light(group_fd, &fput_needed); 4344 group_file = fget_light(group_fd, &fput_needed);
4274 if (!group_file) 4345 if (!group_file)
@@ -4310,6 +4381,12 @@ SYSCALL_DEFINE5(perf_counter_open,
4310 if (!counter_file) 4381 if (!counter_file)
4311 goto err_free_put_context; 4382 goto err_free_put_context;
4312 4383
4384 if (flags & PERF_FLAG_FD_OUTPUT) {
4385 ret = perf_counter_set_output(counter, group_fd);
4386 if (ret)
4387 goto err_free_put_context;
4388 }
4389
4313 counter->filp = counter_file; 4390 counter->filp = counter_file;
4314 WARN_ON_ONCE(ctx->parent_ctx); 4391 WARN_ON_ONCE(ctx->parent_ctx);
4315 mutex_lock(&ctx->mutex); 4392 mutex_lock(&ctx->mutex);