kfree(tags);
 }
 
-int blk_mq_tag_update_depth(struct blk_mq_tags *tags, unsigned int tdepth)
+int blk_mq_tag_update_depth(struct blk_mq_hw_ctx *hctx,
+                           struct blk_mq_tags **tagsptr, unsigned int tdepth,
+                           bool can_grow)
 {
-       tdepth -= tags->nr_reserved_tags;
-       if (tdepth > tags->nr_tags)
+       struct blk_mq_tags *tags = *tagsptr;
+
+       if (tdepth <= tags->nr_reserved_tags)
                return -EINVAL;
 
+       tdepth -= tags->nr_reserved_tags;
+
        /*
-        * Don't need (or can't) update reserved tags here, they remain
-        * static and should never need resizing.
+        * If we are allowed to grow beyond the original size, allocate
+        * a new set of tags before freeing the old one.
         */
-       sbitmap_queue_resize(&tags->bitmap_tags, tdepth);
+       if (tdepth > tags->nr_tags) {
+               struct blk_mq_tag_set *set = hctx->queue->tag_set;
+               struct blk_mq_tags *new;
+               bool ret;
+
+               if (!can_grow)
+                       return -EINVAL;
+
+               /*
+                * We need some sort of upper limit, set it high enough that
+                * no valid use cases should require more.
+                */
+               if (tdepth > 16 * BLKDEV_MAX_RQ)
+                       return -EINVAL;
+
+               new = blk_mq_alloc_rq_map(set, hctx->queue_num, tdepth, 0);
+               if (!new)
+                       return -ENOMEM;
+               ret = blk_mq_alloc_rqs(set, new, hctx->queue_num, tdepth);
+               if (ret) {
+                       blk_mq_free_rq_map(new);
+                       return -ENOMEM;
+               }
+
+               blk_mq_free_rqs(set, *tagsptr, hctx->queue_num);
+               blk_mq_free_rq_map(*tagsptr);
+               *tagsptr = new;
+       } else {
+               /*
+                * Don't need (or can't) update reserved tags here, they
+                * remain static and should never need resizing.
+                */
+               sbitmap_queue_resize(&tags->bitmap_tags, tdepth);
+       }
 
-       blk_mq_tag_wakeup_all(tags, false);
        return 0;
 }
 
 
                           struct blk_mq_ctx *ctx, unsigned int tag);
 extern bool blk_mq_has_free_tags(struct blk_mq_tags *tags);
 extern ssize_t blk_mq_tag_sysfs_show(struct blk_mq_tags *tags, char *page);
-extern int blk_mq_tag_update_depth(struct blk_mq_tags *tags, unsigned int depth);
+extern int blk_mq_tag_update_depth(struct blk_mq_hw_ctx *hctx,
+                                       struct blk_mq_tags **tags,
+                                       unsigned int depth, bool can_grow);
 extern void blk_mq_tag_wakeup_all(struct blk_mq_tags *tags, bool);
 void blk_mq_queue_tag_busy_iter(struct request_queue *q, busy_iter_fn *fn,
                void *priv);
 
        if (!set)
                return -EINVAL;
 
+       blk_mq_freeze_queue(q);
+       blk_mq_quiesce_queue(q);
+
        ret = 0;
        queue_for_each_hw_ctx(q, hctx, i) {
                if (!hctx->tags)
                 * If we're using an MQ scheduler, just update the scheduler
                 * queue depth. This is similar to what the old code would do.
                 */
-               if (!hctx->sched_tags)
-                       ret = blk_mq_tag_update_depth(hctx->tags,
-                                                       min(nr, set->queue_depth));
-               else
-                       ret = blk_mq_tag_update_depth(hctx->sched_tags, nr);
+               if (!hctx->sched_tags) {
+                       ret = blk_mq_tag_update_depth(hctx, &hctx->tags,
+                                                       min(nr, set->queue_depth),
+                                                       false);
+               } else {
+                       ret = blk_mq_tag_update_depth(hctx, &hctx->sched_tags,
+                                                       nr, true);
+               }
                if (ret)
                        break;
        }
        if (!ret)
                q->nr_requests = nr;
 
+       blk_mq_unfreeze_queue(q);
+       blk_mq_start_stopped_hw_queues(q, true);
+
        return ret;
 }