[PATCH-next v2 5/5] cgroup/cpuset: Correctly set isolated_cpus when switching partition state
From: Waiman Long
Date: Mon Nov 09 2026 - 14:12:32 EST
When update_prstate() is called to switch the partition state from
root to isolated or vice versa, the whole effective_xcpus is passed to
isolated_cpus_update(). However, if there are child partitions present,
some of the effective_xcpus are dedicated to those child partitions
and their partition states will remain the same or get invalidated in
the case of child root partition with isolated parent in the latter
update_cpumasks_heir() call. In the latter case, their exclusive CPUs
will be returned to their parent.
Therefore isolated_cpus may be set incorrectly with child partitions
underneath. Fix that by excluding their exclusive CPUs in computing
the correct cpumask of exclusive CPUs used by that partition alone.
Add new test cases into test_cpuset_prs.sh to test for such cases.
Fixes: 11e5f407b64a ("cgroup/cpuset: Keep track of CPUs in isolated partitions")
Signed-off-by: Waiman Long <longman@xxxxxxxxxx>
---
kernel/cgroup/cpuset.c | 30 +++++++++++++++----
.../selftests/cgroup/test_cpuset_prs.sh | 7 +++++
2 files changed, 32 insertions(+), 5 deletions(-)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 7aa614c6ac4d..41048997a955 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -3054,12 +3054,32 @@ static int update_prstate(struct cpuset *cs, int new_prs)
cs->effective_xcpus, NULL))
err = PERR_HKEEPING;
else if (parent_prs == PRS_ISOLATED)
- err = PERR_ISOPARENT;
+ err = PERR_ISOPARENT; /* old_prs should be isolated */
- if (!err)
- isolcpus_updated = true;
- else
+ if (err) {
disable_partition = true;
+ } else {
+ struct cgroup_subsys_state *css;
+ struct cpuset *child;
+
+ /*
+ * Need to scan child partitions, if present, and
+ * exclude their effective_xcpus from cs. In the case
+ * of switching to isolated partition with child root
+ * partitions, those child partitions will be
+ * invalidated in update_cpumasks_hier().
+ */
+ isolcpus_updated = true;
+ cpumask_copy(tmpmask.new_cpus, cs->effective_xcpus);
+ rcu_read_lock();
+ cpuset_for_each_child(child, css, cs) {
+ if (!is_partition_valid(child))
+ continue;
+ cpumask_andnot(tmpmask.new_cpus, tmpmask.new_cpus,
+ child->effective_xcpus);
+ }
+ rcu_read_unlock();
+ }
} else {
/*
* Switching back to member is always allowed even if it
@@ -3093,7 +3113,7 @@ static int update_prstate(struct cpuset *cs, int new_prs)
if (!is_partition_valid(cs))
reset_partition_data(cs);
else if (isolcpus_updated)
- isolated_cpus_update(old_prs, new_prs, cs->effective_xcpus);
+ isolated_cpus_update(old_prs, new_prs, tmpmask.new_cpus);
spin_unlock_irq(&callback_lock);
/* Force update if partition is disabled & update effective_xcpus */
diff --git a/tools/testing/selftests/cgroup/test_cpuset_prs.sh b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
index 2d4744f2d6ac..741a13bbeda7 100755
--- a/tools/testing/selftests/cgroup/test_cpuset_prs.sh
+++ b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
@@ -556,6 +556,13 @@ TEST_MATRIX=(
" C2-6:X2-5:P1 CX3-6:P2 \
. . T:X4-6 . . . 0 A1:4-6|A2:4-6 A1:P1|A2:P-2 ."
+ # Switch PRS state (root <=> isolated) will properly update cpuset.cpus.isolated
+ # even if child partitions are present.
+ " C1-6:P1 C3-6:P1 C5-6:P2 . . P2 . . 0 A1:1-2:A2:3-4:A3:5-6
+ A1:P1|A2:P2:A3:P2 3-6"
+ " C1-6:P1 C3-6:P2 C5-6:P2 . . P1 . . 0 A1:1-2:A2:3-4:A3:5-6
+ A1:P1|A2:P1:A3:P2 5-6"
+
#
# old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate ISOLCPUS
# ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------ --------
--
2.56.0