block: properly handle REQ_OP_ZONE_APPEND in __bio_split_to_limits
Currently REQ_OP_ZONE_APPEND is handled by the bio_split_rw case in __bio_split_to_limits. This is harmful because REQ_OP_ZONE_APPEND bios do not adhere to the soft max_limits value but instead use their own capped version of max_hw_sectors, leading to incorrect splits that later blow up in bio_split. We still need the bio_split_rw logic to count nr_segs for blk-mq code, so add a new wrapper that passes in the right limit, and turns any bio that would need a split into an error as an additional debugging aid. Signed-off-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Damien Le Moal <dlemoal@kernel.org> Tested-by: Hans Holmberg <hans.holmberg@wdc.com> Reviewed-by: Hans Holmberg <hans.holmberg@wdc.com> Link: https://lore.kernel.org/r/20240826173820.1690925-4-hch@lst.de Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
parent
379b122a3e
commit
1e8a7f6af9
@ -378,6 +378,26 @@ struct bio *bio_split_rw(struct bio *bio, const struct queue_limits *lim,
|
|||||||
get_max_io_size(bio, lim) << SECTOR_SHIFT));
|
get_max_io_size(bio, lim) << SECTOR_SHIFT));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* REQ_OP_ZONE_APPEND bios must never be split by the block layer.
|
||||||
|
*
|
||||||
|
* But we want the nr_segs calculation provided by bio_split_rw_at, and having
|
||||||
|
* a good sanity check that the submitter built the bio correctly is nice to
|
||||||
|
* have as well.
|
||||||
|
*/
|
||||||
|
struct bio *bio_split_zone_append(struct bio *bio,
|
||||||
|
const struct queue_limits *lim, unsigned *nr_segs)
|
||||||
|
{
|
||||||
|
unsigned int max_sectors = queue_limits_max_zone_append_sectors(lim);
|
||||||
|
int split_sectors;
|
||||||
|
|
||||||
|
split_sectors = bio_split_rw_at(bio, lim, nr_segs,
|
||||||
|
max_sectors << SECTOR_SHIFT);
|
||||||
|
if (WARN_ON_ONCE(split_sectors > 0))
|
||||||
|
split_sectors = -EINVAL;
|
||||||
|
return bio_submit_split(bio, split_sectors);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* bio_split_to_limits - split a bio to fit the queue limits
|
* bio_split_to_limits - split a bio to fit the queue limits
|
||||||
* @bio: bio to be split
|
* @bio: bio to be split
|
||||||
|
@ -337,6 +337,8 @@ struct bio *bio_split_write_zeroes(struct bio *bio,
|
|||||||
const struct queue_limits *lim, unsigned *nsegs);
|
const struct queue_limits *lim, unsigned *nsegs);
|
||||||
struct bio *bio_split_rw(struct bio *bio, const struct queue_limits *lim,
|
struct bio *bio_split_rw(struct bio *bio, const struct queue_limits *lim,
|
||||||
unsigned *nr_segs);
|
unsigned *nr_segs);
|
||||||
|
struct bio *bio_split_zone_append(struct bio *bio,
|
||||||
|
const struct queue_limits *lim, unsigned *nr_segs);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* All drivers must accept single-segments bios that are smaller than PAGE_SIZE.
|
* All drivers must accept single-segments bios that are smaller than PAGE_SIZE.
|
||||||
@ -375,6 +377,8 @@ static inline struct bio *__bio_split_to_limits(struct bio *bio,
|
|||||||
return bio_split_rw(bio, lim, nr_segs);
|
return bio_split_rw(bio, lim, nr_segs);
|
||||||
*nr_segs = 1;
|
*nr_segs = 1;
|
||||||
return bio;
|
return bio;
|
||||||
|
case REQ_OP_ZONE_APPEND:
|
||||||
|
return bio_split_zone_append(bio, lim, nr_segs);
|
||||||
case REQ_OP_DISCARD:
|
case REQ_OP_DISCARD:
|
||||||
case REQ_OP_SECURE_ERASE:
|
case REQ_OP_SECURE_ERASE:
|
||||||
return bio_split_discard(bio, lim, nr_segs);
|
return bio_split_discard(bio, lim, nr_segs);
|
||||||
|
Loading…
Reference in New Issue
Block a user