From nobody Mon Jun 8 04:15:55 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7573B30C15C for ; Thu, 4 Jun 2026 15:03:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780585385; cv=none; b=Hf3/LNbTO3ZBVuLKR3Kku1rrI1QS414FUrFib2H1DBEIwjD78VGtqQJsJPdHsLgaRzk/ZRZCYpNo5tXFWM/VIXN9ld9Mgn3RmddoAIfLQgdeatHWfCsN4q366HnzV+Gd0+4wvwM1a9RNdoOZ6dS+4VYYWoPN2Wk0JoHMYXmNgmg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780585385; c=relaxed/simple; bh=WnNIN/RzbpUh0IyL4vvpzNYRJlHk9UDe83Q/ncPXD4Y=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Jl2XhZT5qcLMqCICd82tVZTGl7oiZpf4xOUpZ+sThHlHeFHzn5bpgwTravPr4IWLStRmBUiSjesu+g8n1EzNCUYbnpYsB85ayJ4dqnB8zz8NBuvv9QQY0xeYTgsyfk1ERt11jIlanLGAWW+G9PnwazZ1h0qiYTR4oB3/kqcwXkQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=XKcOS0YT; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="XKcOS0YT" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1780585383; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=0FrkYdUDwBoR5o4p88v8K3yiyF02buGsjkf+7iTywpA=; b=XKcOS0YT6jMkWAy+ACi4O+N92yA7mFeyTQD32gXl3jTRXuKQc75BqHjLTD4zRGb7j674D+ ClRF6bsOfG40x7z9TgOEJyuJAgSz7md5yqNpyjksVk8aQKmHmE5Ab1BhzmCJG2Lu4p6EeT +SUl6b0S2VLQkb3ggKoLI5ynr7Ub6TY= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-573-nSIbrvXjOPyVVQ6Bzwg5JA-1; Thu, 04 Jun 2026 11:02:59 -0400 X-MC-Unique: nSIbrvXjOPyVVQ6Bzwg5JA-1 X-Mimecast-MFC-AGG-ID: nSIbrvXjOPyVVQ6Bzwg5JA_1780585378 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id E003F19560AD; Thu, 4 Jun 2026 15:02:57 +0000 (UTC) Received: from llong-thinkpadp16vgen1.westford.csb (unknown [10.22.88.175]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 09B08195608E; Thu, 4 Jun 2026 15:02:55 +0000 (UTC) From: Waiman Long To: Ridong Chen , Tejun Heo , Johannes Weiner , =?UTF-8?q?Michal=20Koutn=C3=BD?= , Peter Zijlstra Cc: cgroups@vger.kernel.org, linux-kernel@vger.kernel.org, Aaron Tomlin , Guopeng Zhang , Waiman Long Subject: [PATCH-next v6 1/6] cgroup/cpuset: Fix node inconsistencies between cpuset_update_tasks_nodemask() and cpuset_attach() Date: Thu, 4 Jun 2026 11:02:24 -0400 Message-ID: <20260604150229.414135-2-longman@redhat.com> In-Reply-To: <20260604150229.414135-1-longman@redhat.com> References: <20260604150229.414135-1-longman@redhat.com> 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 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 Content-Type: text/plain; charset="utf-8" Whenever memory node mask is changed, there are 4 places where the node mask has to be updated or used. 1) task's node mask via cpuset_change_task_nodemask() 2) memory policy binding via mpol_rebind_mm() 3) if memory migration is enabled, migrate from old_mems_allowed to the new node mask via cpuset_migrate_mm(). 4) setting old_mems_allowed These memory actions are done in cpuset_update_tasks_nodemask() and cpuset_attach(). However there are inconsistencies in what node masks are being used in these 2 functions. In cpuset_update_tasks_nodemask(), - cpuset_change_task_nodemask(): guarantee_online_mems() - mpol_rebind_mm(): mems_allowed - cpuset_migrate_mm(): guarantee_online_mems() - old_mems_allowed: guarantee_online_mems() In cpuset_attach(), - cpuset_change_task_nodemask(): guarantee_online_mems() - mpol_rebind_mm(): effective_mems - cpuset_migrate_mm(): effective_mems - old_mems_allowed: effective_mems These inconsistencies dates back to quite a long time ago and it is hard to say what should be the correct values. The guarantee_online_mems() function returns a node mask from current or an ancestor cpuset that is a subset of node_states[N_MEMORY]. Nodes in node_states[N_MEMORY] are all online, i.e. in node_states[N_ONLINE]. However, node in node_states[N_ONLINE] may not have memory. So node_states[N_MEMORY] should be a subset of node_states[N_ONLINE]. The guarantee_online_mems() function should only be useful for v1 where mems_allowed is the same as effective_mems. With v2, the memory nodes in effective_mems should always be a subset of node_states[N_MEMORY]. The only time that may not be true is when a memory hot-unplug operation is in progress and a memory node is removed from node_states[N_MEMORY] but not yet reflected in effective_mems as cpuset_handle_hotplug() has not yet been called from cpuset_track_online_nodes(). When cpuset_handle_hotplug() is called later, the memory node setting of the relevant cpusets and tasks will be updated. So replacing the guarantee_online_mems() call by just using cs->effective_mems should be fine. Let use the following setup for both of them and make them consistent. - cpuset_change_task_nodemask(): guarantee_online_mems() - mpol_rebind_mm(): effective_mems - cpuset_migrate_mm(): guarantee_online_mems() - old_mems_allowed: guarantee_online_mems() So for v2, it is effectively all effective_mems. For v1, mpol_rebind_mm() uses mems_allowed which may differ from what guarantee_online_mems() returns. Signed-off-by: Waiman Long Reviewed-by: Ridong Chen --- kernel/cgroup/cpuset.c | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c index 6bdb68689c24..8305b5830c3c 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -489,7 +489,10 @@ static void guarantee_active_cpus(struct task_struct *= tsk, * Return in *pmask the portion of a cpusets's mems_allowed that * are online, with memory. If none are online with memory, walk * up the cpuset hierarchy until we find one that does have some - * online mems. The top cpuset always has some mems online. + * online mems. The top cpuset always has some mems online. With v2, + * effective_mems should always contain online memory nodes except + * during the transition period where a memory node hotunplug operation + * is in progress. * * One way or another, we guarantee to return some non-empty subset * of node_states[N_MEMORY]. @@ -498,6 +501,10 @@ static void guarantee_active_cpus(struct task_struct *= tsk, */ static void guarantee_online_mems(struct cpuset *cs, nodemask_t *pmask) { + if (cpuset_v2()) { + *pmask =3D cs->effective_mems; + return; + } while (!nodes_and(*pmask, cs->effective_mems, node_states[N_MEMORY])) cs =3D parent_cs(cs); } @@ -2616,6 +2623,13 @@ static void *cpuset_being_rebound; * Iterate through each task of @cs updating its mems_allowed to the * effective cpuset's. As this function is called with cpuset_mutex held, * cpuset membership stays stable. + * + * - cpuset_change_task_nodemask(): guarantee_online_mems() + * - mpol_rebind_mm(): effective_mems + * - cpuset_migrate_mm(): guarantee_online_mems() + * - old_mems_allowed: guarantee_online_mems() + * + * For v2, guarantee_online_mems() should just return effective_mems. */ void cpuset_update_tasks_nodemask(struct cpuset *cs) { @@ -2624,7 +2638,6 @@ void cpuset_update_tasks_nodemask(struct cpuset *cs) struct task_struct *task; =20 cpuset_being_rebound =3D cs; /* causes mpol_dup() rebind */ - guarantee_online_mems(cs, &newmems); =20 /* @@ -2650,7 +2663,7 @@ void cpuset_update_tasks_nodemask(struct cpuset *cs) =20 migrate =3D is_memory_migrate(cs); =20 - mpol_rebind_mm(mm, &cs->mems_allowed); + mpol_rebind_mm(mm, &cs->effective_mems); if (migrate) cpuset_migrate_mm(mm, &cs->old_mems_allowed, &newmems); else @@ -3148,17 +3161,18 @@ static void cpuset_attach(struct cgroup_taskset *ts= et) =20 /* * In the default hierarchy, enabling cpuset in the child cgroups - * will trigger a number of cpuset_attach() calls with no change - * in effective cpus and mems. In that case, we can optimize out - * by skipping the task iteration and update. + * will trigger a cpuset_attach() call with no change in effective cpus + * and mems. In that case, we can optimize out by skipping the task + * iteration and update. */ - if (cpuset_v2() && !cpus_updated && !mems_updated) { + if (cpuset_v2()) { cpuset_attach_nodemask_to =3D cs->effective_mems; - goto out; + if (!cpus_updated && !mems_updated) + goto out; + } else { + guarantee_online_mems(cs, &cpuset_attach_nodemask_to); } =20 - guarantee_online_mems(cs, &cpuset_attach_nodemask_to); - cgroup_taskset_for_each(task, css, tset) cpuset_attach_task(cs, task); =20 @@ -3168,7 +3182,6 @@ static void cpuset_attach(struct cgroup_taskset *tset) * if there is no change in effective_mems and CS_MEMORY_MIGRATE is * not set. */ - cpuset_attach_nodemask_to =3D cs->effective_mems; if (!is_memory_migrate(cs) && !mems_updated) goto out; =20 @@ -3176,7 +3189,7 @@ static void cpuset_attach(struct cgroup_taskset *tset) struct mm_struct *mm =3D get_task_mm(leader); =20 if (mm) { - mpol_rebind_mm(mm, &cpuset_attach_nodemask_to); + mpol_rebind_mm(mm, &cs->effective_mems); =20 /* * old_mems_allowed is the same with mems_allowed --=20 2.54.0 From nobody Mon Jun 8 04:15:55 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A0D0D337BAB for ; Thu, 4 Jun 2026 15:03:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780585392; cv=none; b=Wqzy4kLPp17Y2AQsIYTSu91S4QXtNa2oCAmYnhJfEyFRjwKkWN/prEcYTo7sUf8yiaBftwfYVuwgBBVAZkMa8+j+5oioGQlO88FIVb9Y8/vq/S2YkBiel+dWsLD9HVGzt7JjVFvCPpSacnyQBlNXaUrZI4L4BzonEwiREV4px64= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780585392; c=relaxed/simple; bh=kCXjFiV3OaQmRHtJRYcDNtFee0sVIlUqFWC2gVb5Nao=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Lpm0k2e+wpDQjsT0LFH4FUDktmpQVCTeDOnTUqZlcoFoMzVVff9Eutijar1VU/Dum6nZGyQv7a9wV8PGGI3djl4QU0677nbpsT6e1GAuZFFsTDcYDdUJd6/t6YYAYJY+nyyHg0sHozUvuunobsNPiaI1OXpLhFBeYm6/dvkrukk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=UXdrVmnd; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="UXdrVmnd" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1780585390; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=wze3ha0fD0FeJfke0hywdP2ew+PgvCUHHXwDCurAWsk=; b=UXdrVmndTA9lD6E4naUIoPzP3l/lgS0YUzB/IkgnJfzzG607CvqRe41j8r4DdlvuIHvmOP DLR7UHXGI+xEMrjvAjvKpGpdBK4fgY4NFytmpxnlzPlXpoE4gnhplqKLBrLItzIZ1tV+vC r3huVOauKgcOFEDRbeIVAS1VCnWqNKI= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-247--s-UT8zEOAiqcwfmnDh62A-1; Thu, 04 Jun 2026 11:03:02 -0400 X-MC-Unique: -s-UT8zEOAiqcwfmnDh62A-1 X-Mimecast-MFC-AGG-ID: -s-UT8zEOAiqcwfmnDh62A_1780585380 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 08F9A1944D2B; Thu, 4 Jun 2026 15:03:00 +0000 (UTC) Received: from llong-thinkpadp16vgen1.westford.csb (unknown [10.22.88.175]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 21D4B1954193; Thu, 4 Jun 2026 15:02:58 +0000 (UTC) From: Waiman Long To: Ridong Chen , Tejun Heo , Johannes Weiner , =?UTF-8?q?Michal=20Koutn=C3=BD?= , Peter Zijlstra Cc: cgroups@vger.kernel.org, linux-kernel@vger.kernel.org, Aaron Tomlin , Guopeng Zhang , Waiman Long Subject: [PATCH-next v6 2/6] cgroup/cpuset: Add a cpuset_reserve_dl_bw() helper Date: Thu, 4 Jun 2026 11:02:25 -0400 Message-ID: <20260604150229.414135-3-longman@redhat.com> In-Reply-To: <20260604150229.414135-1-longman@redhat.com> References: <20260604150229.414135-1-longman@redhat.com> 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 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 Content-Type: text/plain; charset="utf-8" Extract the DL bandwidth allocation code in cpuset_attach() to a new cpuset_reserve_dl_bw() helper to simplify code. No functional change is expected. Reviewed-by: Ridong Chen Signed-off-by: Waiman Long --- kernel/cgroup/cpuset.c | 53 ++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c index 8305b5830c3c..7c23d26a04fc 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -2994,6 +2994,25 @@ static int cpuset_can_attach_check(struct cpuset *cs) return 0; } =20 +static int cpuset_reserve_dl_bw(struct cpuset *cs) +{ + int cpu, ret; + + if (!cs->sum_migrate_dl_bw) + return 0; + + cpu =3D cpumask_any_and(cpu_active_mask, cs->effective_cpus); + if (unlikely(cpu >=3D nr_cpu_ids)) + return -EINVAL; + + ret =3D dl_bw_alloc(cpu, cs->sum_migrate_dl_bw); + if (ret) + return ret; + + cs->dl_bw_cpu =3D cpu; + return 0; +} + static void reset_migrate_dl_data(struct cpuset *cs) { cs->nr_migrate_dl_tasks =3D 0; @@ -3008,7 +3027,7 @@ static int cpuset_can_attach(struct cgroup_taskset *t= set) struct cpuset *cs, *oldcs; struct task_struct *task; bool setsched_check; - int cpu, ret; + int ret; =20 /* used later by cpuset_attach() */ cpuset_attach_old_cs =3D task_cs(cgroup_taskset_first(tset, &css)); @@ -3064,31 +3083,19 @@ static int cpuset_can_attach(struct cgroup_taskset = *tset) } } =20 - if (!cs->sum_migrate_dl_bw) - goto out_success; - - cpu =3D cpumask_any_and(cpu_active_mask, cs->effective_cpus); - if (unlikely(cpu >=3D nr_cpu_ids)) { - ret =3D -EINVAL; - goto out_unlock; - } - - ret =3D dl_bw_alloc(cpu, cs->sum_migrate_dl_bw); - if (ret) - goto out_unlock; - - cs->dl_bw_cpu =3D cpu; - -out_success: - /* - * Mark attach is in progress. This makes validate_change() fail - * changes which zero cpus/mems_allowed. - */ - cs->attach_in_progress++; + ret =3D cpuset_reserve_dl_bw(cs); =20 out_unlock: - if (ret) + if (ret) { reset_migrate_dl_data(cs); + } else { + /* + * Mark attach is in progress. This makes validate_change() fail + * changes which zero cpus/mems_allowed. + */ + cs->attach_in_progress++; + } + mutex_unlock(&cpuset_mutex); return ret; } --=20 2.54.0 From nobody Mon Jun 8 04:15:55 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 243B133B97D for ; Thu, 4 Jun 2026 15:03:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780585393; cv=none; b=lcci3sB2Pp9jMYZuPFrn8kkjmKsjh+calsILeMvebD9IqO/n6p5hLqUNCX3shwKm1mRh0YMVCE0rfrXPKm0tW6jLeIBnaniIfMkbLFdexvNcnc3vOEBiDhVfuFcOoRtak8Ls+r9jYA/ieUPJv+CZpRAIBNyh6IOVl2G6JNLo5dc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780585393; c=relaxed/simple; bh=Web3NhGOEgJly7BqxpjC+2T3f6MY2rQiRKu4qUJn/Ho=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=pw4QDohCcmiLQ0KeWWC5ows3DI5EpJsmkDLen8ElHUj8uK7Oyb/285y/dtuWvW8r/h9+rNaKsVQab/2Fa6jY9CwP+fz3hbh2gXAvELQuaFc/tDVaWM9LhwdrG4pEAhRSf4b8KhxKQ/vACksFXWJ9hpfcakWdlkDLFUxzAZguoqI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=AzWpTHuC; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="AzWpTHuC" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1780585391; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=JxdjWzovNnqXR2QwuqvSpwhzvQnZWNNgULA2P9EiwMk=; b=AzWpTHuCFcdW9BQeUzet5KVOzGR3GcFe2rVy9PnRJzn5luC9/4XMU7/RUKkpAasY7iNyoa 2URXijjqBEgYHEXGm5NFtgVa9zKgifxgVPwuvXgK6aFVISKrHzZFVr/AwJzcbf0SKtAyQB 3pqTjJgV+vHAhbozq7QUhVJcoXsLrUA= Received: from mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-634-uCFo5UABNVOyAg9Yf1O8NQ-1; Thu, 04 Jun 2026 11:03:05 -0400 X-MC-Unique: uCFo5UABNVOyAg9Yf1O8NQ-1 X-Mimecast-MFC-AGG-ID: uCFo5UABNVOyAg9Yf1O8NQ_1780585382 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 6FCD61801BEC; Thu, 4 Jun 2026 15:03:02 +0000 (UTC) Received: from llong-thinkpadp16vgen1.westford.csb (unknown [10.22.88.175]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 50738195608E; Thu, 4 Jun 2026 15:03:00 +0000 (UTC) From: Waiman Long To: Ridong Chen , Tejun Heo , Johannes Weiner , =?UTF-8?q?Michal=20Koutn=C3=BD?= , Peter Zijlstra Cc: cgroups@vger.kernel.org, linux-kernel@vger.kernel.org, Aaron Tomlin , Guopeng Zhang , Waiman Long Subject: [PATCH-next v6 3/6] cgroup/cpuset: Expand the scope of cpuset_can_attach_check() Date: Thu, 4 Jun 2026 11:02:26 -0400 Message-ID: <20260604150229.414135-4-longman@redhat.com> In-Reply-To: <20260604150229.414135-1-longman@redhat.com> References: <20260604150229.414135-1-longman@redhat.com> 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 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 Content-Type: text/plain; charset="utf-8" Expand the scope of cpuset_can_attach_check() by including the setting of setsched flag inside cpuset_can_attach_check() with the new @oldcs and @psetsched argument. As cpuset_can_attach_check() is also called from cpuset_can_fork(), set the new arguments to NULL from that caller. Reviewed-by: Ridong Chen Signed-off-by: Waiman Long --- kernel/cgroup/cpuset.c | 52 ++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c index 7c23d26a04fc..90fb40760dcc 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -2985,12 +2985,39 @@ static struct cpuset *cpuset_attach_old_cs; * For v1, cpus_allowed and mems_allowed can't be empty. * For v2, effective_cpus can't be empty. * Note that in v1, effective_cpus =3D cpus_allowed. + * + * Also set the boolean flag passed in by @psetsched depending on if + * security_task_setscheduler() call is needed and @oldcs is not NULL. */ -static int cpuset_can_attach_check(struct cpuset *cs) +static int cpuset_can_attach_check(struct cpuset *cs, struct cpuset *oldcs, + bool *psetsched) { if (cpumask_empty(cs->effective_cpus) || (!is_in_v2_mode() && nodes_empty(cs->mems_allowed))) return -ENOSPC; + + if (!oldcs) + return 0; + + /* + * Skip rights over task setsched check in v2 when nothing changes, + * migration permission derives from hierarchy ownership in + * cgroup_procs_write_permission()). + */ + *psetsched =3D !cpuset_v2() || + !cpumask_equal(cs->effective_cpus, oldcs->effective_cpus) || + !nodes_equal(cs->effective_mems, oldcs->effective_mems); + + /* + * A v1 cpuset with tasks will have no CPU left only when CPU hotplug + * brings the last online CPU offline as users are not allowed to empty + * cpuset.cpus when there are active tasks inside. When that happens, + * we should allow tasks to migrate out without security check to make + * sure they will be able to run after migration. + */ + if (!is_in_v2_mode() && cpumask_empty(oldcs->effective_cpus)) + *psetsched =3D false; + return 0; } =20 @@ -3037,29 +3064,10 @@ static int cpuset_can_attach(struct cgroup_taskset = *tset) mutex_lock(&cpuset_mutex); =20 /* Check to see if task is allowed in the cpuset */ - ret =3D cpuset_can_attach_check(cs); + ret =3D cpuset_can_attach_check(cs, oldcs, &setsched_check); if (ret) goto out_unlock; =20 - /* - * Skip rights over task setsched check in v2 when nothing changes, - * migration permission derives from hierarchy ownership in - * cgroup_procs_write_permission()). - */ - setsched_check =3D !cpuset_v2() || - !cpumask_equal(cs->effective_cpus, oldcs->effective_cpus) || - !nodes_equal(cs->effective_mems, oldcs->effective_mems); - - /* - * A v1 cpuset with tasks will have no CPU left only when CPU hotplug - * brings the last online CPU offline as users are not allowed to empty - * cpuset.cpus when there are active tasks inside. When that happens, - * we should allow tasks to migrate out without security check to make - * sure they will be able to run after migration. - */ - if (!is_in_v2_mode() && cpumask_empty(oldcs->effective_cpus)) - setsched_check =3D false; - cgroup_taskset_for_each(task, css, tset) { ret =3D task_can_attach(task); if (ret) @@ -3604,7 +3612,7 @@ static int cpuset_can_fork(struct task_struct *task, = struct css_set *cset) mutex_lock(&cpuset_mutex); =20 /* Check to see if task is allowed in the cpuset */ - ret =3D cpuset_can_attach_check(cs); + ret =3D cpuset_can_attach_check(cs, NULL, NULL); if (ret) goto out_unlock; =20 --=20 2.54.0 From nobody Mon Jun 8 04:15:55 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8C3963358AF for ; Thu, 4 Jun 2026 15:03:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780585392; cv=none; b=E+KxHP9/jprw6iq88I3yTs1cGu7zVvVh06UJoXa98npM5VrNWkTxOnveRKxeAAeercWNdhYF0tKFGN72d62Gb0abr/sG0HPzO5ZdHXJuLT/YZhHwYSNvQAUwtG8RuijD7b4hwMjUMmMdB5nOO2AAHD7PLggDS26L08R+4AFCo+Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780585392; c=relaxed/simple; bh=5B99D09DdyBmXZ5XDwXOMeMAK7xGYCfxD+zxkPwMjNU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=SL/MV1Jl+ExUiCqhheG3TfoFk0TGVCIFQLxdd2QbxcXTup2QJsFRR03Spi6oj9rJMk6iA65hIEI0WtHFBS1uPs+rWz2F1Kp5Lh/81fk5I8o7thcMsn00jSNDntCFBEplldIK6r0Gezw18RNWMGVuukYah14SnvW55+68zsSvBW0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=XVetNsAf; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="XVetNsAf" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1780585390; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=xIDyanHY/OuAC5+5/iBjnUiC6sENK2eFiQwui8IGqGU=; b=XVetNsAfbC8qkWCR0NO1Kx4eJXBhvjQCQDCqqt4IbINHCjzFt8gPyxn097DdLa2bXZRDs+ +fF7Q58s++WhHp8c1ye06LeCDse+k8iB3r6OTPIpDbbG0TsZK9uEQOX2GsvLpwAo6N0QX6 lfzgEs77pNhuu9674jz0L2FU6LbzsNQ= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-269-nU2XziAhMgSUVSZYDBhIUA-1; Thu, 04 Jun 2026 11:03:07 -0400 X-MC-Unique: nU2XziAhMgSUVSZYDBhIUA-1 X-Mimecast-MFC-AGG-ID: nU2XziAhMgSUVSZYDBhIUA_1780585384 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id BE3801944CC5; Thu, 4 Jun 2026 15:03:04 +0000 (UTC) Received: from llong-thinkpadp16vgen1.westford.csb (unknown [10.22.88.175]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id A8AA9195608E; Thu, 4 Jun 2026 15:03:02 +0000 (UTC) From: Waiman Long To: Ridong Chen , Tejun Heo , Johannes Weiner , =?UTF-8?q?Michal=20Koutn=C3=BD?= , Peter Zijlstra Cc: cgroups@vger.kernel.org, linux-kernel@vger.kernel.org, Aaron Tomlin , Guopeng Zhang , Waiman Long Subject: [PATCH-next v6 4/6] cgroup/cpuset: Make cpuset_attach_old_cs track task group leaders Date: Thu, 4 Jun 2026 11:02:27 -0400 Message-ID: <20260604150229.414135-5-longman@redhat.com> In-Reply-To: <20260604150229.414135-1-longman@redhat.com> References: <20260604150229.414135-1-longman@redhat.com> 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 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 Content-Type: text/plain; charset="utf-8" There are two possible ways that migration of tasks from multiple source cpusets to a target cpuset can happen. Either a multithread application with threads in different cpusets is wholely moved to a new cpuset or disabling of v2 cpuset controller will move all the tasks in child cpusets to the parent cpuset. In the former case, it is the mm setting of the group leader that really matters. So cpuset_attach_old_cs should track the oldcs of the thread leader. In the latter case, effective_mems of child cpusets must always be a subset of the parent. So no real page migration will be necessary no matter which child cpuset is selected as cpuset_attach_old_cs. IOW, cpuset_attach_old_cs should be updated to match the latest task group leader in cpuset_can_attach(), but fall back to that of the first task if there is no group leader in the taskset. Suggested-by: Ridong Chen Reviewed-by: Ridong Chen Signed-off-by: Waiman Long --- kernel/cgroup/cpuset.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c index 90fb40760dcc..e29129467c98 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -2978,6 +2978,10 @@ static int update_prstate(struct cpuset *cs, int new= _prs) return 0; } =20 +/* + * cpuset_can_attach() and cpuset_attach() specific internal data + * Protected by cpuset_mutex + */ static struct cpuset *cpuset_attach_old_cs; =20 /* @@ -3068,11 +3072,32 @@ static int cpuset_can_attach(struct cgroup_taskset = *tset) if (ret) goto out_unlock; =20 + /* + * The cpuset_attach_old_cs is used mainly by cpuset_migrate_mm() to get + * the old_mems_allowed value. There are two ways that many-to-one + * cpuset migration can happen: + * 1) A multithread application with threads in different cpusets is + * wholely migrated to a new cpuset. + * 2) Disabling v2 cpuset controller will move all the tasks in child + * cpusets to the parent cpuset. + * + * In the former case, it is the mm setting of the group leader that + * really matters. So cpuset_attach_old_cs should track the oldcs of the + * group leader. It falls back to the oldcs of the first task if there + * is no group leader in the taskset. In the latter case, effective_mems + * of child cpusets must always be a subset of the parent. So no real + * page migration will be necessary no matter which child cpuset is + * selected as cpuset_attach_old_cs. + */ cgroup_taskset_for_each(task, css, tset) { ret =3D task_can_attach(task); if (ret) goto out_unlock; =20 + /* Update cpuset_attach_old_cs to the latest group leader */ + if (task =3D=3D task->group_leader) + cpuset_attach_old_cs =3D task_cs(task); + if (setsched_check) { ret =3D security_task_setscheduler(task); if (ret) --=20 2.54.0 From nobody Mon Jun 8 04:15:55 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7D21D33EB06 for ; Thu, 4 Jun 2026 15:03:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780585395; cv=none; b=In0EPaNrwPvuag4O6h/KraFaZ5TSTMHU2i7Vru2SpMox3HNiiGCAf1IxAATclqxUv5unNFYL9OO2arWD2NF47OLLCNPDZ1vMvfJw4Gu5g06puz55VsnTUpciP3Ej+ZmtGLLL3FKQ1Q53hc4XTRf+twzSGyafOAcfJweDH438DkU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780585395; c=relaxed/simple; bh=znMM3647Sh828ZACjredD0kc198ohO48pkMjQGu5wTU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=o0EDF/HGtuePu14FtvTqggqWpXia/gmj19LsWrnAnB1cAQpA/meRDkmA/JMttKYntk0W56rlvEpXQ4Rdngs5LC7w8INY04bR9+HqdZJT34MuSCVSQ7rLb+sQCIY2+2G0EnLpvmzDNhLzt2VqYLtqqjYMXkhTTFVQ1diIzJ2zv78= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=E7DtDVhs; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="E7DtDVhs" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1780585392; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=NLkvco/hYViqdTfggj3+WAooj52GCaGs2AJxcM/3BHw=; b=E7DtDVhsbJ/tGUGtaQV8e66Cp42mX2GS1lf3ywNOLcLujsWr8Xoi+S6B5G3jD3K30ZhmKB EbxBfMtS+qn9gw4jfWyQrVyoAFMaBqiLPbGBlt6eFt6sVsg0YhJztKgt0ev7UBsGpwTiom dV9SvzJS8uMitva/mpwwNQ1+r83fg58= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-391-FMUuwMKeOIuHGpEkMziPEw-1; Thu, 04 Jun 2026 11:03:08 -0400 X-MC-Unique: FMUuwMKeOIuHGpEkMziPEw-1 X-Mimecast-MFC-AGG-ID: FMUuwMKeOIuHGpEkMziPEw_1780585386 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id A88C91956089; Thu, 4 Jun 2026 15:03:06 +0000 (UTC) Received: from llong-thinkpadp16vgen1.westford.csb (unknown [10.22.88.175]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id F37EA195608E; Thu, 4 Jun 2026 15:03:04 +0000 (UTC) From: Waiman Long To: Ridong Chen , Tejun Heo , Johannes Weiner , =?UTF-8?q?Michal=20Koutn=C3=BD?= , Peter Zijlstra Cc: cgroups@vger.kernel.org, linux-kernel@vger.kernel.org, Aaron Tomlin , Guopeng Zhang , Waiman Long Subject: [PATCH-next v6 5/6] cgroup/cpuset: Move mpol_rebind_mm/cpuset_migrate_mm() calls inside cpuset_attach_task() Date: Thu, 4 Jun 2026 11:02:28 -0400 Message-ID: <20260604150229.414135-6-longman@redhat.com> In-Reply-To: <20260604150229.414135-1-longman@redhat.com> References: <20260604150229.414135-1-longman@redhat.com> 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 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 Content-Type: text/plain; charset="utf-8" The cpuset_attach_task() was introduced in commit 42a11bf5c543 ("cgroup/cpuset: Make cpuset_fork() handle CLONE_INTO_CGROUP properly") to enable the CLONE_INTO_CGROUP flag of clone(2) to behave more like moving a task from one cpuset into another one. That commits didn't move the mpol_rebind_mm() and cpuset_migrate_mm() calls for group leader into cpuset_attach_task(). When the CLONE_INTO_CGROUP flag is used without CLONE_THREAD, the new task is its own group leader. So it is still not equivalent to moving task between cpusets in this case. Make CLONE_INTO_CGROUP behaves more close to cpuset_attach() by moving the mpol_rebind_mm() and cpuset_migrate_mm() calls inside cpuset_attach_task(). As a result, the following static variables will have to be updated in cpuset_fork(). - cpuset_attach_old_cs - attach_cpus_updated - attach_mems_updated - queue_task_work Signed-off-by: Waiman Long --- kernel/cgroup/cpuset.c | 103 ++++++++++++++++++++++++----------------- 1 file changed, 60 insertions(+), 43 deletions(-) diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c index e29129467c98..1142d5eba58d 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -2981,8 +2981,13 @@ static int update_prstate(struct cpuset *cs, int new= _prs) /* * cpuset_can_attach() and cpuset_attach() specific internal data * Protected by cpuset_mutex + * + * The attach_cpus_updated/attach_mems_updated flags are set in either + * cpuset_attach() or cpuset_fork() and used in cpuset_attach_task(). */ static struct cpuset *cpuset_attach_old_cs; +static bool attach_cpus_updated; +static bool attach_mems_updated; =20 /* * Check to see if a cpuset can accept a new task @@ -3160,9 +3165,12 @@ static void cpuset_cancel_attach(struct cgroup_tasks= et *tset) */ static cpumask_var_t cpus_attach; static nodemask_t cpuset_attach_nodemask_to; +static bool queue_task_work; =20 static void cpuset_attach_task(struct cpuset *cs, struct task_struct *task) { + struct mm_struct *mm; + lockdep_assert_cpuset_lock_held(); =20 if (cs !=3D &top_cpuset) @@ -3176,28 +3184,60 @@ static void cpuset_attach_task(struct cpuset *cs, s= truct task_struct *task) */ WARN_ON_ONCE(set_cpus_allowed_ptr(task, cpus_attach)); =20 + if (cpuset_v2() && !attach_mems_updated) + return; + cpuset_change_task_nodemask(task, &cpuset_attach_nodemask_to); cpuset1_update_task_spread_flags(cs, task); + + if ((task !=3D task->group_leader) || + (!is_memory_migrate(cs) && !attach_mems_updated)) + return; + + /* + * Change mm for threadgroup leader. This is expensive and may + * sleep and should be moved outside migration path proper. + */ + mm =3D get_task_mm(task); + if (mm) { + struct cpuset *oldcs =3D cpuset_attach_old_cs; + + mpol_rebind_mm(mm, &cs->effective_mems); + + /* + * old_mems_allowed is the same with mems_allowed + * here, except if this task is being moved + * automatically due to hotplug. In that case + * @mems_allowed has been updated and is empty, so + * @old_mems_allowed is the right nodesets that we + * migrate mm from. + */ + if (is_memory_migrate(cs)) { + cpuset_migrate_mm(mm, &oldcs->old_mems_allowed, + &cpuset_attach_nodemask_to); + queue_task_work =3D true; + } else { + mmput(mm); + } + } } =20 static void cpuset_attach(struct cgroup_taskset *tset) { struct task_struct *task; - struct task_struct *leader; struct cgroup_subsys_state *css; struct cpuset *cs; struct cpuset *oldcs =3D cpuset_attach_old_cs; - bool cpus_updated, mems_updated; - bool queue_task_work =3D false; =20 cgroup_taskset_first(tset, &css); cs =3D css_cs(css); =20 lockdep_assert_cpus_held(); /* see cgroup_attach_lock() */ mutex_lock(&cpuset_mutex); - cpus_updated =3D !cpumask_equal(cs->effective_cpus, - oldcs->effective_cpus); - mems_updated =3D !nodes_equal(cs->effective_mems, oldcs->effective_mems); + queue_task_work =3D false; + + attach_cpus_updated =3D !cpumask_equal(cs->effective_cpus, oldcs->effecti= ve_cpus); + attach_mems_updated =3D !nodes_equal(cs->effective_mems, oldcs->effective= _mems); =20 /* * In the default hierarchy, enabling cpuset in the child cgroups @@ -3207,7 +3247,7 @@ static void cpuset_attach(struct cgroup_taskset *tset) */ if (cpuset_v2()) { cpuset_attach_nodemask_to =3D cs->effective_mems; - if (!cpus_updated && !mems_updated) + if (!attach_cpus_updated && !attach_mems_updated) goto out; } else { guarantee_online_mems(cs, &cpuset_attach_nodemask_to); @@ -3216,38 +3256,6 @@ static void cpuset_attach(struct cgroup_taskset *tse= t) cgroup_taskset_for_each(task, css, tset) cpuset_attach_task(cs, task); =20 - /* - * Change mm for all threadgroup leaders. This is expensive and may - * sleep and should be moved outside migration path proper. Skip it - * if there is no change in effective_mems and CS_MEMORY_MIGRATE is - * not set. - */ - if (!is_memory_migrate(cs) && !mems_updated) - goto out; - - cgroup_taskset_for_each_leader(leader, css, tset) { - struct mm_struct *mm =3D get_task_mm(leader); - - if (mm) { - mpol_rebind_mm(mm, &cs->effective_mems); - - /* - * old_mems_allowed is the same with mems_allowed - * here, except if this task is being moved - * automatically due to hotplug. In that case - * @mems_allowed has been updated and is empty, so - * @old_mems_allowed is the right nodesets that we - * migrate mm from. - */ - if (is_memory_migrate(cs)) { - cpuset_migrate_mm(mm, &oldcs->old_mems_allowed, - &cpuset_attach_nodemask_to); - queue_task_work =3D true; - } else - mmput(mm); - } - } - out: if (queue_task_work) schedule_flush_migrate_mm(); @@ -3681,15 +3689,14 @@ static void cpuset_cancel_fork(struct task_struct *= task, struct css_set *cset) */ static void cpuset_fork(struct task_struct *task) { - struct cpuset *cs; - bool same_cs; + struct cpuset *cs, *oldcs; =20 rcu_read_lock(); cs =3D task_cs(task); - same_cs =3D (cs =3D=3D task_cs(current)); + oldcs =3D task_cs(current); rcu_read_unlock(); =20 - if (same_cs) { + if (cs =3D=3D oldcs) { if (cs =3D=3D &top_cpuset) return; =20 @@ -3701,7 +3708,17 @@ static void cpuset_fork(struct task_struct *task) /* CLONE_INTO_CGROUP */ mutex_lock(&cpuset_mutex); guarantee_online_mems(cs, &cpuset_attach_nodemask_to); + /* + * Assume CPUs and memory nodes are updated + * A CLONE_INTO_CGROUP operation should have taken the cgroup mutex + * and so there shouldn't be a competing cpuset_attach() operation. + */ + attach_cpus_updated =3D attach_mems_updated =3D true; + queue_task_work =3D false; + cpuset_attach_old_cs =3D oldcs; cpuset_attach_task(cs, task); + if (queue_task_work) + schedule_flush_migrate_mm(); =20 dec_attach_in_progress_locked(cs); mutex_unlock(&cpuset_mutex); --=20 2.54.0 From nobody Mon Jun 8 04:15:55 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9FCE535675E for ; Thu, 4 Jun 2026 15:03:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780585397; cv=none; b=kHDcs2nT5gtiub9cl83AWn7FbiNq1BulHBdiu+cILif5v18VVy7oFMbwsAKIjw2r7QzTaiKZup2qwaS5xNQWUvdgWZ7cuMkLKnJ/IYGkc5p+ozvMbmyz3THQp5CzCcFvKyF18cpHWZ6C83kUZZfTmnb9oIQ4nLEBGan39g+17mY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780585397; c=relaxed/simple; bh=9VJj+oBaynIvLfUtNkLOHvAHD6LQQFW2lzJiYG4MD5g=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=K5oyospVSP67ZijRTlgzCSTlPgsfa5QZJUKbXEh86NekCIF/v8YrrYV5z/fUNQa3yWXJoLARkkn9X7T9+1CFkCtU+uYX0mCQ9j76gSGWiR/cbv8f1Z0B/l8BI+4hQt0uo7mIufIhQxhpR2F2x81arRQPbXG/dVU42p7FRgQM1P0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=Du7h62FI; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="Du7h62FI" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1780585394; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=2G0yOtxTEFcsRk3EJ7heasRynQmxfGwehRgDqz1xesw=; b=Du7h62FIViWbA+Yn1Q30O8hIPJrlWpdhHYajLnbRZNDVlIwQHL+EgWl5bYfoFzoDp4LE6W IioR6lnbuT45UQkcpoIOkB7f1BmZaV8/XOQ0tfQ+uAsxM+P6fBFvCAGVnlmbHYMcpVxUDf zOpaBCURSPjrH/oMQr77ZJnNuR9/7V4= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-421-4wZsULbJOrqs6NFHC-nkXg-1; Thu, 04 Jun 2026 11:03:11 -0400 X-MC-Unique: 4wZsULbJOrqs6NFHC-nkXg-1 X-Mimecast-MFC-AGG-ID: 4wZsULbJOrqs6NFHC-nkXg_1780585389 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 0D0FB1956053; Thu, 4 Jun 2026 15:03:09 +0000 (UTC) Received: from llong-thinkpadp16vgen1.westford.csb (unknown [10.22.88.175]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id DF662195608E; Thu, 4 Jun 2026 15:03:06 +0000 (UTC) From: Waiman Long To: Ridong Chen , Tejun Heo , Johannes Weiner , =?UTF-8?q?Michal=20Koutn=C3=BD?= , Peter Zijlstra Cc: cgroups@vger.kernel.org, linux-kernel@vger.kernel.org, Aaron Tomlin , Guopeng Zhang , Waiman Long Subject: [PATCH-next v6 6/6] cgroup/cpuset: Support multiple source/destination cpusets for cpuset_*attach() Date: Thu, 4 Jun 2026 11:02:29 -0400 Message-ID: <20260604150229.414135-7-longman@redhat.com> In-Reply-To: <20260604150229.414135-1-longman@redhat.com> References: <20260604150229.414135-1-longman@redhat.com> 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 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 Content-Type: text/plain; charset="utf-8" With cgroup v2, the cgroup_taskset structure passed into the cgroup can_attach() and attach() methods can contain task migration data with multiple destination or source cpusets when the cpuset controller is enabled or disabled respectively. Since cpuset is threaded in both v1 and v2, another possible way to cause many-to-one migration is to move the whole process with multiple threads in different cpuset enabled threaded cgroups into another cpuset enabled cgroup. The current cpuset_can_attach() and cpuset_attach() functions still expect task migration is from one source cpuset to one destination cpuset. This has been the case since cpuset was enabled for cgroup v2 in commit 4ec22e9c5a90 ("cpuset: Enable cpuset controller in default hierarchy"). This problem is less an issue when enabling the cpuset controller as all the newly created child cpusets will have exactly the same set of CPUs and memory nodes except when deadline tasks are involved in migration as the deadline task accounting data can be off. It can be more problematic when the cpuset controller is disabled as their set of CPUs and memory nodes may differ from their parent or with the moving of multi-threaded process from different threaded cgroups. Fix that by tracking the set of source (old) and destination cpusets in singly linked lists and iterating them all to properly update the internal data. Also keep the current cs and oldcs variables up-to-date with the css and task iterators. This commit assumes that the set of source and destination cpusets are distnct. IOW, the cgroup core shouldn't move a task from a given cpuset back to itself. So a given cpuset cannot be both a source and a destination cpuset. Running an experiment by moving a multithreaded process with threads in multiple cpusets in threaded cgroups back to its domain cgroup confirms that only threads that need to be moved are included into the cgroup_taskset passed to cpuset_can_attach(). A WARN_ON_ONCE() is added to cpuset_can_attach_check() to make sure that this should always be the case. To ensure proper DL tasks accounting, the nr_migrate_dl_tasks in both the source and destination cpusets are decremented/incremented with their values added to nr_deadline_tasks when the migration is successful. Fixes: 4ec22e9c5a90 ("cpuset: Enable cpuset controller in default hierarchy= ") Signed-off-by: Waiman Long --- kernel/cgroup/cpuset-internal.h | 6 + kernel/cgroup/cpuset.c | 216 ++++++++++++++++++++++++-------- 2 files changed, 172 insertions(+), 50 deletions(-) diff --git a/kernel/cgroup/cpuset-internal.h b/kernel/cgroup/cpuset-interna= l.h index f7aaf01f7cd5..4c2772a7fd5e 100644 --- a/kernel/cgroup/cpuset-internal.h +++ b/kernel/cgroup/cpuset-internal.h @@ -161,6 +161,12 @@ struct cpuset { */ bool remote_partition; =20 + /* + * cpuset_can_attach() and cpuset_attach() specific data + */ + bool attach_node_in_llist; + struct llist_node attach_node; + /* * number of SCHED_DEADLINE tasks attached to this cpuset, so that we * know when to rebuild associated root domain bandwidth information. diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c index 1142d5eba58d..d624cd0a1e04 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -37,6 +37,7 @@ #include #include #include +#include =20 DEFINE_STATIC_KEY_FALSE(cpusets_pre_enable_key); DEFINE_STATIC_KEY_FALSE(cpusets_enabled_key); @@ -2986,8 +2987,11 @@ static int update_prstate(struct cpuset *cs, int new= _prs) * cpuset_attach() or cpuset_fork() and used in cpuset_attach_task(). */ static struct cpuset *cpuset_attach_old_cs; +static LLIST_HEAD(src_cs_head); +static LLIST_HEAD(dst_cs_head); static bool attach_cpus_updated; static bool attach_mems_updated; +static bool attach_conflict; =20 /* * Check to see if a cpuset can accept a new task @@ -3008,6 +3012,23 @@ static int cpuset_can_attach_check(struct cpuset *cs= , struct cpuset *oldcs, if (!oldcs) return 0; =20 + /* + * The cgroup core shouldn't migrate a task to its original cpuset. + * In the unlikely event that it happens, it will be treated as a + * destination cpuset only and the attach_conflict flag will be set. + */ + if (WARN_ON_ONCE(cs =3D=3D oldcs)) + attach_conflict =3D true; + + if (!cs->attach_node_in_llist) { + llist_add(&cs->attach_node, &dst_cs_head); + cs->attach_node_in_llist =3D true; + } + if (!oldcs->attach_node_in_llist) { + llist_add(&oldcs->attach_node, &src_cs_head); + oldcs->attach_node_in_llist =3D true; + } + /* * Skip rights over task setsched check in v2 when nothing changes, * migration permission derives from hierarchy ownership in @@ -3030,33 +3051,101 @@ static int cpuset_can_attach_check(struct cpuset *= cs, struct cpuset *oldcs, return 0; } =20 -static int cpuset_reserve_dl_bw(struct cpuset *cs) +/* + * If reset_dl_bw is set, reset the previous dl_bw_alloc() call. Otherwise, + * update nr_deadline_tasks according to nr_migrate_dl_tasks in both source + * and destination cpusets. + */ +static void clear_attach_data(bool reset_dl_bw) { + struct cpuset *cs, *next; + + llist_for_each_entry_safe(cs, next, src_cs_head.first, attach_node) { + cs->attach_node.next =3D NULL; + cs->attach_node_in_llist =3D false; + if (cs->nr_migrate_dl_tasks && !reset_dl_bw) + cs->nr_deadline_tasks +=3D cs->nr_migrate_dl_tasks; + cs->nr_migrate_dl_tasks =3D 0; + } + + llist_for_each_entry_safe(cs, next, dst_cs_head.first, attach_node) { + cs->attach_node.next =3D NULL; + cs->attach_node_in_llist =3D false; + if (reset_dl_bw && cs->dl_bw_cpu >=3D 0) + dl_bw_free(cs->dl_bw_cpu, cs->sum_migrate_dl_bw); + if (cs->nr_migrate_dl_tasks && !reset_dl_bw) + cs->nr_deadline_tasks +=3D cs->nr_migrate_dl_tasks; + cs->nr_migrate_dl_tasks =3D 0; + cs->sum_migrate_dl_bw =3D 0; + cs->dl_bw_cpu =3D -1; + } + + src_cs_head.first =3D NULL; + dst_cs_head.first =3D NULL; +} + +static int cpuset_reserve_dl_bw(void) +{ + struct cpuset *cs; int cpu, ret; =20 - if (!cs->sum_migrate_dl_bw) - return 0; + llist_for_each_entry(cs, dst_cs_head.first, attach_node) { + if (!cs->sum_migrate_dl_bw) + continue; =20 - cpu =3D cpumask_any_and(cpu_active_mask, cs->effective_cpus); - if (unlikely(cpu >=3D nr_cpu_ids)) - return -EINVAL; + cpu =3D cpumask_any_and(cpu_active_mask, cs->effective_cpus); + if (unlikely(cpu >=3D nr_cpu_ids)) + return -EINVAL; =20 - ret =3D dl_bw_alloc(cpu, cs->sum_migrate_dl_bw); - if (ret) - return ret; + ret =3D dl_bw_alloc(cpu, cs->sum_migrate_dl_bw); + if (ret) + return ret; =20 - cs->dl_bw_cpu =3D cpu; + cs->dl_bw_cpu =3D cpu; + } return 0; } =20 -static void reset_migrate_dl_data(struct cpuset *cs) +static void set_attach_in_progress(void) { - cs->nr_migrate_dl_tasks =3D 0; - cs->sum_migrate_dl_bw =3D 0; - cs->dl_bw_cpu =3D -1; + struct cpuset *cs; + + /* + * Mark attach is in progress. This makes validate_change() fail + * changes which zero cpus/mems_allowed. + */ + llist_for_each_entry(cs, dst_cs_head.first, attach_node) + cs->attach_in_progress++; } =20 -/* Called by cgroups to determine if a cpuset is usable; cpuset_mutex held= */ +static void reset_attach_in_progress(void) +{ + struct cpuset *cs; + + llist_for_each_entry(cs, dst_cs_head.first, attach_node) + dec_attach_in_progress_locked(cs); +} + +/* + * Called by cgroups to determine if a cpuset is usable; cpuset_mutex held. + * + * With cgroup v2, enabling of cpuset controller in a cgroup subtree can + * cause @tset to contain task migration data from one parent cpuset to mu= ltiple + * child cpusets. Not much is needed to be done here other than tracking t= he + * number of DL tasks in each cpuset as the CPUs and memory nodes of the c= hild + * cpusets are exactly the same as the parent. + * + * Conversely, disabling of cpuset controller can cause @tset to contain t= ask + * migration data from multiple child cpusets to one parent cpuset. Here, = the + * CPUs and memory nodes of the child cpusets may be different from the pa= rent, + * but must be a subset of its parent. + * + * Another possible many-to-one migration is the moving of the whole + * multithreaded process with threads in different cpusets to another cpus= et. + * + * For all other use cases, @tset task migration data should be from one s= ource + * cpuset to one destination cpuset. + */ static int cpuset_can_attach(struct cgroup_taskset *tset) { struct cgroup_subsys_state *css; @@ -3069,6 +3158,7 @@ static int cpuset_can_attach(struct cgroup_taskset *t= set) cpuset_attach_old_cs =3D task_cs(cgroup_taskset_first(tset, &css)); oldcs =3D cpuset_attach_old_cs; cs =3D css_cs(css); + attach_conflict =3D false; =20 mutex_lock(&cpuset_mutex); =20 @@ -3095,6 +3185,16 @@ static int cpuset_can_attach(struct cgroup_taskset *= tset) * selected as cpuset_attach_old_cs. */ cgroup_taskset_for_each(task, css, tset) { + struct cpuset *newcs =3D css_cs(css); + struct cpuset *new_oldcs =3D task_cs(task); + + if ((newcs !=3D cs) || (new_oldcs !=3D oldcs)) { + cs =3D newcs; + oldcs =3D new_oldcs; + ret =3D cpuset_can_attach_check(cs, oldcs, &setsched_check); + if (ret) + goto out_unlock; + } ret =3D task_can_attach(task); if (ret) goto out_unlock; @@ -3116,23 +3216,19 @@ static int cpuset_can_attach(struct cgroup_taskset = *tset) * contribute to sum_migrate_dl_bw. */ cs->nr_migrate_dl_tasks++; + oldcs->nr_migrate_dl_tasks--; if (dl_task_needs_bw_move(task, cs->effective_cpus)) cs->sum_migrate_dl_bw +=3D task->dl.dl_bw; } } =20 - ret =3D cpuset_reserve_dl_bw(cs); + ret =3D cpuset_reserve_dl_bw(); =20 out_unlock: - if (ret) { - reset_migrate_dl_data(cs); - } else { - /* - * Mark attach is in progress. This makes validate_change() fail - * changes which zero cpus/mems_allowed. - */ - cs->attach_in_progress++; - } + if (ret) + clear_attach_data(true); + else + set_attach_in_progress(); =20 mutex_unlock(&cpuset_mutex); return ret; @@ -3147,14 +3243,8 @@ static void cpuset_cancel_attach(struct cgroup_tasks= et *tset) cs =3D css_cs(css); =20 mutex_lock(&cpuset_mutex); - dec_attach_in_progress_locked(cs); - - if (cs->dl_bw_cpu >=3D 0) - dl_bw_free(cs->dl_bw_cpu, cs->sum_migrate_dl_bw); - - if (cs->nr_migrate_dl_tasks) - reset_migrate_dl_data(cs); - + reset_attach_in_progress(); + clear_attach_data(true); mutex_unlock(&cpuset_mutex); } =20 @@ -3227,48 +3317,74 @@ static void cpuset_attach(struct cgroup_taskset *ts= et) struct task_struct *task; struct cgroup_subsys_state *css; struct cpuset *cs; - struct cpuset *oldcs =3D cpuset_attach_old_cs; + bool many_sources =3D src_cs_head.first && src_cs_head.first->next; =20 cgroup_taskset_first(tset, &css); - cs =3D css_cs(css); =20 lockdep_assert_cpus_held(); /* see cgroup_attach_lock() */ mutex_lock(&cpuset_mutex); queue_task_work =3D false; =20 - attach_cpus_updated =3D !cpumask_equal(cs->effective_cpus, oldcs->effecti= ve_cpus); - attach_mems_updated =3D !nodes_equal(cs->effective_mems, oldcs->effective= _mems); + /* + * attach_cpus_updated/attach_mems_updated can be set to false if + * source and destination masks are the same and there is only one + * source cpuset with attach_conflict flag unset. IOW, a task migration + * with many source cpusets is always treated as updated as the tasks + * to old cpuset mapping is lost. + */ + if (many_sources || attach_conflict) { + attach_cpus_updated =3D true; + attach_mems_updated =3D true; + } else { + /* Only one source cpuset */ + struct cpuset *oldcs =3D cpuset_attach_old_cs; + + attach_cpus_updated =3D false; + attach_mems_updated =3D false; + llist_for_each_entry(cs, dst_cs_head.first, attach_node) { + attach_cpus_updated |=3D !cpumask_equal(cs->effective_cpus, + oldcs->effective_cpus); + attach_mems_updated |=3D !nodes_equal(cs->effective_mems, + oldcs->effective_mems); + } + } =20 + cs =3D css_cs(css); /* * In the default hierarchy, enabling cpuset in the child cgroups * will trigger a cpuset_attach() call with no change in effective cpus * and mems. In that case, we can optimize out by skipping the task - * iteration and update. + * iteration and update, but the destination cpuset list is iterated to + * set old_mems_sllowed. */ if (cpuset_v2()) { cpuset_attach_nodemask_to =3D cs->effective_mems; - if (!attach_cpus_updated && !attach_mems_updated) + if (!attach_cpus_updated && !attach_mems_updated) { + llist_for_each_entry(cs, dst_cs_head.first, attach_node) + cs->old_mems_allowed =3D cs->effective_mems; goto out; + } } else { guarantee_online_mems(cs, &cpuset_attach_nodemask_to); } =20 - cgroup_taskset_for_each(task, css, tset) + cgroup_taskset_for_each(task, css, tset) { + struct cpuset *newcs =3D css_cs(css); + + if (newcs !=3D cs) { + cs->old_mems_allowed =3D cpuset_attach_nodemask_to; + cs =3D newcs; + guarantee_online_mems(cs, &cpuset_attach_nodemask_to); + } cpuset_attach_task(cs, task); + } =20 -out: if (queue_task_work) schedule_flush_migrate_mm(); cs->old_mems_allowed =3D cpuset_attach_nodemask_to; - - if (cs->nr_migrate_dl_tasks) { - cs->nr_deadline_tasks +=3D cs->nr_migrate_dl_tasks; - oldcs->nr_deadline_tasks -=3D cs->nr_migrate_dl_tasks; - reset_migrate_dl_data(cs); - } - - dec_attach_in_progress_locked(cs); - +out: + reset_attach_in_progress(); + clear_attach_data(false); mutex_unlock(&cpuset_mutex); } =20 --=20 2.54.0 From nobody Mon Jun 8 04:15:55 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AB603A59 for ; Fri, 5 Jun 2026 00:02:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780617779; cv=none; b=hJFgypYNlAYLP01fI33+dhQz2OPBmjTb7PBOuCcmCBSsSIx7vMxE+DczH2uuU921wJcEgmwqgmgLrMcUONslGYB4s3JmxjPmqms3R/heOdFQR3sDUsylO5592FxfY5a9Nozh/fZYY0VchCMgyiDQ4pxwoizcUUJsHoKl0IASy44= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780617779; c=relaxed/simple; bh=P9p+dDC1Pm0NxCW9h9JkGjTDMxVUUWDqx8OgsaK0rY8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=gFpxL8bi5kv9D6YX00SZWgqRkhl3SSjpb9Op/tjtBh7F0Keo2slL1AP7hhMdI1t6AhGLmHTX410BBB1+eYxAm9WKm2KwVfvyf86/77GS+lVkOq22UjrqC5vnb0u2ph9VIIUrWCePvqJwTvYBVoyy+lzqnuhCYj93t7AZabH+UEI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=FM34IFd/; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="FM34IFd/" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1780617777; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=utEQQW0nk2SmWoR5Bw701AMJOQWS7AJnnjatBLSvYJY=; b=FM34IFd/c5VpysaHMKwetSI5I/MUVvaC5ZRQPBuvMjZSXZbaAu7wAXKRMJwdi5rvM4CJvg gB9reYH33OZ5FEYQ5XMmpHbGsffn0qxONUKOKlZCyib8eQnQooYcAxYty4rqtUzWAS9n3M zU294U5ZMB30SWLdVsrw4JfoBj2Xb8o= Received: from mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-463-zyIxCLm8Mx-2AZh2XlUDew-1; Thu, 04 Jun 2026 20:02:51 -0400 X-MC-Unique: zyIxCLm8Mx-2AZh2XlUDew-1 X-Mimecast-MFC-AGG-ID: zyIxCLm8Mx-2AZh2XlUDew_1780617769 Received: from mx-prod-int-10.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-10.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.95]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 8F65218005B6; Fri, 5 Jun 2026 00:02:48 +0000 (UTC) Received: from llong-thinkpadp16vgen1.westford.csb (unknown [10.22.88.175]) by mx-prod-int-10.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 0AF21414; Fri, 5 Jun 2026 00:02:45 +0000 (UTC) From: Waiman Long To: Ridong Chen , Tejun Heo , Johannes Weiner , =?UTF-8?q?Michal=20Koutn=C3=BD?= , Peter Zijlstra Cc: cgroups@vger.kernel.org, linux-kernel@vger.kernel.org, Aaron Tomlin , Guopeng Zhang , Waiman Long Subject: [PATCH-next v6 7/6] cgroup/cpuset: Set old_mems_allowed from guarantee_online_mems() consistently Date: Thu, 4 Jun 2026 20:02:24 -0400 Message-ID: <20260605000224.451246-1-longman@redhat.com> In-Reply-To: <20260604150229.414135-1-longman@redhat.com> References: <20260604150229.414135-1-longman@redhat.com> 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 X-Scanned-By: MIMEDefang 3.6 on 10.30.177.95 Content-Type: text/plain; charset="utf-8" An earlier patch has added an optimization in guarantee_online_mems() to just return effective_mems for v2. However there is a short window during memory hotunplug operation that it can return a nodemask with no online node leading to possible memory OOM. To avoid this scenario, though highly unlikely, the optimization is dropped. Also set old_mems_allowed of the cpuset structure consistently with the output of guarantee_online_mems() whenever an attach or a related operation is in progress. Signed-off-by: Waiman Long --- kernel/cgroup/cpuset.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c index d624cd0a1e04..5dabe9d040e9 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -502,10 +502,6 @@ static void guarantee_active_cpus(struct task_struct *= tsk, */ static void guarantee_online_mems(struct cpuset *cs, nodemask_t *pmask) { - if (cpuset_v2()) { - *pmask =3D cs->effective_mems; - return; - } while (!nodes_and(*pmask, cs->effective_mems, node_states[N_MEMORY])) cs =3D parent_cs(cs); } @@ -3350,22 +3346,24 @@ static void cpuset_attach(struct cgroup_taskset *ts= et) } =20 cs =3D css_cs(css); + guarantee_online_mems(cs, &cpuset_attach_nodemask_to); + /* * In the default hierarchy, enabling cpuset in the child cgroups * will trigger a cpuset_attach() call with no change in effective cpus * and mems. In that case, we can optimize out by skipping the task * iteration and update, but the destination cpuset list is iterated to - * set old_mems_sllowed. + * set old_mems_allowed. */ - if (cpuset_v2()) { - cpuset_attach_nodemask_to =3D cs->effective_mems; - if (!attach_cpus_updated && !attach_mems_updated) { - llist_for_each_entry(cs, dst_cs_head.first, attach_node) - cs->old_mems_allowed =3D cs->effective_mems; - goto out; + if (cpuset_v2() && !attach_cpus_updated && !attach_mems_updated) { + struct cpuset *tcs; + + llist_for_each_entry(tcs, dst_cs_head.first, attach_node) { + if (tcs =3D=3D cs) + continue; + guarantee_online_mems(tcs, &tcs->old_mems_allowed); } - } else { - guarantee_online_mems(cs, &cpuset_attach_nodemask_to); + goto out; } =20 cgroup_taskset_for_each(task, css, tset) { @@ -3381,8 +3379,8 @@ static void cpuset_attach(struct cgroup_taskset *tset) =20 if (queue_task_work) schedule_flush_migrate_mm(); - cs->old_mems_allowed =3D cpuset_attach_nodemask_to; out: + cs->old_mems_allowed =3D cpuset_attach_nodemask_to; reset_attach_in_progress(); clear_attach_data(false); mutex_unlock(&cpuset_mutex); @@ -3824,6 +3822,8 @@ static void cpuset_fork(struct task_struct *task) /* CLONE_INTO_CGROUP */ mutex_lock(&cpuset_mutex); guarantee_online_mems(cs, &cpuset_attach_nodemask_to); + cs->old_mems_allowed =3D cpuset_attach_nodemask_to; + /* * Assume CPUs and memory nodes are updated * A CLONE_INTO_CGROUP operation should have taken the cgroup mutex --=20 2.54.0