Commit 6d8133919bac4270883b24328500875a49e71b36

Authored by Paul E. McKenney
Committed by Paul E. McKenney
1 parent dabb8aa960

rcu: Document why rcu_blocking_is_gp() is safe

The rcu_blocking_is_gp() function tests to see if there is only one
online CPU, and if so, synchronize_sched() and friends become no-ops.
However, for larger systems, num_online_cpus() scans a large vector,
and might be preempted while doing so.  While preempted, any number
of CPUs might come online and go offline, potentially resulting in
num_online_cpus() returning 1 when there never had only been one
CPU online.  This could result in a too-short RCU grace period, which
could in turn result in total failure, except that the only way that
the grace period is too short is if there is an RCU read-side critical
section spanning it.  For RCU-sched and RCU-bh (which are the only
cases using rcu_blocking_is_gp()), RCU read-side critical sections
have either preemption or bh disabled, which prevents CPUs from going
offline.  This in turn prevents actual failures from occurring.

This commit therefore adds a large block comment to rcu_blocking_is_gp()
documenting why it is safe.  This commit also moves rcu_blocking_is_gp()
into kernel/rcutree.c, which should help prevent unwary developers from
mistaking it for a generally useful function.

Signed-off-by: Paul E. McKenney <paul.mckenney@linaro.org>
Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>

Showing 2 changed files with 32 additions and 7 deletions Side-by-side Diff

include/linux/rcutree.h
... ... @@ -98,13 +98,6 @@
98 98 extern void rcu_bh_force_quiescent_state(void);
99 99 extern void rcu_sched_force_quiescent_state(void);
100 100  
101   -/* A context switch is a grace period for RCU-sched and RCU-bh. */
102   -static inline int rcu_blocking_is_gp(void)
103   -{
104   - might_sleep(); /* Check for RCU read-side critical section. */
105   - return num_online_cpus() == 1;
106   -}
107   -
108 101 extern void rcu_scheduler_starting(void);
109 102 extern int rcu_scheduler_active __read_mostly;
110 103  
... ... @@ -1894,6 +1894,38 @@
1894 1894 }
1895 1895 EXPORT_SYMBOL_GPL(call_rcu_bh);
1896 1896  
  1897 +/*
  1898 + * Because a context switch is a grace period for RCU-sched and RCU-bh,
  1899 + * any blocking grace-period wait automatically implies a grace period
  1900 + * if there is only one CPU online at any point time during execution
  1901 + * of either synchronize_sched() or synchronize_rcu_bh(). It is OK to
  1902 + * occasionally incorrectly indicate that there are multiple CPUs online
  1903 + * when there was in fact only one the whole time, as this just adds
  1904 + * some overhead: RCU still operates correctly.
  1905 + *
  1906 + * Of course, sampling num_online_cpus() with preemption enabled can
  1907 + * give erroneous results if there are concurrent CPU-hotplug operations.
  1908 + * For example, given a demonic sequence of preemptions in num_online_cpus()
  1909 + * and CPU-hotplug operations, there could be two or more CPUs online at
  1910 + * all times, but num_online_cpus() might well return one (or even zero).
  1911 + *
  1912 + * However, all such demonic sequences require at least one CPU-offline
  1913 + * operation. Furthermore, rcu_blocking_is_gp() giving the wrong answer
  1914 + * is only a problem if there is an RCU read-side critical section executing
  1915 + * throughout. But RCU-sched and RCU-bh read-side critical sections
  1916 + * disable either preemption or bh, which prevents a CPU from going offline.
  1917 + * Therefore, the only way that rcu_blocking_is_gp() can incorrectly return
  1918 + * that there is only one CPU when in fact there was more than one throughout
  1919 + * is when there were no RCU readers in the system. If there are no
  1920 + * RCU readers, the grace period by definition can be of zero length,
  1921 + * regardless of the number of online CPUs.
  1922 + */
  1923 +static inline int rcu_blocking_is_gp(void)
  1924 +{
  1925 + might_sleep(); /* Check for RCU read-side critical section. */
  1926 + return num_online_cpus() <= 1;
  1927 +}
  1928 +
1897 1929 /**
1898 1930 * synchronize_sched - wait until an rcu-sched grace period has elapsed.
1899 1931 *