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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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




[Index of Archives]     [Linux RAID]     [Linux SCSI]     [Linux ATA RAID]     [IDE]     [Linux Wireless]     [Linux Kernel]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Device Mapper]

  Powered by Linux