]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
md/raid10: fix writes_pending leak on write request failures
authorAbd-Alrhman Masalkhi <abd.masalkhi@gmail.com>
Sat, 13 Jun 2026 18:28:08 +0000 (18:28 +0000)
committerYu Kuai <yukuai@fygo.io>
Sat, 20 Jun 2026 18:14:44 +0000 (02:14 +0800)
raid10_make_request() acquires a writes_pending reference with
md_write_start() before dispatching write requests. Several failure
paths in raid10_write_request() complete the bio and return without
reaching the normal write completion path, causing the corresponding
md_write_end() to be skipped.

Make raid10_write_request() return a status indicating whether the write
request was successfully queued. This allows raid10_make_request() to
release the writes_pending reference with md_write_end() when a write
request fails.

Fixes: 4cf58d952909 ("md/raid10: Handle bio_split() errors")
Fixes: c9aa889b035f ("md: raid10 add nowait support")
Signed-off-by: Abd-Alrhman Masalkhi <abd.masalkhi@gmail.com>
Link: https://patch.msgid.link/20260613182810.1317258-3-abd.masalkhi@gmail.com
Signed-off-by: Yu Kuai <yukuai@fygo.io>
drivers/md/raid10.c

index cee5a253a281def1ed339f9cb9faa17b8eb93fff..c123a8c76ddcdddc90ccaafb07197538b997168c 100644 (file)
@@ -1349,7 +1349,7 @@ retry_wait:
        }
 }
 
-static void raid10_write_request(struct mddev *mddev, struct bio *bio,
+static bool raid10_write_request(struct mddev *mddev, struct bio *bio,
                                 struct r10bio *r10_bio)
 {
        struct r10conf *conf = mddev->private;
@@ -1365,7 +1365,7 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio,
                /* Bail out if REQ_NOWAIT is set for the bio */
                if (bio->bi_opf & REQ_NOWAIT) {
                        bio_wouldblock_error(bio);
-                       return;
+                       return false;
                }
                for (;;) {
                        prepare_to_wait(&conf->wait_barrier,
@@ -1381,7 +1381,7 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio,
        sectors = r10_bio->sectors;
        if (!regular_request_wait(mddev, conf, bio, sectors)) {
                free_r10bio(r10_bio);
-               return;
+               return false;
        }
 
        if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery) &&
@@ -1398,7 +1398,7 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio,
                if (bio->bi_opf & REQ_NOWAIT) {
                        allow_barrier(conf);
                        bio_wouldblock_error(bio);
-                       return;
+                       return false;
                }
                mddev_add_trace_msg(conf->mddev,
                        "raid10 wait reshape metadata");
@@ -1514,7 +1514,8 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio,
                        raid10_write_one_disk(mddev, r10_bio, bio, true, i);
        }
        one_write_done(r10_bio);
-       return;
+       return true;
+
 err_handle:
        for (k = 0;  k < i; k++) {
                int d = r10_bio->devs[k].devnum;
@@ -1532,10 +1533,12 @@ err_handle:
        }
 
        raid_end_bio_io(r10_bio);
+       return false;
 }
 
-static void __make_request(struct mddev *mddev, struct bio *bio, int sectors)
+static bool __make_request(struct mddev *mddev, struct bio *bio, int sectors)
 {
+       bool ret;
        struct r10conf *conf = mddev->private;
        struct r10bio *r10_bio;
 
@@ -1551,10 +1554,13 @@ static void __make_request(struct mddev *mddev, struct bio *bio, int sectors)
        memset(r10_bio->devs, 0, sizeof(r10_bio->devs[0]) *
                        conf->geo.raid_disks);
 
+       ret = true;
        if (bio_data_dir(bio) == READ)
                raid10_read_request(mddev, bio, r10_bio);
        else
-               raid10_write_request(mddev, bio, r10_bio);
+               ret = raid10_write_request(mddev, bio, r10_bio);
+
+       return ret;
 }
 
 static void raid_end_discard_bio(struct r10bio *r10bio)
@@ -1900,7 +1906,8 @@ static bool raid10_make_request(struct mddev *mddev, struct bio *bio)
                sectors = chunk_sects -
                        (bio->bi_iter.bi_sector &
                         (chunk_sects - 1));
-       __make_request(mddev, bio, sectors);
+       if (!__make_request(mddev, bio, sectors))
+               md_write_end(mddev);
 
        /* In case raid10d snuck in to freeze_array */
        wake_up_barrier(conf);