]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - kernel/cgroup/cgroup.c
cgroup/pids: turn cgroup_subsys->free() into cgroup_subsys->release() to fix the...
[mirror_ubuntu-bionic-kernel.git] / kernel / cgroup / cgroup.c
index 2cf06c274e4ca6cc66194f4ce0fcb9ad0ad82f88..32f66ecd583652d3330e16c6996da14599508d10 100644 (file)
@@ -189,7 +189,7 @@ static u64 css_serial_nr_next = 1;
  */
 static u16 have_fork_callback __read_mostly;
 static u16 have_exit_callback __read_mostly;
-static u16 have_free_callback __read_mostly;
+static u16 have_release_callback __read_mostly;
 static u16 have_canfork_callback __read_mostly;
 
 /* cgroup namespace for init task */
@@ -1724,7 +1724,7 @@ static int parse_cgroup_root_flags(char *data, unsigned int *root_flags)
 
        *root_flags = 0;
 
-       if (!data)
+       if (!data || *data == '\0')
                return 0;
 
        while ((token = strsep(&data, ",")) != NULL) {
@@ -1977,7 +1977,7 @@ struct dentry *cgroup_do_mount(struct file_system_type *fs_type, int flags,
                               struct cgroup_namespace *ns)
 {
        struct dentry *dentry;
-       bool new_sb;
+       bool new_sb = false;
 
        dentry = kernfs_mount(fs_type, flags, root->kf_root, magic, &new_sb);
 
@@ -1987,6 +1987,7 @@ struct dentry *cgroup_do_mount(struct file_system_type *fs_type, int flags,
         */
        if (!IS_ERR(dentry) && ns != &init_cgroup_ns) {
                struct dentry *nsdentry;
+               struct super_block *sb = dentry->d_sb;
                struct cgroup *cgrp;
 
                mutex_lock(&cgroup_mutex);
@@ -1997,12 +1998,14 @@ struct dentry *cgroup_do_mount(struct file_system_type *fs_type, int flags,
                spin_unlock_irq(&css_set_lock);
                mutex_unlock(&cgroup_mutex);
 
-               nsdentry = kernfs_node_dentry(cgrp->kn, dentry->d_sb);
+               nsdentry = kernfs_node_dentry(cgrp->kn, sb);
                dput(dentry);
+               if (IS_ERR(nsdentry))
+                       deactivate_locked_super(sb);
                dentry = nsdentry;
        }
 
-       if (IS_ERR(dentry) || !new_sb)
+       if (!new_sb)
                cgroup_put(&root->cgrp);
 
        return dentry;
@@ -2815,11 +2818,12 @@ restart:
 }
 
 /**
- * cgroup_save_control - save control masks of a subtree
+ * cgroup_save_control - save control masks and dom_cgrp of a subtree
  * @cgrp: root of the target subtree
  *
- * Save ->subtree_control and ->subtree_ss_mask to the respective old_
- * prefixed fields for @cgrp's subtree including @cgrp itself.
+ * Save ->subtree_control, ->subtree_ss_mask and ->dom_cgrp to the
+ * respective old_ prefixed fields for @cgrp's subtree including @cgrp
+ * itself.
  */
 static void cgroup_save_control(struct cgroup *cgrp)
 {
@@ -2829,6 +2833,7 @@ static void cgroup_save_control(struct cgroup *cgrp)
        cgroup_for_each_live_descendant_pre(dsct, d_css, cgrp) {
                dsct->old_subtree_control = dsct->subtree_control;
                dsct->old_subtree_ss_mask = dsct->subtree_ss_mask;
+               dsct->old_dom_cgrp = dsct->dom_cgrp;
        }
 }
 
@@ -2854,11 +2859,12 @@ static void cgroup_propagate_control(struct cgroup *cgrp)
 }
 
 /**
- * cgroup_restore_control - restore control masks of a subtree
+ * cgroup_restore_control - restore control masks and dom_cgrp of a subtree
  * @cgrp: root of the target subtree
  *
- * Restore ->subtree_control and ->subtree_ss_mask from the respective old_
- * prefixed fields for @cgrp's subtree including @cgrp itself.
+ * Restore ->subtree_control, ->subtree_ss_mask and ->dom_cgrp from the
+ * respective old_ prefixed fields for @cgrp's subtree including @cgrp
+ * itself.
  */
 static void cgroup_restore_control(struct cgroup *cgrp)
 {
@@ -2868,6 +2874,7 @@ static void cgroup_restore_control(struct cgroup *cgrp)
        cgroup_for_each_live_descendant_post(dsct, d_css, cgrp) {
                dsct->subtree_control = dsct->old_subtree_control;
                dsct->subtree_ss_mask = dsct->old_subtree_ss_mask;
+               dsct->dom_cgrp = dsct->old_dom_cgrp;
        }
 }
 
@@ -3175,6 +3182,8 @@ static int cgroup_enable_threaded(struct cgroup *cgrp)
 {
        struct cgroup *parent = cgroup_parent(cgrp);
        struct cgroup *dom_cgrp = parent->dom_cgrp;
+       struct cgroup *dsct;
+       struct cgroup_subsys_state *d_css;
        int ret;
 
        lockdep_assert_held(&cgroup_mutex);
@@ -3183,6 +3192,16 @@ static int cgroup_enable_threaded(struct cgroup *cgrp)
        if (cgroup_is_threaded(cgrp))
                return 0;
 
+       /*
+        * If @cgroup is populated or has domain controllers enabled, it
+        * can't be switched.  While the below cgroup_can_be_thread_root()
+        * test can catch the same conditions, that's only when @parent is
+        * not mixable, so let's check it explicitly.
+        */
+       if (cgroup_is_populated(cgrp) ||
+           cgrp->subtree_control & ~cgrp_dfl_threaded_ss_mask)
+               return -EOPNOTSUPP;
+
        /* we're joining the parent's domain, ensure its validity */
        if (!cgroup_is_valid_domain(dom_cgrp) ||
            !cgroup_can_be_thread_root(dom_cgrp))
@@ -3194,12 +3213,13 @@ static int cgroup_enable_threaded(struct cgroup *cgrp)
         */
        cgroup_save_control(cgrp);
 
-       cgrp->dom_cgrp = dom_cgrp;
+       cgroup_for_each_live_descendant_pre(dsct, d_css, cgrp)
+               if (dsct == cgrp || cgroup_is_threaded(dsct))
+                       dsct->dom_cgrp = dom_cgrp;
+
        ret = cgroup_apply_control(cgrp);
        if (!ret)
                parent->nr_threaded_children++;
-       else
-               cgrp->dom_cgrp = cgrp;
 
        cgroup_finalize_control(cgrp, ret);
        return ret;
@@ -3523,7 +3543,9 @@ static int cgroup_add_file(struct cgroup_subsys_state *css, struct cgroup *cgrp,
        key = &cft->lockdep_key;
 #endif
        kn = __kernfs_create_file(cgrp->kn, cgroup_file_name(cgrp, cft, name),
-                                 cgroup_file_mode(cft), 0, cft->kf_ops, cft,
+                                 cgroup_file_mode(cft),
+                                 GLOBAL_ROOT_UID, GLOBAL_ROOT_GID,
+                                 0, cft->kf_ops, cft,
                                  NULL, key);
        if (IS_ERR(kn))
                return PTR_ERR(kn);
@@ -4129,20 +4151,25 @@ static void css_task_iter_advance(struct css_task_iter *it)
 
        lockdep_assert_held(&css_set_lock);
 repeat:
-       /*
-        * Advance iterator to find next entry.  cset->tasks is consumed
-        * first and then ->mg_tasks.  After ->mg_tasks, we move onto the
-        * next cset.
-        */
-       next = it->task_pos->next;
+       if (it->task_pos) {
+               /*
+                * Advance iterator to find next entry.  cset->tasks is
+                * consumed first and then ->mg_tasks.  After ->mg_tasks,
+                * we move onto the next cset.
+                */
+               next = it->task_pos->next;
 
-       if (next == it->tasks_head)
-               next = it->mg_tasks_head->next;
+               if (next == it->tasks_head)
+                       next = it->mg_tasks_head->next;
 
-       if (next == it->mg_tasks_head)
+               if (next == it->mg_tasks_head)
+                       css_task_iter_advance_css_set(it);
+               else
+                       it->task_pos = next;
+       } else {
+               /* called from start, proceed to the first cset */
                css_task_iter_advance_css_set(it);
-       else
-               it->task_pos = next;
+       }
 
        /* if PROCS, skip over tasks which aren't group leaders */
        if ((it->flags & CSS_TASK_ITER_PROCS) && it->task_pos &&
@@ -4182,7 +4209,7 @@ void css_task_iter_start(struct cgroup_subsys_state *css, unsigned int flags,
 
        it->cset_head = it->cset_pos;
 
-       css_task_iter_advance_css_set(it);
+       css_task_iter_advance(it);
 
        spin_unlock_irq(&css_set_lock);
 }
@@ -4447,6 +4474,7 @@ static struct cftype cgroup_base_files[] = {
        },
        {
                .name = "cgroup.threads",
+               .flags = CFTYPE_NS_DELEGATABLE,
                .release = cgroup_procs_release,
                .seq_start = cgroup_threads_start,
                .seq_next = cgroup_procs_next,
@@ -5174,7 +5202,7 @@ static void __init cgroup_init_subsys(struct cgroup_subsys *ss, bool early)
 
        have_fork_callback |= (bool)ss->fork << ss->id;
        have_exit_callback |= (bool)ss->exit << ss->id;
-       have_free_callback |= (bool)ss->free << ss->id;
+       have_release_callback |= (bool)ss->release << ss->id;
        have_canfork_callback |= (bool)ss->can_fork << ss->id;
 
        /* At system boot, before all subsystems have been
@@ -5610,16 +5638,19 @@ void cgroup_exit(struct task_struct *tsk)
        } while_each_subsys_mask();
 }
 
-void cgroup_free(struct task_struct *task)
+void cgroup_release(struct task_struct *task)
 {
-       struct css_set *cset = task_css_set(task);
        struct cgroup_subsys *ss;
        int ssid;
 
-       do_each_subsys_mask(ss, ssid, have_free_callback) {
-               ss->free(task);
+       do_each_subsys_mask(ss, ssid, have_release_callback) {
+               ss->release(task);
        } while_each_subsys_mask();
+}
 
+void cgroup_free(struct task_struct *task)
+{
+       struct css_set *cset = task_css_set(task);
        put_css_set(cset);
 }