All of lore.kernel.org
 help / color / mirror / Atom feed
From: Guopeng Zhang <guopeng.zhang@linux.dev>
To: Waiman Long <longman@redhat.com>, Ridong Chen <ridong.chen@linux.dev>
Cc: "Tejun Heo" <tj@kernel.org>,
	"Johannes Weiner" <hannes@cmpxchg.org>,
	"Michal Koutný" <mkoutny@suse.com>,
	cgroups@vger.kernel.org, linux-kernel@vger.kernel.org,
	"Guopeng Zhang" <zhangguopeng@kylinos.cn>
Subject: [PATCH v4 4/7] cgroup/cpuset: Fix child invalidation after parent CPU changes
Date: Thu, 10 Sep 2026 17:45:43 +0800	[thread overview]
Message-ID: <20260910094546.5852-5-guopeng.zhang@linux.dev> (raw)
In-Reply-To: <20260910094546.5852-1-guopeng.zhang@linux.dev>

From: Guopeng Zhang <zhangguopeng@kylinos.cn>

compute_partition_effective_cpumask() recomputes a partition's exclusive
CPU mask before walking its children, but checks child containment against
cs->effective_xcpus. During an update, that field can still describe an
earlier point in the update, allowing a child outside the newly computed
mask to remain valid.

Use the newly computed exclusive mask for the containment check. Keep this
mask separate from the active-only effective mask because offline CPUs
remain part of the partition's CPU ownership.

After a child is invalidated, update_cpumasks_hier() can revisit it by
calling update_parent_effective_cpumask() with partcmd_update. The
invalid-partition recovery path is currently entered only when the child's
CPUs are already a subset of its parent's effective exclusive mask.
Otherwise part_error remains clear and the subsequent state transition
makes the child valid again. Enter the recovery path for every non-empty
CPU mask and report PERR_INVCPUS when the child is still outside the parent
mask.

The invalidation path also calls make_partition_invalid() without updating
isolated_cpus for the CPUs released by the child. Account each released CPU
according to its new owner. When the parent remains valid and owns the CPU,
use its partition state; otherwise use the state of the nearest valid
partition ancestor. Use the unfiltered exclusive mask so offline CPUs are
included in the accounting.

Fixes: 11e5f407b64a ("cgroup/cpuset: Keep track of CPUs in isolated partitions")
Fixes: 0c7f293efc87 ("cgroup/cpuset: Add cpuset.cpus.exclusive.effective for v2")
Signed-off-by: Guopeng Zhang <zhangguopeng@kylinos.cn>
---
 kernel/cgroup/cpuset.c | 51 +++++++++++++++++++++++++++++++++---------
 1 file changed, 41 insertions(+), 10 deletions(-)

diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 7e8b167a29ed..7ba26b924086 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1281,6 +1281,17 @@ static bool isolated_cpu_update(int new_prs, int cpu)
 	return true;
 }
 
+/* Return the nearest valid partition ancestor of @cs. */
+static struct cpuset *partition_owner(struct cpuset *cs)
+{
+	struct cpuset *owner = parent_cs(cs);
+
+	lockdep_assert_held(&cpuset_mutex);
+	while (!is_partition_valid(owner))
+		owner = parent_cs(owner);
+	return owner;
+}
+
 /*
  * isolated_cpus_update - Update the isolated_cpus mask
  * @old_prs: old partition_root_state
@@ -1976,12 +1987,16 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd,
 				adding = cpumask_and(tmp->addmask,
 						     cs->effective_xcpus,
 						     parent->effective_xcpus);
-		} else if (is_partition_invalid(cs) && !cpumask_empty(xcpus) &&
-			   cpumask_subset(xcpus, parent->effective_xcpus)) {
+		} else if (is_partition_invalid(cs) && !cpumask_empty(xcpus)) {
 			struct cgroup_subsys_state *css;
 			struct cpuset *child;
 			bool exclusive = true;
 
+			if (!cpumask_subset(xcpus, parent->effective_xcpus)) {
+				part_error = PERR_INVCPUS;
+				goto write_error;
+			}
+
 			/*
 			 * Convert invalid partition to valid has to
 			 * pass the cpu exclusivity test.
@@ -2120,6 +2135,7 @@ cs_partition_error(struct cpuset *cs,
  * compute_partition_effective_cpumask - compute effective_cpus for partition
  * @cs: partition root cpuset
  * @new_ecpus: previously computed effective_cpus to be updated
+ * @new_xcpus: scratch mask for the new effective_xcpus
  *
  * Compute the effective_cpus of a partition root by scanning effective_xcpus
  * of child partition roots and excluding their effective_xcpus.
@@ -2133,7 +2149,8 @@ cs_partition_error(struct cpuset *cs,
  * Note that rcu_read_lock() is assumed to be held.
  */
 static void compute_partition_effective_cpumask(struct cpuset *cs,
-						struct cpumask *new_ecpus)
+						struct cpumask *new_ecpus,
+						struct cpumask *new_xcpus)
 {
 	struct cgroup_subsys_state *css;
 	struct cpuset *child;
@@ -2147,8 +2164,8 @@ static void compute_partition_effective_cpumask(struct cpuset *cs,
 	 *  2) All the effective_cpus will be used up and cp
 	 *     has tasks
 	 */
-	compute_excpus(cs, new_ecpus);
-	cpumask_and(new_ecpus, new_ecpus, cpu_active_mask);
+	compute_excpus(cs, new_xcpus);
+	cpumask_and(new_ecpus, new_xcpus, cpu_active_mask);
 
 	rcu_read_lock();
 	cpuset_for_each_child(child, css, cs) {
@@ -2162,17 +2179,31 @@ static void compute_partition_effective_cpumask(struct cpuset *cs,
 		 * partition root.
 		 */
 		WARN_ON_ONCE(is_remote_partition(child));
-		child_err = cs_partition_error(child, cs->effective_xcpus,
+		child_err = cs_partition_error(child, new_xcpus,
 					       new_ecpus, populated);
 		WRITE_ONCE(child->prs_err, child_err);
 
 		if (child_err) {
 			int old_prs = child->partition_root_state;
+			int parent_prs = cs->partition_root_state;
+			int owner_prs = partition_owner(cs)->partition_root_state;
+			int cpu;
 
 			/*
-			 * Invalidate child partition
+			 * Account each released CPU according to whether it is now
+			 * owned by the parent or by the partition that owns the parent.
 			 */
 			spin_lock_irq(&callback_lock);
+			for_each_cpu(cpu, child->effective_xcpus) {
+				int new_prs = parent_prs > 0 &&
+					      cpumask_test_cpu(cpu, new_xcpus)
+					      ? parent_prs : owner_prs;
+
+				if (old_prs == new_prs)
+					continue;
+				if (isolated_cpu_update(new_prs, cpu))
+					update_housekeeping = true;
+			}
 			make_partition_invalid(child);
 			spin_unlock_irq(&callback_lock);
 			notify_partition_change(child, old_prs);
@@ -2274,7 +2305,7 @@ static void update_cpumasks_hier(struct cpuset *cs, struct tmpmasks *tmp,
 		}
 
 		if (remote || (is_partition_valid(parent) && is_partition_valid(cp)))
-			compute_partition_effective_cpumask(cp, tmp->new_cpus);
+			compute_partition_effective_cpumask(cp, tmp->new_cpus, tmp->addmask);
 		else
 			compute_effective_cpumask(tmp->new_cpus, cp, parent);
 
@@ -4111,7 +4142,7 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs, struct tmpmasks *tmp)
 	 */
 	remote = is_remote_partition(cs);
 	if (remote || (is_partition_valid(cs) && is_partition_valid(parent)))
-		compute_partition_effective_cpumask(cs, &new_cpus);
+		compute_partition_effective_cpumask(cs, &new_cpus, tmp->addmask);
 
 	if (remote && (cpumask_empty(subpartitions_cpus) ||
 			(cpumask_empty(&new_cpus) &&
@@ -4146,7 +4177,7 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs, struct tmpmasks *tmp)
 	if (partcmd >= 0) {
 		update_parent_effective_cpumask(cs, partcmd, NULL, tmp);
 		if ((partcmd == partcmd_invalidate) || is_partition_valid(cs)) {
-			compute_partition_effective_cpumask(cs, &new_cpus);
+			compute_partition_effective_cpumask(cs, &new_cpus, tmp->addmask);
 			cpuset_force_rebuild();
 		}
 	}
-- 
2.43.0


  parent reply	other threads:[~2026-09-10  9:47 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-10  9:45 [PATCH v4 0/7] cgroup/cpuset: Fix partition transitions and invalidation Guopeng Zhang
2026-09-10  9:45 ` [PATCH v4 1/7] cgroup/cpuset: Factor out child partition validation Guopeng Zhang
2026-09-10  9:45 ` [PATCH v4 2/7] cgroup/cpuset: Account for child CPU ownership in partition changes Guopeng Zhang
2026-09-10  9:45 ` [PATCH v4 3/7] cgroup/cpuset: Release CPUs when type-change validation fails Guopeng Zhang
2026-09-10  9:45 ` Guopeng Zhang [this message]
2026-09-10  9:45 ` [PATCH v4 5/7] cgroup/cpuset: Fix isolation accounting on propagated invalidation Guopeng Zhang
2026-09-10  9:45 ` [PATCH v4 6/7] cgroup/cpuset: Publish cpus_allowed before partition updates Guopeng Zhang
2026-09-10  9:45 ` [PATCH v4 7/7] cgroup/cpuset: Publish exclusive_cpus " Guopeng Zhang

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260910094546.5852-5-guopeng.zhang@linux.dev \
    --to=guopeng.zhang@linux.dev \
    --cc=cgroups@vger.kernel.org \
    --cc=hannes@cmpxchg.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=longman@redhat.com \
    --cc=mkoutny@suse.com \
    --cc=ridong.chen@linux.dev \
    --cc=tj@kernel.org \
    --cc=zhangguopeng@kylinos.cn \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.