rcu: Increment upper bit only for srcu_read_lock()
authorLai Jiangshan <laijs@cn.fujitsu.com>
Wed, 22 Feb 2012 21:29:06 +0000 (13:29 -0800)
committerPaul E. McKenney <paulmck@linux.vnet.ibm.com>
Mon, 30 Apr 2012 17:48:20 +0000 (10:48 -0700)
The purpose of the upper bit of SRCU's per-CPU counters is to guarantee
that no reasonable series of srcu_read_lock() and srcu_read_unlock()
operations can return the value of the counter to its original value.
This guarantee is require only after the index has been switched to
the other set of counters, so at most one srcu_read_lock() can affect
a given CPU's counter.  The number of srcu_read_unlock() operations
on a given counter is limited to the number of tasks in the system,
which given the Linux kernel's current structure is limited to far less
than 2^30 on 32-bit systems and far less than 2^62 on 64-bit systems.
(Something about a limited number of bytes in the kernel's address space.)

Therefore, if srcu_read_lock() increments the upper bits, then
srcu_read_unlock() need not do so.  In this case, an srcu_read_lock() and
an srcu_read_unlock() will flip the lower bit of the upper field of the
counter.  An unreasonably large additional number of srcu_read_unlock()
operations would be required to return the counter to its initial value,
thus preserving the guarantee.

This commit takes this approach, which further allows it to shrink
the size of the upper field to one bit, making the number of
srcu_read_unlock() operations required to return the counter to its
initial value even more unreasonable than before.

Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com>
Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
include/linux/srcu.h
kernel/srcu.c

index a478c8eb84795cdb0ff437e38ccd7a33854ffc13..5b49d41868c83fc926c62b694f979cb3f8e480e4 100644 (file)
@@ -35,7 +35,7 @@ struct srcu_struct_array {
 };
 
 /* Bit definitions for field ->c above and ->snap below. */
-#define SRCU_USAGE_BITS                2
+#define SRCU_USAGE_BITS                1
 #define SRCU_REF_MASK          (ULONG_MAX >> SRCU_USAGE_BITS)
 #define SRCU_USAGE_COUNT       (SRCU_REF_MASK + 1)
 
index 17e95bcc901c94b19564b4521fa7478e83c3736e..43f1d61e513e95a10d985cc2a9a4c88ccaeedaf7 100644 (file)
@@ -138,14 +138,14 @@ static bool srcu_readers_active_idx_check(struct srcu_struct *sp, int idx)
 
        /*
         * Now, we check the ->snap array that srcu_readers_active_idx()
-        * filled in from the per-CPU counter values.  Since both
-        * __srcu_read_lock() and __srcu_read_unlock() increment the
-        * upper bits of the per-CPU counter, an increment/decrement
-        * pair will change the value of the counter.  Since there is
-        * only one possible increment, the only way to wrap the counter
-        * is to have a huge number of counter decrements, which requires
-        * a huge number of tasks and huge SRCU read-side critical-section
-        * nesting levels, even on 32-bit systems.
+        * filled in from the per-CPU counter values. Since
+        * __srcu_read_lock() increments the upper bits of the per-CPU
+        * counter, an increment/decrement pair will change the value
+        * of the counter.  Since there is only one possible increment,
+        * the only way to wrap the counter is to have a huge number of
+        * counter decrements, which requires a huge number of tasks and
+        * huge SRCU read-side critical-section nesting levels, even on
+        * 32-bit systems.
         *
         * All of the ways of confusing the readings require that the scan
         * in srcu_readers_active_idx() see the read-side task's decrement,
@@ -234,8 +234,7 @@ void __srcu_read_unlock(struct srcu_struct *sp, int idx)
 {
        preempt_disable();
        smp_mb(); /* C */  /* Avoid leaking the critical section. */
-       ACCESS_ONCE(this_cpu_ptr(sp->per_cpu_ref)->c[idx]) +=
-               SRCU_USAGE_COUNT - 1;
+       ACCESS_ONCE(this_cpu_ptr(sp->per_cpu_ref)->c[idx]) -= 1;
        preempt_enable();
 }
 EXPORT_SYMBOL_GPL(__srcu_read_unlock);