Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

block: fix segment calculation for passthrough IO

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: dcebd755926b ("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>

authored by

Ming Lei and committed by
Jens Axboe
05b700ba e61750c8

+8 -7
+8 -7
block/blk-merge.c
··· 180 180 */ 181 181 static bool bvec_split_segs(struct request_queue *q, struct bio_vec *bv, 182 182 unsigned *nsegs, unsigned *last_seg_size, 183 - unsigned *front_seg_size, unsigned *sectors) 183 + unsigned *front_seg_size, unsigned *sectors, unsigned max_segs) 184 184 { 185 185 unsigned len = bv->bv_len; 186 186 unsigned total_len = 0; ··· 190 190 * Multi-page bvec may be too big to hold in one segment, so the 191 191 * current bvec has to be splitted as multiple segments. 192 192 */ 193 - while (len && new_nsegs + *nsegs < queue_max_segments(q)) { 193 + while (len && new_nsegs + *nsegs < max_segs) { 194 194 seg_size = get_max_segment_size(q, bv->bv_offset + total_len); 195 195 seg_size = min(seg_size, len); 196 196 ··· 240 240 bool do_split = true; 241 241 struct bio *new = NULL; 242 242 const unsigned max_sectors = get_max_io_size(q, bio); 243 + const unsigned max_segs = queue_max_segments(q); 243 244 244 245 bio_for_each_bvec(bv, bio, iter) { 245 246 /* ··· 255 254 * Consider this a new segment if we're splitting in 256 255 * the middle of this vector. 257 256 */ 258 - if (nsegs < queue_max_segments(q) && 257 + if (nsegs < max_segs && 259 258 sectors < max_sectors) { 260 259 /* split in the middle of bvec */ 261 260 bv.bv_len = (max_sectors - sectors) << 9; 262 261 bvec_split_segs(q, &bv, &nsegs, 263 262 &seg_size, 264 263 &front_seg_size, 265 - &sectors); 264 + &sectors, max_segs); 266 265 } 267 266 goto split; 268 267 } ··· 284 283 continue; 285 284 } 286 285 new_segment: 287 - if (nsegs == queue_max_segments(q)) 286 + if (nsegs == max_segs) 288 287 goto split; 289 288 290 289 bvprv = bv; ··· 297 296 if (nsegs == 1 && seg_size > front_seg_size) 298 297 front_seg_size = seg_size; 299 298 } else if (bvec_split_segs(q, &bv, &nsegs, &seg_size, 300 - &front_seg_size, &sectors)) { 299 + &front_seg_size, &sectors, max_segs)) { 301 300 goto split; 302 301 } 303 302 } ··· 416 415 bvprv = bv; 417 416 prev = 1; 418 417 bvec_split_segs(q, &bv, &nr_phys_segs, &seg_size, 419 - &front_seg_size, NULL); 418 + &front_seg_size, NULL, UINT_MAX); 420 419 } 421 420 bbio = bio; 422 421 }