The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH v5 00/12] cpuset: rework local partition logic
@ 2026-11-14  8:31 Ridong Chen
  2026-11-14  8:31 ` [PATCH v5 01/12] cpuset: introduce partition_enable() Ridong Chen
                   ` (11 more replies)
  0 siblings, 12 replies; 13+ messages in thread
From: Ridong Chen @ 2026-11-14  8:31 UTC (permalink / raw)
  To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutný
  Cc: Ridong Chen, Guopeng Zhang, cgroups, linux-kernel, Ridong Chen

From: Ridong Chen <chenridong@xiaomi.com>

This is v5 of the cpuset local partition rework [1], which I started
while at Huawei and am now continuing at Xiaomi. Since v4, the fixes and
preparatory changes at the start of the series have been posted
separately, last as [2], and merged into cgroup/for-7.4-fixes. This
version is based on it and only has the part that consolidates the
partition state changes into common helpers, the remaining parts will
follow.

The partition state of a cpuset is currently changed in many places, and
local and remote partitions are handled by different code.
update_parent_effective_cpumask() alone handles five partcmd_* commands
for local partitions, while the remote partition helpers open code the
same updates of partition_root_state, prs_err, effective_xcpus,
subpartitions_cpus and isolated_cpus.

The idea is to have one set of common helpers to enable, disable and
update a partition. Local and remote partitions only do their own
validation and then call these helpers, and all partition related
state is set or changed only inside them. After this series:

     update_prstate()        update_cpumask()
     update_cpumasks_hier()  cpuset_hotplug_update_tasks()
                          |
          +---------------+---------------+
          |                               |
          v                               v
  +-------------------+         +--------------------+
  |  local partition  |         |  remote partition  |
  |                   |         |                    |
  | validate_local_   |         | remote_partition_  |
  |     partition()   |         |     enable()       |
  | local_partition_  |         | remote_cpus_       |
  |     enable()      |         |     update()       |
  |     update()      |         |                    |
  +-------------------+         +--------------------+
          |                               |
          +---------------+---------------+
                          |
                          v
  +------------------------------------------------+
  |  partition_enable()                            |
  |  partition_disable()                           |
  |  partition_update()                            |
  +------------------------------------------------+
                          |
                          |  partition state, only changed here
                          v
  +------------------------------------------------+
  |  cpuset:  partition_root_state, prs_err,       |
  |           remote_partition, effective_xcpus,   |
  |           exclusive_cpus                       |
  |  global:  subpartitions_cpus, isolated_cpus    |
  +------------------------------------------------+

  partition_enable()  - make a cpuset a valid partition root, or switch
                        a valid partition between root and isolated
  partition_disable() - make a partition a member or invalid, give its
                        CPUs back to the parent and propagate the change
  partition_update()  - update the exclusive CPUs of a valid partition

A partition is disabled with partition_disable() directly, for both
local and remote partitions, as giving the CPUs back to the parent needs
no partition specific handling.

With this, every caller follows the same pattern (validate, then
enable/disable/update), and it is much easier to see where and why a
partition state changes.

Patches 1-3 introduce the common helpers and patches 4-6 use them for
remote partitions. Patches 7-11 add validate_local_partition(),
local_partition_enable() and local_partition_update() and convert the
local partition paths, including the hotplug path, to them. Patch 12
removes update_parent_effective_cpumask() and the partition_cmd enum.

The series is meant to be functionally equivalent, except that the
failure reason of an invalid partition is kept more accurately. For
example, PERR_CPUSEMPTY of a partition with empty cpuset.cpus and
cpuset.cpus.exclusive is no longer overwritten by PERR_INVCPUS when its
parent changes.

The remaining parts of the rework, e.g. validate_remote_partition(),
removing the partition field updates left outside the helpers and
simplifying update_prstate(), will be posted as a follow-up series.

test_cpuset_prs.sh passes with this series applied, with no boot option,
with isolcpus=10-11 and with nohz_full=8-11. Patches 10-12, which change
the hotplug path in between, are also tested individually.

Changes in v5:
  - Drop the RFC tag and rebase onto cgroup/for-7.4-fixes, which has
    patches 1-3 of v4 merged [2]. Only post patches 4-14 of v4.
  - Call partition_disable() directly, instead of local_ and
    remote_partition_disable().
  - Add validate_local_partition() in a separate patch.
  - Keep PERR_CPUSEMPTY for empty cpumasks (Sashiko).
  - Add __maybe_unused to unused helpers (Guopeng).
  - Link to v4: https://lore.kernel.org/all/20261101114206.2434398-1-ridong.chen@linux.dev/

[1] https://lore.kernel.org/all/20261101114206.2434398-1-ridong.chen@linux.dev/
[2] https://lore.kernel.org/all/20261112031931.2815548-1-ridong.chen@linux.dev/

Ridong Chen (12):
  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 validate_local_partition()
  cpuset: introduce local_partition_enable()
  cpuset: use partition_disable() for local partition disablement
  cpuset: use partition_disable() to invalidate local partition
  cpuset: introduce local_partition_update()
  cpuset: remove update_parent_effective_cpumask

 kernel/cgroup/cpuset.c | 856 +++++++++++++++++++----------------------
 1 file changed, 398 insertions(+), 458 deletions(-)


base-commit: e234ed1bb82c96a5643fd70784f16cf3d72eeed3
-- 
2.34.1


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v5 01/12] cpuset: introduce partition_enable()
  2026-11-14  8:31 [PATCH v5 00/12] cpuset: rework local partition logic Ridong Chen
@ 2026-11-14  8:31 ` Ridong Chen
  2026-11-14  8:31 ` [PATCH v5 02/12] cpuset: introduce partition_disable() Ridong Chen
                   ` (10 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Ridong Chen @ 2026-11-14  8:31 UTC (permalink / raw)
  To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutný
  Cc: Ridong Chen, Guopeng Zhang, cgroups, linux-kernel, Ridong Chen

From: Ridong Chen <chenridong@xiaomi.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 the isolation state of the CPUs in @new_excpus is updated. The
caller passes the CPUs used by the partition itself, as the CPUs of its
valid child partitions keep their own state.

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: Ridong Chen <chenridong@xiaomi.com>
---
 kernel/cgroup/cpuset.c | 56 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 56 insertions(+)

diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 7e374210c123..860cb7ea3efb 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1702,6 +1702,62 @@ 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, or the
+ *              CPUs whose isolation state changes when switching between
+ *              root and isolated
+ *
+ * Transitions a cpuset to a partition root, only for v2.
+ * It also supports the transition between root and isolated partition.
+ */
+static void __maybe_unused 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.
+		 * @new_excpus holds the CPUs whose isolation state changes.
+		 */
+		isolated_cpus_update(new_prs, new_excpus);
+	} 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] 13+ messages in thread

* [PATCH v5 02/12] cpuset: introduce partition_disable()
  2026-11-14  8:31 [PATCH v5 00/12] cpuset: rework local partition logic Ridong Chen
  2026-11-14  8:31 ` [PATCH v5 01/12] cpuset: introduce partition_enable() Ridong Chen
@ 2026-11-14  8:31 ` Ridong Chen
  2026-11-14  8:31 ` [PATCH v5 03/12] cpuset: introduce partition_update() Ridong Chen
                   ` (9 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Ridong Chen @ 2026-11-14  8:31 UTC (permalink / raw)
  To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutný
  Cc: Ridong Chen, Guopeng Zhang, cgroups, linux-kernel, Ridong Chen

From: Ridong Chen <chenridong@xiaomi.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
7. Propagating the change of the parent's effective_cpus

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.

The parent is derived from @cs, top_cpuset for a remote partition.
When a valid partition gives back its CPUs, the change of the parent's
effective_cpus is propagated to the tasks of the parent and to its
other children. A member or an invalid partition only has its state
updated, so that all the callers can call partition_disable()
directly.

Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
 kernel/cgroup/cpuset.c | 66 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 66 insertions(+)

diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 860cb7ea3efb..12bdcaeb8ae5 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1758,6 +1758,72 @@ static void __maybe_unused partition_enable(struct cpuset *cs, struct cpuset *pa
 	notify_partition_change(cs, old_prs);
 }
 
+/**
+ * partition_disable - Disable partition root state for a cpuset
+ * @cs: The cpuset to disable partition for
+ * @new_prs: New partition root state (should be non-positive)
+ * @prs_err: Error code to set if disabling due to validation failure
+ * @tmp: Temporary masks
+ *
+ * A valid partition gives its CPUs back to its parent, or to top_cpuset
+ * for a remote partition, and the change is propagated to the tasks and
+ * the other children of the parent. A member or an invalid partition only
+ * has its state updated. A valid partition under an invalid parent has
+ * already given back its CPUs, so only the propagation is done.
+ */
+static void __maybe_unused partition_disable(struct cpuset *cs, int new_prs,
+					     enum prs_errcode prs_err,
+					     struct tmpmasks *tmp)
+{
+	struct cpuset *parent = is_remote_partition(cs) ? NULL : parent_cs(cs);
+	int old_prs = cs->partition_root_state;
+
+	lockdep_assert_cpuset_lock_held();
+	WARN_ON_ONCE(new_prs > 0);
+	WARN_ON_ONCE(!cpuset_v2());
+	/*
+	 * When a CPU is offlined, top_cpuset may end up with no available CPUs,
+	 * which should clear subpartitions_cpus. We should not emit a warning for this
+	 * scenario: the hierarchy is updated from top to bottom, so subpartitions_cpus
+	 * may already be cleared when disabling a remote partition.
+	 */
+	if (!parent)
+		WARN_ON_ONCE(!cpumask_subset(cs->effective_xcpus, subpartitions_cpus) &&
+			     !cpumask_empty(subpartitions_cpus));
+
+	spin_lock_irq(&callback_lock);
+	cs->remote_partition = false;
+	/*
+	 * Disabling 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);
+
+	/* A member or an invalid partition owns no CPU, nothing to propagate */
+	if (old_prs <= 0)
+		return;
+
+	/* Propagate changes in the parent's effective_cpus down the hierarchy */
+	if (!parent)
+		parent = &top_cpuset;
+	cpuset_update_tasks_cpumask(parent, tmp->new_cpus);
+	update_sibling_cpumasks(parent, cs, tmp);
+}
+
 /*
  * 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] 13+ messages in thread

* [PATCH v5 03/12] cpuset: introduce partition_update()
  2026-11-14  8:31 [PATCH v5 00/12] cpuset: rework local partition logic Ridong Chen
  2026-11-14  8:31 ` [PATCH v5 01/12] cpuset: introduce partition_enable() Ridong Chen
  2026-11-14  8:31 ` [PATCH v5 02/12] cpuset: introduce partition_disable() Ridong Chen
@ 2026-11-14  8:31 ` Ridong Chen
  2026-11-14  8:31 ` [PATCH v5 04/12] cpuset: use partition_enable() for remote partition enablement Ridong Chen
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Ridong Chen @ 2026-11-14  8:31 UTC (permalink / raw)
  To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutný
  Cc: Ridong Chen, Guopeng Zhang, cgroups, linux-kernel, Ridong Chen

From: Ridong Chen <chenridong@xiaomi.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: Ridong Chen <chenridong@xiaomi.com>
---
 kernel/cgroup/cpuset.c | 52 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 12bdcaeb8ae5..c66864faab32 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1824,6 +1824,58 @@ static void __maybe_unused partition_disable(struct cpuset *cs, int new_prs,
 	update_sibling_cpumasks(parent, cs, tmp);
 }
 
+/**
+ * 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 __maybe_unused 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() called by the callers afterwards 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] 13+ messages in thread

* [PATCH v5 04/12] cpuset: use partition_enable() for remote partition enablement
  2026-11-14  8:31 [PATCH v5 00/12] cpuset: rework local partition logic Ridong Chen
                   ` (2 preceding siblings ...)
  2026-11-14  8:31 ` [PATCH v5 03/12] cpuset: introduce partition_update() Ridong Chen
@ 2026-11-14  8:31 ` Ridong Chen
  2026-11-14  8:31 ` [PATCH v5 05/12] cpuset: use partition_disable() for remote partition disablement Ridong Chen
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Ridong Chen @ 2026-11-14  8:31 UTC (permalink / raw)
  To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutný
  Cc: Ridong Chen, Guopeng Zhang, cgroups, linux-kernel, Ridong Chen

From: Ridong Chen <chenridong@xiaomi.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.

As partition_enable() also sends the partition change notification,
update_prstate() may notify again at its end. cgroup_file_notify()
rate-limits the notifications, and the one in update_prstate() will be
removed later.

Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
 kernel/cgroup/cpuset.c | 13 +++----------
 1 file changed, 3 insertions(+), 10 deletions(-)

diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index c66864faab32..3702e43e95ef 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1725,8 +1725,8 @@ static void partition_state_update(struct cpuset *cs, int new_prs,
  * Transitions a cpuset to a partition root, only for v2.
  * It also supports the transition between root and isolated partition.
  */
-static void __maybe_unused partition_enable(struct cpuset *cs, struct cpuset *parent,
-					    int new_prs, struct cpumask *new_excpus)
+static void partition_enable(struct cpuset *cs, struct cpuset *parent,
+			     int new_prs, struct cpumask *new_excpus)
 {
 	int old_prs = cs->partition_root_state;
 
@@ -1914,14 +1914,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] 13+ messages in thread

* [PATCH v5 05/12] cpuset: use partition_disable() for remote partition disablement
  2026-11-14  8:31 [PATCH v5 00/12] cpuset: rework local partition logic Ridong Chen
                   ` (3 preceding siblings ...)
  2026-11-14  8:31 ` [PATCH v5 04/12] cpuset: use partition_enable() for remote partition enablement Ridong Chen
@ 2026-11-14  8:31 ` Ridong Chen
  2026-11-14  8:31 ` [PATCH v5 06/12] cpuset: use partition_update() for remote partition update Ridong Chen
                   ` (6 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Ridong Chen @ 2026-11-14  8:31 UTC (permalink / raw)
  To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutný
  Cc: Ridong Chen, Guopeng Zhang, cgroups, linux-kernel, Ridong Chen

From: Ridong Chen <chenridong@xiaomi.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.

remote_partition_disable() is removed and its callers call
partition_disable() directly. The new partition root state is passed
in by the caller instead of being derived from cs->prs_err, as only the
caller knows which state the partition is going to.

Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
 kernel/cgroup/cpuset.c | 57 ++++++------------------------------------
 1 file changed, 8 insertions(+), 49 deletions(-)

diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 3702e43e95ef..681adcde10e8 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1771,9 +1771,8 @@ static void partition_enable(struct cpuset *cs, struct cpuset *parent,
  * has its state updated. A valid partition under an invalid parent has
  * already given back its CPUs, so only the propagation is done.
  */
-static void __maybe_unused partition_disable(struct cpuset *cs, int new_prs,
-					     enum prs_errcode prs_err,
-					     struct tmpmasks *tmp)
+static void partition_disable(struct cpuset *cs, int new_prs,
+			      enum prs_errcode prs_err, struct tmpmasks *tmp)
 {
 	struct cpuset *parent = is_remote_partition(cs) ? NULL : parent_cs(cs);
 	int old_prs = cs->partition_root_state;
@@ -1923,48 +1922,6 @@ static int remote_partition_enable(struct cpuset *cs, int new_prs,
 	return 0;
 }
 
-/*
- * remote_partition_disable - Remove current cpuset from remote partition list
- * @cs: the cpuset to update
- * @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)
-{
-	WARN_ON_ONCE(!is_remote_partition(cs));
-	/*
-	 * When a CPU is offlined, top_cpuset may end up with no available CPUs,
-	 * which should clear subpartitions_cpus. We should not emit a warning for this
-	 * scenario: the hierarchy is updated from top to bottom, so subpartitions_cpus
-	 * may already be cleared when disabling the partition.
-	 */
-	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();
-
-	/*
-	 * Propagate changes in top_cpuset's effective_cpus down the hierarchy.
-	 */
-	cpuset_update_tasks_cpumask(&top_cpuset, tmp->new_cpus);
-	update_sibling_cpumasks(&top_cpuset, NULL, tmp);
-}
-
 /*
  * remote_cpus_update - cpus_exclusive change of remote partition
  * @cs: the cpuset to be updated
@@ -2039,7 +1996,7 @@ static void remote_cpus_update(struct cpuset *cs, struct cpumask *xcpus,
 	return;
 
 invalidate:
-	remote_partition_disable(cs, tmp);
+	partition_disable(cs, -prs, cs->prs_err, tmp);
 }
 
 /**
@@ -2667,7 +2624,8 @@ static void partition_cpus_change(struct cpuset *cs, struct cpuset *trialcs,
 
 	if (is_remote_partition(cs)) {
 		if (trialcs->prs_err)
-			remote_partition_disable(cs, tmp);
+			partition_disable(cs, -cs->partition_root_state,
+					  cs->prs_err, tmp);
 		else
 			remote_cpus_update(cs, trialcs->exclusive_cpus,
 					   trialcs->effective_xcpus, tmp);
@@ -3215,7 +3173,7 @@ static int update_prstate(struct cpuset *cs, int new_prs)
 
 	if (disable_partition) {
 		if (is_remote_partition(cs))
-			remote_partition_disable(cs, &tmpmask);
+			partition_disable(cs, PRS_MEMBER, PERR_NONE, &tmpmask);
 		else
 			update_parent_effective_cpumask(cs, partcmd_disable,
 							NULL, &tmpmask);
@@ -4164,7 +4122,8 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs, struct tmpmasks *tmp)
 				       (cpumask_empty(&new_cpus) &&
 					partition_is_populated(cs, NULL)))) {
 		WRITE_ONCE(cs->prs_err, PERR_HOTPLUG);
-		remote_partition_disable(cs, tmp);
+		partition_disable(cs, -cs->partition_root_state, cs->prs_err,
+				  tmp);
 		compute_effective_cpumask(&new_cpus, cs, parent);
 	}
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v5 06/12] cpuset: use partition_update() for remote partition update
  2026-11-14  8:31 [PATCH v5 00/12] cpuset: rework local partition logic Ridong Chen
                   ` (4 preceding siblings ...)
  2026-11-14  8:31 ` [PATCH v5 05/12] cpuset: use partition_disable() for remote partition disablement Ridong Chen
@ 2026-11-14  8:31 ` Ridong Chen
  2026-11-14  8:31 ` [PATCH v5 07/12] cpuset: introduce validate_local_partition() Ridong Chen
                   ` (5 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Ridong Chen @ 2026-11-14  8:31 UTC (permalink / raw)
  To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutný
  Cc: Ridong Chen, Guopeng Zhang, cgroups, linux-kernel, Ridong Chen

From: Ridong Chen <chenridong@xiaomi.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: Ridong Chen <chenridong@xiaomi.com>
---
 kernel/cgroup/cpuset.c | 28 ++++++----------------------
 1 file changed, 6 insertions(+), 22 deletions(-)

diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 681adcde10e8..7a3e797aee52 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1835,8 +1835,8 @@ static void partition_disable(struct cpuset *cs, int new_prs,
  * will be added to the subpartitions_cpus and removed from parent's
  * effective_cpus, and the tmp->delmask vice versa.
  */
-static void __maybe_unused partition_update(struct cpuset *cs, int prs, struct cpumask *xcpus,
-					    struct cpumask *excpus, struct tmpmasks *tmp)
+static void partition_update(struct cpuset *cs, int prs, struct cpumask *xcpus,
+			     struct cpumask *excpus, struct tmpmasks *tmp)
 {
 	struct cpuset *parent;
 
@@ -1935,7 +1935,6 @@ static int remote_partition_enable(struct cpuset *cs, int new_prs,
 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)))
@@ -1948,15 +1947,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);
@@ -1971,22 +1970,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] 13+ messages in thread

* [PATCH v5 07/12] cpuset: introduce validate_local_partition()
  2026-11-14  8:31 [PATCH v5 00/12] cpuset: rework local partition logic Ridong Chen
                   ` (5 preceding siblings ...)
  2026-11-14  8:31 ` [PATCH v5 06/12] cpuset: use partition_update() for remote partition update Ridong Chen
@ 2026-11-14  8:31 ` Ridong Chen
  2026-11-14  8:31 ` [PATCH v5 08/12] cpuset: introduce local_partition_enable() Ridong Chen
                   ` (4 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Ridong Chen @ 2026-11-14  8:31 UTC (permalink / raw)
  To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutný
  Cc: Ridong Chen, Guopeng Zhang, cgroups, linux-kernel, Ridong Chen

From: Ridong Chen <chenridong@xiaomi.com>

Add validate_local_partition() to check the requirements of a local
partition in one place, so that local partition enablement and update
can share it:

1. The parent must be a valid partition root, otherwise PERR_INVPARENT
   or PERR_NOTPART is returned.
2. If both cpuset.cpus and cpuset.cpus.exclusive are empty, return
   PERR_CPUSEMPTY rather than reporting an invalid cpu list.
3. A root partition is not allowed under an isolated parent.
4. Optionally, the user exclusive CPUs must not overlap with any
   sibling. A valid partition has passed this check already, so it is
   only needed when an invalid partition is to become valid again.
5. validate_partition() for the common checks, including the
   housekeeping conflict and tasks_nocpu_error() checks.

It is marked __maybe_unused until local_partition_enable() starts to
use it in the next patch.

Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
 kernel/cgroup/cpuset.c | 61 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 61 insertions(+)

diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 7a3e797aee52..78ca3c39c029 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1983,6 +1983,67 @@ static void remote_cpus_update(struct cpuset *cs, struct cpumask *xcpus,
 	partition_disable(cs, -prs, cs->prs_err, 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 __maybe_unused
+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;
+	}
+
+	/* Report the empty user cpumasks instead of an invalid cpu list */
+	if (cpumask_empty(excpus) && xcpus_empty(cs))
+		return PERR_CPUSEMPTY;
+
+	/* 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);
+}
+
 /**
  * update_parent_effective_cpumask - update effective_cpus mask of parent cpuset
  * @cs:      The cpuset that requests change in partition root state
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v5 08/12] cpuset: introduce local_partition_enable()
  2026-11-14  8:31 [PATCH v5 00/12] cpuset: rework local partition logic Ridong Chen
                   ` (6 preceding siblings ...)
  2026-11-14  8:31 ` [PATCH v5 07/12] cpuset: introduce validate_local_partition() Ridong Chen
@ 2026-11-14  8:31 ` Ridong Chen
  2026-11-14  8:31 ` [PATCH v5 09/12] cpuset: use partition_disable() for local partition disablement Ridong Chen
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Ridong Chen @ 2026-11-14  8:31 UTC (permalink / raw)
  To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutný
  Cc: Ridong Chen, Guopeng Zhang, cgroups, linux-kernel, Ridong Chen

From: Ridong Chen <chenridong@xiaomi.com>

Factor the local partition enablement out of
update_parent_effective_cpumask() into local_partition_enable(). It
computes the effective exclusive CPUs of the cpuset, validates them
with validate_local_partition() and then calls partition_enable() to
set up the partition, the same way as remote_partition_enable() does
for a remote partition.

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.

Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
 kernel/cgroup/cpuset.c | 84 +++++++++++++++++++++++-------------------
 1 file changed, 46 insertions(+), 38 deletions(-)

diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 78ca3c39c029..45605c6ab6a2 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -2016,10 +2016,9 @@ static bool is_user_xcpus_exclusive(struct cpuset *cs)
  * 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 __maybe_unused
-validate_local_partition(struct cpuset *cs, int new_prs,
-			 struct cpumask *excpus, bool excl_check,
-			 struct tmpmasks *tmp)
+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);
 
@@ -2044,6 +2043,47 @@ validate_local_partition(struct cpuset *cs, int new_prs,
 				  tmp ? tmp->delmask : NULL);
 }
 
+/**
+ * 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 */
+
+	compute_excpus(cs, tmp->new_cpus);
+	err = validate_local_partition(cs, new_prs, tmp->new_cpus, false, 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.
+	 *
+	 * (new 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
@@ -2142,27 +2182,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)
@@ -3112,7 +3132,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;
 
@@ -3155,18 +3174,7 @@ 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.
-			 */
-			if ((new_prs == PRS_ROOT) && (parent_prs == PRS_ISOLATED)) {
-				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] 13+ messages in thread

* [PATCH v5 09/12] cpuset: use partition_disable() for local partition disablement
  2026-11-14  8:31 [PATCH v5 00/12] cpuset: rework local partition logic Ridong Chen
                   ` (7 preceding siblings ...)
  2026-11-14  8:31 ` [PATCH v5 08/12] cpuset: introduce local_partition_enable() Ridong Chen
@ 2026-11-14  8:31 ` Ridong Chen
  2026-11-14  8:31 ` [PATCH v5 10/12] cpuset: use partition_disable() to invalidate local partition Ridong Chen
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Ridong Chen @ 2026-11-14  8:31 UTC (permalink / raw)
  To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutný
  Cc: Ridong Chen, Guopeng Zhang, cgroups, linux-kernel, Ridong Chen

From: Ridong Chen <chenridong@xiaomi.com>

Replace the partcmd_disable handling of update_parent_effective_cpumask()
with partition_disable(), the same as for remote partition disablement.
partition_disable() gives the CPUs of a valid partition back to its
parent and propagates the change of the parent's effective_cpus to
the tasks and the other children of the parent.

An invalid partition has already given back its CPUs, so
update_prstate() only calls partition_disable() for a valid partition
and keeps setting the state of an invalid one itself.

Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
 kernel/cgroup/cpuset.c | 19 ++-----------------
 1 file changed, 2 insertions(+), 17 deletions(-)

diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 45605c6ab6a2..211567c61e95 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -2182,18 +2182,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
 		 */
@@ -3225,12 +3214,8 @@ static int update_prstate(struct cpuset *cs, int new_prs)
 	}
 
 	if (disable_partition) {
-		if (is_remote_partition(cs))
+		if (is_partition_valid(cs))
 			partition_disable(cs, PRS_MEMBER, PERR_NONE, &tmpmask);
-		else
-			update_parent_effective_cpumask(cs, partcmd_disable,
-							NULL, &tmpmask);
-
 		/*
 		 * Invalidation of child partitions will be done in
 		 * update_cpumasks_hier() below.
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v5 10/12] cpuset: use partition_disable() to invalidate local partition
  2026-11-14  8:31 [PATCH v5 00/12] cpuset: rework local partition logic Ridong Chen
                   ` (8 preceding siblings ...)
  2026-11-14  8:31 ` [PATCH v5 09/12] cpuset: use partition_disable() for local partition disablement Ridong Chen
@ 2026-11-14  8:31 ` Ridong Chen
  2026-11-14  8:31 ` [PATCH v5 11/12] cpuset: introduce local_partition_update() Ridong Chen
  2026-11-14  8:31 ` [PATCH v5 12/12] cpuset: remove update_parent_effective_cpumask Ridong Chen
  11 siblings, 0 replies; 13+ messages in thread
From: Ridong Chen @ 2026-11-14  8:31 UTC (permalink / raw)
  To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutný
  Cc: Ridong Chen, Guopeng Zhang, cgroups, linux-kernel, Ridong Chen

From: Ridong Chen <chenridong@xiaomi.com>

Use partition_disable() to handle local partition invalidation as well,
replacing partcmd_invalidate in partition_cpus_change(). An invalid
partition has already given back its CPUs, so partition_cpus_change()
leaves it alone. cpuset_hotplug_update_tasks() is converted later
together with the partition revival, by local_partition_update().

As 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.

Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
 kernel/cgroup/cpuset.c | 40 ++++++++++++++++++++++++----------------
 1 file changed, 24 insertions(+), 16 deletions(-)

diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 211567c61e95..038bed1f4fb8 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -2367,20 +2367,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 cleared or recomputed 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);
@@ -2684,12 +2692,12 @@ static void partition_cpus_change(struct cpuset *cs, struct cpuset *trialcs,
 			remote_cpus_update(cs, trialcs->exclusive_cpus,
 					   trialcs->effective_xcpus, tmp);
 	} else {
-		if (trialcs->prs_err)
-			update_parent_effective_cpumask(cs, partcmd_invalidate,
-							NULL, tmp);
-		else
+		if (!trialcs->prs_err)
 			update_parent_effective_cpumask(cs, partcmd_update,
 							trialcs->effective_xcpus, tmp);
+		else if (is_partition_valid(cs))
+			partition_disable(cs, -cs->partition_root_state,
+					  trialcs->prs_err, tmp);
 	}
 }
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v5 11/12] cpuset: introduce local_partition_update()
  2026-11-14  8:31 [PATCH v5 00/12] cpuset: rework local partition logic Ridong Chen
                   ` (9 preceding siblings ...)
  2026-11-14  8:31 ` [PATCH v5 10/12] cpuset: use partition_disable() to invalidate local partition Ridong Chen
@ 2026-11-14  8:31 ` Ridong Chen
  2026-11-14  8:31 ` [PATCH v5 12/12] cpuset: remove update_parent_effective_cpumask Ridong Chen
  11 siblings, 0 replies; 13+ messages in thread
From: Ridong Chen @ 2026-11-14  8:31 UTC (permalink / raw)
  To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutný
  Cc: Ridong Chen, Guopeng Zhang, cgroups, linux-kernel, Ridong Chen

From: Ridong Chen <chenridong@xiaomi.com>

Factor the partcmd_update handling without a new cpumask out of
update_parent_effective_cpumask() into local_partition_update(), and
the one with a new cpumask, used by partition_cpus_change(), into
__local_partition_update().

__local_partition_update() validates the new effective exclusive CPUs
with validate_local_partition(), then:

 - if the validation fails, a valid partition is invalidated with
   partition_disable(), while an invalid partition only has its error
   code updated. The error code is kept if the parent is not a valid
   partition root, e.g. for a remote partition invalidated by hotplug,
   as the cpuset can't become a valid local partition anyway.
 - if the validation passes, an invalid partition becomes valid again
   with partition_enable(), and a valid partition has its CPUs updated
   with partition_update().

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.

cpuset_hotplug_update_tasks() now calls local_partition_update() for
both the invalidation and the revival of a local partition, instead of
open coding the invalidation conditions. The only exception is when
subpartitions_cpus becomes empty because no CPU is left for
top_cpuset, in which case the local partition is disabled directly
with PERR_HOTPLUG. A remote partition only needs the hotplug
invalidation check, so jump to update_tasks right after it. As
partition_disable() gives the CPUs back to the parent and updates
effective_cpus of the cpuset directly, effective_cpus is saved before
any partition state change and the new effective cpumask is compared
against it. Otherwise the tasks of a partition invalidated by hotplug
may be left with only the offlined CPUs.

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: Ridong Chen <chenridong@xiaomi.com>
---
 kernel/cgroup/cpuset.c | 201 +++++++++++++++++++++++++++++------------
 1 file changed, 143 insertions(+), 58 deletions(-)

diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 038bed1f4fb8..1eb7d11e1cbf 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -2084,6 +2084,100 @@ static int local_partition_enable(struct cpuset *cs,
 	return 0;
 }
 
+/**
+ * __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))
+			partition_disable(cs, -new_prs, 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
@@ -2121,9 +2215,9 @@ static int local_partition_enable(struct cpuset *cs,
  * 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)
+static int __maybe_unused 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	*/
@@ -2143,23 +2237,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
@@ -2527,14 +2604,21 @@ 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. Always recompute effective cpumask as tmp->new_cpus
 			 * may be clobbered when update_sibling_cpumasks() is called
-			 * within update_parent_effective_cpumask().
+			 * within local_partition_update().
 			 */
 			new_prs = cp->partition_root_state;
 			compute_effective_cpumask(tmp->new_cpus, cp, parent);
@@ -2693,8 +2777,8 @@ 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_update,
-							trialcs->effective_xcpus, tmp);
+			__local_partition_update(cs, trialcs->exclusive_cpus,
+						 trialcs->effective_xcpus, tmp, false);
 		else if (is_partition_valid(cs))
 			partition_disable(cs, -cs->partition_root_state,
 					  trialcs->prs_err, tmp);
@@ -4149,61 +4233,62 @@ 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;
-	int partcmd = -1;
 	struct cpuset *parent;
 
 	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);
 
 	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);
-		partition_disable(cs, -cs->partition_root_state, cs->prs_err,
-				  tmp);
-		compute_effective_cpumask(&new_cpus, cs, parent);
+	/*
+	 * Compute effective_cpus for valid partition root, may invalidate
+	 * child partition roots if necessary.
+	 */
+	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);
+			partition_disable(cs, -cs->partition_root_state,
+					  cs->prs_err, tmp);
+			compute_effective_cpumask(&new_cpus, cs, parent);
+		}
+		goto update_tasks;
 	}
 
 	/*
-	 * 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.
+	 * 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.
 	 */
-	if (is_local_partition(cs) &&
-	    (!is_partition_valid(parent) ||
-	     tasks_nocpu_error(parent, cs, &new_cpus) ||
-	     cpumask_empty(subpartitions_cpus)))
-		partcmd = partcmd_invalidate;
+	if (is_local_partition(cs) && cpumask_empty(subpartitions_cpus))
+		partition_disable(cs, -cs->partition_root_state, PERR_HOTPLUG,
+				  tmp);
+	else
+		local_partition_update(cs, tmp);
+
 	/*
-	 * On the other hand, an invalid partition root may be transitioned
-	 * back to a regular one with a non-empty effective xcpus.
+	 * Recompute effective CPU mask after partition state update:
+	 * - For valid partitions: calculate partition-specific effective CPUs
+	 * - For invalid partitions: compute member effective CPU mask
 	 */
-	else if (is_partition_valid(parent) && is_partition_invalid(cs) &&
-		 !cpumask_empty(cs->effective_xcpus))
-		partcmd = partcmd_update;
-
-	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();
-		}
-	}
+	compute_effective_cpumask(&new_cpus, cs, parent);
 
 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] 13+ messages in thread

* [PATCH v5 12/12] cpuset: remove update_parent_effective_cpumask
  2026-11-14  8:31 [PATCH v5 00/12] cpuset: rework local partition logic Ridong Chen
                   ` (10 preceding siblings ...)
  2026-11-14  8:31 ` [PATCH v5 11/12] cpuset: introduce local_partition_update() Ridong Chen
@ 2026-11-14  8:31 ` Ridong Chen
  11 siblings, 0 replies; 13+ messages in thread
From: Ridong Chen @ 2026-11-14  8:31 UTC (permalink / raw)
  To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutný
  Cc: Ridong Chen, Guopeng Zhang, cgroups, linux-kernel, Ridong Chen

From: Ridong Chen <chenridong@xiaomi.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.

The partition_cmd enum type is removed as well, as it is no longer
referenced.

Update the comment about where subpartitions_cpus and isolated_cpus are
updated, which is now done in the common partition helpers for both
local and remote partitions.

Signed-off-by: Ridong Chen <chenridong@xiaomi.com>
---
 kernel/cgroup/cpuset.c | 329 +----------------------------------------
 1 file changed, 6 insertions(+), 323 deletions(-)

diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 1eb7d11e1cbf..b4255c033310 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -133,9 +133,9 @@ 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
- * the remote_partition_*() and remote_cpus_update() helpers.
+ * Updates to subpartitions_cpus & isolated_cpus for both local and remote
+ * partitions are done in the partition_enable(), partition_disable() and
+ * partition_update() helpers.
  */
 /*
  * Exclusive CPUs distributed out to local or remote sub-partitions of
@@ -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);
 
@@ -1603,7 +1592,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,
@@ -2178,312 +2167,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 __maybe_unused 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))) {
-			/* Parent partition state changed to PRS_ISOLATED */
-			part_error = PERR_ISOPARENT;
-			if (old_prs == PRS_ROOT) {
-				cpumask_copy(tmp->addmask, cs->effective_xcpus);
-				adding = true;
-			}
-		} else if (is_partition_valid(cs) &&
-			   !cpumask_equal(tmp->delmask, cs->effective_xcpus)) {
-			/*
-			 * If there is an expansion of parent's effective_xcpus,
-			 * we may take more xcpus from parent. The parent's
-			 * effective_cpus should still have active CPUs or nocpu
-			 * should have been set. We also need to do a
-			 * housekeeping check for an isolated child with root
-			 * parent to make sure that it won't exhaust all the
-			 * active housekeeping CPUs.
-			 */
-			cpumask_andnot(tmp->addmask, cs->effective_xcpus, tmp->delmask);
-			if ((parent_prs == PRS_ROOT) && (old_prs == PRS_ISOLATED) &&
-			    prstate_housekeeping_conflict(old_prs, parent_prs,
-							  tmp->delmask, tmp->addmask)) {
-				adding = cpumask_and(tmp->addmask, cs->effective_xcpus,
-						     tmp->delmask);
-				part_error = PERR_HKEEPING;
-			} else {
-				deleting = cpumask_andnot(tmp->delmask, tmp->delmask,
-							  cs->effective_xcpus);
-			}
-		} else if (is_partition_invalid(cs)) {
-			struct cgroup_subsys_state *css;
-			struct cpuset *child;
-			bool exclusive = true;
-
-			/*
-			 * Convert invalid partition to valid has to pass the
-			 * cpu exclusivity test 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;
-		}
-	}
-
-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 cleared or recomputed 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
@@ -2546,7 +2229,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;
@@ -2570,7 +2253,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] 13+ messages in thread

end of thread, other threads:[~2026-11-14  8:32 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-14  8:31 [PATCH v5 00/12] cpuset: rework local partition logic Ridong Chen
2026-11-14  8:31 ` [PATCH v5 01/12] cpuset: introduce partition_enable() Ridong Chen
2026-11-14  8:31 ` [PATCH v5 02/12] cpuset: introduce partition_disable() Ridong Chen
2026-11-14  8:31 ` [PATCH v5 03/12] cpuset: introduce partition_update() Ridong Chen
2026-11-14  8:31 ` [PATCH v5 04/12] cpuset: use partition_enable() for remote partition enablement Ridong Chen
2026-11-14  8:31 ` [PATCH v5 05/12] cpuset: use partition_disable() for remote partition disablement Ridong Chen
2026-11-14  8:31 ` [PATCH v5 06/12] cpuset: use partition_update() for remote partition update Ridong Chen
2026-11-14  8:31 ` [PATCH v5 07/12] cpuset: introduce validate_local_partition() Ridong Chen
2026-11-14  8:31 ` [PATCH v5 08/12] cpuset: introduce local_partition_enable() Ridong Chen
2026-11-14  8:31 ` [PATCH v5 09/12] cpuset: use partition_disable() for local partition disablement Ridong Chen
2026-11-14  8:31 ` [PATCH v5 10/12] cpuset: use partition_disable() to invalidate local partition Ridong Chen
2026-11-14  8:31 ` [PATCH v5 11/12] cpuset: introduce local_partition_update() Ridong Chen
2026-11-14  8:31 ` [PATCH v5 12/12] cpuset: remove update_parent_effective_cpumask Ridong Chen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox