From 666a32836c8f9daf9b0067c49b04d5201fb8f3ba Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Thu, 13 Aug 2026 09:38:55 +0200 Subject: [PATCH 1/2] sched/topology: Add a cpus_read_lock to rebuild_sched_domains() A read from /proc/sys/kernel/sched_rt_runtime_us leads to backtrace due to missing cpu_hotplug_lock with CONFIG_CPUSETS=n. The callchain is sched_rt_handler() -> partition_sched_domains() -> sched_cache_set() -> static_key_enable_cpuslocked(&sched_cache_present). sched_cache_set() itself is also invoked from sched_init_domains() which is early during the boot, holding just the sched_domains_mutex_lock(). Here is no warning because it happens before user space is running (and hotplug operations are not possible). There is also sched_cache_active_set() which acquires the hotplug lock before invoking any of the _cpuslocked() functions. This is only a problem with CONFIG_CPUSETS=n because in the =y case the other implementation of rebuild_sched_domains acquires the CPU-hotplug lock. Acquire CPU hotplug lock before in rebuild_sched_domains(), before partition_sched_domains() is invoked for the CONFIG_CPUSETS=n case. Fixes: a7660ce1590fc ("sched/cache: Fix has_multi_llcs iff at least one partition has multiple LLCs") Signed-off-by: Sebastian Andrzej Siewior Reivewed-by: Ridong Chen Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Chen Yu Reviewed-by: Tim Chen Reviewed-by: Waiman Long Reviewed-by: Valentin Schneider Reviewed-by: Shrikanth Hegde Reviewed-by: Aaron Tomlin Tested-by: Dietmar Eggemann Link: https://patch.msgid.link/20260813073855.ji2UrtVh@linutronix.de --- include/linux/cpuset.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/cpuset.h b/include/linux/cpuset.h index 9db2d4fcead1..6a3f4d4d3978 100644 --- a/include/linux/cpuset.h +++ b/include/linux/cpuset.h @@ -279,6 +279,7 @@ static inline void dl_rebuild_rd_accounting(void) static inline void rebuild_sched_domains(void) { + guard(cpus_read_lock)(); partition_sched_domains(1, NULL, NULL); } From 23906f3a1686c737bf356fdd21183b40722b2437 Mon Sep 17 00:00:00 2001 From: Jake Steinman Date: Wed, 19 Aug 2026 09:20:59 -0400 Subject: [PATCH 2/2] sched/fair: Floor tg_cpus() at 1 tg_cpus() returns cpuset_num_cpus() unfloored, while its sibling tg_tasks() already floors its result at 1. calc_concur_shares() feeds nr = min(tg_tasks(tg), tg_cpus(tg)) into __calc_smp_shares() as shares_max, so an nr of 0 makes shares_max 0. __calc_smp_shares() ends with return clamp_t(long, shares, MIN_SHARES, shares_max); and clamp() yields hi when hi < lo, so a zero shares_max silently defeats the MIN_SHARES floor and returns 0 -- the exact case the comment above that line says must return MIN_SHARES instead of 0. That leaves a group sched_entity with load.weight == 0, and __calc_prop_weight() then divides by cfs_rq->load.weight: weight *= se->load.weight; if (parent_entity(se)) weight /= cfs_rq->load.weight; which takes a #DE inside enqueue_task_fair(): Oops: divide error: 0000 [#1] SMP NOPTI RIP: 0010:enqueue_task_fair+0x422/0x950 Call Trace: enqueue_task+0x8e/0x250 wake_up_new_task+0x148/0x2e0 kernel_clone+0x1c6/0x390 __x64_sys_clone+0xcc/0x100 do_syscall_64+0x147/0x3c0 This is not survivable in practice: with panic_on_oops=0 the kernel took the first #DE and continued for 476 ms, then faulted at the same RIP with identical register state and an identical RSP, because the oops recovery path (kill task -> schedule()) re-enters the same enqueue while the rq lock is held mid-enqueue. The second fault escalates to a panic. Flooring tg_cpus() at 1 makes it symmetric with tg_tasks() and keeps shares_max >= tg_shares, so the MIN_SHARES floor in __calc_smp_shares() can no longer be bypassed. Note this only removes the division hazard. Whether cpuset_num_cpus() can legitimately return 0 -- via the cpu hotplug/suspend path where a v2 cpuset may transiently become empty, or via an RCU race -- is a separate question still open on the report thread. Fixes: 90ac22ffef48 ("sched/fair: Add cgroup_mode: max") Signed-off-by: Jake Steinman Signed-off-by: Peter Zijlstra (Intel) Link: https://lore.kernel.org/all/20260818231333.1441757-1-j@metarealtyinc.ca/ Link: https://patch.msgid.link/20260819132104.2148918-1-j@metarealtyinc.ca --- kernel/sched/fair.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 001140132a7d..6d881e530f89 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -4934,7 +4934,12 @@ static int tg_cpus(struct task_group *tg) nr = cpuset_num_cpus(cgrp); } - return nr; + /* + * An empty cpuset would propagate a 0 shares_max into + * __calc_smp_shares(), where clamp() yields hi when hi < lo and so + * defeats the MIN_SHARES floor. Match tg_tasks(), which floors at 1. + */ + return max(nr, 1); } static inline int tg_tasks(struct task_group *tg)