blkcg: don't offline parent blkcg first
authorTejun Heo <tj@kernel.org>
Wed, 24 Jul 2019 17:37:55 +0000 (10:37 -0700)
committerJens Axboe <axboe@kernel.dk>
Wed, 1 Apr 2020 20:56:44 +0000 (14:56 -0600)
blkcg->cgwb_refcnt is used to delay blkcg offlining so that blkgs
don't get offlined while there are active cgwbs on them.  However, it
ends up making offlining unordered sometimes causing parents to be
offlined before children.

Let's fix this by making child blkcgs pin the parents' online states.

Note that pin/unpin names are chosen over get/put intentionally
because css uses get/put online for something different.

Signed-off-by: Tejun Heo <tj@kernel.org>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
block/blk-cgroup.c
include/linux/blk-cgroup.h

index 2acef6a649541cc41becf6f8b4160e9b0a599e4d..c5dc833212e1d1a58fdf55f4686c421e3e34a55d 100644 (file)
@@ -1006,6 +1006,21 @@ unlock:
        return ret;
 }
 
+static int blkcg_css_online(struct cgroup_subsys_state *css)
+{
+       struct blkcg *blkcg = css_to_blkcg(css);
+       struct blkcg *parent = blkcg_parent(blkcg);
+
+       /*
+        * blkcg_pin_online() is used to delay blkcg offline so that blkgs
+        * don't go offline while cgwbs are still active on them.  Pin the
+        * parent so that offline always happens towards the root.
+        */
+       if (parent)
+               blkcg_pin_online(parent);
+       return 0;
+}
+
 /**
  * blkcg_init_queue - initialize blkcg part of request queue
  * @q: request_queue to initialize
@@ -1199,6 +1214,7 @@ static void blkcg_exit(struct task_struct *tsk)
 
 struct cgroup_subsys io_cgrp_subsys = {
        .css_alloc = blkcg_css_alloc,
+       .css_online = blkcg_css_online,
        .css_offline = blkcg_css_offline,
        .css_free = blkcg_css_free,
        .can_attach = blkcg_can_attach,
index 7fb7caa55a3d2068d009f5c8e9e0f10aa6b9f303..35f8ffe92b702bb62550c6983614ff857335c2cb 100644 (file)
@@ -436,8 +436,12 @@ static inline void blkcg_pin_online(struct blkcg *blkcg)
  */
 static inline void blkcg_unpin_online(struct blkcg *blkcg)
 {
-       if (refcount_dec_and_test(&blkcg->online_pin))
+       do {
+               if (!refcount_dec_and_test(&blkcg->online_pin))
+                       break;
                blkcg_destroy_blkgs(blkcg);
+               blkcg = blkcg_parent(blkcg);
+       } while (blkcg);
 }
 
 /**