When account the nr_phys_segments during merging bios into rq,
only consider segments merging in individual bio but not all
the bios in a rq. This leads to the bigger nr_phys_segments of
rq than the real one when the segments of bios in rq are
contiguous and mergeable. The nr_phys_segments of rq will exceed
max_segmets of q while the sectors of rq maybe far away from
the max_sectors of q.

Consider the segments merge when account nr_phys_segments of rq
during merging bio into rq. Promote the merging of small and
contiguous IO. In addition, it could eliminate the wasting of
scatterlist structure.

Signed-off-by: Jianchao Wang <jianchao.w.w...@oracle.com>
---
 block/blk-merge.c | 98 ++++++++++++++++++++++++++++++++++++-------------------
 1 file changed, 64 insertions(+), 34 deletions(-)

diff --git a/block/blk-merge.c b/block/blk-merge.c
index 14b6e37..b2f54fd 100644
--- a/block/blk-merge.c
+++ b/block/blk-merge.c
@@ -472,54 +472,60 @@ int blk_rq_map_sg(struct request_queue *q, struct request 
*rq,
 }
 EXPORT_SYMBOL(blk_rq_map_sg);
 
-static inline int ll_new_hw_segment(struct request_queue *q,
-                                   struct request *req,
-                                   struct bio *bio)
-{
-       int nr_phys_segs = bio_phys_segments(q, bio);
-
-       if (req->nr_phys_segments + nr_phys_segs > queue_max_segments(q))
-               goto no_merge;
-
-       if (blk_integrity_merge_bio(q, req, bio) == false)
-               goto no_merge;
-
-       /*
-        * This will form the start of a new hw segment.  Bump both
-        * counters.
-        */
-       req->nr_phys_segments += nr_phys_segs;
-       return 1;
-
-no_merge:
-       req_set_nomerge(q, req);
-       return 0;
-}
-
 int ll_back_merge_fn(struct request_queue *q, struct request *req,
                     struct bio *bio)
 {
+       unsigned int seg_size;
+       int total_nr_phys_segs;
+       bool contig;
+
        if (req_gap_back_merge(req, bio))
                return 0;
        if (blk_integrity_rq(req) &&
            integrity_req_gap_back_merge(req, bio))
                return 0;
        if (blk_rq_sectors(req) + bio_sectors(bio) >
-           blk_rq_get_max_sectors(req, blk_rq_pos(req))) {
-               req_set_nomerge(q, req);
-               return 0;
-       }
+           blk_rq_get_max_sectors(req, blk_rq_pos(req)))
+               goto no_merge;
+
        if (!bio_flagged(req->biotail, BIO_SEG_VALID))
                blk_recount_segments(q, req->biotail);
        if (!bio_flagged(bio, BIO_SEG_VALID))
                blk_recount_segments(q, bio);
 
-       return ll_new_hw_segment(q, req, bio);
+       if (blk_integrity_merge_bio(q, req, bio) == false)
+               goto no_merge;
+
+       seg_size = req->biotail->bi_seg_back_size + bio->bi_seg_front_size;
+       total_nr_phys_segs = req->nr_phys_segments + bio_phys_segments(q, bio);
+
+       contig = blk_phys_contig_segment(q, req->biotail, bio);
+       if (contig)
+               total_nr_phys_segs--;
+
+       if (unlikely(total_nr_phys_segs > queue_max_segments(q)))
+               goto no_merge;
+
+       if (contig) {
+               if (req->nr_phys_segments == 1)
+                       req->bio->bi_seg_front_size = seg_size;
+               if (bio->bi_phys_segments == 1)
+                       bio->bi_seg_back_size = seg_size;
+       }
+       req->nr_phys_segments = total_nr_phys_segs;
+       return 1;
+
+no_merge:
+       req_set_nomerge(q, req);
+       return 0;
 }
 
 int ll_front_merge_fn(struct request_queue *q, struct request *req,
                      struct bio *bio)
 {
+       unsigned int seg_size;
+       int total_nr_phys_segs;
+       bool contig;
 
        if (req_gap_front_merge(req, bio))
                return 0;
@@ -527,16 +533,40 @@ int ll_front_merge_fn(struct request_queue *q, struct 
request *req,
            integrity_req_gap_front_merge(req, bio))
                return 0;
        if (blk_rq_sectors(req) + bio_sectors(bio) >
-           blk_rq_get_max_sectors(req, bio->bi_iter.bi_sector)) {
-               req_set_nomerge(q, req);
-               return 0;
-       }
+           blk_rq_get_max_sectors(req, bio->bi_iter.bi_sector))
+               goto no_merge;
+
        if (!bio_flagged(bio, BIO_SEG_VALID))
                blk_recount_segments(q, bio);
        if (!bio_flagged(req->bio, BIO_SEG_VALID))
                blk_recount_segments(q, req->bio);
 
-       return ll_new_hw_segment(q, req, bio);
+       if (blk_integrity_merge_bio(q, req, bio) == false)
+               goto no_merge;
+
+       seg_size = req->bio->bi_seg_front_size + bio->bi_seg_back_size;
+       total_nr_phys_segs = req->nr_phys_segments + bio_phys_segments(q, bio);
+
+       contig = blk_phys_contig_segment(q, bio, req->bio);
+       if (contig)
+               total_nr_phys_segs--;
+
+       if (unlikely(total_nr_phys_segs > queue_max_segments(q)))
+               goto no_merge;
+
+       if (contig) {
+               if (req->nr_phys_segments == 1)
+                       req->biotail->bi_seg_back_size = seg_size;
+               if (bio->bi_phys_segments == 1)
+                       bio->bi_seg_front_size = seg_size;
+       }
+
+       req->nr_phys_segments = total_nr_phys_segs;
+       return 1;
+
+no_merge:
+       req_set_nomerge(q, req);
+       return 0;
 }
 
 /*
-- 
2.7.4

Reply via email to