Re: [REGRESSION] perf: Rewrite core context handling breaks multiplexed cgroup events

From: Peter Zijlstra

Date: Thu Oct 15 2026 - 07:18:14 EST


On Thu, Oct 15, 2026 at 09:03:42AM +0000, Antoine HURET wrote:

/me mutters something about having to go install and learn docker and
what's wrong with simple shells scripts that poke about in /cgroup
filesystems :-)

> Analysis
> ========
>
> A cgroup event takes its times from a per-cpu cgroup clock.
> That clock only moves forward when update_cgrp_time_from_cpuctx() or
> update_cgrp_time_from_event() is called: on a cgroup switch, on a read
> of an active event, and in ctx_sched_out().
>
> Up to v6.1, perf_rotate_context() went through ctx_sched_out(), which
> updated the cgroup clock. Since bd2756811766, it only updates ctx->time
> (update_context_time()) before __pmu_ctx_sched_out().
>
> So between two cgroup switches, the rotations see a frozen cgroup
> clock. The events rotated out lose the running time they accumulated,
> and the next update (usually the read) credits the whole interval to
> the events that happen to be on the PMU at that moment.
>
> Supporting this: a task from another cgroup waking up every 0.5 ms on
> the same CPU (each wake-up goes through perf_cgroup_switch()) makes
> the values correct again.
>
>
> Proposed fix
> ============
>
> Update the cgroup clock in the rotation, as before v6.2:
>
> --- a/kernel/events/core.c
> +++ b/kernel/events/core.c
> @@ static bool perf_rotate_context(struct perf_cpu_pmu_context *cpc)
> if (cpu_event) {
> update_context_time(&cpuctx->ctx);
> + update_cgrp_time_from_cpuctx(cpuctx, false);
> __pmu_ctx_sched_out(cpu_epc, EVENT_FLEXIBLE);
> rotate_ctx(&cpuctx->ctx, cpu_event);
> __pmu_ctx_sched_in(cpu_epc, EVENT_FLEXIBLE);
>
> Tested on v7.3-rc7 (results above). Also tested on the Ubuntu 6.8
> kernel, where the values are correct for a parent cgroup, for two busy
> cgroups sharing the CPU, and for idle or mostly sleeping cgroups.
>
> I am not a perf core expert. The root cause analysis and the fix were
> done with the help of an AI assistant. I reproduced the bug, then
> built and tested the fix on my machines. Corrections or a better fix
> are welcome, and I am happy to test them. If this is the right fix, I
> can send it as a proper patch.

Yes, I think this is right.

That said; while looking at this, I wonder if we should write this like
so instead. Although perhaps we should do this on top, your patch is
trivial, correct and more convenient for backport.



---
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 7846d70be57f..029c11e7358d 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -2535,8 +2535,10 @@ __ctx_time_update(struct perf_cpu_context *cpuctx, struct perf_event_context *ct
return;

update_context_time(ctx);
- /* vPMU should not stop time */
- update_cgrp_time_from_cpuctx(cpuctx, !(event_type & EVENT_GUEST) && final);
+ if (ctx == &cpuctx->ctx) {
+ /* vPMU should not stop time */
+ update_cgrp_time_from_cpuctx(cpuctx, !(event_type & EVENT_GUEST) && final);
+ }
}
}

@@ -4633,12 +4635,12 @@ static bool perf_rotate_context(struct perf_cpu_pmu_context *cpc)
* and then, if needed CPU flexible.
*/
if (task_event || (task_epc && cpu_event)) {
- update_context_time(task_epc->ctx);
+ ctx_time_update(cpuctx, task_epc->ctx);
__pmu_ctx_sched_out(task_epc, EVENT_FLEXIBLE);
}

if (cpu_event) {
- update_context_time(&cpuctx->ctx);
+ ctx_time_update(cpuctx, &cpuctx->ctx);
__pmu_ctx_sched_out(cpu_epc, EVENT_FLEXIBLE);
rotate_ctx(&cpuctx->ctx, cpu_event);
__pmu_ctx_sched_in(cpu_epc, EVENT_FLEXIBLE);