diff --git a/block/blk-mq.c b/block/blk-mq.c index b7da2f7ac60a..fe7ff041bd6d 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -336,12 +336,15 @@ void blk_mq_quiesce_tagset(struct blk_mq_tag_set *set) { struct request_queue *q; + spin_lock(&set->quiesce_lock); + set->quiesce_depth++; rcu_read_lock(); list_for_each_entry_rcu(q, &set->tag_list, tag_set_list) { if (!blk_queue_skip_tagset_quiesce(q)) blk_mq_quiesce_queue_nowait(q); } rcu_read_unlock(); + spin_unlock(&set->quiesce_lock); blk_mq_wait_quiesce_done(set); } @@ -351,12 +354,16 @@ void blk_mq_unquiesce_tagset(struct blk_mq_tag_set *set) { struct request_queue *q; + spin_lock(&set->quiesce_lock); + if (set->quiesce_depth) + set->quiesce_depth--; rcu_read_lock(); list_for_each_entry_rcu(q, &set->tag_list, tag_set_list) { if (!blk_queue_skip_tagset_quiesce(q)) blk_mq_unquiesce_queue(q); } rcu_read_unlock(); + spin_unlock(&set->quiesce_lock); } EXPORT_SYMBOL_GPL(blk_mq_unquiesce_tagset); @@ -4345,6 +4352,8 @@ static void blk_mq_del_queue_tag_set(struct request_queue *q) static void blk_mq_add_queue_tag_set(struct blk_mq_tag_set *set, struct request_queue *q) { + unsigned int i; + mutex_lock(&set->tag_list_lock); /* @@ -4358,7 +4367,18 @@ static void blk_mq_add_queue_tag_set(struct blk_mq_tag_set *set, } if (set->flags & BLK_MQ_F_TAG_QUEUE_SHARED) queue_set_hctx_shared(q, true); + + /* + * A queue that joins a quiesced tag set is quiesced like the others, + * and blk_mq_unquiesce_tagset() finds what it is going to undo. + */ + spin_lock(&set->quiesce_lock); list_add_tail_rcu(&q->tag_set_list, &set->tag_list); + if (!blk_queue_skip_tagset_quiesce(q)) { + for (i = 0; i < set->quiesce_depth; i++) + blk_mq_quiesce_queue_nowait(q); + } + spin_unlock(&set->quiesce_lock); mutex_unlock(&set->tag_list_lock); } @@ -4927,6 +4947,8 @@ int blk_mq_alloc_tag_set(struct blk_mq_tag_set *set) mutex_init(&set->tag_list_lock); INIT_LIST_HEAD(&set->tag_list); + spin_lock_init(&set->quiesce_lock); + set->quiesce_depth = 0; return 0; diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h index 5e130b282b42..aa3b35405edc 100644 --- a/include/linux/blk-mq.h +++ b/include/linux/blk-mq.h @@ -523,6 +523,10 @@ enum hctx_type { * @tag_list_lock: Serializes tag_list accesses. * @tag_list: List of the request queues that use this tag set. See also * request_queue.tag_set_list. + * @quiesce_lock: Serializes blk_mq_[un]quiesce_tagset() against each other + * and against a request queue being added to @tag_list. + * @quiesce_depth: How often blk_mq_quiesce_tagset() has been called without + * blk_mq_unquiesce_tagset(). * @srcu: Use as lock when type of the request queue is blocking * (BLK_MQ_F_BLOCKING). * @update_nr_hwq_lock: @@ -548,6 +552,8 @@ struct blk_mq_tag_set { struct mutex tag_list_lock; struct list_head tag_list; + spinlock_t quiesce_lock; + unsigned int quiesce_depth; struct srcu_struct *srcu; struct rw_semaphore update_nr_hwq_lock;