[PATCH-next v4 1/9] cgroup/cpuset: Fix a missing isolated_cpus update in update_cpumasks_hier()

From: Waiman Long

Date: Mon Oct 19 2026 - 14:10:58 EST


It is found that when a local isolated partition is being invalidated
because its parent partition is no longer valid, the isolated_cpus
cpumask may not be properly updated. Those isolated CPUs may or may
not needed to be removed from isolated_cpus depending on if there is
a local isolated partition ancestor that will now own those isolated
CPUs instead.

Fix that by calling a new ancestor_partition() helper to return the
closeset valid partition root ancestor. If that ancestor is PRS_ROOT,
the isolated CPUs will have to be removed from isolated_cpus. For
a PRS_ISOLATED ancestor, the isolated CPUs are checked against
the ancestor's ones to see if any of them should be removed as it is
possible that the invalidation of the parent may be caused by change in
the effective xcpus of that ancestor. If any of the isolated CPUs should
be removed, isolated_cpus_update() will be called under callback_lock.

An existing test case in test_cpuset_prs.sh is modifed and a new one
added to test that.

Fixes: 11e5f407b64a ("cgroup/cpuset: Keep track of CPUs in isolated partitions")
Signed-off-by: Waiman Long <longman@xxxxxxxxxx>
---
kernel/cgroup/cpuset.c | 57 +++++++++++++++++--
.../selftests/cgroup/test_cpuset_prs.sh | 6 +-
2 files changed, 55 insertions(+), 8 deletions(-)

diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 0ebba2d646c3..e032ee1efeb7 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -2137,6 +2137,25 @@ static void compute_partition_effective_cpumask(struct cpuset *cs,
rcu_read_unlock();
}

+/*
+ * ancestor_partition - Find and return the closest valid partition root ancestor
+ * @cs: the current cpuset
+ *
+ * Return: The closet partition root ancestor cpuset
+ */
+static struct cpuset *ancestor_partition(struct cpuset *cs)
+{
+ struct cpuset *ancestor = parent_cs(cs);
+
+ /*
+ * The top_cpuset is always a valid partition root which will stop
+ * the loop if it is reached.
+ */
+ while (!is_partition_valid(ancestor))
+ ancestor = parent_cs(ancestor);
+ return ancestor;
+}
+
/*
* update_cpumasks_hier - Update effective cpumasks and tasks in the subtree
* @cs: the cpuset to consider
@@ -2229,7 +2248,10 @@ static void update_cpumasks_hier(struct cpuset *cs, struct tmpmasks *tmp,
* cpuset if the parent's effective_cpus changes.
*/
if ((cp != cs) && old_prs) {
- switch (parent->partition_root_state) {
+ int parent_prs = parent->partition_root_state;
+ struct cpumask *del_cpus = NULL;
+
+ switch (parent_prs) {
case PRS_ROOT:
case PRS_ISOLATED:
update_parent = true;
@@ -2237,12 +2259,37 @@ static void update_cpumasks_hier(struct cpuset *cs, struct tmpmasks *tmp,

default:
/*
- * When parent is not a partition root or is
- * invalid, child partition roots become
- * invalid too.
+ * When parent is not a valid partition root,
+ * child partition roots become invalid too.
+ *
+ * For a to be invalidated isolated partition,
+ * its isolated CPUs may or may not need to be
+ * removed from isolated_cpus depending on if
+ * its closest partition root ancestor is a root
+ * or isolated partition. With a PRS_ISOLATED
+ * ancestor, the CPUs are checked against its
+ * effective xcpus to see if any of them needed
+ * to be removed.
*/
if (is_partition_valid(cp))
- new_prs = -cp->partition_root_state;
+ new_prs = -old_prs;
+
+ if (old_prs == PRS_ISOLATED) {
+ struct cpuset *ancestor = ancestor_partition(cp);
+
+ if (ancestor->partition_root_state == PRS_ROOT)
+ del_cpus = cp->effective_xcpus;
+ else if (cpumask_andnot(tmp->delmask,
+ cp->effective_xcpus,
+ ancestor->effective_xcpus))
+ del_cpus = tmp->delmask;
+
+ if (del_cpus) {
+ spin_lock_irq(&callback_lock);
+ isolated_cpus_update(old_prs, new_prs, del_cpus);
+ spin_unlock_irq(&callback_lock);
+ }
+ }
WRITE_ONCE(cp->prs_err,
is_partition_invalid(parent)
? PERR_INVPARENT : PERR_NOTPART);
diff --git a/tools/testing/selftests/cgroup/test_cpuset_prs.sh b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
index 7efd5e645767..f1dda27b1394 100755
--- a/tools/testing/selftests/cgroup/test_cpuset_prs.sh
+++ b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
@@ -292,9 +292,9 @@ TEST_MATRIX=(
A1:P0|A2:P2|A3:P1 2-4|2-3"
" C0-4 C1-4 C2-4 . X2-4 X2-4:P2 X3-4:P1 . 0 A1:0-1|A2:2|A3:3-4 \
A1:P0|A2:P2|A3:P1 2"
- " C0-4:X2-4 C1-4:X2-4:P2 C2-4:X4:P1 \
- . . X5 . . 0 A1:0-4|A2:1-4|A3:2-4 \
- A1:P0|A2:P-2|A3:P-1 ."
+ " C1-4:X2-4 C2-4:X3-4:P1 C3-4:X4:P2 \
+ . . X5 . . 0 A1:1-4|A2:2-4|A3:3-4 \
+ A1:P0|A2:P-1|A3:P-2 ."
" C0-4:X2-4 C1-4:X2-4:P2 C2-4:X4:P1 \
. . . X1 . 0 A1:0-1|A2:2-4|A3:2-4 \
A1:P0|A2:P2|A3:P-1 2-4"
--
2.55.0