md: ensure child flush IO does not affect origin bio->bi_status

When a flush is issued to an RAID array, a child flush IO is created and
issued for each member disk in the RAID array. Since commit b75197e86e
("md: Remove flush handling"), each child flush IO has been chained with
the original bio. As a result, the failure of any child IO could modify
the bi_status of the original bio, potentially impacting the upper-layer
filesystem.

Fix the issue by preventing child flush IO from altering the original
bio->bi_status as before. However, this design introduces a known
issue: in the event of a power failure, if a flush IO on a member
disk fails, the upper layers may not be informed. This issue is not easy
to fix and will not be addressed for the time being in this issue.

Fixes: b75197e86e ("md: Remove flush handling")
Signed-off-by: Li Nan <linan122@huawei.com>
Reviewed-by: Yu Kuai <yukuai3@huawei.com>
Link: https://lore.kernel.org/r/20240919063048.2887579-1-linan666@huaweicloud.com
Signed-off-by: Song Liu <song@kernel.org>
This commit is contained in:
Li Nan 2024-09-19 14:30:48 +08:00 committed by Song Liu
parent 42aafd8b48
commit 62ce0782bb

View File

@ -546,6 +546,26 @@ static int mddev_set_closing_and_sync_blockdev(struct mddev *mddev, int opener_n
return 0;
}
/*
* The only difference from bio_chain_endio() is that the current
* bi_status of bio does not affect the bi_status of parent.
*/
static void md_end_flush(struct bio *bio)
{
struct bio *parent = bio->bi_private;
/*
* If any flush io error before the power failure,
* disk data may be lost.
*/
if (bio->bi_status)
pr_err("md: %pg flush io error %d\n", bio->bi_bdev,
blk_status_to_errno(bio->bi_status));
bio_put(bio);
bio_endio(parent);
}
bool md_flush_request(struct mddev *mddev, struct bio *bio)
{
struct md_rdev *rdev;
@ -565,7 +585,9 @@ bool md_flush_request(struct mddev *mddev, struct bio *bio)
new = bio_alloc_bioset(rdev->bdev, 0,
REQ_OP_WRITE | REQ_PREFLUSH, GFP_NOIO,
&mddev->bio_set);
bio_chain(new, bio);
new->bi_private = bio;
new->bi_end_io = md_end_flush;
bio_inc_remaining(bio);
submit_bio(new);
}