From 10a6f00d82544a1baa901de8d28f41f28c364995 Mon Sep 17 00:00:00 2001 From: Waiman Long Date: Sun, 25 Oct 2026 17:44:44 -0400 Subject: [PATCH] cgroup/cpuset: Prevent recursive call of remote_cpus_update() via update_cpumasks_hier() Guopeng reports that changing the cpumask of a remote partition root may set the effective_cpus of the top_cpuset incorrectly. It is due to the following call sequence: update_cpumask() -> partition_cpus_change() -> remote_cpus_update() -> update_sibling_cpumasks() -> update_cpumasks_hier() -> remote_cpus_update() The second remote_cpus_update() call passes in the cs->effective_xcpus instead of the trialcs->effective_xcpus of the first remote_cpus_update() call. As a result, the second call can revert what has been done in the first call leading to incorrect effective_cpus of the top_cpuset as well as subpartitions_cpus. Fix that by adding a new remote_partition_updating flag into the cpuset structure to indicate that a remote partition is being updated and update_cpumasks_hier() should skip the whole subtree as update_cpumask() or update_exclusive_cpumask() which calls partition_cpus_change() will eventually calls update_cpumasks_hier() for that subtree anyway. This new flag is currently set only in remote_cpus_update() to prevent recursive call of remote_cpus_update() on the same cpuset. A test case is added to test_cpuset_prs.sh to verify this change. Reported-by: Guopeng Zhang Link: https://lore.kernel.org/lkml/20261010075947.333549-2-guopeng.zhang@linux.dev Reviewed-by: Guopeng Zhang Tested-by: Guopeng Zhang Fixes: f62a5d39368e ("cgroup/cpuset: Remove remote_partition_check() & make update_cpumasks_hier() handle remote partition") Signed-off-by: Waiman Long Signed-off-by: Tejun Heo --- kernel/cgroup/cpuset-internal.h | 5 +++++ kernel/cgroup/cpuset.c | 13 +++++++++---- tools/testing/selftests/cgroup/test_cpuset_prs.sh | 1 + 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/kernel/cgroup/cpuset-internal.h b/kernel/cgroup/cpuset-internal.h index 2e44dc8634dc..1ba94ffaff9d 100644 --- a/kernel/cgroup/cpuset-internal.h +++ b/kernel/cgroup/cpuset-internal.h @@ -161,6 +161,11 @@ struct cpuset { */ bool remote_partition; + /* + * Set to true when a remote partition is undergoing update. + */ + bool remote_partition_updating; + /* * number of SCHED_DEADLINE tasks attached to this cpuset, so that we * know when to rebuild associated root domain bandwidth information. diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c index 585abff94e8c..f674a0c9baf7 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -1775,6 +1775,7 @@ static void remote_cpus_update(struct cpuset *cs, struct cpumask *xcpus, goto invalidate; } + cs->remote_partition_updating = true; spin_lock_irq(&callback_lock); if (adding) partition_xcpus_add(prs, NULL, tmp->addmask); @@ -1796,6 +1797,7 @@ static void remote_cpus_update(struct cpuset *cs, struct cpumask *xcpus, */ cpuset_update_tasks_cpumask(&top_cpuset, tmp->new_cpus); update_sibling_cpumasks(&top_cpuset, NULL, tmp); + cs->remote_partition_updating = false; return; invalidate: @@ -2237,16 +2239,19 @@ static void update_cpumasks_hier(struct cpuset *cs, struct tmpmasks *tmp, old_prs = new_prs = cp->partition_root_state; /* - * For child remote partition root (!= cs), we need to call - * remote_cpus_update() if effective_xcpus will be changed. - * Otherwise, we can skip the whole subtree. + * For a child remote partition with remote_partition_updating + * set, we can skip the whole subtree as the this function will + * be called eventually. We can also skip the subtree if the + * effective_xcpus isn't going to be changed. Otherwise, we + * will have to call remote_cpus_update(). * * remote_cpus_update() will reuse tmp->new_cpus only after * its value is being processed. */ if (remote && (cp != cs)) { compute_excpus(cp, tmp->new_cpus); - if (cpumask_equal(cp->effective_xcpus, tmp->new_cpus)) { + if (cp->remote_partition_updating || + cpumask_equal(cp->effective_xcpus, tmp->new_cpus)) { pos_css = css_rightmost_descendant(pos_css); continue; } diff --git a/tools/testing/selftests/cgroup/test_cpuset_prs.sh b/tools/testing/selftests/cgroup/test_cpuset_prs.sh index f09a0a78ead2..ea8e1ff3014c 100755 --- a/tools/testing/selftests/cgroup/test_cpuset_prs.sh +++ b/tools/testing/selftests/cgroup/test_cpuset_prs.sh @@ -347,6 +347,7 @@ TEST_MATRIX=( " C0-3 C1-3 C2-3 C4 X2-3 X2-3 X2-3:P2 P2:C4-5 0 A3:2-3|B1:4-5 A3:P2|B1:P2 2-5" " C4:X0-3 X1-3 X2-3 . . P2 . . 0 A1:4|A2:1-3|A3:1-3 A2:P2 1-3" " C4:X0-3 X1-3 X2-3 . . . P2 . 0 A1:4|A2:4|A3:2-3 A3:P2 2-3" + " CX1-6 C1-4:P1 . C4-5 . C1-3 . . 0 A1:4-6|A2:1-3|B1:4-5 A2:P1" # Nested remote/local partition tests " C0-3 C1-3 C2-3 C4-5 X2-3 X2-3:P1 P2 P1 0 A1:0-1|A2:|A3:2-3|B1:4-5 \