[dm-devel] [PATCH] dm mpath: fix miss calling of path selector type->end_io

Yufen Yu yuyufen at huawei.com
Tue Apr 23 14:46:01 UTC 2019


After commit 396eaf21ee17 ("blk-mq: improve DM's blk-mq IO merging via
blk_insert_cloned_request feedback"), map_request() will requeue the tio
when issued clone request return BLK_STS_RESOURCE or BLK_STS_DEV_RESOURCE.

Thus, if device drive status is error, a tio may be requeued multiple times
until the return value is not DM_MAPIO_REQUEUE. That means type->start_io
may be called multiple tims, while type->end_io just be called when IO complete.

In fact, even without the commit, setup_clone() error also can make the
tio requeue and miss call type->end_io.

For service time path selector, it selects path based on in_flight_size,
which is increased by st_start_io() and decreased by st_end_io().
Missing call of end_io can lead to in_flight_size error and let the
selector make the wrong choice, even it will not choose one path forever.

This patch fixes the bug by adding a new interface end_stat() in target_type.
When map_request() return DM_MAPIO_REQUEUE, we call dm_end_stat() to
decrease the counter.

Initially, we attempt to avoid repeated calling for ->start_io by adding
a flag 'started' in mpio. We just call ->start_io when the flag is not
set and then set the flag after the call. It will be cleared after calling
->end_io. However, we have not find a suitable location to initialize mpio
(map_info->ptr) and clear the flag. dm_mq_init_request() looks like a
reasonable location to clear the flag by memset 0 for the region of
struct dm_mpath_io. But, we can not get the size of the region at that localtion.

Fixes: 396eaf21ee17 ("blk-mq: improve DM's blk-mq IO merging via blk_insert_cloned_request feedback")
Cc: Ming Lei <ming.lei at redhat.com>
Cc: Hou Tao <houtao1 at huawei.com>
Signed-off-by: Yufen Yu <yuyufen at huawei.com>
---
 drivers/md/dm-mpath.c         | 14 ++++++++++++++
 drivers/md/dm-rq.c            |  8 ++++++++
 include/linux/device-mapper.h |  3 +++
 3 files changed, 25 insertions(+)

diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index 2ee5e357a0a7..f1da094a1fa9 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -2006,6 +2006,19 @@ static int multipath_busy(struct dm_target *ti)
 	return busy;
 }
 
+static void multipath_end_stat(union map_info *map_context)
+{
+	struct dm_mpath_io *mpio = get_mpio(map_context);
+	struct pgpath *pgpath = mpio->pgpath;
+
+	if (pgpath) {
+		struct path_selector *ps = &pgpath->pg->ps;
+
+		if (ps->type->end_io)
+			ps->type->end_io(ps, &pgpath->path, mpio->nr_bytes);
+	}
+}
+
 /*-----------------------------------------------------------------
  * Module setup
  *---------------------------------------------------------------*/
@@ -2030,6 +2043,7 @@ static struct target_type multipath_target = {
 	.prepare_ioctl = multipath_prepare_ioctl,
 	.iterate_devices = multipath_iterate_devices,
 	.busy = multipath_busy,
+	.end_stat = multipath_end_stat,
 };
 
 static int __init dm_multipath_init(void)
diff --git a/drivers/md/dm-rq.c b/drivers/md/dm-rq.c
index b66745bd08bb..fe8fd9af5de8 100644
--- a/drivers/md/dm-rq.c
+++ b/drivers/md/dm-rq.c
@@ -487,6 +487,13 @@ static int dm_mq_init_request(struct blk_mq_tag_set *set, struct request *rq,
 	return 0;
 }
 
+static void dm_end_stat(struct dm_rq_target_io *tio)
+{
+	if (tio->ti && tio->ti->type->end_stat) {
+		tio->ti->type->end_stat(&tio->info);
+	}
+}
+
 static blk_status_t dm_mq_queue_rq(struct blk_mq_hw_ctx *hctx,
 			  const struct blk_mq_queue_data *bd)
 {
@@ -520,6 +527,7 @@ static blk_status_t dm_mq_queue_rq(struct blk_mq_hw_ctx *hctx,
 	if (map_request(tio) == DM_MAPIO_REQUEUE) {
 		/* Undo dm_start_request() before requeuing */
 		rq_end_stats(md, rq);
+		dm_end_stat(tio);
 		rq_completed(md);
 		return BLK_STS_RESOURCE;
 	}
diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h
index b0672756d056..0a61a5769fbb 100644
--- a/include/linux/device-mapper.h
+++ b/include/linux/device-mapper.h
@@ -97,6 +97,8 @@ typedef int (*dm_report_zones_fn) (struct dm_target *ti, sector_t sector,
 				   unsigned int *nr_zones,
 				   gfp_t gfp_mask);
 
+typedef void (*dm_end_stat_fn) (union map_info *map_context);
+
 /*
  * These iteration functions are typically used to check (and combine)
  * properties of underlying devices.
@@ -194,6 +196,7 @@ struct target_type {
 	dm_dax_direct_access_fn direct_access;
 	dm_dax_copy_iter_fn dax_copy_from_iter;
 	dm_dax_copy_iter_fn dax_copy_to_iter;
+	dm_end_stat_fn end_stat;
 
 	/* For internal device-mapper use. */
 	struct list_head list;
-- 
2.16.2.dirty




More information about the dm-devel mailing list