* [PATCH-next v3 00/11] cgroup/cpuset: Miscellaneous bug fixes & cleanup
@ 2026-10-25 21:44 Waiman Long
2026-10-25 21:44 ` [PATCH-next v3 01/11] cgroup/cpuset: Find furthest isolated ancestor for invalidated isolated partition Waiman Long
` (10 more replies)
0 siblings, 11 replies; 23+ messages in thread
From: Waiman Long @ 2026-10-25 21:44 UTC (permalink / raw)
To: Ridong Chen, Tejun Heo, Johannes Weiner, Michal Koutný, Shuah Khan
Cc: cgroups, linux-kernel, linux-kselftest, Guopeng Zhang, Waiman Long
v3:
- Update patch 1 to further encapsulate the code to release isolated
CPUs into a new isolated_cpus_release() helper to replace
furthest_isolated_ancestor(). Other than adding a remote partition
break, patch 1 should be functionally similar to the one in v2.
- Add more comment and some minor adjustment into patch 2 to make
the exclusivity rule apply only to invalid partition with empty
exclusive_cpus.
- Add a new fix patch "cgroup/cpuset: Release invalid isolated CPUs
with a shrunk root partition parent" to handle isolated CPUs release
in update_parent_effective_cpumask().
v2:
- https://lore.kernel.org/lkml/20261023200036.60560-1-longman@redhat.com
- Update patch 2 to resolve problem reported by sashiko and Guopeng.
- Add 2 more fix patches to resolve problem found by inspection and
sashiko.
- Add 2 more cleanup patches suggested by Ridong Chen.
This patch series incorporates 9 fix patches for problems reported
by sashiko, Guopenp Zhang and by inspection. The last 2 patches are
cleanup patches suggested by Ridong Chen.
Waiman Long (11):
cgroup/cpuset: Find furthest isolated ancestor for invalidated
isolated partition
cgroup/cpuset: Properly compute effective xcpus in
update_parent_effective_cpumask()
cgroup/cpuset: Fix incorrect effective_cpus bug in
update_cpumasks_hier()
cgroup/cpuset: Minor fixes to prstate_housekeeping_conflict()
selftests/cgroup: Check for a single range nohz_full CPUs before
running test matrix
cgroup/cpuset: Prevent recursive call of remote_cpus_update() via
update_cpumasks_hier()
cgroup/cpuset: Make sure compute_partition_effective_cpumask() is only
called for valid partition
cgroup/cpuset: Release invalid isolated CPUs in
compute_partition_effective_cpumask()
cgroup/cpuset: Release invalid isolated CPUs with a shrunk root
partition parent
cgroup/cpuset: Relocate compute_*effective_cpumask() and
compute_effective_nodemask()
cgroup/cpuset: Integrate compute_partition_effective_cpumask() into
compute_effective_cpumask()
kernel/cgroup/cpuset-internal.h | 5 +
kernel/cgroup/cpuset.c | 505 +++++++++---------
.../selftests/cgroup/test_cpuset_prs.sh | 33 +-
3 files changed, 298 insertions(+), 245 deletions(-)
--
2.56.0
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH-next v3 01/11] cgroup/cpuset: Find furthest isolated ancestor for invalidated isolated partition
2026-10-25 21:44 [PATCH-next v3 00/11] cgroup/cpuset: Miscellaneous bug fixes & cleanup Waiman Long
@ 2026-10-25 21:44 ` Waiman Long
2026-10-26 6:33 ` Guopeng Zhang
2026-10-25 21:44 ` [PATCH-next v3 02/11] cgroup/cpuset: Properly compute effective xcpus in update_parent_effective_cpumask() Waiman Long
` (9 subsequent siblings)
10 siblings, 1 reply; 23+ messages in thread
From: Waiman Long @ 2026-10-25 21:44 UTC (permalink / raw)
To: Ridong Chen, Tejun Heo, Johannes Weiner, Michal Koutný, Shuah Khan
Cc: cgroups, linux-kernel, linux-kselftest, Guopeng Zhang, Waiman Long
Commit 46b885416c27 ("cgroup/cpuset: Fix a missing isolated_cpus
update in update_cpumasks_hier()") searches for the closet partition
root ancestor to determine if the isolated CPUs in a to be invalidated
isolation partition should be removed from isolated_cpus or not.
As reported by sashiko, this method may not work when there is a deeply
nested isolated partition hierarchy. For instance, Consider a nested
hierarchy A0 -> A1 -> A2 -> A3 where all four are PRS_ISOLATED:
A0: effective_xcpus = 0-4
A1: effective_xcpus = 1-4
A2: effective_xcpus = 2-4
A3: effective_xcpus = 3-4
When A1 shrinks its exclusive_cpus from 1-4 to 1-2, A2 will update it
effective_xcpus to 2. A3 will find its closet partition root ancestor
A2 and decides that its effective_xcpus 3-4 should be removed from
isolated_cpus which is incorrect.
Address this corner case by adding a new isolated_cpus_release() helper
to replace the existing ancestor_partition() helper. This new helper
will search for the furthest isolated partition root ancestor to decide
if some or all of its isolated CPUs should be removed from isolated_cpus
and also perform the removal when appropriate.
The is_remote_partition() and is_local_partition() helpers are also
moved up so they can be used in earlier functions.
There are other pre-existing problems that will still prevent the
above test case to work correctly. These problems will be addressed in
subsequent patches.
Fixes: 46b885416c27 ("cgroup/cpuset: Fix a missing isolated_cpus update in update_cpumasks_hier()")
Signed-off-by: Waiman Long <longman@redhat.com>
---
kernel/cgroup/cpuset.c | 104 +++++++++++++++++++++--------------------
1 file changed, 53 insertions(+), 51 deletions(-)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 585a982249ad..bbd44782d4bf 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1257,6 +1257,16 @@ static void reset_partition_data(struct cpuset *cs)
cpumask_copy(cs->effective_cpus, parent->effective_cpus);
}
+static inline bool is_remote_partition(struct cpuset *cs)
+{
+ return cs->remote_partition;
+}
+
+static inline bool is_local_partition(struct cpuset *cs)
+{
+ return is_partition_valid(cs) && !is_remote_partition(cs);
+}
+
/* Return true if isolated_cpus changes. */
static bool isolated_cpu_update(int new_prs, int cpu)
{
@@ -1299,6 +1309,45 @@ static void isolated_cpus_update(int old_prs, int new_prs, struct cpumask *xcpus
update_housekeeping = true;
}
+/*
+ * isolated_cpus_release - Remove the relevant CPUs in @xcpus from isolated_cpus
+ * @cs: the current cpuset
+ * @xcpus: the set of effective xcpus to be considered for removal
+ *
+ * This function is called when an isolated partition is invalidated or when
+ * some of its effective xcpus are to be removed from isolated_cpus due to
+ * changes in parent cpuset. The given @xcpus must be modifiable as the function
+ * will update its value to determine the right set of CPUs to be removed.
+ */
+static void isolated_cpus_release(struct cpuset *cs, struct cpumask *xcpus)
+{
+ struct cpuset *ancestor = parent_cs(cs);
+ struct cpuset *iancestor = NULL;
+
+ /*
+ * Search the furthest isolated partition root ancestor available
+ * until top_cpuset or a remote partition root is hit.
+ */
+ for (; ancestor; ancestor = parent_cs(ancestor)) {
+ if (ancestor->partition_root_state == PRS_ISOLATED)
+ iancestor = ancestor;
+ if (is_remote_partition(ancestor))
+ break;
+ }
+
+ /*
+ * If a furthest isolated partition acnestor is found, use it to
+ * determine which of the CPUs in @xcpus should be removed from
+ * isolated_cpus. Otherwise, all of them will be removed.
+ */
+ if (iancestor && !cpumask_andnot(xcpus, xcpus, iancestor->effective_xcpus))
+ return; /* No CPU should be removed from isolated_cpus */
+
+ spin_lock_irq(&callback_lock);
+ isolated_cpus_update(PRS_ISOLATED, PRS_ROOT, xcpus);
+ spin_unlock_irq(&callback_lock);
+}
+
/*
* partition_xcpus_add - Add new exclusive CPUs to partition
* @new_prs: new partition_root_state
@@ -1571,16 +1620,6 @@ static int compute_trialcs_excpus(struct cpuset *trialcs, struct cpuset *cs)
return rm_siblings_excl_cpus(parent, cs, excpus);
}
-static inline bool is_remote_partition(struct cpuset *cs)
-{
- return cs->remote_partition;
-}
-
-static inline bool is_local_partition(struct cpuset *cs)
-{
- return is_partition_valid(cs) && !is_remote_partition(cs);
-}
-
/*
* remote_partition_enable - Enable current cpuset as a remote partition root
* @cs: the cpuset to update
@@ -2155,25 +2194,6 @@ 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
@@ -2267,7 +2287,6 @@ static void update_cpumasks_hier(struct cpuset *cs, struct tmpmasks *tmp,
*/
if ((cp != cs) && old_prs) {
int parent_prs = parent->partition_root_state;
- struct cpumask *del_cpus = NULL;
switch (parent_prs) {
case PRS_ROOT:
@@ -2281,32 +2300,15 @@ static void update_cpumasks_hier(struct cpuset *cs, struct tmpmasks *tmp,
* 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.
+ * call return_isolated_cpus() to remove the
+ * relevant CPUs from isolated_cpus.
*/
if (is_partition_valid(cp))
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);
- }
+ cpumask_copy(tmp->delmask, cp->effective_xcpus);
+ isolated_cpus_release(cp, tmp->delmask);
}
WRITE_ONCE(cp->prs_err,
is_partition_invalid(parent)
--
2.56.0
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH-next v3 02/11] cgroup/cpuset: Properly compute effective xcpus in update_parent_effective_cpumask()
2026-10-25 21:44 [PATCH-next v3 00/11] cgroup/cpuset: Miscellaneous bug fixes & cleanup Waiman Long
2026-10-25 21:44 ` [PATCH-next v3 01/11] cgroup/cpuset: Find furthest isolated ancestor for invalidated isolated partition Waiman Long
@ 2026-10-25 21:44 ` Waiman Long
2026-10-26 6:39 ` Guopeng Zhang
2026-10-25 21:44 ` [PATCH-next v3 03/11] cgroup/cpuset: Fix incorrect effective_cpus bug in update_cpumasks_hier() Waiman Long
` (8 subsequent siblings)
10 siblings, 1 reply; 23+ messages in thread
From: Waiman Long @ 2026-10-25 21:44 UTC (permalink / raw)
To: Ridong Chen, Tejun Heo, Johannes Weiner, Michal Koutný, Shuah Khan
Cc: cgroups, linux-kernel, linux-kselftest, Guopeng Zhang, Waiman Long
Since the introduction of cpuset.cpus.exclusive in commit
e2ffe502ba45 ("cgroup/cpuset: Add cpuset.cpus.exclusive for v2"), a
compute_effective_exclusive_cpumask() helper which is later renamed to
compute_excpus() is added to compute the effective xcpus of a partition
root. However update_parent_effective_cpumask() is not updated in all
proper places to use the new helper to check if a given cpuset should
remain a valid partition root. This can cause a cpuset to incorrectly
remain a valid partition root even if no effective xcpus is left.
Fix that problem by using compute_excpus() to compute the effective xcpus
into delmask in all cases except partcmd_disable, partcmd_invalidate
or partcmd_update with newmask set and use it for tasks_nocpu_error()
check as well as checking its emptiness. This enables us to catch invalid
partition root more accurately in update_parent_effective_cpumask().
In the case of partcmd_update with newmask set, the passed in new
effective xcpus value will directly be used in the tasks_nocpu_error()
check without relying on old data in the cpuset.
For the partcmd_update with NULL newmask case, the cpu exclusivity test
there is also relaxed to be performed only if exclusive_cpus is empty.
Some test case are added to test_cpuset_prs.sh for invalidating a
partition root due to its effective xcpus becoming empty as well as
making sure that an invalid partition can only be revived when exclusive
CPUs are available agin.
Fixes: e2ffe502ba45 ("cgroup/cpuset: Add cpuset.cpus.exclusive for v2")
Signed-off-by: Waiman Long <longman@redhat.com>
---
kernel/cgroup/cpuset.c | 87 ++++++++++---------
.../selftests/cgroup/test_cpuset_prs.sh | 11 +++
2 files changed, 56 insertions(+), 42 deletions(-)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index bbd44782d4bf..b850aba4d907 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1795,7 +1795,7 @@ static void remote_cpus_update(struct cpuset *cs, struct cpumask *xcpus,
* update_parent_effective_cpumask - update effective_cpus mask of parent cpuset
* @cs: The cpuset that requests change in partition root state
* @cmd: Partition root state change command
- * @newmask: Optional new cpumask for partcmd_update
+ * @newmask: Optional new effective xcpus for partcmd_update
* @tmp: Temporary addmask and delmask
* Return: 0 or a partition root state error code
*
@@ -1810,11 +1810,11 @@ static void remote_cpus_update(struct cpuset *cs, struct cpumask *xcpus,
* given back to parent's effective_cpus. 0 will always be returned.
*
* For partcmd_update, if the optional newmask is specified, the cpu list is
- * to be changed from effective_xcpus to newmask. Otherwise, effective_xcpus is
- * assumed to remain the same. The cpuset should either be a valid or invalid
- * partition root. The partition root state may change from valid to invalid
- * or vice versa. An error code will be returned if transitioning from
- * invalid to valid violates the exclusivity rule.
+ * to be changed from effective_xcpus to newmask. Otherwise, effective_xcpus
+ * will be computed and a valid partition will be invalidated if it is the new
+ * cpumask is empty. The cpuset should either be a valid or invalid partition
+ * root. The partition root state may change from valid to invalid or vice
+ * versa.
*
* For partcmd_invalidate, the current partition will be made invalid.
*
@@ -1837,7 +1837,6 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd,
int deleting; /* Deleting cpus from parent's effective_cpus */
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;
@@ -1880,30 +1879,30 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd,
if (!newmask && xcpus_empty(cs))
return PERR_CPUSEMPTY;
- nocpu = tasks_nocpu_error(parent, cs, xcpus);
+ /*
+ * Except for partcmd_disable and non-NULL newmask, use delmask
+ * to compute the current effective xcpus value and use it for
+ * tasks_nocpu_error check.
+ */
+ if ((cmd != partcmd_disable) && !newmask) {
+ compute_excpus(cs, tmp->delmask);
+ nocpu = tasks_nocpu_error(parent, cs, tmp->delmask);
+ }
if ((cmd == partcmd_enable) || (cmd == partcmd_enablei)) {
- /*
- * Need to call compute_excpus() in case
- * exclusive_cpus not set. Sibling conflict should only happen
- * if exclusive_cpus isn't set.
- */
- xcpus = tmp->delmask;
- if (compute_excpus(cs, xcpus))
- WARN_ON_ONCE(!cpumask_empty(cs->exclusive_cpus));
new_prs = (cmd == partcmd_enable) ? PRS_ROOT : PRS_ISOLATED;
/*
* Enabling partition root is not allowed if its
* effective_xcpus is empty.
*/
- if (cpumask_empty(xcpus))
+ if (cpumask_empty(tmp->delmask))
return PERR_INVCPUS;
- if (prstate_housekeeping_conflict(new_prs, parent_prs, xcpus, NULL))
+ if (prstate_housekeeping_conflict(new_prs, parent_prs, tmp->delmask, NULL))
return PERR_HKEEPING;
- if (tasks_nocpu_error(parent, cs, xcpus))
+ if (nocpu)
return PERR_NOCPUS;
/*
@@ -1915,16 +1914,14 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd,
*
* Warn if it is not the case.
*/
- cpumask_and(tmp->new_cpus, xcpus, cpu_active_mask);
+ cpumask_and(tmp->new_cpus, tmp->delmask, cpu_active_mask);
WARN_ON_ONCE(!cpumask_subset(tmp->new_cpus, parent->effective_cpus));
-
deleting = true;
} else if (cmd == partcmd_disable) {
/*
* May need to add cpus back to parent's effective_cpus
* (and maybe removed from subpartitions_cpus/isolated_cpus)
- * for valid partition root. xcpus may contain CPUs that
- * shouldn't be removed from the two global cpumasks.
+ * for valid partition root.
*/
if (is_partition_valid(cs)) {
cpumask_copy(tmp->addmask, cs->effective_xcpus);
@@ -1944,8 +1941,8 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd,
goto write_error;
}
- /* Check newmask again, whether cpus are available for parent/cs */
- nocpu |= tasks_nocpu_error(parent, cs, newmask);
+ /* Check newmask whether cpus are available for parent/cs */
+ nocpu = tasks_nocpu_error(parent, cs, newmask);
/*
* partcmd_update with newmask:
@@ -2000,7 +1997,7 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd,
/*
* partcmd_update w/o newmask
*
- * delmask = effective_xcpus & parent->effective_cpus
+ * delmask = newly computed effective xcpus value
*
* This can be called from:
* 1) update_cpumasks_hier()
@@ -2012,7 +2009,7 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd,
* A partition error happens when parent has tasks and all
* its effective CPUs will have to be distributed out.
*/
- if (nocpu) {
+ if (nocpu || cpumask_empty(tmp->delmask)) {
part_error = PERR_NOCPUS;
if (is_partition_valid(cs))
adding = cpumask_and(tmp->addmask,
@@ -2025,38 +2022,44 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd,
cpumask_copy(tmp->addmask, cs->effective_xcpus);
adding = true;
}
- } else if (is_partition_invalid(cs) && !cpumask_empty(xcpus) &&
- cpumask_subset(xcpus, parent->effective_xcpus)) {
+ } else if (is_partition_invalid(cs)) {
struct cgroup_subsys_state *css;
struct cpuset *child;
bool exclusive = true;
/*
* Convert invalid partition to valid has to pass the
- * cpu exclusivity test as well as the housekeeping
- * check.
+ * cpu exclusivity test when cpuset.cpus.exclusive is
+ * empty as well as the housekeeping check.
*/
if (prstate_housekeeping_conflict(cs->partition_root_state,
- parent_prs, xcpus, NULL)) {
+ parent_prs, tmp->delmask, NULL)) {
part_error = PERR_HKEEPING;
goto write_error;
}
- rcu_read_lock();
- cpuset_for_each_child(child, css, parent) {
- if (child == cs)
- continue;
- if (!cpusets_are_exclusive(cs, child)) {
- exclusive = false;
- break;
+ if (cpumask_empty(cs->exclusive_cpus)) {
+ rcu_read_lock();
+ cpuset_for_each_child(child, css, parent) {
+ if (child == cs)
+ continue;
+ if (!cpusets_are_exclusive(cs, child)) {
+ exclusive = false;
+ break;
+ }
}
+ rcu_read_unlock();
}
- rcu_read_unlock();
if (exclusive)
- deleting = cpumask_and(tmp->delmask,
- xcpus, parent->effective_cpus);
+ deleting = !cpumask_empty(tmp->delmask);
else
part_error = PERR_NOTEXCL;
}
+ /*
+ * When both cs and parent remain valid, parent's
+ * compute_partition_effective_cpumask() call would have removed
+ * cs's effective_xcpus from the parent's effective_cpus. So
+ * there is no need to adjust parent's effective_cpus here.
+ */
}
write_error:
diff --git a/tools/testing/selftests/cgroup/test_cpuset_prs.sh b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
index d04aad697b96..8bdda955b4a7 100755
--- a/tools/testing/selftests/cgroup/test_cpuset_prs.sh
+++ b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
@@ -359,6 +359,8 @@ TEST_MATRIX=(
A1:P0|A2:P1|A3:P2|B1:P1 2-4|3"
" C1-6:P1 C3-6:P1 C5-6:P1 . . P2 . . 0 A1:1-2|A2:3-6|A3:5-6 \
A1:P1|A2:P2|A3:P-1 3-6"
+ " C1-6:P2 C3-6:P2 C3-4:P2 . . C5-6 . . 0 A1:1-4|A2:5-6|A3:5-6 \
+ A1:P2|A2:P2|A3:P-2 1-6"
" 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 ."
@@ -427,6 +429,10 @@ TEST_MATRIX=(
# cpuset.cpus is allowed, but those extra CPUs are ignored.
" C2-3:P1 C3:P1 . . . C2-4 . . 0 A1:|A2:2-3 A1:P1|A2:P1"
+ # A child cannot recover until its exclusive CPUs are available again.
+ " CX1-4:P1 CX3-4:P1 . . X1-2 . . . 0 A1:1-2|A2:1-2|XA2: A1:P1|A2:P-1"
+ " CX1-4:P1 CX3-4:P1 . . X1-2:X1-4 . . . 0 A1:1-2|A2:3-4|XA2:3-4 A1:P1|A2:P1"
+
# Taking away all CPUs from parent or itself if there are tasks
# will make the partition invalid.
" C2-3:P1 C3:P1 . . T C2-3 . . 0 A1:2-3|A2:2-3 A1:P1|A2:P-1"
@@ -503,6 +509,11 @@ TEST_MATRIX=(
" C1-3:P2 C2:P2 . . . P1 . . 0 A1:1-3|A2:2 A1:P2|A2:P-1 1-3"
" C1-3:P2 C2:P1 . . . C2-3 . . 0 A1:1-3|A2:2-3 A1:P2|A2:P-1 1-3"
+ # A middle isolated parent shrunk below coverage: the released CPUs of
+ # an invalidated child stay isolated as a higher ancestor still covers them.
+ " CX0-4:P2 CX2-4:P2 CX3-4:P2 . . X2 . . 0 A1:0-1,3-4|A2:2|A3:2 \
+ A1:P2|A2:P2|A3:P-2 0-4"
+
#
# old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate ISOLCPUS
# ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------ --------
--
2.56.0
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH-next v3 03/11] cgroup/cpuset: Fix incorrect effective_cpus bug in update_cpumasks_hier()
2026-10-25 21:44 [PATCH-next v3 00/11] cgroup/cpuset: Miscellaneous bug fixes & cleanup Waiman Long
2026-10-25 21:44 ` [PATCH-next v3 01/11] cgroup/cpuset: Find furthest isolated ancestor for invalidated isolated partition Waiman Long
2026-10-25 21:44 ` [PATCH-next v3 02/11] cgroup/cpuset: Properly compute effective xcpus in update_parent_effective_cpumask() Waiman Long
@ 2026-10-25 21:44 ` Waiman Long
2026-10-26 6:46 ` Guopeng Zhang
2026-10-25 21:44 ` [PATCH-next v3 04/11] cgroup/cpuset: Minor fixes to prstate_housekeeping_conflict() Waiman Long
` (7 subsequent siblings)
10 siblings, 1 reply; 23+ messages in thread
From: Waiman Long @ 2026-10-25 21:44 UTC (permalink / raw)
To: Ridong Chen, Tejun Heo, Johannes Weiner, Michal Koutný, Shuah Khan
Cc: cgroups, linux-kernel, linux-kselftest, Guopeng Zhang, Waiman Long
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@redhat.com>
---
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 b850aba4d907..37bc79d0c647 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -2327,10 +2327,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);
+ }
}
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 8bdda955b4a7..f4eb984752a0 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 ."
--
2.56.0
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH-next v3 04/11] cgroup/cpuset: Minor fixes to prstate_housekeeping_conflict()
2026-10-25 21:44 [PATCH-next v3 00/11] cgroup/cpuset: Miscellaneous bug fixes & cleanup Waiman Long
` (2 preceding siblings ...)
2026-10-25 21:44 ` [PATCH-next v3 03/11] cgroup/cpuset: Fix incorrect effective_cpus bug in update_cpumasks_hier() Waiman Long
@ 2026-10-25 21:44 ` Waiman Long
2026-10-26 6:57 ` Guopeng Zhang
2026-10-25 21:44 ` [PATCH-next v3 05/11] selftests/cgroup: Check for a single range nohz_full CPUs before running test matrix Waiman Long
` (6 subsequent siblings)
10 siblings, 1 reply; 23+ messages in thread
From: Waiman Long @ 2026-10-25 21:44 UTC (permalink / raw)
To: Ridong Chen, Tejun Heo, Johannes Weiner, Michal Koutný, Shuah Khan
Cc: cgroups, linux-kernel, linux-kselftest, Guopeng Zhang, Waiman Long
Commit 9527ff78a413 ("cgroup/cpuset: Consider all the exclusive CPUs when
doing housekeeping check") changes the way prstate_housekeeping_conflict()
is being used by considering all the exclusive xcpus to be owned by the
partition when doing housekeeping check. However there are two minor
issues reported by sashiko with that patch:
1) In partition_cpus_change(), prstate_housekeeping_conflict() can
be called with an invalid partition root. In that case, there are
no isolated CPUs that are going to be returned back to the system to
become housekeeping CPUs, so the calculation of the del_cpus cpumask
is meaningless.
2) The checking of del_cpus in prstate_housekeeping_conflict() to see
if it can contain an active housekeeping CPU that can be returned to the
system ignores the fact that an active CPU not in HK_TYPE_DOMAIN_BOOT HK
cpumask will not become an active housekeeping CPU when it is released
by an isolated partition.
Update the existing code to handle these two issues properly to make
prstate_housekeeping_conflict() produce more accurate result.
Fixes: 9527ff78a413 ("cgroup/cpuset: Consider all the exclusive CPUs when doing housekeeping check")
Signed-off-by: Waiman Long <longman@redhat.com>
---
kernel/cgroup/cpuset.c | 45 +++++++++++++++++++++++++++++-------------
1 file changed, 31 insertions(+), 14 deletions(-)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 37bc79d0c647..57e5334eb919 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1446,6 +1446,9 @@ static bool prstate_housekeeping_conflict(int new_prs, int parent_prs,
!cpumask_subset(new_cpus, housekeeping_cpumask(HK_TYPE_DOMAIN_BOOT)))
return true;
+ if (del_cpus && cpumask_empty(del_cpus))
+ del_cpus = NULL;
+
/*
* Exit 2nd check early and return false if
* 1) there is no boot time isolated nohz_full CPU
@@ -1454,8 +1457,7 @@ static bool prstate_housekeeping_conflict(int new_prs, int parent_prs,
* be returned back to the parent.
*/
if (!housekeeping_enabled(HK_TYPE_KERNEL_NOISE) ||
- (new_prs == parent_prs) ||
- ((new_prs == PRS_ROOT) && (!del_cpus || cpumask_empty(del_cpus))))
+ (new_prs == parent_prs) || (!del_cpus && (new_prs == PRS_ROOT)))
return false;
/*
@@ -1466,20 +1468,29 @@ static bool prstate_housekeeping_conflict(int new_prs, int parent_prs,
if (parent_prs == PRS_ISOLATED)
swap(new_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;
+ /*
+ * Check if del_cpus may contain a CPU that can become an active
+ * housekeeping CPU again.
+ */
+ if (del_cpus) {
+ cpumask_and(full_hk_cpus, housekeeping_cpumask(HK_TYPE_KERNEL_NOISE),
+ housekeeping_cpumask(HK_TYPE_DOMAIN_BOOT));
+ if (cpumask_first_and_and(del_cpus, full_hk_cpus,
+ cpu_active_mask) < nr_cpu_ids) {
+ res = false;
+ goto out;
+ }
+ }
+
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);
res = !cpumask_weight_andnot(full_hk_cpus, new_cpus);
-
+out:
free_cpumask_var(full_hk_cpus);
return res;
}
@@ -2486,23 +2497,29 @@ static void partition_cpus_change(struct cpuset *cs, struct cpuset *trialcs,
struct tmpmasks *tmp)
{
enum prs_errcode prs_err;
+ int parent_prs = is_remote_partition(cs)
+ ? PRS_ROOT : parent_cs(cs)->partition_root_state;
if (cs_is_member(cs))
return;
prs_err = validate_partition(cs, trialcs);
- if (!prs_err && !cpumask_empty(trialcs->effective_xcpus)) {
+ if (!prs_err && (parent_prs > 0) &&
+ !cpumask_empty(trialcs->effective_xcpus)) {
int prs = cs->partition_root_state;
- int parent_prs = is_remote_partition(cs)
- ? PRS_ROOT : parent_cs(cs)->partition_root_state;
+
/*
* Check for housekeeping conflict by passing the whole
* effective_xcpus as well as those CPUs that are to be
* removed from cs as delmask.
*/
- cpumask_andnot(tmp->delmask, cs->effective_xcpus, trialcs->effective_xcpus);
- if (parent_prs > 0 &&
- prstate_housekeeping_conflict(prs, parent_prs,
+ if (prs > 0)
+ cpumask_andnot(tmp->delmask, cs->effective_xcpus,
+ trialcs->effective_xcpus);
+ else
+ cpumask_clear(tmp->delmask);
+
+ if (prstate_housekeeping_conflict(prs, parent_prs,
trialcs->effective_xcpus, tmp->delmask))
prs_err = PERR_HKEEPING;
}
--
2.56.0
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH-next v3 05/11] selftests/cgroup: Check for a single range nohz_full CPUs before running test matrix
2026-10-25 21:44 [PATCH-next v3 00/11] cgroup/cpuset: Miscellaneous bug fixes & cleanup Waiman Long
` (3 preceding siblings ...)
2026-10-25 21:44 ` [PATCH-next v3 04/11] cgroup/cpuset: Minor fixes to prstate_housekeeping_conflict() Waiman Long
@ 2026-10-25 21:44 ` Waiman Long
2026-10-25 21:44 ` [PATCH-next v3 06/11] cgroup/cpuset: Prevent recursive call of remote_cpus_update() via update_cpumasks_hier() Waiman Long
` (5 subsequent siblings)
10 siblings, 0 replies; 23+ messages in thread
From: Waiman Long @ 2026-10-25 21:44 UTC (permalink / raw)
To: Ridong Chen, Tejun Heo, Johannes Weiner, Michal Koutný, Shuah Khan
Cc: cgroups, linux-kernel, linux-kselftest, Guopeng Zhang,
Waiman Long, Guopeng Zhang
Commit 989ca25c10ca ("selftests/cgroup: Add tests for nohz_full
housekeeping check") adds a nohz_full test matrix that will only be run
when a nohz_full boot parameter cover all the CPUs beyond CPUs 0-7. The
code looks for the first and last CPU numbers in the nohz_full sysfs
file to determine if that criterion is met. However if there are holes in
the range (i.e. has ",") the test matrix may be incorrectly run leading
to unexpected test failure. Fix that by skipping "," and anything after
that to make sure the first range will cover all the remaining CPUs.
Reviewed-by: Guopeng Zhang <zhangguopeng@kylinos.cn>
Tested-by: Guopeng Zhang <zhangguopeng@kylinos.cn>
Fixes: 989ca25c10ca ("selftests/cgroup: Add tests for nohz_full housekeeping check")
Signed-off-by: Waiman Long <longman@redhat.com>
---
tools/testing/selftests/cgroup/test_cpuset_prs.sh | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/tools/testing/selftests/cgroup/test_cpuset_prs.sh b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
index f4eb984752a0..3c9f67f91366 100755
--- a/tools/testing/selftests/cgroup/test_cpuset_prs.sh
+++ b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
@@ -111,9 +111,9 @@ echo "" > test/cpuset.cpus
#
# 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.
+# set of nohz_full CPUs includes all the CPUs except 0-7 in a single range
+# <first>-<last>, we can run a special set of housekeeping CPUs exhaustion
+# tests to see if the cpuset code will reject them.
#
FIRST_NOHZ_FULL_CPU=
LAST_NOHZ_FULL_CPU=
@@ -124,7 +124,7 @@ NOHZ_FULL_CPUS=
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/.*[,-]//")
+ LAST_NOHZ_FULL_CPU=$(echo $NOHZ_FULL_CPUS | sed -e "s/,.*//;s/.*[-]//")
echo "Boot nohz_full CPUs: $NOHZ_FULL_CPUS"
}
[[ -n "$FIRST_NOHZ_FULL_CPU" && "$FIRST_NOHZ_FULL_CPU" -le 7 ]] &&
--
2.56.0
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH-next v3 06/11] cgroup/cpuset: Prevent recursive call of remote_cpus_update() via update_cpumasks_hier()
2026-10-25 21:44 [PATCH-next v3 00/11] cgroup/cpuset: Miscellaneous bug fixes & cleanup Waiman Long
` (4 preceding siblings ...)
2026-10-25 21:44 ` [PATCH-next v3 05/11] selftests/cgroup: Check for a single range nohz_full CPUs before running test matrix Waiman Long
@ 2026-10-25 21:44 ` Waiman Long
2026-10-25 21:44 ` [PATCH-next v3 07/11] cgroup/cpuset: Make sure compute_partition_effective_cpumask() is only called for valid partition Waiman Long
` (4 subsequent siblings)
10 siblings, 0 replies; 23+ messages in thread
From: Waiman Long @ 2026-10-25 21:44 UTC (permalink / raw)
To: Ridong Chen, Tejun Heo, Johannes Weiner, Michal Koutný, Shuah Khan
Cc: cgroups, linux-kernel, linux-kselftest, Guopeng Zhang,
Waiman Long, Guopeng Zhang
Guopeng reports that changing the cpumask of a remote partition root
may set the effective_cpus of the top_cpuset incorrectly. It is
due to the following call sequence:
update_cpumask()
-> partition_cpus_change()
-> remote_cpus_update()
-> update_sibling_cpumasks()
-> update_cpumasks_hier()
-> remote_cpus_update()
The second remote_cpus_update() call passes in the cs->effective_xcpus
instead of the trialcs->effective_xcpus of the first remote_cpus_update()
call. As a result, the second call can revert what has been done in
the first call leading to incorrect effective_cpus of the top_cpuset
as well as subpartitions_cpus.
Fix that by adding a new remote_partition_updating flag into the cpuset
structure to indicate that a remote partition is being updated and
update_cpumasks_hier() should skip the whole subtree as update_cpumask()
or update_exclusive_cpumask() which calls partition_cpus_change()
will eventually calls update_cpumasks_hier() for that subtree anyway.
This new flag is currently set only in remote_cpus_update() to prevent
recursive call of remote_cpus_update() on the same cpuset.
A test case is added to test_cpuset_prs.sh to verify this change.
Reported-by: Guopeng Zhang <zhangguopeng@kylinos.cn>
Link: https://lore.kernel.org/lkml/20261010075947.333549-2-guopeng.zhang@linux.dev
Reviewed-by: Guopeng Zhang <zhangguopeng@kylinos.cn>
Tested-by: Guopeng Zhang <zhangguopeng@kylinos.cn>
Fixes: f62a5d39368e ("cgroup/cpuset: Remove remote_partition_check() & make update_cpumasks_hier() handle remote partition")
Signed-off-by: Waiman Long <longman@redhat.com>
---
kernel/cgroup/cpuset-internal.h | 5 +++++
kernel/cgroup/cpuset.c | 13 +++++++++----
tools/testing/selftests/cgroup/test_cpuset_prs.sh | 1 +
3 files changed, 15 insertions(+), 4 deletions(-)
diff --git a/kernel/cgroup/cpuset-internal.h b/kernel/cgroup/cpuset-internal.h
index 2e44dc8634dc..1ba94ffaff9d 100644
--- a/kernel/cgroup/cpuset-internal.h
+++ b/kernel/cgroup/cpuset-internal.h
@@ -161,6 +161,11 @@ struct cpuset {
*/
bool remote_partition;
+ /*
+ * Set to true when a remote partition is undergoing update.
+ */
+ bool remote_partition_updating;
+
/*
* number of SCHED_DEADLINE tasks attached to this cpuset, so that we
* know when to rebuild associated root domain bandwidth information.
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 57e5334eb919..8903e441ecc1 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1775,6 +1775,7 @@ static void remote_cpus_update(struct cpuset *cs, struct cpumask *xcpus,
goto invalidate;
}
+ cs->remote_partition_updating = true;
spin_lock_irq(&callback_lock);
if (adding)
partition_xcpus_add(prs, NULL, tmp->addmask);
@@ -1796,6 +1797,7 @@ static void remote_cpus_update(struct cpuset *cs, struct cpumask *xcpus,
*/
cpuset_update_tasks_cpumask(&top_cpuset, tmp->new_cpus);
update_sibling_cpumasks(&top_cpuset, NULL, tmp);
+ cs->remote_partition_updating = false;
return;
invalidate:
@@ -2237,16 +2239,19 @@ static void update_cpumasks_hier(struct cpuset *cs, struct tmpmasks *tmp,
old_prs = new_prs = cp->partition_root_state;
/*
- * For child remote partition root (!= cs), we need to call
- * remote_cpus_update() if effective_xcpus will be changed.
- * Otherwise, we can skip the whole subtree.
+ * For a child remote partition with remote_partition_updating
+ * set, we can skip the whole subtree as the this function will
+ * be called eventually. We can also skip the subtree if the
+ * effective_xcpus isn't going to be changed. Otherwise, we
+ * will have to call remote_cpus_update().
*
* remote_cpus_update() will reuse tmp->new_cpus only after
* its value is being processed.
*/
if (remote && (cp != cs)) {
compute_excpus(cp, tmp->new_cpus);
- if (cpumask_equal(cp->effective_xcpus, tmp->new_cpus)) {
+ if (cp->remote_partition_updating ||
+ cpumask_equal(cp->effective_xcpus, tmp->new_cpus)) {
pos_css = css_rightmost_descendant(pos_css);
continue;
}
diff --git a/tools/testing/selftests/cgroup/test_cpuset_prs.sh b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
index 3c9f67f91366..f0d98da3cfd8 100755
--- a/tools/testing/selftests/cgroup/test_cpuset_prs.sh
+++ b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
@@ -347,6 +347,7 @@ TEST_MATRIX=(
" C0-3 C1-3 C2-3 C4 X2-3 X2-3 X2-3:P2 P2:C4-5 0 A3:2-3|B1:4-5 A3:P2|B1:P2 2-5"
" C4:X0-3 X1-3 X2-3 . . P2 . . 0 A1:4|A2:1-3|A3:1-3 A2:P2 1-3"
" C4:X0-3 X1-3 X2-3 . . . P2 . 0 A1:4|A2:4|A3:2-3 A3:P2 2-3"
+ " CX1-6 C1-4:P1 . C4-5 . C1-3 . . 0 A1:4-6|A2:1-3|B1:4-5 A2:P1"
# Nested remote/local partition tests
" C0-3 C1-3 C2-3 C4-5 X2-3 X2-3:P1 P2 P1 0 A1:0-1|A2:|A3:2-3|B1:4-5 \
--
2.56.0
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH-next v3 07/11] cgroup/cpuset: Make sure compute_partition_effective_cpumask() is only called for valid partition
2026-10-25 21:44 [PATCH-next v3 00/11] cgroup/cpuset: Miscellaneous bug fixes & cleanup Waiman Long
` (5 preceding siblings ...)
2026-10-25 21:44 ` [PATCH-next v3 06/11] cgroup/cpuset: Prevent recursive call of remote_cpus_update() via update_cpumasks_hier() Waiman Long
@ 2026-10-25 21:44 ` Waiman Long
2026-10-26 7:14 ` Guopeng Zhang
2026-10-25 21:44 ` [PATCH-next v3 08/11] cgroup/cpuset: Release invalid isolated CPUs in compute_partition_effective_cpumask() Waiman Long
` (3 subsequent siblings)
10 siblings, 1 reply; 23+ messages in thread
From: Waiman Long @ 2026-10-25 21:44 UTC (permalink / raw)
To: Ridong Chen, Tejun Heo, Johannes Weiner, Michal Koutný, Shuah Khan
Cc: cgroups, linux-kernel, linux-kselftest, Guopeng Zhang, Waiman Long
With visual inspection to make sure that
compute_partition_effective_cpumask() is only called for valid partition
root, it was found that the function can be called for an invalidated
partition near the end of cpuset_hotplug_update_tasks(). When partcmd is
set, update_parent_effective_cpumask() is called to either invalidate a
valid partition or with partcmd_update to check if an invalid partition
can be switched back to valid. In both cases, cpuset_force_rebuild()
should be invoked. However, compute_partition_effective_cpumask()
should only be called when an invalid partition is switched back to
valid. When a partition is invalidated, compute_effective_cpumask()
should be called instead.
Also add a WARN_ON_ONCE() in compute_partition_effective_cpumask() to
ensure that a valid partition root is passed in.
Fixes: 72c6303acfa1 ("cgroup/cpuset: Take isolated CPUs out of workqueue unbound cpumask")
Signed-off-by: Waiman Long <longman@redhat.com>
---
kernel/cgroup/cpuset.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 8903e441ecc1..53fa3970946e 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -2163,6 +2163,8 @@ static void compute_partition_effective_cpumask(struct cpuset *cs,
struct cpuset *child;
bool populated = partition_is_populated(cs, NULL);
+ WARN_ON_ONCE(!is_partition_valid(cs));
+
/*
* Check child partition roots to see if they should be
* invalidated when
@@ -4035,9 +4037,12 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs, struct tmpmasks *tmp)
if (partcmd >= 0) {
update_parent_effective_cpumask(cs, partcmd, NULL, tmp);
- if ((partcmd == partcmd_invalidate) || is_partition_valid(cs)) {
+ if (is_partition_valid(cs)) {
compute_partition_effective_cpumask(cs, &new_cpus);
cpuset_force_rebuild();
+ } else if (partcmd == partcmd_invalidate) {
+ compute_effective_cpumask(&new_cpus, cs, parent);
+ cpuset_force_rebuild();
}
}
--
2.56.0
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH-next v3 08/11] cgroup/cpuset: Release invalid isolated CPUs in compute_partition_effective_cpumask()
2026-10-25 21:44 [PATCH-next v3 00/11] cgroup/cpuset: Miscellaneous bug fixes & cleanup Waiman Long
` (6 preceding siblings ...)
2026-10-25 21:44 ` [PATCH-next v3 07/11] cgroup/cpuset: Make sure compute_partition_effective_cpumask() is only called for valid partition Waiman Long
@ 2026-10-25 21:44 ` Waiman Long
2026-10-26 8:49 ` Guopeng Zhang
2026-10-25 21:44 ` [PATCH-next v3 09/11] cgroup/cpuset: Release invalid isolated CPUs with a shrunk root partition parent Waiman Long
` (2 subsequent siblings)
10 siblings, 1 reply; 23+ messages in thread
From: Waiman Long @ 2026-10-25 21:44 UTC (permalink / raw)
To: Ridong Chen, Tejun Heo, Johannes Weiner, Michal Koutný, Shuah Khan
Cc: cgroups, linux-kernel, linux-kselftest, Guopeng Zhang, Waiman Long
It is found by sashiko that when a child isolated partition with parent
root partition is invalidated in compute_partition_effective_cpumask(),
the switching of the isolated exclusive CPUs from child to parent will
not be reflected in isolated_cpus leading to CPU leakage. Fix that by
calling isolated_cpus_update() when that happens.
Also simplify compute_partition_effective_cpumask() by doing
child partition invalidation only for the special case of no more
effective_cpus left in a populated parent. The other case doesn't
actually warrant invalidation at this stage and subsequent iteration
in update_cpumasks_hier() and update_parent_effective_cpumask() for
that child should handle that.
Also drop the make_partition_invalid() helper by inlining it into
compute_partition_effective_cpumask() as it is the only caller.
A new test case is add to test_cpuset_prs.sh to verify this.
Fixes: 11e5f407b64a ("cgroup/cpuset: Keep track of CPUs in isolated partitions")
Signed-off-by: Waiman Long <longman@redhat.com>
---
kernel/cgroup/cpuset.c | 46 +++++++------------
.../selftests/cgroup/test_cpuset_prs.sh | 4 ++
2 files changed, 21 insertions(+), 29 deletions(-)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 53fa3970946e..6d224e614e74 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -254,15 +254,6 @@ static inline bool cs_is_member(const struct cpuset *cs)
return cs->partition_root_state == PRS_MEMBER;
}
-/*
- * Callers should hold callback_lock to modify partition_root_state.
- */
-static inline void make_partition_invalid(struct cpuset *cs)
-{
- if (cs->partition_root_state > 0)
- cs->partition_root_state = -cs->partition_root_state;
-}
-
/*
* Send notification event of whenever partition_root_state changes.
*/
@@ -2153,8 +2144,6 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd,
* or update_cpumasks_hier() where parent and children are modified
* successively, we don't need to call update_parent_effective_cpumask()
* and the child's effective_cpus will be updated in later iterations.
- *
- * Note that rcu_read_lock() is assumed to be held.
*/
static void compute_partition_effective_cpumask(struct cpuset *cs,
struct cpumask *new_ecpus)
@@ -2166,15 +2155,16 @@ static void compute_partition_effective_cpumask(struct cpuset *cs,
WARN_ON_ONCE(!is_partition_valid(cs));
/*
- * Check child partition roots to see if they should be
- * invalidated when
- * 1) child effective_xcpus not a subset of new
- * excluisve_cpus
- * 2) All the effective_cpus will be used up and cp
- * has tasks
+ * Check child partition roots to see if they should be invalidated
+ * when all the effective_cpus will be used up and cs is populated.
+ * It is better to sacrifice a child partition root than invalidating
+ * the whole subtree.
+ *
+ * Return early if the effective_cpus is already empty.
*/
compute_excpus(cs, new_ecpus);
- cpumask_and(new_ecpus, new_ecpus, cpu_active_mask);
+ if (!cpumask_and(new_ecpus, new_ecpus, cpu_active_mask))
+ return;
rcu_read_lock();
cpuset_for_each_child(child, css, cs) {
@@ -2186,22 +2176,20 @@ static void compute_partition_effective_cpumask(struct cpuset *cs,
* partition root.
*/
WARN_ON_ONCE(is_remote_partition(child));
- WRITE_ONCE(child->prs_err, 0);
- if (!cpumask_subset(child->effective_xcpus,
- cs->effective_xcpus))
- WRITE_ONCE(child->prs_err, PERR_INVCPUS);
- else if (populated &&
- cpumask_subset(new_ecpus, child->effective_xcpus))
- WRITE_ONCE(child->prs_err, PERR_NOCPUS);
-
- if (child->prs_err) {
+ if (populated && cpumask_subset(new_ecpus, child->effective_xcpus)) {
int old_prs = child->partition_root_state;
+ int parent_prs = cs->partition_root_state;
/*
- * Invalidate child partition
+ * Invalidate child partition and update isolated_cpus
+ * if necessary.
*/
spin_lock_irq(&callback_lock);
- make_partition_invalid(child);
+ WRITE_ONCE(child->prs_err, PERR_NOCPUS);
+ if ((old_prs == PRS_ISOLATED) && (parent_prs == PRS_ROOT))
+ isolated_cpus_update(PRS_ISOLATED, PRS_ROOT,
+ child->effective_xcpus);
+ child->partition_root_state = -old_prs;
spin_unlock_irq(&callback_lock);
notify_partition_change(child, old_prs);
continue;
diff --git a/tools/testing/selftests/cgroup/test_cpuset_prs.sh b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
index f0d98da3cfd8..d7d0bef0a17a 100755
--- a/tools/testing/selftests/cgroup/test_cpuset_prs.sh
+++ b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
@@ -518,6 +518,10 @@ TEST_MATRIX=(
" CX0-4:P2 CX2-4:P2 CX3-4:P2 . . X2 . . 0 A1:0-1,3-4|A2:2|A3:2 \
A1:P2|A2:P2|A3:P-2 0-4"
+ # When an isolated partition child with root partition parent is invalidated,
+ # its effective xcpus will be removed from isolated_cpus.
+ " C1-4:P1 C3-4:P2 . . T:C3-4 . . . 0 A1:3-4|A2:3-4 A1:P1|A2:P-2 ."
+
#
# old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate ISOLCPUS
# ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------ --------
--
2.56.0
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH-next v3 09/11] cgroup/cpuset: Release invalid isolated CPUs with a shrunk root partition parent
2026-10-25 21:44 [PATCH-next v3 00/11] cgroup/cpuset: Miscellaneous bug fixes & cleanup Waiman Long
` (7 preceding siblings ...)
2026-10-25 21:44 ` [PATCH-next v3 08/11] cgroup/cpuset: Release invalid isolated CPUs in compute_partition_effective_cpumask() Waiman Long
@ 2026-10-25 21:44 ` Waiman Long
2026-10-25 23:53 ` [PATCH-next v3.1 " Waiman Long
2026-10-25 23:56 ` [PATCH-next v3 " Waiman Long
2026-10-25 21:44 ` [PATCH-next v3 10/11] cgroup/cpuset: Relocate compute_*effective_cpumask() and compute_effective_nodemask() Waiman Long
2026-10-25 21:44 ` [PATCH-next v3 11/11] cgroup/cpuset: Integrate compute_partition_effective_cpumask() into compute_effective_cpumask() Waiman Long
10 siblings, 2 replies; 23+ messages in thread
From: Waiman Long @ 2026-10-25 21:44 UTC (permalink / raw)
To: Ridong Chen, Tejun Heo, Johannes Weiner, Michal Koutný, Shuah Khan
Cc: cgroups, linux-kernel, linux-kselftest, Guopeng Zhang, Waiman Long
It is also reported by sashiko that there may be leakages of isolated
CPUs when a root partition parent shrinks its effective_xcpus with
isolated partition children. Leakage here means that isolated_cpus may
contain extra CPUs that are not in any isolated partitions and not boot
time isolated. Those CPUs cannot be used in any isolated partitions
anymore.
There are 2 different cases here. The effective_xcpus becomes empty and
the isolated partition should be invalidated in this case. Alternatively
some of its effective_xcpus are not in parent's effective xcpus but
still have some valid effective_xcpus left. The isolated partition can
remain valid. In both cases, the invalid isolated CPUs will have to be
released from isolated_cpus if appropriate.
Fix that by calling isolated_cpus_release() to release those isolated
CPUs from isolated_cpus.
New test cases are added to test_cpuset_prs.sh to test this.
Fixes: 11e5f407b64a ("cgroup/cpuset: Keep track of CPUs in isolated partitions")
Signed-off-by: Waiman Long <longman@redhat.com>
---
kernel/cgroup/cpuset.c | 16 +++++++++++++++-
.../testing/selftests/cgroup/test_cpuset_prs.sh | 6 ++++++
2 files changed, 21 insertions(+), 1 deletion(-)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 6d224e614e74..3daab2381f04 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -2012,8 +2012,22 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd,
*
* A partition error happens when parent has tasks and all
* its effective CPUs will have to be distributed out.
+ *
+ * For an isolated partition with a shrunk root partition parent
+ * we may need to return some or all of its isolated CPUs.
*/
- if (nocpu || cpumask_empty(tmp->delmask)) {
+ nocpu |= cpumask_empty(tmp->delmask);
+ if ((parent_prs == PRS_ROOT) && (old_prs == PRS_ISOLATED)) {
+ cpumask_clear(tmp->addmask);
+ if (nocpu)
+ cpumask_copy(tmp->addmask, cs->effective_xcpus);
+ else if (!cpumask_subset(cs->effective_xcpus, parent->effective_xcpus))
+ cpumask_andnot(tmp->addmask, cs->effective_cpus,
+ parent->effective_xcpus);
+ if (!cpumask_empty(tmp->addmask))
+ isolated_cpus_release(cs, tmp->addmask);
+ }
+ if (nocpu) {
part_error = PERR_NOCPUS;
if (is_partition_valid(cs))
adding = cpumask_and(tmp->addmask,
diff --git a/tools/testing/selftests/cgroup/test_cpuset_prs.sh b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
index d7d0bef0a17a..ceb1a3b4f94e 100755
--- a/tools/testing/selftests/cgroup/test_cpuset_prs.sh
+++ b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
@@ -522,6 +522,12 @@ TEST_MATRIX=(
# its effective xcpus will be removed from isolated_cpus.
" C1-4:P1 C3-4:P2 . . T:C3-4 . . . 0 A1:3-4|A2:3-4 A1:P1|A2:P-2 ."
+ # When a root partition parent with an isolated partition child is shrunk
+ # so that its effective xcpus is no longer a superset of its child effective
+ # xcpus, the invalid isolated CPUs should be removed from cpuset.cpus.isolated.
+ " C1-4:P1 C3-4:P2 . . T:C4-6 . . . 0 A1:5-6|A2:4 A1:P1|A2:P2 4"
+ " C1-4:P1 C1-2:P2 . . T:C4-6 . . . 0 A1:4-6|A2:4-6 A1:P1|A2:P-2 ."
+
#
# old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate ISOLCPUS
# ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------ --------
--
2.56.0
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH-next v3 10/11] cgroup/cpuset: Relocate compute_*effective_cpumask() and compute_effective_nodemask()
2026-10-25 21:44 [PATCH-next v3 00/11] cgroup/cpuset: Miscellaneous bug fixes & cleanup Waiman Long
` (8 preceding siblings ...)
2026-10-25 21:44 ` [PATCH-next v3 09/11] cgroup/cpuset: Release invalid isolated CPUs with a shrunk root partition parent Waiman Long
@ 2026-10-25 21:44 ` Waiman Long
2026-10-26 9:08 ` Guopeng Zhang
2026-10-25 21:44 ` [PATCH-next v3 11/11] cgroup/cpuset: Integrate compute_partition_effective_cpumask() into compute_effective_cpumask() Waiman Long
10 siblings, 1 reply; 23+ messages in thread
From: Waiman Long @ 2026-10-25 21:44 UTC (permalink / raw)
To: Ridong Chen, Tejun Heo, Johannes Weiner, Michal Koutný, Shuah Khan
Cc: cgroups, linux-kernel, linux-kselftest, Guopeng Zhang, Waiman Long
Relocate compute_partition_effective_cpumask(),
compute_effective_cpumask(), and compute_effective_nodemask() together so
that compute_effective_cpumask() and compute_partition_effective_cpumask()
can be further integrated together. There is no functional change.
Signed-off-by: Waiman Long <longman@redhat.com>
---
kernel/cgroup/cpuset.c | 212 ++++++++++++++++++++---------------------
1 file changed, 106 insertions(+), 106 deletions(-)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 3daab2381f04..f37d929616a6 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1135,43 +1135,6 @@ void cpuset_update_tasks_cpumask(struct cpuset *cs, struct cpumask *new_cpus)
css_task_iter_end(&it);
}
-/**
- * compute_effective_cpumask - Compute the effective cpumask of the cpuset
- * @new_cpus: the temp variable for the new effective_cpus mask
- * @cs: the cpuset the need to recompute the new effective_cpus mask
- * @parent: the parent cpuset
- *
- * For v2, the parent's effective_cpus is inherited if cpumask is empty.
- * The result is valid only if the given cpuset isn't a partition root.
- */
-static void compute_effective_cpumask(struct cpumask *new_cpus,
- struct cpuset *cs, struct cpuset *parent)
-{
- bool has_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);
-}
-
-/**
- * compute_effective_nodemask - Compute the effective nodemask of the cpuset
- * @new_mems: the temp variable for the new effective_mems mask
- * @cs: the cpuset the need to recompute the new effective_mems mask
- * @parent: the parent cpuset
- *
- * For v2, the parent's effective_mems is inherited if nodemask is empty.
- */
-static void compute_effective_nodemask(nodemask_t *new_mems,
- struct cpuset *cs, struct cpuset *parent)
-{
- bool has_mems;
-
- has_mems = nodes_and(*new_mems, cs->mems_allowed, parent->effective_mems);
- if (!has_mems && is_in_v2_mode())
- nodes_copy(*new_mems, parent->effective_mems);
-}
-
/*
* Commands for update_parent_effective_cpumask
*/
@@ -1622,6 +1585,112 @@ static int compute_trialcs_excpus(struct cpuset *trialcs, struct cpuset *cs)
return rm_siblings_excl_cpus(parent, cs, excpus);
}
+/**
+ * compute_partition_effective_cpumask - compute effective_cpus for partition
+ * @cs: partition root cpuset
+ * @new_ecpus: previously computed effective_cpus to be updated
+ *
+ * Compute the effective_cpus of a partition root by scanning effective_xcpus
+ * of child partition roots and excluding their effective_xcpus.
+ *
+ * This has the side effect of invalidating valid child partition roots,
+ * if necessary. Since it is called from either cpuset_hotplug_update_tasks()
+ * or update_cpumasks_hier() where parent and children are modified
+ * successively, we don't need to call update_parent_effective_cpumask()
+ * and the child's effective_cpus will be updated in later iterations.
+ */
+static void compute_partition_effective_cpumask(struct cpuset *cs,
+ struct cpumask *new_ecpus)
+{
+ struct cgroup_subsys_state *css;
+ struct cpuset *child;
+ bool populated = partition_is_populated(cs, NULL);
+
+ WARN_ON_ONCE(!is_partition_valid(cs));
+
+ /*
+ * Check child partition roots to see if they should be invalidated
+ * when all the effective_cpus will be used up and cs is populated.
+ * It is better to sacrifice a child partition root than invalidating
+ * the whole subtree.
+ *
+ * Return early if the effective_cpus is already empty.
+ */
+ compute_excpus(cs, new_ecpus);
+ if (!cpumask_and(new_ecpus, new_ecpus, cpu_active_mask))
+ return;
+
+ rcu_read_lock();
+ cpuset_for_each_child(child, css, cs) {
+ if (!is_partition_valid(child))
+ continue;
+
+ /*
+ * There shouldn't be a remote partition underneath another
+ * partition root.
+ */
+ WARN_ON_ONCE(is_remote_partition(child));
+ if (populated && cpumask_subset(new_ecpus, child->effective_xcpus)) {
+ int old_prs = child->partition_root_state;
+ int parent_prs = cs->partition_root_state;
+
+ /*
+ * Invalidate child partition and update isolated_cpus
+ * if necessary.
+ */
+ spin_lock_irq(&callback_lock);
+ WRITE_ONCE(child->prs_err, PERR_NOCPUS);
+ if ((old_prs == PRS_ISOLATED) && (parent_prs == PRS_ROOT))
+ isolated_cpus_update(PRS_ISOLATED, PRS_ROOT,
+ child->effective_xcpus);
+ child->partition_root_state = -old_prs;
+ spin_unlock_irq(&callback_lock);
+ notify_partition_change(child, old_prs);
+ continue;
+ }
+ cpumask_andnot(new_ecpus, new_ecpus,
+ child->effective_xcpus);
+ }
+ rcu_read_unlock();
+}
+
+/**
+ * compute_effective_cpumask - Compute the effective cpumask of the cpuset
+ * @new_cpus: the temp variable for the new effective_cpus mask
+ * @cs: the cpuset the need to recompute the new effective_cpus mask
+ * @parent: the parent cpuset
+ *
+ * For v2, the parent's effective_cpus is inherited if cpumask is empty.
+ * The result is valid only if the given cpuset isn't a partition root.
+ */
+static void compute_effective_cpumask(struct cpumask *new_cpus,
+ struct cpuset *cs, struct cpuset *parent)
+{
+ bool has_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);
+}
+
+/**
+ * compute_effective_nodemask - Compute the effective nodemask of the cpuset
+ * @new_mems: the temp variable for the new effective_mems mask
+ * @cs: the cpuset the need to recompute the new effective_mems mask
+ * @parent: the parent cpuset
+ *
+ * For v2, the parent's effective_mems is inherited if nodemask is empty.
+ */
+static void compute_effective_nodemask(nodemask_t *new_mems,
+ struct cpuset *cs, struct cpuset *parent)
+{
+ bool has_mems;
+
+ has_mems = nodes_and(*new_mems, cs->mems_allowed, parent->effective_mems);
+ if (!has_mems && is_in_v2_mode())
+ nodes_copy(*new_mems, parent->effective_mems);
+}
+
/*
* remote_partition_enable - Enable current cpuset as a remote partition root
* @cs: the cpuset to update
@@ -2145,75 +2214,6 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd,
return 0;
}
-/**
- * compute_partition_effective_cpumask - compute effective_cpus for partition
- * @cs: partition root cpuset
- * @new_ecpus: previously computed effective_cpus to be updated
- *
- * Compute the effective_cpus of a partition root by scanning effective_xcpus
- * of child partition roots and excluding their effective_xcpus.
- *
- * This has the side effect of invalidating valid child partition roots,
- * if necessary. Since it is called from either cpuset_hotplug_update_tasks()
- * or update_cpumasks_hier() where parent and children are modified
- * successively, we don't need to call update_parent_effective_cpumask()
- * and the child's effective_cpus will be updated in later iterations.
- */
-static void compute_partition_effective_cpumask(struct cpuset *cs,
- struct cpumask *new_ecpus)
-{
- struct cgroup_subsys_state *css;
- struct cpuset *child;
- bool populated = partition_is_populated(cs, NULL);
-
- WARN_ON_ONCE(!is_partition_valid(cs));
-
- /*
- * Check child partition roots to see if they should be invalidated
- * when all the effective_cpus will be used up and cs is populated.
- * It is better to sacrifice a child partition root than invalidating
- * the whole subtree.
- *
- * Return early if the effective_cpus is already empty.
- */
- compute_excpus(cs, new_ecpus);
- if (!cpumask_and(new_ecpus, new_ecpus, cpu_active_mask))
- return;
-
- rcu_read_lock();
- cpuset_for_each_child(child, css, cs) {
- if (!is_partition_valid(child))
- continue;
-
- /*
- * There shouldn't be a remote partition underneath another
- * partition root.
- */
- WARN_ON_ONCE(is_remote_partition(child));
- if (populated && cpumask_subset(new_ecpus, child->effective_xcpus)) {
- int old_prs = child->partition_root_state;
- int parent_prs = cs->partition_root_state;
-
- /*
- * Invalidate child partition and update isolated_cpus
- * if necessary.
- */
- spin_lock_irq(&callback_lock);
- WRITE_ONCE(child->prs_err, PERR_NOCPUS);
- if ((old_prs == PRS_ISOLATED) && (parent_prs == PRS_ROOT))
- isolated_cpus_update(PRS_ISOLATED, PRS_ROOT,
- child->effective_xcpus);
- child->partition_root_state = -old_prs;
- spin_unlock_irq(&callback_lock);
- notify_partition_change(child, old_prs);
- continue;
- }
- cpumask_andnot(new_ecpus, new_ecpus,
- child->effective_xcpus);
- }
- rcu_read_unlock();
-}
-
/*
* update_cpumasks_hier - Update effective cpumasks and tasks in the subtree
* @cs: the cpuset to consider
--
2.56.0
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH-next v3 11/11] cgroup/cpuset: Integrate compute_partition_effective_cpumask() into compute_effective_cpumask()
2026-10-25 21:44 [PATCH-next v3 00/11] cgroup/cpuset: Miscellaneous bug fixes & cleanup Waiman Long
` (9 preceding siblings ...)
2026-10-25 21:44 ` [PATCH-next v3 10/11] cgroup/cpuset: Relocate compute_*effective_cpumask() and compute_effective_nodemask() Waiman Long
@ 2026-10-25 21:44 ` Waiman Long
2026-10-26 9:44 ` Guopeng Zhang
10 siblings, 1 reply; 23+ messages in thread
From: Waiman Long @ 2026-10-25 21:44 UTC (permalink / raw)
To: Ridong Chen, Tejun Heo, Johannes Weiner, Michal Koutný, Shuah Khan
Cc: cgroups, linux-kernel, linux-kselftest, Guopeng Zhang, Waiman Long
Update compute_effective_cpumask() to call
compute_partition_effective_cpumask() directly if the given cpuset is
a valid remote or local partition root. This simplifies the call sites
where they are being involved. No functional change is expected.
Suggested-by: Ridong Chen <ridong.chen@linux.dev>
Signed-off-by: Waiman Long <longman@redhat.com>
---
kernel/cgroup/cpuset.c | 37 ++++++++++---------------------------
1 file changed, 10 insertions(+), 27 deletions(-)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index f37d929616a6..2598a0e2d667 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1606,8 +1606,6 @@ static void compute_partition_effective_cpumask(struct cpuset *cs,
struct cpuset *child;
bool populated = partition_is_populated(cs, NULL);
- WARN_ON_ONCE(!is_partition_valid(cs));
-
/*
* Check child partition roots to see if they should be invalidated
* when all the effective_cpus will be used up and cs is populated.
@@ -1668,6 +1666,10 @@ static void compute_effective_cpumask(struct cpumask *new_cpus,
{
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);
@@ -2268,10 +2270,7 @@ static void update_cpumasks_hier(struct cpuset *cs, struct tmpmasks *tmp,
remote = (new_prs == old_prs);
}
- if (remote || (is_partition_valid(parent) && is_partition_valid(cp)))
- compute_partition_effective_cpumask(cp, tmp->new_cpus);
- else
- compute_effective_cpumask(tmp->new_cpus, cp, parent);
+ compute_effective_cpumask(tmp->new_cpus, cp, parent);
if (remote)
goto get_css; /* Ready to update cpuset data */
@@ -2352,10 +2351,7 @@ static void update_cpumasks_hier(struct cpuset *cs, struct tmpmasks *tmp,
*/
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);
+ compute_effective_cpumask(tmp->new_cpus, cp, parent);
}
}
@@ -3987,7 +3983,6 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs, struct tmpmasks *tmp)
static nodemask_t new_mems;
bool cpus_updated;
bool mems_updated;
- bool remote;
int partcmd = -1;
struct cpuset *parent;
@@ -3999,21 +3994,12 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs, struct tmpmasks *tmp)
if (!tmp || !cs->partition_root_state)
goto update_tasks;
- /*
- * Compute effective_cpus for valid partition root, may invalidate
- * child partition roots if necessary.
- */
- remote = is_remote_partition(cs);
- if (remote || (is_partition_valid(cs) && is_partition_valid(parent)))
- compute_partition_effective_cpumask(cs, &new_cpus);
-
- if (remote && (cpumask_empty(subpartitions_cpus) ||
- (cpumask_empty(&new_cpus) &&
- partition_is_populated(cs, NULL)))) {
+ if (is_remote_partition(cs) && (cpumask_empty(subpartitions_cpus) ||
+ (cpumask_empty(&new_cpus) &&
+ partition_is_populated(cs, NULL)))) {
WRITE_ONCE(cs->prs_err, PERR_HOTPLUG);
remote_partition_disable(cs, tmp);
compute_effective_cpumask(&new_cpus, cs, parent);
- remote = false;
}
/*
@@ -4039,10 +4025,7 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs, struct tmpmasks *tmp)
if (partcmd >= 0) {
update_parent_effective_cpumask(cs, partcmd, NULL, tmp);
- if (is_partition_valid(cs)) {
- compute_partition_effective_cpumask(cs, &new_cpus);
- cpuset_force_rebuild();
- } else if (partcmd == partcmd_invalidate) {
+ if (is_partition_valid(cs) || (partcmd == partcmd_invalidate)) {
compute_effective_cpumask(&new_cpus, cs, parent);
cpuset_force_rebuild();
}
--
2.56.0
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH-next v3.1 09/11] cgroup/cpuset: Release invalid isolated CPUs with a shrunk root partition parent
2026-10-25 21:44 ` [PATCH-next v3 09/11] cgroup/cpuset: Release invalid isolated CPUs with a shrunk root partition parent Waiman Long
@ 2026-10-25 23:53 ` Waiman Long
2026-10-26 8:51 ` Guopeng Zhang
2026-10-25 23:56 ` [PATCH-next v3 " Waiman Long
1 sibling, 1 reply; 23+ messages in thread
From: Waiman Long @ 2026-10-25 23:53 UTC (permalink / raw)
To: Ridong Chen, Tejun Heo, Johannes Weiner, Michal Koutný, Shuah Khan
Cc: cgroups, linux-kernel, linux-kselftest, Guopeng Zhang, Waiman Long
It is also reported by sashiko that there may be leakages of isolated
CPUs when a root partition parent shrinks its effective_xcpus with
isolated partition children. Leakage here means that isolated_cpus may
contain extra CPUs that are not in any isolated partitions and not boot
time isolated. Those CPUs cannot be used in any isolated partitions
anymore.
There are 2 different cases here. The effective_xcpus becomes empty and
the isolated partition should be invalidated in this case. Alternatively
some of its effective_xcpus are not in parent's effective xcpus but
still have some valid effective_xcpus left. The isolated partition can
remain valid. In both cases, the invalid isolated CPUs will have to be
released from isolated_cpus if appropriate.
Fix that by calling isolated_cpus_release() to release those isolated
CPUs from isolated_cpus.
New test cases are added to test_cpuset_prs.sh to test this.
Fixes: 11e5f407b64a ("cgroup/cpuset: Keep track of CPUs in isolated partitions")
Signed-off-by: Waiman Long <longman@redhat.com>
---
kernel/cgroup/cpuset.c | 16 +++++++++++++++-
.../testing/selftests/cgroup/test_cpuset_prs.sh | 6 ++++++
2 files changed, 21 insertions(+), 1 deletion(-)
[v3.1] Fix the typo "cs->effective_cpus" => "cs->effective_xcpus".
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 6d224e614e74..9a2a6f727929 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -2012,8 +2012,22 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd,
*
* A partition error happens when parent has tasks and all
* its effective CPUs will have to be distributed out.
+ *
+ * For an isolated partition with a shrunk root partition parent
+ * we may need to return some or all of its isolated CPUs.
*/
- if (nocpu || cpumask_empty(tmp->delmask)) {
+ nocpu |= cpumask_empty(tmp->delmask);
+ if ((parent_prs == PRS_ROOT) && (old_prs == PRS_ISOLATED)) {
+ cpumask_clear(tmp->addmask);
+ if (nocpu)
+ cpumask_copy(tmp->addmask, cs->effective_xcpus);
+ else if (!cpumask_subset(cs->effective_xcpus, parent->effective_xcpus))
+ cpumask_andnot(tmp->addmask, cs->effective_xcpus,
+ parent->effective_xcpus);
+ if (!cpumask_empty(tmp->addmask))
+ isolated_cpus_release(cs, tmp->addmask);
+ }
+ if (nocpu) {
part_error = PERR_NOCPUS;
if (is_partition_valid(cs))
adding = cpumask_and(tmp->addmask,
diff --git a/tools/testing/selftests/cgroup/test_cpuset_prs.sh b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
index d7d0bef0a17a..ceb1a3b4f94e 100755
--- a/tools/testing/selftests/cgroup/test_cpuset_prs.sh
+++ b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
@@ -522,6 +522,12 @@ TEST_MATRIX=(
# its effective xcpus will be removed from isolated_cpus.
" C1-4:P1 C3-4:P2 . . T:C3-4 . . . 0 A1:3-4|A2:3-4 A1:P1|A2:P-2 ."
+ # When a root partition parent with an isolated partition child is shrunk
+ # so that its effective xcpus is no longer a superset of its child effective
+ # xcpus, the invalid isolated CPUs should be removed from cpuset.cpus.isolated.
+ " C1-4:P1 C3-4:P2 . . T:C4-6 . . . 0 A1:5-6|A2:4 A1:P1|A2:P2 4"
+ " C1-4:P1 C1-2:P2 . . T:C4-6 . . . 0 A1:4-6|A2:4-6 A1:P1|A2:P-2 ."
+
#
# old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate ISOLCPUS
# ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------ --------
--
2.56.0
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH-next v3 09/11] cgroup/cpuset: Release invalid isolated CPUs with a shrunk root partition parent
2026-10-25 21:44 ` [PATCH-next v3 09/11] cgroup/cpuset: Release invalid isolated CPUs with a shrunk root partition parent Waiman Long
2026-10-25 23:53 ` [PATCH-next v3.1 " Waiman Long
@ 2026-10-25 23:56 ` Waiman Long
1 sibling, 0 replies; 23+ messages in thread
From: Waiman Long @ 2026-10-25 23:56 UTC (permalink / raw)
To: Ridong Chen, Tejun Heo, Johannes Weiner, Michal Koutný, Shuah Khan
Cc: cgroups, linux-kernel, linux-kselftest, Guopeng Zhang
On 10/25/26 5:44 PM, Waiman Long wrote:
> It is also reported by sashiko that there may be leakages of isolated
> CPUs when a root partition parent shrinks its effective_xcpus with
> isolated partition children. Leakage here means that isolated_cpus may
> contain extra CPUs that are not in any isolated partitions and not boot
> time isolated. Those CPUs cannot be used in any isolated partitions
> anymore.
>
> There are 2 different cases here. The effective_xcpus becomes empty and
> the isolated partition should be invalidated in this case. Alternatively
> some of its effective_xcpus are not in parent's effective xcpus but
> still have some valid effective_xcpus left. The isolated partition can
> remain valid. In both cases, the invalid isolated CPUs will have to be
> released from isolated_cpus if appropriate.
>
> Fix that by calling isolated_cpus_release() to release those isolated
> CPUs from isolated_cpus.
>
> New test cases are added to test_cpuset_prs.sh to test this.
>
> Fixes: 11e5f407b64a ("cgroup/cpuset: Keep track of CPUs in isolated partitions")
> Signed-off-by: Waiman Long <longman@redhat.com>
> ---
> kernel/cgroup/cpuset.c | 16 +++++++++++++++-
> .../testing/selftests/cgroup/test_cpuset_prs.sh | 6 ++++++
> 2 files changed, 21 insertions(+), 1 deletion(-)
>
> diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
> index 6d224e614e74..3daab2381f04 100644
> --- a/kernel/cgroup/cpuset.c
> +++ b/kernel/cgroup/cpuset.c
> @@ -2012,8 +2012,22 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd,
> *
> * A partition error happens when parent has tasks and all
> * its effective CPUs will have to be distributed out.
> + *
> + * For an isolated partition with a shrunk root partition parent
> + * we may need to return some or all of its isolated CPUs.
> */
> - if (nocpu || cpumask_empty(tmp->delmask)) {
> + nocpu |= cpumask_empty(tmp->delmask);
> + if ((parent_prs == PRS_ROOT) && (old_prs == PRS_ISOLATED)) {
> + cpumask_clear(tmp->addmask);
> + if (nocpu)
> + cpumask_copy(tmp->addmask, cs->effective_xcpus);
> + else if (!cpumask_subset(cs->effective_xcpus, parent->effective_xcpus))
> + cpumask_andnot(tmp->addmask, cs->effective_cpus,
> + parent->effective_xcpus);
> + if (!cpumask_empty(tmp->addmask))
> + isolated_cpus_release(cs, tmp->addmask);
> + }
> + if (nocpu) {
> part_error = PERR_NOCPUS;
> if (is_partition_valid(cs))
> adding = cpumask_and(tmp->addmask,
> diff --git a/tools/testing/selftests/cgroup/test_cpuset_prs.sh b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
> index d7d0bef0a17a..ceb1a3b4f94e 100755
> --- a/tools/testing/selftests/cgroup/test_cpuset_prs.sh
> +++ b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
> @@ -522,6 +522,12 @@ TEST_MATRIX=(
> # its effective xcpus will be removed from isolated_cpus.
> " C1-4:P1 C3-4:P2 . . T:C3-4 . . . 0 A1:3-4|A2:3-4 A1:P1|A2:P-2 ."
>
> + # When a root partition parent with an isolated partition child is shrunk
> + # so that its effective xcpus is no longer a superset of its child effective
> + # xcpus, the invalid isolated CPUs should be removed from cpuset.cpus.isolated.
> + " C1-4:P1 C3-4:P2 . . T:C4-6 . . . 0 A1:5-6|A2:4 A1:P1|A2:P2 4"
> + " C1-4:P1 C1-2:P2 . . T:C4-6 . . . 0 A1:4-6|A2:4-6 A1:P1|A2:P-2 ."
> +
> #
> # old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate ISOLCPUS
> # ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------ --------
I am sorry that there is a typo in this patch. I have send out a v3.1
version with the fixed code.
Cheers,
Longman
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH-next v3 01/11] cgroup/cpuset: Find furthest isolated ancestor for invalidated isolated partition
2026-10-25 21:44 ` [PATCH-next v3 01/11] cgroup/cpuset: Find furthest isolated ancestor for invalidated isolated partition Waiman Long
@ 2026-10-26 6:33 ` Guopeng Zhang
0 siblings, 0 replies; 23+ messages in thread
From: Guopeng Zhang @ 2026-10-26 6:33 UTC (permalink / raw)
To: Waiman Long, Ridong Chen, Tejun Heo, Johannes Weiner,
Michal Koutný,
Shuah Khan
Cc: cgroups, linux-kernel, linux-kselftest
在 2026/10/26 05:44, Waiman Long 写道:
> Commit 46b885416c27 ("cgroup/cpuset: Fix a missing isolated_cpus
> update in update_cpumasks_hier()") searches for the closet partition
> root ancestor to determine if the isolated CPUs in a to be invalidated
> isolation partition should be removed from isolated_cpus or not.
>
> As reported by sashiko, this method may not work when there is a deeply
> nested isolated partition hierarchy. For instance, Consider a nested
> hierarchy A0 -> A1 -> A2 -> A3 where all four are PRS_ISOLATED:
>
> A0: effective_xcpus = 0-4
> A1: effective_xcpus = 1-4
> A2: effective_xcpus = 2-4
> A3: effective_xcpus = 3-4
>
> When A1 shrinks its exclusive_cpus from 1-4 to 1-2, A2 will update it
> effective_xcpus to 2. A3 will find its closet partition root ancestor
> A2 and decides that its effective_xcpus 3-4 should be removed from
> isolated_cpus which is incorrect.
>
> Address this corner case by adding a new isolated_cpus_release() helper
> to replace the existing ancestor_partition() helper. This new helper
> will search for the furthest isolated partition root ancestor to decide
> if some or all of its isolated CPUs should be removed from isolated_cpus
> and also perform the removal when appropriate.
>
> The is_remote_partition() and is_local_partition() helpers are also
> moved up so they can be used in earlier functions.
>
> There are other pre-existing problems that will still prevent the
> above test case to work correctly. These problems will be addressed in
> subsequent patches.
>
> Fixes: 46b885416c27 ("cgroup/cpuset: Fix a missing isolated_cpus update in update_cpumasks_hier()")
> Signed-off-by: Waiman Long <longman@redhat.com>
> ---
> kernel/cgroup/cpuset.c | 104 +++++++++++++++++++++--------------------
> 1 file changed, 53 insertions(+), 51 deletions(-)
>
> diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
> index 585a982249ad..bbd44782d4bf 100644
> --- a/kernel/cgroup/cpuset.c
> +++ b/kernel/cgroup/cpuset.c
> @@ -1257,6 +1257,16 @@ static void reset_partition_data(struct cpuset *cs)
> cpumask_copy(cs->effective_cpus, parent->effective_cpus);
> }
>
> +static inline bool is_remote_partition(struct cpuset *cs)
> +{
> + return cs->remote_partition;
> +}
> +
> +static inline bool is_local_partition(struct cpuset *cs)
> +{
> + return is_partition_valid(cs) && !is_remote_partition(cs);
> +}
> +
> /* Return true if isolated_cpus changes. */
> static bool isolated_cpu_update(int new_prs, int cpu)
> {
> @@ -1299,6 +1309,45 @@ static void isolated_cpus_update(int old_prs, int new_prs, struct cpumask *xcpus
> update_housekeeping = true;
> }
>
> +/*
> + * isolated_cpus_release - Remove the relevant CPUs in @xcpus from isolated_cpus
> + * @cs: the current cpuset
> + * @xcpus: the set of effective xcpus to be considered for removal
> + *
> + * This function is called when an isolated partition is invalidated or when
> + * some of its effective xcpus are to be removed from isolated_cpus due to
> + * changes in parent cpuset. The given @xcpus must be modifiable as the function
> + * will update its value to determine the right set of CPUs to be removed.
> + */
> +static void isolated_cpus_release(struct cpuset *cs, struct cpumask *xcpus)
> +{
> + struct cpuset *ancestor = parent_cs(cs);
> + struct cpuset *iancestor = NULL;
> +
> + /*
> + * Search the furthest isolated partition root ancestor available
> + * until top_cpuset or a remote partition root is hit.
> + */
> + for (; ancestor; ancestor = parent_cs(ancestor)) {
> + if (ancestor->partition_root_state == PRS_ISOLATED)
> + iancestor = ancestor;
> + if (is_remote_partition(ancestor))
> + break;
> + }
> +
> + /*
> + * If a furthest isolated partition acnestor is found, use it to
> + * determine which of the CPUs in @xcpus should be removed from
> + * isolated_cpus. Otherwise, all of them will be removed.
> + */
> + if (iancestor && !cpumask_andnot(xcpus, xcpus, iancestor->effective_xcpus))
> + return; /* No CPU should be removed from isolated_cpus */
> +
> + spin_lock_irq(&callback_lock);
> + isolated_cpus_update(PRS_ISOLATED, PRS_ROOT, xcpus);
> + spin_unlock_irq(&callback_lock);
> +}
> +
> /*
> * partition_xcpus_add - Add new exclusive CPUs to partition
> * @new_prs: new partition_root_state
> @@ -1571,16 +1620,6 @@ static int compute_trialcs_excpus(struct cpuset *trialcs, struct cpuset *cs)
> return rm_siblings_excl_cpus(parent, cs, excpus);
> }
>
> -static inline bool is_remote_partition(struct cpuset *cs)
> -{
> - return cs->remote_partition;
> -}
> -
> -static inline bool is_local_partition(struct cpuset *cs)
> -{
> - return is_partition_valid(cs) && !is_remote_partition(cs);
> -}
> -
> /*
> * remote_partition_enable - Enable current cpuset as a remote partition root
> * @cs: the cpuset to update
> @@ -2155,25 +2194,6 @@ 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
> @@ -2267,7 +2287,6 @@ static void update_cpumasks_hier(struct cpuset *cs, struct tmpmasks *tmp,
> */
> if ((cp != cs) && old_prs) {
> int parent_prs = parent->partition_root_state;
> - struct cpumask *del_cpus = NULL;
>
> switch (parent_prs) {
> case PRS_ROOT:
> @@ -2281,32 +2300,15 @@ static void update_cpumasks_hier(struct cpuset *cs, struct tmpmasks *tmp,
> * 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.
> + * call return_isolated_cpus() to remove the
> + * relevant CPUs from isolated_cpus.
> */
> if (is_partition_valid(cp))
> 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);
> - }
> + cpumask_copy(tmp->delmask, cp->effective_xcpus);
> + isolated_cpus_release(cp, tmp->delmask);
> }
> WRITE_ONCE(cp->prs_err,
> is_partition_invalid(parent)
Reviewed-by: Guopeng Zhang <zhangguopeng@kylinos.cn>
Thanks,
Guopeng
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH-next v3 02/11] cgroup/cpuset: Properly compute effective xcpus in update_parent_effective_cpumask()
2026-10-25 21:44 ` [PATCH-next v3 02/11] cgroup/cpuset: Properly compute effective xcpus in update_parent_effective_cpumask() Waiman Long
@ 2026-10-26 6:39 ` Guopeng Zhang
0 siblings, 0 replies; 23+ messages in thread
From: Guopeng Zhang @ 2026-10-26 6:39 UTC (permalink / raw)
To: Waiman Long, Ridong Chen, Tejun Heo, Johannes Weiner,
Michal Koutný,
Shuah Khan
Cc: cgroups, linux-kernel, linux-kselftest
在 2026/10/26 05:44, Waiman Long 写道:
> Since the introduction of cpuset.cpus.exclusive in commit
> e2ffe502ba45 ("cgroup/cpuset: Add cpuset.cpus.exclusive for v2"), a
> compute_effective_exclusive_cpumask() helper which is later renamed to
> compute_excpus() is added to compute the effective xcpus of a partition
> root. However update_parent_effective_cpumask() is not updated in all
> proper places to use the new helper to check if a given cpuset should
> remain a valid partition root. This can cause a cpuset to incorrectly
> remain a valid partition root even if no effective xcpus is left.
>
> Fix that problem by using compute_excpus() to compute the effective xcpus
> into delmask in all cases except partcmd_disable, partcmd_invalidate
> or partcmd_update with newmask set and use it for tasks_nocpu_error()
> check as well as checking its emptiness. This enables us to catch invalid
> partition root more accurately in update_parent_effective_cpumask().
>
> In the case of partcmd_update with newmask set, the passed in new
> effective xcpus value will directly be used in the tasks_nocpu_error()
> check without relying on old data in the cpuset.
>
> For the partcmd_update with NULL newmask case, the cpu exclusivity test
> there is also relaxed to be performed only if exclusive_cpus is empty.
>
> Some test case are added to test_cpuset_prs.sh for invalidating a
> partition root due to its effective xcpus becoming empty as well as
> making sure that an invalid partition can only be revived when exclusive
> CPUs are available agin.
>
> Fixes: e2ffe502ba45 ("cgroup/cpuset: Add cpuset.cpus.exclusive for v2")
> Signed-off-by: Waiman Long <longman@redhat.com>
> ---
> kernel/cgroup/cpuset.c | 87 ++++++++++---------
> .../selftests/cgroup/test_cpuset_prs.sh | 11 +++
> 2 files changed, 56 insertions(+), 42 deletions(-)
>
> diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
> index bbd44782d4bf..b850aba4d907 100644
> --- a/kernel/cgroup/cpuset.c
> +++ b/kernel/cgroup/cpuset.c
> @@ -1795,7 +1795,7 @@ static void remote_cpus_update(struct cpuset *cs, struct cpumask *xcpus,
> * update_parent_effective_cpumask - update effective_cpus mask of parent cpuset
> * @cs: The cpuset that requests change in partition root state
> * @cmd: Partition root state change command
> - * @newmask: Optional new cpumask for partcmd_update
> + * @newmask: Optional new effective xcpus for partcmd_update
> * @tmp: Temporary addmask and delmask
> * Return: 0 or a partition root state error code
> *
> @@ -1810,11 +1810,11 @@ static void remote_cpus_update(struct cpuset *cs, struct cpumask *xcpus,
> * given back to parent's effective_cpus. 0 will always be returned.
> *
> * For partcmd_update, if the optional newmask is specified, the cpu list is
> - * to be changed from effective_xcpus to newmask. Otherwise, effective_xcpus is
> - * assumed to remain the same. The cpuset should either be a valid or invalid
> - * partition root. The partition root state may change from valid to invalid
> - * or vice versa. An error code will be returned if transitioning from
> - * invalid to valid violates the exclusivity rule.
> + * to be changed from effective_xcpus to newmask. Otherwise, effective_xcpus
> + * will be computed and a valid partition will be invalidated if it is the new
> + * cpumask is empty. The cpuset should either be a valid or invalid partition
> + * root. The partition root state may change from valid to invalid or vice
> + * versa.
> *
> * For partcmd_invalidate, the current partition will be made invalid.
> *
> @@ -1837,7 +1837,6 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd,
> int deleting; /* Deleting cpus from parent's effective_cpus */
> 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;
>
> @@ -1880,30 +1879,30 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd,
> if (!newmask && xcpus_empty(cs))
> return PERR_CPUSEMPTY;
>
> - nocpu = tasks_nocpu_error(parent, cs, xcpus);
> + /*
> + * Except for partcmd_disable and non-NULL newmask, use delmask
> + * to compute the current effective xcpus value and use it for
> + * tasks_nocpu_error check.
> + */
> + if ((cmd != partcmd_disable) && !newmask) {
> + compute_excpus(cs, tmp->delmask);
> + nocpu = tasks_nocpu_error(parent, cs, tmp->delmask);
> + }
>
> if ((cmd == partcmd_enable) || (cmd == partcmd_enablei)) {
> - /*
> - * Need to call compute_excpus() in case
> - * exclusive_cpus not set. Sibling conflict should only happen
> - * if exclusive_cpus isn't set.
> - */
> - xcpus = tmp->delmask;
> - if (compute_excpus(cs, xcpus))
> - WARN_ON_ONCE(!cpumask_empty(cs->exclusive_cpus));
> new_prs = (cmd == partcmd_enable) ? PRS_ROOT : PRS_ISOLATED;
>
> /*
> * Enabling partition root is not allowed if its
> * effective_xcpus is empty.
> */
> - if (cpumask_empty(xcpus))
> + if (cpumask_empty(tmp->delmask))
> return PERR_INVCPUS;
>
> - if (prstate_housekeeping_conflict(new_prs, parent_prs, xcpus, NULL))
> + if (prstate_housekeeping_conflict(new_prs, parent_prs, tmp->delmask, NULL))
> return PERR_HKEEPING;
>
> - if (tasks_nocpu_error(parent, cs, xcpus))
> + if (nocpu)
> return PERR_NOCPUS;
>
> /*
> @@ -1915,16 +1914,14 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd,
> *
> * Warn if it is not the case.
> */
> - cpumask_and(tmp->new_cpus, xcpus, cpu_active_mask);
> + cpumask_and(tmp->new_cpus, tmp->delmask, cpu_active_mask);
> WARN_ON_ONCE(!cpumask_subset(tmp->new_cpus, parent->effective_cpus));
> -
> deleting = true;
> } else if (cmd == partcmd_disable) {
> /*
> * May need to add cpus back to parent's effective_cpus
> * (and maybe removed from subpartitions_cpus/isolated_cpus)
> - * for valid partition root. xcpus may contain CPUs that
> - * shouldn't be removed from the two global cpumasks.
> + * for valid partition root.
> */
> if (is_partition_valid(cs)) {
> cpumask_copy(tmp->addmask, cs->effective_xcpus);
> @@ -1944,8 +1941,8 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd,
> goto write_error;
> }
>
> - /* Check newmask again, whether cpus are available for parent/cs */
> - nocpu |= tasks_nocpu_error(parent, cs, newmask);
> + /* Check newmask whether cpus are available for parent/cs */
> + nocpu = tasks_nocpu_error(parent, cs, newmask);
>
> /*
> * partcmd_update with newmask:
> @@ -2000,7 +1997,7 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd,
> /*
> * partcmd_update w/o newmask
> *
> - * delmask = effective_xcpus & parent->effective_cpus
> + * delmask = newly computed effective xcpus value
> *
> * This can be called from:
> * 1) update_cpumasks_hier()
> @@ -2012,7 +2009,7 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd,
> * A partition error happens when parent has tasks and all
> * its effective CPUs will have to be distributed out.
> */
> - if (nocpu) {
> + if (nocpu || cpumask_empty(tmp->delmask)) {
> part_error = PERR_NOCPUS;
> if (is_partition_valid(cs))
> adding = cpumask_and(tmp->addmask,
> @@ -2025,38 +2022,44 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd,
> cpumask_copy(tmp->addmask, cs->effective_xcpus);
> adding = true;
> }
> - } else if (is_partition_invalid(cs) && !cpumask_empty(xcpus) &&
> - cpumask_subset(xcpus, parent->effective_xcpus)) {
> + } else if (is_partition_invalid(cs)) {
> struct cgroup_subsys_state *css;
> struct cpuset *child;
> bool exclusive = true;
>
> /*
> * Convert invalid partition to valid has to pass the
> - * cpu exclusivity test as well as the housekeeping
> - * check.
> + * cpu exclusivity test when cpuset.cpus.exclusive is
> + * empty as well as the housekeeping check.
> */
> if (prstate_housekeeping_conflict(cs->partition_root_state,
> - parent_prs, xcpus, NULL)) {
> + parent_prs, tmp->delmask, NULL)) {
> part_error = PERR_HKEEPING;
> goto write_error;
> }
> - rcu_read_lock();
> - cpuset_for_each_child(child, css, parent) {
> - if (child == cs)
> - continue;
> - if (!cpusets_are_exclusive(cs, child)) {
> - exclusive = false;
> - break;
> + if (cpumask_empty(cs->exclusive_cpus)) {
> + rcu_read_lock();
> + cpuset_for_each_child(child, css, parent) {
> + if (child == cs)
> + continue;
> + if (!cpusets_are_exclusive(cs, child)) {
> + exclusive = false;
> + break;
> + }
> }
> + rcu_read_unlock();
> }
> - rcu_read_unlock();
> if (exclusive)
> - deleting = cpumask_and(tmp->delmask,
> - xcpus, parent->effective_cpus);
> + deleting = !cpumask_empty(tmp->delmask);
> else
> part_error = PERR_NOTEXCL;
> }
> + /*
> + * When both cs and parent remain valid, parent's
> + * compute_partition_effective_cpumask() call would have removed
> + * cs's effective_xcpus from the parent's effective_cpus. So
> + * there is no need to adjust parent's effective_cpus here.
> + */
> }
>
> write_error:
> diff --git a/tools/testing/selftests/cgroup/test_cpuset_prs.sh b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
> index d04aad697b96..8bdda955b4a7 100755
> --- a/tools/testing/selftests/cgroup/test_cpuset_prs.sh
> +++ b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
> @@ -359,6 +359,8 @@ TEST_MATRIX=(
> A1:P0|A2:P1|A3:P2|B1:P1 2-4|3"
> " C1-6:P1 C3-6:P1 C5-6:P1 . . P2 . . 0 A1:1-2|A2:3-6|A3:5-6 \
> A1:P1|A2:P2|A3:P-1 3-6"
> + " C1-6:P2 C3-6:P2 C3-4:P2 . . C5-6 . . 0 A1:1-4|A2:5-6|A3:5-6 \
> + A1:P2|A2:P2|A3:P-2 1-6"
> " 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 ."
> @@ -427,6 +429,10 @@ TEST_MATRIX=(
> # cpuset.cpus is allowed, but those extra CPUs are ignored.
> " C2-3:P1 C3:P1 . . . C2-4 . . 0 A1:|A2:2-3 A1:P1|A2:P1"
>
> + # A child cannot recover until its exclusive CPUs are available again.
> + " CX1-4:P1 CX3-4:P1 . . X1-2 . . . 0 A1:1-2|A2:1-2|XA2: A1:P1|A2:P-1"
> + " CX1-4:P1 CX3-4:P1 . . X1-2:X1-4 . . . 0 A1:1-2|A2:3-4|XA2:3-4 A1:P1|A2:P1"
> +
> # Taking away all CPUs from parent or itself if there are tasks
> # will make the partition invalid.
> " C2-3:P1 C3:P1 . . T C2-3 . . 0 A1:2-3|A2:2-3 A1:P1|A2:P-1"
> @@ -503,6 +509,11 @@ TEST_MATRIX=(
> " C1-3:P2 C2:P2 . . . P1 . . 0 A1:1-3|A2:2 A1:P2|A2:P-1 1-3"
> " C1-3:P2 C2:P1 . . . C2-3 . . 0 A1:1-3|A2:2-3 A1:P2|A2:P-1 1-3"
>
> + # A middle isolated parent shrunk below coverage: the released CPUs of
> + # an invalidated child stay isolated as a higher ancestor still covers them.
> + " CX0-4:P2 CX2-4:P2 CX3-4:P2 . . X2 . . 0 A1:0-1,3-4|A2:2|A3:2 \
> + A1:P2|A2:P2|A3:P-2 0-4"
> +
> #
> # old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate ISOLCPUS
> # ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------ --------
Reviewed-by: Guopeng Zhang <zhangguopeng@kylinos.cn>
Tested-by: Guopeng Zhang <zhangguopeng@kylinos.cn>
Thanks,
Guopeng
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH-next v3 03/11] cgroup/cpuset: Fix incorrect effective_cpus bug in update_cpumasks_hier()
2026-10-25 21:44 ` [PATCH-next v3 03/11] cgroup/cpuset: Fix incorrect effective_cpus bug in update_cpumasks_hier() Waiman Long
@ 2026-10-26 6:46 ` Guopeng Zhang
0 siblings, 0 replies; 23+ messages in thread
From: Guopeng Zhang @ 2026-10-26 6:46 UTC (permalink / raw)
To: Waiman Long, Ridong Chen, Tejun Heo, Johannes Weiner,
Michal Koutný,
Shuah Khan
Cc: cgroups, linux-kernel, linux-kselftest
在 2026/10/26 05:44, Waiman Long 写道:
> 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@redhat.com>
> ---
> 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 b850aba4d907..37bc79d0c647 100644
> --- a/kernel/cgroup/cpuset.c
> +++ b/kernel/cgroup/cpuset.c
> @@ -2327,10 +2327,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);
> + }
> }
>
> 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 8bdda955b4a7..f4eb984752a0 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 ."
Reviewed-by: Guopeng Zhang <zhangguopeng@kylinos.cn>
Thanks,
Guopeng
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH-next v3 04/11] cgroup/cpuset: Minor fixes to prstate_housekeeping_conflict()
2026-10-25 21:44 ` [PATCH-next v3 04/11] cgroup/cpuset: Minor fixes to prstate_housekeeping_conflict() Waiman Long
@ 2026-10-26 6:57 ` Guopeng Zhang
0 siblings, 0 replies; 23+ messages in thread
From: Guopeng Zhang @ 2026-10-26 6:57 UTC (permalink / raw)
To: Waiman Long, Ridong Chen, Tejun Heo, Johannes Weiner,
Michal Koutný,
Shuah Khan
Cc: cgroups, linux-kernel, linux-kselftest
在 2026/10/26 05:44, Waiman Long 写道:
> Commit 9527ff78a413 ("cgroup/cpuset: Consider all the exclusive CPUs when
> doing housekeeping check") changes the way prstate_housekeeping_conflict()
> is being used by considering all the exclusive xcpus to be owned by the
> partition when doing housekeeping check. However there are two minor
> issues reported by sashiko with that patch:
>
> 1) In partition_cpus_change(), prstate_housekeeping_conflict() can
> be called with an invalid partition root. In that case, there are
> no isolated CPUs that are going to be returned back to the system to
> become housekeeping CPUs, so the calculation of the del_cpus cpumask
> is meaningless.
>
> 2) The checking of del_cpus in prstate_housekeeping_conflict() to see
> if it can contain an active housekeeping CPU that can be returned to the
> system ignores the fact that an active CPU not in HK_TYPE_DOMAIN_BOOT HK
> cpumask will not become an active housekeeping CPU when it is released
> by an isolated partition.
>
> Update the existing code to handle these two issues properly to make
> prstate_housekeeping_conflict() produce more accurate result.
>
> Fixes: 9527ff78a413 ("cgroup/cpuset: Consider all the exclusive CPUs when doing housekeeping check")
> Signed-off-by: Waiman Long <longman@redhat.com>
> ---
> kernel/cgroup/cpuset.c | 45 +++++++++++++++++++++++++++++-------------
> 1 file changed, 31 insertions(+), 14 deletions(-)
>
> diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
> index 37bc79d0c647..57e5334eb919 100644
> --- a/kernel/cgroup/cpuset.c
> +++ b/kernel/cgroup/cpuset.c
> @@ -1446,6 +1446,9 @@ static bool prstate_housekeeping_conflict(int new_prs, int parent_prs,
> !cpumask_subset(new_cpus, housekeeping_cpumask(HK_TYPE_DOMAIN_BOOT)))
> return true;
>
> + if (del_cpus && cpumask_empty(del_cpus))
> + del_cpus = NULL;
> +
> /*
> * Exit 2nd check early and return false if
> * 1) there is no boot time isolated nohz_full CPU
> @@ -1454,8 +1457,7 @@ static bool prstate_housekeeping_conflict(int new_prs, int parent_prs,
> * be returned back to the parent.
> */
> if (!housekeeping_enabled(HK_TYPE_KERNEL_NOISE) ||
> - (new_prs == parent_prs) ||
> - ((new_prs == PRS_ROOT) && (!del_cpus || cpumask_empty(del_cpus))))
> + (new_prs == parent_prs) || (!del_cpus && (new_prs == PRS_ROOT)))
> return false;
>
> /*
> @@ -1466,20 +1468,29 @@ static bool prstate_housekeeping_conflict(int new_prs, int parent_prs,
> if (parent_prs == PRS_ISOLATED)
> swap(new_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;
>
> + /*
> + * Check if del_cpus may contain a CPU that can become an active
> + * housekeeping CPU again.
> + */
> + if (del_cpus) {
> + cpumask_and(full_hk_cpus, housekeeping_cpumask(HK_TYPE_KERNEL_NOISE),
> + housekeeping_cpumask(HK_TYPE_DOMAIN_BOOT));
> + if (cpumask_first_and_and(del_cpus, full_hk_cpus,
> + cpu_active_mask) < nr_cpu_ids) {
> + res = false;
> + goto out;
> + }
> + }
> +
> 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);
> res = !cpumask_weight_andnot(full_hk_cpus, new_cpus);
> -
> +out:
> free_cpumask_var(full_hk_cpus);
> return res;
> }
> @@ -2486,23 +2497,29 @@ static void partition_cpus_change(struct cpuset *cs, struct cpuset *trialcs,
> struct tmpmasks *tmp)
> {
> enum prs_errcode prs_err;
> + int parent_prs = is_remote_partition(cs)
> + ? PRS_ROOT : parent_cs(cs)->partition_root_state;
>
> if (cs_is_member(cs))
> return;
>
> prs_err = validate_partition(cs, trialcs);
> - if (!prs_err && !cpumask_empty(trialcs->effective_xcpus)) {
> + if (!prs_err && (parent_prs > 0) &&
> + !cpumask_empty(trialcs->effective_xcpus)) {
> int prs = cs->partition_root_state;
> - int parent_prs = is_remote_partition(cs)
> - ? PRS_ROOT : parent_cs(cs)->partition_root_state;
> +
> /*
> * Check for housekeeping conflict by passing the whole
> * effective_xcpus as well as those CPUs that are to be
> * removed from cs as delmask.
> */
> - cpumask_andnot(tmp->delmask, cs->effective_xcpus, trialcs->effective_xcpus);
> - if (parent_prs > 0 &&
> - prstate_housekeeping_conflict(prs, parent_prs,
> + if (prs > 0)
> + cpumask_andnot(tmp->delmask, cs->effective_xcpus,
> + trialcs->effective_xcpus);
> + else
> + cpumask_clear(tmp->delmask);
> +
> + if (prstate_housekeeping_conflict(prs, parent_prs,
> trialcs->effective_xcpus, tmp->delmask))
> prs_err = PERR_HKEEPING;
> }
Reviewed-by: Guopeng Zhang <zhangguopeng@kylinos.cn>
Thanks,
Guopeng
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH-next v3 07/11] cgroup/cpuset: Make sure compute_partition_effective_cpumask() is only called for valid partition
2026-10-25 21:44 ` [PATCH-next v3 07/11] cgroup/cpuset: Make sure compute_partition_effective_cpumask() is only called for valid partition Waiman Long
@ 2026-10-26 7:14 ` Guopeng Zhang
0 siblings, 0 replies; 23+ messages in thread
From: Guopeng Zhang @ 2026-10-26 7:14 UTC (permalink / raw)
To: Waiman Long, Ridong Chen, Tejun Heo, Johannes Weiner,
Michal Koutný,
Shuah Khan
Cc: cgroups, linux-kernel, linux-kselftest
在 2026/10/26 05:44, Waiman Long 写道:
> With visual inspection to make sure that
> compute_partition_effective_cpumask() is only called for valid partition
> root, it was found that the function can be called for an invalidated
> partition near the end of cpuset_hotplug_update_tasks(). When partcmd is
> set, update_parent_effective_cpumask() is called to either invalidate a
> valid partition or with partcmd_update to check if an invalid partition
> can be switched back to valid. In both cases, cpuset_force_rebuild()
> should be invoked. However, compute_partition_effective_cpumask()
> should only be called when an invalid partition is switched back to
> valid. When a partition is invalidated, compute_effective_cpumask()
> should be called instead.
>
> Also add a WARN_ON_ONCE() in compute_partition_effective_cpumask() to
> ensure that a valid partition root is passed in.
>
> Fixes: 72c6303acfa1 ("cgroup/cpuset: Take isolated CPUs out of workqueue unbound cpumask")
> Signed-off-by: Waiman Long <longman@redhat.com>
> ---
> kernel/cgroup/cpuset.c | 7 ++++++-
> 1 file changed, 6 insertions(+), 1 deletion(-)
>
> diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
> index 8903e441ecc1..53fa3970946e 100644
> --- a/kernel/cgroup/cpuset.c
> +++ b/kernel/cgroup/cpuset.c
> @@ -2163,6 +2163,8 @@ static void compute_partition_effective_cpumask(struct cpuset *cs,
> struct cpuset *child;
> bool populated = partition_is_populated(cs, NULL);
>
> + WARN_ON_ONCE(!is_partition_valid(cs));
> +
> /*
> * Check child partition roots to see if they should be
> * invalidated when
> @@ -4035,9 +4037,12 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs, struct tmpmasks *tmp)
>
> if (partcmd >= 0) {
> update_parent_effective_cpumask(cs, partcmd, NULL, tmp);
> - if ((partcmd == partcmd_invalidate) || is_partition_valid(cs)) {
> + if (is_partition_valid(cs)) {
> compute_partition_effective_cpumask(cs, &new_cpus);
> cpuset_force_rebuild();
> + } else if (partcmd == partcmd_invalidate) {
> + compute_effective_cpumask(&new_cpus, cs, parent);
> + cpuset_force_rebuild();
> }
> }
>
Acked-by: Guopeng Zhang <zhangguopeng@kylinos.cn>
Thanks,
Guopeng
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH-next v3 08/11] cgroup/cpuset: Release invalid isolated CPUs in compute_partition_effective_cpumask()
2026-10-25 21:44 ` [PATCH-next v3 08/11] cgroup/cpuset: Release invalid isolated CPUs in compute_partition_effective_cpumask() Waiman Long
@ 2026-10-26 8:49 ` Guopeng Zhang
0 siblings, 0 replies; 23+ messages in thread
From: Guopeng Zhang @ 2026-10-26 8:49 UTC (permalink / raw)
To: Waiman Long, Ridong Chen, Tejun Heo, Johannes Weiner,
Michal Koutný,
Shuah Khan
Cc: cgroups, linux-kernel, linux-kselftest
在 2026/10/26 05:44, Waiman Long 写道:
> It is found by sashiko that when a child isolated partition with parent
> root partition is invalidated in compute_partition_effective_cpumask(),
> the switching of the isolated exclusive CPUs from child to parent will
> not be reflected in isolated_cpus leading to CPU leakage. Fix that by
> calling isolated_cpus_update() when that happens.
>
> Also simplify compute_partition_effective_cpumask() by doing
> child partition invalidation only for the special case of no more
> effective_cpus left in a populated parent. The other case doesn't
> actually warrant invalidation at this stage and subsequent iteration
> in update_cpumasks_hier() and update_parent_effective_cpumask() for
> that child should handle that.
>
> Also drop the make_partition_invalid() helper by inlining it into
> compute_partition_effective_cpumask() as it is the only caller.
>
> A new test case is add to test_cpuset_prs.sh to verify this.
>
> Fixes: 11e5f407b64a ("cgroup/cpuset: Keep track of CPUs in isolated partitions")
> Signed-off-by: Waiman Long <longman@redhat.com>
> ---
> kernel/cgroup/cpuset.c | 46 +++++++------------
> .../selftests/cgroup/test_cpuset_prs.sh | 4 ++
> 2 files changed, 21 insertions(+), 29 deletions(-)
>
> diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
> index 53fa3970946e..6d224e614e74 100644
> --- a/kernel/cgroup/cpuset.c
> +++ b/kernel/cgroup/cpuset.c
> @@ -254,15 +254,6 @@ static inline bool cs_is_member(const struct cpuset *cs)
> return cs->partition_root_state == PRS_MEMBER;
> }
>
> -/*
> - * Callers should hold callback_lock to modify partition_root_state.
> - */
> -static inline void make_partition_invalid(struct cpuset *cs)
> -{
> - if (cs->partition_root_state > 0)
> - cs->partition_root_state = -cs->partition_root_state;
> -}
> -
> /*
> * Send notification event of whenever partition_root_state changes.
> */
> @@ -2153,8 +2144,6 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd,
> * or update_cpumasks_hier() where parent and children are modified
> * successively, we don't need to call update_parent_effective_cpumask()
> * and the child's effective_cpus will be updated in later iterations.
> - *
> - * Note that rcu_read_lock() is assumed to be held.
> */
> static void compute_partition_effective_cpumask(struct cpuset *cs,
> struct cpumask *new_ecpus)
> @@ -2166,15 +2155,16 @@ static void compute_partition_effective_cpumask(struct cpuset *cs,
> WARN_ON_ONCE(!is_partition_valid(cs));
>
> /*
> - * Check child partition roots to see if they should be
> - * invalidated when
> - * 1) child effective_xcpus not a subset of new
> - * excluisve_cpus
> - * 2) All the effective_cpus will be used up and cp
> - * has tasks
> + * Check child partition roots to see if they should be invalidated
> + * when all the effective_cpus will be used up and cs is populated.
> + * It is better to sacrifice a child partition root than invalidating
> + * the whole subtree.
> + *
> + * Return early if the effective_cpus is already empty.
> */
> compute_excpus(cs, new_ecpus);
> - cpumask_and(new_ecpus, new_ecpus, cpu_active_mask);
> + if (!cpumask_and(new_ecpus, new_ecpus, cpu_active_mask))
> + return;
>
> rcu_read_lock();
> cpuset_for_each_child(child, css, cs) {
> @@ -2186,22 +2176,20 @@ static void compute_partition_effective_cpumask(struct cpuset *cs,
> * partition root.
> */
> WARN_ON_ONCE(is_remote_partition(child));
> - WRITE_ONCE(child->prs_err, 0);
> - if (!cpumask_subset(child->effective_xcpus,
> - cs->effective_xcpus))
> - WRITE_ONCE(child->prs_err, PERR_INVCPUS);
> - else if (populated &&
> - cpumask_subset(new_ecpus, child->effective_xcpus))
> - WRITE_ONCE(child->prs_err, PERR_NOCPUS);
> -
> - if (child->prs_err) {
> + if (populated && cpumask_subset(new_ecpus, child->effective_xcpus)) {
> int old_prs = child->partition_root_state;
> + int parent_prs = cs->partition_root_state;
>
> /*
> - * Invalidate child partition
> + * Invalidate child partition and update isolated_cpus
> + * if necessary.
> */
> spin_lock_irq(&callback_lock);
> - make_partition_invalid(child);
> + WRITE_ONCE(child->prs_err, PERR_NOCPUS);
> + if ((old_prs == PRS_ISOLATED) && (parent_prs == PRS_ROOT))
> + isolated_cpus_update(PRS_ISOLATED, PRS_ROOT,
> + child->effective_xcpus);
> + child->partition_root_state = -old_prs;
> spin_unlock_irq(&callback_lock);
> notify_partition_change(child, old_prs);
> continue;
> diff --git a/tools/testing/selftests/cgroup/test_cpuset_prs.sh b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
> index f0d98da3cfd8..d7d0bef0a17a 100755
> --- a/tools/testing/selftests/cgroup/test_cpuset_prs.sh
> +++ b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
> @@ -518,6 +518,10 @@ TEST_MATRIX=(
> " CX0-4:P2 CX2-4:P2 CX3-4:P2 . . X2 . . 0 A1:0-1,3-4|A2:2|A3:2 \
> A1:P2|A2:P2|A3:P-2 0-4"
>
> + # When an isolated partition child with root partition parent is invalidated,
> + # its effective xcpus will be removed from isolated_cpus.
> + " C1-4:P1 C3-4:P2 . . T:C3-4 . . . 0 A1:3-4|A2:3-4 A1:P1|A2:P-2 ."
> +
> #
> # old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate ISOLCPUS
> # ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------ --------
Reviewed-by: Guopeng Zhang <zhangguopeng@kylinos.cn>
Tested-by: Guopeng Zhang <zhangguopeng@kylinos.cn>
Thanks,
Guopeng
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH-next v3.1 09/11] cgroup/cpuset: Release invalid isolated CPUs with a shrunk root partition parent
2026-10-25 23:53 ` [PATCH-next v3.1 " Waiman Long
@ 2026-10-26 8:51 ` Guopeng Zhang
0 siblings, 0 replies; 23+ messages in thread
From: Guopeng Zhang @ 2026-10-26 8:51 UTC (permalink / raw)
To: Waiman Long, Ridong Chen, Tejun Heo, Johannes Weiner,
Michal Koutný,
Shuah Khan
Cc: cgroups, linux-kernel, linux-kselftest
在 2026/10/26 07:53, Waiman Long 写道:
> It is also reported by sashiko that there may be leakages of isolated
> CPUs when a root partition parent shrinks its effective_xcpus with
> isolated partition children. Leakage here means that isolated_cpus may
> contain extra CPUs that are not in any isolated partitions and not boot
> time isolated. Those CPUs cannot be used in any isolated partitions
> anymore.
>
> There are 2 different cases here. The effective_xcpus becomes empty and
> the isolated partition should be invalidated in this case. Alternatively
> some of its effective_xcpus are not in parent's effective xcpus but
> still have some valid effective_xcpus left. The isolated partition can
> remain valid. In both cases, the invalid isolated CPUs will have to be
> released from isolated_cpus if appropriate.
>
> Fix that by calling isolated_cpus_release() to release those isolated
> CPUs from isolated_cpus.
>
> New test cases are added to test_cpuset_prs.sh to test this.
>
> Fixes: 11e5f407b64a ("cgroup/cpuset: Keep track of CPUs in isolated partitions")
> Signed-off-by: Waiman Long <longman@redhat.com>
> ---
> kernel/cgroup/cpuset.c | 16 +++++++++++++++-
> .../testing/selftests/cgroup/test_cpuset_prs.sh | 6 ++++++
> 2 files changed, 21 insertions(+), 1 deletion(-)
>
> [v3.1] Fix the typo "cs->effective_cpus" => "cs->effective_xcpus".
>
> diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
> index 6d224e614e74..9a2a6f727929 100644
> --- a/kernel/cgroup/cpuset.c
> +++ b/kernel/cgroup/cpuset.c
> @@ -2012,8 +2012,22 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd,
> *
> * A partition error happens when parent has tasks and all
> * its effective CPUs will have to be distributed out.
> + *
> + * For an isolated partition with a shrunk root partition parent
> + * we may need to return some or all of its isolated CPUs.
> */
> - if (nocpu || cpumask_empty(tmp->delmask)) {
> + nocpu |= cpumask_empty(tmp->delmask);
> + if ((parent_prs == PRS_ROOT) && (old_prs == PRS_ISOLATED)) {
> + cpumask_clear(tmp->addmask);
> + if (nocpu)
> + cpumask_copy(tmp->addmask, cs->effective_xcpus);
> + else if (!cpumask_subset(cs->effective_xcpus, parent->effective_xcpus))
> + cpumask_andnot(tmp->addmask, cs->effective_xcpus,
> + parent->effective_xcpus);
> + if (!cpumask_empty(tmp->addmask))
> + isolated_cpus_release(cs, tmp->addmask);
> + }
> + if (nocpu) {
> part_error = PERR_NOCPUS;
> if (is_partition_valid(cs))
> adding = cpumask_and(tmp->addmask,
> diff --git a/tools/testing/selftests/cgroup/test_cpuset_prs.sh b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
> index d7d0bef0a17a..ceb1a3b4f94e 100755
> --- a/tools/testing/selftests/cgroup/test_cpuset_prs.sh
> +++ b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
> @@ -522,6 +522,12 @@ TEST_MATRIX=(
> # its effective xcpus will be removed from isolated_cpus.
> " C1-4:P1 C3-4:P2 . . T:C3-4 . . . 0 A1:3-4|A2:3-4 A1:P1|A2:P-2 ."
>
> + # When a root partition parent with an isolated partition child is shrunk
> + # so that its effective xcpus is no longer a superset of its child effective
> + # xcpus, the invalid isolated CPUs should be removed from cpuset.cpus.isolated.
> + " C1-4:P1 C3-4:P2 . . T:C4-6 . . . 0 A1:5-6|A2:4 A1:P1|A2:P2 4"
> + " C1-4:P1 C1-2:P2 . . T:C4-6 . . . 0 A1:4-6|A2:4-6 A1:P1|A2:P-2 ."
> +
> #
> # old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate ISOLCPUS
> # ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------ --------
Reviewed-by: Guopeng Zhang <zhangguopeng@kylinos.cn>
Tested-by: Guopeng Zhang <zhangguopeng@kylinos.cn>
Thanks,
Guopeng
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH-next v3 10/11] cgroup/cpuset: Relocate compute_*effective_cpumask() and compute_effective_nodemask()
2026-10-25 21:44 ` [PATCH-next v3 10/11] cgroup/cpuset: Relocate compute_*effective_cpumask() and compute_effective_nodemask() Waiman Long
@ 2026-10-26 9:08 ` Guopeng Zhang
0 siblings, 0 replies; 23+ messages in thread
From: Guopeng Zhang @ 2026-10-26 9:08 UTC (permalink / raw)
To: Waiman Long, Ridong Chen, Tejun Heo, Johannes Weiner,
Michal Koutný,
Shuah Khan
Cc: cgroups, linux-kernel, linux-kselftest
在 2026/10/26 05:44, Waiman Long 写道:
> Relocate compute_partition_effective_cpumask(),
> compute_effective_cpumask(), and compute_effective_nodemask() together so
> that compute_effective_cpumask() and compute_partition_effective_cpumask()
> can be further integrated together. There is no functional change.
>
> Signed-off-by: Waiman Long <longman@redhat.com>
> ---
> kernel/cgroup/cpuset.c | 212 ++++++++++++++++++++---------------------
> 1 file changed, 106 insertions(+), 106 deletions(-)
>
> diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
> index 3daab2381f04..f37d929616a6 100644
> --- a/kernel/cgroup/cpuset.c
> +++ b/kernel/cgroup/cpuset.c
> @@ -1135,43 +1135,6 @@ void cpuset_update_tasks_cpumask(struct cpuset *cs, struct cpumask *new_cpus)
> css_task_iter_end(&it);
> }
>
> -/**
> - * compute_effective_cpumask - Compute the effective cpumask of the cpuset
> - * @new_cpus: the temp variable for the new effective_cpus mask
> - * @cs: the cpuset the need to recompute the new effective_cpus mask
> - * @parent: the parent cpuset
> - *
> - * For v2, the parent's effective_cpus is inherited if cpumask is empty.
> - * The result is valid only if the given cpuset isn't a partition root.
> - */
> -static void compute_effective_cpumask(struct cpumask *new_cpus,
> - struct cpuset *cs, struct cpuset *parent)
> -{
> - bool has_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);
> -}
> -
> -/**
> - * compute_effective_nodemask - Compute the effective nodemask of the cpuset
> - * @new_mems: the temp variable for the new effective_mems mask
> - * @cs: the cpuset the need to recompute the new effective_mems mask
> - * @parent: the parent cpuset
> - *
> - * For v2, the parent's effective_mems is inherited if nodemask is empty.
> - */
> -static void compute_effective_nodemask(nodemask_t *new_mems,
> - struct cpuset *cs, struct cpuset *parent)
> -{
> - bool has_mems;
> -
> - has_mems = nodes_and(*new_mems, cs->mems_allowed, parent->effective_mems);
> - if (!has_mems && is_in_v2_mode())
> - nodes_copy(*new_mems, parent->effective_mems);
> -}
> -
> /*
> * Commands for update_parent_effective_cpumask
> */
> @@ -1622,6 +1585,112 @@ static int compute_trialcs_excpus(struct cpuset *trialcs, struct cpuset *cs)
> return rm_siblings_excl_cpus(parent, cs, excpus);
> }
>
> +/**
> + * compute_partition_effective_cpumask - compute effective_cpus for partition
> + * @cs: partition root cpuset
> + * @new_ecpus: previously computed effective_cpus to be updated
> + *
> + * Compute the effective_cpus of a partition root by scanning effective_xcpus
> + * of child partition roots and excluding their effective_xcpus.
> + *
> + * This has the side effect of invalidating valid child partition roots,
> + * if necessary. Since it is called from either cpuset_hotplug_update_tasks()
> + * or update_cpumasks_hier() where parent and children are modified
> + * successively, we don't need to call update_parent_effective_cpumask()
> + * and the child's effective_cpus will be updated in later iterations.
> + */
> +static void compute_partition_effective_cpumask(struct cpuset *cs,
> + struct cpumask *new_ecpus)
> +{
> + struct cgroup_subsys_state *css;
> + struct cpuset *child;
> + bool populated = partition_is_populated(cs, NULL);
> +
> + WARN_ON_ONCE(!is_partition_valid(cs));
> +
> + /*
> + * Check child partition roots to see if they should be invalidated
> + * when all the effective_cpus will be used up and cs is populated.
> + * It is better to sacrifice a child partition root than invalidating
> + * the whole subtree.
> + *
> + * Return early if the effective_cpus is already empty.
> + */
> + compute_excpus(cs, new_ecpus);
> + if (!cpumask_and(new_ecpus, new_ecpus, cpu_active_mask))
> + return;
> +
> + rcu_read_lock();
> + cpuset_for_each_child(child, css, cs) {
> + if (!is_partition_valid(child))
> + continue;
> +
> + /*
> + * There shouldn't be a remote partition underneath another
> + * partition root.
> + */
> + WARN_ON_ONCE(is_remote_partition(child));
> + if (populated && cpumask_subset(new_ecpus, child->effective_xcpus)) {
> + int old_prs = child->partition_root_state;
> + int parent_prs = cs->partition_root_state;
> +
> + /*
> + * Invalidate child partition and update isolated_cpus
> + * if necessary.
> + */
> + spin_lock_irq(&callback_lock);
> + WRITE_ONCE(child->prs_err, PERR_NOCPUS);
> + if ((old_prs == PRS_ISOLATED) && (parent_prs == PRS_ROOT))
> + isolated_cpus_update(PRS_ISOLATED, PRS_ROOT,
> + child->effective_xcpus);
> + child->partition_root_state = -old_prs;
> + spin_unlock_irq(&callback_lock);
> + notify_partition_change(child, old_prs);
> + continue;
> + }
> + cpumask_andnot(new_ecpus, new_ecpus,
> + child->effective_xcpus);
> + }
> + rcu_read_unlock();
> +}
> +
> +/**
> + * compute_effective_cpumask - Compute the effective cpumask of the cpuset
> + * @new_cpus: the temp variable for the new effective_cpus mask
> + * @cs: the cpuset the need to recompute the new effective_cpus mask
> + * @parent: the parent cpuset
> + *
> + * For v2, the parent's effective_cpus is inherited if cpumask is empty.
> + * The result is valid only if the given cpuset isn't a partition root.
> + */
> +static void compute_effective_cpumask(struct cpumask *new_cpus,
> + struct cpuset *cs, struct cpuset *parent)
> +{
> + bool has_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);
> +}
> +
> +/**
> + * compute_effective_nodemask - Compute the effective nodemask of the cpuset
> + * @new_mems: the temp variable for the new effective_mems mask
> + * @cs: the cpuset the need to recompute the new effective_mems mask
> + * @parent: the parent cpuset
> + *
> + * For v2, the parent's effective_mems is inherited if nodemask is empty.
> + */
> +static void compute_effective_nodemask(nodemask_t *new_mems,
> + struct cpuset *cs, struct cpuset *parent)
> +{
> + bool has_mems;
> +
> + has_mems = nodes_and(*new_mems, cs->mems_allowed, parent->effective_mems);
> + if (!has_mems && is_in_v2_mode())
> + nodes_copy(*new_mems, parent->effective_mems);
> +}
> +
> /*
> * remote_partition_enable - Enable current cpuset as a remote partition root
> * @cs: the cpuset to update
> @@ -2145,75 +2214,6 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd,
> return 0;
> }
>
> -/**
> - * compute_partition_effective_cpumask - compute effective_cpus for partition
> - * @cs: partition root cpuset
> - * @new_ecpus: previously computed effective_cpus to be updated
> - *
> - * Compute the effective_cpus of a partition root by scanning effective_xcpus
> - * of child partition roots and excluding their effective_xcpus.
> - *
> - * This has the side effect of invalidating valid child partition roots,
> - * if necessary. Since it is called from either cpuset_hotplug_update_tasks()
> - * or update_cpumasks_hier() where parent and children are modified
> - * successively, we don't need to call update_parent_effective_cpumask()
> - * and the child's effective_cpus will be updated in later iterations.
> - */
> -static void compute_partition_effective_cpumask(struct cpuset *cs,
> - struct cpumask *new_ecpus)
> -{
> - struct cgroup_subsys_state *css;
> - struct cpuset *child;
> - bool populated = partition_is_populated(cs, NULL);
> -
> - WARN_ON_ONCE(!is_partition_valid(cs));
> -
> - /*
> - * Check child partition roots to see if they should be invalidated
> - * when all the effective_cpus will be used up and cs is populated.
> - * It is better to sacrifice a child partition root than invalidating
> - * the whole subtree.
> - *
> - * Return early if the effective_cpus is already empty.
> - */
> - compute_excpus(cs, new_ecpus);
> - if (!cpumask_and(new_ecpus, new_ecpus, cpu_active_mask))
> - return;
> -
> - rcu_read_lock();
> - cpuset_for_each_child(child, css, cs) {
> - if (!is_partition_valid(child))
> - continue;
> -
> - /*
> - * There shouldn't be a remote partition underneath another
> - * partition root.
> - */
> - WARN_ON_ONCE(is_remote_partition(child));
> - if (populated && cpumask_subset(new_ecpus, child->effective_xcpus)) {
> - int old_prs = child->partition_root_state;
> - int parent_prs = cs->partition_root_state;
> -
> - /*
> - * Invalidate child partition and update isolated_cpus
> - * if necessary.
> - */
> - spin_lock_irq(&callback_lock);
> - WRITE_ONCE(child->prs_err, PERR_NOCPUS);
> - if ((old_prs == PRS_ISOLATED) && (parent_prs == PRS_ROOT))
> - isolated_cpus_update(PRS_ISOLATED, PRS_ROOT,
> - child->effective_xcpus);
> - child->partition_root_state = -old_prs;
> - spin_unlock_irq(&callback_lock);
> - notify_partition_change(child, old_prs);
> - continue;
> - }
> - cpumask_andnot(new_ecpus, new_ecpus,
> - child->effective_xcpus);
> - }
> - rcu_read_unlock();
> -}
> -
> /*
> * update_cpumasks_hier - Update effective cpumasks and tasks in the subtree
> * @cs: the cpuset to consider
Acked-by: Guopeng Zhang <zhangguopeng@kylinos.cn>
Thanks,
Guopeng
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH-next v3 11/11] cgroup/cpuset: Integrate compute_partition_effective_cpumask() into compute_effective_cpumask()
2026-10-25 21:44 ` [PATCH-next v3 11/11] cgroup/cpuset: Integrate compute_partition_effective_cpumask() into compute_effective_cpumask() Waiman Long
@ 2026-10-26 9:44 ` Guopeng Zhang
0 siblings, 0 replies; 23+ messages in thread
From: Guopeng Zhang @ 2026-10-26 9:44 UTC (permalink / raw)
To: Waiman Long, Ridong Chen, Tejun Heo, Johannes Weiner,
Michal Koutný,
Shuah Khan
Cc: cgroups, linux-kernel, linux-kselftest
在 2026/10/26 05:44, Waiman Long 写道:
> Update compute_effective_cpumask() to call
> compute_partition_effective_cpumask() directly if the given cpuset is
> a valid remote or local partition root. This simplifies the call sites
> where they are being involved. No functional change is expected.
>
> Suggested-by: Ridong Chen <ridong.chen@linux.dev>
> Signed-off-by: Waiman Long <longman@redhat.com>
> ---
> kernel/cgroup/cpuset.c | 37 ++++++++++---------------------------
> 1 file changed, 10 insertions(+), 27 deletions(-)
>
> diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
> index f37d929616a6..2598a0e2d667 100644
> --- a/kernel/cgroup/cpuset.c
> +++ b/kernel/cgroup/cpuset.c
> @@ -1606,8 +1606,6 @@ static void compute_partition_effective_cpumask(struct cpuset *cs,
> struct cpuset *child;
> bool populated = partition_is_populated(cs, NULL);
>
> - WARN_ON_ONCE(!is_partition_valid(cs));
> -
> /*
> * Check child partition roots to see if they should be invalidated
> * when all the effective_cpus will be used up and cs is populated.
> @@ -1668,6 +1666,10 @@ static void compute_effective_cpumask(struct cpumask *new_cpus,
> {
> 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);
> @@ -2268,10 +2270,7 @@ static void update_cpumasks_hier(struct cpuset *cs, struct tmpmasks *tmp,
> remote = (new_prs == old_prs);
> }
>
> - if (remote || (is_partition_valid(parent) && is_partition_valid(cp)))
> - compute_partition_effective_cpumask(cp, tmp->new_cpus);
> - else
> - compute_effective_cpumask(tmp->new_cpus, cp, parent);
> + compute_effective_cpumask(tmp->new_cpus, cp, parent);
>
> if (remote)
> goto get_css; /* Ready to update cpuset data */
> @@ -2352,10 +2351,7 @@ static void update_cpumasks_hier(struct cpuset *cs, struct tmpmasks *tmp,
> */
> 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);
> + compute_effective_cpumask(tmp->new_cpus, cp, parent);
> }
> }
>
> @@ -3987,7 +3983,6 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs, struct tmpmasks *tmp)
> static nodemask_t new_mems;
> bool cpus_updated;
> bool mems_updated;
> - bool remote;
> int partcmd = -1;
> struct cpuset *parent;
>
> @@ -3999,21 +3994,12 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs, struct tmpmasks *tmp)
> if (!tmp || !cs->partition_root_state)
> goto update_tasks;
>
> - /*
> - * Compute effective_cpus for valid partition root, may invalidate
> - * child partition roots if necessary.
> - */
> - remote = is_remote_partition(cs);
> - if (remote || (is_partition_valid(cs) && is_partition_valid(parent)))
> - compute_partition_effective_cpumask(cs, &new_cpus);
> -
> - if (remote && (cpumask_empty(subpartitions_cpus) ||
> - (cpumask_empty(&new_cpus) &&
> - partition_is_populated(cs, NULL)))) {
> + if (is_remote_partition(cs) && (cpumask_empty(subpartitions_cpus) ||
> + (cpumask_empty(&new_cpus) &&
> + partition_is_populated(cs, NULL)))) {
> WRITE_ONCE(cs->prs_err, PERR_HOTPLUG);
> remote_partition_disable(cs, tmp);
> compute_effective_cpumask(&new_cpus, cs, parent);
> - remote = false;
> }
>
> /*
> @@ -4039,10 +4025,7 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs, struct tmpmasks *tmp)
>
> if (partcmd >= 0) {
> update_parent_effective_cpumask(cs, partcmd, NULL, tmp);
> - if (is_partition_valid(cs)) {
> - compute_partition_effective_cpumask(cs, &new_cpus);
> - cpuset_force_rebuild();
> - } else if (partcmd == partcmd_invalidate) {
> + if (is_partition_valid(cs) || (partcmd == partcmd_invalidate)) {
> compute_effective_cpumask(&new_cpus, cs, parent);
> cpuset_force_rebuild();
> }
Acked-by: Guopeng Zhang <zhangguopeng@kylinos.cn>
Thanks,
Guopeng
^ permalink raw reply [flat|nested] 23+ messages in thread
end of thread, other threads:[~2026-10-26 9:44 UTC | newest]
Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-25 21:44 [PATCH-next v3 00/11] cgroup/cpuset: Miscellaneous bug fixes & cleanup Waiman Long
2026-10-25 21:44 ` [PATCH-next v3 01/11] cgroup/cpuset: Find furthest isolated ancestor for invalidated isolated partition Waiman Long
2026-10-26 6:33 ` Guopeng Zhang
2026-10-25 21:44 ` [PATCH-next v3 02/11] cgroup/cpuset: Properly compute effective xcpus in update_parent_effective_cpumask() Waiman Long
2026-10-26 6:39 ` Guopeng Zhang
2026-10-25 21:44 ` [PATCH-next v3 03/11] cgroup/cpuset: Fix incorrect effective_cpus bug in update_cpumasks_hier() Waiman Long
2026-10-26 6:46 ` Guopeng Zhang
2026-10-25 21:44 ` [PATCH-next v3 04/11] cgroup/cpuset: Minor fixes to prstate_housekeeping_conflict() Waiman Long
2026-10-26 6:57 ` Guopeng Zhang
2026-10-25 21:44 ` [PATCH-next v3 05/11] selftests/cgroup: Check for a single range nohz_full CPUs before running test matrix Waiman Long
2026-10-25 21:44 ` [PATCH-next v3 06/11] cgroup/cpuset: Prevent recursive call of remote_cpus_update() via update_cpumasks_hier() Waiman Long
2026-10-25 21:44 ` [PATCH-next v3 07/11] cgroup/cpuset: Make sure compute_partition_effective_cpumask() is only called for valid partition Waiman Long
2026-10-26 7:14 ` Guopeng Zhang
2026-10-25 21:44 ` [PATCH-next v3 08/11] cgroup/cpuset: Release invalid isolated CPUs in compute_partition_effective_cpumask() Waiman Long
2026-10-26 8:49 ` Guopeng Zhang
2026-10-25 21:44 ` [PATCH-next v3 09/11] cgroup/cpuset: Release invalid isolated CPUs with a shrunk root partition parent Waiman Long
2026-10-25 23:53 ` [PATCH-next v3.1 " Waiman Long
2026-10-26 8:51 ` Guopeng Zhang
2026-10-25 23:56 ` [PATCH-next v3 " Waiman Long
2026-10-25 21:44 ` [PATCH-next v3 10/11] cgroup/cpuset: Relocate compute_*effective_cpumask() and compute_effective_nodemask() Waiman Long
2026-10-26 9:08 ` Guopeng Zhang
2026-10-25 21:44 ` [PATCH-next v3 11/11] cgroup/cpuset: Integrate compute_partition_effective_cpumask() into compute_effective_cpumask() Waiman Long
2026-10-26 9:44 ` Guopeng Zhang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®