block: ensure we hold a queue reference when using queue limits
authorJens Axboe <axboe@kernel.dk>
Fri, 12 Jan 2024 16:12:20 +0000 (09:12 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 25 Jan 2024 23:35:57 +0000 (15:35 -0800)
[ Upstream commit 7b4f36cd22a65b750b4cb6ac14804fb7d6e6c67d ]

q_usage_counter is the only thing preventing us from the limits changing
under us in __bio_split_to_limits, but blk_mq_submit_bio doesn't hold
it while calling into it.

Move the splitting inside the region where we know we've got a queue
reference. Ideally this could still remain a shared section of code, but
let's keep the fix simple and defer any refactoring here to later.

Reported-by: Christoph Hellwig <hch@lst.de>
Fixes: 900e08075202 ("block: move queue enter logic into blk_mq_submit_bio()")
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Ming Lei <ming.lei@redhat.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Sasha Levin <sashal@kernel.org>
block/blk-mq.c

index 20ecd0a..6041e17 100644 (file)
@@ -2968,12 +2968,6 @@ void blk_mq_submit_bio(struct bio *bio)
        blk_status_t ret;
 
        bio = blk_queue_bounce(bio, q);
-       if (bio_may_exceed_limits(bio, &q->limits)) {
-               bio = __bio_split_to_limits(bio, &q->limits, &nr_segs);
-               if (!bio)
-                       return;
-       }
-
        bio_set_ioprio(bio);
 
        if (plug) {
@@ -2982,6 +2976,11 @@ void blk_mq_submit_bio(struct bio *bio)
                        rq = NULL;
        }
        if (rq) {
+               if (unlikely(bio_may_exceed_limits(bio, &q->limits))) {
+                       bio = __bio_split_to_limits(bio, &q->limits, &nr_segs);
+                       if (!bio)
+                               return;
+               }
                if (!bio_integrity_prep(bio))
                        return;
                if (blk_mq_attempt_bio_merge(q, bio, nr_segs))
@@ -2992,6 +2991,11 @@ void blk_mq_submit_bio(struct bio *bio)
        } else {
                if (unlikely(bio_queue_enter(bio)))
                        return;
+               if (unlikely(bio_may_exceed_limits(bio, &q->limits))) {
+                       bio = __bio_split_to_limits(bio, &q->limits, &nr_segs);
+                       if (!bio)
+                               goto fail;
+               }
                if (!bio_integrity_prep(bio))
                        goto fail;
        }