cgroup: track migration context in cgroup_mgctx

cgroup migration is performed in four steps - css_set preloading,
addition of target tasks, actual migration, and clean up.  A list
named preloaded_csets is used to track the preloading.  This is a bit
too restricted and the code is already depending on the subtlety that
all source css_sets appear before destination ones.

Let's create struct cgroup_mgctx which keeps track of everything
during migration.  Currently, it has separate preload lists for source
and destination csets and also embeds cgroup_taskset which is used
during the actual migration.  This moves struct cgroup_taskset
definition to cgroup-internal.h.

This patch doesn't cause any functional changes.

Signed-off-by: Tejun Heo <tj@kernel.org>
Acked-by: Zefan Li <lizefan@huawei.com>
This commit is contained in:
Tejun Heo 2017-01-15 19:03:41 -05:00
parent d8ebf5191d
commit e595cd7069
3 changed files with 122 additions and 98 deletions

View File

@ -26,6 +26,61 @@ struct cgrp_cset_link {
struct list_head cgrp_link; struct list_head cgrp_link;
}; };
/* used to track tasks and csets during migration */
struct cgroup_taskset {
/* the src and dst cset list running through cset->mg_node */
struct list_head src_csets;
struct list_head dst_csets;
/* the subsys currently being processed */
int ssid;
/*
* Fields for cgroup_taskset_*() iteration.
*
* Before migration is committed, the target migration tasks are on
* ->mg_tasks of the csets on ->src_csets. After, on ->mg_tasks of
* the csets on ->dst_csets. ->csets point to either ->src_csets
* or ->dst_csets depending on whether migration is committed.
*
* ->cur_csets and ->cur_task point to the current task position
* during iteration.
*/
struct list_head *csets;
struct css_set *cur_cset;
struct task_struct *cur_task;
};
/* migration context also tracks preloading */
struct cgroup_mgctx {
/*
* Preloaded source and destination csets. Used to guarantee
* atomic success or failure on actual migration.
*/
struct list_head preloaded_src_csets;
struct list_head preloaded_dst_csets;
/* tasks and csets to migrate */
struct cgroup_taskset tset;
};
#define CGROUP_TASKSET_INIT(tset) \
{ \
.src_csets = LIST_HEAD_INIT(tset.src_csets), \
.dst_csets = LIST_HEAD_INIT(tset.dst_csets), \
.csets = &tset.src_csets, \
}
#define CGROUP_MGCTX_INIT(name) \
{ \
LIST_HEAD_INIT(name.preloaded_src_csets), \
LIST_HEAD_INIT(name.preloaded_dst_csets), \
CGROUP_TASKSET_INIT(name.tset), \
}
#define DEFINE_CGROUP_MGCTX(name) \
struct cgroup_mgctx name = CGROUP_MGCTX_INIT(name)
struct cgroup_sb_opts { struct cgroup_sb_opts {
u16 subsys_mask; u16 subsys_mask;
unsigned int flags; unsigned int flags;
@ -112,13 +167,12 @@ struct dentry *cgroup_do_mount(struct file_system_type *fs_type, int flags,
struct cgroup_namespace *ns); struct cgroup_namespace *ns);
bool cgroup_may_migrate_to(struct cgroup *dst_cgrp); bool cgroup_may_migrate_to(struct cgroup *dst_cgrp);
void cgroup_migrate_finish(struct list_head *preloaded_csets); void cgroup_migrate_finish(struct cgroup_mgctx *mgctx);
void cgroup_migrate_add_src(struct css_set *src_cset, void cgroup_migrate_add_src(struct css_set *src_cset, struct cgroup *dst_cgrp,
struct cgroup *dst_cgrp, struct cgroup_mgctx *mgctx);
struct list_head *preloaded_csets); int cgroup_migrate_prepare_dst(struct cgroup_mgctx *mgctx);
int cgroup_migrate_prepare_dst(struct list_head *preloaded_csets);
int cgroup_migrate(struct task_struct *leader, bool threadgroup, int cgroup_migrate(struct task_struct *leader, bool threadgroup,
struct cgroup_root *root); struct cgroup_mgctx *mgctx, struct cgroup_root *root);
int cgroup_attach_task(struct cgroup *dst_cgrp, struct task_struct *leader, int cgroup_attach_task(struct cgroup *dst_cgrp, struct task_struct *leader,
bool threadgroup); bool threadgroup);

View File

@ -87,7 +87,7 @@ EXPORT_SYMBOL_GPL(cgroup_attach_task_all);
*/ */
int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from) int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from)
{ {
LIST_HEAD(preloaded_csets); DEFINE_CGROUP_MGCTX(mgctx);
struct cgrp_cset_link *link; struct cgrp_cset_link *link;
struct css_task_iter it; struct css_task_iter it;
struct task_struct *task; struct task_struct *task;
@ -106,10 +106,10 @@ int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from)
/* all tasks in @from are being moved, all csets are source */ /* all tasks in @from are being moved, all csets are source */
spin_lock_irq(&css_set_lock); spin_lock_irq(&css_set_lock);
list_for_each_entry(link, &from->cset_links, cset_link) list_for_each_entry(link, &from->cset_links, cset_link)
cgroup_migrate_add_src(link->cset, to, &preloaded_csets); cgroup_migrate_add_src(link->cset, to, &mgctx);
spin_unlock_irq(&css_set_lock); spin_unlock_irq(&css_set_lock);
ret = cgroup_migrate_prepare_dst(&preloaded_csets); ret = cgroup_migrate_prepare_dst(&mgctx);
if (ret) if (ret)
goto out_err; goto out_err;
@ -125,14 +125,14 @@ int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from)
css_task_iter_end(&it); css_task_iter_end(&it);
if (task) { if (task) {
ret = cgroup_migrate(task, false, to->root); ret = cgroup_migrate(task, false, &mgctx, to->root);
if (!ret) if (!ret)
trace_cgroup_transfer_tasks(to, task, false); trace_cgroup_transfer_tasks(to, task, false);
put_task_struct(task); put_task_struct(task);
} }
} while (task && !ret); } while (task && !ret);
out_err: out_err:
cgroup_migrate_finish(&preloaded_csets); cgroup_migrate_finish(&mgctx);
percpu_up_write(&cgroup_threadgroup_rwsem); percpu_up_write(&cgroup_threadgroup_rwsem);
mutex_unlock(&cgroup_mutex); mutex_unlock(&cgroup_mutex);
return ret; return ret;

View File

@ -1916,49 +1916,18 @@ int task_cgroup_path(struct task_struct *task, char *buf, size_t buflen)
} }
EXPORT_SYMBOL_GPL(task_cgroup_path); EXPORT_SYMBOL_GPL(task_cgroup_path);
/* used to track tasks and other necessary states during migration */
struct cgroup_taskset {
/* the src and dst cset list running through cset->mg_node */
struct list_head src_csets;
struct list_head dst_csets;
/* the subsys currently being processed */
int ssid;
/*
* Fields for cgroup_taskset_*() iteration.
*
* Before migration is committed, the target migration tasks are on
* ->mg_tasks of the csets on ->src_csets. After, on ->mg_tasks of
* the csets on ->dst_csets. ->csets point to either ->src_csets
* or ->dst_csets depending on whether migration is committed.
*
* ->cur_csets and ->cur_task point to the current task position
* during iteration.
*/
struct list_head *csets;
struct css_set *cur_cset;
struct task_struct *cur_task;
};
#define CGROUP_TASKSET_INIT(tset) (struct cgroup_taskset){ \
.src_csets = LIST_HEAD_INIT(tset.src_csets), \
.dst_csets = LIST_HEAD_INIT(tset.dst_csets), \
.csets = &tset.src_csets, \
}
/** /**
* cgroup_taskset_add - try to add a migration target task to a taskset * cgroup_migrate_add_task - add a migration target task to a migration context
* @task: target task * @task: target task
* @tset: target taskset * @mgctx: target migration context
* *
* Add @task, which is a migration target, to @tset. This function becomes * Add @task, which is a migration target, to @mgctx->tset. This function
* noop if @task doesn't need to be migrated. @task's css_set should have * becomes noop if @task doesn't need to be migrated. @task's css_set
* been added as a migration source and @task->cg_list will be moved from * should have been added as a migration source and @task->cg_list will be
* the css_set's tasks list to mg_tasks one. * moved from the css_set's tasks list to mg_tasks one.
*/ */
static void cgroup_taskset_add(struct task_struct *task, static void cgroup_migrate_add_task(struct task_struct *task,
struct cgroup_taskset *tset) struct cgroup_mgctx *mgctx)
{ {
struct css_set *cset; struct css_set *cset;
@ -1978,10 +1947,11 @@ static void cgroup_taskset_add(struct task_struct *task,
list_move_tail(&task->cg_list, &cset->mg_tasks); list_move_tail(&task->cg_list, &cset->mg_tasks);
if (list_empty(&cset->mg_node)) if (list_empty(&cset->mg_node))
list_add_tail(&cset->mg_node, &tset->src_csets); list_add_tail(&cset->mg_node,
&mgctx->tset.src_csets);
if (list_empty(&cset->mg_dst_cset->mg_node)) if (list_empty(&cset->mg_dst_cset->mg_node))
list_add_tail(&cset->mg_dst_cset->mg_node, list_add_tail(&cset->mg_dst_cset->mg_node,
&tset->dst_csets); &mgctx->tset.dst_csets);
} }
/** /**
@ -2048,17 +2018,18 @@ struct task_struct *cgroup_taskset_next(struct cgroup_taskset *tset,
/** /**
* cgroup_taskset_migrate - migrate a taskset * cgroup_taskset_migrate - migrate a taskset
* @tset: taget taskset * @mgctx: migration context
* @root: cgroup root the migration is taking place on * @root: cgroup root the migration is taking place on
* *
* Migrate tasks in @tset as setup by migration preparation functions. * Migrate tasks in @mgctx as setup by migration preparation functions.
* This function fails iff one of the ->can_attach callbacks fails and * This function fails iff one of the ->can_attach callbacks fails and
* guarantees that either all or none of the tasks in @tset are migrated. * guarantees that either all or none of the tasks in @mgctx are migrated.
* @tset is consumed regardless of success. * @mgctx is consumed regardless of success.
*/ */
static int cgroup_taskset_migrate(struct cgroup_taskset *tset, static int cgroup_migrate_execute(struct cgroup_mgctx *mgctx,
struct cgroup_root *root) struct cgroup_root *root)
{ {
struct cgroup_taskset *tset = &mgctx->tset;
struct cgroup_subsys *ss; struct cgroup_subsys *ss;
struct task_struct *task, *tmp_task; struct task_struct *task, *tmp_task;
struct css_set *cset, *tmp_cset; struct css_set *cset, *tmp_cset;
@ -2151,25 +2122,31 @@ bool cgroup_may_migrate_to(struct cgroup *dst_cgrp)
/** /**
* cgroup_migrate_finish - cleanup after attach * cgroup_migrate_finish - cleanup after attach
* @preloaded_csets: list of preloaded css_sets * @mgctx: migration context
* *
* Undo cgroup_migrate_add_src() and cgroup_migrate_prepare_dst(). See * Undo cgroup_migrate_add_src() and cgroup_migrate_prepare_dst(). See
* those functions for details. * those functions for details.
*/ */
void cgroup_migrate_finish(struct list_head *preloaded_csets) void cgroup_migrate_finish(struct cgroup_mgctx *mgctx)
{ {
LIST_HEAD(preloaded);
struct css_set *cset, *tmp_cset; struct css_set *cset, *tmp_cset;
lockdep_assert_held(&cgroup_mutex); lockdep_assert_held(&cgroup_mutex);
spin_lock_irq(&css_set_lock); spin_lock_irq(&css_set_lock);
list_for_each_entry_safe(cset, tmp_cset, preloaded_csets, mg_preload_node) {
list_splice_tail_init(&mgctx->preloaded_src_csets, &preloaded);
list_splice_tail_init(&mgctx->preloaded_dst_csets, &preloaded);
list_for_each_entry_safe(cset, tmp_cset, &preloaded, mg_preload_node) {
cset->mg_src_cgrp = NULL; cset->mg_src_cgrp = NULL;
cset->mg_dst_cgrp = NULL; cset->mg_dst_cgrp = NULL;
cset->mg_dst_cset = NULL; cset->mg_dst_cset = NULL;
list_del_init(&cset->mg_preload_node); list_del_init(&cset->mg_preload_node);
put_css_set_locked(cset); put_css_set_locked(cset);
} }
spin_unlock_irq(&css_set_lock); spin_unlock_irq(&css_set_lock);
} }
@ -2177,10 +2154,10 @@ void cgroup_migrate_finish(struct list_head *preloaded_csets)
* cgroup_migrate_add_src - add a migration source css_set * cgroup_migrate_add_src - add a migration source css_set
* @src_cset: the source css_set to add * @src_cset: the source css_set to add
* @dst_cgrp: the destination cgroup * @dst_cgrp: the destination cgroup
* @preloaded_csets: list of preloaded css_sets * @mgctx: migration context
* *
* Tasks belonging to @src_cset are about to be migrated to @dst_cgrp. Pin * Tasks belonging to @src_cset are about to be migrated to @dst_cgrp. Pin
* @src_cset and add it to @preloaded_csets, which should later be cleaned * @src_cset and add it to @mgctx->src_csets, which should later be cleaned
* up by cgroup_migrate_finish(). * up by cgroup_migrate_finish().
* *
* This function may be called without holding cgroup_threadgroup_rwsem * This function may be called without holding cgroup_threadgroup_rwsem
@ -2191,7 +2168,7 @@ void cgroup_migrate_finish(struct list_head *preloaded_csets)
*/ */
void cgroup_migrate_add_src(struct css_set *src_cset, void cgroup_migrate_add_src(struct css_set *src_cset,
struct cgroup *dst_cgrp, struct cgroup *dst_cgrp,
struct list_head *preloaded_csets) struct cgroup_mgctx *mgctx)
{ {
struct cgroup *src_cgrp; struct cgroup *src_cgrp;
@ -2219,32 +2196,32 @@ void cgroup_migrate_add_src(struct css_set *src_cset,
src_cset->mg_src_cgrp = src_cgrp; src_cset->mg_src_cgrp = src_cgrp;
src_cset->mg_dst_cgrp = dst_cgrp; src_cset->mg_dst_cgrp = dst_cgrp;
get_css_set(src_cset); get_css_set(src_cset);
list_add(&src_cset->mg_preload_node, preloaded_csets); list_add_tail(&src_cset->mg_preload_node, &mgctx->preloaded_src_csets);
} }
/** /**
* cgroup_migrate_prepare_dst - prepare destination css_sets for migration * cgroup_migrate_prepare_dst - prepare destination css_sets for migration
* @preloaded_csets: list of preloaded source css_sets * @mgctx: migration context
* *
* Tasks are about to be moved and all the source css_sets have been * Tasks are about to be moved and all the source css_sets have been
* preloaded to @preloaded_csets. This function looks up and pins all * preloaded to @mgctx->preloaded_src_csets. This function looks up and
* destination css_sets, links each to its source, and append them to * pins all destination css_sets, links each to its source, and append them
* @preloaded_csets. * to @mgctx->preloaded_dst_csets.
* *
* This function must be called after cgroup_migrate_add_src() has been * This function must be called after cgroup_migrate_add_src() has been
* called on each migration source css_set. After migration is performed * called on each migration source css_set. After migration is performed
* using cgroup_migrate(), cgroup_migrate_finish() must be called on * using cgroup_migrate(), cgroup_migrate_finish() must be called on
* @preloaded_csets. * @mgctx.
*/ */
int cgroup_migrate_prepare_dst(struct list_head *preloaded_csets) int cgroup_migrate_prepare_dst(struct cgroup_mgctx *mgctx)
{ {
LIST_HEAD(csets);
struct css_set *src_cset, *tmp_cset; struct css_set *src_cset, *tmp_cset;
lockdep_assert_held(&cgroup_mutex); lockdep_assert_held(&cgroup_mutex);
/* look up the dst cset for each src cset and link it to src */ /* look up the dst cset for each src cset and link it to src */
list_for_each_entry_safe(src_cset, tmp_cset, preloaded_csets, mg_preload_node) { list_for_each_entry_safe(src_cset, tmp_cset, &mgctx->preloaded_src_csets,
mg_preload_node) {
struct css_set *dst_cset; struct css_set *dst_cset;
dst_cset = find_css_set(src_cset, src_cset->mg_dst_cgrp); dst_cset = find_css_set(src_cset, src_cset->mg_dst_cgrp);
@ -2270,15 +2247,15 @@ int cgroup_migrate_prepare_dst(struct list_head *preloaded_csets)
src_cset->mg_dst_cset = dst_cset; src_cset->mg_dst_cset = dst_cset;
if (list_empty(&dst_cset->mg_preload_node)) if (list_empty(&dst_cset->mg_preload_node))
list_add(&dst_cset->mg_preload_node, &csets); list_add_tail(&dst_cset->mg_preload_node,
&mgctx->preloaded_dst_csets);
else else
put_css_set(dst_cset); put_css_set(dst_cset);
} }
list_splice_tail(&csets, preloaded_csets);
return 0; return 0;
err: err:
cgroup_migrate_finish(&csets); cgroup_migrate_finish(mgctx);
return -ENOMEM; return -ENOMEM;
} }
@ -2287,6 +2264,7 @@ int cgroup_migrate_prepare_dst(struct list_head *preloaded_csets)
* @leader: the leader of the process or the task to migrate * @leader: the leader of the process or the task to migrate
* @threadgroup: whether @leader points to the whole process or a single task * @threadgroup: whether @leader points to the whole process or a single task
* @root: cgroup root migration is taking place on * @root: cgroup root migration is taking place on
* @mgctx: migration context
* *
* Migrate a process or task denoted by @leader. If migrating a process, * Migrate a process or task denoted by @leader. If migrating a process,
* the caller must be holding cgroup_threadgroup_rwsem. The caller is also * the caller must be holding cgroup_threadgroup_rwsem. The caller is also
@ -2301,9 +2279,8 @@ int cgroup_migrate_prepare_dst(struct list_head *preloaded_csets)
* actually starting migrating. * actually starting migrating.
*/ */
int cgroup_migrate(struct task_struct *leader, bool threadgroup, int cgroup_migrate(struct task_struct *leader, bool threadgroup,
struct cgroup_root *root) struct cgroup_mgctx *mgctx, struct cgroup_root *root)
{ {
struct cgroup_taskset tset = CGROUP_TASKSET_INIT(tset);
struct task_struct *task; struct task_struct *task;
/* /*
@ -2315,14 +2292,14 @@ int cgroup_migrate(struct task_struct *leader, bool threadgroup,
rcu_read_lock(); rcu_read_lock();
task = leader; task = leader;
do { do {
cgroup_taskset_add(task, &tset); cgroup_migrate_add_task(task, mgctx);
if (!threadgroup) if (!threadgroup)
break; break;
} while_each_thread(leader, task); } while_each_thread(leader, task);
rcu_read_unlock(); rcu_read_unlock();
spin_unlock_irq(&css_set_lock); spin_unlock_irq(&css_set_lock);
return cgroup_taskset_migrate(&tset, root); return cgroup_migrate_execute(mgctx, root);
} }
/** /**
@ -2336,7 +2313,7 @@ int cgroup_migrate(struct task_struct *leader, bool threadgroup,
int cgroup_attach_task(struct cgroup *dst_cgrp, struct task_struct *leader, int cgroup_attach_task(struct cgroup *dst_cgrp, struct task_struct *leader,
bool threadgroup) bool threadgroup)
{ {
LIST_HEAD(preloaded_csets); DEFINE_CGROUP_MGCTX(mgctx);
struct task_struct *task; struct task_struct *task;
int ret; int ret;
@ -2348,8 +2325,7 @@ int cgroup_attach_task(struct cgroup *dst_cgrp, struct task_struct *leader,
rcu_read_lock(); rcu_read_lock();
task = leader; task = leader;
do { do {
cgroup_migrate_add_src(task_css_set(task), dst_cgrp, cgroup_migrate_add_src(task_css_set(task), dst_cgrp, &mgctx);
&preloaded_csets);
if (!threadgroup) if (!threadgroup)
break; break;
} while_each_thread(leader, task); } while_each_thread(leader, task);
@ -2357,11 +2333,11 @@ int cgroup_attach_task(struct cgroup *dst_cgrp, struct task_struct *leader,
spin_unlock_irq(&css_set_lock); spin_unlock_irq(&css_set_lock);
/* prepare dst csets and commit */ /* prepare dst csets and commit */
ret = cgroup_migrate_prepare_dst(&preloaded_csets); ret = cgroup_migrate_prepare_dst(&mgctx);
if (!ret) if (!ret)
ret = cgroup_migrate(leader, threadgroup, dst_cgrp->root); ret = cgroup_migrate(leader, threadgroup, &mgctx, dst_cgrp->root);
cgroup_migrate_finish(&preloaded_csets); cgroup_migrate_finish(&mgctx);
if (!ret) if (!ret)
trace_cgroup_attach_task(dst_cgrp, leader, threadgroup); trace_cgroup_attach_task(dst_cgrp, leader, threadgroup);
@ -2528,8 +2504,7 @@ static int cgroup_subtree_control_show(struct seq_file *seq, void *v)
*/ */
static int cgroup_update_dfl_csses(struct cgroup *cgrp) static int cgroup_update_dfl_csses(struct cgroup *cgrp)
{ {
LIST_HEAD(preloaded_csets); DEFINE_CGROUP_MGCTX(mgctx);
struct cgroup_taskset tset = CGROUP_TASKSET_INIT(tset);
struct cgroup_subsys_state *d_css; struct cgroup_subsys_state *d_css;
struct cgroup *dsct; struct cgroup *dsct;
struct css_set *src_cset; struct css_set *src_cset;
@ -2545,33 +2520,28 @@ static int cgroup_update_dfl_csses(struct cgroup *cgrp)
struct cgrp_cset_link *link; struct cgrp_cset_link *link;
list_for_each_entry(link, &dsct->cset_links, cset_link) list_for_each_entry(link, &dsct->cset_links, cset_link)
cgroup_migrate_add_src(link->cset, dsct, cgroup_migrate_add_src(link->cset, dsct, &mgctx);
&preloaded_csets);
} }
spin_unlock_irq(&css_set_lock); spin_unlock_irq(&css_set_lock);
/* NULL dst indicates self on default hierarchy */ /* NULL dst indicates self on default hierarchy */
ret = cgroup_migrate_prepare_dst(&preloaded_csets); ret = cgroup_migrate_prepare_dst(&mgctx);
if (ret) if (ret)
goto out_finish; goto out_finish;
spin_lock_irq(&css_set_lock); spin_lock_irq(&css_set_lock);
list_for_each_entry(src_cset, &preloaded_csets, mg_preload_node) { list_for_each_entry(src_cset, &mgctx.preloaded_src_csets, mg_preload_node) {
struct task_struct *task, *ntask; struct task_struct *task, *ntask;
/* src_csets precede dst_csets, break on the first dst_cset */
if (!src_cset->mg_src_cgrp)
break;
/* all tasks in src_csets need to be migrated */ /* all tasks in src_csets need to be migrated */
list_for_each_entry_safe(task, ntask, &src_cset->tasks, cg_list) list_for_each_entry_safe(task, ntask, &src_cset->tasks, cg_list)
cgroup_taskset_add(task, &tset); cgroup_migrate_add_task(task, &mgctx);
} }
spin_unlock_irq(&css_set_lock); spin_unlock_irq(&css_set_lock);
ret = cgroup_taskset_migrate(&tset, cgrp->root); ret = cgroup_migrate_execute(&mgctx, cgrp->root);
out_finish: out_finish:
cgroup_migrate_finish(&preloaded_csets); cgroup_migrate_finish(&mgctx);
percpu_up_write(&cgroup_threadgroup_rwsem); percpu_up_write(&cgroup_threadgroup_rwsem);
return ret; return ret;
} }