[Devel] [PATCH vz10] ve/proc/loadavg: fold nr_unint drift exactly in cpu_cgroup_proc_loadavg()

Konstantin Khorenko khorenko at virtuozzo.com
Thu Jun 11 19:57:26 MSK 2026


cpu_cgroup_proc_loadavg() sums the per-CPU cfs_rq::nr_unint counters
into a plain int accumulator:

	int nr_running = 0;
	...
	nr_running += tg->cfs_rq[i]->nr_unint;

nr_unint is unsigned long and relies on signed reinterpretation to
fold the migration drift: a task can increment the counter on one CPU
while the matching decrement lands on another CPU, so an individual
per-CPU value can drift far negative and is stored as a huge unsigned
number; only the sum over all CPUs is meaningful.

Adding such a huge unsigned long into an int stacks two different
overflows in one expression: the modular 64-bit unsigned addition and
the implementation-defined truncating conversion back to the 32-bit
int. The drift cancels out only as long as the compiler implements
both as plain wraparound; nothing in the C standard guarantees that
for the conversion, and any innocent-looking rework of the
accumulator type or of the arithmetic breaks the cancellation and
makes the printed nr_running garbage.

Accumulate in long with an explicit (long) cast - the exact 64-bit
folding, same as calc_load_ve() does - and print it with %ld.

Fixes: e4a432fe473c ("ve/proc/loadavg: Virtualize /proc/loadavg in Containers")

Reported-by: Pavel Tikhomirov <ptikhomirov at virtuozzo.com>
https://virtuozzo.atlassian.net/browse/VSTOR-132310
Signed-off-by: Konstantin Khorenko <khorenko at virtuozzo.com>
---
 kernel/sched/core.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 9d3e83a40b0d..f19cfce5fece 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -10045,7 +10045,7 @@ int cpu_cgroup_proc_loadavg(struct cgroup_subsys_state *css,
 	struct cgroup *cgrp = css->cgroup;
 	struct task_group *tg = css_tg(css);
 	unsigned long avnrun[3];
-	int nr_running = 0;
+	long nr_running = 0;
 	int i;
 
 	if (!test_bit(CGRP_VE_ROOT, &cgrp->flags))
@@ -10064,14 +10064,14 @@ int cpu_cgroup_proc_loadavg(struct cgroup_subsys_state *css,
 		 * overhead for activate/deactivate operations. So, we
 		 * don't account child cgroup unint tasks here.
 		 */
-		nr_running += tg->cfs_rq[i]->nr_unint;
+		nr_running += (long)tg->cfs_rq[i]->nr_unint;
 #endif
 #ifdef CONFIG_RT_GROUP_SCHED
 		nr_running += tg->rt_rq[i]->rt_nr_running;
 #endif
 	}
 
-	seq_printf(p, "%lu.%02lu %lu.%02lu %lu.%02lu %d/%d %d\n",
+	seq_printf(p, "%lu.%02lu %lu.%02lu %lu.%02lu %ld/%d %d\n",
 		LOAD_INT(avnrun[0]), LOAD_FRAC(avnrun[0]),
 		LOAD_INT(avnrun[1]), LOAD_FRAC(avnrun[1]),
 		LOAD_INT(avnrun[2]), LOAD_FRAC(avnrun[2]),
-- 
2.47.1



More information about the Devel mailing list