Re: [PATCH] md/raid5: avoid R5_Overlap races while breaking stripe batches

"yu kuai" <[email protected]>
Newsgroups gmane.linux.kernel,gmane.linux.raid
Message-ID <[email protected]>
在 2026/6/19 12:10, Chen Cheng 写道:

> From: Chen Cheng <[email protected]>
>
> KCSAN report a race in break_stripe_batch_list() vs. raid5_make_request()
> on sh->dev[i].flags (plain word write vs. atomic bit op)..
>
> and .. one possible scenario is:
>
> CPU1                            CPU2
> break_stripe_batch_list(sh1)
> -> handle sh2
> -> lock(sh2)
> -> sh2->batch_head = NULL
> -> unlock(sh2)
> -> test_and_clear_bit(R5_Overlap, sh2->dev[i].flags)
> -> wake_up_bit(sh2->dev[i].flags)
>                                  raid5_make_request()
>                                  -> add_all_stripe_bios(sh2)
>                                  -> lock(sh2)
>                                  -> stripe_bio_overlaps(sh2) returns true
> 				   batch_head is NULL, so new bio overlap
> 				   exist bio on sh2 -> true
>                                  -> set_bit(R5_Overlap, sh2->dev[i].flags)
>                                  -> unlock(sh2)
>                                  -> wait_on_bit(sh2->dev[i].flags)
> -> sh2->dev[i].flags = sh1->dev[i].flags & ~R5_Overlap
>
> No wait_up_bit(), CPU2 could be wait_on_bit() forever...
>
> Fix by :
> - Expand the protect zone.
> - Use batch_head's device flag's snaphot when no held head_sh->stripe_lock.
> - Move sh/head_sh->batch_head = NULL to the end of protected zone , and ,
>    any concurrent add_all_stripe_bios() grabs sh->stripe_lock now either:
> 	- see batch_head != null, and , is rejected by stripe_bio_overlaps()
> 	  under the lock (no R5_Overlap wait ) , or ,
> 	- sees batch_head == NULL, only after dev[i].flags has already been
> 	  set and the prior R5_Overlap waiters worken.
>
> KCSAN report:
> ================================================
>    BUG: KCSAN: data-race in break_stripe_batch_list / raid5_make_request
>
>    write (marked) to 0xffff8e89c8117548 of 8 bytes by task 4042 on cpu 0:
>      raid5_make_request+0xea0/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
>      __x64_sys_io_submit+0xf7/0x220
>      x64_sys_call+0x1907/0x1c60
>      do_syscall_64+0x130/0x570
>      entry_SYSCALL_64_after_hwframe+0x76/0x7e
>
>    read to 0xffff8e89c8117548 of 8 bytes by task 4010 on cpu 5:
>      break_stripe_batch_list+0x249/0x480
>      handle_stripe_clean_event+0x720/0x9b0
>      handle_stripe+0x32fb/0x4500
>      handle_active_stripes.isra.0+0x6e0/0xa50
>      raid5d+0x7e0/0xba0
>      md_thread+0x15a/0x2d0
>      kthread+0x1e3/0x220
>      ret_from_fork+0x37a/0x410
>      ret_from_fork_asm+0x1a/0x30
>
>    value changed: 0x0000000000000019 -> 0x0000000000000099 --> R5_Overlap
>
> Fixes: fb642b92c267b

Fix this tag and applied to md-7.2

>
> Signed-off-by: Chen Cheng <[email protected]>
> ---
>   drivers/md/raid5.c | 10 +++++-----
>   1 file changed, 5 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
> index a376560be92e..5521051a9425 100644
> --- a/drivers/md/raid5.c
> +++ b/drivers/md/raid5.c
> @@ -4939,30 +4939,30 @@ static void break_stripe_batch_list(struct stripe_head *head_sh,
>   			      state & (1 << STRIPE_INSYNC));
>   
>   		sh->check_state = head_sh->check_state;
>   		sh->reconstruct_state = head_sh->reconstruct_state;
>   		spin_lock_irq(&sh->stripe_lock);
> -		sh->batch_head = NULL;
> -		spin_unlock_irq(&sh->stripe_lock);
>   		for (i = 0; i < sh->disks; i++) {
>   			if (test_and_clear_bit(R5_Overlap, &sh->dev[i].flags))
>   				wake_up_bit(&sh->dev[i].flags, R5_Overlap);
> -			sh->dev[i].flags = head_sh->dev[i].flags &
> +			sh->dev[i].flags = READ_ONCE(head_sh->dev[i].flags) &
>   				(~((1 << R5_WriteError) | (1 << R5_Overlap)));
>   		}
> +		sh->batch_head = NULL;
> +		spin_unlock_irq(&sh->stripe_lock);
>   
>   		state = READ_ONCE(sh->state);
>   		if (handle_flags == 0 || (state & handle_flags))
>   			set_bit(STRIPE_HANDLE, &sh->state);
>   		raid5_release_stripe(sh);
>   	}
>   	spin_lock_irq(&head_sh->stripe_lock);
> -	head_sh->batch_head = NULL;
> -	spin_unlock_irq(&head_sh->stripe_lock);
>   	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);
> +	head_sh->batch_head = NULL;
> +	spin_unlock_irq(&head_sh->stripe_lock);
>   
>   	state = READ_ONCE(head_sh->state);
>   	if (state & handle_flags)
>   		set_bit(STRIPE_HANDLE, &head_sh->state);
>   }

-- 
Thanks,
Kuai
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.