]> www.infradead.org Git - nvme.git/commitdiff
md: ensure child flush IO does not affect origin bio->bi_status
authorLi Nan <linan122@huawei.com>
Thu, 19 Sep 2024 06:30:48 +0000 (14:30 +0800)
committerSong Liu <song@kernel.org>
Thu, 17 Oct 2024 18:35:36 +0000 (11:35 -0700)
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 b75197e86e6d
("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: b75197e86e6d ("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>
drivers/md/md.c

index 179ee4afe9376610db586d253982892be0512252..67108c397c5a8600fdfca6c89de76752c4e72fcb 100644 (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);
        }