]> git.karo-electronics.de Git - linux-beck.git/blobdiff - kernel/cgroup.c
cgroup: link all cgroup_subsys_states in their sibling lists
[linux-beck.git] / kernel / cgroup.c
index c01e8e8dfad0791175f4db3e57ba949fcdee0fba..dcb06e181ce4f6178a02bd82e3bc6d898faf533b 100644 (file)
@@ -218,6 +218,15 @@ static void cgroup_idr_remove(struct idr *idr, int id)
        spin_unlock_bh(&cgroup_idr_lock);
 }
 
+static struct cgroup *cgroup_parent(struct cgroup *cgrp)
+{
+       struct cgroup_subsys_state *parent_css = cgrp->self.parent;
+
+       if (parent_css)
+               return container_of(parent_css, struct cgroup, self);
+       return NULL;
+}
+
 /**
  * cgroup_css - obtain a cgroup's css for the specified subsystem
  * @cgrp: the cgroup of interest
@@ -260,9 +269,9 @@ static struct cgroup_subsys_state *cgroup_e_css(struct cgroup *cgrp,
        if (!(cgrp->root->subsys_mask & (1 << ss->id)))
                return NULL;
 
-       while (cgrp->parent &&
-              !(cgrp->parent->child_subsys_mask & (1 << ss->id)))
-               cgrp = cgrp->parent;
+       while (cgroup_parent(cgrp) &&
+              !(cgroup_parent(cgrp)->child_subsys_mask & (1 << ss->id)))
+               cgrp = cgroup_parent(cgrp);
 
        return cgroup_css(cgrp, ss);
 }
@@ -307,7 +316,7 @@ bool cgroup_is_descendant(struct cgroup *cgrp, struct cgroup *ancestor)
        while (cgrp) {
                if (cgrp == ancestor)
                        return true;
-               cgrp = cgrp->parent;
+               cgrp = cgroup_parent(cgrp);
        }
        return false;
 }
@@ -369,7 +378,7 @@ static int notify_on_release(const struct cgroup *cgrp)
 
 /* iterate over child cgrps, lock should be held throughout iteration */
 #define cgroup_for_each_live_child(child, cgrp)                                \
-       list_for_each_entry((child), &(cgrp)->children, sibling)        \
+       list_for_each_entry((child), &(cgrp)->self.children, self.sibling) \
                if (({ lockdep_assert_held(&cgroup_mutex);              \
                       cgroup_is_dead(child); }))                       \
                        ;                                               \
@@ -454,7 +463,7 @@ static void cgroup_update_populated(struct cgroup *cgrp, bool populated)
 
                if (cgrp->populated_kn)
                        kernfs_notify(cgrp->populated_kn);
-               cgrp = cgrp->parent;
+               cgrp = cgroup_parent(cgrp);
        } while (cgrp);
 }
 
@@ -861,7 +870,7 @@ static void cgroup_destroy_root(struct cgroup_root *root)
        mutex_lock(&cgroup_mutex);
 
        BUG_ON(atomic_read(&root->nr_cgrps));
-       BUG_ON(!list_empty(&cgrp->children));
+       BUG_ON(!list_empty(&cgrp->self.children));
 
        /* Rebind all subsystems back to the default hierarchy */
        rebind_subsystems(&cgrp_dfl_root, root->subsys_mask);
@@ -1423,7 +1432,7 @@ static int cgroup_remount(struct kernfs_root *kf_root, int *flags, char *data)
        }
 
        /* remounting is not allowed for populated hierarchies */
-       if (!list_empty(&root->cgrp.children)) {
+       if (!list_empty(&root->cgrp.self.children)) {
                ret = -EBUSY;
                goto out_unlock;
        }
@@ -1503,8 +1512,8 @@ static void init_cgroup_housekeeping(struct cgroup *cgrp)
        struct cgroup_subsys *ss;
        int ssid;
 
-       INIT_LIST_HEAD(&cgrp->sibling);
-       INIT_LIST_HEAD(&cgrp->children);
+       INIT_LIST_HEAD(&cgrp->self.sibling);
+       INIT_LIST_HEAD(&cgrp->self.children);
        INIT_LIST_HEAD(&cgrp->cset_links);
        INIT_LIST_HEAD(&cgrp->release_list);
        INIT_LIST_HEAD(&cgrp->pidlists);
@@ -1603,7 +1612,7 @@ static int cgroup_setup_root(struct cgroup_root *root, unsigned int ss_mask)
                link_css_set(&tmp_links, cset, root_cgrp);
        up_write(&css_set_rwsem);
 
-       BUG_ON(!list_empty(&root_cgrp->children));
+       BUG_ON(!list_empty(&root_cgrp->self.children));
        BUG_ON(atomic_read(&root->nr_cgrps) != 1);
 
        kernfs_activate(root_cgrp->kn);
@@ -2018,7 +2027,7 @@ static int cgroup_migrate_prepare_dst(struct cgroup *dst_cgrp,
         * Except for the root, child_subsys_mask must be zero for a cgroup
         * with tasks so that child cgroups don't compete against tasks.
         */
-       if (dst_cgrp && cgroup_on_dfl(dst_cgrp) && dst_cgrp->parent &&
+       if (dst_cgrp && cgroup_on_dfl(dst_cgrp) && cgroup_parent(dst_cgrp) &&
            dst_cgrp->child_subsys_mask)
                return -EBUSY;
 
@@ -2427,7 +2436,7 @@ static int cgroup_controllers_show(struct seq_file *seq, void *v)
 {
        struct cgroup *cgrp = seq_css(seq)->cgroup;
 
-       cgroup_print_ss_mask(seq, cgrp->parent->child_subsys_mask);
+       cgroup_print_ss_mask(seq, cgroup_parent(cgrp)->child_subsys_mask);
        return 0;
 }
 
@@ -2610,8 +2619,8 @@ static ssize_t cgroup_subtree_control_write(struct kernfs_open_file *of,
 
                        /* unavailable or not enabled on the parent? */
                        if (!(cgrp_dfl_root.subsys_mask & (1 << ssid)) ||
-                           (cgrp->parent &&
-                            !(cgrp->parent->child_subsys_mask & (1 << ssid)))) {
+                           (cgroup_parent(cgrp) &&
+                            !(cgroup_parent(cgrp)->child_subsys_mask & (1 << ssid)))) {
                                ret = -ENOENT;
                                goto out_unlock;
                        }
@@ -2640,7 +2649,7 @@ static ssize_t cgroup_subtree_control_write(struct kernfs_open_file *of,
         * Except for the root, child_subsys_mask must be zero for a cgroup
         * with tasks so that child cgroups don't compete against tasks.
         */
-       if (enable && cgrp->parent && !list_empty(&cgrp->cset_links)) {
+       if (enable && cgroup_parent(cgrp) && !list_empty(&cgrp->cset_links)) {
                ret = -EBUSY;
                goto out_unlock;
        }
@@ -2898,9 +2907,9 @@ static int cgroup_addrm_files(struct cgroup *cgrp, struct cftype cfts[],
                        continue;
                if ((cft->flags & CFTYPE_INSANE) && cgroup_sane_behavior(cgrp))
                        continue;
-               if ((cft->flags & CFTYPE_NOT_ON_ROOT) && !cgrp->parent)
+               if ((cft->flags & CFTYPE_NOT_ON_ROOT) && !cgroup_parent(cgrp))
                        continue;
-               if ((cft->flags & CFTYPE_ONLY_ON_ROOT) && cgrp->parent)
+               if ((cft->flags & CFTYPE_ONLY_ON_ROOT) && cgroup_parent(cgrp))
                        continue;
 
                if (is_add) {
@@ -3119,11 +3128,11 @@ css_next_child(struct cgroup_subsys_state *pos_css,
         * cgroup is removed or iteration and removal race.
         */
        if (!pos) {
-               next = list_entry_rcu(cgrp->children.next, struct cgroup, sibling);
+               next = list_entry_rcu(cgrp->self.children.next, struct cgroup, self.sibling);
        } else if (likely(!cgroup_is_dead(pos))) {
-               next = list_entry_rcu(pos->sibling.next, struct cgroup, sibling);
+               next = list_entry_rcu(pos->self.sibling.next, struct cgroup, self.sibling);
        } else {
-               list_for_each_entry_rcu(next, &cgrp->children, sibling)
+               list_for_each_entry_rcu(next, &cgrp->self.children, self.sibling)
                        if (next->serial_nr > pos->serial_nr)
                                break;
        }
@@ -3133,12 +3142,12 @@ css_next_child(struct cgroup_subsys_state *pos_css,
         * the next sibling; however, it might have @ss disabled.  If so,
         * fast-forward to the next enabled one.
         */
-       while (&next->sibling != &cgrp->children) {
+       while (&next->self.sibling != &cgrp->self.children) {
                struct cgroup_subsys_state *next_css = cgroup_css(next, parent_css->ss);
 
                if (next_css)
                        return next_css;
-               next = list_entry_rcu(next->sibling.next, struct cgroup, sibling);
+               next = list_entry_rcu(next->self.sibling.next, struct cgroup, self.sibling);
        }
        return NULL;
 }
@@ -3176,10 +3185,10 @@ css_next_descendant_pre(struct cgroup_subsys_state *pos,
 
        /* no child, visit my or the closest ancestor's next sibling */
        while (pos != root) {
-               next = css_next_child(pos, css_parent(pos));
+               next = css_next_child(pos, pos->parent);
                if (next)
                        return next;
-               pos = css_parent(pos);
+               pos = pos->parent;
        }
 
        return NULL;
@@ -3261,12 +3270,12 @@ css_next_descendant_post(struct cgroup_subsys_state *pos,
                return NULL;
 
        /* if there's an unvisited sibling, visit its leftmost descendant */
-       next = css_next_child(pos, css_parent(pos));
+       next = css_next_child(pos, pos->parent);
        if (next)
                return css_leftmost_descendant(next);
 
        /* no sibling left, visit parent */
-       return css_parent(pos);
+       return pos->parent;
 }
 
 static bool cgroup_has_live_children(struct cgroup *cgrp)
@@ -3274,7 +3283,7 @@ static bool cgroup_has_live_children(struct cgroup *cgrp)
        struct cgroup *child;
 
        rcu_read_lock();
-       list_for_each_entry_rcu(child, &cgrp->children, sibling) {
+       list_for_each_entry_rcu(child, &cgrp->self.children, self.sibling) {
                if (!cgroup_is_dead(child)) {
                        rcu_read_unlock();
                        return true;
@@ -4092,14 +4101,14 @@ static void css_free_work_fn(struct work_struct *work)
                atomic_dec(&cgrp->root->nr_cgrps);
                cgroup_pidlist_destroy_all(cgrp);
 
-               if (cgrp->parent) {
+               if (cgroup_parent(cgrp)) {
                        /*
                         * We get a ref to the parent, and put the ref when
                         * this cgroup is being freed, so it's guaranteed
                         * that the parent won't be destroyed before its
                         * children.
                         */
-                       cgroup_put(cgrp->parent);
+                       cgroup_put(cgroup_parent(cgrp));
                        kernfs_put(cgrp->kn);
                        kfree(cgrp);
                } else {
@@ -4129,19 +4138,21 @@ static void css_release_work_fn(struct work_struct *work)
        struct cgroup_subsys *ss = css->ss;
        struct cgroup *cgrp = css->cgroup;
 
+       mutex_lock(&cgroup_mutex);
+
+       list_del_rcu(&css->sibling);
+
        if (ss) {
                /* css release path */
                cgroup_idr_remove(&ss->css_idr, css->id);
        } else {
                /* cgroup release path */
-               mutex_lock(&cgroup_mutex);
-               list_del_rcu(&cgrp->sibling);
-               mutex_unlock(&cgroup_mutex);
-
                cgroup_idr_remove(&cgrp->root->cgroup_idr, cgrp->id);
                cgrp->id = -1;
        }
 
+       mutex_unlock(&cgroup_mutex);
+
        call_rcu(&css->rcu_head, css_free_rcu_fn);
 }
 
@@ -4159,12 +4170,14 @@ static void init_and_link_css(struct cgroup_subsys_state *css,
 {
        cgroup_get(cgrp);
 
+       memset(css, 0, sizeof(*css));
        css->cgroup = cgrp;
        css->ss = ss;
-       css->flags = 0;
+       INIT_LIST_HEAD(&css->sibling);
+       INIT_LIST_HEAD(&css->children);
 
-       if (cgrp->parent) {
-               css->parent = cgroup_css(cgrp->parent, ss);
+       if (cgroup_parent(cgrp)) {
+               css->parent = cgroup_css(cgroup_parent(cgrp), ss);
                css_get(css->parent);
        }
 
@@ -4218,13 +4231,14 @@ static void offline_css(struct cgroup_subsys_state *css)
  */
 static int create_css(struct cgroup *cgrp, struct cgroup_subsys *ss)
 {
-       struct cgroup *parent = cgrp->parent;
+       struct cgroup *parent = cgroup_parent(cgrp);
+       struct cgroup_subsys_state *parent_css = cgroup_css(parent, ss);
        struct cgroup_subsys_state *css;
        int err;
 
        lockdep_assert_held(&cgroup_mutex);
 
-       css = ss->css_alloc(cgroup_css(parent, ss));
+       css = ss->css_alloc(parent_css);
        if (IS_ERR(css))
                return PTR_ERR(css);
 
@@ -4244,14 +4258,15 @@ static int create_css(struct cgroup *cgrp, struct cgroup_subsys *ss)
                goto err_free_id;
 
        /* @css is ready to be brought online now, make it visible */
+       list_add_tail_rcu(&css->sibling, &parent_css->children);
        cgroup_idr_replace(&ss->css_idr, css, css->id);
 
        err = online_css(css);
        if (err)
-               goto err_clear_dir;
+               goto err_list_del;
 
        if (ss->broken_hierarchy && !ss->warned_broken_hierarchy &&
-           parent->parent) {
+           cgroup_parent(parent)) {
                pr_warn("%s (%d) created nested cgroup for controller \"%s\" which has incomplete hierarchy support. Nested cgroups may change behavior in the future.\n",
                        current->comm, current->pid, ss->name);
                if (!strcmp(ss->name, "memory"))
@@ -4261,7 +4276,8 @@ static int create_css(struct cgroup *cgrp, struct cgroup_subsys *ss)
 
        return 0;
 
-err_clear_dir:
+err_list_del:
+       list_del_rcu(&css->sibling);
        cgroup_clear_dir(css->cgroup, 1 << css->ss->id);
 err_free_id:
        cgroup_idr_remove(&ss->css_idr, css->id);
@@ -4309,7 +4325,6 @@ static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name,
 
        init_cgroup_housekeeping(cgrp);
 
-       cgrp->parent = parent;
        cgrp->self.parent = &parent->self;
        cgrp->root = root;
 
@@ -4336,7 +4351,7 @@ static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name,
        cgrp->serial_nr = cgroup_serial_nr_next++;
 
        /* allocation complete, commit to creation */
-       list_add_tail_rcu(&cgrp->sibling, &cgrp->parent->children);
+       list_add_tail_rcu(&cgrp->self.sibling, &cgroup_parent(cgrp)->self.children);
        atomic_inc(&root->nr_cgrps);
        cgroup_get(parent);
 
@@ -4499,9 +4514,9 @@ static int cgroup_destroy_locked(struct cgroup *cgrp)
                return -EBUSY;
 
        /*
-        * Make sure there's no live children.  We can't test ->children
-        * emptiness as dead children linger on it while being destroyed;
-        * otherwise, "rmdir parent/child parent" may fail with -EBUSY.
+        * Make sure there's no live children.  We can't test emptiness of
+        * ->self.children as dead children linger on it while being
+        * drained; otherwise, "rmdir parent/child parent" may fail.
         */
        if (cgroup_has_live_children(cgrp))
                return -EBUSY;
@@ -4531,8 +4546,8 @@ static int cgroup_destroy_locked(struct cgroup *cgrp)
         */
        kernfs_remove(cgrp->kn);
 
-       set_bit(CGRP_RELEASABLE, &cgrp->parent->flags);
-       check_for_release(cgrp->parent);
+       set_bit(CGRP_RELEASABLE, &cgroup_parent(cgrp)->flags);
+       check_for_release(cgroup_parent(cgrp));
 
        /* put the base reference */
        percpu_ref_kill(&cgrp->self.refcnt);
@@ -4593,11 +4608,17 @@ static void __init cgroup_init_subsys(struct cgroup_subsys *ss, bool early)
        /* We don't handle early failures gracefully */
        BUG_ON(IS_ERR(css));
        init_and_link_css(css, ss, &cgrp_dfl_root.cgrp);
+
+       /*
+        * Root csses are never destroyed and we can't initialize
+        * percpu_ref during early init.  Disable refcnting.
+        */
+       css->flags |= CSS_NO_REF;
+
        if (early) {
                /* allocation can't be done safely during early init */
                css->id = 1;
        } else {
-               BUG_ON(percpu_ref_init(&css->refcnt, css_release));
                css->id = cgroup_idr_alloc(&ss->css_idr, css, 1, 2, GFP_KERNEL);
                BUG_ON(css->id < 0);
        }
@@ -4636,6 +4657,8 @@ int __init cgroup_init_early(void)
        int i;
 
        init_cgroup_root(&cgrp_dfl_root, &opts);
+       cgrp_dfl_root.cgrp.self.flags |= CSS_NO_REF;
+
        RCU_INIT_POINTER(init_task.cgroups, &init_css_set);
 
        for_each_subsys(ss, i) {
@@ -4684,7 +4707,6 @@ int __init cgroup_init(void)
                        struct cgroup_subsys_state *css =
                                init_css_set.subsys[ss->id];
 
-                       BUG_ON(percpu_ref_init(&css->refcnt, css_release));
                        css->id = cgroup_idr_alloc(&ss->css_idr, css, 1, 2,
                                                   GFP_KERNEL);
                        BUG_ON(css->id < 0);