On Mon, Sep 07, 2026 at 03:58:29PM +0800, Kunwu Chan wrote:
> From: Kunwu Chan <[email protected]>
> 
> srcu_read_lock_atomic() and srcu_read_unlock_atomic() arm and disarm
> might_sleep() checks via non_block_start()/non_block_end(), skipping
> hardirq so the update does not land on the interrupted task's
> ->non_block_count.
> 
> Inline softirqs run on the interrupted task's stack as well, so a
> timer callback running atomic-SRCU readers races with the interrupted
> task's own ->non_block_count updates, as KCSAN reports:
> 
>       BUG: KCSAN: data-race in srcu_torture_read_lock / 
> srcu_torture_read_unlock
> 
>       write to 0xffffa00f818ea418 of 4 bytes by interrupt on cpu 0:
>               srcu_torture_read_lock+0x422/0x470
>               rcutorture_one_extend+0xdc/0x600
>               rcu_torture_one_read+0xd1/0x330
>               rcu_torture_timer+0x75/0x140
>               call_timer_fn+0xe6/0x2f0
>               ...
>               run_timer_softirq+0xb7/0x130
>               handle_softirqs+0xfc/0x3f0
>               __irq_exit_rcu+0x8e/0x100
> 
> Use in_task() so the annotation is applied only in task context; it
> is redundant elsewhere because might_sleep() already warns about
> sleeping from atomic context.
> 
> Signed-off-by: Kunwu Chan <[email protected]>

Again, good catch!  In theory, this should be squashed into the original,
but I don't know of many people bisecting with KCSAN.  I might need to
squash it later (of course with attribution), but at least for the time
being, queued for testing and further review.

                                                        Thanx, Paul

> ---
>  include/linux/srcu.h | 10 ++++++----
>  1 file changed, 6 insertions(+), 4 deletions(-)
> 
> diff --git a/include/linux/srcu.h b/include/linux/srcu.h
> index 0de21155abc1..403fbe57ab15 100644
> --- a/include/linux/srcu.h
> +++ b/include/linux/srcu.h
> @@ -346,11 +346,13 @@ static inline int srcu_read_lock_atomic(struct 
> srcu_struct *ssp)
>       /*
>        * Arm might_sleep() to catch even a *potentially* sleeping call
>        * in the section, not just an actual schedule: the atomic-domain
> -      * promise must hold on every path, contended or not. In hardirq
> -      * the annotation would land on the interrupted task; it is also
> +      * promise must hold on every path, contended or not. In hardirq,
> +      * softirq, or NMI the annotation would land on the interrupted
> +      * task, and can also result in data races against that task's
> +      * own non_block_start()/non_block_end() invocations; it is also
>        * redundant there, so skip it.
>        */
> -     if (!in_hardirq())
> +     if (in_task())
>               non_block_start();
>       srcu_check_read_flavor(ssp, SRCU_READ_FLAVOR_ATOMIC);
>       retval = __srcu_read_lock(ssp);
> @@ -562,7 +564,7 @@ static inline void srcu_read_unlock_atomic(struct 
> srcu_struct *ssp, int idx)
>       srcu_check_read_flavor(ssp, SRCU_READ_FLAVOR_ATOMIC);
>       srcu_lock_release(&ssp->dep_map);
>       __srcu_read_unlock(ssp, idx);
> -     if (!in_hardirq())
> +     if (in_task())
>               non_block_end();
>       preempt_enable();
>  }
> -- 
> 2.43.0
> 

Reply via email to