Commit 95450709 authored by Jens Axboe's avatar Jens Axboe Committed by Linus Torvalds

[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.
parent f32732f3
...@@ -819,6 +819,7 @@ void blk_recount_segments(request_queue_t *q, struct bio *bio) ...@@ -819,6 +819,7 @@ void blk_recount_segments(request_queue_t *q, struct bio *bio)
{ {
struct bio_vec *bv, *bvprv = NULL; struct bio_vec *bv, *bvprv = NULL;
int i, nr_phys_segs, nr_hw_segs, seg_size, cluster; int i, nr_phys_segs, nr_hw_segs, seg_size, cluster;
int high, highprv = 1;
if (unlikely(!bio->bi_io_vec)) if (unlikely(!bio->bi_io_vec))
return; return;
...@@ -826,7 +827,15 @@ void blk_recount_segments(request_queue_t *q, struct bio *bio) ...@@ -826,7 +827,15 @@ void blk_recount_segments(request_queue_t *q, struct bio *bio)
cluster = q->queue_flags & (1 << QUEUE_FLAG_CLUSTER); cluster = q->queue_flags & (1 << QUEUE_FLAG_CLUSTER);
seg_size = nr_phys_segs = nr_hw_segs = 0; seg_size = nr_phys_segs = nr_hw_segs = 0;
bio_for_each_segment(bv, bio, i) { 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) if (seg_size + bv->bv_len > q->max_segment_size)
goto new_segment; goto new_segment;
if (!BIOVEC_PHYS_MERGEABLE(bvprv, bv)) if (!BIOVEC_PHYS_MERGEABLE(bvprv, bv))
...@@ -839,12 +848,14 @@ void blk_recount_segments(request_queue_t *q, struct bio *bio) ...@@ -839,12 +848,14 @@ void blk_recount_segments(request_queue_t *q, struct bio *bio)
continue; continue;
} }
new_segment: new_segment:
if (!bvprv || !BIOVEC_VIRT_MERGEABLE(bvprv, bv)) if (!BIOVEC_VIRT_MERGEABLE(bvprv, bv))
new_hw_segment:
nr_hw_segs++; nr_hw_segs++;
nr_phys_segs++; nr_phys_segs++;
bvprv = bv; bvprv = bv;
seg_size = bv->bv_len; seg_size = bv->bv_len;
highprv = high;
} }
bio->bi_phys_segments = nr_phys_segs; bio->bi_phys_segments = nr_phys_segs;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment