From: Ridong Chen <ridong.chen@linux.dev>
To: "Waiman Long" <longman@redhat.com>, "Tejun Heo" <tj@kernel.org>,
"Johannes Weiner" <hannes@cmpxchg.org>,
"Michal Koutný" <mkoutny@suse.com>,
"Shuah Khan" <shuah@kernel.org>,
"Juri Lelli" <juri.lelli@redhat.com>
Cc: cgroups@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-kselftest@vger.kernel.org,
Aaron Tomlin <atomlin@atomlin.com>,
Guopeng Zhang <guopeng.zhang@linux.dev>
Subject: Re: [PATCH-next v9 08/11] cgroup/cpuset: Move mpol_rebind_mm/cpuset_migrate_mm() calls inside cpuset_attach_task()
Date: Wed, 1 Jul 2026 10:14:39 +0800 [thread overview]
Message-ID: <33151a98-3d3d-4a11-9919-2bf38aa83f76@linux.dev> (raw)
In-Reply-To: <20260630033344.352702-9-longman@redhat.com>
On 6/30/2026 11:33 AM, Waiman Long wrote:
> 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().
>
> Also move the stack local cpus_updated, mems_updated and queue_task_work
> flags into attach_ctx so that these flags can be accessed inside and
> outside of cpuset_attach_task(). The cpuset_fork() function is updated
> to set up these flags and do memory migration if necessary.
>
> Signed-off-by: Waiman Long <longman@redhat.com>
> ---
> kernel/cgroup/cpuset.c | 104 ++++++++++++++++++++++++-----------------
> 1 file changed, 60 insertions(+), 44 deletions(-)
>
> diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
> index 55cd580373b7..0b9df38e9a63 100644
> --- a/kernel/cgroup/cpuset.c
> +++ b/kernel/cgroup/cpuset.c
> @@ -362,6 +362,9 @@ static DECLARE_WAIT_QUEUE_HEAD(cpuset_attach_wq);
> */
> static struct {
> int in_progress;
> + bool cpus_updated;
> + bool mems_updated;
> + bool task_work_queued;
> struct cpuset *old_cs; /* Source cpuset */
> nodemask_t nodemask_to;
> } attach_ctx;
> @@ -3190,6 +3193,8 @@ static cpumask_var_t cpus_attach;
>
> static void cpuset_attach_task(struct cpuset *cs, struct task_struct *task)
> {
> + struct mm_struct *mm;
> +
> lockdep_assert_cpuset_lock_held();
>
> if (cs != &top_cpuset)
> @@ -3203,28 +3208,60 @@ static void cpuset_attach_task(struct cpuset *cs, struct task_struct *task)
> */
> WARN_ON_ONCE(set_cpus_allowed_ptr(task, cpus_attach));
>
> + if (cpuset_v2() && !attach_ctx.mems_updated)
> + return;
> +
> cpuset_change_task_nodemask(task, &attach_ctx.nodemask_to);
> cpuset1_update_task_spread_flags(cs, task);
> +
> + if ((task != task->group_leader) ||
> + (!is_memory_migrate(cs) && !attach_ctx.mems_updated))
> + return;
> +
Nit.
IIUC, the !is_memory_migrate(cs) check may be unnecessary. Previously,
placing this condition outside could prevent an unnecessary loop, but in
its current position, it appears redundant.
if (task != task->group_leader ||
!attach_ctx.mems_updated)
> + /*
> + * Change mm for threadgroup leader. This is expensive and may
> + * sleep and should be moved outside migration path proper.
> + */
> + mm = get_task_mm(task);
> + if (mm) {
> + struct cpuset *oldcs = attach_ctx.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,
> + &attach_ctx.nodemask_to);
> + attach_ctx.task_work_queued = true;
> + } else {
> + mmput(mm);
> + }
> + }
> }
>
> 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 = attach_ctx.old_cs;
> - bool cpus_updated, mems_updated;
> - bool queue_task_work = false;
>
> cgroup_taskset_first(tset, &css);
> cs = css_cs(css);
>
> lockdep_assert_cpus_held(); /* see cgroup_attach_lock() */
> mutex_lock(&cpuset_mutex);
> - cpus_updated = !cpumask_equal(cs->effective_cpus,
> - oldcs->effective_cpus);
> - mems_updated = !nodes_equal(cs->effective_mems, oldcs->effective_mems);
> + attach_ctx.task_work_queued = false;
> +
> + attach_ctx.cpus_updated = !cpumask_equal(cs->effective_cpus, oldcs->effective_cpus);
> + attach_ctx.mems_updated = !nodes_equal(cs->effective_mems, oldcs->effective_mems);
> guarantee_online_mems(cs, &attach_ctx.nodemask_to);
>
> /*
> @@ -3233,46 +3270,14 @@ static void cpuset_attach(struct cgroup_taskset *tset)
> * 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() && !attach_ctx.cpus_updated && !attach_ctx.mems_updated)
> goto out;
>
> cgroup_taskset_for_each(task, css, tset)
> cpuset_attach_task(cs, task);
>
> - /*
> - * 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 = 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,
> - &attach_ctx.nodemask_to);
> - queue_task_work = true;
> - } else
> - mmput(mm);
> - }
> - }
> -
> out:
> - if (queue_task_work)
> + if (attach_ctx.task_work_queued)
> schedule_flush_migrate_mm();
> cs->old_mems_allowed = attach_ctx.nodemask_to;
>
> @@ -3708,15 +3713,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;
>
> rcu_read_lock();
> cs = task_cs(task);
> - same_cs = (cs == task_cs(current));
> + oldcs = task_cs(current);
> rcu_read_unlock();
>
> - if (same_cs) {
> + if (cs == oldcs) {
> if (cs == &top_cpuset)
> return;
>
> @@ -3728,7 +3732,19 @@ static void cpuset_fork(struct task_struct *task)
> /* CLONE_INTO_CGROUP */
> mutex_lock(&cpuset_mutex);
> guarantee_online_mems(cs, &attach_ctx.nodemask_to);
> + cs->old_mems_allowed = attach_ctx.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_ctx.cpus_updated = attach_ctx.mems_updated = true;
> + attach_ctx.task_work_queued = false;
> + attach_ctx.old_cs = oldcs;
> cpuset_attach_task(cs, task);
> + if (attach_ctx.task_work_queued)
> + schedule_flush_migrate_mm();
>
> dec_attach_in_progress_locked();
> mutex_unlock(&cpuset_mutex);
Reviewed-by: Ridong Chen <ridong.chen@linux.dev>
--
Best regards
Ridong
next prev parent reply other threads:[~2026-07-01 2:14 UTC|newest]
Thread overview: 24+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-30 3:33 [PATCH-next v9 00/11] cgroup/cpuset: Support multiple source/destination cpusets for cpuset_*attach() Waiman Long
2026-06-30 3:33 ` [PATCH-next v9 01/11] cgroup/cpuset: Make nr_deadline_tasks an atomic_t Waiman Long
2026-06-30 14:01 ` Juri Lelli
2026-06-30 17:56 ` Waiman Long
2026-07-01 9:00 ` Juri Lelli
2026-07-01 1:19 ` Ridong Chen
2026-06-30 3:33 ` [PATCH-next v9 02/11] cgroup/cpuset: Fix node inconsistencies between cpuset_update_tasks_nodemask() and cpuset_attach() Waiman Long
2026-06-30 3:33 ` [PATCH-next v9 03/11] cgroup/cpuset: Prevent race between task attach and cpuset state change Waiman Long
2026-07-01 1:41 ` Ridong Chen
2026-07-01 20:19 ` Waiman Long
2026-06-30 3:33 ` [PATCH-next v9 04/11] cgroup/cpuset: Put all task attach related variables into attach_ctx Waiman Long
2026-06-30 3:33 ` [PATCH-next v9 05/11] cgroup/cpuset: Add a cpuset_reserve_dl_bw() helper Waiman Long
2026-06-30 3:33 ` [PATCH-next v9 06/11] cgroup/cpuset: Expand the scope of cpuset_can_attach_check() Waiman Long
2026-06-30 3:33 ` [PATCH-next v9 07/11] cgroup/cpuset: Make attach_ctx.old_cs track task group leader Waiman Long
2026-06-30 3:33 ` [PATCH-next v9 08/11] cgroup/cpuset: Move mpol_rebind_mm/cpuset_migrate_mm() calls inside cpuset_attach_task() Waiman Long
2026-07-01 2:14 ` Ridong Chen [this message]
2026-07-01 20:30 ` Waiman Long
2026-06-30 3:33 ` [PATCH-next v9 09/11] cgroup/cpuset: Support multiple source cpusets for cpuset_*attach() Waiman Long
2026-07-01 2:35 ` Ridong Chen
2026-07-01 20:44 ` Waiman Long
2026-06-30 3:33 ` [PATCH-next v9 10/11] cgroup/cpuset: Support multiple destination " Waiman Long
2026-07-01 2:51 ` Ridong Chen
2026-07-01 21:16 ` Waiman Long
2026-06-30 3:33 ` [PATCH-next v9 11/11] selftests/cgroup: Add test for cpuset affinity on controller disable Waiman Long
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=33151a98-3d3d-4a11-9919-2bf38aa83f76@linux.dev \
--to=ridong.chen@linux.dev \
--cc=atomlin@atomlin.com \
--cc=cgroups@vger.kernel.org \
--cc=guopeng.zhang@linux.dev \
--cc=hannes@cmpxchg.org \
--cc=juri.lelli@redhat.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=longman@redhat.com \
--cc=mkoutny@suse.com \
--cc=shuah@kernel.org \
--cc=tj@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.