cgroup: Include dying leaders with live threads in PROCS iterations [Linux 4.19.66]

This Linux kernel change "cgroup: Include dying leaders with live threads in PROCS iterations" is included in the Linux 4.19.66 release. This change is authored by Tejun Heo <tj [at] kernel.org> on Fri May 31 10:38:58 2019 -0700. The commit for this change in Linux stable tree is 4340d17 (patch) which is from upstream commit c03cd77. The same Linux upstream change may have been applied to various maintained Linux releases and you can find all Linux releases containing changes from upstream c03cd77.

cgroup: Include dying leaders with live threads in PROCS iterations

commit c03cd7738a83b13739f00546166969342c8ff014 upstream.

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 <[email protected]>
Reported-and-tested-by: Topi Miettinen <[email protected]>
Cc: Oleg Nesterov <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>

There are 46 lines of Linux source code added/deleted in this change. Code changes to Linux kernel are as follows.

 include/linux/cgroup-defs.h |  1 +
 include/linux/cgroup.h      |  1 +
 kernel/cgroup/cgroup.c      | 44 +++++++++++++++++++++++++++++++++++++-------
 3 files changed, 39 insertions(+), 7 deletions(-)

diff --git a/include/linux/cgroup-defs.h b/include/linux/cgroup-defs.h
index a609015..a01ebb630 100644
--- a/include/linux/cgroup-defs.h
+++ b/include/linux/cgroup-defs.h
@@ -207,6 +207,7 @@ struct css_set {
     */
    struct list_head tasks;
    struct list_head mg_tasks;
+   struct list_head dying_tasks;

    /* all css_task_iters currently walking this cset */
    struct list_head task_iters;
diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h
index f85e65b..b4854b4 100644
--- a/include/linux/cgroup.h
+++ b/include/linux/cgroup.h
@@ -60,6 +60,7 @@ struct css_task_iter {
    struct list_head        *task_pos;
    struct list_head        *tasks_head;
    struct list_head        *mg_tasks_head;
+   struct list_head        *dying_tasks_head;

    struct css_set          *cur_cset;
    struct css_set          *cur_dcset;
diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c
index c093e18..89dd464 100644
--- a/kernel/cgroup/cgroup.c
+++ b/kernel/cgroup/cgroup.c
@@ -673,6 +673,7 @@ struct css_set init_css_set = {
    .dom_cset       = &init_css_set,
    .tasks          = LIST_HEAD_INIT(init_css_set.tasks),
    .mg_tasks       = LIST_HEAD_INIT(init_css_set.mg_tasks),
+   .dying_tasks        = LIST_HEAD_INIT(init_css_set.dying_tasks),
    .task_iters     = LIST_HEAD_INIT(init_css_set.task_iters),
    .threaded_csets     = LIST_HEAD_INIT(init_css_set.threaded_csets),
    .cgrp_links     = LIST_HEAD_INIT(init_css_set.cgrp_links),
@@ -1145,6 +1146,7 @@ static struct css_set *find_css_set(struct css_set *old_cset,
    cset->dom_cset = cset;
    INIT_LIST_HEAD(&cset->tasks);
    INIT_LIST_HEAD(&cset->mg_tasks);
+   INIT_LIST_HEAD(&cset->dying_tasks);
    INIT_LIST_HEAD(&cset->task_iters);
    INIT_LIST_HEAD(&cset->threaded_csets);
    INIT_HLIST_NODE(&cset->hlist);
@@ -4152,15 +4154,18 @@ static void css_task_iter_advance_css_set(struct css_task_iter *it)
            it->task_pos = NULL;
            return;
        }
-   } while (!css_set_populated(cset));
+   } while (!css_set_populated(cset) && !list_empty(&cset->dying_tasks));

    if (!list_empty(&cset->tasks))
        it->task_pos = cset->tasks.next;
-   else
+   else if (!list_empty(&cset->mg_tasks))
        it->task_pos = cset->mg_tasks.next;
+   else
+       it->task_pos = cset->dying_tasks.next;

    it->tasks_head = &cset->tasks;
    it->mg_tasks_head = &cset->mg_tasks;
+   it->dying_tasks_head = &cset->dying_tasks;

    /*
     * We don't keep css_sets locked across iteration steps and thus
@@ -4199,6 +4204,8 @@ static void css_task_iter_skip(struct css_task_iter *it,

 static void css_task_iter_advance(struct css_task_iter *it)
 {
+   struct task_struct *task;
+
    lockdep_assert_held(&css_set_lock);
 repeat:
    if (it->task_pos) {
@@ -4215,17 +4222,32 @@ static void css_task_iter_advance(struct css_task_iter *it)
        if (it->task_pos == it->tasks_head)
            it->task_pos = it->mg_tasks_head->next;
        if (it->task_pos == it->mg_tasks_head)
+           it->task_pos = it->dying_tasks_head->next;
+       if (it->task_pos == it->dying_tasks_head)
            css_task_iter_advance_css_set(it);
    } else {
        /* called from start, proceed to the first cset */
        css_task_iter_advance_css_set(it);
    }

-   /* if PROCS, skip over tasks which aren't group leaders */
-   if ((it->flags & CSS_TASK_ITER_PROCS) && it->task_pos &&
-       !thread_group_leader(list_entry(it->task_pos, struct task_struct,
-                       cg_list)))
-       goto repeat;
+   if (!it->task_pos)
+       return;
+
+   task = list_entry(it->task_pos, struct task_struct, cg_list);
+
+   if (it->flags & CSS_TASK_ITER_PROCS) {
+       /* if PROCS, skip over tasks which aren't group leaders */
+       if (!thread_group_leader(task))
+           goto repeat;
+
+       /* and dying leaders w/o live member threads */
+       if (!atomic_read(&task->signal->live))
+           goto repeat;
+   } else {
+       /* skip all dying ones */
+       if (task->flags & PF_EXITING)
+           goto repeat;
+   }
 }

 /**
@@ -5682,6 +5704,7 @@ void cgroup_exit(struct task_struct *tsk)
    if (!list_empty(&tsk->cg_list)) {
        spin_lock_irq(&css_set_lock);
        css_set_move_task(tsk, cset, NULL, false);
+       list_add_tail(&tsk->cg_list, &cset->dying_tasks);
        cset->nr_tasks--;
        spin_unlock_irq(&css_set_lock);
    } else {
@@ -5702,6 +5725,13 @@ void cgroup_release(struct task_struct *task)
    do_each_subsys_mask(ss, ssid, have_release_callback) {
        ss->release(task);
    } while_each_subsys_mask();
+
+   if (use_task_css_set_links) {
+       spin_lock_irq(&css_set_lock);
+       css_set_skip_task_iters(task_css_set(task), task);
+       list_del_init(&task->cg_list);
+       spin_unlock_irq(&css_set_lock);
+   }
 }

 void cgroup_free(struct task_struct *task)

Leave a Reply

Your email address will not be published. Required fields are marked *