Skip to content

Commit

Permalink
cgroup/cpuset: Add cpuset_can_fork() and cpuset_cancel_fork() methods
Browse files Browse the repository at this point in the history
In the case of CLONE_INTO_CGROUP, not all cpusets are ready to accept
new tasks. It is too late to check that in cpuset_fork(). So we need
to add the cpuset_can_fork() and cpuset_cancel_fork() methods to
pre-check it before we can allow attachment to a different cpuset.

We also need to set the attach_in_progress flag to alert other code
that a new task is going to be added to the cpuset.

Fixes: ef2c41c ("clone3: allow spawning processes into cgroups")
Suggested-by: Michal Koutný <[email protected]>
Signed-off-by: Waiman Long <[email protected]>
Cc: [email protected] # v5.7+
Signed-off-by: Tejun Heo <[email protected]>
  • Loading branch information
Waiman-Long authored and htejun committed Apr 12, 2023
1 parent 42a11bf commit eee8785
Showing 1 changed file with 86 additions and 11 deletions.
97 changes: 86 additions & 11 deletions kernel/cgroup/cpuset.c
Original file line number Diff line number Diff line change
Expand Up @@ -2453,6 +2453,20 @@ static int fmeter_getrate(struct fmeter *fmp)

static struct cpuset *cpuset_attach_old_cs;

/*
* Check to see if a cpuset can accept a new task
* 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 = cpus_allowed.
*/
static int cpuset_can_attach_check(struct cpuset *cs)
{
if (cpumask_empty(cs->effective_cpus) ||
(!is_in_v2_mode() && nodes_empty(cs->mems_allowed)))
return -ENOSPC;
return 0;
}

/* Called by cgroups to determine if a cpuset is usable; cpuset_rwsem held */
static int cpuset_can_attach(struct cgroup_taskset *tset)
{
Expand All @@ -2467,16 +2481,9 @@ static int cpuset_can_attach(struct cgroup_taskset *tset)

percpu_down_write(&cpuset_rwsem);

/* allow moving tasks into an empty cpuset if on default hierarchy */
ret = -ENOSPC;
if (!is_in_v2_mode() &&
(cpumask_empty(cs->cpus_allowed) || nodes_empty(cs->mems_allowed)))
goto out_unlock;

/*
* Task cannot be moved to a cpuset with empty effective cpus.
*/
if (cpumask_empty(cs->effective_cpus))
/* Check to see if task is allowed in the cpuset */
ret = cpuset_can_attach_check(cs);
if (ret)
goto out_unlock;

cgroup_taskset_for_each(task, css, tset) {
Expand All @@ -2493,7 +2500,6 @@ static int cpuset_can_attach(struct cgroup_taskset *tset)
* changes which zero cpus/mems_allowed.
*/
cs->attach_in_progress++;
ret = 0;
out_unlock:
percpu_up_write(&cpuset_rwsem);
return ret;
Expand Down Expand Up @@ -3264,6 +3270,68 @@ static void cpuset_bind(struct cgroup_subsys_state *root_css)
percpu_up_write(&cpuset_rwsem);
}

/*
* In case the child is cloned into a cpuset different from its parent,
* additional checks are done to see if the move is allowed.
*/
static int cpuset_can_fork(struct task_struct *task, struct css_set *cset)
{
struct cpuset *cs = css_cs(cset->subsys[cpuset_cgrp_id]);
bool same_cs;
int ret;

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

if (same_cs)
return 0;

lockdep_assert_held(&cgroup_mutex);
percpu_down_write(&cpuset_rwsem);

/* Check to see if task is allowed in the cpuset */
ret = cpuset_can_attach_check(cs);
if (ret)
goto out_unlock;

ret = task_can_attach(task, cs->effective_cpus);
if (ret)
goto out_unlock;

ret = security_task_setscheduler(task);
if (ret)
goto out_unlock;

/*
* Mark attach is in progress. This makes validate_change() fail
* changes which zero cpus/mems_allowed.
*/
cs->attach_in_progress++;
out_unlock:
percpu_up_write(&cpuset_rwsem);
return ret;
}

static void cpuset_cancel_fork(struct task_struct *task, struct css_set *cset)
{
struct cpuset *cs = css_cs(cset->subsys[cpuset_cgrp_id]);
bool same_cs;

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

if (same_cs)
return;

percpu_down_write(&cpuset_rwsem);
cs->attach_in_progress--;
if (!cs->attach_in_progress)
wake_up(&cpuset_attach_wq);
percpu_up_write(&cpuset_rwsem);
}

/*
* Make sure the new task conform to the current state of its parent,
* which could have been changed by cpuset just after it inherits the
Expand Down Expand Up @@ -3292,6 +3360,11 @@ static void cpuset_fork(struct task_struct *task)
percpu_down_write(&cpuset_rwsem);
guarantee_online_mems(cs, &cpuset_attach_nodemask_to);
cpuset_attach_task(cs, task);

cs->attach_in_progress--;
if (!cs->attach_in_progress)
wake_up(&cpuset_attach_wq);

percpu_up_write(&cpuset_rwsem);
}

Expand All @@ -3305,6 +3378,8 @@ struct cgroup_subsys cpuset_cgrp_subsys = {
.attach = cpuset_attach,
.post_attach = cpuset_post_attach,
.bind = cpuset_bind,
.can_fork = cpuset_can_fork,
.cancel_fork = cpuset_cancel_fork,
.fork = cpuset_fork,
.legacy_cftypes = legacy_files,
.dfl_cftypes = dfl_files,
Expand Down

0 comments on commit eee8785

Please sign in to comment.