[PATCH v3 1/7] srcutiny: Do not call synchronize_rcu() from synchronize_srcu_atomic()
From: Mathieu Desnoyers
Date: Thu Nov 12 2026 - 09:28:04 EST
synchronize_srcu_atomic() never blocks, so that it can be called from
atomic context. This includes interrupt handlers, and code within
rcu_read_lock().
With CONFIG_PREEMPTION=y, the Tiny SRCU version first calls
synchronize_rcu(). Tiny synchronize_rcu() does not wait: it only
advances rcu_ctrlblk.gp_seq. This is valid where the caller is in a
quiescent state, in task context and outside of any RCU reader.
The callers of synchronize_srcu_atomic() need not be in a quiescent
state. An interrupt handler may have interrupted an RCU reader. A
caller within rcu_read_lock() is itself an RCU reader. In both cases,
synchronize_rcu() ends an RCU grace period while an RCU reader is still
in its critical section. poll_state_synchronize_rcu() would then
report a grace period that did not happen. The caller within
rcu_read_lock() also gets "Illegal synchronize_rcu() in RCU read-side
critical section" from lockdep.
srcu_drive_gp() has the same call. There, it makes an RCU Tasks
Trace grace period imply an RCU grace period. RCU Tasks Trace uses
call_srcu() and synchronize_srcu(), which are both forbidden on a
srcu_struct that uses synchronize_srcu_atomic(). And the Tree SRCU
version of synchronize_srcu_atomic() does not wait for an RCU grace
period either.
Fix this by removing the call.
This was found by reading the code. An rcutorture test added later in
this series fails without this change.
Fixes: a48669f0c49c ("srcutiny: Add an atomic Tiny SRCU")
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@xxxxxxxxxxxx>
Reviewed-by: Paul E. McKenney <paulmck@xxxxxxxxxx>
Tested-by: David Woodhouse <dwmw@xxxxxxxxxxxx>
Reviewed-by: Kunwu Chan <chentao@xxxxxxxxxx>
---
kernel/rcu/srcutiny.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/kernel/rcu/srcutiny.c b/kernel/rcu/srcutiny.c
index 63a3f0a8bc53..d58f4160f925 100644
--- a/kernel/rcu/srcutiny.c
+++ b/kernel/rcu/srcutiny.c
@@ -367,9 +367,10 @@ void synchronize_srcu_atomic(struct srcu_struct *ssp)
if (rcu_scheduler_active == RCU_SCHEDULER_INACTIVE)
return;
- if (IS_ENABLED(CONFIG_PREEMPTION))
- synchronize_rcu(); // Needed for RCU Tasks Trace to imply RCU grace period.
- // And in Tiny RCU, it is near zero cost and doesn't block.
+ // No synchronize_rcu() here, unlike in srcu_drive_gp(). The caller
+ // may be in an interrupt handler or within rcu_read_lock(). Tiny
+ // synchronize_rcu() would then end an RCU grace period that an RCU
+ // reader still holds up.
// Usually, there will be no readers.
preempt_disable(); // Guard against lazy preemption and some other grace period.
--
2.47.3