md: add helper to split bios at reshape offset

Add mddev_bio_split_at_reshape_offset() so personalities can share
reshape-offset bio splitting instead of open-coding the same boundary
handling in multiple places.

The helper first applies the optional max_sectors limit. If reshape is
running and the bio crosses reshape_position, it further limits the front
bio to the current reshape boundary so callers can account and submit one
side of the reshape at a time.

Snapshot reshape_position with READ_ONCE(). RAID5 and RAID10 update this
field as reshape progresses, while the I/O path only needs one consistent
decision point for the current bio. Using an explicit single load avoids a
plain lockless access and prevents the compiler from refetching a different
boundary while deciding whether and where to split.

When a split is needed, bio_submit_split_bioset() submits the remainder and
returns the front bio. Callers must therefore continue processing the
returned bio, not the original pointer.

Tested-by: Mykola Marzhan <mykola@meshstor.io>
Link: https://patch.msgid.link/20260802195038.164272-12-yukuai@kernel.org
Signed-off-by: Yu Kuai <yukuai@fygo.io>
This commit is contained in:
Yu Kuai
2026-08-03 03:50:20 +08:00
parent 87c10252e3
commit ffd3e73d6e
2 changed files with 43 additions and 0 deletions

View File

@@ -9388,6 +9388,45 @@ void md_submit_discard_bio(struct mddev *mddev, struct md_rdev *rdev,
}
EXPORT_SYMBOL_GPL(md_submit_discard_bio);
struct bio *mddev_bio_split_at_reshape_offset(struct mddev *mddev,
struct bio *bio,
unsigned int *max_sectors,
struct bio_set *bs)
{
sector_t boundary;
sector_t start;
sector_t end;
unsigned int split_sectors;
split_sectors = bio_sectors(bio);
if (max_sectors && *max_sectors && *max_sectors < split_sectors)
split_sectors = *max_sectors;
if (!test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery))
goto split;
boundary = READ_ONCE(mddev->reshape_position);
start = bio->bi_iter.bi_sector;
end = bio_end_sector(bio);
if (start >= boundary || end <= boundary)
goto split;
if (boundary - start < split_sectors)
split_sectors = boundary - start;
split:
if (max_sectors)
*max_sectors = split_sectors;
if (split_sectors < bio_sectors(bio)) {
bio = bio_submit_split_bioset(bio, split_sectors, bs);
if (bio)
bio->bi_opf |= REQ_NOMERGE;
}
return bio;
}
EXPORT_SYMBOL_GPL(mddev_bio_split_at_reshape_offset);
static void md_bitmap_start(struct mddev *mddev,
struct md_io_clone *md_io_clone)
{

View File

@@ -920,6 +920,10 @@ extern void md_error(struct mddev *mddev, struct md_rdev *rdev);
extern void md_finish_reshape(struct mddev *mddev);
void md_submit_discard_bio(struct mddev *mddev, struct md_rdev *rdev,
struct bio *bio, sector_t start, sector_t size);
struct bio *mddev_bio_split_at_reshape_offset(struct mddev *mddev,
struct bio *bio,
unsigned int *max_sectors,
struct bio_set *bs);
void md_account_bio(struct mddev *mddev, struct bio **bio);
extern bool __must_check md_flush_request(struct mddev *mddev, struct bio *bio);