]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
md/raid5: fix IO hang when array is broken with IO inflight
authorYu Kuai <yukuai@fnnas.com>
Mon, 17 Nov 2025 08:55:57 +0000 (16:55 +0800)
committerYu Kuai <yukuai@fnnas.com>
Sun, 30 Nov 2025 01:38:45 +0000 (09:38 +0800)
Following test can cause IO hang:

mdadm -CvR /dev/md0 -l10 -n4 /dev/sd[abcd] --assume-clean --chunk=64K --bitmap=none
sleep 5
echo 1 > /sys/block/sda/device/delete
echo 1 > /sys/block/sdb/device/delete
echo 1 > /sys/block/sdc/device/delete
echo 1 > /sys/block/sdd/device/delete

dd if=/dev/md0 of=/dev/null bs=8k count=1 iflag=direct

Root cause:

1) all disks removed, however all rdevs in the array is still in sync,
IO will be issued normally.

2) IO failure from sda, and set badblocks failed, sda will be faulty
and MD_SB_CHANGING_PENDING will be set.

3) error recovery try to recover this IO from other disks, IO will be
issued to sdb, sdc, and sdd.

4) IO failure from sdb, and set badblocks failed again, now array is
broken and will become read-only.

5) IO failure from sdc and sdd, however, stripe can't be handled anymore
because MD_SB_CHANGING_PENDING is set:

handle_stripe
 handle_stripe
 if (test_bit MD_SB_CHANGING_PENDING)
  set_bit STRIPE_HANDLE
  goto finish
  // skip handling failed stripe

release_stripe
 if (test_bit STRIPE_HANDLE)
  list_add_tail conf->hand_list

6) later raid5d can't handle failed stripe as well:

raid5d
 md_check_recovery
  md_update_sb
   if (!md_is_rdwr())
    // can't clear pending bit
    return
 if (test_bit MD_SB_CHANGING_PENDING)
  break;
  // can't handle failed stripe

Since MD_SB_CHANGING_PENDING can never be cleared for read-only array,
fix this problem by skip this checking for read-only array.

Link: https://lore.kernel.org/linux-raid/20251117085557.770572-3-yukuai@fnnas.com
Fixes: d87f064f5874 ("md: never update metadata when array is read-only.")
Signed-off-by: Yu Kuai <yukuai@fnnas.com>
Reviewed-by: Li Nan <linan122@huawei.com>
drivers/md/raid5.c

index cdbc7eba5c54018de59dd070b6b1566c3506621d..e57ce3295292ba0d5ebb74237a23512ea3efd89f 100644 (file)
@@ -4956,7 +4956,8 @@ static void handle_stripe(struct stripe_head *sh)
                goto finish;
 
        if (s.handle_bad_blocks ||
-           test_bit(MD_SB_CHANGE_PENDING, &conf->mddev->sb_flags)) {
+           (md_is_rdwr(conf->mddev) &&
+            test_bit(MD_SB_CHANGE_PENDING, &conf->mddev->sb_flags))) {
                set_bit(STRIPE_HANDLE, &sh->state);
                goto finish;
        }
@@ -6768,7 +6769,8 @@ static void raid5d(struct md_thread *thread)
                int batch_size, released;
                unsigned int offset;
 
-               if (test_bit(MD_SB_CHANGE_PENDING, &mddev->sb_flags))
+               if (md_is_rdwr(mddev) &&
+                   test_bit(MD_SB_CHANGE_PENDING, &mddev->sb_flags))
                        break;
 
                released = release_stripe_list(conf, conf->temp_inactive_list);