mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
srcutree: Add reader-free fastpath to synchronize_srcu_atomic()
synchronize_srcu_atomic() is restricted to srcu_read_lock_atomic() and srcu_read_unlock_atomic(), whose read-side critical sections disable preemption. In the common case where there are no readers at all, the grace period therefore need not do the index flip. Add a fastpath that sums both ranks of the per-CPU ->srcu_ctrs[] counters and, if the lock counts match the unlock counts on both ranks, ends the grace period immediately, skipping the srcu_advance_state() scans, mirroring the similar Tiny SRCU fastpath. Correctness requires the counter-sum proof to follow the grace-period anchor written by srcu_gp_start(); placing it before the anchor could let this grace period miss a pre-existing reader and return without waiting for it. The smp_mb() between the unlock and lock sums pairs with the smp_mb() in __srcu_read_lock(). The grace period is ended manually under ->lock and ->srcu_atomic_gp_flag. In theory, this is slower than David Woodhouse's earlier patch, but David's measurements showed that the performance was close enough that it makes sense to keep the get_state_synchronize_srcu() and poll_state_synchronize_srcu semantics. Link: https://lore.kernel.org/all/20260907075829.2073224-4-kunwu.chan@linux.dev/ Link: https://lore.kernel.org/all/0f4dea21bac43685d3286df329401177e4452b36.camel@infradead.org/ Link: https://lore.kernel.org/all/0d4af6318ac67486858be1df8d436147b444a2d2.camel@infradead.org/ Signed-off-by: Kunwu Chan <kunwu.chan@gmail.com> Signed-off-by: Paul E. McKenney <paulmck@kernel.org> Cc: David Woodhouse <dwmw2@infradead.org>
This commit is contained in:
parent
ddd326d8f3
commit
30d89cea5f
|
|
@ -2119,6 +2119,8 @@ void synchronize_srcu_atomic(struct srcu_struct *ssp)
|
|||
{
|
||||
unsigned long srcu_state;
|
||||
struct srcu_usage *sup = ssp->srcu_sup;
|
||||
unsigned long rdm0, rdm1;
|
||||
unsigned long unlocks0, unlocks1;
|
||||
|
||||
// Initialize. Either init_srcu_struct() was invoked or
|
||||
// DEFINE_SRCU() or similar was used. Therefore, no allocation
|
||||
|
|
@ -2154,6 +2156,52 @@ void synchronize_srcu_atomic(struct srcu_struct *ssp)
|
|||
srcu_gp_start(ssp);
|
||||
raw_spin_unlock_irq_rcu_node(sup);
|
||||
|
||||
//
|
||||
// Fastpath: If there are no readers at all, neither grace-period
|
||||
// scan need wait, so both can be satisfied at once without doing
|
||||
// the index flip. The counter-sum proof is the same as that of
|
||||
// srcu_readers_active_idx_check(), but spanning both indices.
|
||||
// Atomic SRCU guarantees that all readers are of
|
||||
// SRCU_READ_FLAVOR_ATOMIC, so the SLOWGP check never triggers and
|
||||
// the ->srcu_reader_flavor masks returned by
|
||||
// srcu_readers_unlock_idx() are unused.
|
||||
//
|
||||
// This proof must follow the grace-period anchor written by the
|
||||
// srcu_gp_start() above, never precede it. With the anchor first,
|
||||
// a reader whose lock increment is missed by the sums below cannot
|
||||
// have incremented its lock counter before the anchor, and therefore
|
||||
// cannot be a pre-existing reader of this grace period. Placing the
|
||||
// proof before the anchor would let this grace period miss a
|
||||
// pre-existing reader and return without waiting for it.
|
||||
//
|
||||
// The smp_mb() pairs with the smp_mb() in __srcu_read_lock()
|
||||
// (store-buffering pattern), which guarantees that a lock is always
|
||||
// counted if the corresponding unlock is counted, the same
|
||||
// memory-ordering guarantee as is provided by
|
||||
// srcu_readers_active_idx_check().
|
||||
//
|
||||
unlocks0 = srcu_readers_unlock_idx(ssp, 0, &rdm0);
|
||||
unlocks1 = srcu_readers_unlock_idx(ssp, 1, &rdm1);
|
||||
smp_mb(); /* A */
|
||||
if (srcu_readers_lock_idx(ssp, 0, false, unlocks0) &&
|
||||
srcu_readers_lock_idx(ssp, 1, false, unlocks1)) {
|
||||
// No readers, so end this grace period manually, skipping
|
||||
// the index flip. Advancing the sequence number via
|
||||
// rcu_seq_start() in srcu_gp_start() above and rcu_seq_end()
|
||||
// below keeps get_state_synchronize_srcu() and
|
||||
// poll_state_synchronize_srcu() working, all under ->lock
|
||||
// and ->srcu_atomic_gp_flag, which excludes concurrent
|
||||
// sequence-number updates.
|
||||
raw_spin_lock_irq_rcu_node(sup);
|
||||
rcu_seq_end(&sup->srcu_gp_seq);
|
||||
raw_spin_unlock_irq_rcu_node(sup);
|
||||
WARN_ON_ONCE(!poll_state_synchronize_srcu(ssp, srcu_state));
|
||||
atomic_set_release(&sup->srcu_atomic_gp_flag, 0);
|
||||
preempt_enable();
|
||||
non_block_end();
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait for it to complete, helping it along.
|
||||
while (!poll_state_synchronize_srcu(ssp, srcu_state)) {
|
||||
cpu_relax();
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user