]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
md/raid5: use stripe state snapshot in break_stripe_batch_list()
authorChen Cheng <chencheng@fnnas.com>
Thu, 18 Jun 2026 13:47:48 +0000 (21:47 +0800)
committerYu Kuai <yukuai@fygo.io>
Tue, 23 Jun 2026 01:44:01 +0000 (09:44 +0800)
The patch just suppress KCSAN noise. No functional change.

RAID-5 can group multi full-stripe-write aka stripe_head into a
batch aka batch_list, with one head_sh leading them. Call
break_stripe_batch_list() when the batch is finished, or,
a stripe has to be dropped out of the batch.

break_stripe_batch_list() reads stripe state several times while
request paths can update thost state words concurrently with
lockless bitops, which reported by KCSAN.

Use a snapshot to guarantees that the value used for
warning, copying, and handle checks is internally consistent
at current read moment.

KCSAN report:
==============================================
BUG: KCSAN: data-race in __add_stripe_bio / break_stripe_batch_list

write (marked) to 0xffff8e89d4f0b988 of 8 bytes by task 4323 on cpu 3:
  __add_stripe_bio+0x35e/0x400
  raid5_make_request+0x6ac/0x2930
  md_handle_request+0x4a2/0xa40
  md_submit_bio+0x109/0x1a0
  __submit_bio+0x2ec/0x390
  submit_bio_noacct_nocheck+0x457/0x710
  submit_bio_noacct+0x2a7/0xc20
  submit_bio+0x56/0x250
  blkdev_direct_IO+0x54c/0xda0
  blkdev_write_iter+0x38f/0x570
  aio_write+0x22b/0x490
  io_submit_one+0xa51/0xf70

read to 0xffff8e89d4f0b988 of 8 bytes by task 4290 on cpu 4:
  break_stripe_batch_list+0x3ce/0x480
  handle_stripe_clean_event+0x720/0x9b0
  handle_stripe+0x32fb/0x4500
  handle_active_stripes.isra.0+0x6e0/0xa50
  raid5d+0x7e0/0xba0

Signed-off-by: Chen Cheng <chencheng@fnnas.com>
Link: https://patch.msgid.link/20260618134748.1168360-1-chencheng@fnnas.com
Signed-off-by: Yu Kuai <yukuai@fygo.io>
drivers/md/raid5.c

index f35c2a7b2be1b8de73542998417aa50039eef63b..6d982c54f2d170c4c6bb545829d653387bd05a1d 100644 (file)
@@ -4852,31 +4852,35 @@ static void break_stripe_batch_list(struct stripe_head *head_sh,
 {
        struct stripe_head *sh, *next;
        int i;
+       unsigned long state;
 
        list_for_each_entry_safe(sh, next, &head_sh->batch_list, batch_list) {
 
                list_del_init(&sh->batch_list);
 
-               WARN_ONCE(sh->state & ((1 << STRIPE_ACTIVE) |
-                                         (1 << STRIPE_SYNCING) |
-                                         (1 << STRIPE_REPLACED) |
-                                         (1 << STRIPE_DELAYED) |
-                                         (1 << STRIPE_BIT_DELAY) |
-                                         (1 << STRIPE_FULL_WRITE) |
-                                         (1 << STRIPE_BIOFILL_RUN) |
-                                         (1 << STRIPE_COMPUTE_RUN)  |
-                                         (1 << STRIPE_DISCARD) |
-                                         (1 << STRIPE_BATCH_READY) |
-                                         (1 << STRIPE_BATCH_ERR)),
-                       "stripe state: %lx\n", sh->state);
-               WARN_ONCE(head_sh->state & ((1 << STRIPE_DISCARD) |
-                                             (1 << STRIPE_REPLACED)),
-                       "head stripe state: %lx\n", head_sh->state);
+               state = READ_ONCE(sh->state);
+               WARN_ONCE(state & ((1 << STRIPE_ACTIVE) |
+                                  (1 << STRIPE_SYNCING) |
+                                  (1 << STRIPE_REPLACED) |
+                                  (1 << STRIPE_DELAYED) |
+                                  (1 << STRIPE_BIT_DELAY) |
+                                  (1 << STRIPE_FULL_WRITE) |
+                                  (1 << STRIPE_BIOFILL_RUN) |
+                                  (1 << STRIPE_COMPUTE_RUN)  |
+                                  (1 << STRIPE_DISCARD) |
+                                  (1 << STRIPE_BATCH_READY) |
+                                  (1 << STRIPE_BATCH_ERR)),
+                       "stripe state: %lx\n", state);
+
+               state = READ_ONCE(head_sh->state);
+               WARN_ONCE(state & ((1 << STRIPE_DISCARD) |
+                                  (1 << STRIPE_REPLACED)),
+                       "head stripe state: %lx\n", state);
 
                set_mask_bits(&sh->state, ~(STRIPE_EXPAND_SYNC_FLAGS |
                                            (1 << STRIPE_PREREAD_ACTIVE) |
                                            (1 << STRIPE_ON_UNPLUG_LIST)),
-                             head_sh->state & (1 << STRIPE_INSYNC));
+                             state & (1 << STRIPE_INSYNC));
 
                sh->check_state = head_sh->check_state;
                sh->reconstruct_state = head_sh->reconstruct_state;
@@ -4889,8 +4893,9 @@ static void break_stripe_batch_list(struct stripe_head *head_sh,
                        sh->dev[i].flags = head_sh->dev[i].flags &
                                (~((1 << R5_WriteError) | (1 << R5_Overlap)));
                }
-               if (handle_flags == 0 ||
-                   sh->state & handle_flags)
+
+               state = READ_ONCE(sh->state);
+               if (handle_flags == 0 || (state & handle_flags))
                        set_bit(STRIPE_HANDLE, &sh->state);
                raid5_release_stripe(sh);
        }
@@ -4900,7 +4905,9 @@ static void break_stripe_batch_list(struct stripe_head *head_sh,
        for (i = 0; i < head_sh->disks; i++)
                if (test_and_clear_bit(R5_Overlap, &head_sh->dev[i].flags))
                        wake_up_bit(&head_sh->dev[i].flags, R5_Overlap);
-       if (head_sh->state & handle_flags)
+
+       state = READ_ONCE(head_sh->state);
+       if (state & handle_flags)
                set_bit(STRIPE_HANDLE, &head_sh->state);
 }