[PATCH v4 6/7] cgroup/cpuset: Publish cpus_allowed before partition updates
From: Guopeng Zhang
Date: Thu Sep 10 2026 - 06:23:04 EST
From: Guopeng Zhang <zhangguopeng@xxxxxxxxxx>
update_cpumask() calls partition_cpus_change() before copying the new
cpus_allowed mask. A remote partition update can propagate through an
ancestor and revisit the cpuset while the old mask is still visible. The
second visit then adds back CPUs that the first visit released.
This can be reproduced on a cgroup v2 system with CPUs 1-7 online:
cd /sys/fs/cgroup
echo +cpuset > cgroup.subtree_control
mkdir remote-repro
echo 1-7 > remote-repro/cpuset.cpus
echo 1-7 > remote-repro/cpuset.cpus.exclusive
echo +cpuset > remote-repro/cgroup.subtree_control
mkdir remote-repro/part
echo 1-4 > remote-repro/part/cpuset.cpus
echo root > remote-repro/part/cpuset.cpus.partition
echo 1-3 > remote-repro/part/cpuset.cpus
cat cpuset.cpus.effective
Without this fix, CPU 4 remains missing from the top cpuset after the
remote partition is narrowed.
Copy cpus_allowed before partition_cpus_change(). All checks and
allocations that can fail have completed by this point, and cpuset_mutex
remains held for the rest of the update. Keep effective_xcpus unchanged
until afterward so the partition code can calculate the old-to-new
difference.
Fixes: f62a5d39368e ("cgroup/cpuset: Remove remote_partition_check() & make update_cpumasks_hier() handle remote partition")
Signed-off-by: Guopeng Zhang <zhangguopeng@xxxxxxxxxx>
---
kernel/cgroup/cpuset.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index bbc4868f026b..80a709bfa4b7 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -2618,10 +2618,17 @@ static int update_cpumask(struct cpuset *cs, struct cpuset *trialcs,
*/
force = !cpumask_equal(cs->effective_xcpus, trialcs->effective_xcpus);
+ /*
+ * remote_cpus_update() can propagate through an ancestor and revisit
+ * this cpuset. Make sure that it sees the new configured CPU mask.
+ */
+ spin_lock_irq(&callback_lock);
+ cpumask_copy(cs->cpus_allowed, trialcs->cpus_allowed);
+ spin_unlock_irq(&callback_lock);
+
partition_cpus_change(cs, trialcs, &tmp);
spin_lock_irq(&callback_lock);
- cpumask_copy(cs->cpus_allowed, trialcs->cpus_allowed);
cpumask_copy(cs->effective_xcpus, trialcs->effective_xcpus);
if ((old_prs > 0) && !is_partition_valid(cs))
reset_partition_data(cs);
--
2.43.0