blkio: Take care of cgroup deletion and cfq group reference counting
[GitHub/LineageOS/android_kernel_motorola_exynos9610.git] / block / blk-cgroup.c
index 4f6afd76ec59613a85750737bac296577336dc9c..0426ab692fd5b8d283a65794377bb184fe89d03f 100644 (file)
@@ -13,6 +13,8 @@
 #include <linux/ioprio.h>
 #include "blk-cgroup.h"
 
+extern void cfq_unlink_blkio_group(void *, struct blkio_group *);
+
 struct blkio_cgroup blkio_root_cgroup = { .weight = 2*BLKIO_WEIGHT_DEFAULT };
 
 struct blkio_cgroup *cgroup_to_blkio_cgroup(struct cgroup *cgroup)
@@ -28,14 +30,43 @@ void blkiocg_add_blkio_group(struct blkio_cgroup *blkcg,
 
        spin_lock_irqsave(&blkcg->lock, flags);
        rcu_assign_pointer(blkg->key, key);
+       blkg->blkcg_id = css_id(&blkcg->css);
        hlist_add_head_rcu(&blkg->blkcg_node, &blkcg->blkg_list);
        spin_unlock_irqrestore(&blkcg->lock, flags);
 }
 
+static void __blkiocg_del_blkio_group(struct blkio_group *blkg)
+{
+       hlist_del_init_rcu(&blkg->blkcg_node);
+       blkg->blkcg_id = 0;
+}
+
+/*
+ * returns 0 if blkio_group was still on cgroup list. Otherwise returns 1
+ * indicating that blk_group was unhashed by the time we got to it.
+ */
 int blkiocg_del_blkio_group(struct blkio_group *blkg)
 {
-       /* Implemented later */
-       return 0;
+       struct blkio_cgroup *blkcg;
+       unsigned long flags;
+       struct cgroup_subsys_state *css;
+       int ret = 1;
+
+       rcu_read_lock();
+       css = css_lookup(&blkio_subsys, blkg->blkcg_id);
+       if (!css)
+               goto out;
+
+       blkcg = container_of(css, struct blkio_cgroup, css);
+       spin_lock_irqsave(&blkcg->lock, flags);
+       if (!hlist_unhashed(&blkg->blkcg_node)) {
+               __blkiocg_del_blkio_group(blkg);
+               ret = 0;
+       }
+       spin_unlock_irqrestore(&blkcg->lock, flags);
+out:
+       rcu_read_unlock();
+       return ret;
 }
 
 /* called under rcu_read_lock(). */
@@ -97,8 +128,39 @@ static int blkiocg_populate(struct cgroup_subsys *subsys, struct cgroup *cgroup)
 static void blkiocg_destroy(struct cgroup_subsys *subsys, struct cgroup *cgroup)
 {
        struct blkio_cgroup *blkcg = cgroup_to_blkio_cgroup(cgroup);
+       unsigned long flags;
+       struct blkio_group *blkg;
+       void *key;
 
+       rcu_read_lock();
+remove_entry:
+       spin_lock_irqsave(&blkcg->lock, flags);
+
+       if (hlist_empty(&blkcg->blkg_list)) {
+               spin_unlock_irqrestore(&blkcg->lock, flags);
+               goto done;
+       }
+
+       blkg = hlist_entry(blkcg->blkg_list.first, struct blkio_group,
+                               blkcg_node);
+       key = rcu_dereference(blkg->key);
+       __blkiocg_del_blkio_group(blkg);
+
+       spin_unlock_irqrestore(&blkcg->lock, flags);
+
+       /*
+        * This blkio_group is being unlinked as associated cgroup is going
+        * away. Let all the IO controlling policies know about this event.
+        *
+        * Currently this is static call to one io controlling policy. Once
+        * we have more policies in place, we need some dynamic registration
+        * of callback function.
+        */
+       cfq_unlink_blkio_group(key, blkg);
+       goto remove_entry;
+done:
        free_css_id(&blkio_subsys, &blkcg->css);
+       rcu_read_unlock();
        kfree(blkcg);
 }