summaryrefslogtreecommitdiffstats
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
commitc03cd7738a83b13739f00546166969342c8ff014 (patch)
tree6ac8b0e1001dbfc3c7eb8df089bc500aeeab9a24
parentb636fd38dc40113f853337a7d2a6885ad23b8811 (diff)
cgroup: Include dying leaders with live threads in PROCS iterations
CSS_TASK_ITER_PROCS currently iterates live group leaders; however, this means that a process with dying leader and live threads will be skipped. IOW, cgroup.procs might be empty while cgroup.threads isn't, which is confusing to say the least. Fix it by making cset track dying tasks and include dying leaders with live threads in PROCS iteration. Signed-off-by: Tejun Heo <tj@kernel.org> Reported-and-tested-by: Topi Miettinen <toiwoton@gmail.com> Cc: Oleg Nesterov <oleg@redhat.com>
-rw-r--r--include/linux/cgroup-defs.h1
-rw-r--r--include/linux/cgroup.h1
-rw-r--r--kernel/cgroup/cgroup.c44
3 files changed, 39 insertions, 7 deletions
diff --git a/include/linux/cgroup-defs.h b/include/linux/cgroup-defs.h
index 77258d276f93..1615b9c17e02 100644
--- a/include/linux/cgroup-defs.h
+++ b/include/linux/cgroup-defs.h
@@ -216,6 +216,7 @@ struct css_set {
216 */ 216 */
217 struct list_head tasks; 217 struct list_head tasks;
218 struct list_head mg_tasks; 218 struct list_head mg_tasks;
219 struct list_head dying_tasks;
219 220
220 /* all css_task_iters currently walking this cset */ 221 /* all css_task_iters currently walking this cset */
221 struct list_head task_iters; 222 struct list_head task_iters;
diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h
index 05ed2a209e74..0297f930a56e 100644
--- a/include/linux/cgroup.h
+++ b/include/linux/cgroup.h
@@ -60,6 +60,7 @@ struct css_task_iter {
60 struct list_head *task_pos; 60 struct list_head *task_pos;
61 struct list_head *tasks_head; 61 struct list_head *tasks_head;
62 struct list_head *mg_tasks_head; 62 struct list_head *mg_tasks_head;
63 struct list_head *dying_tasks_head;
63 64
64 struct css_set *cur_cset; 65 struct css_set *cur_cset;
65 struct css_set *cur_dcset; 66 struct css_set *cur_dcset;
diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c
index 035aee466bbf..a7df319c2e9a 100644
--- a/kernel/cgroup/cgroup.c
+++ b/kernel/cgroup/cgroup.c
@@ -739,6 +739,7 @@ struct css_set init_css_set = {
739 .dom_cset = &init_css_set, 739 .dom_cset = &init_css_set,
740 .tasks = LIST_HEAD_INIT(init_css_set.tasks), 740 .tasks = LIST_HEAD_INIT(init_css_set.tasks),
741 .mg_tasks = LIST_HEAD_INIT(init_css_set.mg_tasks), 741 .mg_tasks = LIST_HEAD_INIT(init_css_set.mg_tasks),
742 .dying_tasks = LIST_HEAD_INIT(init_css_set.dying_tasks),
742 .task_iters = LIST_HEAD_INIT(init_css_set.task_iters), 743 .task_iters = LIST_HEAD_INIT(init_css_set.task_iters),
743 .threaded_csets = LIST_HEAD_INIT(init_css_set.threaded_csets), 744 .threaded_csets = LIST_HEAD_INIT(init_css_set.threaded_csets),
744 .cgrp_links = LIST_HEAD_INIT(init_css_set.cgrp_links), 745 .cgrp_links = LIST_HEAD_INIT(init_css_set.cgrp_links),
@@ -1213,6 +1214,7 @@ static struct css_set *find_css_set(struct css_set *old_cset,
1213 cset->dom_cset = cset; 1214 cset->dom_cset = cset;
1214 INIT_LIST_HEAD(&cset->tasks); 1215 INIT_LIST_HEAD(&cset->tasks);
1215 INIT_LIST_HEAD(&cset->mg_tasks); 1216 INIT_LIST_HEAD(&cset->mg_tasks);
1217 INIT_LIST_HEAD(&cset->dying_tasks);
1216 INIT_LIST_HEAD(&cset->task_iters); 1218 INIT_LIST_HEAD(&cset->task_iters);
1217 INIT_LIST_HEAD(&cset->threaded_csets); 1219 INIT_LIST_HEAD(&cset->threaded_csets);
1218 INIT_HLIST_NODE(&cset->hlist); 1220 INIT_HLIST_NODE(&cset->hlist);
@@ -4399,15 +4401,18 @@ static void css_task_iter_advance_css_set(struct css_task_iter *it)
4399 it->task_pos = NULL; 4401 it->task_pos = NULL;
4400 return; 4402 return;
4401 } 4403 }
4402 } while (!css_set_populated(cset)); 4404 } while (!css_set_populated(cset) && !list_empty(&cset->dying_tasks));
4403 4405
4404 if (!list_empty(&cset->tasks)) 4406 if (!list_empty(&cset->tasks))
4405 it->task_pos = cset->tasks.next; 4407 it->task_pos = cset->tasks.next;
4406 else 4408 else if (!list_empty(&cset->mg_tasks))
4407 it->task_pos = cset->mg_tasks.next; 4409 it->task_pos = cset->mg_tasks.next;
4410 else
4411 it->task_pos = cset->dying_tasks.next;
4408 4412
4409 it->tasks_head = &cset->tasks; 4413 it->tasks_head = &cset->tasks;
4410 it->mg_tasks_head = &cset->mg_tasks; 4414 it->mg_tasks_head = &cset->mg_tasks;
4415 it->dying_tasks_head = &cset->dying_tasks;
4411 4416
4412 /* 4417 /*
4413 * We don't keep css_sets locked across iteration steps and thus 4418 * We don't keep css_sets locked across iteration steps and thus
@@ -4446,6 +4451,8 @@ static void css_task_iter_skip(struct css_task_iter *it,
4446 4451
4447static void css_task_iter_advance(struct css_task_iter *it) 4452static void css_task_iter_advance(struct css_task_iter *it)
4448{ 4453{
4454 struct task_struct *task;
4455
4449 lockdep_assert_held(&css_set_lock); 4456 lockdep_assert_held(&css_set_lock);
4450repeat: 4457repeat:
4451 if (it->task_pos) { 4458 if (it->task_pos) {
@@ -4462,17 +4469,32 @@ repeat:
4462 if (it->task_pos == it->tasks_head) 4469 if (it->task_pos == it->tasks_head)
4463 it->task_pos = it->mg_tasks_head->next; 4470 it->task_pos = it->mg_tasks_head->next;
4464 if (it->task_pos == it->mg_tasks_head) 4471 if (it->task_pos == it->mg_tasks_head)
4472 it->task_pos = it->dying_tasks_head->next;
4473 if (it->task_pos == it->dying_tasks_head)
4465 css_task_iter_advance_css_set(it); 4474 css_task_iter_advance_css_set(it);
4466 } else { 4475 } else {
4467 /* called from start, proceed to the first cset */ 4476 /* called from start, proceed to the first cset */
4468 css_task_iter_advance_css_set(it); 4477 css_task_iter_advance_css_set(it);
4469 } 4478 }
4470 4479
4471 /* if PROCS, skip over tasks which aren't group leaders */ 4480 if (!it->task_pos)
4472 if ((it->flags & CSS_TASK_ITER_PROCS) && it->task_pos && 4481 return;
4473 !thread_group_leader(list_entry(it->task_pos, struct task_struct, 4482
4474 cg_list))) 4483 task = list_entry(it->task_pos, struct task_struct, cg_list);
4475 goto repeat; 4484
4485 if (it->flags & CSS_TASK_ITER_PROCS) {
4486 /* if PROCS, skip over tasks which aren't group leaders */
4487 if (!thread_group_leader(task))
4488 goto repeat;
4489
4490 /* and dying leaders w/o live member threads */
4491 if (!atomic_read(&task->signal->live))
4492 goto repeat;
4493 } else {
4494 /* skip all dying ones */
4495 if (task->flags & PF_EXITING)
4496 goto repeat;
4497 }
4476} 4498}
4477 4499
4478/** 4500/**
@@ -6009,6 +6031,7 @@ void cgroup_exit(struct task_struct *tsk)
6009 if (!list_empty(&tsk->cg_list)) { 6031 if (!list_empty(&tsk->cg_list)) {
6010 spin_lock_irq(&css_set_lock); 6032 spin_lock_irq(&css_set_lock);
6011 css_set_move_task(tsk, cset, NULL, false); 6033 css_set_move_task(tsk, cset, NULL, false);
6034 list_add_tail(&tsk->cg_list, &cset->dying_tasks);
6012 cset->nr_tasks--; 6035 cset->nr_tasks--;
6013 6036
6014 WARN_ON_ONCE(cgroup_task_frozen(tsk)); 6037 WARN_ON_ONCE(cgroup_task_frozen(tsk));
@@ -6034,6 +6057,13 @@ void cgroup_release(struct task_struct *task)
6034 do_each_subsys_mask(ss, ssid, have_release_callback) { 6057 do_each_subsys_mask(ss, ssid, have_release_callback) {
6035 ss->release(task); 6058 ss->release(task);
6036 } while_each_subsys_mask(); 6059 } while_each_subsys_mask();
6060
6061 if (use_task_css_set_links) {
6062 spin_lock_irq(&css_set_lock);
6063 css_set_skip_task_iters(task_css_set(task), task);
6064 list_del_init(&task->cg_list);
6065 spin_unlock_irq(&css_set_lock);
6066 }
6037} 6067}
6038 6068
6039void cgroup_free(struct task_struct *task) 6069void cgroup_free(struct task_struct *task)