Commit 69409854 authored by Yufen Yu's avatar Yufen Yu Committed by Greg Kroah-Hartman

dm mpath: fix missing call of path selector type->end_io

[ Upstream commit 5de719e3 ]

After commit 396eaf21 ("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 driver 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 times, while type->end_io is only
called when IO complete.

In fact, even without commit 396eaf21, setup_clone() failure can
also cause tio requeue and associated missed call to type->end_io.

The service-time path selector selects path based on in_flight_size,
which is increased by st_start_io() and decreased by st_end_io().
Missed calls to st_end_io() can lead to in_flight_size count error and
will cause the selector to make the wrong choice.  In addition,
queue-length path selector will also be affected.

To fix the problem, call type->end_io in ->release_clone_rq before tio
requeue.  map_info is passed to ->release_clone_rq() for map_request()
error path that result in requeue.

Fixes: 396eaf21 ("blk-mq: improve DM's blk-mq IO merging via blk_insert_cloned_request feedback")
Cc: stable@vger.kernl.org
Signed-off-by: default avatarYufen Yu <yuyufen@huawei.com>
Signed-off-by: default avatarMike Snitzer <snitzer@redhat.com>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent 0fe09701
...@@ -554,8 +554,23 @@ static int multipath_clone_and_map(struct dm_target *ti, struct request *rq, ...@@ -554,8 +554,23 @@ static int multipath_clone_and_map(struct dm_target *ti, struct request *rq,
return DM_MAPIO_REMAPPED; return DM_MAPIO_REMAPPED;
} }
static void multipath_release_clone(struct request *clone) static void multipath_release_clone(struct request *clone,
union map_info *map_context)
{ {
if (unlikely(map_context)) {
/*
* non-NULL map_context means caller is still map
* method; must undo multipath_clone_and_map()
*/
struct dm_mpath_io *mpio = get_mpio(map_context);
struct pgpath *pgpath = mpio->pgpath;
if (pgpath && pgpath->pg->ps.type->end_io)
pgpath->pg->ps.type->end_io(&pgpath->pg->ps,
&pgpath->path,
mpio->nr_bytes);
}
blk_put_request(clone); blk_put_request(clone);
} }
......
...@@ -219,7 +219,7 @@ static void dm_end_request(struct request *clone, blk_status_t error) ...@@ -219,7 +219,7 @@ static void dm_end_request(struct request *clone, blk_status_t error)
struct request *rq = tio->orig; struct request *rq = tio->orig;
blk_rq_unprep_clone(clone); blk_rq_unprep_clone(clone);
tio->ti->type->release_clone_rq(clone); tio->ti->type->release_clone_rq(clone, NULL);
rq_end_stats(md, rq); rq_end_stats(md, rq);
if (!rq->q->mq_ops) if (!rq->q->mq_ops)
...@@ -270,7 +270,7 @@ static void dm_requeue_original_request(struct dm_rq_target_io *tio, bool delay_ ...@@ -270,7 +270,7 @@ static void dm_requeue_original_request(struct dm_rq_target_io *tio, bool delay_
rq_end_stats(md, rq); rq_end_stats(md, rq);
if (tio->clone) { if (tio->clone) {
blk_rq_unprep_clone(tio->clone); blk_rq_unprep_clone(tio->clone);
tio->ti->type->release_clone_rq(tio->clone); tio->ti->type->release_clone_rq(tio->clone, NULL);
} }
if (!rq->q->mq_ops) if (!rq->q->mq_ops)
...@@ -495,7 +495,7 @@ static int map_request(struct dm_rq_target_io *tio) ...@@ -495,7 +495,7 @@ static int map_request(struct dm_rq_target_io *tio)
case DM_MAPIO_REMAPPED: case DM_MAPIO_REMAPPED:
if (setup_clone(clone, rq, tio, GFP_ATOMIC)) { if (setup_clone(clone, rq, tio, GFP_ATOMIC)) {
/* -ENOMEM */ /* -ENOMEM */
ti->type->release_clone_rq(clone); ti->type->release_clone_rq(clone, &tio->info);
return DM_MAPIO_REQUEUE; return DM_MAPIO_REQUEUE;
} }
...@@ -505,7 +505,7 @@ static int map_request(struct dm_rq_target_io *tio) ...@@ -505,7 +505,7 @@ static int map_request(struct dm_rq_target_io *tio)
ret = dm_dispatch_clone_request(clone, rq); ret = dm_dispatch_clone_request(clone, rq);
if (ret == BLK_STS_RESOURCE || ret == BLK_STS_DEV_RESOURCE) { if (ret == BLK_STS_RESOURCE || ret == BLK_STS_DEV_RESOURCE) {
blk_rq_unprep_clone(clone); blk_rq_unprep_clone(clone);
tio->ti->type->release_clone_rq(clone); tio->ti->type->release_clone_rq(clone, &tio->info);
tio->clone = NULL; tio->clone = NULL;
if (!rq->q->mq_ops) if (!rq->q->mq_ops)
r = DM_MAPIO_DELAY_REQUEUE; r = DM_MAPIO_DELAY_REQUEUE;
......
...@@ -136,7 +136,8 @@ static int io_err_clone_and_map_rq(struct dm_target *ti, struct request *rq, ...@@ -136,7 +136,8 @@ static int io_err_clone_and_map_rq(struct dm_target *ti, struct request *rq,
return DM_MAPIO_KILL; return DM_MAPIO_KILL;
} }
static void io_err_release_clone_rq(struct request *clone) static void io_err_release_clone_rq(struct request *clone,
union map_info *map_context)
{ {
} }
......
...@@ -62,7 +62,8 @@ typedef int (*dm_clone_and_map_request_fn) (struct dm_target *ti, ...@@ -62,7 +62,8 @@ typedef int (*dm_clone_and_map_request_fn) (struct dm_target *ti,
struct request *rq, struct request *rq,
union map_info *map_context, union map_info *map_context,
struct request **clone); struct request **clone);
typedef void (*dm_release_clone_request_fn) (struct request *clone); typedef void (*dm_release_clone_request_fn) (struct request *clone,
union map_info *map_context);
/* /*
* Returns: * Returns:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment