block: don't update BLK_FEAT_POLL in __blk_mq_update_nr_hw_queues
authorChristoph Hellwig <hch@lst.de>
Fri, 10 Jan 2025 05:47:12 +0000 (06:47 +0100)
committerJens Axboe <axboe@kernel.dk>
Fri, 10 Jan 2025 14:29:23 +0000 (07:29 -0700)
When __blk_mq_update_nr_hw_queues changes the number of tag sets, it
might have to disable poll queues.  Currently it does so by adjusting
the BLK_FEAT_POLL, which is a bit against the intent of features that
describe hardware / driver capabilities, but more importantly causes
nasty lock order problems with the broadly held freeze when updating the
number of hardware queues and the limits lock.  Fix this by leaving
BLK_FEAT_POLL alone, and instead check for the number of poll queues in
the bio submission and poll handlers.  While this adds extra work to the
fast path, the variables are in cache lines used by these operations
anyway, so it should be cheap enough.

Fixes: 8023e144f9d6 ("block: move the poll flag to queue_limits")
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Ming Lei <ming.lei@redhat.com>
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Reviewed-by: Nilay Shroff <nilay@linux.ibm.com>
Link: https://lore.kernel.org/r/20250110054726.1499538-5-hch@lst.de
Signed-off-by: Jens Axboe <axboe@kernel.dk>
block/blk-core.c
block/blk-mq.c
block/blk-mq.h
block/blk-sysfs.c

index 6309b3f5a89dc4b972e5722201f9646bc359465b..32fb28a6372cdf62dc5b842a97e89fe164049889 100644 (file)
@@ -951,14 +951,13 @@ int bio_poll(struct bio *bio, struct io_comp_batch *iob, unsigned int flags)
         */
        if (!percpu_ref_tryget(&q->q_usage_counter))
                return 0;
-       if (!(q->limits.features & BLK_FEAT_POLL)) {
-               ret = 0;
-       } else if (queue_is_mq(q)) {
+       if (queue_is_mq(q)) {
                ret = blk_mq_poll(q, cookie, iob, flags);
        } else {
                struct gendisk *disk = q->disk;
 
-               if (disk && disk->fops->poll_bio)
+               if ((q->limits.features & BLK_FEAT_POLL) && disk &&
+                   disk->fops->poll_bio)
                        ret = disk->fops->poll_bio(bio, iob, flags);
        }
        blk_queue_exit(q);
index 02c9232a8fff9438990a86d8a0d112052827847a..655dcc16db76951810d84f836f5c9e25b1a2ccce 100644 (file)
@@ -3105,8 +3105,7 @@ void blk_mq_submit_bio(struct bio *bio)
                goto queue_exit;
        }
 
-       if ((bio->bi_opf & REQ_POLLED) &&
-           !(q->limits.features & BLK_FEAT_POLL)) {
+       if ((bio->bi_opf & REQ_POLLED) && !blk_mq_can_poll(q)) {
                bio->bi_status = BLK_STS_NOTSUPP;
                bio_endio(bio);
                goto queue_exit;
@@ -4328,12 +4327,6 @@ void blk_mq_release(struct request_queue *q)
        blk_mq_sysfs_deinit(q);
 }
 
-static bool blk_mq_can_poll(struct blk_mq_tag_set *set)
-{
-       return set->nr_maps > HCTX_TYPE_POLL &&
-               set->map[HCTX_TYPE_POLL].nr_queues;
-}
-
 struct request_queue *blk_mq_alloc_queue(struct blk_mq_tag_set *set,
                struct queue_limits *lim, void *queuedata)
 {
@@ -4344,7 +4337,7 @@ struct request_queue *blk_mq_alloc_queue(struct blk_mq_tag_set *set,
        if (!lim)
                lim = &default_lim;
        lim->features |= BLK_FEAT_IO_STAT | BLK_FEAT_NOWAIT;
-       if (blk_mq_can_poll(set))
+       if (set->nr_maps > HCTX_TYPE_POLL)
                lim->features |= BLK_FEAT_POLL;
 
        q = blk_alloc_queue(lim, set->numa_node);
@@ -5032,8 +5025,6 @@ static void __blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set,
 fallback:
        blk_mq_update_queue_map(set);
        list_for_each_entry(q, &set->tag_list, tag_set_list) {
-               struct queue_limits lim;
-
                blk_mq_realloc_hw_ctxs(set, q);
 
                if (q->nr_hw_queues != set->nr_hw_queues) {
@@ -5047,13 +5038,6 @@ fallback:
                        set->nr_hw_queues = prev_nr_hw_queues;
                        goto fallback;
                }
-               lim = queue_limits_start_update(q);
-               if (blk_mq_can_poll(set))
-                       lim.features |= BLK_FEAT_POLL;
-               else
-                       lim.features &= ~BLK_FEAT_POLL;
-               if (queue_limits_commit_update(q, &lim) < 0)
-                       pr_warn("updating the poll flag failed\n");
                blk_mq_map_swqueue(q);
        }
 
@@ -5113,9 +5097,9 @@ static int blk_hctx_poll(struct request_queue *q, struct blk_mq_hw_ctx *hctx,
 int blk_mq_poll(struct request_queue *q, blk_qc_t cookie,
                struct io_comp_batch *iob, unsigned int flags)
 {
-       struct blk_mq_hw_ctx *hctx = xa_load(&q->hctx_table, cookie);
-
-       return blk_hctx_poll(q, hctx, iob, flags);
+       if (!blk_mq_can_poll(q))
+               return 0;
+       return blk_hctx_poll(q, xa_load(&q->hctx_table, cookie), iob, flags);
 }
 
 int blk_rq_poll(struct request *rq, struct io_comp_batch *iob,
index c872bbbe641185d1ae044d0bc8afd830c671afd8..44979e92b79f934919c7931604e4d758ff0bcc72 100644 (file)
@@ -448,4 +448,10 @@ do {                                                               \
 #define blk_mq_run_dispatch_ops(q, dispatch_ops)               \
        __blk_mq_run_dispatch_ops(q, true, dispatch_ops)        \
 
+static inline bool blk_mq_can_poll(struct request_queue *q)
+{
+       return (q->limits.features & BLK_FEAT_POLL) &&
+               q->tag_set->map[HCTX_TYPE_POLL].nr_queues;
+}
+
 #endif
index 767598e719ab0ee8b6bc7ad477ec010fea9c8f07..e9f1c82b2f3e37a0d822d6c74bf00dec1df46592 100644 (file)
@@ -245,10 +245,17 @@ static ssize_t queue_##_name##_show(struct gendisk *disk, char *page)     \
                !!(disk->queue->limits.features & _feature));           \
 }
 
-QUEUE_SYSFS_FEATURE_SHOW(poll, BLK_FEAT_POLL);
 QUEUE_SYSFS_FEATURE_SHOW(fua, BLK_FEAT_FUA);
 QUEUE_SYSFS_FEATURE_SHOW(dax, BLK_FEAT_DAX);
 
+static ssize_t queue_poll_show(struct gendisk *disk, char *page)
+{
+       if (queue_is_mq(disk->queue))
+               return sysfs_emit(page, "%u\n", blk_mq_can_poll(disk->queue));
+       return sysfs_emit(page, "%u\n",
+               !!(disk->queue->limits.features & BLK_FEAT_POLL));
+}
+
 static ssize_t queue_zoned_show(struct gendisk *disk, char *page)
 {
        if (blk_queue_is_zoned(disk->queue))