Skip to content

Commit 037a3c4

Browse files
5unkn0wn-TheoriPeter Zijlstra
authored andcommitted
perf/core: Detach event groups during remove_on_exec
perf_event_remove_on_exec() removes events by calling perf_event_exit_event(). For top-level events, this removes the event from the context with DETACH_EXIT only. This can leave inconsistent group state when a removed event is a group leader and the group contains siblings without remove_on_exec. If the group was active, the surviving siblings can remain active and attached to the removed leader's sibling list, but are no longer represented by a valid group leader on the PMU context active lists. A later close of the removed leader uses DETACH_GROUP and can promote the still-active siblings from this stale group state. The next schedule-in can then add an already-linked active_list entry again, corrupting the PMU context active list. With DEBUG_LIST enabled, this is caught as a list_add double-add in merge_sched_in(). Fix this by detaching group relationships when remove_on_exec removes an event. This preserves the existing task-exit and revoke behavior, while ensuring surviving siblings are ungrouped before the removed event leaves the context. Fixes: 2e498d0 ("perf: Add support for event removal on exec") Signed-off-by: Taeyang Lee <0wn@theori.io> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Link: https://patch.msgid.link/ai65GgZcC0LAlWLG@Taeyangs-MacBook-Pro.local
1 parent dc59e4f commit 037a3c4

1 file changed

Lines changed: 9 additions & 8 deletions

File tree

kernel/events/core.c

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4729,7 +4729,7 @@ static void perf_remove_from_owner(struct perf_event *event);
47294729
static void perf_event_exit_event(struct perf_event *event,
47304730
struct perf_event_context *ctx,
47314731
struct task_struct *task,
4732-
bool revoke);
4732+
unsigned long detach_flags);
47334733

47344734
/*
47354735
* Removes all events from the current task that have been marked
@@ -4756,7 +4756,7 @@ static void perf_event_remove_on_exec(struct perf_event_context *ctx)
47564756

47574757
modified = true;
47584758

4759-
perf_event_exit_event(event, ctx, ctx->task, false);
4759+
perf_event_exit_event(event, ctx, ctx->task, DETACH_GROUP);
47604760
}
47614761

47624762
raw_spin_lock_irqsave(&ctx->lock, flags);
@@ -12937,7 +12937,7 @@ static void __pmu_detach_event(struct pmu *pmu, struct perf_event *event,
1293712937
/*
1293812938
* De-schedule the event and mark it REVOKED.
1293912939
*/
12940-
perf_event_exit_event(event, ctx, ctx->task, true);
12940+
perf_event_exit_event(event, ctx, ctx->task, DETACH_REVOKE);
1294112941

1294212942
/*
1294312943
* All _free_event() bits that rely on event->pmu:
@@ -14525,12 +14525,13 @@ static void
1452514525
perf_event_exit_event(struct perf_event *event,
1452614526
struct perf_event_context *ctx,
1452714527
struct task_struct *task,
14528-
bool revoke)
14528+
unsigned long detach_flags)
1452914529
{
1453014530
struct perf_event *parent_event = event->parent;
14531-
unsigned long detach_flags = DETACH_EXIT;
1453214531
unsigned int attach_state;
1453314532

14533+
detach_flags |= DETACH_EXIT;
14534+
1453414535
if (parent_event) {
1453514536
/*
1453614537
* Do not destroy the 'original' grouping; because of the
@@ -14553,8 +14554,8 @@ perf_event_exit_event(struct perf_event *event,
1455314554
sync_child_event(event, task);
1455414555
}
1455514556

14556-
if (revoke)
14557-
detach_flags |= DETACH_GROUP | DETACH_REVOKE;
14557+
if (detach_flags & DETACH_REVOKE)
14558+
detach_flags |= DETACH_GROUP;
1455814559

1455914560
perf_remove_from_context(event, detach_flags);
1456014561
/*
@@ -14642,7 +14643,7 @@ static void perf_event_exit_task_context(struct task_struct *task, bool exit)
1464214643
perf_event_task(task, ctx, 0);
1464314644

1464414645
list_for_each_entry_safe(child_event, next, &ctx->event_list, event_entry)
14645-
perf_event_exit_event(child_event, ctx, exit ? task : NULL, false);
14646+
perf_event_exit_event(child_event, ctx, exit ? task : NULL, 0);
1464614647

1464714648
mutex_unlock(&ctx->mutex);
1464814649

0 commit comments

Comments
 (0)