* [RFC PATCH -v4 00/22] cpuset: rework local partition logic
@ 2026-11-01 11:41 Ridong Chen
2026-11-01 11:41 ` [RFC PATCH -v4 01/22] cpuset: add early empty cpumask check in partition_xcpus_add/del Ridong Chen
` (21 more replies)
0 siblings, 22 replies; 23+ messages in thread
From: Ridong Chen @ 2026-11-01 11:41 UTC (permalink / raw)
To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutn?
Cc: Guopeng Zhang, Ridong Chen, cgroups, linux-kernel, Ridong Chen
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=a, Size: 5452 bytes --]
From: Ridong Chen <chenridong@xiaomi.com>
There have been quite a few partition related fixes recently, most of
them caused by the scattered handling of partition state and the
isolated CPUs in different code paths. This convinces me again that the
rework of the local partition logic is necessary, so I am restarting
this series. As it has been a long time since the last posting, this
series is marked as RFC v4.
The current local partition implementation consolidates all operations
(enable, disable, invalidate, and update) within the large
update_parent_effective_cpumask() function, which exceeds 300 lines.
This monolithic approach has become increasingly difficult to understand
and maintain. Additionally, partition-related fields are updated in
multiple locations, leading to redundant code and potential corner case
oversights.
This patch series refactors the local partition logic by separating
operations into dedicated functions: local_partition_enable(),
local_partition_disable(), and local_partition_update(), creating
symmetry with the existing remote partition infrastructure.
After the rework:
- The code is reduced by about 90 lines (568 insertions, 657
deletions), and the 312-line update_parent_effective_cpumask() is
removed.
- Both local and remote partitions go through the same
partition_enable(), partition_disable() and partition_update()
helpers, and are validated by validate_local_partition() and
validate_remote_partition() on top of the common
validate_partition(). The structure and semantics of each operation
are much clearer.
- The partition fields are updated in a unified way. The direct writes
of partition_root_state are reduced from 6 places to 2, and the
writes of prs_err from 12 places to 3, most of them now done in
partition_state_update(). The isolation state of the CPUs given back
to the parent is settled in partition_xcpus_del(), so
isolated_cpus_release() is removed.
The series is organized as follows:
1. Infrastructure Preparation (Patches 1-3):
- Code cleanup and preparation for the refactoring work
2. Introduce partition operation helpers (Patches 4-6):
- Introduce out partition_enable(), partition_disable(), and
partition_update() functions.
3. Use new helpers for remote partition (Patches 7-9)
4. Local Partition Implementation (Patches 10-13):
- Separate update_parent_effective_cpumask() into dedicated functions:
* local_partition_enable()
* local_partition_disable()
* local_partition_update()
5. Optimization and Cleanup (Patches 14-22):
- Remove redundant partition-related operations
- Additional optimizations based on the new architecture
base-commit: 6d0933ae9d46674a6ea63a87e9e9ab4e662670c9
---
Changes in RFC v4:
1. Rebase on the next-20261028 and adapt patches 3-22 to the current
partition code.
2. Add patch 2 to settle the isolation state of CPUs given back to the
parent in partition_xcpus_del(), replacing isolated_cpus_release().
3. Fix several issues of the hotplug, prs_err and remote partition
update handling found in review and testing.
4. The issues fixed by Waiman's series [1] are also covered by this
rework, and its new test cases pass with this series applied.
[1] https://lore.kernel.org/all/20261030190701.78308-1-longman@redhat.com/
RESEND: https://lore.kernel.org/all/20251225123058.231765-1-chenridong@huaweicloud.com/
Changes in RESEND:
1. Rebase on the next-20251219
Changes from RFC v2:
1. Dropped the bugfix (already merged/fixed upstream)
2. Rebased onto next
3. Introduced partition_switch to handle root state switches
4. Directly use local_partition_disable()—no longer first introduce
local_partition_invalidate() before unifying the two
5. Incorporated modifications based on Longman's suggestions
Changes in RFC v1:
1. Added bugfix for root partition isolcpus at series start.
2. Completed helper function implementations when first introduced.
3. Split larger patches into smaller, more reviewable units.
4. Incorporated feedback from Longman.
Chen Ridong (21):
cpuset: add early empty cpumask check in partition_xcpus_add/del
cpuset: generalize the validate_partition() interface
cpuset: introduce partition_enable()
cpuset: introduce partition_disable()
cpuset: introduce partition_update()
cpuset: use partition_enable() for remote partition enablement
cpuset: use partition_disable() for remote partition disablement
cpuset: use partition_update() for remote partition update
cpuset: introduce local_partition_enable()
cpuset: introduce local_partition_disable()
cpuset: use local_partition_disable() to invalidate local partition
cpuset: introduce local_partition_update()
cpuset: remove update_parent_effective_cpumask
cpuset: remove redundant partition field updates
cpuset: simplify partition update logic for hotplug tasks
cpuset: use partition_disable for compute_partition_effective_cpumask
cpuset: use validate_local_partition in local_partition_enable
cpuset: introduce validate_remote_partition
cpuset: simplify the update_prstate() function
cpuset: remove prs_err clear when notify_partition_change
cpuset: remove unnecessary validation in partition_cpus_change
Ridong Chen (1):
cpuset: settle isolation state when CPUs are given back to parent
kernel/cgroup/cpuset.c | 1225 +++++++++++++++++++---------------------
1 file changed, 568 insertions(+), 657 deletions(-)
--
2.34.1
^ permalink raw reply [flat|nested] 23+ messages in thread
* [RFC PATCH -v4 01/22] cpuset: add early empty cpumask check in partition_xcpus_add/del
2026-11-01 11:41 [RFC PATCH -v4 00/22] cpuset: rework local partition logic Ridong Chen
@ 2026-11-01 11:41 ` Ridong Chen
2026-11-01 11:41 ` [RFC PATCH -v4 02/22] cpuset: settle isolation state when CPUs are given back to parent Ridong Chen
` (20 subsequent siblings)
21 siblings, 0 replies; 23+ messages in thread
From: Ridong Chen @ 2026-11-01 11:41 UTC (permalink / raw)
To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutn?
Cc: Guopeng Zhang, Ridong Chen, cgroups, linux-kernel, Chen Ridong,
Ridong Chen
From: Chen Ridong <chenridong@huawei.com>
Add a check for an empty cpumask at the start of partition_xcpus_add()
and partition_xcpus_del(). This allows the functions to return early,
avoiding unnecessary computation when there is no work to be done. With
these changes, partition_xcpus_add() and partition_xcpus_del() can be
called even if xcpus is empty. The caller no longer needs to check whether
xcpus is empty, eliminating many conditional statements.
Signed-off-by: Chen Ridong <chenridong@huawei.com>
Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
kernel/cgroup/cpuset.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 3219f927c35e..72e1df1295e1 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1315,10 +1315,13 @@ static void partition_xcpus_add(int new_prs, struct cpuset *parent,
{
WARN_ON_ONCE(new_prs < 0);
lockdep_assert_held(&callback_lock);
+
+ if (cpumask_empty(xcpus))
+ return;
+
if (!parent)
parent = &top_cpuset;
-
if (parent == &top_cpuset)
cpumask_or(subpartitions_cpus, subpartitions_cpus, xcpus);
@@ -1342,6 +1345,10 @@ static void partition_xcpus_del(int old_prs, struct cpuset *parent,
{
WARN_ON_ONCE(old_prs < 0);
lockdep_assert_held(&callback_lock);
+
+ if (cpumask_empty(xcpus))
+ return;
+
if (!parent)
parent = &top_cpuset;
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH -v4 02/22] cpuset: settle isolation state when CPUs are given back to parent
2026-11-01 11:41 [RFC PATCH -v4 00/22] cpuset: rework local partition logic Ridong Chen
2026-11-01 11:41 ` [RFC PATCH -v4 01/22] cpuset: add early empty cpumask check in partition_xcpus_add/del Ridong Chen
@ 2026-11-01 11:41 ` Ridong Chen
2026-11-01 11:41 ` [RFC PATCH -v4 03/22] cpuset: generalize the validate_partition() interface Ridong Chen
` (19 subsequent siblings)
21 siblings, 0 replies; 23+ messages in thread
From: Ridong Chen @ 2026-11-01 11:41 UTC (permalink / raw)
To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutn?
Cc: Guopeng Zhang, Ridong Chen, cgroups, linux-kernel, Ridong Chen
From: Ridong Chen <chenridong@xiaomi.com>
partition_xcpus_del() only updates isolated_cpus when the partition that
gives back the CPUs has a different state than its parent. CPUs that were
further distributed to descendant isolated partitions are not taken into
account, so isolated_cpus_release() has to be called afterwards in
several places to walk up the ancestors and fix isolated_cpus.
Make partition_xcpus_del() always set the isolation state of the given
back CPUs to the state of the parent that receives them. As the
effective_xcpus of a partition covers the CPUs distributed to its
descendants, the isolation state of those CPUs is settled at the same
time. Boot-time isolated CPUs are still preserved by isolated_cpu_update().
compute_partition_effective_cpumask() invalidates a child partition
without going through partition_xcpus_del(), so do the same there: the
CPUs of the invalidated child take the isolation state of its parent.
isolated_cpus_release() is no longer needed and is removed.
Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
kernel/cgroup/cpuset.c | 86 ++++++++++++------------------------------
1 file changed, 24 insertions(+), 62 deletions(-)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 72e1df1295e1..df9fc126e391 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1264,42 +1264,21 @@ static void isolated_cpus_update(int old_prs, int new_prs, struct cpumask *xcpus
}
/*
- * 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.
+ * isolated_cpus_set_state - Set the isolation state of @xcpus
+ * @prs: partition root state that @xcpus will belong to
+ * @xcpus: exclusive CPUs whose isolation state may change
*/
-static void isolated_cpus_release(struct cpuset *cs, struct cpumask *xcpus)
+static void isolated_cpus_set_state(int prs, 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 */
+ bool updated = false;
+ int cpu;
- spin_lock_irq(&callback_lock);
- isolated_cpus_update(PRS_ISOLATED, PRS_ROOT, xcpus);
- spin_unlock_irq(&callback_lock);
+ lockdep_assert_held(&callback_lock);
+ lockdep_assert_held(&cpuset_mutex);
+ for_each_cpu(cpu, xcpus)
+ updated |= isolated_cpu_update(prs, cpu);
+ if (updated)
+ update_housekeeping = true;
}
/*
@@ -1355,9 +1334,12 @@ static void partition_xcpus_del(int old_prs, struct cpuset *parent,
if (parent == &top_cpuset)
cpumask_andnot(subpartitions_cpus, subpartitions_cpus, xcpus);
- if (old_prs != parent->partition_root_state)
- isolated_cpus_update(old_prs, parent->partition_root_state,
- xcpus);
+ /*
+ * CPUs given back to parent take the parent's isolation state. This
+ * also covers CPUs that were further distributed to descendant
+ * isolated partitions.
+ */
+ isolated_cpus_set_state(parent->partition_root_state, xcpus);
cpumask_or(parent->effective_cpus, parent->effective_cpus, xcpus);
cpumask_and(parent->effective_cpus, parent->effective_cpus, cpu_active_mask);
@@ -1640,14 +1622,13 @@ static void compute_partition_effective_cpumask(struct cpuset *cs,
int parent_prs = cs->partition_root_state;
/*
- * Invalidate child partition and update isolated_cpus
- * if necessary.
+ * Invalidate child partition. Its CPUs, including those
+ * further distributed to its descendants, take the
+ * isolation state of cs.
*/
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);
+ isolated_cpus_set_state(parent_prs, child->effective_xcpus);
child->partition_root_state = -old_prs;
spin_unlock_irq(&callback_lock);
notify_partition_change(child, old_prs);
@@ -2090,21 +2071,8 @@ 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.
*/
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))
@@ -2327,18 +2295,12 @@ static void update_cpumasks_hier(struct cpuset *cs, struct tmpmasks *tmp,
/*
* When parent is not a valid partition root,
* child partition roots become invalid too.
- *
- * For a to be invalidated isolated partition,
- * call return_isolated_cpus() to remove the
- * relevant CPUs from isolated_cpus.
+ * The isolation state of their CPUs has been
+ * settled when the parent gave them back.
*/
if (is_partition_valid(cp))
new_prs = -old_prs;
- if (old_prs == PRS_ISOLATED) {
- cpumask_copy(tmp->delmask, cp->effective_xcpus);
- isolated_cpus_release(cp, tmp->delmask);
- }
WRITE_ONCE(cp->prs_err,
is_partition_invalid(parent)
? PERR_INVPARENT : PERR_NOTPART);
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH -v4 03/22] cpuset: generalize the validate_partition() interface
2026-11-01 11:41 [RFC PATCH -v4 00/22] cpuset: rework local partition logic Ridong Chen
2026-11-01 11:41 ` [RFC PATCH -v4 01/22] cpuset: add early empty cpumask check in partition_xcpus_add/del Ridong Chen
2026-11-01 11:41 ` [RFC PATCH -v4 02/22] cpuset: settle isolation state when CPUs are given back to parent Ridong Chen
@ 2026-11-01 11:41 ` Ridong Chen
2026-11-01 11:41 ` [RFC PATCH -v4 04/22] cpuset: introduce partition_enable() Ridong Chen
` (18 subsequent siblings)
21 siblings, 0 replies; 23+ messages in thread
From: Ridong Chen @ 2026-11-01 11:41 UTC (permalink / raw)
To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutn?
Cc: Guopeng Zhang, Ridong Chen, cgroups, linux-kernel, Chen Ridong,
Ridong Chen
From: Chen Ridong <chenridong@huawei.com>
Refactor validate_partition() to take the parent partition, the new
partition root state, the new effective exclusive CPUs and the CPUs to
be deleted, instead of a trial cpuset. A NULL parent stands for a
remote partition, whose CPUs come from top_cpuset. This makes the
interface flexible enough to handle both local and remote partition
validation.
The housekeeping conflict check is folded into validate_partition(). It
is skipped when the parent is not a valid partition root, as @cs can't
be a valid local partition in that case.
Use the new interface for local partition enablement in
update_parent_effective_cpumask() and in partition_cpus_change(),
replacing the open-coded checks there. The order of the checks is now
the same for both paths: empty CPUs, housekeeping conflict, then no CPU
left for tasks.
Signed-off-by: Chen Ridong <chenridong@huawei.com>
Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
kernel/cgroup/cpuset.c | 112 +++++++++++++++++++----------------------
1 file changed, 52 insertions(+), 60 deletions(-)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index df9fc126e391..df52f6e5e99f 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1438,6 +1438,41 @@ static bool prstate_housekeeping_conflict(int new_prs, int parent_prs,
return res;
}
+/**
+ * validate_partition - Validate a cpuset partition configuration
+ * @cs: The cpuset to validate
+ * @parent: Parent partition of @cs, NULL for remote partition
+ * @new_prs: The proposed new partition root state
+ * @new_excpus: The new effective exclusive CPUs mask to validate
+ * @del: exclusive CPUs to be deleted from @cs, can be NULL
+ *
+ * The housekeeping check is skipped if @parent is not a valid partition
+ * root as @cs can't be a valid local partition in that case.
+ *
+ * Return: PRS error code (0 if valid, non-zero error code if invalid)
+ */
+static enum prs_errcode validate_partition(struct cpuset *cs,
+ struct cpuset *parent, int new_prs,
+ struct cpumask *new_excpus, struct cpumask *del)
+{
+ int parent_prs = parent ? parent->partition_root_state : PRS_ROOT;
+
+ if (new_prs == PRS_MEMBER)
+ return PERR_NONE;
+
+ if (cpumask_empty(new_excpus))
+ return PERR_INVCPUS;
+
+ if ((parent_prs > 0) &&
+ prstate_housekeeping_conflict(new_prs, parent_prs, new_excpus, del))
+ return PERR_HKEEPING;
+
+ if (tasks_nocpu_error(parent_cs(cs), cs, new_excpus))
+ return PERR_NOCPUS;
+
+ return PERR_NONE;
+}
+
/*
* cpuset_update_sd_hk_unlock - Rebuild sched domains, update HK & unlock
*
@@ -1955,18 +1990,10 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd,
if ((cmd == partcmd_enable) || (cmd == partcmd_enablei)) {
new_prs = (cmd == partcmd_enable) ? PRS_ROOT : PRS_ISOLATED;
- /*
- * Enabling partition root is not allowed if its
- * effective_xcpus is empty.
- */
- if (cpumask_empty(tmp->delmask))
- return PERR_INVCPUS;
-
- if (prstate_housekeeping_conflict(new_prs, parent_prs, tmp->delmask, NULL))
- return PERR_HKEEPING;
-
- if (nocpu)
- return PERR_NOCPUS;
+ part_error = validate_partition(cs, parent, new_prs,
+ tmp->delmask, NULL);
+ if (part_error)
+ return part_error;
/*
* This function will only be called when all the preliminary
@@ -2432,32 +2459,6 @@ static int parse_cpuset_cpulist(const char *buf, struct cpumask *out_mask)
return 0;
}
-/**
- * validate_partition - Validate a cpuset partition configuration
- * @cs: The cpuset to validate
- * @trialcs: The trial cpuset containing proposed configuration changes
- *
- * If any validation check fails, the appropriate error code is set in the
- * cpuset's prs_err field.
- *
- * Return: PRS error code (0 if valid, non-zero error code if invalid)
- */
-static enum prs_errcode validate_partition(struct cpuset *cs, struct cpuset *trialcs)
-{
- struct cpuset *parent = parent_cs(cs);
-
- if (cs_is_member(trialcs))
- return PERR_NONE;
-
- if (cpumask_empty(trialcs->effective_xcpus))
- return PERR_INVCPUS;
-
- if (tasks_nocpu_error(parent, cs, trialcs->effective_xcpus))
- return PERR_NOCPUS;
-
- return PERR_NONE;
-}
-
/**
* partition_cpus_change - Handle partition state changes due to CPU mask updates
* @cs: The target cpuset being modified
@@ -2471,33 +2472,24 @@ 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;
+ struct cpuset *parent = is_remote_partition(cs) ? NULL : parent_cs(cs);
if (cs_is_member(cs))
return;
- prs_err = validate_partition(cs, trialcs);
- if (!prs_err && (parent_prs > 0) &&
- !cpumask_empty(trialcs->effective_xcpus)) {
- int prs = 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.
- */
- 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;
- }
+ /*
+ * Check for housekeeping conflict by passing the whole
+ * effective_xcpus as well as those CPUs that are to be
+ * removed from cs as delmask.
+ */
+ if (is_partition_valid(cs))
+ cpumask_andnot(tmp->delmask, cs->effective_xcpus,
+ trialcs->effective_xcpus);
+ else
+ cpumask_clear(tmp->delmask);
+ prs_err = validate_partition(cs, parent, trialcs->partition_root_state,
+ trialcs->effective_xcpus, tmp->delmask);
if (prs_err) {
WRITE_ONCE(cs->prs_err, prs_err);
trialcs->prs_err = prs_err;
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH -v4 04/22] cpuset: introduce partition_enable()
2026-11-01 11:41 [RFC PATCH -v4 00/22] cpuset: rework local partition logic Ridong Chen
` (2 preceding siblings ...)
2026-11-01 11:41 ` [RFC PATCH -v4 03/22] cpuset: generalize the validate_partition() interface Ridong Chen
@ 2026-11-01 11:41 ` Ridong Chen
2026-11-01 11:41 ` [RFC PATCH -v4 05/22] cpuset: introduce partition_disable() Ridong Chen
` (17 subsequent siblings)
21 siblings, 0 replies; 23+ messages in thread
From: Ridong Chen @ 2026-11-01 11:41 UTC (permalink / raw)
To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutn?
Cc: Guopeng Zhang, Ridong Chen, cgroups, linux-kernel, Chen Ridong,
Ridong Chen
From: Chen Ridong <chenridong@huawei.com>
Add partition_enable() to consolidate updates to key cpuset structures
during partition enablement, including:
1. remote_partition
2. effective_xcpus
3. partition_root_state
4. prs_err
Key operations performed:
1. Invokes partition_xcpus_add() to assign exclusive CPUs
2. Maintains remote partition flag
3. Syncs the effective_xcpus mask
4. Updates partition_root_state and prs_err
5. Triggers scheduler domain rebuilds
6. Sends partition change notifications
This helper also handles the transition between root and isolated
states of a valid partition. In that case the cpumasks are unchanged
and only isolated_cpus is updated.
A partition_state_update() helper is added to set partition_root_state
and prs_err, and to reset the partition data when the new state is not
a valid partition.
Signed-off-by: Chen Ridong <chenridong@huawei.com>
Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
kernel/cgroup/cpuset.c | 51 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 51 insertions(+)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index df52f6e5e99f..052a131731dd 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1716,6 +1716,57 @@ static void compute_effective_nodemask(nodemask_t *new_mems,
nodes_copy(*new_mems, parent->effective_mems);
}
+static void partition_state_update(struct cpuset *cs, int new_prs,
+ enum prs_errcode prs_err)
+{
+ lockdep_assert_held(&callback_lock);
+
+ cs->partition_root_state = new_prs;
+ WRITE_ONCE(cs->prs_err, prs_err);
+ if (!is_partition_valid(cs))
+ reset_partition_data(cs);
+}
+
+/**
+ * partition_enable - Transitions a cpuset to a partition root
+ * @cs: The cpuset to enable partition for
+ * @parent: Parent cpuset of @cs, NULL for remote parent
+ * @new_prs: New partition state to set
+ * @new_excpus: New effective exclusive CPUs mask for the partition
+ *
+ * Transitions a cpuset to a partition root, only for v2.
+ * It also supports the transition between root and isolated partition.
+ */
+static void partition_enable(struct cpuset *cs, struct cpuset *parent,
+ int new_prs, struct cpumask *new_excpus)
+{
+ int old_prs = cs->partition_root_state;
+
+ lockdep_assert_cpuset_lock_held();
+ WARN_ON_ONCE(new_prs <= 0);
+ WARN_ON_ONCE(!cpuset_v2());
+
+ if (old_prs == new_prs)
+ return;
+
+ spin_lock_irq(&callback_lock);
+ if (old_prs > 0) {
+ /* Switch between root and isolated, cpumasks are unchanged */
+ isolated_cpus_update(old_prs, new_prs, cs->effective_xcpus);
+ } else {
+ /* enable partition should only add exclusive cpus */
+ partition_xcpus_add(new_prs, parent, new_excpus);
+ /* enable remote partition */
+ if (!parent)
+ cs->remote_partition = true;
+ cpumask_copy(cs->effective_xcpus, new_excpus);
+ }
+ partition_state_update(cs, new_prs, PERR_NONE);
+ spin_unlock_irq(&callback_lock);
+ cpuset_force_rebuild();
+ notify_partition_change(cs, old_prs);
+}
+
/*
* remote_partition_enable - Enable current cpuset as a remote partition root
* @cs: the cpuset to update
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH -v4 05/22] cpuset: introduce partition_disable()
2026-11-01 11:41 [RFC PATCH -v4 00/22] cpuset: rework local partition logic Ridong Chen
` (3 preceding siblings ...)
2026-11-01 11:41 ` [RFC PATCH -v4 04/22] cpuset: introduce partition_enable() Ridong Chen
@ 2026-11-01 11:41 ` Ridong Chen
2026-11-01 11:41 ` [RFC PATCH -v4 06/22] cpuset: introduce partition_update() Ridong Chen
` (16 subsequent siblings)
21 siblings, 0 replies; 23+ messages in thread
From: Ridong Chen @ 2026-11-01 11:41 UTC (permalink / raw)
To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutn?
Cc: Guopeng Zhang, Ridong Chen, cgroups, linux-kernel, Chen Ridong,
Ridong Chen
From: Chen Ridong <chenridong@huawei.com>
Add partition_disable() to consolidate updates to key cpuset structures
during partition disablement, including:
1. remote_partition
2. effective_xcpus
3. partition_root_state
4. prs_err
Key operations performed:
1. Cleaning up remote_partition
2. Removing exclusive CPUs via partition_xcpus_del()
3. Recomputing effective exclusive CPUs mask
4. Updating partition state and error status
5. Triggering required scheduler domain rebuilds
6. Sending partition change notifications
Only the CPUs actually granted by the parent are given back, as the
parent may have shrunk. An invalid partition, or a partition under an
invalid parent, has already given back its exclusive CPUs, so there is
nothing to delete. A remote partition gets its CPUs from top_cpuset,
which owns all the CPUs.
Signed-off-by: Chen Ridong <chenridong@huawei.com>
Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
kernel/cgroup/cpuset.c | 39 +++++++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 052a131731dd..c0d6dd7297f1 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1767,6 +1767,45 @@ static void partition_enable(struct cpuset *cs, struct cpuset *parent,
notify_partition_change(cs, old_prs);
}
+/**
+ * partition_disable - Disable partition root state for a cpuset
+ * @cs: The cpuset to disable partition for
+ * @parent: Parent cpuset of @cs, NULL for remote parent
+ * @new_prs: New partition root state (should be non-positive)
+ * @prs_err: Error code to set if disabling due to validation failure
+ */
+static void partition_disable(struct cpuset *cs, struct cpuset *parent,
+ int new_prs, enum prs_errcode prs_err)
+{
+ int old_prs = cs->partition_root_state;
+
+ lockdep_assert_cpuset_lock_held();
+ WARN_ON_ONCE(new_prs > 0);
+ WARN_ON_ONCE(!cpuset_v2());
+
+ spin_lock_irq(&callback_lock);
+ cs->remote_partition = false;
+ /*
+ * Disable a partition should only delete exclusive cpus, and only
+ * those actually granted by the parent as the parent may have shrunk.
+ * An invalid partition or an invalid parent has already given back
+ * its exclusive CPUs, there is nothing to delete. A remote partition
+ * gets its CPUs from top_cpuset which owns all the CPUs.
+ */
+ if (is_partition_valid(cs) && (!parent || is_partition_valid(parent))) {
+ if (parent)
+ cpumask_and(cs->effective_xcpus, cs->effective_xcpus,
+ parent->effective_xcpus);
+ partition_xcpus_del(old_prs, parent, cs->effective_xcpus);
+ }
+ /* effective_xcpus may need to be changed */
+ compute_excpus(cs, cs->effective_xcpus);
+ partition_state_update(cs, new_prs, prs_err);
+ spin_unlock_irq(&callback_lock);
+ cpuset_force_rebuild();
+ notify_partition_change(cs, old_prs);
+}
+
/*
* remote_partition_enable - Enable current cpuset as a remote partition root
* @cs: the cpuset to update
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH -v4 06/22] cpuset: introduce partition_update()
2026-11-01 11:41 [RFC PATCH -v4 00/22] cpuset: rework local partition logic Ridong Chen
` (4 preceding siblings ...)
2026-11-01 11:41 ` [RFC PATCH -v4 05/22] cpuset: introduce partition_disable() Ridong Chen
@ 2026-11-01 11:41 ` Ridong Chen
2026-11-01 11:41 ` [RFC PATCH -v4 07/22] cpuset: use partition_enable() for remote partition enablement Ridong Chen
` (15 subsequent siblings)
21 siblings, 0 replies; 23+ messages in thread
From: Ridong Chen @ 2026-11-01 11:41 UTC (permalink / raw)
To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutn?
Cc: Guopeng Zhang, Ridong Chen, cgroups, linux-kernel, Chen Ridong,
Ridong Chen
From: Chen Ridong <chenridong@huawei.com>
Introduce partition_update() to centralize updates to key cpuset structures
during a partition update, including:
1. effective_xcpus
2. exclusive_cpus
Key operations performed:
1. Adding and removing exclusive CPUs via partition_xcpus_add()/del()
2. Synchronizing the effective exclusive CPUs mask
3. Updating the exclusive CPUs mask when modification is required
4. Triggering scheduler domain rebuilds
For a local partition, the CPUs to be added or deleted are restricted to
those in the parent's effective_xcpus, as the parent may have shrunk.
The function returns early if nothing is to be changed.
The partition root state is unchanged by this function, so no partition
change notification is sent.
Signed-off-by: Chen Ridong <chenridong@huawei.com>
Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
kernel/cgroup/cpuset.c | 51 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 51 insertions(+)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index c0d6dd7297f1..23ae548c3994 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1806,6 +1806,57 @@ static void partition_disable(struct cpuset *cs, struct cpuset *parent,
notify_partition_change(cs, old_prs);
}
+/**
+ * partition_update - Update an existing partition configuration
+ * @cs: The cpuset to update
+ * @prs: Partition root state (must be positive)
+ * @xcpus: New exclusive CPUs mask for the partition (NULL to keep current)
+ * @excpus: New effective exclusive CPUs mask
+ * @tmp: Temporary masks
+ *
+ * Updates partition-related fields. The tmp->addmask is the CPU mask that
+ * will be added to the subpartitions_cpus and removed from parent's
+ * effective_cpus, and the tmp->delmask vice versa.
+ */
+static void partition_update(struct cpuset *cs, int prs, struct cpumask *xcpus,
+ struct cpumask *excpus, struct tmpmasks *tmp)
+{
+ struct cpuset *parent;
+
+ lockdep_assert_cpuset_lock_held();
+ WARN_ON_ONCE(!cpuset_v2());
+ WARN_ON_ONCE(prs <= 0);
+
+ parent = is_remote_partition(cs) ? NULL : parent_cs(cs);
+
+ /*
+ * Only CPUs owned by the parent can be taken from or given back to
+ * it, the parent may have shrunk. A remote partition gets its CPUs
+ * from top_cpuset which owns all the CPUs.
+ */
+ if (parent) {
+ cpumask_and(tmp->addmask, tmp->addmask, parent->effective_xcpus);
+ cpumask_and(tmp->delmask, tmp->delmask, parent->effective_xcpus);
+ }
+ if (cpumask_empty(tmp->addmask) &&
+ cpumask_empty(tmp->delmask) &&
+ cpumask_equal(cs->effective_xcpus, excpus))
+ return;
+
+ spin_lock_irq(&callback_lock);
+ partition_xcpus_add(prs, parent, tmp->addmask);
+ partition_xcpus_del(prs, parent, tmp->delmask);
+ /*
+ * Need to update effective_xcpus and exclusive_cpus now as
+ * update_sibling_cpumasks() below may iterate back to the same cs.
+ */
+ cpumask_copy(cs->effective_xcpus, excpus);
+ if (xcpus)
+ cpumask_copy(cs->exclusive_cpus, xcpus);
+ spin_unlock_irq(&callback_lock);
+ cpuset_force_rebuild();
+}
+
/*
* remote_partition_enable - Enable current cpuset as a remote partition root
* @cs: the cpuset to update
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH -v4 07/22] cpuset: use partition_enable() for remote partition enablement
2026-11-01 11:41 [RFC PATCH -v4 00/22] cpuset: rework local partition logic Ridong Chen
` (5 preceding siblings ...)
2026-11-01 11:41 ` [RFC PATCH -v4 06/22] cpuset: introduce partition_update() Ridong Chen
@ 2026-11-01 11:41 ` Ridong Chen
2026-11-01 11:41 ` [RFC PATCH -v4 08/22] cpuset: use partition_disable() for remote partition disablement Ridong Chen
` (14 subsequent siblings)
21 siblings, 0 replies; 23+ messages in thread
From: Ridong Chen @ 2026-11-01 11:41 UTC (permalink / raw)
To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutn?
Cc: Guopeng Zhang, Ridong Chen, cgroups, linux-kernel, Chen Ridong,
Ridong Chen
From: Chen Ridong <chenridong@huawei.com>
Now that the partition_enable() helper is available, replace the
existing implementation for remote partition enablement with this
centralized function to unify the enablement logic.
Signed-off-by: Chen Ridong <chenridong@huawei.com>
Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
kernel/cgroup/cpuset.c | 9 +--------
1 file changed, 1 insertion(+), 8 deletions(-)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 23ae548c3994..d7f9f6e1fa3c 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1895,14 +1895,7 @@ static int remote_partition_enable(struct cpuset *cs, int new_prs,
if (prstate_housekeeping_conflict(new_prs, PRS_ROOT, tmp->new_cpus, NULL))
return PERR_HKEEPING;
- spin_lock_irq(&callback_lock);
- partition_xcpus_add(new_prs, NULL, tmp->new_cpus);
- cs->remote_partition = true;
- cpumask_copy(cs->effective_xcpus, tmp->new_cpus);
- spin_unlock_irq(&callback_lock);
- cpuset_force_rebuild();
- WRITE_ONCE(cs->prs_err, 0);
-
+ partition_enable(cs, NULL, new_prs, tmp->new_cpus);
/*
* Propagate changes in top_cpuset's effective_cpus down the hierarchy.
*/
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH -v4 08/22] cpuset: use partition_disable() for remote partition disablement
2026-11-01 11:41 [RFC PATCH -v4 00/22] cpuset: rework local partition logic Ridong Chen
` (6 preceding siblings ...)
2026-11-01 11:41 ` [RFC PATCH -v4 07/22] cpuset: use partition_enable() for remote partition enablement Ridong Chen
@ 2026-11-01 11:41 ` Ridong Chen
2026-11-01 11:41 ` [RFC PATCH -v4 09/22] cpuset: use partition_update() for remote partition update Ridong Chen
` (13 subsequent siblings)
21 siblings, 0 replies; 23+ messages in thread
From: Ridong Chen @ 2026-11-01 11:41 UTC (permalink / raw)
To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutn?
Cc: Guopeng Zhang, Ridong Chen, cgroups, linux-kernel, Chen Ridong,
Ridong Chen
From: Chen Ridong <chenridong@huawei.com>
Now that the partition_disable() helper is available, replace the
existing implementation for remote partition disablement with this
centralized function to unify the disablement logic.
Signed-off-by: Chen Ridong <chenridong@huawei.com>
Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
kernel/cgroup/cpuset.c | 17 ++++-------------
1 file changed, 4 insertions(+), 13 deletions(-)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index d7f9f6e1fa3c..9a683e6ef0be 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1915,6 +1915,8 @@ static int remote_partition_enable(struct cpuset *cs, int new_prs,
*/
static void remote_partition_disable(struct cpuset *cs, struct tmpmasks *tmp)
{
+ int new_prs;
+
WARN_ON_ONCE(!is_remote_partition(cs));
/*
* When a CPU is offlined, top_cpuset may end up with no available CPUs,
@@ -1925,19 +1927,8 @@ static void remote_partition_disable(struct cpuset *cs, struct tmpmasks *tmp)
WARN_ON_ONCE(!cpumask_subset(cs->effective_xcpus, subpartitions_cpus) &&
!cpumask_empty(subpartitions_cpus));
- spin_lock_irq(&callback_lock);
- cs->remote_partition = false;
- partition_xcpus_del(cs->partition_root_state, NULL, cs->effective_xcpus);
- if (cs->prs_err)
- cs->partition_root_state = -cs->partition_root_state;
- else
- cs->partition_root_state = PRS_MEMBER;
-
- /* effective_xcpus may need to be changed */
- compute_excpus(cs, cs->effective_xcpus);
- reset_partition_data(cs);
- spin_unlock_irq(&callback_lock);
- cpuset_force_rebuild();
+ new_prs = cs->prs_err ? -cs->partition_root_state : PRS_MEMBER;
+ partition_disable(cs, NULL, new_prs, cs->prs_err);
/*
* Propagate changes in top_cpuset's effective_cpus down the hierarchy.
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH -v4 09/22] cpuset: use partition_update() for remote partition update
2026-11-01 11:41 [RFC PATCH -v4 00/22] cpuset: rework local partition logic Ridong Chen
` (7 preceding siblings ...)
2026-11-01 11:41 ` [RFC PATCH -v4 08/22] cpuset: use partition_disable() for remote partition disablement Ridong Chen
@ 2026-11-01 11:41 ` Ridong Chen
2026-11-01 11:41 ` [RFC PATCH -v4 10/22] cpuset: introduce local_partition_enable() Ridong Chen
` (12 subsequent siblings)
21 siblings, 0 replies; 23+ messages in thread
From: Ridong Chen @ 2026-11-01 11:41 UTC (permalink / raw)
To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutn?
Cc: Guopeng Zhang, Ridong Chen, cgroups, linux-kernel, Chen Ridong,
Ridong Chen
From: Chen Ridong <chenridong@huawei.com>
Now that the partition_update() helper is available, use it to replace
the existing remote partition update logic. This unifies the update
path through a single centralized function.
Signed-off-by: Chen Ridong <chenridong@huawei.com>
Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
kernel/cgroup/cpuset.c | 24 ++++--------------------
1 file changed, 4 insertions(+), 20 deletions(-)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 9a683e6ef0be..ef162935ba5a 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1950,7 +1950,6 @@ static void remote_partition_disable(struct cpuset *cs, struct tmpmasks *tmp)
static void remote_cpus_update(struct cpuset *cs, struct cpumask *xcpus,
struct cpumask *excpus, struct tmpmasks *tmp)
{
- bool adding, deleting;
int prs = cs->partition_root_state;
if (WARN_ON_ONCE(!is_remote_partition(cs)))
@@ -1963,15 +1962,15 @@ static void remote_cpus_update(struct cpuset *cs, struct cpumask *xcpus,
goto invalidate;
}
- adding = cpumask_andnot(tmp->addmask, excpus, cs->effective_xcpus);
- deleting = cpumask_andnot(tmp->delmask, cs->effective_xcpus, excpus);
+ cpumask_andnot(tmp->addmask, excpus, cs->effective_xcpus);
+ cpumask_andnot(tmp->delmask, cs->effective_xcpus, excpus);
/*
* Additions of remote CPUs is only allowed if those CPUs are
* not allocated to other partitions and there are effective_cpus
* left in the top cpuset.
*/
- if (adding) {
+ if (!cpumask_empty(tmp->addmask)) {
WARN_ON_ONCE(cpumask_intersects(tmp->addmask, subpartitions_cpus));
if (!capable(CAP_SYS_ADMIN))
WRITE_ONCE(cs->prs_err, PERR_ACCESS);
@@ -1986,22 +1985,7 @@ static void remote_cpus_update(struct cpuset *cs, struct cpumask *xcpus,
}
cs->remote_partition_updating = true;
- spin_lock_irq(&callback_lock);
- if (adding)
- partition_xcpus_add(prs, NULL, tmp->addmask);
- if (deleting)
- partition_xcpus_del(prs, NULL, tmp->delmask);
- /*
- * Need to update effective_xcpus and exclusive_cpus now as
- * update_sibling_cpumasks() below may iterate back to the same cs.
- */
- cpumask_copy(cs->effective_xcpus, excpus);
- if (xcpus)
- cpumask_copy(cs->exclusive_cpus, xcpus);
- spin_unlock_irq(&callback_lock);
- if (adding || deleting)
- cpuset_force_rebuild();
-
+ partition_update(cs, prs, xcpus, excpus, tmp);
/*
* Propagate changes in top_cpuset's effective_cpus down the hierarchy.
*/
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH -v4 10/22] cpuset: introduce local_partition_enable()
2026-11-01 11:41 [RFC PATCH -v4 00/22] cpuset: rework local partition logic Ridong Chen
` (8 preceding siblings ...)
2026-11-01 11:41 ` [RFC PATCH -v4 09/22] cpuset: use partition_update() for remote partition update Ridong Chen
@ 2026-11-01 11:41 ` Ridong Chen
2026-11-01 11:41 ` [RFC PATCH -v4 11/22] cpuset: introduce local_partition_disable() Ridong Chen
` (11 subsequent siblings)
21 siblings, 0 replies; 23+ messages in thread
From: Ridong Chen @ 2026-11-01 11:41 UTC (permalink / raw)
To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutn?
Cc: Guopeng Zhang, Ridong Chen, cgroups, linux-kernel, Chen Ridong,
Ridong Chen
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=a, Size: 4566 bytes --]
From: Chen Ridong <chenridong@huawei.com>
The partition_enable() function introduced in the previous patch can be
reused to enable local partitions.
The local_partition_enable() function is introduced, which factors out the
local partition enablement logic from update_parent_effective_cpumask().
After passing local partition validation checks, it delegates to
partition_enable() to complete the partition setup.
This refactoring creates a clear separation between local and remote
partition operations while maintaining code reuse through the shared
partition_enable() infrastructure.
Signed-off-by: Chen Ridong <chenridong@huawei.com>
Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
kernel/cgroup/cpuset.c | 78 ++++++++++++++++++++++++++++--------------
1 file changed, 53 insertions(+), 25 deletions(-)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index ef162935ba5a..847713110738 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1998,6 +1998,57 @@ static void remote_cpus_update(struct cpuset *cs, struct cpumask *xcpus,
remote_partition_disable(cs, tmp);
}
+/**
+ * local_partition_enable - Enable local partition for a cpuset
+ * @cs: Target cpuset to become a local partition root
+ * @new_prs: New partition root state to apply
+ * @tmp: Temporary masks for CPU calculations
+ *
+ * Return: 0 on success, error code on failure
+ */
+static int local_partition_enable(struct cpuset *cs,
+ int new_prs, struct tmpmasks *tmp)
+{
+ struct cpuset *parent = parent_cs(cs);
+ enum prs_errcode err;
+
+ lockdep_assert_cpuset_lock_held();
+ WARN_ON_ONCE(is_remote_partition(cs)); /* For local partition only */
+
+ /*
+ * The parent must be a partition root.
+ * The new cpumask, if present, or the current cpus_allowed must
+ * not be empty.
+ */
+ if (!is_partition_valid(parent)) {
+ return is_partition_invalid(parent)
+ ? PERR_INVPARENT : PERR_NOTPART;
+ }
+
+ compute_excpus(cs, tmp->new_cpus);
+ err = validate_partition(cs, parent, new_prs, tmp->new_cpus, NULL);
+ if (err)
+ return err;
+
+ /*
+ * This function will only be called when all the preliminary
+ * checks have passed. At this point, the following condition
+ * should hold.
+ *
+ * (cs->effective_xcpus & cpu_active_mask) ⊆ parent->effective_cpus
+ *
+ * Warn if it is not the case.
+ */
+ cpumask_and(tmp->addmask, tmp->new_cpus, cpu_active_mask);
+ WARN_ON_ONCE(!cpumask_subset(tmp->addmask, parent->effective_cpus));
+
+ partition_enable(cs, parent, new_prs, tmp->new_cpus);
+
+ cpuset_update_tasks_cpumask(parent, tmp->addmask);
+ update_sibling_cpumasks(parent, cs, tmp);
+ return 0;
+}
+
/**
* update_parent_effective_cpumask - update effective_cpus mask of parent cpuset
* @cs: The cpuset that requests change in partition root state
@@ -2096,27 +2147,7 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd,
nocpu = tasks_nocpu_error(parent, cs, tmp->delmask);
}
- if ((cmd == partcmd_enable) || (cmd == partcmd_enablei)) {
- new_prs = (cmd == partcmd_enable) ? PRS_ROOT : PRS_ISOLATED;
-
- part_error = validate_partition(cs, parent, new_prs,
- tmp->delmask, NULL);
- if (part_error)
- return part_error;
-
- /*
- * This function will only be called when all the preliminary
- * checks have passed. At this point, the following condition
- * should hold.
- *
- * (cs->effective_xcpus & cpu_active_mask) ⊆ parent->effective_cpus
- *
- * Warn if it is not the case.
- */
- 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) {
+ if (cmd == partcmd_disable) {
/*
* May need to add cpus back to parent's effective_cpus
* (and maybe removed from subpartitions_cpus/isolated_cpus)
@@ -3085,9 +3116,6 @@ static int update_prstate(struct cpuset *cs, int new_prs)
* Otherwise, enable a remote partition.
*/
if (is_partition_valid(parent)) {
- enum partition_cmd cmd = (new_prs == PRS_ROOT)
- ? partcmd_enable : partcmd_enablei;
-
/*
* It is not allowed to create a root partition under
* an isolated parent partition.
@@ -3096,7 +3124,7 @@ static int update_prstate(struct cpuset *cs, int new_prs)
err = PERR_ISOPARENT;
goto out;
}
- err = update_parent_effective_cpumask(cs, cmd, NULL, &tmpmask);
+ err = local_partition_enable(cs, new_prs, &tmpmask);
} else {
err = remote_partition_enable(cs, new_prs, &tmpmask);
}
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH -v4 11/22] cpuset: introduce local_partition_disable()
2026-11-01 11:41 [RFC PATCH -v4 00/22] cpuset: rework local partition logic Ridong Chen
` (9 preceding siblings ...)
2026-11-01 11:41 ` [RFC PATCH -v4 10/22] cpuset: introduce local_partition_enable() Ridong Chen
@ 2026-11-01 11:41 ` Ridong Chen
2026-11-01 11:41 ` [RFC PATCH -v4 12/22] cpuset: use local_partition_disable() to invalidate local partition Ridong Chen
` (10 subsequent siblings)
21 siblings, 0 replies; 23+ messages in thread
From: Ridong Chen @ 2026-11-01 11:41 UTC (permalink / raw)
To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutn?
Cc: Guopeng Zhang, Ridong Chen, cgroups, linux-kernel, Chen Ridong,
Ridong Chen
From: Chen Ridong <chenridong@huawei.com>
The partition_disable() function introduced earlier can be extended to
handle local partition disablement.
The local_partition_disable() function is introduced, which extracts the
local partition disable logic from update_parent_effective_cpumask(). It
calls partition_disable() to complete the disablement process.
This refactoring establishes a clear separation between local and remote
partition operations while promoting code reuse through the shared
partition_disable() infrastructure.
Signed-off-by: Chen Ridong <chenridong@huawei.com>
Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
kernel/cgroup/cpuset.c | 42 +++++++++++++++++++++++++++---------------
1 file changed, 27 insertions(+), 15 deletions(-)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 847713110738..3d6ad6cd634a 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -2049,6 +2049,31 @@ static int local_partition_enable(struct cpuset *cs,
return 0;
}
+/**
+ * local_partition_disable - Disable a local partition
+ * @cs: Target cpuset (local partition root) to disable
+ * @part_error: partition error when @cs is disabled
+ * @tmp: Temporary masks for CPU calculations
+ */
+static void local_partition_disable(struct cpuset *cs, enum prs_errcode part_error,
+ struct tmpmasks *tmp)
+{
+ struct cpuset *parent = parent_cs(cs);
+ int new_prs;
+
+ lockdep_assert_cpuset_lock_held();
+ WARN_ON_ONCE(is_remote_partition(cs)); /* For local partition only */
+
+ if (!is_partition_valid(cs))
+ return;
+
+ new_prs = part_error ? -cs->partition_root_state : PRS_MEMBER;
+ partition_disable(cs, parent, new_prs, part_error);
+
+ cpuset_update_tasks_cpumask(parent, tmp->addmask);
+ update_sibling_cpumasks(parent, cs, tmp);
+}
+
/**
* update_parent_effective_cpumask - update effective_cpus mask of parent cpuset
* @cs: The cpuset that requests change in partition root state
@@ -2147,18 +2172,7 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd,
nocpu = tasks_nocpu_error(parent, cs, tmp->delmask);
}
- 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.
- */
- if (is_partition_valid(cs)) {
- cpumask_copy(tmp->addmask, cs->effective_xcpus);
- adding = true;
- }
- new_prs = PRS_MEMBER;
- } else if (newmask) {
+ if (newmask) {
/*
* Empty cpumask is not allowed
*/
@@ -3158,9 +3172,7 @@ static int update_prstate(struct cpuset *cs, int new_prs)
if (is_remote_partition(cs))
remote_partition_disable(cs, &tmpmask);
else
- update_parent_effective_cpumask(cs, partcmd_disable,
- NULL, &tmpmask);
-
+ local_partition_disable(cs, PERR_NONE, &tmpmask);
/*
* Invalidation of child partitions will be done in
* update_cpumasks_hier() below.
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH -v4 12/22] cpuset: use local_partition_disable() to invalidate local partition
2026-11-01 11:41 [RFC PATCH -v4 00/22] cpuset: rework local partition logic Ridong Chen
` (10 preceding siblings ...)
2026-11-01 11:41 ` [RFC PATCH -v4 11/22] cpuset: introduce local_partition_disable() Ridong Chen
@ 2026-11-01 11:41 ` Ridong Chen
2026-11-01 11:41 ` [RFC PATCH -v4 13/22] cpuset: introduce local_partition_update() Ridong Chen
` (9 subsequent siblings)
21 siblings, 0 replies; 23+ messages in thread
From: Ridong Chen @ 2026-11-01 11:41 UTC (permalink / raw)
To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutn?
Cc: Guopeng Zhang, Ridong Chen, cgroups, linux-kernel, Chen Ridong,
Ridong Chen
From: Chen Ridong <chenridong@huawei.com>
Build on the local_partition_disable() infrastructure introduced in the
previous patch to handle local partition invalidation, replacing
partcmd_invalidate in partition_cpus_change() and
cpuset_hotplug_update_tasks().
In cpuset_hotplug_update_tasks(), the partition error is now derived
from the reason of the invalidation (PERR_INVPARENT, PERR_NOTPART,
PERR_HOTPLUG or PERR_NOCPUS) and passed to local_partition_disable(),
which sets prs_err together with the partition root state.
As local_partition_disable() resets the partition data, the
effective_xcpus of an invalid partition is no longer kept. When
update_parent_effective_cpumask() turns an invalid partition back into
a valid one, use partition_enable() to set up effective_xcpus and the
partition state again.
Additionally, correct the transition logic in cpuset_hotplug_update_tasks()
when determining whether to transition an invalid partition root, the check
should be based on non-empty user_xcpus rather than non-empty
effective_xcpus. This correction addresses the scenario where
exclusive_cpus is not set but cpus_allowed is configured. In this case,
effective_xcpus may be empty even though the partition should be considered
for re-enablement. The user_xcpus based check ensures proper partition
state transitions under these conditions.
Since partition_disable() gives the CPUs back to the parent and updates
effective_cpus of the cpuset directly, save effective_cpus before any
partition state change in cpuset_hotplug_update_tasks() and compare the
new effective cpumask against it. Otherwise the tasks of a partition
invalidated by hotplug may be left with only the offlined CPUs.
Signed-off-by: Chen Ridong <chenridong@huawei.com>
Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
kernel/cgroup/cpuset.c | 81 +++++++++++++++++++++++-------------------
1 file changed, 44 insertions(+), 37 deletions(-)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 3d6ad6cd634a..822cc1d97fca 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -2133,23 +2133,6 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd,
adding = deleting = false;
old_prs = new_prs = cs->partition_root_state;
- if (cmd == partcmd_invalidate) {
- if (is_partition_invalid(cs))
- return 0;
-
- /*
- * Make the current partition invalid.
- */
- if (is_partition_valid(parent))
- adding = cpumask_and(tmp->addmask,
- cs->effective_xcpus,
- parent->effective_xcpus);
- if (old_prs > 0)
- new_prs = -old_prs;
-
- goto write_error;
- }
-
/*
* The parent must be a partition root.
* The new cpumask, if present, or the current cpus_allowed must
@@ -2340,20 +2323,28 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd,
* Newly added CPUs will be removed from effective_cpus and
* newly deleted ones will be added back to effective_cpus.
*/
- spin_lock_irq(&callback_lock);
- if (old_prs != new_prs)
- cs->partition_root_state = new_prs;
+ if ((old_prs < 0) && (new_prs > 0)) {
+ /*
+ * An invalid partition becomes valid again, its effective_xcpus
+ * was reset when it was invalidated.
+ */
+ partition_enable(cs, parent, new_prs, tmp->delmask);
+ } else {
+ spin_lock_irq(&callback_lock);
+ if (old_prs != new_prs)
+ cs->partition_root_state = new_prs;
- /*
- * Adding to parent's effective_cpus means deletion CPUs from cs
- * and vice versa.
- */
- if (adding)
- partition_xcpus_del(old_prs, parent, tmp->addmask);
- if (deleting)
- partition_xcpus_add(new_prs, parent, tmp->delmask);
+ /*
+ * Adding to parent's effective_cpus means deletion CPUs from cs
+ * and vice versa.
+ */
+ if (adding)
+ partition_xcpus_del(old_prs, parent, tmp->addmask);
+ if (deleting)
+ partition_xcpus_add(new_prs, parent, tmp->delmask);
- spin_unlock_irq(&callback_lock);
+ spin_unlock_irq(&callback_lock);
+ }
if (adding || deleting) {
cpuset_update_tasks_cpumask(parent, tmp->addmask);
@@ -2657,8 +2648,7 @@ static void partition_cpus_change(struct cpuset *cs, struct cpuset *trialcs,
trialcs->effective_xcpus, tmp);
} else {
if (trialcs->prs_err)
- update_parent_effective_cpumask(cs, partcmd_invalidate,
- NULL, tmp);
+ local_partition_disable(cs, trialcs->prs_err, tmp);
else
update_parent_effective_cpumask(cs, partcmd_update,
trialcs->effective_xcpus, tmp);
@@ -4089,7 +4079,7 @@ void cpuset_force_rebuild(void)
*/
static void cpuset_hotplug_update_tasks(struct cpuset *cs, struct tmpmasks *tmp)
{
- static cpumask_t new_cpus;
+ static cpumask_t new_cpus, old_cpus;
static nodemask_t new_mems;
bool cpus_updated;
bool mems_updated;
@@ -4098,6 +4088,11 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs, struct tmpmasks *tmp)
wait_attach_done_lock();
parent = parent_cs(cs);
+ /*
+ * Partition state changes below may update effective_cpus directly,
+ * save it to check if tasks need to be updated.
+ */
+ cpumask_copy(&old_cpus, cs->effective_cpus);
compute_effective_cpumask(&new_cpus, cs, parent);
compute_effective_nodemask(&new_mems, cs, parent);
@@ -4123,18 +4118,30 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs, struct tmpmasks *tmp)
if (is_local_partition(cs) &&
(!is_partition_valid(parent) ||
tasks_nocpu_error(parent, cs, &new_cpus) ||
- cpumask_empty(subpartitions_cpus)))
+ cpumask_empty(subpartitions_cpus))) {
+ enum prs_errcode prs_err;
+
+ if (!is_partition_valid(parent))
+ prs_err = is_partition_invalid(parent)
+ ? PERR_INVPARENT : PERR_NOTPART;
+ else if (cpumask_empty(subpartitions_cpus))
+ prs_err = PERR_HOTPLUG;
+ else
+ prs_err = PERR_NOCPUS;
partcmd = partcmd_invalidate;
+ local_partition_disable(cs, prs_err, tmp);
+ }
/*
* On the other hand, an invalid partition root may be transitioned
- * back to a regular one with a non-empty effective xcpus.
+ * back to a regular one with a non-empty user xcpus.
*/
else if (is_partition_valid(parent) && is_partition_invalid(cs) &&
- !cpumask_empty(cs->effective_xcpus))
+ !cpumask_empty(user_xcpus(cs))) {
partcmd = partcmd_update;
+ update_parent_effective_cpumask(cs, partcmd, NULL, tmp);
+ }
if (partcmd >= 0) {
- update_parent_effective_cpumask(cs, partcmd, NULL, tmp);
if (is_partition_valid(cs) || (partcmd == partcmd_invalidate)) {
compute_effective_cpumask(&new_cpus, cs, parent);
cpuset_force_rebuild();
@@ -4142,7 +4149,7 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs, struct tmpmasks *tmp)
}
update_tasks:
- cpus_updated = !cpumask_equal(&new_cpus, cs->effective_cpus);
+ cpus_updated = !cpumask_equal(&new_cpus, &old_cpus);
mems_updated = !nodes_equal(new_mems, cs->effective_mems);
if (!cpus_updated && !mems_updated)
goto unlock; /* Hotplug doesn't affect this cpuset */
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH -v4 13/22] cpuset: introduce local_partition_update()
2026-11-01 11:41 [RFC PATCH -v4 00/22] cpuset: rework local partition logic Ridong Chen
` (11 preceding siblings ...)
2026-11-01 11:41 ` [RFC PATCH -v4 12/22] cpuset: use local_partition_disable() to invalidate local partition Ridong Chen
@ 2026-11-01 11:41 ` Ridong Chen
2026-11-01 11:41 ` [RFC PATCH -v4 14/22] cpuset: remove update_parent_effective_cpumask Ridong Chen
` (8 subsequent siblings)
21 siblings, 0 replies; 23+ messages in thread
From: Ridong Chen @ 2026-11-01 11:41 UTC (permalink / raw)
To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutn?
Cc: Guopeng Zhang, Ridong Chen, cgroups, linux-kernel, Chen Ridong,
Ridong Chen
From: Chen Ridong <chenridong@huawei.com>
The local_partition_update() function replaces the command partcmd_update
previously handled within update_parent_effective_cpumask(). The update
logic follows a state-based approach:
1. Validation check: First verify if the local partition is currently valid
2. Invalidation handling: If the partition is invalid, trigger invalidation
3. State transition: If an invalid partition has no errors, transition to
valid
4. cpumask updates: For a local partition whose cpumask only changes, use
partition_update() to handle the change.
validate_local_partition() is added to check the local partition
specific requirements: the parent must be a valid partition root, a
root partition is not allowed under an isolated parent, and optionally
the user exclusive CPUs must not overlap with any sibling. It then
calls validate_partition() for the common checks.
local_partition_update() computes the new effective exclusive CPUs
with compute_excpus() as before. As in the old partcmd_update path, the
sibling exclusivity check is only done for an invalid partition with an
empty cpuset.cpus.exclusive, as a valid partition has passed it
already. If an invalid partition fails the validation again, only its
error code is updated, unless its parent is not a valid partition root.
In that case, e.g. for a remote partition invalidated by hotplug, the
original error code is kept as the cpuset can't become a valid local
partition anyway.
In update_cpumasks_hier(), effective_cpus of the child is now updated
before local_partition_update() is called, as tmp->new_cpus is reused
by it. For the same reason, the effective cpumask is always recomputed
afterwards.
Signed-off-by: Chen Ridong <chenridong@huawei.com>
Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
kernel/cgroup/cpuset.c | 171 +++++++++++++++++++++++++++++++++++++++--
1 file changed, 163 insertions(+), 8 deletions(-)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 822cc1d97fca..d2cb989beab9 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -2074,6 +2074,155 @@ static void local_partition_disable(struct cpuset *cs, enum prs_errcode part_err
update_sibling_cpumasks(parent, cs, tmp);
}
+static bool is_user_xcpus_exclusive(struct cpuset *cs)
+{
+ struct cpuset *parent = parent_cs(cs);
+ struct cgroup_subsys_state *css;
+ struct cpuset *child;
+ bool exclusive = true;
+
+ 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();
+ return exclusive;
+}
+
+/**
+ * validate_local_partition - Validate for local partition
+ * @cs: Target cpuset to validate
+ * @new_prs: New partition root state to validate
+ * @excpus: New effective exclusive CPUs mask to validate
+ * @excl_check: Flag to enable exclusive CPUs ownership validation
+ * @tmp: Temporary masks
+ *
+ * Return: PERR_NONE if validation passes, appropriate error code otherwise
+ *
+ * Important: The caller must ensure that @cs's cpu mask is updated before
+ * invoking this function when exclusive CPU validation is required.
+ */
+static enum prs_errcode validate_local_partition(struct cpuset *cs, int new_prs,
+ struct cpumask *excpus, bool excl_check, struct tmpmasks *tmp)
+{
+ struct cpuset *parent = parent_cs(cs);
+
+ /* The parent must be a partition root */
+ if (!is_partition_valid(parent)) {
+ return is_partition_invalid(parent)
+ ? PERR_INVPARENT : PERR_NOTPART;
+ }
+
+ /* A root partition is not allowed under an isolated parent */
+ if (new_prs == PRS_ROOT && parent->partition_root_state == PRS_ISOLATED)
+ return PERR_ISOPARENT;
+
+ if (excl_check && !is_user_xcpus_exclusive(cs))
+ return PERR_NOTEXCL;
+
+ return validate_partition(cs, parent, new_prs, excpus,
+ tmp ? tmp->delmask : NULL);
+}
+
+/**
+ * __local_partition_update - Update local partition configuration
+ * @cs: Target cpuset to update
+ * @xcpus: New exclusive CPU mask
+ * @excpus: New effective exclusive CPU mask
+ * @tmp: Temporary mask storage for intermediate calculations
+ * @excl_check: Flag to enable exclusivity validation
+ *
+ * Handles updates to local CPU partition configurations by validating
+ * changes, managing state transitions, and propagating updates through
+ * the cpuset hierarchy.
+ *
+ * Note on exclusivity checking: Exclusivity validation is required when
+ * transitioning from an invalid to valid partition state. However, when
+ * updating cpus_allowed or exclusive_cpus, exclusivity should have already
+ * been verified by validate_change(). In such cases, excl_check must be
+ * false since the cs cpumasks are not yet updated.
+ *
+ * Return: Partition error code (PERR_NONE indicates success)
+ */
+static int __local_partition_update(struct cpuset *cs, struct cpumask *xcpus,
+ struct cpumask *excpus, struct tmpmasks *tmp,
+ bool excl_check)
+{
+ struct cpuset *parent = parent_cs(cs);
+ int part_error = PERR_NONE; /* Partition error? */
+ int old_prs, new_prs;
+
+ lockdep_assert_cpuset_lock_held();
+ /* For local partition only */
+ if (WARN_ON_ONCE(is_remote_partition(cs) || cs_is_member(cs)))
+ return PERR_NONE;
+
+ old_prs = cs->partition_root_state;
+ /*
+ * If new_prs < 0, it might transition to valid partition state.
+ * Use absolute value for validation checks.
+ */
+ new_prs = old_prs < 0 ? -old_prs : old_prs;
+ cpumask_andnot(tmp->addmask, excpus, cs->effective_xcpus);
+ cpumask_andnot(tmp->delmask, cs->effective_xcpus, excpus);
+ part_error = validate_local_partition(cs, new_prs, excpus,
+ excl_check, tmp);
+ if (part_error) {
+ /*
+ * An invalid partition only needs to update its error code.
+ * Keep the error code if the parent is not a valid partition
+ * root, e.g. for an invalidated remote partition, as the
+ * cpuset can't become a valid local partition anyway.
+ */
+ if (is_partition_valid(cs))
+ local_partition_disable(cs, part_error, tmp);
+ else if (is_partition_valid(parent))
+ WRITE_ONCE(cs->prs_err, part_error);
+ return part_error;
+ }
+
+ /* Nothing changes, return PERR_NONE */
+ if (new_prs == old_prs &&
+ (cpumask_empty(tmp->addmask) && cpumask_empty(tmp->delmask)))
+ return PERR_NONE;
+
+ /*
+ * If partition was previously invalid but now passes checks,
+ * re-enable it and update related flags.
+ * Otherwise, partition state doesn't change, only cpumasks change.
+ */
+ if (is_partition_invalid(cs)) {
+ partition_enable(cs, parent, new_prs, excpus);
+ update_partition_sd_lb(cs, old_prs);
+ } else {
+ partition_update(cs, new_prs, xcpus, excpus, tmp);
+ }
+
+ cpuset_update_tasks_cpumask(parent, tmp->addmask);
+ update_sibling_cpumasks(parent, cs, tmp);
+ return PERR_NONE;
+}
+
+static int local_partition_update(struct cpuset *cs, struct tmpmasks *tmp)
+{
+ /*
+ * A valid partition has passed the exclusivity check already. An
+ * invalid partition has to pass it before becoming valid again when
+ * cpuset.cpus.exclusive is empty.
+ */
+ bool excl_check = is_partition_invalid(cs) &&
+ cpumask_empty(cs->exclusive_cpus);
+
+ compute_excpus(cs, tmp->new_cpus);
+ return __local_partition_update(cs, NULL, tmp->new_cpus, tmp,
+ excl_check);
+}
+
/**
* update_parent_effective_cpumask - update effective_cpus mask of parent cpuset
* @cs: The cpuset that requests change in partition root state
@@ -2483,17 +2632,23 @@ static void update_cpumasks_hier(struct cpuset *cs, struct tmpmasks *tmp,
if (!css_tryget_online(&cp->css))
continue;
rcu_read_unlock();
+ /*
+ * The tmp->new_cpus may be modified.
+ * Update effective_cpus before passing tmp to other functions.
+ */
+ spin_lock_irq(&callback_lock);
+ cpumask_copy(cp->effective_cpus, tmp->new_cpus);
+ spin_unlock_irq(&callback_lock);
if (update_parent) {
- update_parent_effective_cpumask(cp, partcmd_update, NULL, tmp);
+ local_partition_update(cp, tmp);
/*
* Capture current partition_root_state as it may have
- * changed. Recompute effective cpumask if changed.
+ * changed. tmp->new_cpus is reused by
+ * local_partition_update(), recompute effective cpumask.
*/
new_prs = cp->partition_root_state;
- if (new_prs != old_prs) {
- compute_effective_cpumask(tmp->new_cpus, cp, parent);
- }
+ compute_effective_cpumask(tmp->new_cpus, cp, parent);
}
spin_lock_irq(&callback_lock);
@@ -2650,8 +2805,8 @@ static void partition_cpus_change(struct cpuset *cs, struct cpuset *trialcs,
if (trialcs->prs_err)
local_partition_disable(cs, trialcs->prs_err, tmp);
else
- update_parent_effective_cpumask(cs, partcmd_update,
- trialcs->effective_xcpus, tmp);
+ __local_partition_update(cs, trialcs->exclusive_cpus,
+ trialcs->effective_xcpus, tmp, false);
}
}
@@ -4138,7 +4293,7 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs, struct tmpmasks *tmp)
else if (is_partition_valid(parent) && is_partition_invalid(cs) &&
!cpumask_empty(user_xcpus(cs))) {
partcmd = partcmd_update;
- update_parent_effective_cpumask(cs, partcmd, NULL, tmp);
+ local_partition_update(cs, tmp);
}
if (partcmd >= 0) {
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH -v4 14/22] cpuset: remove update_parent_effective_cpumask
2026-11-01 11:41 [RFC PATCH -v4 00/22] cpuset: rework local partition logic Ridong Chen
` (12 preceding siblings ...)
2026-11-01 11:41 ` [RFC PATCH -v4 13/22] cpuset: introduce local_partition_update() Ridong Chen
@ 2026-11-01 11:41 ` Ridong Chen
2026-11-01 11:41 ` [RFC PATCH -v4 15/22] cpuset: remove redundant partition field updates Ridong Chen
` (7 subsequent siblings)
21 siblings, 0 replies; 23+ messages in thread
From: Ridong Chen @ 2026-11-01 11:41 UTC (permalink / raw)
To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutn?
Cc: Guopeng Zhang, Ridong Chen, cgroups, linux-kernel, Chen Ridong,
Ridong Chen
From: Chen Ridong <chenridong@huawei.com>
Clean up by removing the update_parent_effective_cpumask() function.
Its logic has now been fully replaced and centralized by the new
helper functions.
Signed-off-by: Chen Ridong <chenridong@huawei.com>
Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
kernel/cgroup/cpuset.c | 297 +----------------------------------------
1 file changed, 4 insertions(+), 293 deletions(-)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index d2cb989beab9..86d6860a8b15 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -134,7 +134,7 @@ static DEFINE_MUTEX(cpuset_mutex);
/*
* For local partitions, update to subpartitions_cpus & isolated_cpus is done
- * in update_parent_effective_cpumask(). For remote partitions, it is done in
+ * in the local_partition_*() helpers. For remote partitions, it is done in
* the remote_partition_*() and remote_cpus_update() helpers.
*/
/*
@@ -1620,7 +1620,7 @@ static int compute_trialcs_excpus(struct cpuset *trialcs, struct cpuset *cs)
* 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()
+ * successively, we don't need to call local_partition_update()
* and the child's effective_cpus will be updated in later iterations.
*/
static void compute_partition_effective_cpumask(struct cpuset *cs,
@@ -2223,295 +2223,6 @@ static int local_partition_update(struct cpuset *cs, struct tmpmasks *tmp)
excl_check);
}
-/**
- * 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 effective xcpus for partcmd_update
- * @tmp: Temporary addmask and delmask
- * Return: 0 or a partition root state error code
- *
- * For partcmd_enable*, the cpuset is being transformed from a non-partition
- * root to a partition root. The effective_xcpus (cpus_allowed if
- * effective_xcpus not set) mask of the given cpuset will be taken away from
- * parent's effective_cpus. The function will return 0 if all the CPUs listed
- * in effective_xcpus can be granted or an error code will be returned.
- *
- * For partcmd_disable, the cpuset is being transformed from a partition
- * root back to a non-partition root. Any CPUs in effective_xcpus will be
- * 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
- * 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.
- *
- * The partcmd_enable* and partcmd_disable commands are used by
- * update_prstate(). An error code may be returned and the caller will check
- * for error.
- *
- * The partcmd_update command is used by update_cpumasks_hier() with newmask
- * NULL and update_cpumask() with newmask set. The partcmd_invalidate is used
- * by update_cpumask() with NULL newmask. In both cases, the callers won't
- * check for error and so partition_root_state and prs_err will be updated
- * directly.
- */
-static int update_parent_effective_cpumask(struct cpuset *cs, int cmd,
- struct cpumask *newmask,
- struct tmpmasks *tmp)
-{
- struct cpuset *parent = parent_cs(cs);
- int adding; /* Adding cpus to parent's effective_cpus */
- int deleting; /* Deleting cpus from parent's effective_cpus */
- int old_prs, new_prs;
- int part_error = PERR_NONE; /* Partition error? */
- int parent_prs = parent->partition_root_state;
- bool nocpu;
-
- lockdep_assert_cpuset_lock_held();
- WARN_ON_ONCE(is_remote_partition(cs)); /* For local partition only */
-
- /*
- * new_prs will only be changed for the partcmd_update and
- * partcmd_invalidate commands.
- */
- adding = deleting = false;
- old_prs = new_prs = cs->partition_root_state;
-
- /*
- * The parent must be a partition root.
- * The new cpumask, if present, or the current cpus_allowed must
- * not be empty.
- */
- if (!is_partition_valid(parent)) {
- return is_partition_invalid(parent)
- ? PERR_INVPARENT : PERR_NOTPART;
- }
- if (!newmask && xcpus_empty(cs))
- return PERR_CPUSEMPTY;
-
- /*
- * 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 (newmask) {
- /*
- * Empty cpumask is not allowed
- */
- if (cpumask_empty(newmask)) {
- part_error = PERR_CPUSEMPTY;
- goto write_error;
- }
- if ((parent_prs == PRS_ISOLATED) && (old_prs == -PRS_ROOT)) {
- part_error = PERR_ISOPARENT;
- goto write_error;
- }
-
- /* Check newmask whether cpus are available for parent/cs */
- nocpu = tasks_nocpu_error(parent, cs, newmask);
-
- /*
- * partcmd_update with newmask:
- *
- * Compute add/delete mask to/from effective_cpus
- *
- * For valid partition:
- * addmask = effective_xcpus & ~newmask
- * & parent->effective_xcpus
- * delmask = newmask & ~effective_xcpus
- * & parent->effective_xcpus
- *
- * For invalid partition:
- * delmask = newmask & parent->effective_xcpus
- * The partition may become valid soon.
- */
- if (is_partition_invalid(cs)) {
- adding = false;
- deleting = cpumask_and(tmp->delmask,
- newmask, parent->effective_xcpus);
- } else {
- cpumask_andnot(tmp->addmask, cs->effective_xcpus, newmask);
- adding = cpumask_and(tmp->addmask, tmp->addmask,
- parent->effective_xcpus);
-
- cpumask_andnot(tmp->delmask, newmask, cs->effective_xcpus);
- deleting = cpumask_and(tmp->delmask, tmp->delmask,
- parent->effective_xcpus);
- }
-
- /*
- * The new CPUs to be removed from parent's effective CPUs
- * must be present.
- */
- if (deleting) {
- cpumask_and(tmp->new_cpus, tmp->delmask, cpu_active_mask);
- WARN_ON_ONCE(!cpumask_subset(tmp->new_cpus, parent->effective_cpus));
- }
-
- /*
- * Make partition invalid if parent's effective_cpus could
- * become empty and there are tasks in the parent.
- */
- if (nocpu && (!adding ||
- !cpumask_intersects(tmp->addmask, cpu_active_mask))) {
- part_error = PERR_NOCPUS;
- deleting = false;
- adding = cpumask_and(tmp->addmask,
- cs->effective_xcpus, parent->effective_xcpus);
- }
- } else {
- /*
- * partcmd_update w/o newmask
- *
- * delmask = newly computed effective xcpus value
- *
- * This can be called from:
- * 1) update_cpumasks_hier()
- * 2) cpuset_hotplug_update_tasks()
- *
- * Check to see if it can be transitioned from valid to
- * invalid partition or vice versa.
- *
- * A partition error happens when parent has tasks and all
- * its effective CPUs will have to be distributed out.
- */
- nocpu |= cpumask_empty(tmp->delmask);
- if (nocpu) {
- part_error = PERR_NOCPUS;
- if (is_partition_valid(cs))
- adding = cpumask_and(tmp->addmask,
- cs->effective_xcpus,
- parent->effective_xcpus);
- } else if ((parent_prs == PRS_ISOLATED) &&
- ((old_prs == PRS_ROOT) || (old_prs == -PRS_ROOT))) {
- part_error = PERR_ISOPARENT;
- if (old_prs == PRS_ROOT) {
- cpumask_copy(tmp->addmask, cs->effective_xcpus);
- adding = true;
- }
- } 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 when cpuset.cpus.exclusive is
- * empty as well as the housekeeping check.
- */
- if (prstate_housekeeping_conflict(cs->partition_root_state,
- parent_prs, tmp->delmask, NULL)) {
- part_error = PERR_HKEEPING;
- goto write_error;
- }
- 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();
- }
- if (exclusive)
- 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:
- if (part_error)
- WRITE_ONCE(cs->prs_err, part_error);
-
- if (cmd == partcmd_update) {
- /*
- * Check for possible transition between valid and invalid
- * partition root.
- */
- switch (cs->partition_root_state) {
- case PRS_ROOT:
- case PRS_ISOLATED:
- if (part_error)
- new_prs = -old_prs;
- break;
- case PRS_INVALID_ROOT:
- case PRS_INVALID_ISOLATED:
- if (!part_error)
- new_prs = -old_prs;
- break;
- }
- }
-
- if (!adding && !deleting && (new_prs == old_prs))
- return 0;
-
- /*
- * Change the parent's effective_cpus & effective_xcpus (top cpuset
- * only).
- *
- * Newly added CPUs will be removed from effective_cpus and
- * newly deleted ones will be added back to effective_cpus.
- */
- if ((old_prs < 0) && (new_prs > 0)) {
- /*
- * An invalid partition becomes valid again, its effective_xcpus
- * was reset when it was invalidated.
- */
- partition_enable(cs, parent, new_prs, tmp->delmask);
- } else {
- spin_lock_irq(&callback_lock);
- if (old_prs != new_prs)
- cs->partition_root_state = new_prs;
-
- /*
- * Adding to parent's effective_cpus means deletion CPUs from cs
- * and vice versa.
- */
- if (adding)
- partition_xcpus_del(old_prs, parent, tmp->addmask);
- if (deleting)
- partition_xcpus_add(new_prs, parent, tmp->delmask);
-
- spin_unlock_irq(&callback_lock);
- }
-
- if (adding || deleting) {
- cpuset_update_tasks_cpumask(parent, tmp->addmask);
- update_sibling_cpumasks(parent, cs, tmp);
- }
-
- /*
- * For partcmd_update without newmask, it is being called from
- * cpuset_handle_hotplug(). Update the load balance flag and
- * scheduling domain accordingly.
- */
- if ((cmd == partcmd_update) && !newmask)
- update_partition_sd_lb(cs, old_prs);
-
- notify_partition_change(cs, old_prs);
- return 0;
-}
-
/*
* update_cpumasks_hier - Update effective cpumasks and tasks in the subtree
* @cs: the cpuset to consider
@@ -2574,7 +2285,7 @@ static void update_cpumasks_hier(struct cpuset *cs, struct tmpmasks *tmp,
/*
* A partition with no effective_cpus is allowed as long as
* there is no task associated with it. Call
- * update_parent_effective_cpumask() to check it.
+ * local_partition_update() to check it.
*/
if (is_partition_valid(cp) && cpumask_empty(tmp->new_cpus)) {
update_parent = true;
@@ -2598,7 +2309,7 @@ static void update_cpumasks_hier(struct cpuset *cs, struct tmpmasks *tmp,
update_parent_effective:
/*
- * update_parent_effective_cpumask() should have been called
+ * local_partition_update() should have been called
* for cs already in update_cpumask(). We should also call
* cpuset_update_tasks_cpumask() again for tasks in the parent
* cpuset if the parent's effective_cpus changes.
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH -v4 15/22] cpuset: remove redundant partition field updates
2026-11-01 11:41 [RFC PATCH -v4 00/22] cpuset: rework local partition logic Ridong Chen
` (13 preceding siblings ...)
2026-11-01 11:41 ` [RFC PATCH -v4 14/22] cpuset: remove update_parent_effective_cpumask Ridong Chen
@ 2026-11-01 11:41 ` Ridong Chen
2026-11-01 11:42 ` [RFC PATCH -v4 16/22] cpuset: simplify partition update logic for hotplug tasks Ridong Chen
` (6 subsequent siblings)
21 siblings, 0 replies; 23+ messages in thread
From: Ridong Chen @ 2026-11-01 11:41 UTC (permalink / raw)
To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutn?
Cc: Guopeng Zhang, Ridong Chen, cgroups, linux-kernel, Chen Ridong,
Ridong Chen
From: Chen Ridong <chenridong@huawei.com>
With the previous patch series, partition-related fields are now properly
managed during partition enable, disable, and update operations. There
should be no need to set these fields outside of these dedicated partition
operations.
This patch removes the redundant partition field updates from the cpumask
setting code path. However, one exception remains: when setting
cpuset.cpus.exclusive on a non-partition cpuset, update_exclusive_cpumask()
must still set effective_xcpus directly. This is necessary because no
partition operation is invoked in this scenario, yet effective_xcpus needs
to be properly initialized.
Signed-off-by: Chen Ridong <chenridong@huawei.com>
Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
kernel/cgroup/cpuset.c | 5 -----
1 file changed, 5 deletions(-)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 86d6860a8b15..78a783cb6308 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -2563,9 +2563,6 @@ static int update_cpumask(struct cpuset *cs, struct cpuset *trialcs,
spin_lock_irq(&callback_lock);
cpumask_copy(cs->cpus_allowed, trialcs->cpus_allowed);
- cpumask_copy(cs->effective_xcpus, trialcs->effective_xcpus);
- if ((old_prs > 0) && !is_partition_valid(cs))
- reset_partition_data(cs);
spin_unlock_irq(&callback_lock);
/* effective_cpus/effective_xcpus will be updated here */
@@ -2629,8 +2626,6 @@ static int update_exclusive_cpumask(struct cpuset *cs, struct cpuset *trialcs,
spin_lock_irq(&callback_lock);
cpumask_copy(cs->exclusive_cpus, trialcs->exclusive_cpus);
cpumask_copy(cs->effective_xcpus, trialcs->effective_xcpus);
- if ((old_prs > 0) && !is_partition_valid(cs))
- reset_partition_data(cs);
spin_unlock_irq(&callback_lock);
/*
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH -v4 16/22] cpuset: simplify partition update logic for hotplug tasks
2026-11-01 11:41 [RFC PATCH -v4 00/22] cpuset: rework local partition logic Ridong Chen
` (14 preceding siblings ...)
2026-11-01 11:41 ` [RFC PATCH -v4 15/22] cpuset: remove redundant partition field updates Ridong Chen
@ 2026-11-01 11:42 ` Ridong Chen
2026-11-01 11:42 ` [RFC PATCH -v4 17/22] cpuset: use partition_disable for compute_partition_effective_cpumask Ridong Chen
` (5 subsequent siblings)
21 siblings, 0 replies; 23+ messages in thread
From: Ridong Chen @ 2026-11-01 11:42 UTC (permalink / raw)
To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutn?
Cc: Guopeng Zhang, Ridong Chen, cgroups, linux-kernel, Chen Ridong,
Ridong Chen
From: Chen Ridong <chenridong@huawei.com>
Simplify the partition update logic in cpuset_hotplug_update_tasks() by
calling the unified local_partition_update() interface.
For local partitions, the previous patch introduced local_partition_update
which handles both validation state transitions:
- Invalidates local partitions that fail validation checks
- Transitions invalid partitions to valid state when no errors are detected
This eliminates the need for separate transition logic in
cpuset_hotplug_update_tasks(). The only exception is when
subpartitions_cpus becomes empty because no CPU is left for the
top_cpuset, in which case the local partition is disabled directly
with PERR_HOTPLUG. The effective cpumask is then always recomputed.
For remote partitions, only the hotplug invalidation check is needed,
so jump to update_tasks directly after it instead of going through the
local partition checks.
The partition_cmd enum type can now be safely removed as it is no longer
referenced by any code paths after the partition update logic
simplification.
Signed-off-by: Chen Ridong <chenridong@huawei.com>
Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
kernel/cgroup/cpuset.c | 78 ++++++++++++++----------------------------
1 file changed, 25 insertions(+), 53 deletions(-)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 78a783cb6308..00afa4987381 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1135,17 +1135,6 @@ void cpuset_update_tasks_cpumask(struct cpuset *cs, struct cpumask *new_cpus)
css_task_iter_end(&it);
}
-/*
- * Commands for update_parent_effective_cpumask
- */
-enum partition_cmd {
- partcmd_enable, /* Enable partition root */
- partcmd_enablei, /* Enable isolated partition root */
- partcmd_disable, /* Disable partition root */
- partcmd_update, /* Update parent's effective_cpus */
- partcmd_invalidate, /* Make partition invalid */
-};
-
static void update_sibling_cpumasks(struct cpuset *parent, struct cpuset *cs,
struct tmpmasks *tmp);
@@ -3944,7 +3933,6 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs, struct tmpmasks *tmp)
static nodemask_t new_mems;
bool cpus_updated;
bool mems_updated;
- int partcmd = -1;
struct cpuset *parent;
wait_attach_done_lock();
@@ -3960,54 +3948,38 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs, struct tmpmasks *tmp)
if (!tmp || !cs->partition_root_state)
goto update_tasks;
- 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);
- }
-
/*
- * Force the partition to become invalid if either one of
- * the following conditions hold:
- * 1) empty effective cpus but not valid empty partition.
- * 2) parent is invalid or doesn't grant any cpus to child
- * partitions.
- * 3) subpartitions_cpus is empty.
+ * Compute effective_cpus for valid partition root, may invalidate
+ * child partition roots if necessary.
*/
- if (is_local_partition(cs) &&
- (!is_partition_valid(parent) ||
- tasks_nocpu_error(parent, cs, &new_cpus) ||
- cpumask_empty(subpartitions_cpus))) {
- enum prs_errcode prs_err;
-
- if (!is_partition_valid(parent))
- prs_err = is_partition_invalid(parent)
- ? PERR_INVPARENT : PERR_NOTPART;
- else if (cpumask_empty(subpartitions_cpus))
- prs_err = PERR_HOTPLUG;
- else
- prs_err = PERR_NOCPUS;
- partcmd = partcmd_invalidate;
- local_partition_disable(cs, prs_err, tmp);
+ if (is_remote_partition(cs)) {
+ if (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);
+ }
+ goto update_tasks;
}
+
/*
- * On the other hand, an invalid partition root may be transitioned
- * back to a regular one with a non-empty user xcpus.
+ * The subpartitions_cpus may be cleared if no CPUs remain available for
+ * the top_cpuset. In this case, disable the local partition directly.
+ * Otherwise, local_partition_update() will handle the automatic
+ * transition between valid and invalid partition states.
*/
- else if (is_partition_valid(parent) && is_partition_invalid(cs) &&
- !cpumask_empty(user_xcpus(cs))) {
- partcmd = partcmd_update;
+ if (is_local_partition(cs) && cpumask_empty(subpartitions_cpus))
+ local_partition_disable(cs, PERR_HOTPLUG, tmp);
+ else
local_partition_update(cs, tmp);
- }
- if (partcmd >= 0) {
- if (is_partition_valid(cs) || (partcmd == partcmd_invalidate)) {
- compute_effective_cpumask(&new_cpus, cs, parent);
- cpuset_force_rebuild();
- }
- }
+ /*
+ * Recompute effective CPU mask after partition state update:
+ * - For valid partitions: calculate partition-specific effective CPUs
+ * - For invalid partitions: compute member effective CPU mask
+ */
+ compute_effective_cpumask(&new_cpus, cs, parent);
update_tasks:
cpus_updated = !cpumask_equal(&new_cpus, &old_cpus);
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH -v4 17/22] cpuset: use partition_disable for compute_partition_effective_cpumask
2026-11-01 11:41 [RFC PATCH -v4 00/22] cpuset: rework local partition logic Ridong Chen
` (15 preceding siblings ...)
2026-11-01 11:42 ` [RFC PATCH -v4 16/22] cpuset: simplify partition update logic for hotplug tasks Ridong Chen
@ 2026-11-01 11:42 ` Ridong Chen
2026-11-01 11:42 ` [RFC PATCH -v4 18/22] cpuset: use validate_local_partition in local_partition_enable Ridong Chen
` (4 subsequent siblings)
21 siblings, 0 replies; 23+ messages in thread
From: Ridong Chen @ 2026-11-01 11:42 UTC (permalink / raw)
To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutn?
Cc: Guopeng Zhang, Ridong Chen, cgroups, linux-kernel, Chen Ridong,
Ridong Chen
From: Chen Ridong <chenridong@huawei.com>
Replace the partition invalidation logic in the
compute_partition_effective_cpumask() with a call to partition_disable().
This centralizes partition state management and ensures consistent
handling of partition disable operations throughout the cpuset subsystem.
The isolated_cpus update that was open-coded here is now done by
partition_xcpus_del() called from partition_disable(). In addition,
partition_disable() gives the exclusive CPUs of the invalidated child
back to the parent and resets the partition data of the child, the
same as other partition invalidation paths.
Signed-off-by: Chen Ridong <chenridong@huawei.com>
Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
kernel/cgroup/cpuset.c | 19 +++++--------------
1 file changed, 5 insertions(+), 14 deletions(-)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 00afa4987381..af42666d4530 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1137,6 +1137,8 @@ void cpuset_update_tasks_cpumask(struct cpuset *cs, struct cpumask *new_cpus)
static void update_sibling_cpumasks(struct cpuset *parent, struct cpuset *cs,
struct tmpmasks *tmp);
+static void partition_disable(struct cpuset *cs, struct cpuset *parent,
+ int new_prs, enum prs_errcode prs_err);
/*
* Update partition load balance flag and/or rebuild sched domain
@@ -1642,20 +1644,9 @@ static void compute_partition_effective_cpumask(struct cpuset *cs,
*/
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. Its CPUs, including those
- * further distributed to its descendants, take the
- * isolation state of cs.
- */
- spin_lock_irq(&callback_lock);
- WRITE_ONCE(child->prs_err, PERR_NOCPUS);
- isolated_cpus_set_state(parent_prs, child->effective_xcpus);
- child->partition_root_state = -old_prs;
- spin_unlock_irq(&callback_lock);
- notify_partition_change(child, old_prs);
+ /* Invalidate child partition */
+ partition_disable(child, cs, -child->partition_root_state,
+ PERR_NOCPUS);
continue;
}
cpumask_andnot(new_ecpus, new_ecpus,
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH -v4 18/22] cpuset: use validate_local_partition in local_partition_enable
2026-11-01 11:41 [RFC PATCH -v4 00/22] cpuset: rework local partition logic Ridong Chen
` (16 preceding siblings ...)
2026-11-01 11:42 ` [RFC PATCH -v4 17/22] cpuset: use partition_disable for compute_partition_effective_cpumask Ridong Chen
@ 2026-11-01 11:42 ` Ridong Chen
2026-11-01 11:42 ` [RFC PATCH -v4 19/22] cpuset: introduce validate_remote_partition Ridong Chen
` (3 subsequent siblings)
21 siblings, 0 replies; 23+ messages in thread
From: Ridong Chen @ 2026-11-01 11:42 UTC (permalink / raw)
To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutn?
Cc: Guopeng Zhang, Ridong Chen, cgroups, linux-kernel, Chen Ridong,
Ridong Chen
From: Chen Ridong <chenridong@huawei.com>
Replace the partition error checks within local_partition_enable() by
utilizing the common validate_local_partition() function.
As validate_local_partition() already rejects a root partition under an
isolated parent, the PERR_ISOPARENT check in update_prstate() is no
longer needed and is removed.
validate_local_partition() and is_user_xcpus_exclusive() are moved
before local_partition_enable() without change.
Signed-off-by: Chen Ridong <chenridong@huawei.com>
Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
kernel/cgroup/cpuset.c | 131 ++++++++++++++++++-----------------------
1 file changed, 56 insertions(+), 75 deletions(-)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index af42666d4530..ecb7fd378a3f 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1978,6 +1978,61 @@ static void remote_cpus_update(struct cpuset *cs, struct cpumask *xcpus,
remote_partition_disable(cs, tmp);
}
+static bool is_user_xcpus_exclusive(struct cpuset *cs)
+{
+ struct cpuset *parent = parent_cs(cs);
+ struct cgroup_subsys_state *css;
+ struct cpuset *child;
+ bool exclusive = true;
+
+ 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();
+ return exclusive;
+}
+
+/**
+ * validate_local_partition - Validate for local partition
+ * @cs: Target cpuset to validate
+ * @new_prs: New partition root state to validate
+ * @excpus: New effective exclusive CPUs mask to validate
+ * @excl_check: Flag to enable exclusive CPUs ownership validation
+ * @tmp: Temporary masks
+ *
+ * Return: PERR_NONE if validation passes, appropriate error code otherwise
+ *
+ * Important: The caller must ensure that @cs's cpu mask is updated before
+ * invoking this function when exclusive CPU validation is required.
+ */
+static enum prs_errcode validate_local_partition(struct cpuset *cs, int new_prs,
+ struct cpumask *excpus, bool excl_check, struct tmpmasks *tmp)
+{
+ struct cpuset *parent = parent_cs(cs);
+
+ /* The parent must be a partition root */
+ if (!is_partition_valid(parent)) {
+ return is_partition_invalid(parent)
+ ? PERR_INVPARENT : PERR_NOTPART;
+ }
+
+ /* A root partition is not allowed under an isolated parent */
+ if (new_prs == PRS_ROOT && parent->partition_root_state == PRS_ISOLATED)
+ return PERR_ISOPARENT;
+
+ if (excl_check && !is_user_xcpus_exclusive(cs))
+ return PERR_NOTEXCL;
+
+ return validate_partition(cs, parent, new_prs, excpus,
+ tmp ? tmp->delmask : NULL);
+}
+
/**
* local_partition_enable - Enable local partition for a cpuset
* @cs: Target cpuset to become a local partition root
@@ -1995,18 +2050,8 @@ static int local_partition_enable(struct cpuset *cs,
lockdep_assert_cpuset_lock_held();
WARN_ON_ONCE(is_remote_partition(cs)); /* For local partition only */
- /*
- * The parent must be a partition root.
- * The new cpumask, if present, or the current cpus_allowed must
- * not be empty.
- */
- if (!is_partition_valid(parent)) {
- return is_partition_invalid(parent)
- ? PERR_INVPARENT : PERR_NOTPART;
- }
-
compute_excpus(cs, tmp->new_cpus);
- err = validate_partition(cs, parent, new_prs, tmp->new_cpus, NULL);
+ err = validate_local_partition(cs, new_prs, tmp->new_cpus, false, NULL);
if (err)
return err;
@@ -2054,61 +2099,6 @@ static void local_partition_disable(struct cpuset *cs, enum prs_errcode part_err
update_sibling_cpumasks(parent, cs, tmp);
}
-static bool is_user_xcpus_exclusive(struct cpuset *cs)
-{
- struct cpuset *parent = parent_cs(cs);
- struct cgroup_subsys_state *css;
- struct cpuset *child;
- bool exclusive = true;
-
- 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();
- return exclusive;
-}
-
-/**
- * validate_local_partition - Validate for local partition
- * @cs: Target cpuset to validate
- * @new_prs: New partition root state to validate
- * @excpus: New effective exclusive CPUs mask to validate
- * @excl_check: Flag to enable exclusive CPUs ownership validation
- * @tmp: Temporary masks
- *
- * Return: PERR_NONE if validation passes, appropriate error code otherwise
- *
- * Important: The caller must ensure that @cs's cpu mask is updated before
- * invoking this function when exclusive CPU validation is required.
- */
-static enum prs_errcode validate_local_partition(struct cpuset *cs, int new_prs,
- struct cpumask *excpus, bool excl_check, struct tmpmasks *tmp)
-{
- struct cpuset *parent = parent_cs(cs);
-
- /* The parent must be a partition root */
- if (!is_partition_valid(parent)) {
- return is_partition_invalid(parent)
- ? PERR_INVPARENT : PERR_NOTPART;
- }
-
- /* A root partition is not allowed under an isolated parent */
- if (new_prs == PRS_ROOT && parent->partition_root_state == PRS_ISOLATED)
- return PERR_ISOPARENT;
-
- if (excl_check && !is_user_xcpus_exclusive(cs))
- return PERR_NOTEXCL;
-
- return validate_partition(cs, parent, new_prs, excpus,
- tmp ? tmp->delmask : NULL);
-}
-
/**
* __local_partition_update - Update local partition configuration
* @cs: Target cpuset to update
@@ -2918,7 +2908,6 @@ static int update_prstate(struct cpuset *cs, int new_prs)
int err = PERR_NONE, old_prs = cs->partition_root_state;
struct cpuset *parent = parent_cs(cs);
struct tmpmasks tmpmask;
- int parent_prs = parent->partition_root_state;
bool isolcpus_updated = false;
bool disable_partition = false;
@@ -2961,14 +2950,6 @@ static int update_prstate(struct cpuset *cs, int new_prs)
* Otherwise, enable a remote partition.
*/
if (is_partition_valid(parent)) {
- /*
- * It is not allowed to create a root partition under
- * an isolated parent partition.
- */
- if ((new_prs == PRS_ROOT) && (parent_prs == PRS_ISOLATED)) {
- err = PERR_ISOPARENT;
- goto out;
- }
err = local_partition_enable(cs, new_prs, &tmpmask);
} else {
err = remote_partition_enable(cs, new_prs, &tmpmask);
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH -v4 19/22] cpuset: introduce validate_remote_partition
2026-11-01 11:41 [RFC PATCH -v4 00/22] cpuset: rework local partition logic Ridong Chen
` (17 preceding siblings ...)
2026-11-01 11:42 ` [RFC PATCH -v4 18/22] cpuset: use validate_local_partition in local_partition_enable Ridong Chen
@ 2026-11-01 11:42 ` Ridong Chen
2026-11-01 11:42 ` [RFC PATCH -v4 20/22] cpuset: simplify the update_prstate() function Ridong Chen
` (2 subsequent siblings)
21 siblings, 0 replies; 23+ messages in thread
From: Ridong Chen @ 2026-11-01 11:42 UTC (permalink / raw)
To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutn?
Cc: Guopeng Zhang, Ridong Chen, cgroups, linux-kernel, Chen Ridong,
Ridong Chen
From: Chen Ridong <chenridong@huawei.com>
This introduces validate_remote_partition() to consolidate validation
logic for remote partition operations. It is used by both
remote_partition_enable() and remote_cpus_update().
The new function performs the following checks:
1. Empty check: the new effective exclusive CPUs must not be empty.
2. Privilege requirement: only CAP_SYS_ADMIN can add CPUs to a
remote partition.
3. Online check: a new remote partition must have at least one
active CPU. An existing remote partition is allowed to have all
its CPUs offline, the same as before.
4. Resource check: the added CPUs can't use up all the top cpuset's
effective CPUs, and must not be in any existing sub-partitions.
5. Common partition validation via validate_partition(), including
the housekeeping conflict and tasks_nocpu_error() checks.
As a result, remote_cpus_update() reports PERR_INVCPUS instead of
PERR_NOCPUS when the added CPUs would use up the top cpuset's effective
CPUs, and checks housekeeping conflicts even when no CPU is added.
Additionally, remote_partition_disable() takes the partition error as
a parameter so that cs->prs_err can be updated centrally by
partition_disable(), instead of being written by each caller
beforehand.
Signed-off-by: Chen Ridong <chenridong@huawei.com>
Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
kernel/cgroup/cpuset.c | 120 ++++++++++++++++++++++-------------------
1 file changed, 65 insertions(+), 55 deletions(-)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index ecb7fd378a3f..1ff5f9e4c2fb 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1837,6 +1837,51 @@ static void partition_update(struct cpuset *cs, int prs, struct cpumask *xcpus,
cpuset_force_rebuild();
}
+/**
+ * validate_remote_partition - Validate for remote partition
+ * @cs: Target cpuset to validate
+ * @new_prs: New partition root state to validate
+ * @excpus: New exclusive effective CPUs mask to validate
+ * @add: exclusive CPUs to be added
+ * @del: exclusive CPUs to be deleted, can be NULL
+ *
+ * Return: PERR_NONE if validation passes, appropriate error code otherwise
+ */
+static enum prs_errcode validate_remote_partition(struct cpuset *cs,
+ int new_prs, struct cpumask *excpus, struct cpumask *add, struct cpumask *del)
+{
+ if (cpumask_empty(excpus))
+ return PERR_CPUSEMPTY;
+
+ /*
+ * The user must have sysadmin privilege.
+ */
+ if (!cpumask_empty(add) && !capable(CAP_SYS_ADMIN))
+ return PERR_ACCESS;
+
+ /*
+ * The effective_xcpus mask can contain offline CPUs, but there must
+ * be at least one or more online CPUs present before it can be enabled.
+ */
+ if (!is_remote_partition(cs) &&
+ !cpumask_intersects(excpus, cpu_active_mask))
+ return PERR_INVCPUS;
+
+ /*
+ * Additions of remote CPUs is only allowed if those CPUs are
+ * not allocated to other partitions and there are effective_cpus
+ * left in the top cpuset.
+ */
+ if (!cpumask_empty(add)) {
+ if (cpumask_subset(top_cpuset.effective_cpus, add))
+ return PERR_INVCPUS;
+ if (cpumask_intersects(add, subpartitions_cpus))
+ return PERR_NOCPUS;
+ }
+
+ return validate_partition(cs, NULL, new_prs, excpus, del);
+}
+
/*
* remote_partition_enable - Enable current cpuset as a remote partition root
* @cs: the cpuset to update
@@ -1850,30 +1895,17 @@ static void partition_update(struct cpuset *cs, int prs, struct cpumask *xcpus,
static int remote_partition_enable(struct cpuset *cs, int new_prs,
struct tmpmasks *tmp)
{
- /*
- * The user must have sysadmin privilege.
- */
- if (!capable(CAP_SYS_ADMIN))
- return PERR_ACCESS;
+ enum prs_errcode err;
/*
- * The requested exclusive_cpus must not be allocated to other
- * partitions and it can't use up all the root's effective_cpus.
- *
- * The effective_xcpus mask can contain offline CPUs, but there must
- * be at least one or more online CPUs present before it can be enabled.
- *
* Note that creating a remote partition with any local partition root
* above it or remote partition root underneath it is not allowed.
*/
compute_excpus(cs, tmp->new_cpus);
- if (!cpumask_intersects(tmp->new_cpus, cpu_active_mask) ||
- cpumask_subset(top_cpuset.effective_cpus, tmp->new_cpus))
- return PERR_INVCPUS;
- if (cpumask_intersects(tmp->new_cpus, subpartitions_cpus))
- return PERR_NOCPUS;
- if (prstate_housekeeping_conflict(new_prs, PRS_ROOT, tmp->new_cpus, NULL))
- return PERR_HKEEPING;
+ err = validate_remote_partition(cs, new_prs, tmp->new_cpus,
+ tmp->new_cpus, NULL);
+ if (err)
+ return err;
partition_enable(cs, NULL, new_prs, tmp->new_cpus);
/*
@@ -1887,13 +1919,15 @@ static int remote_partition_enable(struct cpuset *cs, int new_prs,
/*
* remote_partition_disable - Remove current cpuset from remote partition list
* @cs: the cpuset to update
+ * @prs_err: partition error, PERR_NONE when switching back to member
* @tmp: temporary masks
*
* The effective_cpus is also updated.
*
* cpuset_mutex must be held by the caller.
*/
-static void remote_partition_disable(struct cpuset *cs, struct tmpmasks *tmp)
+static void remote_partition_disable(struct cpuset *cs,
+ enum prs_errcode prs_err, struct tmpmasks *tmp)
{
int new_prs;
@@ -1907,8 +1941,8 @@ static void remote_partition_disable(struct cpuset *cs, struct tmpmasks *tmp)
WARN_ON_ONCE(!cpumask_subset(cs->effective_xcpus, subpartitions_cpus) &&
!cpumask_empty(subpartitions_cpus));
- new_prs = cs->prs_err ? -cs->partition_root_state : PRS_MEMBER;
- partition_disable(cs, NULL, new_prs, cs->prs_err);
+ new_prs = prs_err ? -cs->partition_root_state : PRS_MEMBER;
+ partition_disable(cs, NULL, new_prs, prs_err);
/*
* Propagate changes in top_cpuset's effective_cpus down the hierarchy.
@@ -1931,37 +1965,20 @@ static void remote_cpus_update(struct cpuset *cs, struct cpumask *xcpus,
struct cpumask *excpus, struct tmpmasks *tmp)
{
int prs = cs->partition_root_state;
+ enum prs_errcode err;
if (WARN_ON_ONCE(!is_remote_partition(cs)))
return;
WARN_ON_ONCE(!cpumask_subset(cs->effective_xcpus, subpartitions_cpus));
-
- if (cpumask_empty(excpus)) {
- WRITE_ONCE(cs->prs_err, PERR_CPUSEMPTY);
- goto invalidate;
- }
-
cpumask_andnot(tmp->addmask, excpus, cs->effective_xcpus);
cpumask_andnot(tmp->delmask, cs->effective_xcpus, excpus);
- /*
- * Additions of remote CPUs is only allowed if those CPUs are
- * not allocated to other partitions and there are effective_cpus
- * left in the top cpuset.
- */
- if (!cpumask_empty(tmp->addmask)) {
- WARN_ON_ONCE(cpumask_intersects(tmp->addmask, subpartitions_cpus));
- if (!capable(CAP_SYS_ADMIN))
- WRITE_ONCE(cs->prs_err, PERR_ACCESS);
- else if (cpumask_intersects(tmp->addmask, subpartitions_cpus) ||
- cpumask_subset(top_cpuset.effective_cpus, tmp->addmask))
- WRITE_ONCE(cs->prs_err, PERR_NOCPUS);
- else if (prstate_housekeeping_conflict(prs, PRS_ROOT,
- excpus, tmp->delmask))
- WRITE_ONCE(cs->prs_err, PERR_HKEEPING);
- if (cs->prs_err)
- goto invalidate;
+ err = validate_remote_partition(cs, prs, excpus, tmp->addmask,
+ tmp->delmask);
+ if (err) {
+ remote_partition_disable(cs, err, tmp);
+ return;
}
cs->remote_partition_updating = true;
@@ -1972,10 +1989,6 @@ 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:
- remote_partition_disable(cs, tmp);
}
static bool is_user_xcpus_exclusive(struct cpuset *cs)
@@ -2471,14 +2484,12 @@ static void partition_cpus_change(struct cpuset *cs, struct cpuset *trialcs,
prs_err = validate_partition(cs, parent, trialcs->partition_root_state,
trialcs->effective_xcpus, tmp->delmask);
- if (prs_err) {
- WRITE_ONCE(cs->prs_err, prs_err);
+ if (prs_err)
trialcs->prs_err = prs_err;
- }
if (is_remote_partition(cs)) {
if (trialcs->prs_err)
- remote_partition_disable(cs, tmp);
+ remote_partition_disable(cs, trialcs->prs_err, tmp);
else
remote_cpus_update(cs, trialcs->exclusive_cpus,
trialcs->effective_xcpus, tmp);
@@ -2982,7 +2993,7 @@ static int update_prstate(struct cpuset *cs, int new_prs)
if (disable_partition) {
if (is_remote_partition(cs))
- remote_partition_disable(cs, &tmpmask);
+ remote_partition_disable(cs, PERR_NONE, &tmpmask);
else
local_partition_disable(cs, PERR_NONE, &tmpmask);
/*
@@ -3928,8 +3939,7 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs, struct tmpmasks *tmp)
if (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);
+ remote_partition_disable(cs, PERR_HOTPLUG, tmp);
compute_effective_cpumask(&new_cpus, cs, parent);
}
goto update_tasks;
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH -v4 20/22] cpuset: simplify the update_prstate() function
2026-11-01 11:41 [RFC PATCH -v4 00/22] cpuset: rework local partition logic Ridong Chen
` (18 preceding siblings ...)
2026-11-01 11:42 ` [RFC PATCH -v4 19/22] cpuset: introduce validate_remote_partition Ridong Chen
@ 2026-11-01 11:42 ` Ridong Chen
2026-11-01 11:42 ` [RFC PATCH -v4 21/22] cpuset: remove prs_err clear when notify_partition_change Ridong Chen
2026-11-01 11:42 ` [RFC PATCH -v4 22/22] cpuset: remove unnecessary validation in partition_cpus_change Ridong Chen
21 siblings, 0 replies; 23+ messages in thread
From: Ridong Chen @ 2026-11-01 11:42 UTC (permalink / raw)
To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutn?
Cc: Guopeng Zhang, Ridong Chen, cgroups, linux-kernel, Chen Ridong,
Ridong Chen
From: Chen Ridong <chenridong@huawei.com>
This patch introduces partition_switch() to handle both local partition
and remote partition root state switches between "root" and "isolated".
It first validates the partition, via validate_local_partition() for a
local partition or validate_partition() for a remote one. If validation
fails, it returns an error and the partition is disabled; if validation
passes, it enables the partition with the new root state.
update_prstate_disable() is added to disable a partition from
update_prstate(). A valid partition gives back its CPUs through
remote_partition_disable() or local_partition_disable(), while a member
or an invalid partition only has its state updated through
partition_disable().
To support this, remote_partition_disable() and local_partition_disable()
now take the new partition root state from the caller instead of
deriving it from the error code.
With these helpers, update_prstate() can be simplified. The
partition-related assignments and the partition change notification in
update_prstate() are redundant, as these operations are already handled
by partition_enable() and partition_disable(). They are therefore
removed.
Signed-off-by: Chen Ridong <chenridong@huawei.com>
Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
kernel/cgroup/cpuset.c | 141 ++++++++++++++++++++++-------------------
1 file changed, 77 insertions(+), 64 deletions(-)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 1ff5f9e4c2fb..29bce718ef92 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1919,6 +1919,7 @@ static int remote_partition_enable(struct cpuset *cs, int new_prs,
/*
* remote_partition_disable - Remove current cpuset from remote partition list
* @cs: the cpuset to update
+ * @new_prs: new partition root state, PRS_MEMBER or an invalid state
* @prs_err: partition error, PERR_NONE when switching back to member
* @tmp: temporary masks
*
@@ -1926,11 +1927,9 @@ static int remote_partition_enable(struct cpuset *cs, int new_prs,
*
* cpuset_mutex must be held by the caller.
*/
-static void remote_partition_disable(struct cpuset *cs,
+static void remote_partition_disable(struct cpuset *cs, int new_prs,
enum prs_errcode prs_err, struct tmpmasks *tmp)
{
- int new_prs;
-
WARN_ON_ONCE(!is_remote_partition(cs));
/*
* When a CPU is offlined, top_cpuset may end up with no available CPUs,
@@ -1941,7 +1940,6 @@ static void remote_partition_disable(struct cpuset *cs,
WARN_ON_ONCE(!cpumask_subset(cs->effective_xcpus, subpartitions_cpus) &&
!cpumask_empty(subpartitions_cpus));
- new_prs = prs_err ? -cs->partition_root_state : PRS_MEMBER;
partition_disable(cs, NULL, new_prs, prs_err);
/*
@@ -1977,7 +1975,7 @@ static void remote_cpus_update(struct cpuset *cs, struct cpumask *xcpus,
err = validate_remote_partition(cs, prs, excpus, tmp->addmask,
tmp->delmask);
if (err) {
- remote_partition_disable(cs, err, tmp);
+ remote_partition_disable(cs, -prs, err, tmp);
return;
}
@@ -2090,14 +2088,15 @@ static int local_partition_enable(struct cpuset *cs,
/**
* local_partition_disable - Disable a local partition
* @cs: Target cpuset (local partition root) to disable
+ * @new_prs: new partition root state, PRS_MEMBER or an invalid state
* @part_error: partition error when @cs is disabled
* @tmp: Temporary masks for CPU calculations
*/
-static void local_partition_disable(struct cpuset *cs, enum prs_errcode part_error,
+static void local_partition_disable(struct cpuset *cs, int new_prs,
+ enum prs_errcode part_error,
struct tmpmasks *tmp)
{
struct cpuset *parent = parent_cs(cs);
- int new_prs;
lockdep_assert_cpuset_lock_held();
WARN_ON_ONCE(is_remote_partition(cs)); /* For local partition only */
@@ -2105,7 +2104,6 @@ static void local_partition_disable(struct cpuset *cs, enum prs_errcode part_err
if (!is_partition_valid(cs))
return;
- new_prs = part_error ? -cs->partition_root_state : PRS_MEMBER;
partition_disable(cs, parent, new_prs, part_error);
cpuset_update_tasks_cpumask(parent, tmp->addmask);
@@ -2163,7 +2161,7 @@ static int __local_partition_update(struct cpuset *cs, struct cpumask *xcpus,
* cpuset can't become a valid local partition anyway.
*/
if (is_partition_valid(cs))
- local_partition_disable(cs, part_error, tmp);
+ local_partition_disable(cs, -new_prs, part_error, tmp);
else if (is_partition_valid(parent))
WRITE_ONCE(cs->prs_err, part_error);
return part_error;
@@ -2489,13 +2487,15 @@ static void partition_cpus_change(struct cpuset *cs, struct cpuset *trialcs,
if (is_remote_partition(cs)) {
if (trialcs->prs_err)
- remote_partition_disable(cs, trialcs->prs_err, tmp);
+ remote_partition_disable(cs, -cs->partition_root_state,
+ trialcs->prs_err, tmp);
else
remote_cpus_update(cs, trialcs->exclusive_cpus,
trialcs->effective_xcpus, tmp);
} else {
if (trialcs->prs_err)
- local_partition_disable(cs, trialcs->prs_err, tmp);
+ local_partition_disable(cs, -cs->partition_root_state,
+ trialcs->prs_err, tmp);
else
__local_partition_update(cs, trialcs->exclusive_cpus,
trialcs->effective_xcpus, tmp, false);
@@ -2906,6 +2906,56 @@ bool current_cpuset_is_being_rebound(void)
return ret;
}
+/**
+ * partition_switch - Switch a valid partition between root and isolated
+ * @cs: The cpuset (local or remote partition root) to switch
+ * @new_prs: New partition root state, PRS_ROOT or PRS_ISOLATED
+ *
+ * Return: PERR_NONE if successful, error code otherwise
+ */
+static enum prs_errcode partition_switch(struct cpuset *cs, int new_prs)
+{
+ struct cpuset *parent = is_remote_partition(cs) ? NULL : parent_cs(cs);
+ enum prs_errcode err;
+
+ lockdep_assert_cpuset_lock_held();
+ WARN_ON_ONCE(new_prs <= 0 || !is_partition_valid(cs));
+ WARN_ON_ONCE(!cpuset_v2());
+
+ if (parent)
+ err = validate_local_partition(cs, new_prs, cs->effective_xcpus,
+ false, NULL);
+ else
+ err = validate_partition(cs, NULL, new_prs, cs->effective_xcpus,
+ NULL);
+ if (err)
+ return err;
+
+ partition_enable(cs, parent, new_prs, cs->effective_xcpus);
+ return PERR_NONE;
+}
+
+/*
+ * update_prstate_disable - Disable a partition from update_prstate()
+ * @cs: the cpuset to disable
+ * @new_prs: new partition root state, PRS_MEMBER or an invalid state
+ * @err: partition error
+ * @tmp: temporary masks
+ *
+ * A valid partition gives back its CPUs, a member or an invalid partition
+ * only has its state updated.
+ */
+static void update_prstate_disable(struct cpuset *cs, int new_prs,
+ enum prs_errcode err, struct tmpmasks *tmp)
+{
+ if (is_remote_partition(cs))
+ remote_partition_disable(cs, new_prs, err, tmp);
+ else if (is_partition_valid(cs))
+ local_partition_disable(cs, new_prs, err, tmp);
+ else
+ partition_disable(cs, parent_cs(cs), new_prs, err);
+}
+
/**
* update_prstate - update partition_root_state
* @cs: the cpuset to update
@@ -2919,8 +2969,6 @@ static int update_prstate(struct cpuset *cs, int new_prs)
int err = PERR_NONE, old_prs = cs->partition_root_state;
struct cpuset *parent = parent_cs(cs);
struct tmpmasks tmpmask;
- bool isolcpus_updated = false;
- bool disable_partition = false;
if (old_prs == new_prs)
return 0;
@@ -2966,68 +3014,31 @@ static int update_prstate(struct cpuset *cs, int new_prs)
err = remote_partition_enable(cs, new_prs, &tmpmask);
}
} else if (old_prs && new_prs) {
- int parent_prs = is_remote_partition(cs)
- ? PRS_ROOT : parent->partition_root_state;
-
- /*
- * A change in load balance state only, no change in cpumasks.
- * Need to update isolated_cpus.
- */
- if (prstate_housekeeping_conflict(new_prs, parent_prs,
- cs->effective_xcpus, NULL))
- err = PERR_HKEEPING;
- else if (parent_prs == PRS_ISOLATED)
- err = PERR_ISOPARENT;
-
- if (!err)
- isolcpus_updated = true;
- else
- disable_partition = true;
- } else {
- /*
- * Switching back to member is always allowed even if it
- * disables child partitions.
- */
- disable_partition = true;
- }
-
- if (disable_partition) {
- if (is_remote_partition(cs))
- remote_partition_disable(cs, PERR_NONE, &tmpmask);
- else
- local_partition_disable(cs, PERR_NONE, &tmpmask);
- /*
- * Invalidation of child partitions will be done in
- * update_cpumasks_hier() below.
- */
+ /* Root state switches, eg, root --> isolated */
+ err = partition_switch(cs, new_prs);
}
-out:
/*
- * Make partition invalid if an error happens.
+ * Switching back to member is always allowed even if it disables
+ * child partitions. Invalidation of child partitions will be done in
+ * update_cpumasks_hier() below.
*/
+ if (!new_prs)
+ update_prstate_disable(cs, PRS_MEMBER, PERR_NONE, &tmpmask);
+out:
+ /* Make partition invalid if an error happens */
if (err)
- new_prs = -new_prs;
-
- spin_lock_irq(&callback_lock);
- cs->partition_root_state = new_prs;
- WRITE_ONCE(cs->prs_err, err);
- if (!is_partition_valid(cs))
- reset_partition_data(cs);
- else if (isolcpus_updated)
- isolated_cpus_update(old_prs, new_prs, cs->effective_xcpus);
- spin_unlock_irq(&callback_lock);
+ update_prstate_disable(cs, -new_prs, err, &tmpmask);
/* Force update if partition is disabled & update effective_xcpus */
- update_cpumasks_hier(cs, &tmpmask, disable_partition);
+ update_cpumasks_hier(cs, &tmpmask, !new_prs || err);
/* A newly created partition must have effective_xcpus set */
- WARN_ON_ONCE(!old_prs && (new_prs > 0)
+ WARN_ON_ONCE(!old_prs && (new_prs > 0) && !err
&& cpumask_empty(cs->effective_xcpus));
/* Update sched domains and load balance flag */
update_partition_sd_lb(cs, old_prs);
- notify_partition_change(cs, old_prs);
free_tmpmasks(&tmpmask);
return 0;
}
@@ -3939,7 +3950,8 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs, struct tmpmasks *tmp)
if (cpumask_empty(subpartitions_cpus) ||
(cpumask_empty(&new_cpus) &&
partition_is_populated(cs, NULL))) {
- remote_partition_disable(cs, PERR_HOTPLUG, tmp);
+ remote_partition_disable(cs, -cs->partition_root_state,
+ PERR_HOTPLUG, tmp);
compute_effective_cpumask(&new_cpus, cs, parent);
}
goto update_tasks;
@@ -3952,7 +3964,8 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs, struct tmpmasks *tmp)
* transition between valid and invalid partition states.
*/
if (is_local_partition(cs) && cpumask_empty(subpartitions_cpus))
- local_partition_disable(cs, PERR_HOTPLUG, tmp);
+ local_partition_disable(cs, -cs->partition_root_state,
+ PERR_HOTPLUG, tmp);
else
local_partition_update(cs, tmp);
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH -v4 21/22] cpuset: remove prs_err clear when notify_partition_change
2026-11-01 11:41 [RFC PATCH -v4 00/22] cpuset: rework local partition logic Ridong Chen
` (19 preceding siblings ...)
2026-11-01 11:42 ` [RFC PATCH -v4 20/22] cpuset: simplify the update_prstate() function Ridong Chen
@ 2026-11-01 11:42 ` Ridong Chen
2026-11-01 11:42 ` [RFC PATCH -v4 22/22] cpuset: remove unnecessary validation in partition_cpus_change Ridong Chen
21 siblings, 0 replies; 23+ messages in thread
From: Ridong Chen @ 2026-11-01 11:42 UTC (permalink / raw)
To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutn?
Cc: Guopeng Zhang, Ridong Chen, cgroups, linux-kernel, Chen Ridong,
Ridong Chen
From: Chen Ridong <chenridong@huawei.com>
The prs_err should be properly set when partition state is set, it
doesn't have to be reset in notify_partition_change(), just remove it.
Signed-off-by: Chen Ridong <chenridong@huawei.com>
Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
kernel/cgroup/cpuset.c | 4 ----
1 file changed, 4 deletions(-)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 29bce718ef92..4fd8b0025a5a 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -262,10 +262,6 @@ static inline void notify_partition_change(struct cpuset *cs, int old_prs)
if (old_prs == cs->partition_root_state)
return;
cgroup_file_notify(&cs->partition_file);
-
- /* Reset prs_err if not invalid */
- if (is_partition_valid(cs))
- WRITE_ONCE(cs->prs_err, PERR_NONE);
}
/*
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH -v4 22/22] cpuset: remove unnecessary validation in partition_cpus_change
2026-11-01 11:41 [RFC PATCH -v4 00/22] cpuset: rework local partition logic Ridong Chen
` (20 preceding siblings ...)
2026-11-01 11:42 ` [RFC PATCH -v4 21/22] cpuset: remove prs_err clear when notify_partition_change Ridong Chen
@ 2026-11-01 11:42 ` Ridong Chen
21 siblings, 0 replies; 23+ messages in thread
From: Ridong Chen @ 2026-11-01 11:42 UTC (permalink / raw)
To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutn?
Cc: Guopeng Zhang, Ridong Chen, cgroups, linux-kernel, Chen Ridong,
Ridong Chen
From: Chen Ridong <chenridong@huawei.com>
When user-configured CPUs (including cpuset.cpus and cpuset.cpus.exclusive)
are modified, the partition may either be disabled or updated. Both local
and remote partitions update can fully validate their own validity: if the
validation fails, the partition will be disabled. Therefore, the
validation in partition_cpus_change is redundant and can be removed.
With that, trialcs->prs_err is never set, so remove its error branches
in partition_cpus_change() and its initialization in update_cpumask()
and update_exclusive_cpumask().
Signed-off-by: Chen Ridong <chenridong@huawei.com>
Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
kernel/cgroup/cpuset.c | 40 +++++-----------------------------------
1 file changed, 5 insertions(+), 35 deletions(-)
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 4fd8b0025a5a..e710e7236922 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -2459,43 +2459,15 @@ static int parse_cpuset_cpulist(const char *buf, struct cpumask *out_mask)
static void partition_cpus_change(struct cpuset *cs, struct cpuset *trialcs,
struct tmpmasks *tmp)
{
- enum prs_errcode prs_err;
- struct cpuset *parent = is_remote_partition(cs) ? NULL : parent_cs(cs);
-
if (cs_is_member(cs))
return;
- /*
- * Check for housekeeping conflict by passing the whole
- * effective_xcpus as well as those CPUs that are to be
- * removed from cs as delmask.
- */
- if (is_partition_valid(cs))
- cpumask_andnot(tmp->delmask, cs->effective_xcpus,
- trialcs->effective_xcpus);
+ if (is_remote_partition(cs))
+ remote_cpus_update(cs, trialcs->exclusive_cpus,
+ trialcs->effective_xcpus, tmp);
else
- cpumask_clear(tmp->delmask);
-
- prs_err = validate_partition(cs, parent, trialcs->partition_root_state,
- trialcs->effective_xcpus, tmp->delmask);
- if (prs_err)
- trialcs->prs_err = prs_err;
-
- if (is_remote_partition(cs)) {
- if (trialcs->prs_err)
- remote_partition_disable(cs, -cs->partition_root_state,
- trialcs->prs_err, tmp);
- else
- remote_cpus_update(cs, trialcs->exclusive_cpus,
- trialcs->effective_xcpus, tmp);
- } else {
- if (trialcs->prs_err)
- local_partition_disable(cs, -cs->partition_root_state,
- trialcs->prs_err, tmp);
- else
- __local_partition_update(cs, trialcs->exclusive_cpus,
- trialcs->effective_xcpus, tmp, false);
- }
+ __local_partition_update(cs, trialcs->exclusive_cpus,
+ trialcs->effective_xcpus, tmp, false);
}
/**
@@ -2521,7 +2493,6 @@ static int update_cpumask(struct cpuset *cs, struct cpuset *trialcs,
return 0;
compute_trialcs_excpus(trialcs, cs);
- trialcs->prs_err = PERR_NONE;
retval = validate_change(cs, trialcs);
if (retval < 0)
@@ -2597,7 +2568,6 @@ static int update_exclusive_cpumask(struct cpuset *cs, struct cpuset *trialcs,
if (alloc_tmpmasks(&tmp))
return -ENOMEM;
- trialcs->prs_err = PERR_NONE;
partition_cpus_change(cs, trialcs, &tmp);
spin_lock_irq(&callback_lock);
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
end of thread, other threads:[~2026-11-01 11:45 UTC | newest]
Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-01 11:41 [RFC PATCH -v4 00/22] cpuset: rework local partition logic Ridong Chen
2026-11-01 11:41 ` [RFC PATCH -v4 01/22] cpuset: add early empty cpumask check in partition_xcpus_add/del Ridong Chen
2026-11-01 11:41 ` [RFC PATCH -v4 02/22] cpuset: settle isolation state when CPUs are given back to parent Ridong Chen
2026-11-01 11:41 ` [RFC PATCH -v4 03/22] cpuset: generalize the validate_partition() interface Ridong Chen
2026-11-01 11:41 ` [RFC PATCH -v4 04/22] cpuset: introduce partition_enable() Ridong Chen
2026-11-01 11:41 ` [RFC PATCH -v4 05/22] cpuset: introduce partition_disable() Ridong Chen
2026-11-01 11:41 ` [RFC PATCH -v4 06/22] cpuset: introduce partition_update() Ridong Chen
2026-11-01 11:41 ` [RFC PATCH -v4 07/22] cpuset: use partition_enable() for remote partition enablement Ridong Chen
2026-11-01 11:41 ` [RFC PATCH -v4 08/22] cpuset: use partition_disable() for remote partition disablement Ridong Chen
2026-11-01 11:41 ` [RFC PATCH -v4 09/22] cpuset: use partition_update() for remote partition update Ridong Chen
2026-11-01 11:41 ` [RFC PATCH -v4 10/22] cpuset: introduce local_partition_enable() Ridong Chen
2026-11-01 11:41 ` [RFC PATCH -v4 11/22] cpuset: introduce local_partition_disable() Ridong Chen
2026-11-01 11:41 ` [RFC PATCH -v4 12/22] cpuset: use local_partition_disable() to invalidate local partition Ridong Chen
2026-11-01 11:41 ` [RFC PATCH -v4 13/22] cpuset: introduce local_partition_update() Ridong Chen
2026-11-01 11:41 ` [RFC PATCH -v4 14/22] cpuset: remove update_parent_effective_cpumask Ridong Chen
2026-11-01 11:41 ` [RFC PATCH -v4 15/22] cpuset: remove redundant partition field updates Ridong Chen
2026-11-01 11:42 ` [RFC PATCH -v4 16/22] cpuset: simplify partition update logic for hotplug tasks Ridong Chen
2026-11-01 11:42 ` [RFC PATCH -v4 17/22] cpuset: use partition_disable for compute_partition_effective_cpumask Ridong Chen
2026-11-01 11:42 ` [RFC PATCH -v4 18/22] cpuset: use validate_local_partition in local_partition_enable Ridong Chen
2026-11-01 11:42 ` [RFC PATCH -v4 19/22] cpuset: introduce validate_remote_partition Ridong Chen
2026-11-01 11:42 ` [RFC PATCH -v4 20/22] cpuset: simplify the update_prstate() function Ridong Chen
2026-11-01 11:42 ` [RFC PATCH -v4 21/22] cpuset: remove prs_err clear when notify_partition_change Ridong Chen
2026-11-01 11:42 ` [RFC PATCH -v4 22/22] cpuset: remove unnecessary validation in partition_cpus_change Ridong Chen
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox