mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-09-22 09:34:56 +02:00
perf/core: Detach event groups during remove_on_exec
[ Upstream commit037a3c43ed] 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:2e498d0a74("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 Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
a44343fe23
commit
4cdb1b3ab9
@@ -4449,7 +4449,8 @@ out:
|
||||
|
||||
static void perf_remove_from_owner(struct perf_event *event);
|
||||
static void perf_event_exit_event(struct perf_event *event,
|
||||
struct perf_event_context *ctx);
|
||||
struct perf_event_context *ctx,
|
||||
unsigned long detach_flags);
|
||||
|
||||
/*
|
||||
* Removes all events from the current task that have been marked
|
||||
@@ -4476,7 +4477,7 @@ static void perf_event_remove_on_exec(struct perf_event_context *ctx)
|
||||
|
||||
modified = true;
|
||||
|
||||
perf_event_exit_event(event, ctx);
|
||||
perf_event_exit_event(event, ctx, DETACH_GROUP);
|
||||
}
|
||||
|
||||
raw_spin_lock_irqsave(&ctx->lock, flags);
|
||||
@@ -13230,10 +13231,11 @@ static void sync_child_event(struct perf_event *child_event)
|
||||
}
|
||||
|
||||
static void
|
||||
perf_event_exit_event(struct perf_event *event, struct perf_event_context *ctx)
|
||||
perf_event_exit_event(struct perf_event *event,
|
||||
struct perf_event_context *ctx,
|
||||
unsigned long detach_flags)
|
||||
{
|
||||
struct perf_event *parent_event = event->parent;
|
||||
unsigned long detach_flags = 0;
|
||||
|
||||
if (parent_event) {
|
||||
/*
|
||||
@@ -13248,7 +13250,7 @@ perf_event_exit_event(struct perf_event *event, struct perf_event_context *ctx)
|
||||
* Do destroy all inherited groups, we don't care about those
|
||||
* and being thorough is better.
|
||||
*/
|
||||
detach_flags = DETACH_GROUP | DETACH_CHILD;
|
||||
detach_flags |= DETACH_GROUP | DETACH_CHILD;
|
||||
mutex_lock(&parent_event->child_mutex);
|
||||
}
|
||||
|
||||
@@ -13328,7 +13330,7 @@ static void perf_event_exit_task_context(struct task_struct *child)
|
||||
perf_event_task(child, child_ctx, 0);
|
||||
|
||||
list_for_each_entry_safe(child_event, next, &child_ctx->event_list, event_entry)
|
||||
perf_event_exit_event(child_event, child_ctx);
|
||||
perf_event_exit_event(child_event, child_ctx, 0);
|
||||
|
||||
mutex_unlock(&child_ctx->mutex);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user