diff options
| author | Kunwu Chan <kunwu.chan@gmail.com> | 2026-09-07 15:58:29 +0800 |
|---|---|---|
| committer | Paul E. McKenney <paulmck@kernel.org> | 2026-10-12 16:50:40 -0700 |
| commit | cc7287f9ee696e71c3adc23baf21a2dbe0e94874 (patch) | |
| tree | 2c4e8e06e3fb8b2535c4e8dc576a5cd6f8614ff4 /include/linux | |
| parent | 47138812bc786b74a9f96874782668c64860dcc1 (diff) | |
srcu: Restrict atomic-SRCU non_block annotation to task context
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 <kunwu.chan@gmail.com>
Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
Diffstat (limited to 'include/linux')
| -rw-r--r-- | include/linux/srcu.h | 10 |
1 files changed, 6 insertions, 4 deletions
diff --git a/include/linux/srcu.h b/include/linux/srcu.h index 3f232f2e0524..1a8a465a5650 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(); } |
