]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - kernel/cgroup/cgroup.c
cgroup: cgroup v2 freezer
[linux.git] / kernel / cgroup / cgroup.c
index 786ceef2f222e8ca8c556d3526017fd061f3a137..6895464b54c6ddae55e8cffabe6cbef38410befc 100644 (file)
@@ -2435,8 +2435,15 @@ static int cgroup_migrate_execute(struct cgroup_mgctx *mgctx)
                        get_css_set(to_cset);
                        to_cset->nr_tasks++;
                        css_set_move_task(task, from_cset, to_cset, true);
-                       put_css_set_locked(from_cset);
                        from_cset->nr_tasks--;
+                       /*
+                        * If the source or destination cgroup is frozen,
+                        * the task might require to change its state.
+                        */
+                       cgroup_freezer_migrate_task(task, from_cset->dfl_cgrp,
+                                                   to_cset->dfl_cgrp);
+                       put_css_set_locked(from_cset);
+
                }
        }
        spin_unlock_irq(&css_set_lock);
@@ -3477,8 +3484,11 @@ static ssize_t cgroup_max_depth_write(struct kernfs_open_file *of,
 
 static int cgroup_events_show(struct seq_file *seq, void *v)
 {
-       seq_printf(seq, "populated %d\n",
-                  cgroup_is_populated(seq_css(seq)->cgroup));
+       struct cgroup *cgrp = seq_css(seq)->cgroup;
+
+       seq_printf(seq, "populated %d\n", cgroup_is_populated(cgrp));
+       seq_printf(seq, "frozen %d\n", test_bit(CGRP_FROZEN, &cgrp->flags));
+
        return 0;
 }
 
@@ -3540,6 +3550,40 @@ static int cgroup_cpu_pressure_show(struct seq_file *seq, void *v)
 }
 #endif
 
+static int cgroup_freeze_show(struct seq_file *seq, void *v)
+{
+       struct cgroup *cgrp = seq_css(seq)->cgroup;
+
+       seq_printf(seq, "%d\n", cgrp->freezer.freeze);
+
+       return 0;
+}
+
+static ssize_t cgroup_freeze_write(struct kernfs_open_file *of,
+                                  char *buf, size_t nbytes, loff_t off)
+{
+       struct cgroup *cgrp;
+       ssize_t ret;
+       int freeze;
+
+       ret = kstrtoint(strstrip(buf), 0, &freeze);
+       if (ret)
+               return ret;
+
+       if (freeze < 0 || freeze > 1)
+               return -ERANGE;
+
+       cgrp = cgroup_kn_lock_live(of->kn, false);
+       if (!cgrp)
+               return -ENOENT;
+
+       cgroup_freeze(cgrp, freeze);
+
+       cgroup_kn_unlock(of->kn);
+
+       return nbytes;
+}
+
 static int cgroup_file_open(struct kernfs_open_file *of)
 {
        struct cftype *cft = of->kn->priv;
@@ -4683,6 +4727,12 @@ static struct cftype cgroup_base_files[] = {
                .name = "cgroup.stat",
                .seq_show = cgroup_stat_show,
        },
+       {
+               .name = "cgroup.freeze",
+               .flags = CFTYPE_NOT_ON_ROOT,
+               .seq_show = cgroup_freeze_show,
+               .write = cgroup_freeze_write,
+       },
        {
                .name = "cpu.stat",
                .flags = CFTYPE_NOT_ON_ROOT,
@@ -5033,12 +5083,29 @@ static struct cgroup *cgroup_create(struct cgroup *parent)
        if (ret)
                goto out_psi_free;
 
+       /*
+        * New cgroup inherits effective freeze counter, and
+        * if the parent has to be frozen, the child has too.
+        */
+       cgrp->freezer.e_freeze = parent->freezer.e_freeze;
+       if (cgrp->freezer.e_freeze)
+               set_bit(CGRP_FROZEN, &cgrp->flags);
+
        spin_lock_irq(&css_set_lock);
        for (tcgrp = cgrp; tcgrp; tcgrp = cgroup_parent(tcgrp)) {
                cgrp->ancestor_ids[tcgrp->level] = tcgrp->id;
 
-               if (tcgrp != cgrp)
+               if (tcgrp != cgrp) {
                        tcgrp->nr_descendants++;
+
+                       /*
+                        * If the new cgroup is frozen, all ancestor cgroups
+                        * get a new frozen descendant, but their state can't
+                        * change because of this.
+                        */
+                       if (cgrp->freezer.e_freeze)
+                               tcgrp->freezer.nr_frozen_descendants++;
+               }
        }
        spin_unlock_irq(&css_set_lock);
 
@@ -5329,6 +5396,12 @@ static int cgroup_destroy_locked(struct cgroup *cgrp)
        for (tcgrp = cgroup_parent(cgrp); tcgrp; tcgrp = cgroup_parent(tcgrp)) {
                tcgrp->nr_descendants--;
                tcgrp->nr_dying_descendants++;
+               /*
+                * If the dying cgroup is frozen, decrease frozen descendants
+                * counters of ancestor cgroups.
+                */
+               if (test_bit(CGRP_FROZEN, &cgrp->flags))
+                       tcgrp->freezer.nr_frozen_descendants--;
        }
        spin_unlock_irq(&css_set_lock);
 
@@ -5782,6 +5855,29 @@ void cgroup_post_fork(struct task_struct *child)
                        cset->nr_tasks++;
                        css_set_move_task(child, NULL, cset, false);
                }
+
+               /*
+                * If the cgroup has to be frozen, the new task has too.
+                * Let's set the JOBCTL_TRAP_FREEZE jobctl bit to get
+                * the task into the frozen state.
+                */
+               if (unlikely(cgroup_task_freeze(child))) {
+                       struct cgroup *cgrp;
+
+                       spin_lock(&child->sighand->siglock);
+                       WARN_ON_ONCE(child->frozen);
+                       cgrp = cset->dfl_cgrp;
+                       child->jobctl |= JOBCTL_TRAP_FREEZE;
+                       spin_unlock(&child->sighand->siglock);
+
+                       /*
+                        * Calling cgroup_update_frozen() isn't required here,
+                        * because it will be called anyway a bit later
+                        * from do_freezer_trap(). So we avoid cgroup's
+                        * transient switch from the frozen state and back.
+                        */
+               }
+
                spin_unlock_irq(&css_set_lock);
        }
 
@@ -5830,6 +5926,12 @@ void cgroup_exit(struct task_struct *tsk)
                spin_lock_irq(&css_set_lock);
                css_set_move_task(tsk, cset, NULL, false);
                cset->nr_tasks--;
+
+               if (unlikely(cgroup_task_frozen(tsk)))
+                       cgroup_freezer_frozen_exit(tsk);
+               else if (unlikely(cgroup_task_freeze(tsk)))
+                       cgroup_update_frozen(task_dfl_cgroup(tsk));
+
                spin_unlock_irq(&css_set_lock);
        } else {
                get_css_set(cset);