* [PATCH-next v3 0/9] cgroup/cpuset: Fix various housekeeping check problems
@ 2026-10-14 14:05 Waiman Long
2026-10-14 14:05 ` [PATCH-next v3 1/9] cgroup/cpuset: Fix a missing isolated_cpus update in update_cpumasks_hier() Waiman Long
` (8 more replies)
0 siblings, 9 replies; 10+ messages in thread
From: Waiman Long @ 2026-10-14 14:05 UTC (permalink / raw)
To: Ridong Chen, Tejun Heo, Johannes Weiner, Michal Koutný,
Shuah Khan, Jonathan Corbet, Randy Dunlap
Cc: cgroups, linux-doc, linux-kernel, linux-kselftest, Hui Peng,
Guopeng Zhang, Waiman Long
v3:
- Further revise the code based on feedback from sashiko and
reviewers.
- Add a new fix patch which fixes a bug found during the testing process.
- Add a patch to impose a new restriction that creating a child root
partition under an isolated parent partition is not allowed.
v2:
- Update patches 2 and 3 to address bugs reported by sashiko.
- Add a new test_cpuset_prs.sh patch to skip the test if some of the
required CPUs are offline.
Commit 4a74e418881f ("cgroup/cpuset: Check partition conflict with
housekeeping setup") and commit 103b08709e8a ("cgroup/cpuset: Fail if
isolated and nohz_full don't leave any housekeeping") add housekeeping
checks to fail partition changes that will violate the housekeeping
rules. However there are several issues that make the check incomplete and
missing in some cases. This series fixes the various known housekeeing
check issues as well as imposing a new restriction that a local root
partition cannot be created underneath an isolated partition. This
new restriction will eliminate a number of hard to tackle corner cases
which are unlikely to be used by the end users at all.
The first patch fixes a bug found in the testing process which is not
directly related to the goal of tackling the housekeeping check problem.
This series also updates test_cpuset_prs.sh by adding new test cases
to test the new code.
Waiman Long (9):
cgroup/cpuset: Fix a missing isolated_cpus update in
update_cpumasks_hier()
cgroup/cpuset: Consolidate isolated_cpus_can_update() into
prstate_housekeeping_conflict()
cgroup/cpuset: Consider all the exclusive CPUs when doing housekeeping
check
cgroup/cpuset: Do housekeeping check before converting invalid
partition to valid
cgroup/cpuset: Properly disabling partition when partition state
switching fails
cgroup/cpuset: Don't allow a child root partition under an isolated
parent
selftests/cgroup: Add tests for isolcpus housekeeping check
selftests/cgroup: Add tests for nohz_full housekeeping check
selftests/cgroup: Skip test_cpuset_prs.sh test if some required CPUs
are offline
Documentation/admin-guide/cgroup-v2.rst | 4 +-
kernel/cgroup/cpuset-internal.h | 1 +
kernel/cgroup/cpuset.c | 191 +++++++-------
.../selftests/cgroup/test_cpuset_prs.sh | 234 ++++++++++++++----
4 files changed, 294 insertions(+), 136 deletions(-)
--
2.55.0
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH-next v3 1/9] cgroup/cpuset: Fix a missing isolated_cpus update in update_cpumasks_hier()
2026-10-14 14:05 [PATCH-next v3 0/9] cgroup/cpuset: Fix various housekeeping check problems Waiman Long
@ 2026-10-14 14:05 ` Waiman Long
2026-10-14 14:05 ` [PATCH-next v3 2/9] cgroup/cpuset: Consolidate isolated_cpus_can_update() into prstate_housekeeping_conflict() Waiman Long
` (7 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Waiman Long @ 2026-10-14 14:05 UTC (permalink / raw)
To: Ridong Chen, Tejun Heo, Johannes Weiner, Michal Koutný,
Shuah Khan, Jonathan Corbet, Randy Dunlap
Cc: cgroups, linux-doc, linux-kernel, linux-kselftest, Hui Peng,
Guopeng Zhang, Waiman Long
It is found that when a local isolated partition is being invalidated
because its root partition parent is no longer valid, the isolated_cpus
cpumask may not be properly updated. If the parent was previously
an isolated partition, its invalidation should have properly updated
isolated_cpus before and no action will be needed.
Fix that by calling isolated_cpus_update() in this invalidation case
to update isolated_cpus. A test case in test_cpuset_prs.sh is modified
to test that.
Fixes: 11e5f407b64a ("cgroup/cpuset: Keep track of CPUs in isolated partitions")
Signed-off-by: Waiman Long <longman@redhat.com>
---
kernel/cgroup/cpuset.c | 19 ++++++++++++++-----
.../selftests/cgroup/test_cpuset_prs.sh | 4 ++--
2 files changed, 16 insertions(+), 7 deletions(-)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 0ebba2d646c3..6b5c870e5607 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -2229,7 +2229,9 @@ 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;
+
+ switch (parent_prs) {
case PRS_ROOT:
case PRS_ISOLATED:
update_parent = true;
@@ -2238,11 +2240,18 @@ 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.
+ * invalid, child partition roots become invalid
+ * too. For an isolated partition with root
+ * partition parent, isolated_cpus_updated()
+ * is called to update isolated_cpus.
*/
- if (is_partition_valid(cp))
- new_prs = -cp->partition_root_state;
+ if (is_partition_valid(cp)) {
+ new_prs = -old_prs;
+ if ((old_prs == PRS_ISOLATED) &&
+ (parent_prs == -PRS_ROOT))
+ isolated_cpus_update(old_prs, new_prs,
+ cp->effective_xcpus);
+ }
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..874a8a161236 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 \
+ " C0-4:X2-4 C1-4:X2-4:P1 C2-4:X4:P2 \
. . X5 . . 0 A1:0-4|A2:1-4|A3:2-4 \
- A1:P0|A2:P-2|A3:P-1 ."
+ 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
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH-next v3 2/9] cgroup/cpuset: Consolidate isolated_cpus_can_update() into prstate_housekeeping_conflict()
2026-10-14 14:05 [PATCH-next v3 0/9] cgroup/cpuset: Fix various housekeeping check problems Waiman Long
2026-10-14 14:05 ` [PATCH-next v3 1/9] cgroup/cpuset: Fix a missing isolated_cpus update in update_cpumasks_hier() Waiman Long
@ 2026-10-14 14:05 ` Waiman Long
2026-10-14 14:05 ` [PATCH-next v3 3/9] cgroup/cpuset: Consider all the exclusive CPUs when doing housekeeping check Waiman Long
` (6 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Waiman Long @ 2026-10-14 14:05 UTC (permalink / raw)
To: Ridong Chen, Tejun Heo, Johannes Weiner, Michal Koutný,
Shuah Khan, Jonathan Corbet, Randy Dunlap
Cc: cgroups, linux-doc, linux-kernel, linux-kselftest, Hui Peng,
Guopeng Zhang, Waiman Long
The isolated_cpus_can_update() and prstate_housekeeping_conflict() are
checking different aspects of upcoming cpumask changes that may conflict
with the current setting of the housekeeping cpumasks. There are places
where both are called together. There are also places where only one
of them is called. That inconsistency can contribute to missing check
where invalid cpumask changes may be allowed to move forward.
Fix that by consolidating isolated_cpus_can_update() into
prstate_housekeeping_conflict() and call prstate_housekeeping_conflict()
in all the places where either one of them or both are called. The
exception is the validate_partition() function where the
prstate_housekeeping_conflict() call is removed. It is because
validate_partition() is called only from partition_cpus_change()
where prstate_housekeeping_conflict() will be called from either
remote_cpus_update() or update_parent_effective_cpumask() with
partcmd_update if not for partition invalidatation or disablement.
Now prstate_housekeeping_conflict() will be called in the following
locations:
- when a partition is enabled in remote_partition_enable() or in
update_parent_effective_cpumask() with partcmd_enable*.
- when a cpumask is updated in remote_cpus_update() or in
update_parent_effective_cpumask() with partcmd_update.
- when a partition state changes from root to isolated or vice versa
in update_prstate().
Fixes: 4a74e418881f ("cgroup/cpuset: Check partition conflict with housekeeping setup")
Fixes: 103b08709e8a ("cgroup/cpuset: Fail if isolated and nohz_full don't leave any housekeeping")
Signed-off-by: Waiman Long <longman@redhat.com>
---
kernel/cgroup/cpuset.c | 146 +++++++++++++++++++++--------------------
1 file changed, 75 insertions(+), 71 deletions(-)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 6b5c870e5607..2b9aefc0edb8 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1353,65 +1353,85 @@ static void partition_xcpus_del(int old_prs, struct cpuset *parent,
}
/*
- * isolated_cpus_can_update - check for isolated & nohz_full conflicts
- * @add_cpus: cpu mask for cpus that are going to be isolated
- * @del_cpus: cpu mask for cpus that are no longer isolated, can be NULL
- * Return: false if there is conflict, true otherwise
- *
- * If nohz_full is enabled and we have isolated CPUs, their combination must
- * still leave housekeeping CPUs.
+ * prstate_housekeeping_conflict - check for partition & housekeeping conflicts
+ * @new_prs: new partition root state to be checked
+ * @parent_prs: Parent partition root state
+ * @add_cpus: additional CPUs to be added to current cpuset
+ * @del_cpus: CPUs to be removed from current cpuset, can be NULL
+ * Return: true if there is conflict, false otherwise
*
- * TBD: Should consider merging this function into
- * prstate_housekeeping_conflict().
+ * There are two different housekeeping conflicts to be checked:
+ * 1) If new_prs is PRS_ROOT, none of the @add_cpus can be a boot-time isolated
+ * CPU. IOW, the whole @add_cpus must be a subset of HK_TYPE_DOMAIN_BOOT.
+ * 2) If nohz_full is enabled and we have isolated CPUs, their combination must
+ * still leave housekeeping CPUs. This check is only needed if new_prs
+ * differs from parent_prs and one of them is PRS_ISOLATED.
*/
-static bool isolated_cpus_can_update(struct cpumask *add_cpus,
- struct cpumask *del_cpus)
+static bool prstate_housekeeping_conflict(int new_prs, int parent_prs,
+ struct cpumask *add_cpus,
+ struct cpumask *del_cpus)
{
cpumask_var_t full_hk_cpus;
- int res = true;
+ int res;
- if (!housekeeping_enabled(HK_TYPE_KERNEL_NOISE))
- return true;
+ WARN_ON_ONCE(parent_prs <= 0);
+ if (!new_prs)
+ return false;
+
+ /*
+ * For a currently invalid partition root state, do the check assuming
+ * that it may be converted back to valid.
+ *
+ * There are code paths where an invalid partition may be reverted back
+ * to a valid partition but we still need to run the housekeeping check
+ * before the conversion happens to make sure the switch is valid. We
+ * can either do the negative to positive PRS switch individually in all
+ * these code paths or do it here which I think will make the code a bit
+ * easier to read.
+ */
+ if (new_prs < 0)
+ new_prs = -new_prs;
- if (del_cpus && cpumask_weight_and(del_cpus,
- housekeeping_cpumask(HK_TYPE_KERNEL_NOISE)))
+ if (housekeeping_enabled(HK_TYPE_DOMAIN_BOOT) && (new_prs == PRS_ROOT) &&
+ !cpumask_subset(add_cpus, housekeeping_cpumask(HK_TYPE_DOMAIN_BOOT)))
return true;
- if (!alloc_cpumask_var(&full_hk_cpus, GFP_KERNEL))
+ /*
+ * Exit 2nd check early and return false if
+ * 1) there is no boot time isolated nohz_full CPU
+ * 2) new_prs is PRS_ROOT, parent_prs is PRS_ISOLATED and no CPU will
+ * be returned back to the parent.
+ */
+ if (!housekeeping_enabled(HK_TYPE_KERNEL_NOISE) ||
+ (!del_cpus && (new_prs == PRS_ROOT) && (parent_prs == PRS_ISOLATED)))
+ return false;
+
+ /*
+ * Now make sure that @add_cpus contains new CPUs to be isolated and
+ * @del_cpus contains isolated CPUs to be un-isolated.
+ * With the guards above, @add_cpus can't be NULL after the swap.
+ */
+ if (parent_prs == PRS_ISOLATED)
+ swap(add_cpus, del_cpus);
+
+ if (del_cpus &&
+ (cpumask_first_and_and(del_cpus, housekeeping_cpumask(HK_TYPE_KERNEL_NOISE),
+ cpu_active_mask) < nr_cpu_ids))
return false;
+ if (!alloc_cpumask_var(&full_hk_cpus, GFP_KERNEL))
+ return true;
+
cpumask_and(full_hk_cpus, housekeeping_cpumask(HK_TYPE_KERNEL_NOISE),
housekeeping_cpumask(HK_TYPE_DOMAIN));
cpumask_andnot(full_hk_cpus, full_hk_cpus, isolated_cpus);
cpumask_and(full_hk_cpus, full_hk_cpus, cpu_active_mask);
- if (!cpumask_weight_andnot(full_hk_cpus, add_cpus))
- res = false;
+ res = !cpumask_weight_andnot(full_hk_cpus, add_cpus);
free_cpumask_var(full_hk_cpus);
return res;
}
-/*
- * prstate_housekeeping_conflict - check for partition & housekeeping conflicts
- * @prstate: partition root state to be checked
- * @new_cpus: cpu mask
- * Return: true if there is conflict, false otherwise
- *
- * CPUs outside of HK_TYPE_DOMAIN_BOOT, if defined, can only be used in an
- * isolated partition.
- */
-static bool prstate_housekeeping_conflict(int prstate, struct cpumask *new_cpus)
-{
- if (!housekeeping_enabled(HK_TYPE_DOMAIN_BOOT))
- return false;
-
- if ((prstate != PRS_ISOLATED) &&
- !cpumask_subset(new_cpus, housekeeping_cpumask(HK_TYPE_DOMAIN_BOOT)))
- return true;
-
- return false;
-}
-
/*
* cpuset_update_sd_hk_unlock - Rebuild sched domains, update HK & unlock
*
@@ -1593,9 +1613,7 @@ static int remote_partition_enable(struct cpuset *cs, int new_prs,
return PERR_INVCPUS;
if (cpumask_intersects(tmp->new_cpus, subpartitions_cpus))
return PERR_NOCPUS;
- if (((new_prs == PRS_ISOLATED) &&
- !isolated_cpus_can_update(tmp->new_cpus, NULL)) ||
- prstate_housekeeping_conflict(new_prs, tmp->new_cpus))
+ if (prstate_housekeeping_conflict(new_prs, PRS_ROOT, tmp->new_cpus, NULL))
return PERR_HKEEPING;
spin_lock_irq(&callback_lock);
@@ -1697,8 +1715,8 @@ static void remote_cpus_update(struct cpuset *cs, struct cpumask *xcpus,
else if (cpumask_intersects(tmp->addmask, subpartitions_cpus) ||
cpumask_subset(top_cpuset.effective_cpus, tmp->addmask))
WRITE_ONCE(cs->prs_err, PERR_NOCPUS);
- else if ((prs == PRS_ISOLATED) &&
- !isolated_cpus_can_update(tmp->addmask, tmp->delmask))
+ else if (prstate_housekeeping_conflict(prs, PRS_ROOT,
+ tmp->addmask, tmp->delmask))
WRITE_ONCE(cs->prs_err, PERR_HKEEPING);
if (cs->prs_err)
goto invalidate;
@@ -1840,11 +1858,7 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd,
if (cpumask_empty(xcpus))
return PERR_INVCPUS;
- if (prstate_housekeeping_conflict(new_prs, xcpus))
- return PERR_HKEEPING;
-
- if ((new_prs == PRS_ISOLATED) && (new_prs != parent_prs) &&
- !isolated_cpus_can_update(xcpus, NULL))
+ if (prstate_housekeeping_conflict(new_prs, parent_prs, xcpus, NULL))
return PERR_HKEEPING;
if (tasks_nocpu_error(parent, cs, xcpus))
@@ -1917,20 +1931,12 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd,
}
/*
- * TBD: Invalidate a currently valid child root partition may
- * still break isolated_cpus_can_update() rule if parent is an
- * isolated partition.
+ * Check for housekeeping conflicts
*/
- if (is_partition_valid(cs) && (old_prs != parent_prs)) {
- if ((parent_prs == PRS_ROOT) &&
- /* Adding to parent means removing isolated CPUs */
- !isolated_cpus_can_update(tmp->delmask, tmp->addmask))
- part_error = PERR_HKEEPING;
- if ((parent_prs == PRS_ISOLATED) &&
- /* Adding to parent means adding isolated CPUs */
- !isolated_cpus_can_update(tmp->addmask, tmp->delmask))
- part_error = PERR_HKEEPING;
- }
+ if (is_partition_valid(cs) &&
+ prstate_housekeeping_conflict(old_prs, parent_prs,
+ tmp->delmask, tmp->addmask))
+ part_error = PERR_HKEEPING;
/*
* The new CPUs to be removed from parent's effective CPUs
@@ -2400,10 +2406,6 @@ static enum prs_errcode validate_partition(struct cpuset *cs, struct cpuset *tri
if (cpumask_empty(trialcs->effective_xcpus))
return PERR_INVCPUS;
- if (prstate_housekeeping_conflict(trialcs->partition_root_state,
- trialcs->effective_xcpus))
- return PERR_HKEEPING;
-
if (tasks_nocpu_error(parent, cs, trialcs->effective_xcpus))
return PERR_NOCPUS;
@@ -2420,7 +2422,7 @@ static enum prs_errcode validate_partition(struct cpuset *cs, struct cpuset *tri
* CPU modifications may cause a partition to be disabled or require state updates.
*/
static void partition_cpus_change(struct cpuset *cs, struct cpuset *trialcs,
- struct tmpmasks *tmp)
+ struct tmpmasks *tmp)
{
enum prs_errcode prs_err;
@@ -2920,13 +2922,15 @@ static int update_prstate(struct cpuset *cs, int new_prs)
err = remote_partition_enable(cs, new_prs, &tmpmask);
}
} else if (old_prs && new_prs) {
+ int parent_prs = is_remote_partition(cs)
+ ? PRS_ROOT : parent->partition_root_state;
+
/*
* A change in load balance state only, no change in cpumasks.
* Need to update isolated_cpus.
*/
- if (((new_prs == PRS_ISOLATED) &&
- !isolated_cpus_can_update(cs->effective_xcpus, NULL)) ||
- prstate_housekeeping_conflict(new_prs, cs->effective_xcpus))
+ if (prstate_housekeeping_conflict(new_prs, parent_prs,
+ cs->effective_xcpus, NULL))
err = PERR_HKEEPING;
else
isolcpus_updated = true;
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH-next v3 3/9] cgroup/cpuset: Consider all the exclusive CPUs when doing housekeeping check
2026-10-14 14:05 [PATCH-next v3 0/9] cgroup/cpuset: Fix various housekeeping check problems Waiman Long
2026-10-14 14:05 ` [PATCH-next v3 1/9] cgroup/cpuset: Fix a missing isolated_cpus update in update_cpumasks_hier() Waiman Long
2026-10-14 14:05 ` [PATCH-next v3 2/9] cgroup/cpuset: Consolidate isolated_cpus_can_update() into prstate_housekeeping_conflict() Waiman Long
@ 2026-10-14 14:05 ` Waiman Long
2026-10-14 14:05 ` [PATCH-next v3 4/9] cgroup/cpuset: Do housekeeping check before converting invalid partition to valid Waiman Long
` (5 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Waiman Long @ 2026-10-14 14:05 UTC (permalink / raw)
To: Ridong Chen, Tejun Heo, Johannes Weiner, Michal Koutný,
Shuah Khan, Jonathan Corbet, Randy Dunlap
Cc: cgroups, linux-doc, linux-kernel, linux-kselftest, Hui Peng,
Guopeng Zhang, Waiman Long
When prstate_housekeeping_conflict() is called to perform housekeeping
check with cpumask changes, the cpumask changes are passed in via addmask
and delmask. With this arrangement, the exclusive CPUs owned by the child
partitions and not affected by the cpumask change, if any, are excluded
in the analysis. If the current cpuset is an isolated partition with
root partition children, it is possible the cpumask change will still
leave active housekeeping CPUs but then no housekeeping CPUs will be
left when the child partitions are disabled or invalidated.
As a first step to protect against this possibility, all the exclusive
CPUs of the cpuset should be considered to be owned by the current cpuset
when doing housekeeping check. As a result, there is no need to pass in
separate add_mask and del_mask parameters. They are combined into a single
new_cpus parameter. The parent_prs parameter can be removed as well.
With this change, the prstate_housekeeping_conflict() call in
update_parent_effective_cpumask() with partcmd_update and newmask
is now duplicative and can be removed. The housekeeping check in
remote_cpus_update(), however, will still be useful as this function can
be called directly from update_cpumasks_hier() without going through
partition_cpus_change(). The update_parent_effective_cpumask() call
from update_cpumasks_hier() is partcmd_update with no cpumask which
still have the housekeeping check and so is covered.
Fixes: 103b08709e8a ("cgroup/cpuset: Fail if isolated and nohz_full don't leave any housekeeping")
Signed-off-by: Waiman Long <longman@redhat.com>
---
kernel/cgroup/cpuset.c | 77 ++++++++++++++++--------------------------
1 file changed, 30 insertions(+), 47 deletions(-)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 2b9aefc0edb8..b2684c98d8c0 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1355,26 +1355,20 @@ static void partition_xcpus_del(int old_prs, struct cpuset *parent,
/*
* prstate_housekeeping_conflict - check for partition & housekeeping conflicts
* @new_prs: new partition root state to be checked
- * @parent_prs: Parent partition root state
- * @add_cpus: additional CPUs to be added to current cpuset
- * @del_cpus: CPUs to be removed from current cpuset, can be NULL
+ * @new_cpus: cpu mask to be checked
* Return: true if there is conflict, false otherwise
*
* There are two different housekeeping conflicts to be checked:
- * 1) If new_prs is PRS_ROOT, none of the @add_cpus can be a boot-time isolated
- * CPU. IOW, the whole @add_cpus must be a subset of HK_TYPE_DOMAIN_BOOT.
+ * 1) If new_prs is PRS_ROOT, none of the @new_cpus can be a boot-time isolated
+ * CPU. IOW, the whole @new_cpus must be a subset of HK_TYPE_DOMAIN_BOOT.
* 2) If nohz_full is enabled and we have isolated CPUs, their combination must
- * still leave housekeeping CPUs. This check is only needed if new_prs
- * differs from parent_prs and one of them is PRS_ISOLATED.
+ * still leave housekeeping CPUs.
*/
-static bool prstate_housekeeping_conflict(int new_prs, int parent_prs,
- struct cpumask *add_cpus,
- struct cpumask *del_cpus)
+static bool prstate_housekeeping_conflict(int new_prs, struct cpumask *new_cpus)
{
cpumask_var_t full_hk_cpus;
int res;
- WARN_ON_ONCE(parent_prs <= 0);
if (!new_prs)
return false;
@@ -1393,30 +1387,18 @@ static bool prstate_housekeeping_conflict(int new_prs, int parent_prs,
new_prs = -new_prs;
if (housekeeping_enabled(HK_TYPE_DOMAIN_BOOT) && (new_prs == PRS_ROOT) &&
- !cpumask_subset(add_cpus, housekeeping_cpumask(HK_TYPE_DOMAIN_BOOT)))
+ !cpumask_subset(new_cpus, housekeeping_cpumask(HK_TYPE_DOMAIN_BOOT)))
return true;
/*
* Exit 2nd check early and return false if
* 1) there is no boot time isolated nohz_full CPU
- * 2) new_prs is PRS_ROOT, parent_prs is PRS_ISOLATED and no CPU will
- * be returned back to the parent.
+ * 2) @new_prs is not PRS_ISOLATED
+ * 3) @new_cpus is a subset of isolated_cpus
*/
if (!housekeeping_enabled(HK_TYPE_KERNEL_NOISE) ||
- (!del_cpus && (new_prs == PRS_ROOT) && (parent_prs == PRS_ISOLATED)))
- return false;
-
- /*
- * Now make sure that @add_cpus contains new CPUs to be isolated and
- * @del_cpus contains isolated CPUs to be un-isolated.
- * With the guards above, @add_cpus can't be NULL after the swap.
- */
- if (parent_prs == PRS_ISOLATED)
- swap(add_cpus, del_cpus);
-
- if (del_cpus &&
- (cpumask_first_and_and(del_cpus, housekeeping_cpumask(HK_TYPE_KERNEL_NOISE),
- cpu_active_mask) < nr_cpu_ids))
+ (new_prs != PRS_ISOLATED) ||
+ cpumask_subset(new_cpus, isolated_cpus))
return false;
if (!alloc_cpumask_var(&full_hk_cpus, GFP_KERNEL))
@@ -1426,7 +1408,7 @@ static bool prstate_housekeeping_conflict(int new_prs, int parent_prs,
housekeeping_cpumask(HK_TYPE_DOMAIN));
cpumask_andnot(full_hk_cpus, full_hk_cpus, isolated_cpus);
cpumask_and(full_hk_cpus, full_hk_cpus, cpu_active_mask);
- res = !cpumask_weight_andnot(full_hk_cpus, add_cpus);
+ res = !cpumask_weight_andnot(full_hk_cpus, new_cpus);
free_cpumask_var(full_hk_cpus);
return res;
@@ -1613,7 +1595,7 @@ static int remote_partition_enable(struct cpuset *cs, int new_prs,
return PERR_INVCPUS;
if (cpumask_intersects(tmp->new_cpus, subpartitions_cpus))
return PERR_NOCPUS;
- if (prstate_housekeeping_conflict(new_prs, PRS_ROOT, tmp->new_cpus, NULL))
+ if (prstate_housekeeping_conflict(new_prs, tmp->new_cpus))
return PERR_HKEEPING;
spin_lock_irq(&callback_lock);
@@ -1715,8 +1697,7 @@ static void remote_cpus_update(struct cpuset *cs, struct cpumask *xcpus,
else if (cpumask_intersects(tmp->addmask, subpartitions_cpus) ||
cpumask_subset(top_cpuset.effective_cpus, tmp->addmask))
WRITE_ONCE(cs->prs_err, PERR_NOCPUS);
- else if (prstate_housekeeping_conflict(prs, PRS_ROOT,
- tmp->addmask, tmp->delmask))
+ else if (prstate_housekeeping_conflict(prs, excpus))
WRITE_ONCE(cs->prs_err, PERR_HKEEPING);
if (cs->prs_err)
goto invalidate;
@@ -1796,7 +1777,6 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd,
int old_prs, new_prs;
int part_error = PERR_NONE; /* Partition error? */
struct cpumask *xcpus = user_xcpus(cs);
- int parent_prs = parent->partition_root_state;
bool nocpu;
lockdep_assert_cpuset_lock_held();
@@ -1858,7 +1838,7 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd,
if (cpumask_empty(xcpus))
return PERR_INVCPUS;
- if (prstate_housekeeping_conflict(new_prs, parent_prs, xcpus, NULL))
+ if (prstate_housekeeping_conflict(new_prs, xcpus))
return PERR_HKEEPING;
if (tasks_nocpu_error(parent, cs, xcpus))
@@ -1930,14 +1910,6 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd,
parent->effective_xcpus);
}
- /*
- * Check for housekeeping conflicts
- */
- if (is_partition_valid(cs) &&
- prstate_housekeeping_conflict(old_prs, parent_prs,
- tmp->delmask, tmp->addmask))
- part_error = PERR_HKEEPING;
-
/*
* The new CPUs to be removed from parent's effective CPUs
* must be present.
@@ -2430,6 +2402,21 @@ static void partition_cpus_change(struct cpuset *cs, struct cpuset *trialcs,
return;
prs_err = validate_partition(cs, trialcs);
+ if (!prs_err && !cpumask_empty(trialcs->effective_xcpus)) {
+ int parent_prs = is_remote_partition(cs)
+ ? PRS_ROOT : parent_cs(cs)->partition_root_state;
+ /*
+ * Check for housekeeping CPUs conflict assuming that all
+ * the exclusive CPUs belong to the current cpuset. We rely
+ * on the fact that effective_xcpus has been set up by
+ * compute_trial_excpus() which is called previously in
+ * update_cpumask() and update_exclusive_cpumask().
+ */
+ if (parent_prs > 0 &&
+ prstate_housekeeping_conflict(trialcs->partition_root_state,
+ trialcs->effective_xcpus))
+ prs_err = PERR_HKEEPING;
+ }
if (prs_err) {
WRITE_ONCE(cs->prs_err, prs_err);
trialcs->prs_err = prs_err;
@@ -2922,15 +2909,11 @@ static int update_prstate(struct cpuset *cs, int new_prs)
err = remote_partition_enable(cs, new_prs, &tmpmask);
}
} else if (old_prs && new_prs) {
- int parent_prs = is_remote_partition(cs)
- ? PRS_ROOT : parent->partition_root_state;
-
/*
* A change in load balance state only, no change in cpumasks.
* Need to update isolated_cpus.
*/
- if (prstate_housekeeping_conflict(new_prs, parent_prs,
- cs->effective_xcpus, NULL))
+ if (prstate_housekeeping_conflict(new_prs, cs->effective_xcpus))
err = PERR_HKEEPING;
else
isolcpus_updated = true;
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH-next v3 4/9] cgroup/cpuset: Do housekeeping check before converting invalid partition to valid
2026-10-14 14:05 [PATCH-next v3 0/9] cgroup/cpuset: Fix various housekeeping check problems Waiman Long
` (2 preceding siblings ...)
2026-10-14 14:05 ` [PATCH-next v3 3/9] cgroup/cpuset: Consider all the exclusive CPUs when doing housekeeping check Waiman Long
@ 2026-10-14 14:05 ` Waiman Long
2026-10-14 14:05 ` [PATCH-next v3 5/9] cgroup/cpuset: Properly disabling partition when partition state switching fails Waiman Long
` (4 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Waiman Long @ 2026-10-14 14:05 UTC (permalink / raw)
To: Ridong Chen, Tejun Heo, Johannes Weiner, Michal Koutný,
Shuah Khan, Jonathan Corbet, Randy Dunlap
Cc: cgroups, linux-doc, linux-kernel, linux-kselftest, Hui Peng,
Guopeng Zhang, Waiman Long
When a hotplug event happens or when update_cpumasks_hier() is called, an
invalid partition may be converted back to valid. However check against
housekeeping conflict isn't being done which can lead to a partition
state that violates the housekeeping rules. Update the code to allow
reviving an invalid partition only if it can pass the housekeeping check.
Fixes: 4a74e418881f ("cgroup/cpuset: Check partition conflict with housekeeping setup")
Signed-off-by: Waiman Long <longman@redhat.com>
---
kernel/cgroup/cpuset.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index b2684c98d8c0..51723c299ca5 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1959,9 +1959,14 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd,
bool exclusive = true;
/*
- * Convert invalid partition to valid has to
- * pass the cpu exclusivity test.
+ * Convert invalid partition to valid has to pass the
+ * cpu exclusivity test as well as the housekeeping
+ * check.
*/
+ if (prstate_housekeeping_conflict(cs->partition_root_state, xcpus)) {
+ part_error = PERR_HKEEPING;
+ goto write_error;
+ }
rcu_read_lock();
cpuset_for_each_child(child, css, parent) {
if (child == cs)
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH-next v3 5/9] cgroup/cpuset: Properly disabling partition when partition state switching fails
2026-10-14 14:05 [PATCH-next v3 0/9] cgroup/cpuset: Fix various housekeeping check problems Waiman Long
` (3 preceding siblings ...)
2026-10-14 14:05 ` [PATCH-next v3 4/9] cgroup/cpuset: Do housekeeping check before converting invalid partition to valid Waiman Long
@ 2026-10-14 14:05 ` Waiman Long
2026-10-14 14:05 ` [PATCH-next v3 6/9] cgroup/cpuset: Don't allow a child root partition under an isolated parent Waiman Long
` (3 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Waiman Long @ 2026-10-14 14:05 UTC (permalink / raw)
To: Ridong Chen, Tejun Heo, Johannes Weiner, Michal Koutný,
Shuah Khan, Jonathan Corbet, Randy Dunlap
Cc: cgroups, linux-doc, linux-kernel, linux-kselftest, Hui Peng,
Guopeng Zhang, Waiman Long
Before commit 103b08709e8a ("cgroup/cpuset: Fail if isolated and nohz_full
don't leave any housekeeping"), the partition state can be freely switched
from "root" to "isolated" and vice versa. After that commit, the switch
from "root" to "isolated" can fail if it exhausts all the housekeeping
CPUs. Later on, the switch from "isolated" to "root" can also fail if
some of the partition CPUs are boot-time isolated by "isolcpus".
A local or remote partition is made invalid when the switch fails.
However there are 2 problems with the existing invalidation code.
1) Both the subpartitions_cpus and isolated_cpus are not properly
updated.
2) In the case of remote partition with remote_partition flag set, it
is not cleared.
Fix this by calling the proper partition disabling function in this case.
On an x86 test system with boot option "isolcpus=10 cgroup_debug" set
and more than 16 cores, the following commands were executed after boot.
# cd /sys/fs/cgroup
# echo +cpuset > cgroup.subtree_control
# cat cpuset.cpus.isolated
10
# mkdir A
# echo 10-12 > A/cpuset.cpus
# echo isolated > A/cpuset.cpus.partition
# cat A/cpuset.cpus.partition
isolated
Before the patch:
# echo root > A/cpuset.cpus.partition
# cat A/cpuset.cpus.partition
root invalid (partition config conflicts with housekeeping setup)
# cat cpuset.cpus.isolated .__DEBUG__.cpuset.cpus.subpartitions
10-12
10-12
After the patch:
# echo root > A/cpuset.cpus.partition
# cat A/cpuset.cpus.partition
root invalid (partition config conflicts with housekeeping setup)
# cat cpuset.cpus.isolated .__DEBUG__.cpuset.cpus.subpartitions
10
Reported-by: Tejun Heo <tj@kernel.org>
Link: https://lore.kernel.org/lkml/7f4c57b26ad120ab30adf35653f1dd94@kernel.org/
Fixes: 4a74e418881f ("cgroup/cpuset: Check partition conflict with housekeeping setup")
Fixes: 103b08709e8a ("cgroup/cpuset: Fail if isolated and nohz_full don't leave any housekeeping")
Signed-off-by: Waiman Long <longman@redhat.com>
---
kernel/cgroup/cpuset.c | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 51723c299ca5..40c004386af0 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -2866,6 +2866,7 @@ static int update_prstate(struct cpuset *cs, int new_prs)
struct cpuset *parent = parent_cs(cs);
struct tmpmasks tmpmask;
bool isolcpus_updated = false;
+ bool disable_partition = false;
if (old_prs == new_prs)
return 0;
@@ -2918,15 +2919,21 @@ static int update_prstate(struct cpuset *cs, int new_prs)
* A change in load balance state only, no change in cpumasks.
* Need to update isolated_cpus.
*/
- if (prstate_housekeeping_conflict(new_prs, cs->effective_xcpus))
+ if (prstate_housekeeping_conflict(new_prs, cs->effective_xcpus)) {
+ disable_partition = true;
err = PERR_HKEEPING;
- else
+ } else {
isolcpus_updated = true;
+ }
} else {
/*
* Switching back to member is always allowed even if it
* disables child partitions.
*/
+ disable_partition = true;
+ }
+
+ if (disable_partition) {
if (is_remote_partition(cs))
remote_partition_disable(cs, &tmpmask);
else
@@ -2935,7 +2942,7 @@ static int update_prstate(struct cpuset *cs, int new_prs)
/*
* Invalidation of child partitions will be done in
- * update_cpumasks_hier().
+ * update_cpumasks_hier() below.
*/
}
out:
@@ -2954,8 +2961,8 @@ static int update_prstate(struct cpuset *cs, int new_prs)
isolated_cpus_update(old_prs, new_prs, cs->effective_xcpus);
spin_unlock_irq(&callback_lock);
- /* Force update if switching back to member & update effective_xcpus */
- update_cpumasks_hier(cs, &tmpmask, !new_prs);
+ /* Force update if partition is disabled & update effective_xcpus */
+ update_cpumasks_hier(cs, &tmpmask, disable_partition);
/* A newly created partition must have effective_xcpus set */
WARN_ON_ONCE(!old_prs && (new_prs > 0)
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH-next v3 6/9] cgroup/cpuset: Don't allow a child root partition under an isolated parent
2026-10-14 14:05 [PATCH-next v3 0/9] cgroup/cpuset: Fix various housekeeping check problems Waiman Long
` (4 preceding siblings ...)
2026-10-14 14:05 ` [PATCH-next v3 5/9] cgroup/cpuset: Properly disabling partition when partition state switching fails Waiman Long
@ 2026-10-14 14:05 ` Waiman Long
2026-10-14 14:05 ` [PATCH-next v3 7/9] selftests/cgroup: Add tests for isolcpus housekeeping check Waiman Long
` (2 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Waiman Long @ 2026-10-14 14:05 UTC (permalink / raw)
To: Ridong Chen, Tejun Heo, Johannes Weiner, Michal Koutný,
Shuah Khan, Jonathan Corbet, Randy Dunlap
Cc: cgroups, linux-doc, linux-kernel, linux-kselftest, Hui Peng,
Guopeng Zhang, Waiman Long
Although it is not typical for real users to create a complicated nested
partition hierarchy, it is currently allowed to create nested root and
isolated partitions. In addition, disabling or invalidating a partition
should always be allowed especially when the cpuset is being deleted.
With a nested root and isolated partition hierarchy, it is possible
that all active housekeeping CPUs can be exhausted when disabling or
invalidating a root partition and returning its CPUs to the isolated
parent.
One way to avoid this problem is to disallow the creation of a root
partition under an isolated parent. It is indeed a new limitation imposed
on the existing cpuset partition functionality. However an isolated
partition is usually a left node where scheduling and some other kernel
noises are disabled. It doesn't make much sense to create a child root
partition under an isolated parent and remove some CPUs from the set
of isolated CPUs again. Creation of a child isolated partition under a
root or isolated parent is still allowed.
The cgroup-v2 documentation is also updated to reflect this new
limitation. New test cases are added to test_cpuset_prs.sh to verify
this new behavior. Some existing test cases are also updated accordingly.
Signed-off-by: Waiman Long <longman@redhat.com>
---
Documentation/admin-guide/cgroup-v2.rst | 4 +++-
kernel/cgroup/cpuset-internal.h | 1 +
kernel/cgroup/cpuset.c | 13 +++++++++++++
tools/testing/selftests/cgroup/test_cpuset_prs.sh | 14 ++++++++------
4 files changed, 25 insertions(+), 7 deletions(-)
diff --git a/Documentation/admin-guide/cgroup-v2.rst b/Documentation/admin-guide/cgroup-v2.rst
index bd8c80f3bbc9..2c445829e24d 100644
--- a/Documentation/admin-guide/cgroup-v2.rst
+++ b/Documentation/admin-guide/cgroup-v2.rst
@@ -2767,7 +2767,9 @@ Cpuset Interface Files
be retained, but behaves more like a "member".
All possible state transitions among "member", "root" and
- "isolated" are allowed.
+ "isolated" are allowed with the exception that a local child
+ "root" partition is not allowed to be created under an "isolated"
+ parent partition.
On read, the "cpuset.cpus.partition" file can show the following
values.
diff --git a/kernel/cgroup/cpuset-internal.h b/kernel/cgroup/cpuset-internal.h
index 5da9c91295b0..2e44dc8634dc 100644
--- a/kernel/cgroup/cpuset-internal.h
+++ b/kernel/cgroup/cpuset-internal.h
@@ -35,6 +35,7 @@ enum prs_errcode {
PERR_HKEEPING,
PERR_ACCESS,
PERR_REMOTE,
+ PERR_ISOPARENT,
};
/* bits in struct cpuset flags field */
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 40c004386af0..681701ba46e1 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -60,6 +60,7 @@ static const char * const perr_strings[] = {
[PERR_HKEEPING] = "partition config conflicts with housekeeping setup",
[PERR_ACCESS] = "Enable partition not permitted",
[PERR_REMOTE] = "Have remote partition underneath",
+ [PERR_ISOPARENT] = "Root partition is not allowed with isolated parent",
};
/*
@@ -2865,6 +2866,7 @@ static int update_prstate(struct cpuset *cs, int new_prs)
int err = PERR_NONE, old_prs = cs->partition_root_state;
struct cpuset *parent = parent_cs(cs);
struct tmpmasks tmpmask;
+ int parent_prs = parent->partition_root_state;
bool isolcpus_updated = false;
bool disable_partition = false;
@@ -2910,6 +2912,14 @@ static int update_prstate(struct cpuset *cs, int new_prs)
enum partition_cmd cmd = (new_prs == PRS_ROOT)
? partcmd_enable : partcmd_enablei;
+ /*
+ * It is not allowed to create a root partition under
+ * an isolated parent partition.
+ */
+ if ((new_prs == PRS_ROOT) && (parent_prs == PRS_ISOLATED)) {
+ err = PERR_ISOPARENT;
+ goto out;
+ }
err = update_parent_effective_cpumask(cs, cmd, NULL, &tmpmask);
} else {
err = remote_partition_enable(cs, new_prs, &tmpmask);
@@ -2922,6 +2932,9 @@ static int update_prstate(struct cpuset *cs, int new_prs)
if (prstate_housekeeping_conflict(new_prs, cs->effective_xcpus)) {
disable_partition = true;
err = PERR_HKEEPING;
+ } else if (parent_prs == PRS_ISOLATED) {
+ disable_partition = true;
+ err = PERR_ISOPARENT;
} else {
isolcpus_updated = true;
}
diff --git a/tools/testing/selftests/cgroup/test_cpuset_prs.sh b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
index 874a8a161236..85a4f87840b4 100755
--- a/tools/testing/selftests/cgroup/test_cpuset_prs.sh
+++ b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
@@ -288,16 +288,12 @@ TEST_MATRIX=(
A1:P0|A2:P1|A3:P0|B1:P1"
" C0-3 C1-3 C3 C4 X2-3 X2-3:P1 P2 P1 0 A1:0-1|A2:2|A3:3|B1:4 \
A1:P0|A2:P1|A3:P2|B1:P1 2-4|3"
- " C0-4 C1-4 C2-4 . X2-4 X2-4:P2 X4:P1 . 0 A1:0-1|A2:2-3|A3:4 \
- 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:P1 C2-4:X4:P2 \
. . X5 . . 0 A1:0-4|A2:1-4|A3:2-4 \
A1:P0|A2:P-1|A3:P-2 ."
- " C0-4:X2-4 C1-4:X2-4:P2 C2-4:X4:P1 \
+ " C0-4:X2-4 C1-4:X2-4:P1 C2-4:X4:P2 \
. . . X1 . 0 A1:0-1|A2:2-4|A3:2-4 \
- A1:P0|A2:P2|A3:P-1 2-4"
+ A1:P0|A2:P1|A3:P-2 ."
" 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"
@@ -430,6 +426,12 @@ TEST_MATRIX=(
# cpuset.cpus can become empty with task in it as it inherits parent's effective CPUs
" C1-3 C2 . . . T:C . . 0 A1:1-3|A2:1-3"
+ # Creation of root partition under an isolated partition is not allowed
+ " C1-3:P2 C2 . . . P1 . . 0 A1:1-3|A2:2 A1:P2|A2:P-1 1-3"
+ " C1-3:P2 C2 . . . P2 . . 0 A1:1,3|A2:2 A1:P2|A2:P2 1-3"
+ " C1-3:P2 C2:P2 . . . P1 . . 0 A1:1-3|A2:2 A1:P2|A2:P-1 1-3"
+
+ #
# old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate ISOLCPUS
# ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------ --------
# Failure cases:
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH-next v3 7/9] selftests/cgroup: Add tests for isolcpus housekeeping check
2026-10-14 14:05 [PATCH-next v3 0/9] cgroup/cpuset: Fix various housekeeping check problems Waiman Long
` (5 preceding siblings ...)
2026-10-14 14:05 ` [PATCH-next v3 6/9] cgroup/cpuset: Don't allow a child root partition under an isolated parent Waiman Long
@ 2026-10-14 14:05 ` Waiman Long
2026-10-14 14:05 ` [PATCH-next v3 8/9] selftests/cgroup: Add tests for nohz_full " Waiman Long
2026-10-14 14:05 ` [PATCH-next v3 9/9] selftests/cgroup: Skip test_cpuset_prs.sh test if some required CPUs are offline Waiman Long
8 siblings, 0 replies; 10+ messages in thread
From: Waiman Long @ 2026-10-14 14:05 UTC (permalink / raw)
To: Ridong Chen, Tejun Heo, Johannes Weiner, Michal Koutný,
Shuah Khan, Jonathan Corbet, Randy Dunlap
Cc: cgroups, linux-doc, linux-kernel, linux-kselftest, Hui Peng,
Guopeng Zhang, Waiman Long
Whenever a partition is enabled or the cpumask of a valid partition
changes, a housekeeping check will be performed to ensure that the
change won't violate the imposed housekeeping constraints. One of the
constraints is that a boot-time isolated CPU will not be used to form
a non-isolated root partition. This requires the presence of boot-time
isolated CPUs when a test is run.
The test_cpuset_prs.sh test script is now updated to run a set a
special housekeeping check tests that will only be run if a suitable
boot-time isolated CPU is present. This will enable us to detect bugs
or regressions in the housekeeping checking code though a "isolcpus="
boot parameter with CPUs beyond CPU 8 must be present.
This patch also includes a minor fix to the REMOTE_TEST_MATRIX to avoid
test failure when the -v option is used.
Signed-off-by: Waiman Long <longman@redhat.com>
---
.../selftests/cgroup/test_cpuset_prs.sh | 120 ++++++++++++++----
1 file changed, 98 insertions(+), 22 deletions(-)
diff --git a/tools/testing/selftests/cgroup/test_cpuset_prs.sh b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
index 85a4f87840b4..5aac0df86593 100755
--- a/tools/testing/selftests/cgroup/test_cpuset_prs.sh
+++ b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
@@ -86,26 +86,37 @@ echo "" > test/cpuset.cpus
#
# If isolated CPUs have been reserved at boot time (as shown in
-# cpuset.cpus.isolated), these isolated CPUs should be outside of CPUs 0-8
-# that will be used by this script for testing purpose. If not, some of
-# the tests may fail incorrectly. Wait a bit and retry again just in case
-# these isolated CPUs are leftover from previous run and have just been
-# cleaned up earlier in this script.
+# /sys/devices/system/cpu/isolated), these isolated CPUs should be outside of
+# CPUs 0-8 that will be used by this script for testing purpose. If not, some
+# of the tests may fail incorrectly.
#
-# These pre-isolated CPUs should stay in an isolated state throughout the
+# The current set of isolated CPUs (as shown in cpuset.cpus.isolated) should
+# be the same as the boot value. If not, wait a bit and retry again just in
+# case these isolated CPUs are leftover from previous run and have just been
+# cleaned up earlier in this script. If they still don't match, we report an
+# warning and skip the test.
+#
+# These boot isolated CPUs should stay in an isolated state throughout the
# testing process for now.
#
-BOOT_ISOLCPUS=$(cat $CGROUP2/cpuset.cpus.isolated)
+BOOT_ISOLCPUS=$(cat /sys/devices/system/cpu/isolated)
+CURRENT_ISOLCPUS=$(cat $CGROUP2/cpuset.cpus.isolated)
+FIRST_ISOLCPUS=
+TEST_ISOLCPUS=
+
[[ -n "$BOOT_ISOLCPUS" ]] && {
+ FIRST_ISOLCPUS=$(echo $BOOT_ISOLCPUS | sed -e "s/[,-].*//")
+ echo "Boot isolated CPUs: $BOOT_ISOLCPUS"
+ [[ $FIRST_ISOLCPUS -le 8 ]] &&
+ skip_test "Boot isolated CPUs overlap CPUs to be tested"
+}
+
+[[ "$BOOT_ISOLCPUS" != "$CURRENT_ISOLCPUS" ]] && {
sleep 0.5
- BOOT_ISOLCPUS=$(cat $CGROUP2/cpuset.cpus.isolated)
+ CURRENT_ISOLCPUS=$(cat $CGROUP2/cpuset.cpus.isolated)
+ [[ "$BOOT_ISOLCPUS" != "$CURRENT_ISOLCPUS" ]] &&
+ skip_test "Current isolated CPUs ($CURRENT_ISOLCPUS) don't match boot isolated CPUs!"
}
-if [[ -n "$BOOT_ISOLCPUS" ]]
-then
- [[ $(echo $BOOT_ISOLCPUS | sed -e "s/[,-].*//") -le 8 ]] &&
- skip_test "Pre-isolated CPUs ($BOOT_ISOLCPUS) overlap CPUs to be tested"
- echo "Pre-isolated CPUs: $BOOT_ISOLCPUS"
-fi
cleanup()
{
@@ -202,7 +213,7 @@ test_add_proc()
#
# ECPUs - effective CPUs of cpusets
# Pstate - partition root state
-# ISOLCPUS - isolated CPUs (<icpus>[,<icpus2>])
+# ISOLCPUS - isolated CPUs (<icpus>[,<icpus2>] or .)
#
# Note that if there are 2 fields in ISOLCPUS, the first one is for
# sched-debug matching which includes offline CPUs and single-CPU partitions
@@ -446,6 +457,46 @@ TEST_MATRIX=(
" C0-3 . . C4-5 X3-5 . . . 1 A1:0-3|B1:4-5"
)
+# Test matrix with boot time isolated CPUs present for housekeeping check testing
+ISOLCPUS_TEST_MATRIX=(
+ # old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate ISOLCPUS
+ # ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------ --------
+ # A local isolated partition containing boot isolated CPU cannot be
+ # switched to root partition.
+ " C1-2,I:P2 . . . P1 . . . 0 A1:1-2,I A1:P-1 ."
+
+ # A remote isolated partition containing boot isolated CPU cannot be
+ # switched to root partition.
+ " X1-2,I CX1-2,I:P2 . . . P1 . . 0 A2:1-2,I A2:P-1 ."
+
+ # A local isolated partition with a boot isolated CPU distributed to a
+ # child isolated partition also cannot be switched to a root partition.
+ " C1-3,I:P2 C1,I:P2 . . P1 . . . 0 A1:1-3,I|A2:1,I A1:P-1|A2:P-2 ."
+
+ # A remote isolated partition with a boot isolated CPU distributed to a
+ # child isolated partition also cannot be switched to a root partition.
+ " X1-3,I C1-3,I:P2 C1,I:P2 . . P1 . . 0 A2:1-3,I|A3:1,I A2:P-1|A3:P-2 ."
+
+ # The cpumask of a local root partition cannot be changed to include a
+ # boot isolated CPU.
+ " C1-2:P1 . . . C2,I . . . 0 A1:2,I A1:P-1 ."
+
+ # The cpumask of a remote root partition cannot be changed to include a
+ # boot isolated CPU.
+ " CX1-2,I CX1-2:P1 . . . CX2,I . . 0 A1:1-2,I|A2:2,I A2:P-1 ."
+
+ # Passing an boot isolated CPU down to a child root partition will invalidate it
+ " C2-3,I:X2-3 CX2-3,I . . . P1 . . 0 A1:I|A2:2-3 A2:P1 ."
+ " C2-3,I:X2-3 CX2-3,I:P1 . . X2-3,I . . . 0 A1:2-3,I|A2:2-3,I A2:P-1 ."
+
+ # An invalidated root partition must stay invalid when cpus grows but
+ # effective xcpus still contains a boot isolated CPU.
+ " CX1,I:P1 . . . C1,2,I . . . 0 A1:1-2,I|XA1:1,I A1:P-1 ."
+
+ # Growing cpus.exclusive must not revive it either
+ " CX1-2,I:X1,I:P1 . . . X1,2,I . . . 0 A1:1-2,I|XA1:1-2,I A1:P-1 ."
+)
+
#
# Cpuset controller remote partition test matrix.
#
@@ -498,8 +549,8 @@ REMOTE_TEST_MATRIX=(
p1:P1|c11:P1|c12:P-1"
# Narrowing cpuset.cpus to previously sibling-excluded CPUs should
# not return CPUs that were never actually owned.
- " C1-4:P1 . C1-2:P1 C1-3:P2 . . \
- . . . C3 . . p1:4|c11:1-2|c12:3 \
+ " C1-5:P1 . C1-2:P1 C1-3:P2 . . \
+ . . . C3 . . p1:4-5|c11:1-2|c12:3 \
p1:P1|c11:P1|c12:P2 3"
# Expanding cpuset.cpus to include a previously sibling-excluded CPU
# after the sibling has become a member should correctly request it.
@@ -543,6 +594,27 @@ write_cpu_online()
pause 0.05
}
+#
+# Set the CPUS variable
+# $1 - the passed in cpu parameter
+#
+# The special "I" CPU if present is being replaced by the first boot CPU
+# and assigned to the "CPUS" variable when the TEST_ISOLCPUS flag is set.
+# If not, the program will exit with an error.
+#
+set_cpus()
+{
+ local cpus=$1
+ if [[ $(expr "$cpus" : ".*I") -gt 0 ]]
+ then
+ [[ -z "$TEST_ISOLCPUS" ]] &&
+ skip_test "Special 'I' CPU added to non-isolcpus testing matrix!"
+ CPUS=$(echo $cpus | sed -e "s/I/$FIRST_ISOLCPUS/")
+ else
+ CPUS=$cpus
+ fi
+}
+
#
# Set controller state
# $1 - cgroup directory
@@ -577,16 +649,16 @@ set_ctrl_state()
}
case $CMD in
X*)
- CPUS=${CMD#?}
+ set_cpus ${CMD#?}
COMM="echo $CPUS > $XFILE"
eval $COMM $REDIRECT
;;
CX*)
- CPUS=${CMD#??}
+ set_cpus ${CMD#??}
COMM="echo $CPUS > $CFILE; echo $CPUS > $XFILE"
eval $COMM $REDIRECT
;;
- C*) CPUS=${CMD#?}
+ C*) set_cpus ${CMD#?}
COMM="echo $CPUS > $CFILE"
eval $COMM $REDIRECT
;;
@@ -980,8 +1052,11 @@ run_state_test()
I=0
eval CNT="\${#$TEST[@]}"
+ # Skip isolcpus specific test matrix if FIRST_ISOLCPUS not defined
+ [[ -n "$TEST_ISOLCPUS" && -z "$FIRST_ISOLCPUS" ]] && return
+
reset_cgroup_states
- console_msg "Running state transition test ..."
+ console_msg "Running state transition test for $TEST ..."
while [[ $I -lt $CNT ]]
do
@@ -1000,7 +1075,7 @@ run_state_test()
NEW_A3=$7
NEW_B1=$8
RESULT=$9
- ECPUS=${10}
+ ECPUS=$(echo ${10} | sed -e "s/I/${FIRST_ISOLCPUS}/g")
STATES=${11}
ICPUS=${12}
@@ -1286,6 +1361,7 @@ test_inotify()
trap cleanup 0 2 3 6
run_state_test TEST_MATRIX
run_remote_state_test REMOTE_TEST_MATRIX
+TEST_ISOLCPUS=1 run_state_test ISOLCPUS_TEST_MATRIX
test_isolated
test_boot_isolated
test_inotify
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH-next v3 8/9] selftests/cgroup: Add tests for nohz_full housekeeping check
2026-10-14 14:05 [PATCH-next v3 0/9] cgroup/cpuset: Fix various housekeeping check problems Waiman Long
` (6 preceding siblings ...)
2026-10-14 14:05 ` [PATCH-next v3 7/9] selftests/cgroup: Add tests for isolcpus housekeeping check Waiman Long
@ 2026-10-14 14:05 ` Waiman Long
2026-10-14 14:05 ` [PATCH-next v3 9/9] selftests/cgroup: Skip test_cpuset_prs.sh test if some required CPUs are offline Waiman Long
8 siblings, 0 replies; 10+ messages in thread
From: Waiman Long @ 2026-10-14 14:05 UTC (permalink / raw)
To: Ridong Chen, Tejun Heo, Johannes Weiner, Michal Koutný,
Shuah Khan, Jonathan Corbet, Randy Dunlap
Cc: cgroups, linux-doc, linux-kernel, linux-kselftest, Hui Peng,
Guopeng Zhang, Waiman Long
When a boot time nohz_full CPUs are present and they cover CPUs 0-7,
some of the test cases may fail. So the test run will be skipped with
a warning message in this case. On the other hand, if the boot time
nohz_full CPUs cover all the CPUs except 0-7, a special set of nohz_full
specific housekeeping check related tests could be run to make sure the
housekeeping check are behaving properly. The test_cpuset_prs.sh test
script is now updated to do exactly that.
Signed-off-by: Waiman Long <longman@redhat.com>
---
.../selftests/cgroup/test_cpuset_prs.sh | 34 +++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/tools/testing/selftests/cgroup/test_cpuset_prs.sh b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
index 5aac0df86593..ea0ebbdf4bb3 100755
--- a/tools/testing/selftests/cgroup/test_cpuset_prs.sh
+++ b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
@@ -84,6 +84,29 @@ echo member > test/cpuset.cpus.partition
echo "" > test/cpuset.cpus
[[ $RESULT -eq 0 ]] && skip_test "Child cgroups are using cpuset!"
+#
+# If nohz_full CPUs have been reserved at boot time, they should not include
+# CPUs 0-7 as that may fail some of the test cases. On the other hand if the
+# set of nohz_full CPUs includes all the CPUs except 0-7, we can run a special
+# set of housekeeping CPUs exhaustion tests to see if the cpuset code will
+# reject them.
+#
+NOHZ_FULL_CPUS=$(cat /sys/devices/system/cpu/nohz_full)
+FIRST_NOHZ_FULL_CPU=
+LAST_NOHZ_FULL_CPU=
+RUN_NOHZ_FULL_TESTS=
+LAST_PRESENT_CPU=$(cat /sys/devices/system/cpu/present | sed -e "s/.*[,-]//")
+[[ -n "$NOHZ_FULL_CPUS" ]] && {
+ FIRST_NOHZ_FULL_CPU=$(echo $NOHZ_FULL_CPUS | sed -e "s/[,-].*//")
+ LAST_NOHZ_FULL_CPU=$(echo $NOHZ_FULL_CPUS | sed -e "s/.*[,-]//")
+ echo "Boot nohz_full CPUs: $NOHZ_FULL_CPUS"
+}
+[[ -n "$FIRST_NOHZ_FULL_CPU" && "$FIRST_NOHZ_FULL_CPU" -le 7 ]] &&
+ skip_test "Boot NOHZ_FULL CPUs overlap CPUs to be tested!"
+
+[[ -n "$FIRST_NOHZ_FULL_CPU" && "$FIRST_NOHZ_FULL_CPU" = 8 &&
+ "$LAST_NOHZ_FULL_CPU" = $LAST_PRESENT_CPU ]] && RUN_NOHZ_FULL_TESTS=1
+
#
# If isolated CPUs have been reserved at boot time (as shown in
# /sys/devices/system/cpu/isolated), these isolated CPUs should be outside of
@@ -497,6 +520,16 @@ ISOLCPUS_TEST_MATRIX=(
" CX1-2,I:X1,I:P1 . . . X1,2,I . . . 0 A1:1-2,I|XA1:1-2,I A1:P-1 ."
)
+# Test matrix to be run when boot time nohz_full CPUs cover all CPUs except CPUs 0-7
+NOHZ_FULL_TEST_MATRIX=(
+ # old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate ISOLCPUS
+ # ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------ --------
+ # Using all the nohz_full HK CPUs 0-7 in isolated partition is not allowed
+ " C0-7 . . . P2 . . . 0 A1:0-7 A1:P-2 ."
+ " C1-7:P2 C5-7:P2 . . . . . C0:P2 0 A1:1-4|A2:5-7|B1:0
+ A1:P2|A2:P2|B1:P-2 1-7"
+)
+
#
# Cpuset controller remote partition test matrix.
#
@@ -1362,6 +1395,7 @@ trap cleanup 0 2 3 6
run_state_test TEST_MATRIX
run_remote_state_test REMOTE_TEST_MATRIX
TEST_ISOLCPUS=1 run_state_test ISOLCPUS_TEST_MATRIX
+[[ -n "$RUN_NOHZ_FULL_TESTS" ]] && run_state_test NOHZ_FULL_TEST_MATRIX
test_isolated
test_boot_isolated
test_inotify
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH-next v3 9/9] selftests/cgroup: Skip test_cpuset_prs.sh test if some required CPUs are offline
2026-10-14 14:05 [PATCH-next v3 0/9] cgroup/cpuset: Fix various housekeeping check problems Waiman Long
` (7 preceding siblings ...)
2026-10-14 14:05 ` [PATCH-next v3 8/9] selftests/cgroup: Add tests for nohz_full " Waiman Long
@ 2026-10-14 14:05 ` Waiman Long
8 siblings, 0 replies; 10+ messages in thread
From: Waiman Long @ 2026-10-14 14:05 UTC (permalink / raw)
To: Ridong Chen, Tejun Heo, Johannes Weiner, Michal Koutný,
Shuah Khan, Jonathan Corbet, Randy Dunlap
Cc: cgroups, linux-doc, linux-kernel, linux-kselftest, Hui Peng,
Guopeng Zhang, Waiman Long
The test_cpuset_prs.sh test assumes that all the relevant CPUs being
tested are online. If some of them are offline, some of the test cases
may fail. So skip the test if the required CPUs are either offline or
not present.
While at it, the cleanup() and online_cpus() functions as well as the
trap statement to run cleanup() on exit are moved earlier to make sure
the cleanup function will be run if the test is somehow aborted early.
Signed-off-by: Waiman Long <longman@redhat.com>
---
.../selftests/cgroup/test_cpuset_prs.sh | 64 +++++++++++++------
1 file changed, 43 insertions(+), 21 deletions(-)
diff --git a/tools/testing/selftests/cgroup/test_cpuset_prs.sh b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
index ea0ebbdf4bb3..078475c9387a 100755
--- a/tools/testing/selftests/cgroup/test_cpuset_prs.sh
+++ b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
@@ -67,6 +67,30 @@ then
echo Y > /sys/kernel/debug/sched/verbose
fi
+cleanup()
+{
+ online_cpus
+ cd $CGROUP2
+ rmdir A1/A2/A3 A1/A2 A1 B1 test/A1 test/B1 test > /dev/null 2>&1
+ rmdir rtest/p1/c11 rtest/p1/c12 rtest/p2/c21 \
+ rtest/p2/c22 rtest/p1 rtest/p2 rtest > /dev/null 2>&1
+ [[ -n "$SCHED_DEBUG" ]] &&
+ echo "$SCHED_DEBUG" > /sys/kernel/debug/sched/verbose
+}
+
+online_cpus()
+{
+ [[ -n "$OFFLINE_CPUS" ]] && {
+ for C in $OFFLINE_CPUS
+ do
+ write_cpu_online ${C}=1
+ done
+ }
+}
+
+# Clean up the cgroupfs directory if the test is aborted.
+trap cleanup 0 2 3 6
+
cd $CGROUP2
echo +cpuset > cgroup.subtree_control
@@ -141,16 +165,25 @@ TEST_ISOLCPUS=
skip_test "Current isolated CPUs ($CURRENT_ISOLCPUS) don't match boot isolated CPUs!"
}
-cleanup()
-{
- online_cpus
- cd $CGROUP2
- rmdir A1/A2/A3 A1/A2 A1 B1 test/A1 test/B1 test > /dev/null 2>&1
- rmdir rtest/p1/c11 rtest/p1/c12 rtest/p2/c21 \
- rtest/p2/c22 rtest/p1 rtest/p2 rtest > /dev/null 2>&1
- [[ -n "$SCHED_DEBUG" ]] &&
- echo "$SCHED_DEBUG" > /sys/kernel/debug/sched/verbose
-}
+#
+# This test assumes that all the relevant CPUs being tested are online.
+# If some of them are offline, some of the tests may fail. So we are going to
+# skip running this test script if some of the required CPUs may be offline.
+# The required CPUs include CPUs 0-7 and the first boot time isolated CPU if
+# present. However the check below is not exhaustive and can be a false
+# positive, but users should not run this test if some CPUs are offline.
+#
+ONLINE_CPUS=$(cat /sys/devices/system/cpu/online | sed -e "s/,.*//")
+ONLINE_FIRST=$(echo $ONLINE_CPUS | sed -e "s/-.*//")
+ONLINE_LAST=$(echo $ONLINE_CPUS | sed -e "s/.*-//")
+if [[ -n "$FIRST_ISOLCPUS" ]]
+then
+ LAST_REQUIRED_CPU=$FIRST_ISOLCPUS
+else
+ LAST_REQUIRED_CPU=7
+fi
+[[ $ONLINE_FIRST != 0 || $ONLINE_LAST -lt $LAST_REQUIRED_CPU ]] &&
+ skip_test "Some of the required CPUs are offline or not present!"
# Pause in ms
pause()
@@ -748,16 +781,6 @@ set_ctrl_state_noerr()
}
}
-online_cpus()
-{
- [[ -n "$OFFLINE_CPUS" ]] && {
- for C in $OFFLINE_CPUS
- do
- write_cpu_online ${C}=1
- done
- }
-}
-
#
# Remove all the test cgroup directories
#
@@ -1391,7 +1414,6 @@ test_inotify()
echo "" > cpuset.cpus
}
-trap cleanup 0 2 3 6
run_state_test TEST_MATRIX
run_remote_state_test REMOTE_TEST_MATRIX
TEST_ISOLCPUS=1 run_state_test ISOLCPUS_TEST_MATRIX
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-10-14 14:06 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-14 14:05 [PATCH-next v3 0/9] cgroup/cpuset: Fix various housekeeping check problems Waiman Long
2026-10-14 14:05 ` [PATCH-next v3 1/9] cgroup/cpuset: Fix a missing isolated_cpus update in update_cpumasks_hier() Waiman Long
2026-10-14 14:05 ` [PATCH-next v3 2/9] cgroup/cpuset: Consolidate isolated_cpus_can_update() into prstate_housekeeping_conflict() Waiman Long
2026-10-14 14:05 ` [PATCH-next v3 3/9] cgroup/cpuset: Consider all the exclusive CPUs when doing housekeeping check Waiman Long
2026-10-14 14:05 ` [PATCH-next v3 4/9] cgroup/cpuset: Do housekeeping check before converting invalid partition to valid Waiman Long
2026-10-14 14:05 ` [PATCH-next v3 5/9] cgroup/cpuset: Properly disabling partition when partition state switching fails Waiman Long
2026-10-14 14:05 ` [PATCH-next v3 6/9] cgroup/cpuset: Don't allow a child root partition under an isolated parent Waiman Long
2026-10-14 14:05 ` [PATCH-next v3 7/9] selftests/cgroup: Add tests for isolcpus housekeeping check Waiman Long
2026-10-14 14:05 ` [PATCH-next v3 8/9] selftests/cgroup: Add tests for nohz_full " Waiman Long
2026-10-14 14:05 ` [PATCH-next v3 9/9] selftests/cgroup: Skip test_cpuset_prs.sh test if some required CPUs are offline Waiman Long
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox