[dm-devel] [PATCH 7/7] dm-mpath: Fix a race condition in __multipath_map()

Bart Van Assche bart.vanassche at sandisk.com
Tue Nov 15 23:35:23 UTC 2016


If a single-queue dm device is stacked on top of multi-queue block
devices and map_tio_request() is called while there are no paths then
the request will be prepared for a single-queue path. If a path is
added after a request was prepared and before __multipath_map() is
called return DM_MAPIO_REQUEUE such that it gets unprepared and
reprepared as a blk-mq request.

Signed-off-by: Bart Van Assche <bart.vanassche at sandisk.com>
---
 drivers/md/dm-mpath.c | 21 ++++++++++++++++-----
 1 file changed, 16 insertions(+), 5 deletions(-)

diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index 7559537..6b20349 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -541,6 +541,7 @@ static int __multipath_map(struct dm_target *ti, struct request *clone,
 	size_t nr_bytes = clone ? blk_rq_bytes(clone) : blk_rq_bytes(rq);
 	struct pgpath *pgpath;
 	struct block_device *bdev;
+	struct request_queue *q;
 	struct dm_mpath_io *mpio;
 
 	/* Do we need to select a new pgpath? */
@@ -558,6 +559,18 @@ static int __multipath_map(struct dm_target *ti, struct request *clone,
 		return r;
 	}
 
+	bdev = pgpath->path.dev->bdev;
+	q = bdev_get_queue(bdev);
+
+	/*
+	 * When a request is prepared if there are no paths it may happen that
+	 * the request was prepared for a single-queue path and that a
+	 * multiqueue path is added before __multipath_map() is called. If
+	 * that happens requeue to trigger unprepare and reprepare.
+	 */
+	if ((clone && q->mq_ops) || (!clone && !q->mq_ops))
+		return r;
+
 	mpio = set_mpio(m, map_context);
 	if (!mpio)
 		/* ENOMEM, requeue */
@@ -566,22 +579,20 @@ static int __multipath_map(struct dm_target *ti, struct request *clone,
 	mpio->pgpath = pgpath;
 	mpio->nr_bytes = nr_bytes;
 
-	bdev = pgpath->path.dev->bdev;
-
 	if (clone) {
 		/*
 		 * Old request-based interface: allocated clone is passed in.
 		 * Used by: .request_fn stacked on .request_fn path(s).
 		 */
-		clone->q = bdev_get_queue(bdev);
+		clone->q = q;
 	} else {
 		/*
 		 * blk-mq request-based interface; used by both:
 		 * .request_fn stacked on blk-mq path(s) and
 		 * blk-mq stacked on blk-mq path(s).
 		 */
-		clone = blk_mq_alloc_request(bdev_get_queue(bdev),
-					rq_data_dir(rq), BLK_MQ_REQ_NOWAIT);
+		clone = blk_mq_alloc_request(q, rq_data_dir(rq),
+					     BLK_MQ_REQ_NOWAIT);
 		if (IS_ERR(clone)) {
 			/* EBUSY, ENODEV or EWOULDBLOCK; requeue */
 			clear_request_fn_mpio(m, map_context);
-- 
2.10.1




More information about the dm-devel mailing list