blkcg: convert blkg_lookup_create() to find closest blkg
authorDennis Zhou <dennis@kernel.org>
Wed, 5 Dec 2018 17:10:28 +0000 (12:10 -0500)
committerJens Axboe <axboe@kernel.dk>
Sat, 8 Dec 2018 05:26:36 +0000 (22:26 -0700)
There are several scenarios where blkg_lookup_create() can fail such as
the blkcg dying, request_queue is dying, or simply being OOM. Most
handle this by simply falling back to the q->root_blkg and calling it a
day.

This patch implements the notion of closest blkg. During
blkg_lookup_create(), if it fails to create, return the closest blkg
found or the q->root_blkg. blkg_try_get_closest() is introduced and used
during association so a bio is always attached to a blkg.

Signed-off-by: Dennis Zhou <dennis@kernel.org>
Acked-by: Tejun Heo <tj@kernel.org>
Reviewed-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
block/bio.c
block/blk-cgroup.c
block/blk-iolatency.c
block/blk-throttle.c
include/linux/blk-cgroup.h

index 346a7f5cb2dd3a678d8246335a0b0d8121c90a12..5c9828524adc476d10fa339fc366fcd1e2b05972 100644 (file)
@@ -2009,21 +2009,24 @@ int bio_associate_blkcg(struct bio *bio, struct cgroup_subsys_state *blkcg_css)
 EXPORT_SYMBOL_GPL(bio_associate_blkcg);
 
 /**
- * bio_associate_blkg - associate a bio with the specified blkg
+ * bio_associate_blkg - associate a bio with the a blkg
  * @bio: target bio
  * @blkg: the blkg to associate
  *
- * Associate @bio with the blkg specified by @blkg.  This is the queue specific
- * blkcg information associated with the @bio, a reference will be taken on the
- * @blkg and will be freed when the bio is freed.
+ * This tries to associate @bio with the specified @blkg.  Association failure
+ * is handled by walking up the blkg tree.  Therefore, the blkg associated can
+ * be anything between @blkg and the root_blkg.  This situation only happens
+ * when a cgroup is dying and then the remaining bios will spill to the closest
+ * alive blkg.
+ *
+ * A reference will be taken on the @blkg and will be released when @bio is
+ * freed.
  */
 int bio_associate_blkg(struct bio *bio, struct blkcg_gq *blkg)
 {
        if (unlikely(bio->bi_blkg))
                return -EBUSY;
-       if (!blkg_try_get(blkg))
-               return -ENODEV;
-       bio->bi_blkg = blkg;
+       bio->bi_blkg = blkg_try_get_closest(blkg);
        return 0;
 }
 
index b421a9457e058be6c7a69af07cb8b37e7aab2969..120f2e2835fbbe3895173717f9780f5c87096b85 100644 (file)
@@ -258,9 +258,8 @@ err_free_blkg:
  * that all non-root blkg's have access to the parent blkg.  This function
  * should be called under RCU read lock and @q->queue_lock.
  *
- * Returns pointer to the looked up or created blkg on success, ERR_PTR()
- * value on error.  If @q is dead, returns ERR_PTR(-EINVAL).  If @q is not
- * dead and bypassing, returns ERR_PTR(-EBUSY).
+ * Returns the blkg or the closest blkg if blkg_create() fails as it walks
+ * down from root.
  */
 struct blkcg_gq *__blkg_lookup_create(struct blkcg *blkcg,
                                      struct request_queue *q)
@@ -276,19 +275,29 @@ struct blkcg_gq *__blkg_lookup_create(struct blkcg *blkcg,
 
        /*
         * Create blkgs walking down from blkcg_root to @blkcg, so that all
-        * non-root blkgs have access to their parents.
+        * non-root blkgs have access to their parents.  Returns the closest
+        * blkg to the intended blkg should blkg_create() fail.
         */
        while (true) {
                struct blkcg *pos = blkcg;
                struct blkcg *parent = blkcg_parent(blkcg);
-
-               while (parent && !__blkg_lookup(parent, q, false)) {
+               struct blkcg_gq *ret_blkg = q->root_blkg;
+
+               while (parent) {
+                       blkg = __blkg_lookup(parent, q, false);
+                       if (blkg) {
+                               /* remember closest blkg */
+                               ret_blkg = blkg;
+                               break;
+                       }
                        pos = parent;
                        parent = blkcg_parent(parent);
                }
 
                blkg = blkg_create(pos, q, NULL);
-               if (pos == blkcg || IS_ERR(blkg))
+               if (IS_ERR(blkg))
+                       return ret_blkg;
+               if (pos == blkcg)
                        return blkg;
        }
 }
index e6f68f15dee98e8a7f939a15b8141bbe9d9d682e..46e86c34cf790cc7eaa58ebb60a76cd6c956ea9f 100644 (file)
@@ -483,21 +483,11 @@ static void blkcg_iolatency_throttle(struct rq_qos *rqos, struct bio *bio)
        rcu_read_lock();
        bio_associate_blkcg(bio, NULL);
        blkcg = bio_blkcg(bio);
-       blkg = blkg_lookup(blkcg, q);
-       if (unlikely(!blkg)) {
-               spin_lock_irq(&q->queue_lock);
-               blkg = __blkg_lookup_create(blkcg, q);
-               if (IS_ERR(blkg))
-                       blkg = NULL;
-               spin_unlock_irq(&q->queue_lock);
-       }
-       if (!blkg)
-               goto out;
-
+       blkg = blkg_lookup_create(blkcg, q);
        bio_issue_init(&bio->bi_issue, bio_sectors(bio));
        bio_associate_blkg(bio, blkg);
-out:
        rcu_read_unlock();
+
        while (blkg && blkg->parent) {
                struct iolatency_grp *iolat = blkg_to_lat(blkg);
                if (!iolat) {
index 8f0a104770ee166d1c9d721ea0e46991512ee194..d648d6720f4691c69adddbbb0c934e01702fa0aa 100644 (file)
@@ -2118,9 +2118,7 @@ static inline void throtl_update_latency_buckets(struct throtl_data *td)
 static void blk_throtl_assoc_bio(struct throtl_grp *tg, struct bio *bio)
 {
 #ifdef CONFIG_BLK_DEV_THROTTLING_LOW
-       /* fallback to root_blkg if we fail to get a blkg ref */
-       if (bio->bi_css && (bio_associate_blkg(bio, tg_to_blkg(tg)) == -ENODEV))
-               bio_associate_blkg(bio, bio->bi_disk->queue->root_blkg);
+       bio_associate_blkg(bio, tg_to_blkg(tg));
        bio_issue_init(&bio->bi_issue, bio_sectors(bio));
 #endif
 }
index b3b1a8187d23271da15e3ac8ae22419a3e54e471..c08e96e521edd0c331f5895f67df3c319d632690 100644 (file)
@@ -545,6 +545,20 @@ static inline struct blkcg_gq *blkg_try_get(struct blkcg_gq *blkg)
        return NULL;
 }
 
+/**
+ * blkg_try_get_closest - try and get a blkg ref on the closet blkg
+ * @blkg: blkg to get
+ *
+ * This walks up the blkg tree to find the closest non-dying blkg and returns
+ * the blkg that it did association with as it may not be the passed in blkg.
+ */
+static inline struct blkcg_gq *blkg_try_get_closest(struct blkcg_gq *blkg)
+{
+       while (!atomic_inc_not_zero(&blkg->refcnt))
+               blkg = blkg->parent;
+
+       return blkg;
+}
 
 void __blkg_release_rcu(struct rcu_head *rcu);
 
@@ -797,15 +811,7 @@ static inline bool blkcg_bio_issue_check(struct request_queue *q,
        /* associate blkcg if bio hasn't attached one */
        bio_associate_blkcg(bio, NULL);
        blkcg = bio_blkcg(bio);
-
-       blkg = blkg_lookup(blkcg, q);
-       if (unlikely(!blkg)) {
-               spin_lock_irq(&q->queue_lock);
-               blkg = __blkg_lookup_create(blkcg, q);
-               if (IS_ERR(blkg))
-                       blkg = NULL;
-               spin_unlock_irq(&q->queue_lock);
-       }
+       blkg = blkg_lookup_create(blkcg, q);
 
        throtl = blk_throtl_bio(q, blkg, bio);