diff options
author | Peter Zijlstra <peterz@infradead.org> | 2014-09-30 13:23:08 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2014-10-02 23:41:06 -0400 |
commit | 211de6eba8960521e2be450a7d07db85fba4604c (patch) | |
tree | 8906de527a99e671d03f6f9fc0b4a11eac892a62 /kernel/events | |
parent | fe82dcec644244676d55a1384c958d5f67979adb (diff) |
perf: Fix unclone_ctx() vs. locking
The idiot who did 4a1c0f262f88 ("perf: Fix lockdep warning on process exit")
forgot to pay attention and fix all similar cases. Do so now.
In particular, unclone_ctx() must be called while holding ctx->lock,
therefore all such sites are broken for the same reason. Pull the
put_ctx() call out from under ctx->lock.
Reported-by: Sasha Levin <sasha.levin@oracle.com>
Probably-also-reported-by: Vince Weaver <vincent.weaver@maine.edu>
Fixes: 4a1c0f262f88 ("perf: Fix lockdep warning on process exit")
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Sasha Levin <sasha.levin@oracle.com>
Cc: Cong Wang <cwang@twopensource.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Link: http://lkml.kernel.org/r/20140930172308.GI4241@worktop.programming.kicks-ass.net
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'kernel/events')
-rw-r--r-- | kernel/events/core.c | 54 |
1 files changed, 31 insertions, 23 deletions
diff --git a/kernel/events/core.c b/kernel/events/core.c index d640a8b4dcbc..afdd9e1d7144 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c | |||
@@ -902,13 +902,23 @@ static void put_ctx(struct perf_event_context *ctx) | |||
902 | } | 902 | } |
903 | } | 903 | } |
904 | 904 | ||
905 | static void unclone_ctx(struct perf_event_context *ctx) | 905 | /* |
906 | * This must be done under the ctx->lock, such as to serialize against | ||
907 | * context_equiv(), therefore we cannot call put_ctx() since that might end up | ||
908 | * calling scheduler related locks and ctx->lock nests inside those. | ||
909 | */ | ||
910 | static __must_check struct perf_event_context * | ||
911 | unclone_ctx(struct perf_event_context *ctx) | ||
906 | { | 912 | { |
907 | if (ctx->parent_ctx) { | 913 | struct perf_event_context *parent_ctx = ctx->parent_ctx; |
908 | put_ctx(ctx->parent_ctx); | 914 | |
915 | lockdep_assert_held(&ctx->lock); | ||
916 | |||
917 | if (parent_ctx) | ||
909 | ctx->parent_ctx = NULL; | 918 | ctx->parent_ctx = NULL; |
910 | } | ||
911 | ctx->generation++; | 919 | ctx->generation++; |
920 | |||
921 | return parent_ctx; | ||
912 | } | 922 | } |
913 | 923 | ||
914 | static u32 perf_event_pid(struct perf_event *event, struct task_struct *p) | 924 | static u32 perf_event_pid(struct perf_event *event, struct task_struct *p) |
@@ -2210,6 +2220,9 @@ static void ctx_sched_out(struct perf_event_context *ctx, | |||
2210 | static int context_equiv(struct perf_event_context *ctx1, | 2220 | static int context_equiv(struct perf_event_context *ctx1, |
2211 | struct perf_event_context *ctx2) | 2221 | struct perf_event_context *ctx2) |
2212 | { | 2222 | { |
2223 | lockdep_assert_held(&ctx1->lock); | ||
2224 | lockdep_assert_held(&ctx2->lock); | ||
2225 | |||
2213 | /* Pinning disables the swap optimization */ | 2226 | /* Pinning disables the swap optimization */ |
2214 | if (ctx1->pin_count || ctx2->pin_count) | 2227 | if (ctx1->pin_count || ctx2->pin_count) |
2215 | return 0; | 2228 | return 0; |
@@ -2943,6 +2956,7 @@ static int event_enable_on_exec(struct perf_event *event, | |||
2943 | */ | 2956 | */ |
2944 | static void perf_event_enable_on_exec(struct perf_event_context *ctx) | 2957 | static void perf_event_enable_on_exec(struct perf_event_context *ctx) |
2945 | { | 2958 | { |
2959 | struct perf_event_context *clone_ctx = NULL; | ||
2946 | struct perf_event *event; | 2960 | struct perf_event *event; |
2947 | unsigned long flags; | 2961 | unsigned long flags; |
2948 | int enabled = 0; | 2962 | int enabled = 0; |
@@ -2974,7 +2988,7 @@ static void perf_event_enable_on_exec(struct perf_event_context *ctx) | |||
2974 | * Unclone this context if we enabled any event. | 2988 | * Unclone this context if we enabled any event. |
2975 | */ | 2989 | */ |
2976 | if (enabled) | 2990 | if (enabled) |
2977 | unclone_ctx(ctx); | 2991 | clone_ctx = unclone_ctx(ctx); |
2978 | 2992 | ||
2979 | raw_spin_unlock(&ctx->lock); | 2993 | raw_spin_unlock(&ctx->lock); |
2980 | 2994 | ||
@@ -2984,6 +2998,9 @@ static void perf_event_enable_on_exec(struct perf_event_context *ctx) | |||
2984 | perf_event_context_sched_in(ctx, ctx->task); | 2998 | perf_event_context_sched_in(ctx, ctx->task); |
2985 | out: | 2999 | out: |
2986 | local_irq_restore(flags); | 3000 | local_irq_restore(flags); |
3001 | |||
3002 | if (clone_ctx) | ||
3003 | put_ctx(clone_ctx); | ||
2987 | } | 3004 | } |
2988 | 3005 | ||
2989 | void perf_event_exec(void) | 3006 | void perf_event_exec(void) |
@@ -3135,7 +3152,7 @@ errout: | |||
3135 | static struct perf_event_context * | 3152 | static struct perf_event_context * |
3136 | find_get_context(struct pmu *pmu, struct task_struct *task, int cpu) | 3153 | find_get_context(struct pmu *pmu, struct task_struct *task, int cpu) |
3137 | { | 3154 | { |
3138 | struct perf_event_context *ctx; | 3155 | struct perf_event_context *ctx, *clone_ctx = NULL; |
3139 | struct perf_cpu_context *cpuctx; | 3156 | struct perf_cpu_context *cpuctx; |
3140 | unsigned long flags; | 3157 | unsigned long flags; |
3141 | int ctxn, err; | 3158 | int ctxn, err; |
@@ -3169,9 +3186,12 @@ find_get_context(struct pmu *pmu, struct task_struct *task, int cpu) | |||
3169 | retry: | 3186 | retry: |
3170 | ctx = perf_lock_task_context(task, ctxn, &flags); | 3187 | ctx = perf_lock_task_context(task, ctxn, &flags); |
3171 | if (ctx) { | 3188 | if (ctx) { |
3172 | unclone_ctx(ctx); | 3189 | clone_ctx = unclone_ctx(ctx); |
3173 | ++ctx->pin_count; | 3190 | ++ctx->pin_count; |
3174 | raw_spin_unlock_irqrestore(&ctx->lock, flags); | 3191 | raw_spin_unlock_irqrestore(&ctx->lock, flags); |
3192 | |||
3193 | if (clone_ctx) | ||
3194 | put_ctx(clone_ctx); | ||
3175 | } else { | 3195 | } else { |
3176 | ctx = alloc_perf_context(pmu, task); | 3196 | ctx = alloc_perf_context(pmu, task); |
3177 | err = -ENOMEM; | 3197 | err = -ENOMEM; |
@@ -7523,7 +7543,7 @@ __perf_event_exit_task(struct perf_event *child_event, | |||
7523 | static void perf_event_exit_task_context(struct task_struct *child, int ctxn) | 7543 | static void perf_event_exit_task_context(struct task_struct *child, int ctxn) |
7524 | { | 7544 | { |
7525 | struct perf_event *child_event, *next; | 7545 | struct perf_event *child_event, *next; |
7526 | struct perf_event_context *child_ctx, *parent_ctx; | 7546 | struct perf_event_context *child_ctx, *clone_ctx = NULL; |
7527 | unsigned long flags; | 7547 | unsigned long flags; |
7528 | 7548 | ||
7529 | if (likely(!child->perf_event_ctxp[ctxn])) { | 7549 | if (likely(!child->perf_event_ctxp[ctxn])) { |
@@ -7550,28 +7570,16 @@ static void perf_event_exit_task_context(struct task_struct *child, int ctxn) | |||
7550 | child->perf_event_ctxp[ctxn] = NULL; | 7570 | child->perf_event_ctxp[ctxn] = NULL; |
7551 | 7571 | ||
7552 | /* | 7572 | /* |
7553 | * In order to avoid freeing: child_ctx->parent_ctx->task | ||
7554 | * under perf_event_context::lock, grab another reference. | ||
7555 | */ | ||
7556 | parent_ctx = child_ctx->parent_ctx; | ||
7557 | if (parent_ctx) | ||
7558 | get_ctx(parent_ctx); | ||
7559 | |||
7560 | /* | ||
7561 | * If this context is a clone; unclone it so it can't get | 7573 | * If this context is a clone; unclone it so it can't get |
7562 | * swapped to another process while we're removing all | 7574 | * swapped to another process while we're removing all |
7563 | * the events from it. | 7575 | * the events from it. |
7564 | */ | 7576 | */ |
7565 | unclone_ctx(child_ctx); | 7577 | clone_ctx = unclone_ctx(child_ctx); |
7566 | update_context_time(child_ctx); | 7578 | update_context_time(child_ctx); |
7567 | raw_spin_unlock_irqrestore(&child_ctx->lock, flags); | 7579 | raw_spin_unlock_irqrestore(&child_ctx->lock, flags); |
7568 | 7580 | ||
7569 | /* | 7581 | if (clone_ctx) |
7570 | * Now that we no longer hold perf_event_context::lock, drop | 7582 | put_ctx(clone_ctx); |
7571 | * our extra child_ctx->parent_ctx reference. | ||
7572 | */ | ||
7573 | if (parent_ctx) | ||
7574 | put_ctx(parent_ctx); | ||
7575 | 7583 | ||
7576 | /* | 7584 | /* |
7577 | * Report the task dead after unscheduling the events so that we | 7585 | * Report the task dead after unscheduling the events so that we |