[PATCH v3 1/3] cgroup/cpuset: return PERR_NOCPUS in remote_partition_enable()

From: Hui Peng

Date: Sun Sep 20 2026 - 14:15:00 EST


Commit 6da580ec656a ("cgroup/cpuset: Don't allow creation of local
partition over a remote one") updated update_prstate() to prevent creating
a top-level local partition whose exclusive CPUs conflict with an existing
remote partition.

However, if a top-level local partition A1 is created on CPUs 1-3, has a
non-partition member child A1/A2 with cpuset.cpus.exclusive = 1-3, and a
grandchild A1/A2/A3 is then enabled as a partition root on 1-3,
update_prstate() sees !is_partition_valid(parent) (since A1/A2 is a member)
and calls remote_partition_enable(). Because remote_partition_enable() does
not check whether tmp->new_cpus intersects subpartitions_cpus, A1/A2/A3 is
enabled as a remote partition on CPUs already owned by A1, triggering:

WARNING: kernel/cgroup/cpuset.c:1594 at update_prstate+0xbef/0xd70

Return PERR_NOCPUS in remote_partition_enable() when tmp->new_cpus
intersects subpartitions_cpus, matching the error code used by
remote_cpus_update() for subpartitions_cpus conflicts, and add a
corresponding regression test case to
tools/testing/selftests/cgroup/test_cpuset_prs.sh.

Tested in QEMU on Linux 7.3.0-rc3 using
tools/testing/selftests/cgroup/test_cpuset_prs.sh.

Fixes: 6da580ec656a ("cgroup/cpuset: Don't allow creation of local partition over a remote one")
Suggested-by: Guopeng Zhang <guopeng.zhang@xxxxxxxxx>
Cc: stable@xxxxxxxxxxxxxxx
Assisted-by: LLM
Signed-off-by: Hui Peng <benquike@xxxxxxxxx>
---
Changes in v3:
- Split the series into 3 patches (one per logical change and Fixes: tag).

kernel/cgroup/cpuset.c | 4 +++-
tools/testing/selftests/cgroup/test_cpuset_prs.sh | 2 ++
2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index c8ab89cfbe3d..7381fa8502e7 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1678,8 +1678,10 @@ static int remote_partition_enable(struct cpuset *cs, int new_prs,
* Callers must have validated that effective_xcpus is not empty.
*/
spin_lock_irq(&callback_lock);
- if (cpumask_subset(top_cpuset.effective_cpus, tmp->new_cpus)) {
+ if (cpumask_intersects(tmp->new_cpus, subpartitions_cpus) ||
+ cpumask_subset(top_cpuset.effective_cpus, tmp->new_cpus)) {
spin_unlock_irq(&callback_lock);
+ if (cpumask_intersects(tmp->new_cpus, subpartitions_cpus))
+ return PERR_NOCPUS;
return PERR_HKEEPING;
}

diff --git a/tools/testing/selftests/cgroup/test_cpuset_prs.sh b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
index a1f42ef64bc1..b732078bf319 100755
--- a/tools/testing/selftests/cgroup/test_cpuset_prs.sh
+++ b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
@@ -298,6 +298,8 @@ TEST_MATRIX=(
" 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"
+ " CX1-3:P1 CX1-3 CX1-3 . . . P1 . 0 A1:1-3|A2:1-3|A3:1-3 \
+ A1:P1|A2:P0|A3:P-1"

# Remote partition offline tests
" C0-3 C1-3 C2-3 . X2-3 X2-3 X2-3:P2:O2=0 . 0 A1:0-1|A2:1|A3:3 A1:P0|A3:P2 2-3"
--
2.49.0