On Mon, Jan 23, 2017 at 07:26:45PM -0800, Lance Roy wrote:
> > Yeah, we did have this same conversation awhile back, didn't we?
> > 
> > Back then, did I think to ask if this could be minimized or even prevented
> > by adding memory barriers appropriately?  ;-)
> > 
> >                                                     Thanx, Paul
> 
> Yes, it can be fixed by adding a memory barrier after incrementing ->completed
> inside srcu_flip(). The upper limit on NR_CPUS turns out to be more 
> complicated
> than this, as it needs to deal with highly nested read side critical sections
> mixed with the critical section loops, but only the one memory barrier should
> be necessary.

Something like this, then?

                                                        Thanx, Paul

------------------------------------------------------------------------

commit 35be9e413dde662fc9661352e595105ac4b0b167
Author: Paul E. McKenney <paul...@linux.vnet.ibm.com>
Date:   Tue Jan 24 08:51:34 2017 -0800

    srcu: Reduce probability of SRCU ->unlock_count[] counter overflow
    
    Because there are no memory barriers between the srcu_flip() ->completed
    increment and the summation of the read-side ->unlock_count[] counters,
    both the compiler and the CPU can reorder the summation with the
    ->completed increment.  If the updater is preempted long enough during
    this process, the read-side counters could overflow, resulting in a
    too-short grace period.
    
    This commit therefore adds a memory barrier just after the ->completed
    increment, ensuring that if the summation misses an increment of
    ->unlock_count[] from __srcu_read_unlock(), the next __srcu_read_lock()
    will see the new value of ->completed, thus bounding the number of
    ->unlock_count[] increments that can be missed to NR_CPUS.  The actual
    overflow computation is more complex due to the possibility of nesting
    of __srcu_read_lock().
    
    Reported-by: Lance Roy <ldr...@gmail.com>
    Signed-off-by: Paul E. McKenney <paul...@linux.vnet.ibm.com>

diff --git a/kernel/rcu/srcu.c b/kernel/rcu/srcu.c
index d3378ceb9762..aefe3ab20a6a 100644
--- a/kernel/rcu/srcu.c
+++ b/kernel/rcu/srcu.c
@@ -337,7 +337,16 @@ static bool try_check_zero(struct srcu_struct *sp, int 
idx, int trycount)
  */
 static void srcu_flip(struct srcu_struct *sp)
 {
-       sp->completed++;
+       WRITE_ONCE(sp->completed, sp->completed + 1);
+
+       /*
+        * Ensure that if the updater misses an __srcu_read_unlock()
+        * increment, that task's next __srcu_read_lock() will see the
+        * above counter update.  Note that both this memory barrier
+        * and the one in srcu_readers_active_idx_check() provide the
+        * guarantee for __srcu_read_lock().
+        */
+       smp_mb(); /* D */  /* Pairs with C. */
 }
 
 /*

Reply via email to