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;