From: Jens Axboe blk_recalc_rq_segments forgots to take ->max_segment_size into account and gladly merges segments bigger than we can support, thus underestimating the number of segments needed to fill it. Signed-off-by: Jens Axboe Signed-off-by: Andrew Morton --- 25-akpm/drivers/block/ll_rw_blk.c | 24 +++++++++++++++++++----- 1 files changed, 19 insertions(+), 5 deletions(-) diff -puN drivers/block/ll_rw_blk.c~fix-bad-segment-coalescing-in-blk_recalc_rq_segments drivers/block/ll_rw_blk.c --- 25/drivers/block/ll_rw_blk.c~fix-bad-segment-coalescing-in-blk_recalc_rq_segments Wed Oct 20 16:14:23 2004 +++ 25-akpm/drivers/block/ll_rw_blk.c Wed Oct 20 16:14:23 2004 @@ -2767,22 +2767,36 @@ void blk_recalc_rq_segments(struct reque { struct bio *bio, *prevbio = NULL; int nr_phys_segs, nr_hw_segs; + unsigned int phys_size, hw_size; + request_queue_t *q = rq->q; if (!rq->bio) return; - nr_phys_segs = nr_hw_segs = 0; + phys_size = hw_size = nr_phys_segs = nr_hw_segs = 0; rq_for_each_bio(bio, rq) { /* Force bio hw/phys segs to be recalculated. */ bio->bi_flags &= ~(1 << BIO_SEG_VALID); - nr_phys_segs += bio_phys_segments(rq->q, bio); - nr_hw_segs += bio_hw_segments(rq->q, bio); + nr_phys_segs += bio_phys_segments(q, bio); + nr_hw_segs += bio_hw_segments(q, bio); if (prevbio) { - if (blk_phys_contig_segment(rq->q, prevbio, bio)) + int pseg = phys_size + prevbio->bi_size + bio->bi_size; + int hseg = hw_size + prevbio->bi_size + bio->bi_size; + + if (blk_phys_contig_segment(q, prevbio, bio) && + pseg <= q->max_segment_size) { nr_phys_segs--; - if (blk_hw_contig_segment(rq->q, prevbio, bio)) + phys_size += prevbio->bi_size + bio->bi_size; + } else + phys_size = 0; + + if (blk_hw_contig_segment(q, prevbio, bio) && + hseg <= q->max_segment_size) { nr_hw_segs--; + hw_size += prevbio->bi_size + bio->bi_size; + } else + hw_size = 0; } prevbio = bio; } _