During requeue, block layer won't change the request any
more, such as no merge, so we can cache ti->clone and
let .clone_and_map_rq check if the cache can be hit.

Signed-off-by: Ming Lei <ming....@redhat.com>
---
 drivers/md/dm-mpath.c | 31 ++++++++++++++++++++++++-------
 drivers/md/dm-rq.c    | 41 +++++++++++++++++++++++++++++------------
 2 files changed, 53 insertions(+), 19 deletions(-)

diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index 9ee223170ee9..52e4730541fd 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -457,6 +457,11 @@ do {                                                       
                \
                 dm_noflush_suspending((m)->ti));                       \
 } while (0)
 
+static void multipath_release_clone(struct request *clone)
+{
+       blk_put_request(clone);
+}
+
 /*
  * Map cloned requests (request-based multipath)
  */
@@ -470,7 +475,7 @@ static int multipath_clone_and_map(struct dm_target *ti, 
struct request *rq,
        struct block_device *bdev;
        struct dm_mpath_io *mpio = get_mpio(map_context);
        struct request_queue *q;
-       struct request *clone;
+       struct request *clone = *__clone;
 
        /* Do we need to select a new pgpath? */
        pgpath = lockless_dereference(m->current_pgpath);
@@ -493,7 +498,23 @@ static int multipath_clone_and_map(struct dm_target *ti, 
struct request *rq,
 
        bdev = pgpath->path.dev->bdev;
        q = bdev_get_queue(bdev);
-       clone = blk_get_request(q, rq->cmd_flags | REQ_NOMERGE, GFP_ATOMIC);
+
+       /*
+        * This request may be from requeue path, and its clone
+        * may have been allocated before. We need to check
+        * if the cached clone can be hit.
+        */
+       if (clone) {
+               if (clone->q != q) {
+                       blk_rq_unprep_clone(clone);
+                       multipath_release_clone(clone);
+                       clone = NULL;
+               } else
+                       goto start_io;
+       }
+
+       if (!clone)
+               clone = blk_get_request(q, rq->cmd_flags | REQ_NOMERGE, 
GFP_ATOMIC);
        if (IS_ERR(clone)) {
                /* EBUSY, ENODEV or EWOULDBLOCK: requeue */
                bool queue_dying = blk_queue_dying(q);
@@ -520,6 +541,7 @@ static int multipath_clone_and_map(struct dm_target *ti, 
struct request *rq,
        clone->cmd_flags |= REQ_FAILFAST_TRANSPORT;
        *__clone = clone;
 
+ start_io:
        if (pgpath->pg->ps.type->start_io)
                pgpath->pg->ps.type->start_io(&pgpath->pg->ps,
                                              &pgpath->path,
@@ -527,11 +549,6 @@ static int multipath_clone_and_map(struct dm_target *ti, 
struct request *rq,
        return DM_MAPIO_REMAPPED;
 }
 
-static void multipath_release_clone(struct request *clone)
-{
-       blk_put_request(clone);
-}
-
 /*
  * Map cloned bios (bio-based multipath)
  */
diff --git a/drivers/md/dm-rq.c b/drivers/md/dm-rq.c
index 46f012185b43..2ef524bddd38 100644
--- a/drivers/md/dm-rq.c
+++ b/drivers/md/dm-rq.c
@@ -221,6 +221,12 @@ static void dm_end_request(struct request *clone, 
blk_status_t error)
        blk_rq_unprep_clone(clone);
        tio->ti->type->release_clone_rq(clone);
 
+       /*
+        * We move the clearing from tio_init in .queue_rq to here because
+        * tio->clone may be cached during requeue
+        */
+       tio->clone = NULL;
+
        rq_end_stats(md, rq);
        if (!rq->q->mq_ops)
                blk_end_request_all(rq, error);
@@ -267,11 +273,15 @@ static void dm_requeue_original_request(struct 
dm_rq_target_io *tio, bool delay_
        int rw = rq_data_dir(rq);
        unsigned long delay_ms = delay_requeue ? 100 : 0;
 
+       /*
+        * This request won't be changed any more during requeue,
+        * so we cache tio->clone and let .clone_and_map_rq decide
+        * to use the cached clone or allocate a new clone, and
+        * the cached clone has to be freed before allocating a
+        * new one.
+        */
+
        rq_end_stats(md, rq);
-       if (tio->clone) {
-               blk_rq_unprep_clone(tio->clone);
-               tio->ti->type->release_clone_rq(tio->clone);
-       }
 
        if (!rq->q->mq_ops)
                dm_old_requeue_request(rq, delay_ms);
@@ -448,7 +458,6 @@ static void init_tio(struct dm_rq_target_io *tio, struct 
request *rq,
 {
        tio->md = md;
        tio->ti = NULL;
-       tio->clone = NULL;
        tio->orig = rq;
        tio->error = 0;
        tio->completed = 0;
@@ -456,8 +465,12 @@ static void init_tio(struct dm_rq_target_io *tio, struct 
request *rq,
         * Avoid initializing info for blk-mq; it passes
         * target-specific data through info.ptr
         * (see: dm_mq_init_request)
+        *
+        * If tio->clone is cached during requeue, we don't
+        * clear tio->info, and delay the initialization
+        * to .clone_and_map_rq if the cache isn't hit.
         */
-       if (!md->init_tio_pdu)
+       if (!md->init_tio_pdu && !tio->clone)
                memset(&tio->info, 0, sizeof(tio->info));
        if (md->kworker_task)
                kthread_init_work(&tio->work, map_tio_request);
@@ -475,7 +488,8 @@ static int map_request(struct dm_rq_target_io *tio)
        struct dm_target *ti = tio->ti;
        struct mapped_device *md = tio->md;
        struct request *rq = tio->orig;
-       struct request *clone = NULL;
+       struct request *cache = tio->clone;
+       struct request *clone = cache;
 
        r = ti->type->clone_and_map_rq(ti, rq, &tio->info, &clone);
        switch (r) {
@@ -483,10 +497,13 @@ static int map_request(struct dm_rq_target_io *tio)
                /* The target has taken the I/O to submit by itself later */
                break;
        case DM_MAPIO_REMAPPED:
-               if (setup_clone(clone, rq, tio, GFP_ATOMIC)) {
-                       /* -ENOMEM */
-                       ti->type->release_clone_rq(clone);
-                       return DM_MAPIO_REQUEUE;
+               /* cache not hit or not cached */
+               if (!cache || clone != cache) {
+                       if (setup_clone(clone, rq, tio, GFP_ATOMIC)) {
+                               /* -ENOMEM */
+                               ti->type->release_clone_rq(clone);
+                               return DM_MAPIO_REQUEUE;
+                       }
                }
 
                /* The target has remapped the I/O so dispatch it */
@@ -555,7 +572,7 @@ static int __dm_rq_init_rq(struct mapped_device *md, struct 
request *rq)
         * be available in dm_mq_queue_rq.
         */
        tio->md = md;
-
+       tio->clone = NULL;
        if (md->init_tio_pdu) {
                /* target-specific per-io data is immediately after the tio */
                tio->info.ptr = tio + 1;
-- 
2.9.5

Reply via email to