From 95450709c1de3152706a6b1df652b9c3952c8a7c Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Wed, 5 Nov 2003 16:23:58 -0800 Subject: [PATCH] [PATCH] fix segment accounting with bounced pages There's a problem with bio segment accounting for pages that reside above the bounce limit of a queue. When submitted, they may be considered part of another segment. A condition that changes when the page gets bounced. This can cause us to send bio's that have too many segments to a driver. The best fix is to always consider pages above q->bounce_pfn as seperate segments. That's the conservative approach and the easy fix. Problem identified and fixed by Herbert Xu. --- drivers/block/ll_rw_blk.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index a784f8c14ae6f..3dd05e2478ee6 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -819,6 +819,7 @@ void blk_recount_segments(request_queue_t *q, struct bio *bio) { struct bio_vec *bv, *bvprv = NULL; int i, nr_phys_segs, nr_hw_segs, seg_size, cluster; + int high, highprv = 1; if (unlikely(!bio->bi_io_vec)) return; @@ -826,7 +827,15 @@ void blk_recount_segments(request_queue_t *q, struct bio *bio) cluster = q->queue_flags & (1 << QUEUE_FLAG_CLUSTER); seg_size = nr_phys_segs = nr_hw_segs = 0; bio_for_each_segment(bv, bio, i) { - if (bvprv && cluster) { + /* + * the trick here is making sure that a high page is never + * considered part of another segment, since that might + * change with the bounce page. + */ + high = page_to_pfn(bv->bv_page) >= q->bounce_pfn; + if (high || highprv) + goto new_hw_segment; + if (cluster) { if (seg_size + bv->bv_len > q->max_segment_size) goto new_segment; if (!BIOVEC_PHYS_MERGEABLE(bvprv, bv)) @@ -839,12 +848,14 @@ void blk_recount_segments(request_queue_t *q, struct bio *bio) continue; } new_segment: - if (!bvprv || !BIOVEC_VIRT_MERGEABLE(bvprv, bv)) + if (!BIOVEC_VIRT_MERGEABLE(bvprv, bv)) +new_hw_segment: nr_hw_segs++; nr_phys_segs++; bvprv = bv; seg_size = bv->bv_len; + highprv = high; } bio->bi_phys_segments = nr_phys_segs; -- 2.53.0