From 4fcc499ea9bc31b02393904a1903e794d146f5f7 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Wed, 28 Oct 2026 10:41:41 -0600 Subject: [PATCH] blk-mq: set up a queue under tag_list_lock blk_mq_init_allocated_queue() only takes tag_list_lock at the end, when it adds the queue to the set. Before that it sizes the queue by set->nr_hw_queues, hands out set->tags[], maps by set->map[], and blk_mq_map_swqueue() frees tags of the set. An update clearing the maps at that time makes the new queue free tags that all the other queues share. An NVMe controller reset during a namespace scan does exactly that. Take the lock before the first look at the set. An update can be part of error handling, so the allocations under the lock must not wait for IO: memalloc_noio_save(), as in add_disk_fwnode(). Fixes: 868f2f0b7206 ("blk-mq: dynamic h/w context count") Signed-off-by: Josef Bacik Link: https://patch.msgid.link/20261028164415.1262013-39-axboe@kernel.dk Signed-off-by: Jens Axboe --- block/blk-mq.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/block/blk-mq.c b/block/blk-mq.c index 6418c2a7cd2c..5fca78778c31 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -4354,7 +4354,7 @@ static void blk_mq_add_queue_tag_set(struct blk_mq_tag_set *set, { unsigned int i; - mutex_lock(&set->tag_list_lock); + lockdep_assert_held(&set->tag_list_lock); /* * Check to see if we're transitioning to shared (from 1 to 2 queues). @@ -4379,8 +4379,6 @@ static void blk_mq_add_queue_tag_set(struct blk_mq_tag_set *set, blk_mq_quiesce_queue_nowait(q); } spin_unlock(&set->quiesce_lock); - - mutex_unlock(&set->tag_list_lock); } /* All allocations will be freed in release handler of q->mq_kobj */ @@ -4657,6 +4655,8 @@ static void blk_mq_realloc_hw_ctxs(struct blk_mq_tag_set *set, int blk_mq_init_allocated_queue(struct blk_mq_tag_set *set, struct request_queue *q) { + unsigned int memflags; + /* mark the queue as mq asap */ q->mq_ops = set->ops; @@ -4675,6 +4675,16 @@ int blk_mq_init_allocated_queue(struct blk_mq_tag_set *set, INIT_LIST_HEAD(&q->unused_hctx_list); spin_lock_init(&q->unused_hctx_lock); + /* + * Keep blk_mq_update_nr_hw_queues() away until the queue is on + * set->tag_list: it changes the number of hardware queues, the tags and + * the maps, and blk_mq_map_swqueue() allocates and frees tags of the set. + * An update can be part of the error handling of a driver, so it must + * not have to wait for somebody who waits for I/O. + */ + memflags = memalloc_noio_save(); + mutex_lock(&set->tag_list_lock); + blk_mq_realloc_hw_ctxs(set, q); if (!q->nr_hw_queues) goto err_hctxs; @@ -4695,9 +4705,14 @@ int blk_mq_init_allocated_queue(struct blk_mq_tag_set *set, blk_mq_init_cpu_queues(q, set->nr_hw_queues); blk_mq_map_swqueue(q); blk_mq_add_queue_tag_set(set, q); + + mutex_unlock(&set->tag_list_lock); + memalloc_noio_restore(memflags); return 0; err_hctxs: + mutex_unlock(&set->tag_list_lock); + memalloc_noio_restore(memflags); blk_mq_release(q); err_exit: q->mq_ops = NULL;