Commit a1ffc8ad authored by Yi Tao's avatar Yi Tao Committed by Tejun Heo
Browse files

cgroup: refactor the cgroup_attach_lock code to make it clearer



Dynamic cgroup migration involving threadgroup locks can be in one of
two states: no lock held, or holding the global lock. Explicitly
declaring the different lock modes to make the code easier to
understand and facilitates future extensions of the lock modes.

Signed-off-by: default avatarYi Tao <escape@linux.alibaba.com>
Signed-off-by: default avatarTejun Heo <tj@kernel.org>
parent 4a3e62df
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -140,6 +140,14 @@ enum {
	__CFTYPE_ADDED		= (1 << 18),
};

enum cgroup_attach_lock_mode {
	/* Default */
	CGRP_ATTACH_LOCK_GLOBAL,

	/* When pid=0 && threadgroup=false, see comments in cgroup_procs_write_start */
	CGRP_ATTACH_LOCK_NONE,
};

/*
 * cgroup_file is the handle for a file instance created in a cgroup which
 * is used, for example, to generate file changed notifications.  This can
+5 −4
Original line number Diff line number Diff line
@@ -249,12 +249,13 @@ int cgroup_migrate(struct task_struct *leader, bool threadgroup,

int cgroup_attach_task(struct cgroup *dst_cgrp, struct task_struct *leader,
		       bool threadgroup);
void cgroup_attach_lock(bool lock_threadgroup);
void cgroup_attach_unlock(bool lock_threadgroup);
void cgroup_attach_lock(enum cgroup_attach_lock_mode lock_mode);
void cgroup_attach_unlock(enum cgroup_attach_lock_mode lock_mode);
struct task_struct *cgroup_procs_write_start(char *buf, bool threadgroup,
					     bool *locked)
					     enum cgroup_attach_lock_mode *lock_mode)
	__acquires(&cgroup_threadgroup_rwsem);
void cgroup_procs_write_finish(struct task_struct *task, bool locked)
void cgroup_procs_write_finish(struct task_struct *task,
			       enum cgroup_attach_lock_mode lock_mode)
	__releases(&cgroup_threadgroup_rwsem);

void cgroup_lock_and_drain_offline(struct cgroup *cgrp);
+7 −7
Original line number Diff line number Diff line
@@ -69,7 +69,7 @@ int cgroup_attach_task_all(struct task_struct *from, struct task_struct *tsk)
	int retval = 0;

	cgroup_lock();
	cgroup_attach_lock(true);
	cgroup_attach_lock(CGRP_ATTACH_LOCK_GLOBAL);
	for_each_root(root) {
		struct cgroup *from_cgrp;

@@ -81,7 +81,7 @@ int cgroup_attach_task_all(struct task_struct *from, struct task_struct *tsk)
		if (retval)
			break;
	}
	cgroup_attach_unlock(true);
	cgroup_attach_unlock(CGRP_ATTACH_LOCK_GLOBAL);
	cgroup_unlock();

	return retval;
@@ -118,7 +118,7 @@ int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from)

	cgroup_lock();

	cgroup_attach_lock(true);
	cgroup_attach_lock(CGRP_ATTACH_LOCK_GLOBAL);

	/* all tasks in @from are being moved, all csets are source */
	spin_lock_irq(&css_set_lock);
@@ -154,7 +154,7 @@ int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from)
	} while (task && !ret);
out_err:
	cgroup_migrate_finish(&mgctx);
	cgroup_attach_unlock(true);
	cgroup_attach_unlock(CGRP_ATTACH_LOCK_GLOBAL);
	cgroup_unlock();
	return ret;
}
@@ -503,13 +503,13 @@ static ssize_t __cgroup1_procs_write(struct kernfs_open_file *of,
	struct task_struct *task;
	const struct cred *cred, *tcred;
	ssize_t ret;
	bool locked;
	enum cgroup_attach_lock_mode lock_mode;

	cgrp = cgroup_kn_lock_live(of->kn, false);
	if (!cgrp)
		return -ENODEV;

	task = cgroup_procs_write_start(buf, threadgroup, &locked);
	task = cgroup_procs_write_start(buf, threadgroup, &lock_mode);
	ret = PTR_ERR_OR_ZERO(task);
	if (ret)
		goto out_unlock;
@@ -532,7 +532,7 @@ static ssize_t __cgroup1_procs_write(struct kernfs_open_file *of,
	ret = cgroup_attach_task(cgrp, task, threadgroup);

out_finish:
	cgroup_procs_write_finish(task, locked);
	cgroup_procs_write_finish(task, lock_mode);
out_unlock:
	cgroup_kn_unlock(of->kn);

+49 −18
Original line number Diff line number Diff line
@@ -2482,7 +2482,7 @@ EXPORT_SYMBOL_GPL(cgroup_path_ns);

/**
 * cgroup_attach_lock - Lock for ->attach()
 * @lock_threadgroup: whether to down_write cgroup_threadgroup_rwsem
 * @lock_mode: whether to down_write cgroup_threadgroup_rwsem
 *
 * cgroup migration sometimes needs to stabilize threadgroups against forks and
 * exits by write-locking cgroup_threadgroup_rwsem. However, some ->attach()
@@ -2503,21 +2503,39 @@ EXPORT_SYMBOL_GPL(cgroup_path_ns);
 * write-locking cgroup_threadgroup_rwsem. This allows ->attach() to assume that
 * CPU hotplug is disabled on entry.
 */
void cgroup_attach_lock(bool lock_threadgroup)
void cgroup_attach_lock(enum cgroup_attach_lock_mode lock_mode)
{
	cpus_read_lock();
	if (lock_threadgroup)

	switch (lock_mode) {
	case CGRP_ATTACH_LOCK_NONE:
		break;
	case CGRP_ATTACH_LOCK_GLOBAL:
		percpu_down_write(&cgroup_threadgroup_rwsem);
		break;
	default:
		pr_warn("cgroup: Unexpected attach lock mode.");
		break;
	}
}

/**
 * cgroup_attach_unlock - Undo cgroup_attach_lock()
 * @lock_threadgroup: whether to up_write cgroup_threadgroup_rwsem
 * @lock_mode: whether to up_write cgroup_threadgroup_rwsem
 */
void cgroup_attach_unlock(bool lock_threadgroup)
void cgroup_attach_unlock(enum cgroup_attach_lock_mode lock_mode)
{
	if (lock_threadgroup)
	switch (lock_mode) {
	case CGRP_ATTACH_LOCK_NONE:
		break;
	case CGRP_ATTACH_LOCK_GLOBAL:
		percpu_up_write(&cgroup_threadgroup_rwsem);
		break;
	default:
		pr_warn("cgroup: Unexpected attach lock mode.");
		break;
	}

	cpus_read_unlock();
}

@@ -2991,7 +3009,7 @@ int cgroup_attach_task(struct cgroup *dst_cgrp, struct task_struct *leader,
}

struct task_struct *cgroup_procs_write_start(char *buf, bool threadgroup,
					     bool *threadgroup_locked)
					     enum cgroup_attach_lock_mode *lock_mode)
{
	struct task_struct *tsk;
	pid_t pid;
@@ -3008,8 +3026,13 @@ struct task_struct *cgroup_procs_write_start(char *buf, bool threadgroup,
	 * Therefore, we can skip the global lock.
	 */
	lockdep_assert_held(&cgroup_mutex);
	*threadgroup_locked = pid || threadgroup;
	cgroup_attach_lock(*threadgroup_locked);

	if (pid || threadgroup)
		*lock_mode = CGRP_ATTACH_LOCK_GLOBAL;
	else
		*lock_mode = CGRP_ATTACH_LOCK_NONE;

	cgroup_attach_lock(*lock_mode);

	rcu_read_lock();
	if (pid) {
@@ -3040,19 +3063,20 @@ struct task_struct *cgroup_procs_write_start(char *buf, bool threadgroup,
	goto out_unlock_rcu;

out_unlock_threadgroup:
	cgroup_attach_unlock(*threadgroup_locked);
	*threadgroup_locked = false;
	cgroup_attach_unlock(*lock_mode);
	*lock_mode = CGRP_ATTACH_LOCK_NONE;
out_unlock_rcu:
	rcu_read_unlock();
	return tsk;
}

void cgroup_procs_write_finish(struct task_struct *task, bool threadgroup_locked)
void cgroup_procs_write_finish(struct task_struct *task,
			       enum cgroup_attach_lock_mode lock_mode)
{
	/* release reference from cgroup_procs_write_start() */
	put_task_struct(task);

	cgroup_attach_unlock(threadgroup_locked);
	cgroup_attach_unlock(lock_mode);
}

static void cgroup_print_ss_mask(struct seq_file *seq, u16 ss_mask)
@@ -3104,6 +3128,7 @@ static int cgroup_update_dfl_csses(struct cgroup *cgrp)
	struct cgroup_subsys_state *d_css;
	struct cgroup *dsct;
	struct css_set *src_cset;
	enum cgroup_attach_lock_mode lock_mode;
	bool has_tasks;
	int ret;

@@ -3135,7 +3160,13 @@ static int cgroup_update_dfl_csses(struct cgroup *cgrp)
	 * write-locking can be skipped safely.
	 */
	has_tasks = !list_empty(&mgctx.preloaded_src_csets);
	cgroup_attach_lock(has_tasks);

	if (has_tasks)
		lock_mode = CGRP_ATTACH_LOCK_GLOBAL;
	else
		lock_mode = CGRP_ATTACH_LOCK_NONE;

	cgroup_attach_lock(lock_mode);

	/* NULL dst indicates self on default hierarchy */
	ret = cgroup_migrate_prepare_dst(&mgctx);
@@ -3156,7 +3187,7 @@ static int cgroup_update_dfl_csses(struct cgroup *cgrp)
	ret = cgroup_migrate_execute(&mgctx);
out_finish:
	cgroup_migrate_finish(&mgctx);
	cgroup_attach_unlock(has_tasks);
	cgroup_attach_unlock(lock_mode);
	return ret;
}

@@ -5279,13 +5310,13 @@ static ssize_t __cgroup_procs_write(struct kernfs_open_file *of, char *buf,
	struct task_struct *task;
	const struct cred *saved_cred;
	ssize_t ret;
	bool threadgroup_locked;
	enum cgroup_attach_lock_mode lock_mode;

	dst_cgrp = cgroup_kn_lock_live(of->kn, false);
	if (!dst_cgrp)
		return -ENODEV;

	task = cgroup_procs_write_start(buf, threadgroup, &threadgroup_locked);
	task = cgroup_procs_write_start(buf, threadgroup, &lock_mode);
	ret = PTR_ERR_OR_ZERO(task);
	if (ret)
		goto out_unlock;
@@ -5311,7 +5342,7 @@ static ssize_t __cgroup_procs_write(struct kernfs_open_file *of, char *buf,
	ret = cgroup_attach_task(dst_cgrp, task, threadgroup);

out_finish:
	cgroup_procs_write_finish(task, threadgroup_locked);
	cgroup_procs_write_finish(task, lock_mode);
out_unlock:
	cgroup_kn_unlock(of->kn);