]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
md/raid1: honor REQ_NOWAIT when waiting for behind writes
authorAbd-Alrhman Masalkhi <abd.masalkhi@gmail.com>
Thu, 11 Jun 2026 08:35:14 +0000 (08:35 +0000)
committerYu Kuai <yukuai@fygo.io>
Sat, 20 Jun 2026 20:19:27 +0000 (04:19 +0800)
raid1 supports REQ_NOWAIT reads by avoiding waits in the barrier path
through wait_read_barrier(). However, a read can still block on a
WriteMostly device when the array uses a bitmap and there are
outstanding behind writes.

In that case raid1 unconditionally calls wait_behind_writes(), which
may sleep until all behind writes complete. As a result, a REQ_NOWAIT
read can block despite the caller explicitly requesting non-blocking
behavior.

This ensures that raid1 consistently honors REQ_NOWAIT reads across all
paths that may otherwise wait for behind writes.

Fixes: 5aa705039c4f ("md: raid1 add nowait support")
Signed-off-by: Abd-Alrhman Masalkhi <abd.masalkhi@gmail.com>
Link: https://patch.msgid.link/20260611083514.754922-1-abd.masalkhi@gmail.com
Signed-off-by: Yu Kuai <yukuai@fygo.io>
drivers/md/md-bitmap.c
drivers/md/md-bitmap.h
drivers/md/md-llbitmap.c
drivers/md/md.c
drivers/md/raid1.c

index 7d778fe1c47cab13b4a6f05021e14bcd06cf3904..0f02e2956398dad592ca7e9aaf0ccbf40909c07e 100644 (file)
@@ -2064,18 +2064,23 @@ static void bitmap_end_behind_write(struct mddev *mddev)
                 bitmap->mddev->bitmap_info.max_write_behind);
 }
 
-static void bitmap_wait_behind_writes(struct mddev *mddev)
+static bool bitmap_wait_behind_writes(struct mddev *mddev, bool nowait)
 {
        struct bitmap *bitmap = mddev->bitmap;
 
        /* wait for behind writes to complete */
        if (bitmap && atomic_read(&bitmap->behind_writes) > 0) {
+               if (nowait)
+                       return false;
+
                pr_debug("md:%s: behind writes in progress - waiting to stop.\n",
                         mdname(mddev));
                /* need to kick something here to make sure I/O goes? */
                wait_event(bitmap->behind_wait,
                           atomic_read(&bitmap->behind_writes) == 0);
        }
+
+       return true;
 }
 
 static void bitmap_destroy(struct mddev *mddev)
@@ -2085,7 +2090,7 @@ static void bitmap_destroy(struct mddev *mddev)
        if (!bitmap) /* there was no bitmap */
                return;
 
-       bitmap_wait_behind_writes(mddev);
+       bitmap_wait_behind_writes(mddev, false);
        if (!test_bit(MD_SERIALIZE_POLICY, &mddev->flags))
                mddev_destroy_serial_pool(mddev, NULL);
 
index 214f623c7e790b3e64e47134445a5b4334f1ff9d..f46674bdfeb918197c8ddaf3aa2fa55cdaeef775 100644 (file)
@@ -98,7 +98,7 @@ struct bitmap_operations {
 
        void (*start_behind_write)(struct mddev *mddev);
        void (*end_behind_write)(struct mddev *mddev);
-       void (*wait_behind_writes)(struct mddev *mddev);
+       bool (*wait_behind_writes)(struct mddev *mddev, bool nowait);
 
        md_bitmap_fn *start_write;
        md_bitmap_fn *end_write;
index 1adc5b11782164cf71ae8d90d9625a921ca40696..5a4e2abaa7577071d30e95b578404e81f60be57d 100644 (file)
@@ -1574,16 +1574,19 @@ static void llbitmap_end_behind_write(struct mddev *mddev)
                wake_up(&llbitmap->behind_wait);
 }
 
-static void llbitmap_wait_behind_writes(struct mddev *mddev)
+static bool llbitmap_wait_behind_writes(struct mddev *mddev, bool nowait)
 {
        struct llbitmap *llbitmap = mddev->bitmap;
 
-       if (!llbitmap)
-               return;
+       if (llbitmap && atomic_read(&llbitmap->behind_writes) > 0) {
+               if (nowait)
+                       return false;
 
-       wait_event(llbitmap->behind_wait,
-                  atomic_read(&llbitmap->behind_writes) == 0);
+               wait_event(llbitmap->behind_wait,
+                          atomic_read(&llbitmap->behind_writes) == 0);
+       }
 
+       return true;
 }
 
 static ssize_t bits_show(struct mddev *mddev, char *page)
index 096bb64e87bd5c1fe664765316d79ee99f155532..d1465bcd86c8120840b893ba0e5bcd7d7aa5c541 100644 (file)
@@ -7050,7 +7050,7 @@ EXPORT_SYMBOL_GPL(md_stop_writes);
 static void mddev_detach(struct mddev *mddev)
 {
        if (md_bitmap_enabled(mddev, false))
-               mddev->bitmap_ops->wait_behind_writes(mddev);
+               mddev->bitmap_ops->wait_behind_writes(mddev, false);
        if (mddev->pers && mddev->pers->quiesce && !is_md_suspended(mddev)) {
                mddev->pers->quiesce(mddev, 1);
                mddev->pers->quiesce(mddev, 0);
index 86d4f224ffb11cf0c21f5f67a5636ab387851172..e2a50816e5a06de8693953790a0b4344a768bac2 100644 (file)
@@ -1341,6 +1341,7 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio,
        int max_sectors;
        int rdisk;
        bool r1bio_existed = !!r1_bio;
+       bool nowait = bio->bi_opf & REQ_NOWAIT;
 
        /*
         * An md cloned bio indicates we are in the error path.
@@ -1360,8 +1361,7 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio,
         * Still need barrier for READ in case that whole
         * array is frozen.
         */
-       if (!wait_read_barrier(conf, bio->bi_iter.bi_sector,
-                               bio->bi_opf & REQ_NOWAIT)) {
+       if (!wait_read_barrier(conf, bio->bi_iter.bi_sector, nowait)) {
                bio_wouldblock_error(bio);
                return;
        }
@@ -1402,7 +1402,11 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio,
                 * over-take any writes that are 'behind'
                 */
                mddev_add_trace_msg(mddev, "raid1 wait behind writes");
-               mddev->bitmap_ops->wait_behind_writes(mddev);
+               if (!mddev->bitmap_ops->wait_behind_writes(mddev, nowait)) {
+                       bio_wouldblock_error(bio);
+                       set_bit(R1BIO_Returned, &r1_bio->state);
+                       goto err_handle;
+               }
        }
 
        if (max_sectors < bio_sectors(bio)) {