diff options
author | David S. Miller <davem@davemloft.net> | 2014-08-05 18:57:18 -0700 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-08-05 18:57:18 -0700 |
commit | e9011d086674caeedb0ffb6eb5b8bc5920821df3 (patch) | |
tree | ed5d06353d8dc1fa5f6da349808f8345ca2291a5 /kernel/events/core.c | |
parent | cfcfe22256d5a8a14924a1145d56017b043b554f (diff) | |
parent | c78f77e20d2ba5d4d5e478e85a6fb42556893e2d (diff) | |
download | linux-e9011d086674caeedb0ffb6eb5b8bc5920821df3.tar.gz linux-e9011d086674caeedb0ffb6eb5b8bc5920821df3.tar.bz2 linux-e9011d086674caeedb0ffb6eb5b8bc5920821df3.zip |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc
Conflicts:
arch/sparc/mm/init_64.c
Conflict was simple non-overlapping additions.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'kernel/events/core.c')
-rw-r--r-- | kernel/events/core.c | 32 |
1 files changed, 30 insertions, 2 deletions
diff --git a/kernel/events/core.c b/kernel/events/core.c index b0c95f0f06fd..6b17ac1b0c2a 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -7458,7 +7458,19 @@ __perf_event_exit_task(struct perf_event *child_event, struct perf_event_context *child_ctx, struct task_struct *child) { - perf_remove_from_context(child_event, true); + /* + * Do not destroy the 'original' grouping; because of the context + * switch optimization the original events could've ended up in a + * random child task. + * + * If we were to destroy the original group, all group related + * operations would cease to function properly after this random + * child dies. + * + * Do destroy all inherited groups, we don't care about those + * and being thorough is better. + */ + perf_remove_from_context(child_event, !!child_event->parent); /* * It can happen that the parent exits first, and has events @@ -7474,7 +7486,7 @@ __perf_event_exit_task(struct perf_event *child_event, static void perf_event_exit_task_context(struct task_struct *child, int ctxn) { struct perf_event *child_event, *next; - struct perf_event_context *child_ctx; + struct perf_event_context *child_ctx, *parent_ctx; unsigned long flags; if (likely(!child->perf_event_ctxp[ctxn])) { @@ -7499,6 +7511,15 @@ static void perf_event_exit_task_context(struct task_struct *child, int ctxn) raw_spin_lock(&child_ctx->lock); task_ctx_sched_out(child_ctx); child->perf_event_ctxp[ctxn] = NULL; + + /* + * In order to avoid freeing: child_ctx->parent_ctx->task + * under perf_event_context::lock, grab another reference. + */ + parent_ctx = child_ctx->parent_ctx; + if (parent_ctx) + get_ctx(parent_ctx); + /* * If this context is a clone; unclone it so it can't get * swapped to another process while we're removing all @@ -7509,6 +7530,13 @@ static void perf_event_exit_task_context(struct task_struct *child, int ctxn) raw_spin_unlock_irqrestore(&child_ctx->lock, flags); /* + * Now that we no longer hold perf_event_context::lock, drop + * our extra child_ctx->parent_ctx reference. + */ + if (parent_ctx) + put_ctx(parent_ctx); + + /* * Report the task dead after unscheduling the events so that we * won't get any samples after PERF_RECORD_EXIT. We can however still * get a few PERF_RECORD_READ events. |