aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/rcutree_plugin.h
diff options
context:
space:
mode:
authorPaul E. McKenney <paul.mckenney@linaro.org>2012-04-30 17:16:19 -0400
committerPaul E. McKenney <paulmck@linux.vnet.ibm.com>2012-05-09 17:26:56 -0400
commit21e52e15666323078b8517a4312712579176b56f (patch)
tree6e9e75c6e1400e0426a7c7985a3f986fea5a7782 /kernel/rcutree_plugin.h
parentf511fc624642f0bb8cf65aaa28979737514d4746 (diff)
rcu: Make RCU_FAST_NO_HZ handle timer migration
The current RCU_FAST_NO_HZ assumes that timers do not migrate unless a CPU goes offline, in which case it assumes that the CPU will have to come out of dyntick-idle mode (cancelling the timer) in order to go offline. This is important because when RCU_FAST_NO_HZ permits a CPU to enter dyntick-idle mode despite having RCU callbacks pending, it posts a timer on that CPU to force a wakeup on that CPU. This wakeup ensures that the CPU will eventually handle the end of the grace period, including invoking its RCU callbacks. However, Pascal Chapperon's test setup shows that the timer handler rcu_idle_gp_timer_func() really does get invoked in some cases. This is problematic because this can cause the CPU that entered dyntick-idle mode despite still having RCU callbacks pending to remain in dyntick-idle mode indefinitely, which means that its RCU callbacks might never be invoked. This situation can result in grace-period delays or even system hangs, which matches Pascal's observations of slow boot-up and shutdown (https://lkml.org/lkml/2012/4/5/142). See also the bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=806548 This commit therefore causes the "should never be invoked" timer handler rcu_idle_gp_timer_func() to use smp_call_function_single() to wake up the CPU for which the timer was intended, allowing that CPU to invoke its RCU callbacks in a timely manner. Reported-by: Pascal Chapperon <pascal.chapperon@wanadoo.fr> Signed-off-by: Paul E. McKenney <paul.mckenney@linaro.org> Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Diffstat (limited to 'kernel/rcutree_plugin.h')
-rw-r--r--kernel/rcutree_plugin.h24
1 files changed, 21 insertions, 3 deletions
diff --git a/kernel/rcutree_plugin.h b/kernel/rcutree_plugin.h
index d01e26df55a1..bbb43cad755e 100644
--- a/kernel/rcutree_plugin.h
+++ b/kernel/rcutree_plugin.h
@@ -2057,16 +2057,34 @@ static bool rcu_cpu_has_nonlazy_callbacks(int cpu)
2057} 2057}
2058 2058
2059/* 2059/*
2060 * Handler for smp_call_function_single(). The only point of this
2061 * handler is to wake the CPU up, so the handler does only tracing.
2062 */
2063void rcu_idle_demigrate(void *unused)
2064{
2065 trace_rcu_prep_idle("Demigrate");
2066}
2067
2068/*
2060 * Timer handler used to force CPU to start pushing its remaining RCU 2069 * Timer handler used to force CPU to start pushing its remaining RCU
2061 * callbacks in the case where it entered dyntick-idle mode with callbacks 2070 * callbacks in the case where it entered dyntick-idle mode with callbacks
2062 * pending. The hander doesn't really need to do anything because the 2071 * pending. The hander doesn't really need to do anything because the
2063 * real work is done upon re-entry to idle, or by the next scheduling-clock 2072 * real work is done upon re-entry to idle, or by the next scheduling-clock
2064 * interrupt should idle not be re-entered. 2073 * interrupt should idle not be re-entered.
2074 *
2075 * One special case: the timer gets migrated without awakening the CPU
2076 * on which the timer was scheduled on. In this case, we must wake up
2077 * that CPU. We do so with smp_call_function_single().
2065 */ 2078 */
2066static void rcu_idle_gp_timer_func(unsigned long unused) 2079static void rcu_idle_gp_timer_func(unsigned long cpu_in)
2067{ 2080{
2068 WARN_ON_ONCE(1); /* Getting here can hang the system... */ 2081 int cpu = (int)cpu_in;
2082
2069 trace_rcu_prep_idle("Timer"); 2083 trace_rcu_prep_idle("Timer");
2084 if (cpu != smp_processor_id())
2085 smp_call_function_single(cpu, rcu_idle_demigrate, NULL, 0);
2086 else
2087 WARN_ON_ONCE(1); /* Getting here can hang the system... */
2070} 2088}
2071 2089
2072/* 2090/*
@@ -2075,7 +2093,7 @@ static void rcu_idle_gp_timer_func(unsigned long unused)
2075static void rcu_prepare_for_idle_init(int cpu) 2093static void rcu_prepare_for_idle_init(int cpu)
2076{ 2094{
2077 setup_timer(&per_cpu(rcu_idle_gp_timer, cpu), 2095 setup_timer(&per_cpu(rcu_idle_gp_timer, cpu),
2078 rcu_idle_gp_timer_func, 0); 2096 rcu_idle_gp_timer_func, cpu);
2079} 2097}
2080 2098
2081/* 2099/*