Commit 42a11bf5 authored by Waiman Long's avatar Waiman Long Committed by Tejun Heo
Browse files

cgroup/cpuset: Make cpuset_fork() handle CLONE_INTO_CGROUP properly



By default, the clone(2) syscall spawn a child process into the same
cgroup as its parent. With the use of the CLONE_INTO_CGROUP flag
introduced by commit ef2c41cf ("clone3: allow spawning processes
into cgroups"), the child will be spawned into a different cgroup which
is somewhat similar to writing the child's tid into "cgroup.threads".

The current cpuset_fork() method does not properly handle the
CLONE_INTO_CGROUP case where the cpuset of the child may be different
from that of its parent.  Update the cpuset_fork() method to treat the
CLONE_INTO_CGROUP case similar to cpuset_attach().

Since the newly cloned task has not been running yet, its actual
memory usage isn't known. So it is not necessary to make change to mm
in cpuset_fork().

Fixes: ef2c41cf ("clone3: allow spawning processes into cgroups")
Reported-by: default avatarGiuseppe Scrivano <gscrivan@redhat.com>
Signed-off-by: default avatarWaiman Long <longman@redhat.com>
Cc: stable@vger.kernel.org # v5.7+
Signed-off-by: default avatarTejun Heo <tj@kernel.org>
parent ba9182a8
Loading
Loading
Loading
Loading
+42 −20
Original line number Diff line number Diff line
@@ -2515,16 +2515,33 @@ static void cpuset_cancel_attach(struct cgroup_taskset *tset)
}

/*
 * Protected by cpuset_rwsem.  cpus_attach is used only by cpuset_attach()
 * Protected by cpuset_rwsem. cpus_attach is used only by cpuset_attach_task()
 * but we can't allocate it dynamically there.  Define it global and
 * allocate from cpuset_init().
 */
static cpumask_var_t cpus_attach;
static nodemask_t cpuset_attach_nodemask_to;

static void cpuset_attach_task(struct cpuset *cs, struct task_struct *task)
{
	percpu_rwsem_assert_held(&cpuset_rwsem);

	if (cs != &top_cpuset)
		guarantee_online_cpus(task, cpus_attach);
	else
		cpumask_copy(cpus_attach, task_cpu_possible_mask(task));
	/*
	 * can_attach beforehand should guarantee that this doesn't
	 * fail.  TODO: have a better way to handle failure here
	 */
	WARN_ON_ONCE(set_cpus_allowed_ptr(task, cpus_attach));

	cpuset_change_task_nodemask(task, &cpuset_attach_nodemask_to);
	cpuset_update_task_spread_flags(cs, task);
}

static void cpuset_attach(struct cgroup_taskset *tset)
{
	/* static buf protected by cpuset_rwsem */
	static nodemask_t cpuset_attach_nodemask_to;
	struct task_struct *task;
	struct task_struct *leader;
	struct cgroup_subsys_state *css;
@@ -2555,20 +2572,8 @@ static void cpuset_attach(struct cgroup_taskset *tset)

	guarantee_online_mems(cs, &cpuset_attach_nodemask_to);

	cgroup_taskset_for_each(task, css, tset) {
		if (cs != &top_cpuset)
			guarantee_online_cpus(task, cpus_attach);
		else
			cpumask_copy(cpus_attach, task_cpu_possible_mask(task));
		/*
		 * can_attach beforehand should guarantee that this doesn't
		 * fail.  TODO: have a better way to handle failure here
		 */
		WARN_ON_ONCE(set_cpus_allowed_ptr(task, cpus_attach));

		cpuset_change_task_nodemask(task, &cpuset_attach_nodemask_to);
		cpuset_update_task_spread_flags(cs, task);
	}
	cgroup_taskset_for_each(task, css, tset)
		cpuset_attach_task(cs, task);

	/*
	 * Change mm for all threadgroup leaders. This is expensive and may
@@ -3266,11 +3271,28 @@ static void cpuset_bind(struct cgroup_subsys_state *root_css)
 */
static void cpuset_fork(struct task_struct *task)
{
	if (task_css_is_root(task, cpuset_cgrp_id))
	struct cpuset *cs;
	bool same_cs;

	rcu_read_lock();
	cs = task_cs(task);
	same_cs = (cs == task_cs(current));
	rcu_read_unlock();

	if (same_cs) {
		if (cs == &top_cpuset)
			return;

		set_cpus_allowed_ptr(task, current->cpus_ptr);
		task->mems_allowed = current->mems_allowed;
		return;
	}

	/* CLONE_INTO_CGROUP */
	percpu_down_write(&cpuset_rwsem);
	guarantee_online_mems(cs, &cpuset_attach_nodemask_to);
	cpuset_attach_task(cs, task);
	percpu_up_write(&cpuset_rwsem);
}

struct cgroup_subsys cpuset_cgrp_subsys = {