@@ -7486,7 +7486,7 @@ __perf_event_exit_task(struct perf_event *child_event,
7486
7486
static void perf_event_exit_task_context (struct task_struct * child , int ctxn )
7487
7487
{
7488
7488
struct perf_event * child_event , * next ;
7489
- struct perf_event_context * child_ctx ;
7489
+ struct perf_event_context * child_ctx , * parent_ctx ;
7490
7490
unsigned long flags ;
7491
7491
7492
7492
if (likely (!child -> perf_event_ctxp [ctxn ])) {
@@ -7511,6 +7511,15 @@ static void perf_event_exit_task_context(struct task_struct *child, int ctxn)
7511
7511
raw_spin_lock (& child_ctx -> lock );
7512
7512
task_ctx_sched_out (child_ctx );
7513
7513
child -> perf_event_ctxp [ctxn ] = NULL ;
7514
+
7515
+ /*
7516
+ * In order to avoid freeing: child_ctx->parent_ctx->task
7517
+ * under perf_event_context::lock, grab another reference.
7518
+ */
7519
+ parent_ctx = child_ctx -> parent_ctx ;
7520
+ if (parent_ctx )
7521
+ get_ctx (parent_ctx );
7522
+
7514
7523
/*
7515
7524
* If this context is a clone; unclone it so it can't get
7516
7525
* swapped to another process while we're removing all
@@ -7520,6 +7529,13 @@ static void perf_event_exit_task_context(struct task_struct *child, int ctxn)
7520
7529
update_context_time (child_ctx );
7521
7530
raw_spin_unlock_irqrestore (& child_ctx -> lock , flags );
7522
7531
7532
+ /*
7533
+ * Now that we no longer hold perf_event_context::lock, drop
7534
+ * our extra child_ctx->parent_ctx reference.
7535
+ */
7536
+ if (parent_ctx )
7537
+ put_ctx (parent_ctx );
7538
+
7523
7539
/*
7524
7540
* Report the task dead after unscheduling the events so that we
7525
7541
* won't get any samples after PERF_RECORD_EXIT. We can however still
0 commit comments