Re: [PATCH-next 3/6] cgroup/cpuset: Fix incorrect effective_cpus bug in update_cpumasks_hier()

From: Waiman Long

Date: Fri Oct 23 2026 - 13:02:12 EST


On 10/23/26 3:07 AM, Ridong Chen wrote:


On 10/22/2026 11:16 AM, Waiman Long wrote:
With the introduction of cpuset.cpus.exclusive, the computation of
effective_cpus differs depending on if a cpuset is a valid partition
root or not.

In update_cpumasks_hier(), effective_cpus is computed early
to check if the current subtree can be skipped. Later on
update_parent_effective_cpumask() may be called which can change the
partition state from valid to invalid or vice versa. If the partition
state changes, the previously computed effective_cpus may no longer be
valid. Fix that by recomputing effective_cpus again when that happens.

Also add a new test case into test_cpuset_prs.sh to verify this change.

Fixes: e2ffe502ba45 ("cgroup/cpuset: Add cpuset.cpus.exclusive for v2")
Signed-off-by: Waiman Long <longman@xxxxxxxxxx>
---
  kernel/cgroup/cpuset.c                            | 10 ++++++++--
  tools/testing/selftests/cgroup/test_cpuset_prs.sh |  3 +++
  2 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index db6e4663f4be..7b9062326f35 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -2316,10 +2316,16 @@ static void update_cpumasks_hier(struct cpuset *cs, struct tmpmasks *tmp,
          if (update_parent) {
              update_parent_effective_cpumask(cp, partcmd_update, NULL, tmp);
              /*
-             * The cpuset partition_root_state may become
-             * invalid. Capture it.
+             * Capture current partition_root_state as it may have
+             * changed. Recompute effective cpumask if changed.
               */
              new_prs = cp->partition_root_state;
+            if (new_prs != old_prs) {
+                if (new_prs > 0)
+                    compute_partition_effective_cpumask(cp, tmp->new_cpus);
+                else
+                    compute_effective_cpumask(tmp->new_cpus, cp, parent);
+            }
          }

If cs is a partition (remote or local), we call compute_partition_effective_cpumask to compute the effective cpumask; otherwise we should call compute_effective_cpumask. Wouldn't it be better to call compute_partition_effective_cpumask whenever cs is a valid partition, so we can eliminate the branch outside?

```
static void compute_effective_cpumask(struct cpumask *new_cpus,
                      struct cpuset *cs, struct cpuset *parent)
{
    bool has_cpus;

    if (is_remote_partition (cs) ||
        (is_partition_valid(cs) && is_partition_valid(parent)))
        return compute_partition_effective_cpumask(cs, &new_cpus);

    has_cpus = cpumask_and(new_cpus, cs->cpus_allowed, parent->effective_cpus);
    if (!has_cpus && is_in_v2_mode())
        cpumask_copy(new_cpus, parent->effective_cpus);
}
```

Good point. I will send a separate patch to do the consolidation.

Cheers,
Longman

spin_lock_irq(&callback_lock);
diff --git a/tools/testing/selftests/cgroup/test_cpuset_prs.sh b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
index 47791591002f..8595b6b4b7bf 100755
--- a/tools/testing/selftests/cgroup/test_cpuset_prs.sh
+++ b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
@@ -364,6 +364,9 @@ TEST_MATRIX=(
      " 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 ."
+    " C1-5:X3-5:P2 C3-5:X4-5:P2 C4-5:X5:P2 \
+                   .    X1-4     .       .      .    0 A1:1-3|A2:4|A3:4 \
+                                       A1:P2|A2:P2|A3:P-2 1-4"
      " C1-4:X2-4 C1-4:X3-4:P1 C3-4:X4:P2 \
                     .      .      .      X1      .    0 A1:1-2|A2:3-4|A3:3-4 \
                                         A1:P0|A2:P1|A3:P-2 ."