Commit 8677142710516d986d932d6f1fba7be8382c1fec

Authored by FUJITA Tomonori
Committed by Jens Axboe
1 parent 0fc71e3d65

block: fix nr_phys_segments miscalculation bug

This fixes the bug reported by Nikanth Karthikesan <knikanth@suse.de>:

http://lkml.org/lkml/2008/10/2/203

The root cause of the bug is that blk_phys_contig_segment
miscalculates q->max_segment_size.

blk_phys_contig_segment checks:

req->biotail->bi_size + next_req->bio->bi_size > q->max_segment_size

But blk_recalc_rq_segments might expect that req->biotail and the
previous bio in the req are supposed be merged into one
segment. blk_recalc_rq_segments might also expect that next_req->bio
and the next bio in the next_req are supposed be merged into one
segment. In such case, we merge two requests that can't be merged
here. Later, blk_rq_map_sg gives more segments than it should.

We need to keep track of segment size in blk_recalc_rq_segments and
use it to see if two requests can be merged. This patch implements it
in the similar way that we used to do for hw merging (virtual
merging).

Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
Signed-off-by: Jens Axboe <jens.axboe@oracle.com>

Showing 2 changed files with 25 additions and 2 deletions Side-by-side Diff

... ... @@ -77,12 +77,20 @@
77 77 continue;
78 78 }
79 79 new_segment:
  80 + if (nr_phys_segs == 1 && seg_size > rq->bio->bi_seg_front_size)
  81 + rq->bio->bi_seg_front_size = seg_size;
  82 +
80 83 nr_phys_segs++;
81 84 bvprv = bv;
82 85 seg_size = bv->bv_len;
83 86 highprv = high;
84 87 }
85 88  
  89 + if (nr_phys_segs == 1 && seg_size > rq->bio->bi_seg_front_size)
  90 + rq->bio->bi_seg_front_size = seg_size;
  91 + if (seg_size > rq->biotail->bi_seg_back_size)
  92 + rq->biotail->bi_seg_back_size = seg_size;
  93 +
86 94 rq->nr_phys_segments = nr_phys_segs;
87 95 }
88 96  
... ... @@ -106,7 +114,8 @@
106 114 if (!test_bit(QUEUE_FLAG_CLUSTER, &q->queue_flags))
107 115 return 0;
108 116  
109   - if (bio->bi_size + nxt->bi_size > q->max_segment_size)
  117 + if (bio->bi_seg_back_size + nxt->bi_seg_front_size >
  118 + q->max_segment_size)
110 119 return 0;
111 120  
112 121 if (!bio_has_data(bio))
... ... @@ -309,6 +318,8 @@
309 318 struct request *next)
310 319 {
311 320 int total_phys_segments;
  321 + unsigned int seg_size =
  322 + req->biotail->bi_seg_back_size + next->bio->bi_seg_front_size;
312 323  
313 324 /*
314 325 * First check if the either of the requests are re-queued
315 326  
... ... @@ -324,8 +335,13 @@
324 335 return 0;
325 336  
326 337 total_phys_segments = req->nr_phys_segments + next->nr_phys_segments;
327   - if (blk_phys_contig_segment(q, req->biotail, next->bio))
  338 + if (blk_phys_contig_segment(q, req->biotail, next->bio)) {
  339 + if (req->nr_phys_segments == 1)
  340 + req->bio->bi_seg_front_size = seg_size;
  341 + if (next->nr_phys_segments == 1)
  342 + next->biotail->bi_seg_back_size = seg_size;
328 343 total_phys_segments--;
  344 + }
329 345  
330 346 if (total_phys_segments > q->max_phys_segments)
331 347 return 0;
... ... @@ -79,6 +79,13 @@
79 79  
80 80 unsigned int bi_size; /* residual I/O count */
81 81  
  82 + /*
  83 + * To keep track of the max segment size, we account for the
  84 + * sizes of the first and last mergeable segments in this bio.
  85 + */
  86 + unsigned int bi_seg_front_size;
  87 + unsigned int bi_seg_back_size;
  88 +
82 89 unsigned int bi_max_vecs; /* max bvl_vecs we can hold */
83 90  
84 91 unsigned int bi_comp_cpu; /* completion CPU */