Commit 452477fa authored by Tejun Heo's avatar Tejun Heo

cpuset: pin down cpus and mems while a task is being attached

cpuset is scheduled to be decoupled from cgroup_lock which will make
configuration updates race with task migration.  Any config update
will be allowed to happen between ->can_attach() and ->attach().  If
such config update removes either all cpus or mems, by the time
->attach() is called, the condition verified by ->can_attach(), that
the cpuset is capable of hosting the tasks, is no longer true.

This patch adds cpuset->attach_in_progress which is incremented from
->can_attach() and decremented when the attach operation finishes
either successfully or not.  validate_change() treats cpusets w/
non-zero ->attach_in_progress like cpusets w/ tasks and refuses to
remove all cpus or mems from it.

This currently doesn't make any functional difference as everything is
protected by cgroup_mutex but enables decoupling the locking.
Signed-off-by: default avatarTejun Heo <>
Acked-by: default avatarLi Zefan <>
parent 8d033948
......@@ -91,6 +91,12 @@ struct cpuset {
struct fmeter fmeter; /* memory_pressure filter */
* Tasks are being attached to this cpuset. Used to prevent
* zeroing cpus/mems_allowed between ->can_attach() and ->attach().
int attach_in_progress;
/* partition number for rebuild_sched_domains() */
int pn;
......@@ -468,9 +474,12 @@ static int validate_change(const struct cpuset *cur, const struct cpuset *trial)
goto out;
/* Cpusets with tasks can't have empty cpus_allowed or mems_allowed */
* Cpusets with tasks - existing or newly being attached - can't
* have empty cpus_allowed or mems_allowed.
ret = -ENOSPC;
if (cgroup_task_count(cur->css.cgroup) &&
if ((cgroup_task_count(cur->css.cgroup) || cur->attach_in_progress) &&
(cpumask_empty(trial->cpus_allowed) ||
goto out;
......@@ -1386,9 +1395,21 @@ static int cpuset_can_attach(struct cgroup *cgrp, struct cgroup_taskset *tset)
return ret;
* Mark attach is in progress. This makes validate_change() fail
* changes which zero cpus/mems_allowed.
return 0;
static void cpuset_cancel_attach(struct cgroup *cgrp,
struct cgroup_taskset *tset)
* Protected by cgroup_mutex. cpus_attach is used only by cpuset_attach()
* but we can't allocate it dynamically there. Define it global and
......@@ -1441,6 +1462,8 @@ static void cpuset_attach(struct cgroup *cgrp, struct cgroup_taskset *tset)
/* The various types of files and directories in a cpuset file system */
......@@ -1908,6 +1931,7 @@ struct cgroup_subsys cpuset_subsys = {
.css_offline = cpuset_css_offline,
.css_free = cpuset_css_free,
.can_attach = cpuset_can_attach,
.cancel_attach = cpuset_cancel_attach,
.attach = cpuset_attach,
.subsys_id = cpuset_subsys_id,
.base_cftypes = files,
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment