Skip to content

Commit

Permalink
block: fix segment calculation for passthrough IO
Browse files Browse the repository at this point in the history
blk_recount_segments() can be called in bio_add_pc_page() for
calculating how many segments this bio will has after one page is added
to this bio. If the resulted segment number is beyond the queue limit,
the added page will be removed.

The try-and-fix policy requires blk_recount_segments(__blk_recalc_rq_segments)
to not consider the segment number limit. Unfortunately bvec_split_segs()
does check this limit, and causes small segment number returned to
bio_add_pc_page(), then page still may be added to the bio even though
segment number limit becomes broken.

Fixes this issue by not considering segment number limit when calcualting
bio's segment number.

Fixes: dcebd75 ("block: use bio_for_each_bvec() to compute multi-page bvec count")
Cc: Christoph Hellwig <hch@lst.de>
Cc: Omar Sandoval <osandov@fb.com>
Signed-off-by: Ming Lei <ming.lei@redhat.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
  • Loading branch information
Ming Lei authored and Jens Axboe committed Mar 6, 2019
1 parent e61750c commit 05b700b
Showing 1 changed file with 8 additions and 7 deletions.
15 changes: 8 additions & 7 deletions block/blk-merge.c
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ static unsigned get_max_segment_size(struct request_queue *q,
*/
static bool bvec_split_segs(struct request_queue *q, struct bio_vec *bv,
unsigned *nsegs, unsigned *last_seg_size,
unsigned *front_seg_size, unsigned *sectors)
unsigned *front_seg_size, unsigned *sectors, unsigned max_segs)
{
unsigned len = bv->bv_len;
unsigned total_len = 0;
Expand All @@ -190,7 +190,7 @@ static bool bvec_split_segs(struct request_queue *q, struct bio_vec *bv,
* Multi-page bvec may be too big to hold in one segment, so the
* current bvec has to be splitted as multiple segments.
*/
while (len && new_nsegs + *nsegs < queue_max_segments(q)) {
while (len && new_nsegs + *nsegs < max_segs) {
seg_size = get_max_segment_size(q, bv->bv_offset + total_len);
seg_size = min(seg_size, len);

Expand Down Expand Up @@ -240,6 +240,7 @@ static struct bio *blk_bio_segment_split(struct request_queue *q,
bool do_split = true;
struct bio *new = NULL;
const unsigned max_sectors = get_max_io_size(q, bio);
const unsigned max_segs = queue_max_segments(q);

bio_for_each_bvec(bv, bio, iter) {
/*
Expand All @@ -254,14 +255,14 @@ static struct bio *blk_bio_segment_split(struct request_queue *q,
* Consider this a new segment if we're splitting in
* the middle of this vector.
*/
if (nsegs < queue_max_segments(q) &&
if (nsegs < max_segs &&
sectors < max_sectors) {
/* split in the middle of bvec */
bv.bv_len = (max_sectors - sectors) << 9;
bvec_split_segs(q, &bv, &nsegs,
&seg_size,
&front_seg_size,
&sectors);
&sectors, max_segs);
}
goto split;
}
Expand All @@ -283,7 +284,7 @@ static struct bio *blk_bio_segment_split(struct request_queue *q,
continue;
}
new_segment:
if (nsegs == queue_max_segments(q))
if (nsegs == max_segs)
goto split;

bvprv = bv;
Expand All @@ -296,7 +297,7 @@ static struct bio *blk_bio_segment_split(struct request_queue *q,
if (nsegs == 1 && seg_size > front_seg_size)
front_seg_size = seg_size;
} else if (bvec_split_segs(q, &bv, &nsegs, &seg_size,
&front_seg_size, &sectors)) {
&front_seg_size, &sectors, max_segs)) {
goto split;
}
}
Expand Down Expand Up @@ -415,7 +416,7 @@ static unsigned int __blk_recalc_rq_segments(struct request_queue *q,
bvprv = bv;
prev = 1;
bvec_split_segs(q, &bv, &nr_phys_segs, &seg_size,
&front_seg_size, NULL);
&front_seg_size, NULL, UINT_MAX);
}
bbio = bio;
}
Expand Down

0 comments on commit 05b700b

Please sign in to comment.