Skip to content

Commit bddec73

Browse files
Peter Zijlstragregkh
authored andcommitted
perf: Fix cgroup state vs ERROR
[ Upstream commit 61988e3 ] While chasing down a missing perf_cgroup_event_disable() elsewhere, Leo Yan found that both perf_put_aux_event() and perf_remove_sibling_event() were also missing one. Specifically, the rule is that events that switch to OFF,ERROR need to call perf_cgroup_event_disable(). Unify the disable paths to ensure this. Fixes: ab43762 ("perf: Allow normal events to output AUX data") Fixes: 9f0c4fa ("perf/core: Add a new PERF_EV_CAP_SIBLING event capability") Reported-by: Leo Yan <leo.yan@arm.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Link: https://lkml.kernel.org/r/20250605123343.GD35970@noisy.programming.kicks-ass.net Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent 2ee6044 commit bddec73

1 file changed

Lines changed: 30 additions & 21 deletions

File tree

kernel/events/core.c

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2067,8 +2067,9 @@ perf_aux_output_match(struct perf_event *event, struct perf_event *aux_event)
20672067
}
20682068

20692069
static void put_event(struct perf_event *event);
2070-
static void event_sched_out(struct perf_event *event,
2071-
struct perf_event_context *ctx);
2070+
static void __event_disable(struct perf_event *event,
2071+
struct perf_event_context *ctx,
2072+
enum perf_event_state state);
20722073

20732074
static void perf_put_aux_event(struct perf_event *event)
20742075
{
@@ -2101,8 +2102,7 @@ static void perf_put_aux_event(struct perf_event *event)
21012102
* state so that we don't try to schedule it again. Note
21022103
* that perf_event_enable() will clear the ERROR status.
21032104
*/
2104-
event_sched_out(iter, ctx);
2105-
perf_event_set_state(event, PERF_EVENT_STATE_ERROR);
2105+
__event_disable(iter, ctx, PERF_EVENT_STATE_ERROR);
21062106
}
21072107
}
21082108

@@ -2156,18 +2156,6 @@ static inline struct list_head *get_event_list(struct perf_event *event)
21562156
&event->pmu_ctx->flexible_active;
21572157
}
21582158

2159-
/*
2160-
* Events that have PERF_EV_CAP_SIBLING require being part of a group and
2161-
* cannot exist on their own, schedule them out and move them into the ERROR
2162-
* state. Also see _perf_event_enable(), it will not be able to recover
2163-
* this ERROR state.
2164-
*/
2165-
static inline void perf_remove_sibling_event(struct perf_event *event)
2166-
{
2167-
event_sched_out(event, event->ctx);
2168-
perf_event_set_state(event, PERF_EVENT_STATE_ERROR);
2169-
}
2170-
21712159
static void perf_group_detach(struct perf_event *event)
21722160
{
21732161
struct perf_event *leader = event->group_leader;
@@ -2203,8 +2191,15 @@ static void perf_group_detach(struct perf_event *event)
22032191
*/
22042192
list_for_each_entry_safe(sibling, tmp, &event->sibling_list, sibling_list) {
22052193

2194+
/*
2195+
* Events that have PERF_EV_CAP_SIBLING require being part of
2196+
* a group and cannot exist on their own, schedule them out
2197+
* and move them into the ERROR state. Also see
2198+
* _perf_event_enable(), it will not be able to recover this
2199+
* ERROR state.
2200+
*/
22062201
if (sibling->event_caps & PERF_EV_CAP_SIBLING)
2207-
perf_remove_sibling_event(sibling);
2202+
__event_disable(sibling, ctx, PERF_EVENT_STATE_ERROR);
22082203

22092204
sibling->group_leader = sibling;
22102205
list_del_init(&sibling->sibling_list);
@@ -2437,6 +2432,15 @@ static void perf_remove_from_context(struct perf_event *event, unsigned long fla
24372432
event_function_call(event, __perf_remove_from_context, (void *)flags);
24382433
}
24392434

2435+
static void __event_disable(struct perf_event *event,
2436+
struct perf_event_context *ctx,
2437+
enum perf_event_state state)
2438+
{
2439+
event_sched_out(event, ctx);
2440+
perf_cgroup_event_disable(event, ctx);
2441+
perf_event_set_state(event, state);
2442+
}
2443+
24402444
/*
24412445
* Cross CPU call to disable a performance event
24422446
*/
@@ -2455,13 +2459,18 @@ static void __perf_event_disable(struct perf_event *event,
24552459

24562460
perf_pmu_disable(event->pmu_ctx->pmu);
24572461

2462+
/*
2463+
* When disabling a group leader, the whole group becomes ineligible
2464+
* to run, so schedule out the full group.
2465+
*/
24582466
if (event == event->group_leader)
24592467
group_sched_out(event, ctx);
2460-
else
2461-
event_sched_out(event, ctx);
24622468

2463-
perf_event_set_state(event, PERF_EVENT_STATE_OFF);
2464-
perf_cgroup_event_disable(event, ctx);
2469+
/*
2470+
* But only mark the leader OFF; the siblings will remain
2471+
* INACTIVE.
2472+
*/
2473+
__event_disable(event, ctx, PERF_EVENT_STATE_OFF);
24652474

24662475
perf_pmu_enable(event->pmu_ctx->pmu);
24672476
}

0 commit comments

Comments
 (0)