D.1.3.3 Read-Side Implementation

The code implementing srcu_read_lock() is shown in Figure [*]. This function has been carefully constructed to avoid the need for memory barriers and atomic instructions.

Lines 5 and 11 disable and re-enable preemption, in order to force the sequence of code to execute unpreempted on a single CPU. Line 6 picks up the bottom bit of the grace-period counter, which will be used to select which rank of per-CPU counters is to be used for this SRCU read-side critical section. The barrier() call on line 7 is a directive to the compiler that ensures that the index is fetched but once,D.2so that the index used on line 9 is the same one returned on line 12. Lines 8-9 increment the selected counter for the current CPU.D.3Line 10 forces subsequent execution to occur after lines 8-9, in order to prevent to misordering of any code in a non-CONFIG_PREEMPT build, but only from the perspective of an intervening interrupt handler. However, in a CONFIG_PREEMPT kernel, the required barrier() call is embedded in the preempt_enable() on line 11, so the srcu_barrier() is a no-op in that case. Finally, line 12 returns the index so that it may be passed in to the corresponding srcu_read_unlock().

Figure: SRCU Read-Side Acquisition
\begin{figure}{ \scriptsize
\begin{verbatim}1 int srcu_read_lock(struct srcu_...
...ier();
11 preempt_enable();
12 return idx;
13 }\end{verbatim}
}\end{figure}

The code for srcu_read_unlock() is shown in Figure [*]. Again, lines 3 and 7 disable and re-enable preemption so that the whole code sequence executes unpreempted on a single CPU. In CONFIG_PREEMPT kernels, the preempt_disable() on line 3 contains a barrier() primitive, otherwise, the barrier() is supplied by line 4. Again, this directive forces the subsequent code to execute after the critical section from the perspective of intervening interrupt handlers. Lines 5 and 6 decrement the counter for this CPU, but with the same index as was used by the corresponding srcu_read_lock().

Figure: SRCU Read-Side Release
\begin{figure}{ \scriptsize
\begin{verbatim}1 void srcu_read_unlock(struct sr...
...ocessor_id())->c[idx]--;
7 preempt_enable();
8 }\end{verbatim}
}\end{figure}

The key point is that a given CPU's counters can be observed by other CPUs only in cooperation with that CPU's interrupt handlers. These interrupt handlers are responsible for ensuring that any needed memory barriers are executed prior to observing the counters.

Paul E. McKenney 2011-12-16