aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/events
diff options
context:
space:
mode:
authorAl Viro <viro@ZenIV.linux.org.uk>2012-08-20 09:59:25 -0400
committerIngo Molnar <mingo@kernel.org>2012-09-04 11:29:22 -0400
commita6fa941d94b411bbd2b6421ffbde6db3c93e65ab (patch)
tree2d80af531f906c6b27184148bbf15b8a7469c2a7 /kernel/events
parente3e45c01ae690e65f2650e5288b9af802e95a136 (diff)
perf_event: Switch to internal refcount, fix race with close()
Don't mess with file refcounts (or keep a reference to file, for that matter) in perf_event. Use explicit refcount of its own instead. Deal with the race between the final reference to event going away and new children getting created for it by use of atomic_long_inc_not_zero() in inherit_event(); just have the latter free what it had allocated and return NULL, that works out just fine (children of siblings of something doomed are created as singletons, same as if the child of leader had been created and immediately killed). Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> Cc: stable@kernel.org Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl> Link: http://lkml.kernel.org/r/20120820135925.GG23464@ZenIV.linux.org.uk Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'kernel/events')
-rw-r--r--kernel/events/core.c62
1 files changed, 33 insertions, 29 deletions
diff --git a/kernel/events/core.c b/kernel/events/core.c
index b7935fcec7d9..efef4282b8e8 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -2935,12 +2935,12 @@ EXPORT_SYMBOL_GPL(perf_event_release_kernel);
2935/* 2935/*
2936 * Called when the last reference to the file is gone. 2936 * Called when the last reference to the file is gone.
2937 */ 2937 */
2938static int perf_release(struct inode *inode, struct file *file) 2938static void put_event(struct perf_event *event)
2939{ 2939{
2940 struct perf_event *event = file->private_data;
2941 struct task_struct *owner; 2940 struct task_struct *owner;
2942 2941
2943 file->private_data = NULL; 2942 if (!atomic_long_dec_and_test(&event->refcount))
2943 return;
2944 2944
2945 rcu_read_lock(); 2945 rcu_read_lock();
2946 owner = ACCESS_ONCE(event->owner); 2946 owner = ACCESS_ONCE(event->owner);
@@ -2975,7 +2975,13 @@ static int perf_release(struct inode *inode, struct file *file)
2975 put_task_struct(owner); 2975 put_task_struct(owner);
2976 } 2976 }
2977 2977
2978 return perf_event_release_kernel(event); 2978 perf_event_release_kernel(event);
2979}
2980
2981static int perf_release(struct inode *inode, struct file *file)
2982{
2983 put_event(file->private_data);
2984 return 0;
2979} 2985}
2980 2986
2981u64 perf_event_read_value(struct perf_event *event, u64 *enabled, u64 *running) 2987u64 perf_event_read_value(struct perf_event *event, u64 *enabled, u64 *running)
@@ -3227,7 +3233,7 @@ unlock:
3227 3233
3228static const struct file_operations perf_fops; 3234static const struct file_operations perf_fops;
3229 3235
3230static struct perf_event *perf_fget_light(int fd, int *fput_needed) 3236static struct file *perf_fget_light(int fd, int *fput_needed)
3231{ 3237{
3232 struct file *file; 3238 struct file *file;
3233 3239
@@ -3241,7 +3247,7 @@ static struct perf_event *perf_fget_light(int fd, int *fput_needed)
3241 return ERR_PTR(-EBADF); 3247 return ERR_PTR(-EBADF);
3242 } 3248 }
3243 3249
3244 return file->private_data; 3250 return file;
3245} 3251}
3246 3252
3247static int perf_event_set_output(struct perf_event *event, 3253static int perf_event_set_output(struct perf_event *event,
@@ -3273,19 +3279,21 @@ static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
3273 3279
3274 case PERF_EVENT_IOC_SET_OUTPUT: 3280 case PERF_EVENT_IOC_SET_OUTPUT:
3275 { 3281 {
3282 struct file *output_file = NULL;
3276 struct perf_event *output_event = NULL; 3283 struct perf_event *output_event = NULL;
3277 int fput_needed = 0; 3284 int fput_needed = 0;
3278 int ret; 3285 int ret;
3279 3286
3280 if (arg != -1) { 3287 if (arg != -1) {
3281 output_event = perf_fget_light(arg, &fput_needed); 3288 output_file = perf_fget_light(arg, &fput_needed);
3282 if (IS_ERR(output_event)) 3289 if (IS_ERR(output_file))
3283 return PTR_ERR(output_event); 3290 return PTR_ERR(output_file);
3291 output_event = output_file->private_data;
3284 } 3292 }
3285 3293
3286 ret = perf_event_set_output(event, output_event); 3294 ret = perf_event_set_output(event, output_event);
3287 if (output_event) 3295 if (output_event)
3288 fput_light(output_event->filp, fput_needed); 3296 fput_light(output_file, fput_needed);
3289 3297
3290 return ret; 3298 return ret;
3291 } 3299 }
@@ -5950,6 +5958,7 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu,
5950 5958
5951 mutex_init(&event->mmap_mutex); 5959 mutex_init(&event->mmap_mutex);
5952 5960
5961 atomic_long_set(&event->refcount, 1);
5953 event->cpu = cpu; 5962 event->cpu = cpu;
5954 event->attr = *attr; 5963 event->attr = *attr;
5955 event->group_leader = group_leader; 5964 event->group_leader = group_leader;
@@ -6260,12 +6269,12 @@ SYSCALL_DEFINE5(perf_event_open,
6260 return event_fd; 6269 return event_fd;
6261 6270
6262 if (group_fd != -1) { 6271 if (group_fd != -1) {
6263 group_leader = perf_fget_light(group_fd, &fput_needed); 6272 group_file = perf_fget_light(group_fd, &fput_needed);
6264 if (IS_ERR(group_leader)) { 6273 if (IS_ERR(group_file)) {
6265 err = PTR_ERR(group_leader); 6274 err = PTR_ERR(group_file);
6266 goto err_fd; 6275 goto err_fd;
6267 } 6276 }
6268 group_file = group_leader->filp; 6277 group_leader = group_file->private_data;
6269 if (flags & PERF_FLAG_FD_OUTPUT) 6278 if (flags & PERF_FLAG_FD_OUTPUT)
6270 output_event = group_leader; 6279 output_event = group_leader;
6271 if (flags & PERF_FLAG_FD_NO_GROUP) 6280 if (flags & PERF_FLAG_FD_NO_GROUP)
@@ -6402,7 +6411,6 @@ SYSCALL_DEFINE5(perf_event_open,
6402 put_ctx(gctx); 6411 put_ctx(gctx);
6403 } 6412 }
6404 6413
6405 event->filp = event_file;
6406 WARN_ON_ONCE(ctx->parent_ctx); 6414 WARN_ON_ONCE(ctx->parent_ctx);
6407 mutex_lock(&ctx->mutex); 6415 mutex_lock(&ctx->mutex);
6408 6416
@@ -6496,7 +6504,6 @@ perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu,
6496 goto err_free; 6504 goto err_free;
6497 } 6505 }
6498 6506
6499 event->filp = NULL;
6500 WARN_ON_ONCE(ctx->parent_ctx); 6507 WARN_ON_ONCE(ctx->parent_ctx);
6501 mutex_lock(&ctx->mutex); 6508 mutex_lock(&ctx->mutex);
6502 perf_install_in_context(ctx, event, cpu); 6509 perf_install_in_context(ctx, event, cpu);
@@ -6578,7 +6585,7 @@ static void sync_child_event(struct perf_event *child_event,
6578 * Release the parent event, if this was the last 6585 * Release the parent event, if this was the last
6579 * reference to it. 6586 * reference to it.
6580 */ 6587 */
6581 fput(parent_event->filp); 6588 put_event(parent_event);
6582} 6589}
6583 6590
6584static void 6591static void
@@ -6654,9 +6661,8 @@ static void perf_event_exit_task_context(struct task_struct *child, int ctxn)
6654 * 6661 *
6655 * __perf_event_exit_task() 6662 * __perf_event_exit_task()
6656 * sync_child_event() 6663 * sync_child_event()
6657 * fput(parent_event->filp) 6664 * put_event()
6658 * perf_release() 6665 * mutex_lock(&ctx->mutex)
6659 * mutex_lock(&ctx->mutex)
6660 * 6666 *
6661 * But since its the parent context it won't be the same instance. 6667 * But since its the parent context it won't be the same instance.
6662 */ 6668 */
@@ -6724,7 +6730,7 @@ static void perf_free_event(struct perf_event *event,
6724 list_del_init(&event->child_list); 6730 list_del_init(&event->child_list);
6725 mutex_unlock(&parent->child_mutex); 6731 mutex_unlock(&parent->child_mutex);
6726 6732
6727 fput(parent->filp); 6733 put_event(parent);
6728 6734
6729 perf_group_detach(event); 6735 perf_group_detach(event);
6730 list_del_event(event, ctx); 6736 list_del_event(event, ctx);
@@ -6804,6 +6810,12 @@ inherit_event(struct perf_event *parent_event,
6804 NULL, NULL); 6810 NULL, NULL);
6805 if (IS_ERR(child_event)) 6811 if (IS_ERR(child_event))
6806 return child_event; 6812 return child_event;
6813
6814 if (!atomic_long_inc_not_zero(&parent_event->refcount)) {
6815 free_event(child_event);
6816 return NULL;
6817 }
6818
6807 get_ctx(child_ctx); 6819 get_ctx(child_ctx);
6808 6820
6809 /* 6821 /*
@@ -6845,14 +6857,6 @@ inherit_event(struct perf_event *parent_event,
6845 raw_spin_unlock_irqrestore(&child_ctx->lock, flags); 6857 raw_spin_unlock_irqrestore(&child_ctx->lock, flags);
6846 6858
6847 /* 6859 /*
6848 * Get a reference to the parent filp - we will fput it
6849 * when the child event exits. This is safe to do because
6850 * we are in the parent and we know that the filp still
6851 * exists and has a nonzero count:
6852 */
6853 atomic_long_inc(&parent_event->filp->f_count);
6854
6855 /*
6856 * Link this into the parent event's child list 6860 * Link this into the parent event's child list
6857 */ 6861 */
6858 WARN_ON_ONCE(parent_event->ctx->parent_ctx); 6862 WARN_ON_ONCE(parent_event->ctx->parent_ctx);