• Frederic Weisbecker's avatar
    rcu/nocb: Remove needless full barrier after callback advancing · 1e8e6951
    Frederic Weisbecker authored
    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: default avatarFrederic Weisbecker <frederic@kernel.org>
    Reviewed-by: default avatarPaul E. McKenney <paulmck@kernel.org>
    Signed-off-by: default avatarBoqun Feng <boqun.feng@gmail.com>
    1e8e6951
tree.c 169 KB