rcu: Add RCU-sched flavors of get-state and cond-sync
authorPaul E. McKenney <paulmck@linux.vnet.ibm.com>
Sat, 30 May 2015 17:11:24 +0000 (10:11 -0700)
committerPaul E. McKenney <paulmck@linux.vnet.ibm.com>
Wed, 22 Jul 2015 22:26:58 +0000 (15:26 -0700)
The get_state_synchronize_rcu() and cond_synchronize_rcu() functions
allow polling for grace-period completion, with an actual wait for a
grace period occurring only when cond_synchronize_rcu() is called too
soon after the corresponding get_state_synchronize_rcu().  However,
these functions work only for vanilla RCU.  This commit adds the
get_state_synchronize_sched() and cond_synchronize_sched(), which provide
the same capability for RCU-sched.

Reported-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
include/linux/rcutiny.h
include/linux/rcutree.h
kernel/rcu/rcutorture.c
kernel/rcu/tree.c

index 3df6c1ec4e25503583cb14656474d6727495a530..ff968b7af3a4b61c9496f5222a7f600a42f5eca6 100644 (file)
@@ -37,6 +37,16 @@ static inline void cond_synchronize_rcu(unsigned long oldstate)
        might_sleep();
 }
 
+static inline unsigned long get_state_synchronize_sched(void)
+{
+       return 0;
+}
+
+static inline void cond_synchronize_sched(unsigned long oldstate)
+{
+       might_sleep();
+}
+
 static inline void rcu_barrier_bh(void)
 {
        wait_rcu_gp(call_rcu_bh);
index 456879143f89f9db45d0f79315f728f50a9f9d0c..5abec82f325ecf5e2a0c06c21d566f94f5559721 100644 (file)
@@ -76,6 +76,8 @@ void rcu_barrier_bh(void);
 void rcu_barrier_sched(void);
 unsigned long get_state_synchronize_rcu(void);
 void cond_synchronize_rcu(unsigned long oldstate);
+unsigned long get_state_synchronize_sched(void);
+void cond_synchronize_sched(unsigned long oldstate);
 
 extern unsigned long rcutorture_testseq;
 extern unsigned long rcutorture_vernum;
index 59e32684c23b58714ecb26215856f67866cfc58e..0f2cb55f0ab3b917b33e1df7caa17cb12dc7bbfc 100644 (file)
@@ -635,6 +635,8 @@ static struct rcu_torture_ops sched_ops = {
        .deferred_free  = rcu_sched_torture_deferred_free,
        .sync           = synchronize_sched,
        .exp_sync       = synchronize_sched_expedited,
+       .get_state      = get_state_synchronize_sched,
+       .cond_sync      = cond_synchronize_sched,
        .call           = call_rcu_sched,
        .cb_barrier     = rcu_barrier_sched,
        .fqs            = rcu_sched_force_quiescent_state,
index 8b5dd8ba9495b13960d5390f439cd7492c235cab..9629298eea240300a7cca3a5e299f88917e54eae 100644 (file)
@@ -3253,6 +3253,58 @@ void cond_synchronize_rcu(unsigned long oldstate)
 }
 EXPORT_SYMBOL_GPL(cond_synchronize_rcu);
 
+/**
+ * get_state_synchronize_sched - Snapshot current RCU-sched state
+ *
+ * Returns a cookie that is used by a later call to cond_synchronize_sched()
+ * to determine whether or not a full grace period has elapsed in the
+ * meantime.
+ */
+unsigned long get_state_synchronize_sched(void)
+{
+       /*
+        * Any prior manipulation of RCU-protected data must happen
+        * before the load from ->gpnum.
+        */
+       smp_mb();  /* ^^^ */
+
+       /*
+        * Make sure this load happens before the purportedly
+        * time-consuming work between get_state_synchronize_sched()
+        * and cond_synchronize_sched().
+        */
+       return smp_load_acquire(&rcu_sched_state.gpnum);
+}
+EXPORT_SYMBOL_GPL(get_state_synchronize_sched);
+
+/**
+ * cond_synchronize_sched - Conditionally wait for an RCU-sched grace period
+ *
+ * @oldstate: return value from earlier call to get_state_synchronize_sched()
+ *
+ * If a full RCU-sched grace period has elapsed since the earlier call to
+ * get_state_synchronize_sched(), just return.  Otherwise, invoke
+ * synchronize_sched() to wait for a full grace period.
+ *
+ * Yes, this function does not take counter wrap into account.  But
+ * counter wrap is harmless.  If the counter wraps, we have waited for
+ * more than 2 billion grace periods (and way more on a 64-bit system!),
+ * so waiting for one additional grace period should be just fine.
+ */
+void cond_synchronize_sched(unsigned long oldstate)
+{
+       unsigned long newstate;
+
+       /*
+        * Ensure that this load happens before any RCU-destructive
+        * actions the caller might carry out after we return.
+        */
+       newstate = smp_load_acquire(&rcu_sched_state.completed);
+       if (ULONG_CMP_GE(oldstate, newstate))
+               synchronize_sched();
+}
+EXPORT_SYMBOL_GPL(cond_synchronize_sched);
+
 static int synchronize_sched_expedited_cpu_stop(void *data)
 {
        /*