Skip to content

Commit 1e8e695

Browse files
Frederic Weisbeckerfbq
authored andcommitted
rcu/nocb: Remove needless full barrier after callback advancing
A full barrier is issued from nocb_gp_wait() upon callbacks advancing to order grace period completion with callbacks execution. However these two events are already ordered by the smp_mb__after_unlock_lock() barrier within the call to raw_spin_lock_rcu_node() that is necessary for callbacks advancing to happen. The following litmus test shows the kind of guarantee that this barrier provides: C smp_mb__after_unlock_lock {} // rcu_gp_cleanup() P0(spinlock_t *rnp_lock, int *gpnum) { // Grace period cleanup increase gp sequence number spin_lock(rnp_lock); WRITE_ONCE(*gpnum, 1); spin_unlock(rnp_lock); } // nocb_gp_wait() P1(spinlock_t *rnp_lock, spinlock_t *nocb_lock, int *gpnum, int *cb_ready) { int r1; // Call rcu_advance_cbs() from nocb_gp_wait() spin_lock(nocb_lock); spin_lock(rnp_lock); smp_mb__after_unlock_lock(); r1 = READ_ONCE(*gpnum); WRITE_ONCE(*cb_ready, 1); spin_unlock(rnp_lock); spin_unlock(nocb_lock); } // nocb_cb_wait() P2(spinlock_t *nocb_lock, int *cb_ready, int *cb_executed) { int r2; // rcu_do_batch() -> rcu_segcblist_extract_done_cbs() spin_lock(nocb_lock); r2 = READ_ONCE(*cb_ready); spin_unlock(nocb_lock); // Actual callback execution WRITE_ONCE(*cb_executed, 1); } P3(int *cb_executed, int *gpnum) { int r3; WRITE_ONCE(*cb_executed, 2); smp_mb(); r3 = READ_ONCE(*gpnum); } exists (1:r1=1 /\ 2:r2=1 /\ cb_executed=2 /\ 3:r3=0) (* Bad outcome. *) Here the bad outcome only occurs if the smp_mb__after_unlock_lock() is removed. This barrier orders the grace period completion against callbacks advancing and even later callbacks invocation, thanks to the opportunistic propagation via the ->nocb_lock to nocb_cb_wait(). Therefore the smp_mb() placed after callbacks advancing can be safely removed. Signed-off-by: Frederic Weisbecker <frederic@kernel.org> Reviewed-by: Paul E. McKenney <paulmck@kernel.org> Signed-off-by: Boqun Feng <boqun.feng@gmail.com>
1 parent ca16265 commit 1e8e695

File tree

2 files changed

+6
-1
lines changed

2 files changed

+6
-1
lines changed

kernel/rcu/tree.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2145,6 +2145,12 @@ static void rcu_do_batch(struct rcu_data *rdp)
21452145
* Extract the list of ready callbacks, disabling IRQs to prevent
21462146
* races with call_rcu() from interrupt handlers. Leave the
21472147
* callback counts, as rcu_barrier() needs to be conservative.
2148+
*
2149+
* Callbacks execution is fully ordered against preceding grace period
2150+
* completion (materialized by rnp->gp_seq update) thanks to the
2151+
* smp_mb__after_unlock_lock() upon node locking required for callbacks
2152+
* advancing. In NOCB mode this ordering is then further relayed through
2153+
* the nocb locking that protects both callbacks advancing and extraction.
21482154
*/
21492155
rcu_nocb_lock_irqsave(rdp, flags);
21502156
WARN_ON_ONCE(cpu_is_offline(smp_processor_id()));

kernel/rcu/tree_nocb.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -779,7 +779,6 @@ static void nocb_gp_wait(struct rcu_data *my_rdp)
779779
if (rcu_segcblist_ready_cbs(&rdp->cblist)) {
780780
needwake = rdp->nocb_cb_sleep;
781781
WRITE_ONCE(rdp->nocb_cb_sleep, false);
782-
smp_mb(); /* CB invocation -after- GP end. */
783782
} else {
784783
needwake = false;
785784
}

0 commit comments

Comments
 (0)