[SRU,bionic-aws,5/6] xen-blkfront: resurrect request-based mode

Message ID 20181102090010.2643-6-daniel.axtens@canonical.com
State New
Headers show
Series
  • LP: #1801305: xen-blkfront: restore request mode
Related show

Commit Message

Daniel Axtens Nov. 2, 2018, 9 a.m.
From: Munehisa Kamata <kamatam@amazon.com>

BugLink: https://bugs.launchpad.net/bugs/1801305

This change resurrect request-based mode which was completely dropped in
commit 907c3eb18e0b ("xen-blkfront: convert to blk-mq APIs").

Not to make the queue lock stale, resurrect per-device (vbd) lock in
blkfront_info which is never freed during Xen suspend and use it in
request-based mode. This is bascially the same as what the driver was
doing until commit 11659569f720 ("xen/blkfront: split per device
io_lock"). If the driver is in blk-mq mode, just use the lock(s) in
blkfront_ring_info.

In commit b7420c1eaeac ("drivers/amazon: xen-blkfront: resurrect
request-based mode"), we accidentally didn't bring piece of code which
empties the request queue while saving bios. The logic was originally
introduced in commit 402b27f9f2c2 ("xen-block: implement indirect
descriptors"). It seems to be still required for request-based mode,
so just do the same thing as before.

Note that some suspend/resume logic were moved from blkif_recover() to
blkfront_resume() in commit 7b427a59538a ("xen-blkfront: save uncompleted
reqs in blkfront_resume()"), so add the logic to blkfront_resume().

Forward-port notes: As part of this forward port, we are no longer
using out of tree xen-blkfront. Request based patch and its releated
per device vbd lock has now been ported on top of intree xen-blkfront.
For reference:
4.9 CR for resurrect request based mode: https://cr.amazon.com/r/6834653/
4.9 CR for resurrect per-device (vbd) lock: https://cr.amazon.com/r/7475903/
4.9 CR for empty the request queue while resuming: https://cr.amazon.com/r/7475918/
As part of forward-port, all the above 3 related patches, have been merged into
a single commit.
In 4.14.y kernel, we realized during forward-port and testing, that blk-mq stashes
the error code for request right after the request structure in memory. Care was
taken to not reuse this piece of memory for stashing error code in request mode
as this can cause memory corruption.
Hibernation: To not break git bisect and the hibernation feature, blkfront_freeze()
and blkfront_resume() were modified as well to support request-based mode.

Reported-by: Imre Palik <imrep@amazon.com>
Reviewed-by: Eduardo Valentin <eduval@amazon.com>
Reviewed-by: Munehisa Kamata <kamatam@amazon.com>
Reviewed-by: Anchal Agarwal <anchalag@amazon.com>
Signed-off-by: Munehisa Kamata <kamatam@amazon.com>
Signed-off-by: Vallish Vaidyeshwara <vallish@amazon.com>

CR: https://cr.amazon.com/r/8309443
(cherry-picked from
 0026-xen-blkfront-resurrect-request-based-mode.patch
 in AWS 4.14 kernel SRPM)
Signed-off-by: Daniel Axtens <daniel.axtens@canonical.com>
---
 drivers/block/xen-blkfront.c | 334 ++++++++++++++++++++++++++++-------
 1 file changed, 268 insertions(+), 66 deletions(-)

Patch

diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c
index e410535540f4..a5d0266ee1ba 100644
--- a/drivers/block/xen-blkfront.c
+++ b/drivers/block/xen-blkfront.c
@@ -156,6 +156,15 @@  MODULE_PARM_DESC(max_ring_page_order, "Maximum order of pages to be used for the
 #define BLK_MAX_RING_SIZE	\
 	__CONST_RING_SIZE(blkif, XEN_PAGE_SIZE * XENBUS_MAX_RING_GRANTS)
 
+static unsigned int blkfront_use_blk_mq = 0;
+module_param_named(use_blk_mq, blkfront_use_blk_mq, int, S_IRUGO);
+MODULE_PARM_DESC(use_blk_mq, "Enable blk-mq (default is 0)");
+
+/*
+ * Index to the first available ring.
+ */
+#define FIRST_RING_ID (0)
+
 /*
  * ring-ref%u i=(-1UL) would take 11 characters + 'ring-ref' is 8, so 19
  * characters are enough. Define to 20 to keep consistent with backend.
@@ -194,6 +203,12 @@  struct blkfront_ring_info {
  */
 struct blkfront_info
 {
+	/*
+	 * Per vbd lock which protects an associated blkfront_ring_info if the
+	 * driver is in request-based mode. Use this lock always instead of per
+	 * ring lock in that mode.
+	 */
+	spinlock_t io_lock;
 	struct mutex mutex;
 	struct xenbus_device *xbdev;
 	struct gendisk *gd;
@@ -265,6 +280,19 @@  static DEFINE_SPINLOCK(minor_lock);
 
 #define GREFS(_psegs)	((_psegs) * GRANTS_PER_PSEG)
 
+/* Macro to save error status */
+#define BLKIF_REQ_PUT_ERROR_STATUS(req, error, status)		\
+	do {							\
+		if (blkfront_use_blk_mq)			\
+			blkif_req(req)->error = status;		\
+		else						\
+			error = status;				\
+	} while (0)
+
+/* Macro to retrieve error status */
+#define BLKIF_REQ_GET_ERROR_STATUS(req, error) \
+	((blkfront_use_blk_mq) ? blkif_req(req)->error : error)
+
 static int blkfront_setup_indirect(struct blkfront_ring_info *rinfo);
 static void blkfront_gather_backend_features(struct blkfront_info *info);
 static int negotiate_mq(struct blkfront_info *info);
@@ -895,6 +923,62 @@  static inline bool blkif_request_flush_invalid(struct request *req,
 		 !info->feature_fua));
 }
 
+static inline void blkif_complete_request(struct request *req, int error)
+{
+	if (blkfront_use_blk_mq)
+		blk_mq_complete_request(req);
+	else
+		__blk_end_request_all(req, error);
+}
+
+/*
+ * do_blkif_request
+ * read a block; request is in a request queue
+ */
+static void do_blkif_request(struct request_queue *rq)
+{
+	struct blkfront_info *info = NULL;
+	struct request *req;
+	int queued;
+
+	pr_debug("Entered do_blkif_request\n");
+
+	queued = 0;
+
+	while ((req = blk_peek_request(rq)) != NULL) {
+		info = req->rq_disk->private_data;
+
+		if (RING_FULL(&info->rinfo[FIRST_RING_ID].ring))
+			goto wait;
+
+		blk_start_request(req);
+
+		if (blkif_request_flush_invalid(req, info)) {
+			__blk_end_request_all(req, BLK_STS_NOTSUPP);
+			continue;
+		}
+
+		pr_debug("do_blk req %p: cmd_flags %u, sec %lx, "
+			"(%u/%u) [%s]\n",
+			req, req->cmd_flags, (unsigned long)blk_rq_pos(req),
+			blk_rq_cur_sectors(req), blk_rq_sectors(req),
+			rq_data_dir(req) ? "write" : "read");
+
+		if (blkif_queue_request(req, &info->rinfo[FIRST_RING_ID])) {
+			blk_requeue_request(rq, req);
+wait:
+			/* Avoid pointless unplugs. */
+			blk_stop_queue(rq);
+			break;
+		}
+
+		queued++;
+	}
+
+	if(queued != 0)
+		flush_requests(&info->rinfo[FIRST_RING_ID]);
+}
+
 static blk_status_t blkif_queue_rq(struct blk_mq_hw_ctx *hctx,
 			  const struct blk_mq_queue_data *qd)
 {
@@ -980,30 +1064,37 @@  static int xlvbd_init_blk_queue(struct gendisk *gd, u16 sector_size,
 	struct request_queue *rq;
 	struct blkfront_info *info = gd->private_data;
 
-	memset(&info->tag_set, 0, sizeof(info->tag_set));
-	info->tag_set.ops = &blkfront_mq_ops;
-	info->tag_set.nr_hw_queues = info->nr_rings;
-	if (HAS_EXTRA_REQ && info->max_indirect_segments == 0) {
-		/*
-		 * When indirect descriptior is not supported, the I/O request
-		 * will be split between multiple request in the ring.
-		 * To avoid problems when sending the request, divide by
-		 * 2 the depth of the queue.
-		 */
-		info->tag_set.queue_depth =  BLK_RING_SIZE(info) / 2;
-	} else
-		info->tag_set.queue_depth = BLK_RING_SIZE(info);
-	info->tag_set.numa_node = NUMA_NO_NODE;
-	info->tag_set.flags = BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_SG_MERGE;
-	info->tag_set.cmd_size = sizeof(struct blkif_req);
-	info->tag_set.driver_data = info;
-
-	if (blk_mq_alloc_tag_set(&info->tag_set))
-		return -EINVAL;
-	rq = blk_mq_init_queue(&info->tag_set);
-	if (IS_ERR(rq)) {
-		blk_mq_free_tag_set(&info->tag_set);
-		return PTR_ERR(rq);
+	if (blkfront_use_blk_mq) {
+		memset(&info->tag_set, 0, sizeof(info->tag_set));
+		info->tag_set.ops = &blkfront_mq_ops;
+		info->tag_set.nr_hw_queues = info->nr_rings;
+		if (HAS_EXTRA_REQ && info->max_indirect_segments == 0) {
+			/*
+			 * When indirect descriptior is not supported, the I/O request
+			 * will be split between multiple request in the ring.
+			 * To avoid problems when sending the request, divide by
+			 * 2 the depth of the queue.
+			 */
+			info->tag_set.queue_depth =  BLK_RING_SIZE(info) / 2;
+		} else
+			info->tag_set.queue_depth = BLK_RING_SIZE(info);
+		info->tag_set.numa_node = NUMA_NO_NODE;
+		info->tag_set.flags = BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_SG_MERGE;
+		info->tag_set.cmd_size = sizeof(struct blkif_req);
+		info->tag_set.driver_data = info;
+
+		if (blk_mq_alloc_tag_set(&info->tag_set))
+			return -EINVAL;
+		rq = blk_mq_init_queue(&info->tag_set);
+		if (IS_ERR(rq)) {
+			blk_mq_free_tag_set(&info->tag_set);
+			return PTR_ERR(rq);
+		}
+	} else {
+		spin_lock_init(&info->io_lock);
+		rq = blk_init_queue(do_blkif_request, &info->io_lock);
+		if (IS_ERR(rq))
+			return PTR_ERR(rq);
 	}
 
 	rq->queuedata = info;
@@ -1202,21 +1293,29 @@  static int xlvbd_alloc_gendisk(blkif_sector_t capacity,
 static void xlvbd_release_gendisk(struct blkfront_info *info)
 {
 	unsigned int minor, nr_minors, i;
+	unsigned long flags;
 
 	if (info->rq == NULL)
 		return;
 
 	/* No more blkif_request(). */
-	blk_mq_stop_hw_queues(info->rq);
+	if (blkfront_use_blk_mq) {
+		blk_mq_stop_hw_queues(info->rq);
 
-	for (i = 0; i < info->nr_rings; i++) {
-		struct blkfront_ring_info *rinfo = &info->rinfo[i];
+		for (i = 0; i < info->nr_rings; i++) {
+			struct blkfront_ring_info *rinfo = &info->rinfo[i];
 
-		/* No more gnttab callback work. */
-		gnttab_cancel_free_callback(&rinfo->callback);
+			/* No more gnttab callback work. */
+			gnttab_cancel_free_callback(&rinfo->callback);
 
-		/* Flush gnttab callback work. Must be done with no locks held. */
-		flush_work(&rinfo->work);
+			/* Flush gnttab callback work. Must be done with no locks held. */
+			flush_work(&rinfo->work);
+		}
+	} else {
+		spin_lock_irqsave(&info->io_lock, flags);
+		blk_stop_queue(info->rq);
+		gnttab_cancel_free_callback(&info->rinfo[FIRST_RING_ID].callback);
+		spin_unlock_irqrestore(&info->io_lock, flags);
 	}
 
 	del_gendisk(info->gd);
@@ -1226,7 +1325,8 @@  static void xlvbd_release_gendisk(struct blkfront_info *info)
 	xlbd_release_minors(minor, nr_minors);
 
 	blk_cleanup_queue(info->rq);
-	blk_mq_free_tag_set(&info->tag_set);
+	if (blkfront_use_blk_mq)
+		blk_mq_free_tag_set(&info->tag_set);
 	info->rq = NULL;
 
 	put_disk(info->gd);
@@ -1238,17 +1338,31 @@  static inline void kick_pending_request_queues_locked(struct blkfront_ring_info
 {
 	if (unlikely(rinfo->dev_info->connected == BLKIF_STATE_FREEZING))
                 return;
-	if (!RING_FULL(&rinfo->ring))
+
+	if (RING_FULL(&rinfo->ring))
+		return;
+
+	if (blkfront_use_blk_mq) {
 		blk_mq_start_stopped_hw_queues(rinfo->dev_info->rq, true);
+	} else {
+		/* Re-enable calldowns */
+		blk_start_queue(rinfo->dev_info->rq);
+		/* Kick things off immediately */
+		do_blkif_request(rinfo->dev_info->rq);
+	}
 }
 
 static void kick_pending_request_queues(struct blkfront_ring_info *rinfo)
 {
 	unsigned long flags;
+	struct blkfront_info *info = rinfo->dev_info;
+	spinlock_t *lock;
 
-	spin_lock_irqsave(&rinfo->ring_lock, flags);
+	lock = blkfront_use_blk_mq ? &rinfo->ring_lock : &info->io_lock;
+
+	spin_lock_irqsave(lock, flags);
 	kick_pending_request_queues_locked(rinfo);
-	spin_unlock_irqrestore(&rinfo->ring_lock, flags);
+	spin_unlock_irqrestore(lock, flags);
 }
 
 static void blkif_restart_queue(struct work_struct *work)
@@ -1259,6 +1373,7 @@  static void blkif_restart_queue(struct work_struct *work)
 		kick_pending_request_queues(rinfo);
 }
 
+/* Must be called with per vbd lock held if the frontend uses request-based */
 static void blkif_free_ring(struct blkfront_ring_info *rinfo)
 {
 	struct grant *persistent_gnt, *n;
@@ -1341,6 +1456,9 @@  static void blkif_free_ring(struct blkfront_ring_info *rinfo)
 	/* No more gnttab callback work. */
 	gnttab_cancel_free_callback(&rinfo->callback);
 
+	if (!blkfront_use_blk_mq)
+		spin_unlock_irq(&info->io_lock);
+
 	/* Flush gnttab callback work. Must be done with no locks held. */
 	flush_work(&rinfo->work);
 
@@ -1362,11 +1480,18 @@  static void blkif_free_ring(struct blkfront_ring_info *rinfo)
 static void blkif_free(struct blkfront_info *info, int suspend)
 {
 	/* Prevent new requests being issued until we fix things up. */
+	if (!blkfront_use_blk_mq)
+		spin_lock_irq(&info->io_lock);
+
 	info->connected = suspend ?
 		BLKIF_STATE_SUSPENDED : BLKIF_STATE_DISCONNECTED;
 	/* No more blkif_request(). */
-	if (info->rq)
-		blk_mq_stop_hw_queues(info->rq);
+	if (info->rq) {
+		if (blkfront_use_blk_mq)
+			blk_mq_stop_hw_queues(info->rq);
+		else
+			blk_stop_queue(info->rq);
+	}
 
 	__blkif_free(info);
 }
@@ -1577,13 +1702,17 @@  static irqreturn_t blkif_interrupt(int irq, void *dev_id)
 	unsigned long flags;
 	struct blkfront_ring_info *rinfo = (struct blkfront_ring_info *)dev_id;
 	struct blkfront_info *info = rinfo->dev_info;
+	spinlock_t *lock;
+	int error = BLK_STS_OK;
 
 	if (unlikely(info->connected != BLKIF_STATE_CONNECTED)) {
 		if (info->connected != BLKIF_STATE_FREEZING)
 			return IRQ_HANDLED;
 	}
 
-	spin_lock_irqsave(&rinfo->ring_lock, flags);
+	lock = blkfront_use_blk_mq ? &rinfo->ring_lock : &info->io_lock;
+
+	spin_lock_irqsave(lock, flags);
  again:
 	rp = rinfo->ring.sring->rsp_prod;
 	rmb(); /* Ensure we see queued responses up to 'rp'. */
@@ -1623,9 +1752,9 @@  static irqreturn_t blkif_interrupt(int irq, void *dev_id)
 		}
 
 		if (bret->status == BLKIF_RSP_OKAY)
-			blkif_req(req)->error = BLK_STS_OK;
+			BLKIF_REQ_PUT_ERROR_STATUS(req, error, BLK_STS_OK);
 		else
-			blkif_req(req)->error = BLK_STS_IOERR;
+			BLKIF_REQ_PUT_ERROR_STATUS(req, error, BLK_STS_IOERR);
 
 		switch (bret->operation) {
 		case BLKIF_OP_DISCARD:
@@ -1633,7 +1762,7 @@  static irqreturn_t blkif_interrupt(int irq, void *dev_id)
 				struct request_queue *rq = info->rq;
 				printk(KERN_WARNING "blkfront: %s: %s op failed\n",
 					   info->gd->disk_name, op_name(bret->operation));
-				blkif_req(req)->error = BLK_STS_NOTSUPP;
+				BLKIF_REQ_PUT_ERROR_STATUS(req, error, BLK_STS_NOTSUPP);
 				info->feature_discard = 0;
 				info->feature_secdiscard = 0;
 				queue_flag_clear(QUEUE_FLAG_DISCARD, rq);
@@ -1645,17 +1774,19 @@  static irqreturn_t blkif_interrupt(int irq, void *dev_id)
 			if (unlikely(bret->status == BLKIF_RSP_EOPNOTSUPP)) {
 				printk(KERN_WARNING "blkfront: %s: %s op failed\n",
 				       info->gd->disk_name, op_name(bret->operation));
-				blkif_req(req)->error = BLK_STS_NOTSUPP;
+				BLKIF_REQ_PUT_ERROR_STATUS(req, error, BLK_STS_NOTSUPP);
 			}
 			if (unlikely(bret->status == BLKIF_RSP_ERROR &&
 				     rinfo->shadow[id].req.u.rw.nr_segments == 0)) {
 				printk(KERN_WARNING "blkfront: %s: empty %s op failed\n",
 				       info->gd->disk_name, op_name(bret->operation));
-				blkif_req(req)->error = BLK_STS_NOTSUPP;
+				BLKIF_REQ_PUT_ERROR_STATUS(req, error, BLK_STS_NOTSUPP);
 			}
-			if (unlikely(blkif_req(req)->error)) {
-				if (blkif_req(req)->error == BLK_STS_NOTSUPP)
-					blkif_req(req)->error = BLK_STS_OK;
+			if (unlikely(BLKIF_REQ_GET_ERROR_STATUS(req, error))) {
+				if (BLKIF_REQ_GET_ERROR_STATUS(req, error)
+						== BLK_STS_NOTSUPP)
+					BLKIF_REQ_PUT_ERROR_STATUS(req, error,
+						BLK_STS_OK);
 				info->feature_fua = 0;
 				info->feature_flush = 0;
 				xlvbd_flush(info);
@@ -1672,7 +1803,7 @@  static irqreturn_t blkif_interrupt(int irq, void *dev_id)
 			BUG();
 		}
 
-		blk_mq_complete_request(req);
+		blkif_complete_request(req, BLKIF_REQ_GET_ERROR_STATUS(req, error));
 	}
 
 	rinfo->ring.rsp_cons = i;
@@ -1687,7 +1818,7 @@  static irqreturn_t blkif_interrupt(int irq, void *dev_id)
 
 	kick_pending_request_queues_locked(rinfo);
 
-	spin_unlock_irqrestore(&rinfo->ring_lock, flags);
+	spin_unlock_irqrestore(lock, flags);
 
 	return IRQ_HANDLED;
 }
@@ -1928,8 +2059,11 @@  static int negotiate_mq(struct blkfront_info *info)
 	backend_max_queues = xenbus_read_unsigned(info->xbdev->otherend,
 						  "multi-queue-max-queues", 1);
 	info->nr_rings = min(backend_max_queues, xen_blkif_max_queues);
-	/* We need at least one ring. */
-	if (!info->nr_rings)
+	/*
+	 * We need at least one ring. Also, do not allow to have multiple rings if blk-mq is
+	 * not used.
+	 */
+	if (!info->nr_rings || !blkfront_use_blk_mq)
 		info->nr_rings = 1;
 
 	info->rinfo = kzalloc(sizeof(struct blkfront_ring_info) * info->nr_rings, GFP_KERNEL);
@@ -1946,7 +2080,8 @@  static int negotiate_mq(struct blkfront_info *info)
 		INIT_LIST_HEAD(&rinfo->grants);
 		rinfo->dev_info = info;
 		INIT_WORK(&rinfo->work, blkif_restart_queue);
-		spin_lock_init(&rinfo->ring_lock);
+		if (blkfront_use_blk_mq)
+			spin_lock_init(&rinfo->ring_lock);
 	}
 	return 0;
 }
@@ -2047,6 +2182,10 @@  static int blkif_recover(struct blkfront_info *info)
 	}
 	xenbus_switch_state(info->xbdev, XenbusStateConnected);
 
+	/* blk_requeue_request below must be called with queue lock held */
+	if (!blkfront_use_blk_mq)
+		spin_lock_irq(&info->io_lock);
+
 	/* Now safe for us to use the shared ring */
 	info->connected = BLKIF_STATE_CONNECTED;
 
@@ -2055,20 +2194,34 @@  static int blkif_recover(struct blkfront_info *info)
 
 		rinfo = &info->rinfo[r_index];
 		/* Kick any other new requests queued since we resumed */
-		kick_pending_request_queues(rinfo);
+		if (blkfront_use_blk_mq)
+			kick_pending_request_queues(rinfo);
+		else
+			kick_pending_request_queues_locked(rinfo);
 	}
 
-	if (frozen)
+	if (frozen) {
+		if (!blkfront_use_blk_mq)
+			spin_unlock_irq(&info->io_lock);
 		return 0;
+	}
 
 	list_for_each_entry_safe(req, n, &info->requests, queuelist) {
 		/* Requeue pending requests (flush or discard) */
 		list_del_init(&req->queuelist);
 		BUG_ON(req->nr_phys_segments > segs);
-		blk_mq_requeue_request(req, false);
+		if (blkfront_use_blk_mq)
+			blk_mq_requeue_request(req, false);
+		else
+			blk_requeue_request(info->rq, req);
+	}
+
+	if (blkfront_use_blk_mq) {
+		blk_mq_start_stopped_hw_queues(info->rq, true);
+		blk_mq_kick_requeue_list(info->rq);
+	} else {
+		spin_unlock_irq(&info->io_lock);
 	}
-	blk_mq_start_stopped_hw_queues(info->rq, true);
-	blk_mq_kick_requeue_list(info->rq);
 
 	while ((bio = bio_list_pop(&info->bio_list)) != NULL) {
 		/* Traverse the list of pending bios and re-queue them */
@@ -2125,14 +2278,47 @@  static int blkfront_resume(struct xenbus_device *dev)
 			merge_bio.tail = shadow[j].request->biotail;
 			bio_list_merge(&info->bio_list, &merge_bio);
 			shadow[j].request->bio = NULL;
-			blk_mq_end_request(shadow[j].request, BLK_STS_OK);
+			if (blkfront_use_blk_mq)
+				blk_mq_end_request(shadow[j].request, BLK_STS_OK);
+			else
+				blk_end_request_all(shadow[j].request, BLK_STS_OK);
 		}
 	}
 
+	if (!blkfront_use_blk_mq) {
+		struct request *req;
+		struct bio_list merge_bio;
+
+		/*
+		 * Empty the queue, this is important because we might have
+		 * requests in the queue with more segments than what we
+		 * can handle now.
+		 */
+		spin_lock_irq(&info->io_lock);
+		while ((req = blk_fetch_request(info->rq)) != NULL) {
+			if (req_op(req) == REQ_OP_FLUSH ||
+			    req_op(req) == REQ_OP_DISCARD ||
+			    req_op(req) == REQ_OP_SECURE_ERASE ||
+			    req->cmd_flags & REQ_FUA) {
+				list_add(&req->queuelist, &info->requests);
+				continue;
+			}
+			merge_bio.head = req->bio;
+			merge_bio.tail = req->biotail;
+			bio_list_merge(&info->bio_list, &merge_bio);
+			req->bio = NULL;
+			if (req_op(req) == REQ_OP_FLUSH ||
+			    req->cmd_flags & REQ_FUA)
+				pr_alert("diskcache flush request found!\n");
+			__blk_end_request_all(req, BLK_STS_OK);
+		}
+		spin_unlock_irq(&info->io_lock);
+	}
+
 	blkif_free(info, info->connected == BLKIF_STATE_CONNECTED);
 
 	err = talk_to_blkback(dev, info);
-	if (!err)
+	if (!err && blkfront_use_blk_mq)
 		blk_mq_update_nr_hw_queues(&info->tag_set, info->nr_rings);
 
 	/*
@@ -2485,6 +2671,8 @@  static void blkback_changed(struct xenbus_device *dev,
 	case XenbusStateClosed:
 		if (dev->state == XenbusStateClosed) {
 			if (info->connected == BLKIF_STATE_FREEZING) {
+				if (!blkfront_use_blk_mq)
+					spin_lock_irq(&info->io_lock);
 				__blkif_free(info);
 				info->connected = BLKIF_STATE_FROZEN;
 				complete(&info->wait_backend_disconnected);
@@ -2661,14 +2849,25 @@  static int blkfront_freeze(struct xenbus_device *dev)
 	int err = 0;
 	
 	info->connected = BLKIF_STATE_FREEZING;
+
+	if (blkfront_use_blk_mq) {
+		blk_mq_stop_hw_queues(info->rq);
 	
-	blk_mq_stop_hw_queues(info->rq);
-	
-	for (i = 0; i < info->nr_rings; i++) {
-		rinfo = &info->rinfo[i];
+		for (i = 0; i < info->nr_rings; i++) {
+			rinfo = &info->rinfo[i];
+
+			gnttab_cancel_free_callback(&rinfo->callback);
+			flush_work(&rinfo->work);
+		}
+	} else {
+		spin_lock_irq(&info->io_lock);
+		blk_stop_queue(info->rq);
+		gnttab_cancel_free_callback(
+				&info->rinfo[FIRST_RING_ID].callback);
+		spin_unlock_irq(&info->io_lock);
 
-		gnttab_cancel_free_callback(&rinfo->callback);
-		flush_work(&rinfo->work);
+		blk_sync_queue(info->rq);
+		flush_work(&info->rinfo[FIRST_RING_ID].work);
 	}
 	
 	for (i = 0; i < info->nr_rings; i++) {
@@ -2680,7 +2879,8 @@  static int blkfront_freeze(struct xenbus_device *dev)
                 rinfo = &info->rinfo[i];
                 ring = &rinfo->ring;
 
-                lock = &rinfo->ring_lock;
+                lock = blkfront_use_blk_mq ?
+			&rinfo->ring_lock : &info->io_lock;
 
                 ring_timeout = jiffies +
                         msecs_to_jiffies(req_timeout_ms * RING_SIZE(ring));
@@ -2734,7 +2934,9 @@  static int blkfront_restore(struct xenbus_device *dev)
 	err = talk_to_blkback(dev, info);
 	if (err)
 		goto out;
-	blk_mq_update_nr_hw_queues(&info->tag_set, info->nr_rings);
+
+	if (blkfront_use_blk_mq)
+		blk_mq_update_nr_hw_queues(&info->tag_set, info->nr_rings);
 
 out:
 	return err;