[PATCH] block: consider merge of segments when merge bio into rq

From: Jianchao Wang
Date: Fri Sep 15 2017 - 03:22:03 EST


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.wang@xxxxxxxxxx>
---
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