aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2019-05-31 13:38:58 -0400
committerTejun Heo <tj@kernel.org>2019-05-31 13:38:58 -0400
commitb636fd38dc40113f853337a7d2a6885ad23b8811 (patch)
tree4cce96a0cd2f756d1ef07a22afa0766508121b10
parent6b115bf58e6f013ca75e7115aabcbd56c20ff31d (diff)
cgroup: Implement css_task_iter_skip()
When a task is moved out of a cset, task iterators pointing to the task are advanced using the normal css_task_iter_advance() call. This is fine but we'll be tracking dying tasks on csets and thus moving tasks from cset->tasks to (to be added) cset->dying_tasks. When we remove a task from cset->tasks, if we advance the iterators, they may move over to the next cset before we had the chance to add the task back on the dying list, which can allow the task to escape iteration. This patch separates out skipping from advancing. Skipping only moves the affected iterators to the next pointer rather than fully advancing it and the following advancing will recognize that the cursor has already been moved forward and do the rest of advancing. This ensures that when a task moves from one list to another in its cset, as long as it moves in the right direction, it's always visible to iteration. This doesn't cause any visible behavior changes. Signed-off-by: Tejun Heo <tj@kernel.org> Cc: Oleg Nesterov <oleg@redhat.com>
-rw-r--r--include/linux/cgroup.h3
-rw-r--r--kernel/cgroup/cgroup.c60
2 files changed, 39 insertions, 24 deletions
diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h
index a7e4611e20c8..05ed2a209e74 100644
--- a/include/linux/cgroup.h
+++ b/include/linux/cgroup.h
@@ -43,6 +43,9 @@
43/* walk all threaded css_sets in the domain */ 43/* walk all threaded css_sets in the domain */
44#define CSS_TASK_ITER_THREADED (1U << 1) 44#define CSS_TASK_ITER_THREADED (1U << 1)
45 45
46/* internal flags */
47#define CSS_TASK_ITER_SKIPPED (1U << 16)
48
46/* a css_task_iter should be treated as an opaque object */ 49/* a css_task_iter should be treated as an opaque object */
47struct css_task_iter { 50struct css_task_iter {
48 struct cgroup_subsys *ss; 51 struct cgroup_subsys *ss;
diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c
index 217cec4e22c6..035aee466bbf 100644
--- a/kernel/cgroup/cgroup.c
+++ b/kernel/cgroup/cgroup.c
@@ -215,7 +215,8 @@ static struct cftype cgroup_base_files[];
215 215
216static int cgroup_apply_control(struct cgroup *cgrp); 216static int cgroup_apply_control(struct cgroup *cgrp);
217static void cgroup_finalize_control(struct cgroup *cgrp, int ret); 217static void cgroup_finalize_control(struct cgroup *cgrp, int ret);
218static void css_task_iter_advance(struct css_task_iter *it); 218static void css_task_iter_skip(struct css_task_iter *it,
219 struct task_struct *task);
219static int cgroup_destroy_locked(struct cgroup *cgrp); 220static int cgroup_destroy_locked(struct cgroup *cgrp);
220static struct cgroup_subsys_state *css_create(struct cgroup *cgrp, 221static struct cgroup_subsys_state *css_create(struct cgroup *cgrp,
221 struct cgroup_subsys *ss); 222 struct cgroup_subsys *ss);
@@ -843,6 +844,21 @@ static void css_set_update_populated(struct css_set *cset, bool populated)
843 cgroup_update_populated(link->cgrp, populated); 844 cgroup_update_populated(link->cgrp, populated);
844} 845}
845 846
847/*
848 * @task is leaving, advance task iterators which are pointing to it so
849 * that they can resume at the next position. Advancing an iterator might
850 * remove it from the list, use safe walk. See css_task_iter_skip() for
851 * details.
852 */
853static void css_set_skip_task_iters(struct css_set *cset,
854 struct task_struct *task)
855{
856 struct css_task_iter *it, *pos;
857
858 list_for_each_entry_safe(it, pos, &cset->task_iters, iters_node)
859 css_task_iter_skip(it, task);
860}
861
846/** 862/**
847 * css_set_move_task - move a task from one css_set to another 863 * css_set_move_task - move a task from one css_set to another
848 * @task: task being moved 864 * @task: task being moved
@@ -868,22 +884,9 @@ static void css_set_move_task(struct task_struct *task,
868 css_set_update_populated(to_cset, true); 884 css_set_update_populated(to_cset, true);
869 885
870 if (from_cset) { 886 if (from_cset) {
871 struct css_task_iter *it, *pos;
872
873 WARN_ON_ONCE(list_empty(&task->cg_list)); 887 WARN_ON_ONCE(list_empty(&task->cg_list));
874 888
875 /* 889 css_set_skip_task_iters(from_cset, task);
876 * @task is leaving, advance task iterators which are
877 * pointing to it so that they can resume at the next
878 * position. Advancing an iterator might remove it from
879 * the list, use safe walk. See css_task_iter_advance*()
880 * for details.
881 */
882 list_for_each_entry_safe(it, pos, &from_cset->task_iters,
883 iters_node)
884 if (it->task_pos == &task->cg_list)
885 css_task_iter_advance(it);
886
887 list_del_init(&task->cg_list); 890 list_del_init(&task->cg_list);
888 if (!css_set_populated(from_cset)) 891 if (!css_set_populated(from_cset))
889 css_set_update_populated(from_cset, false); 892 css_set_update_populated(from_cset, false);
@@ -4430,10 +4433,19 @@ static void css_task_iter_advance_css_set(struct css_task_iter *it)
4430 list_add(&it->iters_node, &cset->task_iters); 4433 list_add(&it->iters_node, &cset->task_iters);
4431} 4434}
4432 4435
4433static void css_task_iter_advance(struct css_task_iter *it) 4436static void css_task_iter_skip(struct css_task_iter *it,
4437 struct task_struct *task)
4434{ 4438{
4435 struct list_head *next; 4439 lockdep_assert_held(&css_set_lock);
4440
4441 if (it->task_pos == &task->cg_list) {
4442 it->task_pos = it->task_pos->next;
4443 it->flags |= CSS_TASK_ITER_SKIPPED;
4444 }
4445}
4436 4446
4447static void css_task_iter_advance(struct css_task_iter *it)
4448{
4437 lockdep_assert_held(&css_set_lock); 4449 lockdep_assert_held(&css_set_lock);
4438repeat: 4450repeat:
4439 if (it->task_pos) { 4451 if (it->task_pos) {
@@ -4442,15 +4454,15 @@ repeat:
4442 * consumed first and then ->mg_tasks. After ->mg_tasks, 4454 * consumed first and then ->mg_tasks. After ->mg_tasks,
4443 * we move onto the next cset. 4455 * we move onto the next cset.
4444 */ 4456 */
4445 next = it->task_pos->next; 4457 if (it->flags & CSS_TASK_ITER_SKIPPED)
4446 4458 it->flags &= ~CSS_TASK_ITER_SKIPPED;
4447 if (next == it->tasks_head) 4459 else
4448 next = it->mg_tasks_head->next; 4460 it->task_pos = it->task_pos->next;
4449 4461
4450 if (next == it->mg_tasks_head) 4462 if (it->task_pos == it->tasks_head)
4463 it->task_pos = it->mg_tasks_head->next;
4464 if (it->task_pos == it->mg_tasks_head)
4451 css_task_iter_advance_css_set(it); 4465 css_task_iter_advance_css_set(it);
4452 else
4453 it->task_pos = next;
4454 } else { 4466 } else {
4455 /* called from start, proceed to the first cset */ 4467 /* called from start, proceed to the first cset */
4456 css_task_iter_advance_css_set(it); 4468 css_task_iter_advance_css_set(it);