From nobody Fri Sep 25 16:50:49 2026 Received: from mta0.migadu.com (out-146.mta0.migadu.com [91.218.175.146]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 01F363E0C47 for ; Thu, 10 Sep 2026 09:46:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.146 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789033596; cv=none; b=GAEcJvM8xmsEGMOBc7lBnhwhX2AHMK9h4fTDiYhhTs1v3kh5X+J8NVhB09LymdfPjauLeB59NwuzjNPVJpxCpndTD/Slh3268FX8T9sBPRnTSwIKfRimMhdKGUbKK/hj2cJ9W10Rw+2tVX8H48sw/8KRNDWFYRNNAZO1c4Pqmu0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789033596; c=relaxed/simple; bh=YuTbT0DX6xXRbrjkJTTCCjE2es60hrvQcLZ1FO2Jr6Y=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=soJ8heRMRBHUYI4FkllUaSNVlrmq41M/UkJpJjDNmDTlec+d51TQPHBQhAHfZIfn401jzJBHavrMvie13RyRlnD8PglD18YqoFPWEAtSeTzidugNgdz/f5753CzWkmgSCvCgVcWgs/MIsKKEdiHpC5sIrlc+7C2qugh6Jg697nU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=pi1yv4+o; arc=none smtp.client-ip=91.218.175.146 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="pi1yv4+o" X-Envelope-To: linux-kernel@vger.kernel.org DKIM-Signature: a=rsa-sha256; bh=YuTbT0DX6xXRbrjkJTTCCjE2es60hrvQcLZ1FO2Jr6Y=; c=simple/simple; d=linux.dev; h=from:to:subject:date:message-id:mime-version:content-type; s=key1; t=1789033586; v=1; x=1789638386; b=pi1yv4+oI0guX5frTMgjRDVicvB0w9gL0gLWVN7CsrG5K3Y7DTTlTM/TC6bwjBtSvjxWq63X SwY4drKQ67DQK4p+W9VH2ndiSTiWP3U3Lsx0SjvNw3hE32XLMt3zKgF+1eurHZj6qADlMMSGflc bma5d72ssofYjOCMYzvpWgTY= X-Envelope-To: linux-kernel@vger.kernel.org Received: by smtp.migadu.com with ESMTPS id 1f2f61f473892bfd; Thu, 10 Sep 2026 09:46:26 +0000 X-Mizu-Trace-ID: 1f2f61f473892bfd X-Migadu-Flow: FLOW_OUT From: Guopeng Zhang To: Waiman Long , Ridong Chen Cc: Tejun Heo , Johannes Weiner , =?UTF-8?q?Michal=20Koutn=C3=BD?= , cgroups@vger.kernel.org, linux-kernel@vger.kernel.org, Guopeng Zhang Subject: [PATCH v4 1/7] cgroup/cpuset: Factor out child partition validation Date: Thu, 10 Sep 2026 17:45:40 +0800 Message-ID: <20260910094546.5852-2-guopeng.zhang@linux.dev> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260910094546.5852-1-guopeng.zhang@linux.dev> References: <20260910094546.5852-1-guopeng.zhang@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Guopeng Zhang compute_partition_effective_cpumask() checks whether each valid child partition remains covered by the parent exclusive CPU mask and whether it would consume all remaining active CPUs of a populated parent. Factor these two checks into cs_partition_error() so the same rules can be reused when evaluating a proposed parent configuration. This is a preparatory refactoring with no intended functional change. Signed-off-by: Guopeng Zhang --- kernel/cgroup/cpuset.c | 47 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c index 8f24171b6055..a2514fcb1144 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -2085,6 +2085,37 @@ static int update_parent_effective_cpumask(struct cp= uset *cs, int cmd, return 0; } =20 +/** + * cs_partition_error - Return a cpuset partition invalidation error + * @cs: Partition being evaluated + * @parent_xcpus: Parent's complete effective exclusive CPU mask, including + * offline CPUs + * @remaining_ecpus: Parent's active effective CPUs remaining before @cs is + * evaluated + * @parent_populated: Whether the parent partition contains tasks + * + * @remaining_ecpus may still contain active CPUs assigned to @cs and to + * children that have not yet been evaluated. It excludes only CPUs assign= ed + * to previously evaluated children that remain valid. + * + * Return: The error that would invalidate @cs, or PERR_NONE + */ +static enum prs_errcode +cs_partition_error(struct cpuset *cs, + const struct cpumask *parent_xcpus, + const struct cpumask *remaining_ecpus, + bool parent_populated) +{ + if (!cpumask_subset(cs->effective_xcpus, parent_xcpus)) + return PERR_INVCPUS; + + if (parent_populated && + cpumask_subset(remaining_ecpus, cs->effective_xcpus)) + return PERR_NOCPUS; + + return PERR_NONE; +} + /** * compute_partition_effective_cpumask - compute effective_cpus for partit= ion * @cs: partition root cpuset @@ -2121,6 +2152,8 @@ static void compute_partition_effective_cpumask(struc= t cpuset *cs, =20 rcu_read_lock(); cpuset_for_each_child(child, css, cs) { + enum prs_errcode child_err; + if (!is_partition_valid(child)) continue; =20 @@ -2129,15 +2162,11 @@ static void compute_partition_effective_cpumask(str= uct cpuset *cs, * partition root. */ WARN_ON_ONCE(is_remote_partition(child)); - WRITE_ONCE(child->prs_err, 0); - if (!cpumask_subset(child->effective_xcpus, - cs->effective_xcpus)) - WRITE_ONCE(child->prs_err, PERR_INVCPUS); - else if (populated && - cpumask_subset(new_ecpus, child->effective_xcpus)) - WRITE_ONCE(child->prs_err, PERR_NOCPUS); - - if (child->prs_err) { + child_err =3D cs_partition_error(child, cs->effective_xcpus, + new_ecpus, populated); + WRITE_ONCE(child->prs_err, child_err); + + if (child_err) { int old_prs =3D child->partition_root_state; =20 /* --=20 2.43.0 From nobody Fri Sep 25 16:50:49 2026 Received: from mta1.migadu.com (out-74.mta1.migadu.com [95.215.58.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 09AF33E1D0D for ; Thu, 10 Sep 2026 09:46:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789033609; cv=none; b=eBTh2AbYb83J0SHKUgYtUoF1z0mgCzFjl7lH3FwTi4n66yeLbdqEFzW+8Os4p6hGyogdBK5AkX3IKBxeA5JkMODxOks20jeZhHUGZ2nw3fCpZscYCzDSV9QHVJQO+9n7c5//iyv4B5xVXvAB/5qIoElOlDczxqWmUPdZebmCPGI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789033609; c=relaxed/simple; bh=uLsBzrZ02U23AWa/sdqBVri8O333V2kXYUzwHBlUhu4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=bgXJJGS2cVGeQPVTIKGmJLl/bhF53YdolMhE5ZJBmax1TnY3M/NpjXEwhHVH53FhUbRTUNZE4YkRZx/DDPrt7PloGRRpFBKyzTl1wEohoi3W0/lKJ03YpWp8sGJTQaSNyULTbzSRSm4SmfJ4NDI8BVDKBNeroIl3TbJp2Cgj9eA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=oET3Itl0; arc=none smtp.client-ip=95.215.58.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="oET3Itl0" X-Envelope-To: linux-kernel@vger.kernel.org DKIM-Signature: a=rsa-sha256; bh=uLsBzrZ02U23AWa/sdqBVri8O333V2kXYUzwHBlUhu4=; c=simple/simple; d=linux.dev; h=from:to:subject:date:message-id:mime-version:content-type; s=key1; t=1789033596; v=1; x=1789638396; b=oET3Itl0kR1QgQ4zg7AMF8whCKxuMBcVTAvP/5qAsBfSBC2cTCoViM5IEx4E8NhNWP/6QS7k oooPb7Cm6eXb+LIE0ZS9KRs0ZpWaBobTYQmW2KI72FNhKZ0Z10EhQEBxmERx50xTkCaXkkW8uhG ht6gnv4yBUL0sZXc+EZyklRk= X-Envelope-To: linux-kernel@vger.kernel.org Received: by smtp.migadu.com with ESMTPS id 55f262cd7e46dd25; Thu, 10 Sep 2026 09:46:35 +0000 X-Mizu-Trace-ID: 55f262cd7e46dd25 X-Migadu-Flow: FLOW_OUT From: Guopeng Zhang To: Waiman Long , Ridong Chen Cc: Tejun Heo , Johannes Weiner , =?UTF-8?q?Michal=20Koutn=C3=BD?= , cgroups@vger.kernel.org, linux-kernel@vger.kernel.org, Guopeng Zhang Subject: [PATCH v4 2/7] cgroup/cpuset: Account for child CPU ownership in partition changes Date: Thu, 10 Sep 2026 17:45:41 +0800 Message-ID: <20260910094546.5852-3-guopeng.zhang@linux.dev> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260910094546.5852-1-guopeng.zhang@linux.dev> References: <20260910094546.5852-1-guopeng.zhang@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Guopeng Zhang effective_xcpus includes CPUs granted to valid child partitions. A change to the parent must not apply its isolation state or housekeeping checks to CPUs which remain owned by those children. For example, on a cgroup v2 system with CPUs 0-3 online: cd /sys/fs/cgroup echo +cpuset > cgroup.subtree_control mkdir type-repro echo 1-3 > type-repro/cpuset.cpus echo isolated > type-repro/cpuset.cpus.partition echo +cpuset > type-repro/cgroup.subtree_control mkdir type-repro/child echo 2-3 > type-repro/child/cpuset.cpus echo isolated > type-repro/child/cpuset.cpus.partition echo root > type-repro/cpuset.cpus.partition cat cpuset.cpus.isolated The isolated mask should still contain CPUs 2-3 after the parent becomes a root partition. Without this change, those CPUs are removed even though the child remains isolated. Compute the CPUs owned directly by a partition by excluding CPUs granted to valid children. When validating a trial parent mask, exclude only CPUs granted to children that will remain valid under that mask. Reuse the child validation rules so PERR_INVCPUS and PERR_NOCPUS are handled consistently. Use the directly owned mask for root/isolated type changes and housekeeping validation. If a root child returns the last housekeeping CPU to an isolated parent, invalidate the outermost isolated ancestor so the child can still become a member without violating the housekeeping constraint. Force the hierarchy update from the invalidated ancestor. Otherwise an unchanged member cpuset can cause its subtree to be skipped, leaving task CPU masks or descendant partition states stale. Link: https://sashiko.dev/#/patchset/20260820124202.517160-1-guopeng.zhang%= 40linux.dev?part=3D6 Link: https://sashiko.dev/#/patchset/20260828095643.13395-1-guopeng.zhang@l= inux.dev?part=3D1 Link: https://sashiko.dev/#/patchset/20260902102615.79189-1-guopeng.zhang@l= inux.dev?part=3D2 Fixes: 4a74e418881f ("cgroup/cpuset: Check partition conflict with housekee= ping setup") Fixes: 11e5f407b64a ("cgroup/cpuset: Keep track of CPUs in isolated partiti= ons") Fixes: 103b08709e8a ("cgroup/cpuset: Fail if isolated and nohz_full don't l= eave any housekeeping") Fixes: b1034a690129 ("cgroup/cpuset: Ensure domain isolated CPUs stay in ro= ot or isolated partition") Signed-off-by: Guopeng Zhang --- kernel/cgroup/cpuset.c | 142 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 132 insertions(+), 10 deletions(-) diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c index a2514fcb1144..994ddb79272d 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -2184,6 +2184,44 @@ static void compute_partition_effective_cpumask(stru= ct cpuset *cs, rcu_read_unlock(); } =20 +/* + * Compute CPUs owned directly by a partition under @parent_xcpus by exclu= ding + * CPUs granted to children that remain valid under that mask. + */ +static void compute_partition_owned_cpumask(struct cpuset *cs, + const struct cpumask *parent_xcpus, + struct cpumask *owned_cpus, + struct cpumask *remaining_ecpus) +{ + struct cgroup_subsys_state *css; + struct cpuset *child; + bool populated =3D partition_is_populated(cs, NULL); + + lockdep_assert_held(&cpuset_mutex); + cpumask_copy(owned_cpus, parent_xcpus); + cpumask_and(remaining_ecpus, parent_xcpus, cpu_active_mask); + + rcu_read_lock(); + cpuset_for_each_child(child, css, cs) { + if (!is_partition_valid(child)) + continue; + + /* + * A child that would become invalid under the proposed + * configuration cannot retain ownership of its CPUs. + */ + if (cs_partition_error(child, parent_xcpus, + remaining_ecpus, populated)) + continue; + + cpumask_andnot(owned_cpus, owned_cpus, + child->effective_xcpus); + cpumask_andnot(remaining_ecpus, remaining_ecpus, + child->effective_xcpus); + } + rcu_read_unlock(); +} + /* * update_cpumasks_hier - Update effective cpumasks and tasks in the subtr= ee * @cs: the cpuset to consider @@ -2424,13 +2462,18 @@ static int parse_cpuset_cpulist(const char *buf, st= ruct cpumask *out_mask) * validate_partition - Validate a cpuset partition configuration * @cs: The cpuset to validate * @trialcs: The trial cpuset containing proposed configuration changes + * @owned_cpus: Scratch mask for CPUs owned directly by the trial partition + * @remaining_ecpus: Scratch mask used to predict valid child partitions * * 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 cpuse= t *trialcs) +static enum prs_errcode validate_partition(struct cpuset *cs, + struct cpuset *trialcs, + struct cpumask *owned_cpus, + struct cpumask *remaining_ecpus) { struct cpuset *parent =3D parent_cs(cs); =20 @@ -2440,8 +2483,10 @@ static enum prs_errcode validate_partition(struct cp= uset *cs, struct cpuset *tri if (cpumask_empty(trialcs->effective_xcpus)) return PERR_INVCPUS; =20 + compute_partition_owned_cpumask(cs, trialcs->effective_xcpus, + owned_cpus, remaining_ecpus); if (prstate_housekeeping_conflict(trialcs->partition_root_state, - trialcs->effective_xcpus)) + owned_cpus)) return PERR_HKEEPING; =20 if (tasks_nocpu_error(parent, cs, trialcs->effective_xcpus)) @@ -2467,7 +2512,8 @@ static void partition_cpus_change(struct cpuset *cs, = struct cpuset *trialcs, if (cs_is_member(cs)) return; =20 - prs_err =3D validate_partition(cs, trialcs); + prs_err =3D validate_partition(cs, trialcs, tmp->new_cpus, + tmp->addmask); if (prs_err) { WRITE_ONCE(cs->prs_err, prs_err); trialcs->prs_err =3D prs_err; @@ -2946,6 +2992,49 @@ int cpuset_update_flag(cpuset_flagbits_t bit, struct= cpuset *cs, return err; } =20 +/* + * Invalidate the highest isolated partition that contains @cs. + * + * A root partition returning CPUs to an isolated parent can consume the l= ast + * housekeeping CPU. Invalidating the highest isolated ancestor lets the + * subsequent hierarchy update propagate invalidation down the chain and + * return the CPUs to a root partition. + */ +static struct cpuset *invalidate_isolated_ancestor(struct cpuset *cs, + struct tmpmasks *tmp) +{ + struct cpuset *ancestor =3D parent_cs(cs); + struct cpuset *parent; + int err; + + lockdep_assert_held(&cpuset_mutex); + if (WARN_ON_ONCE(!ancestor)) + return NULL; + + while ((ancestor !=3D &top_cpuset) && + !is_remote_partition(ancestor)) { + parent =3D parent_cs(ancestor); + if (!parent || + parent->partition_root_state !=3D PRS_ISOLATED) + break; + ancestor =3D parent; + } + + if (WARN_ON_ONCE(ancestor =3D=3D &top_cpuset)) + return NULL; + + WRITE_ONCE(ancestor->prs_err, PERR_HKEEPING); + if (is_remote_partition(ancestor)) { + remote_partition_disable(ancestor, tmp); + } else { + err =3D update_parent_effective_cpumask(ancestor, + partcmd_invalidate, NULL, tmp); + WARN_ON_ONCE(err); + } + + return ancestor; +} + /** * update_prstate - update partition_root_state * @cs: the cpuset to update @@ -2958,6 +3047,8 @@ static int update_prstate(struct cpuset *cs, int new_= prs) { int err =3D PERR_NONE, old_prs =3D cs->partition_root_state; struct cpuset *parent =3D parent_cs(cs); + struct cpuset *invalidated =3D NULL; + struct cpumask *isolcpus_update_cpus =3D cs->effective_xcpus; struct tmpmasks tmpmask; bool isolcpus_updated =3D false; =20 @@ -3014,19 +3105,38 @@ static int update_prstate(struct cpuset *cs, int ne= w_prs) } else if (old_prs && new_prs) { /* * A change in load balance state only, no change in cpumasks. - * Need to update isolated_cpus. + * Need to update isolated_cpus for CPUs owned by this partition, + * excluding CPUs distributed to valid child partitions. */ + compute_partition_owned_cpumask(cs, cs->effective_xcpus, + tmpmask.new_cpus, + tmpmask.addmask); if (((new_prs =3D=3D PRS_ISOLATED) && - !isolated_cpus_can_update(cs->effective_xcpus, NULL)) || - prstate_housekeeping_conflict(new_prs, cs->effective_xcpus)) + !isolated_cpus_can_update(tmpmask.new_cpus, NULL)) || + prstate_housekeeping_conflict(new_prs, tmpmask.new_cpus)) { err =3D PERR_HKEEPING; - else + } else { + /* + * Only directly owned CPUs change isolation state for a + * successful root <-> isolated type change. + */ + isolcpus_update_cpus =3D tmpmask.new_cpus; isolcpus_updated =3D true; + } } else { /* * Switching back to member is always allowed even if it - * disables child partitions. + * disables child partitions. If returning CPUs to an isolated + * parent would consume the last housekeeping CPU, invalidate + * the outermost isolated ancestor and return its CPUs instead. */ + if (old_prs =3D=3D PRS_ROOT && + parent->partition_root_state =3D=3D PRS_ISOLATED && + !isolated_cpus_can_update(cs->effective_xcpus, NULL)) + invalidated =3D invalidate_isolated_ancestor(cs, &tmpmask); + if (invalidated) + goto out; + if (is_remote_partition(cs)) remote_partition_disable(cs, &tmpmask); else @@ -3054,11 +3164,23 @@ static int update_prstate(struct cpuset *cs, int ne= w_prs) if (!is_partition_valid(cs)) reset_partition_data(cs); else if (isolcpus_updated) - isolated_cpus_update(old_prs, new_prs, cs->effective_xcpus); + isolated_cpus_update(old_prs, new_prs, + isolcpus_update_cpus); spin_unlock_irq(&callback_lock); =20 /* Force update if switching back to member & update effective_xcpus */ - update_cpumasks_hier(cs, &tmpmask, !new_prs); + if (invalidated) { + /* + * Ancestor invalidation changes the partition hierarchy. Force the + * traversal so an unchanged member cpuset does not cause its subtree + * to be skipped. + */ + update_cpumasks_hier(invalidated, &tmpmask, true); + update_partition_sd_lb(invalidated, PRS_ISOLATED); + notify_partition_change(invalidated, PRS_ISOLATED); + } else { + update_cpumasks_hier(cs, &tmpmask, !new_prs); + } =20 /* A newly created partition must have effective_xcpus set */ WARN_ON_ONCE(!old_prs && (new_prs > 0) --=20 2.43.0 From nobody Fri Sep 25 16:50:49 2026 Received: from mta0.migadu.com (out-154.mta0.migadu.com [91.218.175.154]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A93763B6367 for ; Thu, 10 Sep 2026 09:46:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.154 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789033612; cv=none; b=u5TjjdvA8SdL+ts3PXIThEoFrX5rVBAHwMkKTfephYQpOFXQv/Pa4liIzAUNyBkTFCWwbO3Hyb944+AErCEXEzEulmMe9yHUjzr25sP7yCreVYe1Qe0ZhHrgIupulB36icGXWy7UxT4UJQcUkhFJotsZ2QguHHIncUJ5/3Owd+8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789033612; c=relaxed/simple; bh=Pwm1u3+lX/pmOZ9lXdY5RK0JV39zkhvnKlA3b/Gza+I=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ldl1R3/m0KhT4johrpmh38M7A4oCb0EXnK7ut/8uUQetTLH6hmBDceIjq/1WrRfJhlo46u5Pxn/60D6vHIcy4j+0o4OeZZVsEvsWHPgWkEg8bwXtsHqsiDDzIU6heql1hahTI9h1Hl7kF3d1EreJmJ3nR0lOAiZRJUbRomVRvuI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=ZBGpIZN1; arc=none smtp.client-ip=91.218.175.154 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="ZBGpIZN1" X-Envelope-To: linux-kernel@vger.kernel.org DKIM-Signature: a=rsa-sha256; bh=Pwm1u3+lX/pmOZ9lXdY5RK0JV39zkhvnKlA3b/Gza+I=; c=simple/simple; d=linux.dev; h=from:to:subject:date:message-id:mime-version:content-type; s=key1; t=1789033603; v=1; x=1789638403; b=ZBGpIZN1yT7pLZRbiectZfteDBI7F140PciDUyQx3SDw2hcdvI1vPIXXHWEb7pIm8yfnVHsM jGqEBFmn4dISqiufhUHn/ADg/7EUOszd/4ZuFOw3mT6vnBSceuEICtofKwsdUQiwgZ+Nh+iA3o7 7lQGeTh+siQshaUu/+8kLkeE= X-Envelope-To: linux-kernel@vger.kernel.org Received: by smtp.migadu.com with ESMTPS id be5942a5d437b643; Thu, 10 Sep 2026 09:46:43 +0000 X-Mizu-Trace-ID: be5942a5d437b643 X-Migadu-Flow: FLOW_OUT From: Guopeng Zhang To: Waiman Long , Ridong Chen Cc: Tejun Heo , Johannes Weiner , =?UTF-8?q?Michal=20Koutn=C3=BD?= , cgroups@vger.kernel.org, linux-kernel@vger.kernel.org, Guopeng Zhang Subject: [PATCH v4 3/7] cgroup/cpuset: Release CPUs when type-change validation fails Date: Thu, 10 Sep 2026 17:45:42 +0800 Message-ID: <20260910094546.5852-4-guopeng.zhang@linux.dev> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260910094546.5852-1-guopeng.zhang@linux.dev> References: <20260910094546.5852-1-guopeng.zhang@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Guopeng Zhang When housekeeping validation fails during a root/isolated type change, update_prstate() marks the requested state invalid without running the partition-disable path. The failed partition's effective_xcpus may be cleared, but its CPUs remain unavailable to the partition which owns the invalidated subtree. This can be reproduced on a cgroup v2 system booted with isolcpus=3Ddomain,15: cd /sys/fs/cgroup echo +cpuset > cgroup.subtree_control mkdir type-fail-repro echo 15 > type-fail-repro/cpuset.cpus echo isolated > type-fail-repro/cpuset.cpus.partition echo root > type-fail-repro/cpuset.cpus.partition cat type-fail-repro/cpuset.cpus.partition cat cpuset.cpus.effective The requested root state is recorded as invalid, but CPU 15 remains unavailable to the top cpuset. Run the common partition-disable path when housekeeping validation fails. Disable remote partitions with remote_partition_disable() and return local partition CPUs to their parent. If that would consume the last housekeeping CPU, invalidate the outermost isolated ancestor instead. Fixes: 103b08709e8a ("cgroup/cpuset: Fail if isolated and nohz_full don't l= eave any housekeeping") Fixes: b1034a690129 ("cgroup/cpuset: Ensure domain isolated CPUs stay in ro= ot or isolated partition") Signed-off-by: Guopeng Zhang --- kernel/cgroup/cpuset.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c index 994ddb79272d..7e8b167a29ed 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -3050,6 +3050,7 @@ static int update_prstate(struct cpuset *cs, int new_= prs) struct cpuset *invalidated =3D NULL; struct cpumask *isolcpus_update_cpus =3D cs->effective_xcpus; struct tmpmasks tmpmask; + bool disable_partition =3D false; bool isolcpus_updated =3D false; =20 if (old_prs =3D=3D new_prs) @@ -3115,6 +3116,7 @@ static int update_prstate(struct cpuset *cs, int new_= prs) !isolated_cpus_can_update(tmpmask.new_cpus, NULL)) || prstate_housekeeping_conflict(new_prs, tmpmask.new_cpus)) { err =3D PERR_HKEEPING; + disable_partition =3D true; } else { /* * Only directly owned CPUs change isolation state for a @@ -3130,6 +3132,10 @@ static int update_prstate(struct cpuset *cs, int new= _prs) * parent would consume the last housekeeping CPU, invalidate * the outermost isolated ancestor and return its CPUs instead. */ + disable_partition =3D true; + } + + if (disable_partition) { if (old_prs =3D=3D PRS_ROOT && parent->partition_root_state =3D=3D PRS_ISOLATED && !isolated_cpus_can_update(cs->effective_xcpus, NULL)) --=20 2.43.0 From nobody Fri Sep 25 16:50:49 2026 Received: from mta0.migadu.com (out-163.mta0.migadu.com [91.218.175.163]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E661F2DC798 for ; Thu, 10 Sep 2026 09:47:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.163 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789033625; cv=none; b=e5EKAx1CxtBGO8Y6DhSTeXp0qWInmxHiwbJ/obQUxZcAKO/XzN47cRr/fTc8JIkuAGkg+ofTY1WDhCLLJuK96bh10m87znuvEBVEpVRFVduUmqVWbY8gdDzVFFhAjUIEifUDG0mZR/sc4gBrNgtb0EKjMY3iG3f9jfsUq+p1h4k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789033625; c=relaxed/simple; bh=E7gj/63wJoRVGssDCWIkzHFYWr734CVQs31WRFGmvas=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ALJVPT6D1V7qAQdQBKhpx5fdtDyaqT2tlJjje48M5G164l4Qd1V7aU/yn0i2jwS4qb+tLzBHvSs2OL8Qgh8TrJKHLUtZg3JNgxLAlraL8Ft260w+6CutS7x1UM6JBJTaPfEvdGw5lLKDMNjH5aEu74hpsdvqVQifBGCgCz5W5qU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=rWw30qnd; arc=none smtp.client-ip=91.218.175.163 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="rWw30qnd" X-Envelope-To: linux-kernel@vger.kernel.org DKIM-Signature: a=rsa-sha256; bh=E7gj/63wJoRVGssDCWIkzHFYWr734CVQs31WRFGmvas=; c=simple/simple; d=linux.dev; h=from:to:subject:date:message-id:mime-version:content-type; s=key1; t=1789033617; v=1; x=1789638417; b=rWw30qndfv4+9cBmozJZEVdMx9tYUNyJqyDYTSG2pPCEIosJuXEhTJJGzaiHBU+3ju9WLLqw kyDm+W2dUoaH0SHvJjYG1RKyZ2AGfbj887SW454iSuzQ3YUe/45/ixyjgMCVcMLxmPEzwUyxbOd luMrWh0fCQXQwH4ibEBtiDlg= X-Envelope-To: linux-kernel@vger.kernel.org Received: by smtp.migadu.com with ESMTPS id 06c6d75ab9332f78; Thu, 10 Sep 2026 09:46:56 +0000 X-Mizu-Trace-ID: 06c6d75ab9332f78 X-Migadu-Flow: FLOW_OUT From: Guopeng Zhang To: Waiman Long , Ridong Chen Cc: Tejun Heo , Johannes Weiner , =?UTF-8?q?Michal=20Koutn=C3=BD?= , cgroups@vger.kernel.org, linux-kernel@vger.kernel.org, Guopeng Zhang Subject: [PATCH v4 4/7] cgroup/cpuset: Fix child invalidation after parent CPU changes Date: Thu, 10 Sep 2026 17:45:43 +0800 Message-ID: <20260910094546.5852-5-guopeng.zhang@linux.dev> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260910094546.5852-1-guopeng.zhang@linux.dev> References: <20260910094546.5852-1-guopeng.zhang@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Guopeng Zhang 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 partiti= ons") Fixes: 0c7f293efc87 ("cgroup/cpuset: Add cpuset.cpus.exclusive.effective fo= r v2") Signed-off-by: Guopeng Zhang --- 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; } =20 +/* Return the nearest valid partition ancestor of @cs. */ +static struct cpuset *partition_owner(struct cpuset *cs) +{ + struct cpuset *owner =3D parent_cs(cs); + + lockdep_assert_held(&cpuset_mutex); + while (!is_partition_valid(owner)) + owner =3D 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 c= puset *cs, int cmd, adding =3D 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 =3D true; =20 + if (!cpumask_subset(xcpus, parent->effective_xcpus)) { + part_error =3D 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 partit= ion * @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_xc= pus * 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(struc= t 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); =20 rcu_read_lock(); cpuset_for_each_child(child, css, cs) { @@ -2162,17 +2179,31 @@ static void compute_partition_effective_cpumask(str= uct cpuset *cs, * partition root. */ WARN_ON_ONCE(is_remote_partition(child)); - child_err =3D cs_partition_error(child, cs->effective_xcpus, + child_err =3D cs_partition_error(child, new_xcpus, new_ecpus, populated); WRITE_ONCE(child->prs_err, child_err); =20 if (child_err) { int old_prs =3D child->partition_root_state; + int parent_prs =3D cs->partition_root_state; + int owner_prs =3D partition_owner(cs)->partition_root_state; + int cpu; =20 /* - * 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 =3D parent_prs > 0 && + cpumask_test_cpu(cpu, new_xcpus) + ? parent_prs : owner_prs; + + if (old_prs =3D=3D new_prs) + continue; + if (isolated_cpu_update(new_prs, cpu)) + update_housekeeping =3D 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, s= truct tmpmasks *tmp, } =20 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); =20 @@ -4111,7 +4142,7 @@ static void cpuset_hotplug_update_tasks(struct cpuset= *cs, struct tmpmasks *tmp) */ remote =3D 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); =20 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 >=3D 0) { update_parent_effective_cpumask(cs, partcmd, NULL, tmp); if ((partcmd =3D=3D 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(); } } --=20 2.43.0 From nobody Fri Sep 25 16:50:49 2026 Received: from mta1.migadu.com (out-102.mta1.migadu.com [95.215.58.102]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E2D2B3E0C5F for ; Thu, 10 Sep 2026 09:47:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.102 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789033632; cv=none; b=FQBTY4zs47vxflJajLavl7aVbBncqDX6AiehOdQdAkZ68zRP0x9S7yP4d2rT4g2xD+6ut+k0q7vn3Iu3baZl9wP4islbzZi2aGZWSlVqR/rqwfoVa7IX3+cSTG0rPLSQinm3m/RQV8Is0O74qDJalaVxHVcWQOoZLCU8DRxblVo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789033632; c=relaxed/simple; bh=KGHhFFuOAsPvgHwAFUrFwLqfH+pwxNnBrVU009zmgok=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=pxNeyAZMQuFts3hIPx+S0vkvjlGpnQ8pL7bU4mC+fhNMO7xbJXoo2N2o+nTs+gvZiEad8EOSlm6ov56pBKdi545eMvuVBKS6oTljkipP164epWuEH732hdYG/udyI+1Gg8e21XCF3tNTh8sh3eTrw12PbLtkQfh18TjWXkwe2+Q= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=o+/1GAKU; arc=none smtp.client-ip=95.215.58.102 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="o+/1GAKU" X-Envelope-To: linux-kernel@vger.kernel.org DKIM-Signature: a=rsa-sha256; bh=KGHhFFuOAsPvgHwAFUrFwLqfH+pwxNnBrVU009zmgok=; c=simple/simple; d=linux.dev; h=from:to:subject:date:message-id:mime-version:content-type; s=key1; t=1789033626; v=1; x=1789638426; b=o+/1GAKUleYsMFDDe8zY3zgdUxXCJuwb8heQc1T8YeIpLEuW14CxE+vOyNcwYjpzrn+SStR1 ExImn8LeXS5vlL+82X2MJlx8ZF6Ef5X02LPYEKF1dRYb1Oa5QkRuTl1mKq02SP06jevL2/pneyH w6UvIh0csbQ4DTZNEsNfhdnc= X-Envelope-To: linux-kernel@vger.kernel.org Received: by smtp.migadu.com with ESMTPS id 4413365a2b343b91; Thu, 10 Sep 2026 09:47:06 +0000 X-Mizu-Trace-ID: 4413365a2b343b91 X-Migadu-Flow: FLOW_OUT From: Guopeng Zhang To: Waiman Long , Ridong Chen Cc: Tejun Heo , Johannes Weiner , =?UTF-8?q?Michal=20Koutn=C3=BD?= , cgroups@vger.kernel.org, linux-kernel@vger.kernel.org, Guopeng Zhang Subject: [PATCH v4 5/7] cgroup/cpuset: Fix isolation accounting on propagated invalidation Date: Thu, 10 Sep 2026 17:45:44 +0800 Message-ID: <20260910094546.5852-6-guopeng.zhang@linux.dev> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260910094546.5852-1-guopeng.zhang@linux.dev> References: <20260910094546.5852-1-guopeng.zhang@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Guopeng Zhang update_cpumasks_hier() invalidates a local partition when its parent becomes a member or an invalid partition. Its CPUs return to the nearest valid partition ancestor, but isolated_cpus still reflects the old partition type. This can be reproduced on a cgroup v2 system with CPUs 0-3 online: cd /sys/fs/cgroup echo +cpuset > cgroup.subtree_control mkdir propagation-repro echo 0-3 > propagation-repro/cpuset.cpus echo root > propagation-repro/cpuset.cpus.partition echo +cpuset > propagation-repro/cgroup.subtree_control mkdir propagation-repro/child echo 2-3 > propagation-repro/child/cpuset.cpus echo isolated > propagation-repro/child/cpuset.cpus.partition echo member > propagation-repro/cpuset.cpus.partition cat cpuset.cpus.isolated Without this fix, CPUs 2-3 remain isolated even though the child is invalid and its CPUs have returned to the top partition. The expected dynamic isolated mask is empty. Update isolated_cpus before resetting a local partition whose state changes from valid to invalid below an invalid parent. Remote partitions are excluded because remote_partition_disable() already updates their isolated CPU accounting. Fixes: 11e5f407b64a ("cgroup/cpuset: Keep track of CPUs in isolated partiti= ons") Signed-off-by: Guopeng Zhang --- kernel/cgroup/cpuset.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c index 7ba26b924086..bbc4868f026b 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -2277,9 +2277,12 @@ static void update_cpumasks_hier(struct cpuset *cs, = struct tmpmasks *tmp, cpuset_for_each_descendant_pre(cp, pos_css, cs) { struct cpuset *parent =3D parent_cs(cp); bool remote =3D is_remote_partition(cp); + bool was_remote =3D remote; bool update_parent =3D false; + int owner_prs; =20 old_prs =3D new_prs =3D cp->partition_root_state; + owner_prs =3D old_prs; =20 /* * For child remote partition root (!=3D cs), we need to call @@ -2379,7 +2382,18 @@ static void update_cpumasks_hier(struct cpuset *cs, = struct tmpmasks *tmp, new_prs =3D cp->partition_root_state; } =20 + /* + * With no valid parent partition left, this partition's CPUs + * return to the nearest valid partition ancestor. + */ + if (!was_remote && old_prs > 0 && new_prs < 0 && + !is_partition_valid(parent)) + owner_prs =3D partition_owner(cp)->partition_root_state; + spin_lock_irq(&callback_lock); + if (old_prs !=3D owner_prs) + isolated_cpus_update(old_prs, owner_prs, + cp->effective_xcpus); cpumask_copy(cp->effective_cpus, tmp->new_cpus); cp->partition_root_state =3D new_prs; /* --=20 2.43.0 From nobody Fri Sep 25 16:50:49 2026 Received: from mta0.migadu.com (out-179.mta0.migadu.com [91.218.175.179]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 583493E172E for ; Thu, 10 Sep 2026 09:47:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.179 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789033637; cv=none; b=BkfsITnLP6CLb/eKzQ/QYdjDq77wRODTinlDOxmTUGxt+Sdy0ETXZ7JTq9CSycgM+xq2EfgjGwCWzHgftAzC3aiVb22R5KE5mbvIdI27kLkRTXubIaRoJlKVBRQwpe3hizr30QMeBrqKG4WUOV0v++uDpJbwXwrKE+AAH/JPVD0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789033637; c=relaxed/simple; bh=n38oQvTB3bj6RKoMFOidDkXJyN7EmSAfiUvXEEt0TAc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=iEH8ih212TuWxiWkYGfaxhysIYZFIySycR16BSON/BM5F8FX31pEWpW0XibIYbjIacyWtBqzIrilptLF7l06KVgqyTtNQSpYUPYpStj1MVpKFr2Pi5Bij8oGOExeL1qP7jf5n/QsxuAH8Ye0pFep+dRwr/RP8kcYxx+44Ui3eEE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=kZv+psbl; arc=none smtp.client-ip=91.218.175.179 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="kZv+psbl" X-Envelope-To: linux-kernel@vger.kernel.org DKIM-Signature: a=rsa-sha256; bh=n38oQvTB3bj6RKoMFOidDkXJyN7EmSAfiUvXEEt0TAc=; c=simple/simple; d=linux.dev; h=from:to:subject:date:message-id:mime-version:content-type; s=key1; t=1789033632; v=1; x=1789638432; b=kZv+psbla6tZq2ZCOQ6dLJPQbE258xL8+AmrcCHV/ogptNZf6zsASIJ3CZp2+vJdBfC7vU1W knO55EppZjtjusaOmy55HJNjnhjKd1xWMDiVquzXL2TyE7Gy4ATP0pYZFiXueXL2+VUqJtHJrhS pIWOJXkg5rZE4SN+eqi3sc2U= X-Envelope-To: linux-kernel@vger.kernel.org Received: by smtp.migadu.com with ESMTPS id 498b728418b7d61c; Thu, 10 Sep 2026 09:47:12 +0000 X-Mizu-Trace-ID: 498b728418b7d61c X-Migadu-Flow: FLOW_OUT From: Guopeng Zhang To: Waiman Long , Ridong Chen Cc: Tejun Heo , Johannes Weiner , =?UTF-8?q?Michal=20Koutn=C3=BD?= , cgroups@vger.kernel.org, linux-kernel@vger.kernel.org, Guopeng Zhang Subject: [PATCH v4 6/7] cgroup/cpuset: Publish cpus_allowed before partition updates Date: Thu, 10 Sep 2026 17:45:45 +0800 Message-ID: <20260910094546.5852-7-guopeng.zhang@linux.dev> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260910094546.5852-1-guopeng.zhang@linux.dev> References: <20260910094546.5852-1-guopeng.zhang@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Guopeng Zhang update_cpumask() calls partition_cpus_change() before copying the new cpus_allowed mask. A remote partition update can propagate through an ancestor and revisit the cpuset while the old mask is still visible. The second visit then adds back CPUs that the first visit released. This can be reproduced on a cgroup v2 system with CPUs 1-7 online: cd /sys/fs/cgroup echo +cpuset > cgroup.subtree_control mkdir remote-repro echo 1-7 > remote-repro/cpuset.cpus echo 1-7 > remote-repro/cpuset.cpus.exclusive echo +cpuset > remote-repro/cgroup.subtree_control mkdir remote-repro/part echo 1-4 > remote-repro/part/cpuset.cpus echo root > remote-repro/part/cpuset.cpus.partition echo 1-3 > remote-repro/part/cpuset.cpus cat cpuset.cpus.effective Without this fix, CPU 4 remains missing from the top cpuset after the remote partition is narrowed. Copy cpus_allowed before partition_cpus_change(). All checks and allocations that can fail have completed by this point, and cpuset_mutex remains held for the rest of the update. Keep effective_xcpus unchanged until afterward so the partition code can calculate the old-to-new difference. Fixes: f62a5d39368e ("cgroup/cpuset: Remove remote_partition_check() & make= update_cpumasks_hier() handle remote partition") Signed-off-by: Guopeng Zhang --- 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 bbc4868f026b..80a709bfa4b7 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -2618,10 +2618,17 @@ static int update_cpumask(struct cpuset *cs, struct= cpuset *trialcs, */ force =3D !cpumask_equal(cs->effective_xcpus, trialcs->effective_xcpus); =20 + /* + * remote_cpus_update() can propagate through an ancestor and revisit + * this cpuset. Make sure that it sees the new configured CPU mask. + */ + spin_lock_irq(&callback_lock); + cpumask_copy(cs->cpus_allowed, trialcs->cpus_allowed); + spin_unlock_irq(&callback_lock); + partition_cpus_change(cs, trialcs, &tmp); =20 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); --=20 2.43.0 From nobody Fri Sep 25 16:50:49 2026 Received: from mta0.migadu.com (out-187.mta0.migadu.com [91.218.175.187]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D53D63F1ABF for ; Thu, 10 Sep 2026 09:47:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.187 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789033645; cv=none; b=LKsJSp/xZE72/It0Cn0YagT2zYeCNYvkWKJ2lri5QLeq/O1qYl9GaUHOv7NRsktfhpvuXQhGzAm4vNFW0Rj7R7K/p1CEngMvHyBE+fRClMbiRQkRRNIV3bOnOyiULCQ8EzQF2BoBOPHXnlw/VHaql5I9K9WqJ2z0oa9WNBnlGCE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789033645; c=relaxed/simple; bh=HolPMd8/7+8zon78gTwvmMPvfZlvri2GRivOncH46xI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=YberMz8aYMJIxqZxf2sazKFLEoHmqn2qhfqEZX8GQqpNG9CbAMmGFaYjZwk7xif7vSTyCFiDSJVyfjfLik/HGY6wjAoPYzQCbVVpdBTejlu7Tqap9BjQ/bbegCfoejMWom1LMVU35uORiCs1Q1NmKQCkiflTN5H0fOc1ein1x2c= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=iPEhwuL0; arc=none smtp.client-ip=91.218.175.187 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="iPEhwuL0" X-Envelope-To: linux-kernel@vger.kernel.org DKIM-Signature: a=rsa-sha256; bh=HolPMd8/7+8zon78gTwvmMPvfZlvri2GRivOncH46xI=; c=simple/simple; d=linux.dev; h=from:to:subject:date:message-id:mime-version:content-type; s=key1; t=1789033638; v=1; x=1789638438; b=iPEhwuL0bcM/pn0RHtXKiIZtCCEqE4DrFIabYllKx/zCPdxZNxQ2XlEkZC/JEBzg3jS4Nivh JRp9Gx9JLPyX51aI1IpuMcygDi3SX8ljsvpJeLUOn0bVwuHPtAcl89uYz7QcuTC4MXV8CKvpZoV 49liJKoozG8Q2YNYJ7hF1uiI= X-Envelope-To: linux-kernel@vger.kernel.org Received: by smtp.migadu.com with ESMTPS id d8f0ac91ae137753; Thu, 10 Sep 2026 09:47:18 +0000 X-Mizu-Trace-ID: d8f0ac91ae137753 X-Migadu-Flow: FLOW_OUT From: Guopeng Zhang To: Waiman Long , Ridong Chen Cc: Tejun Heo , Johannes Weiner , =?UTF-8?q?Michal=20Koutn=C3=BD?= , cgroups@vger.kernel.org, linux-kernel@vger.kernel.org, Guopeng Zhang Subject: [PATCH v4 7/7] cgroup/cpuset: Publish exclusive_cpus before partition updates Date: Thu, 10 Sep 2026 17:45:46 +0800 Message-ID: <20260910094546.5852-8-guopeng.zhang@linux.dev> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260910094546.5852-1-guopeng.zhang@linux.dev> References: <20260910094546.5852-1-guopeng.zhang@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Guopeng Zhang update_exclusive_cpumask() calls partition_cpus_change() before publishing the new exclusive_cpus mask. When an invalid partition is recovered, update_parent_effective_cpumask() therefore gets the old mask from user_xcpus(). If that mask contains only offline CPUs and the partition is populated, the valid replacement is rejected with PERR_NOCPUS. This can be reproduced on a cgroup v2 system with CPUs 1-4 online: cd /sys/fs/cgroup echo +cpuset > cgroup.subtree_control mkdir exclusive-repro echo 1-4 > exclusive-repro/cpuset.cpus echo isolated > exclusive-repro/cpuset.cpus.partition echo +cpuset > exclusive-repro/cgroup.subtree_control mkdir exclusive-repro/child echo 4 > exclusive-repro/child/cpuset.cpus.exclusive echo isolated > exclusive-repro/child/cpuset.cpus.partition sleep 1000 & task=3D$! echo $task > exclusive-repro/child/cgroup.procs echo 0 > /sys/devices/system/cpu/cpu4/online echo 3 > exclusive-repro/child/cpuset.cpus.exclusive cat exclusive-repro/child/cpuset.cpus.partition echo 1 > /sys/devices/system/cpu/cpu4/online kill $task Without this fix, the child remains invalid after CPU 3 is written because the recovery path still validates the old CPU 4 mask. Publish exclusive_cpus after all fallible preparation and before partition_cpus_change(), as update_cpumask() already does for cpus_allowed. Keep effective_xcpus unchanged until the partition update has computed the old-to-new difference. Fixes: c49b5e89c45f ("cpuset: use partition_cpus_change for setting exclusi= ve cpus") Signed-off-by: Guopeng Zhang --- kernel/cgroup/cpuset.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c index 80a709bfa4b7..94c56ec8d4f8 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -2690,10 +2690,18 @@ static int update_exclusive_cpumask(struct cpuset *= cs, struct cpuset *trialcs, return -ENOMEM; =20 trialcs->prs_err =3D PERR_NONE; - partition_cpus_change(cs, trialcs, &tmp); =20 + /* + * partition_cpus_change() may inspect user_xcpus(cs). Publish the + * configured mask that was used to compute trialcs->effective_xcpus. + */ spin_lock_irq(&callback_lock); cpumask_copy(cs->exclusive_cpus, trialcs->exclusive_cpus); + spin_unlock_irq(&callback_lock); + + partition_cpus_change(cs, trialcs, &tmp); + + spin_lock_irq(&callback_lock); cpumask_copy(cs->effective_xcpus, trialcs->effective_xcpus); if ((old_prs > 0) && !is_partition_valid(cs)) reset_partition_data(cs); --=20 2.43.0