cgroup: separate out taskset operations from cgroup_migrate()
Currently, cgroup_migreate() implements large part of the migration logic inline including building the target taskset and actually migrating them. This patch separates out the following taskset operations. CGROUP_TASKSET_INIT() : taskset initializer cgroup_taskset_add() : add a task to a taskset cgroup_taskset_migrate() : migrate a taskset to the destination cgroup This will be used to implement atomic multi-process migration in cgroup_update_dfl_csses(). This is pure reorganization which doesn't introduce any functional changes. Signed-off-by: Tejun Heo <tj@kernel.org> Acked-by: Zefan Li <lizefan@huawei.com>
This commit is contained in:
Родитель
9af2ec45c2
Коммит
adaae5dcf8
211
kernel/cgroup.c
211
kernel/cgroup.c
|
@ -2144,6 +2144,49 @@ struct cgroup_taskset {
|
||||||
struct task_struct *cur_task;
|
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
|
||||||
|
* @task: target task
|
||||||
|
* @tset: target taskset
|
||||||
|
*
|
||||||
|
* Add @task, which is a migration target, to @tset. This function becomes
|
||||||
|
* noop if @task doesn't need to be migrated. @task's css_set should have
|
||||||
|
* been added as a migration source and @task->cg_list will be moved from
|
||||||
|
* the css_set's tasks list to mg_tasks one.
|
||||||
|
*/
|
||||||
|
static void cgroup_taskset_add(struct task_struct *task,
|
||||||
|
struct cgroup_taskset *tset)
|
||||||
|
{
|
||||||
|
struct css_set *cset;
|
||||||
|
|
||||||
|
lockdep_assert_held(&css_set_rwsem);
|
||||||
|
|
||||||
|
/* @task either already exited or can't exit until the end */
|
||||||
|
if (task->flags & PF_EXITING)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* leave @task alone if post_fork() hasn't linked it yet */
|
||||||
|
if (list_empty(&task->cg_list))
|
||||||
|
return;
|
||||||
|
|
||||||
|
cset = task_css_set(task);
|
||||||
|
if (!cset->mg_src_cgrp)
|
||||||
|
return;
|
||||||
|
|
||||||
|
list_move_tail(&task->cg_list, &cset->mg_tasks);
|
||||||
|
if (list_empty(&cset->mg_node))
|
||||||
|
list_add_tail(&cset->mg_node, &tset->src_csets);
|
||||||
|
if (list_empty(&cset->mg_dst_cset->mg_node))
|
||||||
|
list_move_tail(&cset->mg_dst_cset->mg_node,
|
||||||
|
&tset->dst_csets);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* cgroup_taskset_first - reset taskset and return the first task
|
* cgroup_taskset_first - reset taskset and return the first task
|
||||||
* @tset: taskset of interest
|
* @tset: taskset of interest
|
||||||
|
@ -2227,6 +2270,84 @@ static void cgroup_task_migrate(struct cgroup *old_cgrp,
|
||||||
put_css_set_locked(old_cset);
|
put_css_set_locked(old_cset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cgroup_taskset_migrate - migrate a taskset to a cgroup
|
||||||
|
* @tset: taget taskset
|
||||||
|
* @dst_cgrp: destination cgroup
|
||||||
|
*
|
||||||
|
* Migrate tasks in @tset to @dst_cgrp. 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. @tset is consumed regardless of
|
||||||
|
* success.
|
||||||
|
*/
|
||||||
|
static int cgroup_taskset_migrate(struct cgroup_taskset *tset,
|
||||||
|
struct cgroup *dst_cgrp)
|
||||||
|
{
|
||||||
|
struct cgroup_subsys_state *css, *failed_css = NULL;
|
||||||
|
struct task_struct *task, *tmp_task;
|
||||||
|
struct css_set *cset, *tmp_cset;
|
||||||
|
int i, ret;
|
||||||
|
|
||||||
|
/* methods shouldn't be called if no task is actually migrating */
|
||||||
|
if (list_empty(&tset->src_csets))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* check that we can legitimately attach to the cgroup */
|
||||||
|
for_each_e_css(css, i, dst_cgrp) {
|
||||||
|
if (css->ss->can_attach) {
|
||||||
|
ret = css->ss->can_attach(css, tset);
|
||||||
|
if (ret) {
|
||||||
|
failed_css = css;
|
||||||
|
goto out_cancel_attach;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now that we're guaranteed success, proceed to move all tasks to
|
||||||
|
* the new cgroup. There are no failure cases after here, so this
|
||||||
|
* is the commit point.
|
||||||
|
*/
|
||||||
|
down_write(&css_set_rwsem);
|
||||||
|
list_for_each_entry(cset, &tset->src_csets, mg_node) {
|
||||||
|
list_for_each_entry_safe(task, tmp_task, &cset->mg_tasks, cg_list)
|
||||||
|
cgroup_task_migrate(cset->mg_src_cgrp, task,
|
||||||
|
cset->mg_dst_cset);
|
||||||
|
}
|
||||||
|
up_write(&css_set_rwsem);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Migration is committed, all target tasks are now on dst_csets.
|
||||||
|
* Nothing is sensitive to fork() after this point. Notify
|
||||||
|
* controllers that migration is complete.
|
||||||
|
*/
|
||||||
|
tset->csets = &tset->dst_csets;
|
||||||
|
|
||||||
|
for_each_e_css(css, i, dst_cgrp)
|
||||||
|
if (css->ss->attach)
|
||||||
|
css->ss->attach(css, tset);
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
goto out_release_tset;
|
||||||
|
|
||||||
|
out_cancel_attach:
|
||||||
|
for_each_e_css(css, i, dst_cgrp) {
|
||||||
|
if (css == failed_css)
|
||||||
|
break;
|
||||||
|
if (css->ss->cancel_attach)
|
||||||
|
css->ss->cancel_attach(css, tset);
|
||||||
|
}
|
||||||
|
out_release_tset:
|
||||||
|
down_write(&css_set_rwsem);
|
||||||
|
list_splice_init(&tset->dst_csets, &tset->src_csets);
|
||||||
|
list_for_each_entry_safe(cset, tmp_cset, &tset->src_csets, mg_node) {
|
||||||
|
list_splice_tail_init(&cset->mg_tasks, &cset->tasks);
|
||||||
|
list_del_init(&cset->mg_node);
|
||||||
|
}
|
||||||
|
up_write(&css_set_rwsem);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* cgroup_migrate_finish - cleanup after attach
|
* cgroup_migrate_finish - cleanup after attach
|
||||||
* @preloaded_csets: list of preloaded css_sets
|
* @preloaded_csets: list of preloaded css_sets
|
||||||
|
@ -2381,15 +2502,8 @@ err:
|
||||||
static int cgroup_migrate(struct task_struct *leader, bool threadgroup,
|
static int cgroup_migrate(struct task_struct *leader, bool threadgroup,
|
||||||
struct cgroup *cgrp)
|
struct cgroup *cgrp)
|
||||||
{
|
{
|
||||||
struct cgroup_taskset tset = {
|
struct cgroup_taskset tset = CGROUP_TASKSET_INIT(tset);
|
||||||
.src_csets = LIST_HEAD_INIT(tset.src_csets),
|
struct task_struct *task;
|
||||||
.dst_csets = LIST_HEAD_INIT(tset.dst_csets),
|
|
||||||
.csets = &tset.src_csets,
|
|
||||||
};
|
|
||||||
struct cgroup_subsys_state *css, *failed_css = NULL;
|
|
||||||
struct css_set *cset, *tmp_cset;
|
|
||||||
struct task_struct *task, *tmp_task;
|
|
||||||
int i, ret;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Prevent freeing of tasks while we take a snapshot. Tasks that are
|
* Prevent freeing of tasks while we take a snapshot. Tasks that are
|
||||||
|
@ -2400,89 +2514,14 @@ static int cgroup_migrate(struct task_struct *leader, bool threadgroup,
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
task = leader;
|
task = leader;
|
||||||
do {
|
do {
|
||||||
/* @task either already exited or can't exit until the end */
|
cgroup_taskset_add(task, &tset);
|
||||||
if (task->flags & PF_EXITING)
|
|
||||||
goto next;
|
|
||||||
|
|
||||||
/* leave @task alone if post_fork() hasn't linked it yet */
|
|
||||||
if (list_empty(&task->cg_list))
|
|
||||||
goto next;
|
|
||||||
|
|
||||||
cset = task_css_set(task);
|
|
||||||
if (!cset->mg_src_cgrp)
|
|
||||||
goto next;
|
|
||||||
|
|
||||||
list_move_tail(&task->cg_list, &cset->mg_tasks);
|
|
||||||
if (list_empty(&cset->mg_node))
|
|
||||||
list_add_tail(&cset->mg_node, &tset.src_csets);
|
|
||||||
if (list_empty(&cset->mg_dst_cset->mg_node))
|
|
||||||
list_move_tail(&cset->mg_dst_cset->mg_node,
|
|
||||||
&tset.dst_csets);
|
|
||||||
next:
|
|
||||||
if (!threadgroup)
|
if (!threadgroup)
|
||||||
break;
|
break;
|
||||||
} while_each_thread(leader, task);
|
} while_each_thread(leader, task);
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
up_write(&css_set_rwsem);
|
up_write(&css_set_rwsem);
|
||||||
|
|
||||||
/* methods shouldn't be called if no task is actually migrating */
|
return cgroup_taskset_migrate(&tset, cgrp);
|
||||||
if (list_empty(&tset.src_csets))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* check that we can legitimately attach to the cgroup */
|
|
||||||
for_each_e_css(css, i, cgrp) {
|
|
||||||
if (css->ss->can_attach) {
|
|
||||||
ret = css->ss->can_attach(css, &tset);
|
|
||||||
if (ret) {
|
|
||||||
failed_css = css;
|
|
||||||
goto out_cancel_attach;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Now that we're guaranteed success, proceed to move all tasks to
|
|
||||||
* the new cgroup. There are no failure cases after here, so this
|
|
||||||
* is the commit point.
|
|
||||||
*/
|
|
||||||
down_write(&css_set_rwsem);
|
|
||||||
list_for_each_entry(cset, &tset.src_csets, mg_node) {
|
|
||||||
list_for_each_entry_safe(task, tmp_task, &cset->mg_tasks, cg_list)
|
|
||||||
cgroup_task_migrate(cset->mg_src_cgrp, task,
|
|
||||||
cset->mg_dst_cset);
|
|
||||||
}
|
|
||||||
up_write(&css_set_rwsem);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Migration is committed, all target tasks are now on dst_csets.
|
|
||||||
* Nothing is sensitive to fork() after this point. Notify
|
|
||||||
* controllers that migration is complete.
|
|
||||||
*/
|
|
||||||
tset.csets = &tset.dst_csets;
|
|
||||||
|
|
||||||
for_each_e_css(css, i, cgrp)
|
|
||||||
if (css->ss->attach)
|
|
||||||
css->ss->attach(css, &tset);
|
|
||||||
|
|
||||||
ret = 0;
|
|
||||||
goto out_release_tset;
|
|
||||||
|
|
||||||
out_cancel_attach:
|
|
||||||
for_each_e_css(css, i, cgrp) {
|
|
||||||
if (css == failed_css)
|
|
||||||
break;
|
|
||||||
if (css->ss->cancel_attach)
|
|
||||||
css->ss->cancel_attach(css, &tset);
|
|
||||||
}
|
|
||||||
out_release_tset:
|
|
||||||
down_write(&css_set_rwsem);
|
|
||||||
list_splice_init(&tset.dst_csets, &tset.src_csets);
|
|
||||||
list_for_each_entry_safe(cset, tmp_cset, &tset.src_csets, mg_node) {
|
|
||||||
list_splice_tail_init(&cset->mg_tasks, &cset->tasks);
|
|
||||||
list_del_init(&cset->mg_node);
|
|
||||||
}
|
|
||||||
up_write(&css_set_rwsem);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Загрузка…
Ссылка в новой задаче