block: have plug stored requests hold references to the queue
authorJens Axboe <axboe@kernel.dk>
Wed, 3 Nov 2021 11:49:07 +0000 (05:49 -0600)
committerJens Axboe <axboe@kernel.dk>
Thu, 4 Nov 2021 18:50:46 +0000 (12:50 -0600)
Requests that were stored in the cache deliberately didn't hold an enter
reference to the queue, instead we grabbed one every time we pulled a
request out of there. That made for awkward logic on freeing the remainder
of the cached list, if needed, where we had to artificially raise the
queue usage count before each free.

Grab references up front for cached plug requests. That's safer, and also
more efficient.

Fixes: 47c122e35d7e ("block: pre-allocate requests if plug is started and is a batch")
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
block/blk-core.c
block/blk-mq.c

index fd389a16013ce714a6334288c47a5c34c516b930..35a87c06276efc1499366f54c1f9d4d218b394f3 100644 (file)
@@ -1643,7 +1643,13 @@ void blk_flush_plug(struct blk_plug *plug, bool from_schedule)
                flush_plug_callbacks(plug, from_schedule);
        if (!rq_list_empty(plug->mq_list))
                blk_mq_flush_plug_list(plug, from_schedule);
-       if (unlikely(!from_schedule && plug->cached_rq))
+       /*
+        * Unconditionally flush out cached requests, even if the unplug
+        * event came from schedule. Since we know hold references to the
+        * queue for cached requests, we don't want a blocked task holding
+        * up a queue freeze/quiesce event.
+        */
+       if (unlikely(!rq_list_empty(plug->cached_rq)))
                blk_mq_free_plug_rqs(plug);
 }
 
index c68aa0a332e1c932312d1c3156781362144730d4..5498454c2164a112c4e7b230344409a405fe1b20 100644 (file)
@@ -410,7 +410,10 @@ __blk_mq_alloc_requests_batch(struct blk_mq_alloc_data *data,
                tag_mask &= ~(1UL << i);
                rq = blk_mq_rq_ctx_init(data, tags, tag, alloc_time_ns);
                rq_list_add(data->cached_rq, rq);
+               nr++;
        }
+       /* caller already holds a reference, add for remainder */
+       percpu_ref_get_many(&data->q->q_usage_counter, nr - 1);
        data->nr_tags -= nr;
 
        return rq_list_pop(data->cached_rq);
@@ -630,10 +633,8 @@ void blk_mq_free_plug_rqs(struct blk_plug *plug)
 {
        struct request *rq;
 
-       while ((rq = rq_list_pop(&plug->cached_rq)) != NULL) {
-               percpu_ref_get(&rq->q->q_usage_counter);
+       while ((rq = rq_list_pop(&plug->cached_rq)) != NULL)
                blk_mq_free_request(rq);
-       }
 }
 
 static void req_bio_endio(struct request *rq, struct bio *bio,