diff options
author | Peter Zijlstra <peterz@infradead.org> | 2014-05-05 06:11:24 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2014-05-07 07:44:19 -0400 |
commit | 683ede43dd412c6cad0d23578a018409ac9c683e (patch) | |
tree | 18a640da4a9355e3ef9b7944f62ab4303eed122d /kernel/events | |
parent | 63342411efd2d9350ad405205da036cd45ed1640 (diff) |
perf: Rework free paths
Primarily make perf_event_release_kernel() into put_event(), this will
allow kernel space to create per-task inherited events, and is safer
in general.
Also, document the free_event() assumptions.
Signed-off-by: Peter Zijlstra <peterz@infradead.org>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Vince Weaver <vincent.weaver@maine.edu>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/n/tip-rk9pvr6e1d0559lxstltbztc@git.kernel.org
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'kernel/events')
-rw-r--r-- | kernel/events/core.c | 66 |
1 files changed, 40 insertions, 26 deletions
diff --git a/kernel/events/core.c b/kernel/events/core.c index 0de199729f04..83505a35afba 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c | |||
@@ -3251,7 +3251,8 @@ static void __free_event(struct perf_event *event) | |||
3251 | 3251 | ||
3252 | call_rcu(&event->rcu_head, free_event_rcu); | 3252 | call_rcu(&event->rcu_head, free_event_rcu); |
3253 | } | 3253 | } |
3254 | static void free_event(struct perf_event *event) | 3254 | |
3255 | static void _free_event(struct perf_event *event) | ||
3255 | { | 3256 | { |
3256 | irq_work_sync(&event->pending); | 3257 | irq_work_sync(&event->pending); |
3257 | 3258 | ||
@@ -3279,42 +3280,31 @@ static void free_event(struct perf_event *event) | |||
3279 | if (is_cgroup_event(event)) | 3280 | if (is_cgroup_event(event)) |
3280 | perf_detach_cgroup(event); | 3281 | perf_detach_cgroup(event); |
3281 | 3282 | ||
3282 | |||
3283 | __free_event(event); | 3283 | __free_event(event); |
3284 | } | 3284 | } |
3285 | 3285 | ||
3286 | int perf_event_release_kernel(struct perf_event *event) | 3286 | /* |
3287 | * Used to free events which have a known refcount of 1, such as in error paths | ||
3288 | * where the event isn't exposed yet and inherited events. | ||
3289 | */ | ||
3290 | static void free_event(struct perf_event *event) | ||
3287 | { | 3291 | { |
3288 | struct perf_event_context *ctx = event->ctx; | 3292 | if (WARN(atomic_long_cmpxchg(&event->refcount, 1, 0) != 1, |
3289 | 3293 | "unexpected event refcount: %ld; ptr=%p\n", | |
3290 | WARN_ON_ONCE(ctx->parent_ctx); | 3294 | atomic_long_read(&event->refcount), event)) { |
3291 | /* | 3295 | /* leak to avoid use-after-free */ |
3292 | * There are two ways this annotation is useful: | 3296 | return; |
3293 | * | 3297 | } |
3294 | * 1) there is a lock recursion from perf_event_exit_task | ||
3295 | * see the comment there. | ||
3296 | * | ||
3297 | * 2) there is a lock-inversion with mmap_sem through | ||
3298 | * perf_event_read_group(), which takes faults while | ||
3299 | * holding ctx->mutex, however this is called after | ||
3300 | * the last filedesc died, so there is no possibility | ||
3301 | * to trigger the AB-BA case. | ||
3302 | */ | ||
3303 | mutex_lock_nested(&ctx->mutex, SINGLE_DEPTH_NESTING); | ||
3304 | perf_remove_from_context(event, true); | ||
3305 | mutex_unlock(&ctx->mutex); | ||
3306 | |||
3307 | free_event(event); | ||
3308 | 3298 | ||
3309 | return 0; | 3299 | _free_event(event); |
3310 | } | 3300 | } |
3311 | EXPORT_SYMBOL_GPL(perf_event_release_kernel); | ||
3312 | 3301 | ||
3313 | /* | 3302 | /* |
3314 | * Called when the last reference to the file is gone. | 3303 | * Called when the last reference to the file is gone. |
3315 | */ | 3304 | */ |
3316 | static void put_event(struct perf_event *event) | 3305 | static void put_event(struct perf_event *event) |
3317 | { | 3306 | { |
3307 | struct perf_event_context *ctx = event->ctx; | ||
3318 | struct task_struct *owner; | 3308 | struct task_struct *owner; |
3319 | 3309 | ||
3320 | if (!atomic_long_dec_and_test(&event->refcount)) | 3310 | if (!atomic_long_dec_and_test(&event->refcount)) |
@@ -3353,9 +3343,33 @@ static void put_event(struct perf_event *event) | |||
3353 | put_task_struct(owner); | 3343 | put_task_struct(owner); |
3354 | } | 3344 | } |
3355 | 3345 | ||
3356 | perf_event_release_kernel(event); | 3346 | WARN_ON_ONCE(ctx->parent_ctx); |
3347 | /* | ||
3348 | * There are two ways this annotation is useful: | ||
3349 | * | ||
3350 | * 1) there is a lock recursion from perf_event_exit_task | ||
3351 | * see the comment there. | ||
3352 | * | ||
3353 | * 2) there is a lock-inversion with mmap_sem through | ||
3354 | * perf_event_read_group(), which takes faults while | ||
3355 | * holding ctx->mutex, however this is called after | ||
3356 | * the last filedesc died, so there is no possibility | ||
3357 | * to trigger the AB-BA case. | ||
3358 | */ | ||
3359 | mutex_lock_nested(&ctx->mutex, SINGLE_DEPTH_NESTING); | ||
3360 | perf_remove_from_context(event, true); | ||
3361 | mutex_unlock(&ctx->mutex); | ||
3362 | |||
3363 | _free_event(event); | ||
3357 | } | 3364 | } |
3358 | 3365 | ||
3366 | int perf_event_release_kernel(struct perf_event *event) | ||
3367 | { | ||
3368 | put_event(event); | ||
3369 | return 0; | ||
3370 | } | ||
3371 | EXPORT_SYMBOL_GPL(perf_event_release_kernel); | ||
3372 | |||
3359 | static int perf_release(struct inode *inode, struct file *file) | 3373 | static int perf_release(struct inode *inode, struct file *file) |
3360 | { | 3374 | { |
3361 | put_event(file->private_data); | 3375 | put_event(file->private_data); |