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 |
