The key point behind SRCU is that synchronize_sched()
blocks until all currently-executing preempt-disabled regions of
code complete.
The synchronize_srcu() primitive makes heavy use of this effect,
as can be seen in
Figure .
Line 5 takes a snapshot of the grace-period counter. Line 6 acquires the mutex, and lines 7-10 check to see whether at least two grace periods have elapsed since the snapshot, and, if so, releases the lock and returns -- in this case, someone else has done our work for us. Otherwise, line 11 guarantees that any other CPU that sees the incremented value of the grace period counter in srcu_read_lock() also sees any changes made by this CPU prior to entering synchronize_srcu(). This guarantee is required to make sure that any SRCU read-side critical sections not blocking the next grace period have seen any prior changes.
Line 12 fetches the bottom bit of the grace-period counter for later use as an index into the per-CPU counter arrays, and then line 13 increments the grace-period counter. Line 14 then waits for any currently-executing srcu_read_lock() to complete, so that by the time that we reach line 15, all extant instances of srcu_read_lock() will be using the updated value from sp->completed. Therefore, the counters sampled in by srcu_readers_active_idx() on line 15 are guaranteed to be monotonically decreasing, so that once their sum reaches zero, it is guaranteed to stay there.
However, there are no memory barriers in the srcu_read_unlock() primitive, so the CPU is within its rights to reorder the counter decrement up into the SRCU critical section, so that references to an SRCU-protected data structure could in effect ``bleed out'' of the SRCU critical section. This scenario is addressed by the synchronize_sched() on line 17, which blocks until all other CPUs executing in preempt_disable() code sequences (such as that in srcu_read_unlock()) complete these sequences. Because completion of a given preempt_disable() code sequence is observed from the CPU executing that sequence, completion of the sequence implies completion of any prior SRCU read-side critical section. Any required memory barriers are supplied by the code making the observation.
At this point, it is therefore safe to release the mutex as shown on line 18 and return to the caller, who can now be assured that all SRCU read-side critical sections sharing the same struct srcu_struct will observe any update made prior to the call to synchronize_srcu().
Quick Quiz D.3: Why is it OK to assume that updates separated by synchronize_sched() will be performed in order? End Quick Quiz
Quick Quiz D.4:
Why must line 17 in synchronize_srcu()
(Figure )
precede the release of the mutex on line 18?
What would have to change to permit these two lines to be
interchanged?
Would such a change be worthwhile?
Why or why not?
End Quick Quiz
Paul E. McKenney 2011-12-16