From: Dario Faggioli Date: Mon, 9 Oct 2017 11:22:07 +0000 (+0200) Subject: RCU: let the RCU idle timer handler run X-Git-Tag: 4.10.0-rc1~122 X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=9e0a3a2a9143c17319660e22dcf4b7a59fa9473e;p=people%2Fdariof%2Fxen.git RCU: let the RCU idle timer handler run If stop_timer() is called between when the RCU idle timer's interrupt arrives (and TIMER_SOFTIRQ is raised) and when softirqs are checked and handled, the timer is deactivated, and the handler never runs. This happens to the RCU idle timer because stop_timer() is called on it during the wakeup from idle (e.g., C-states, on x86) path. To fix that, we avoid calling stop_timer(), in case we see that the timer itself is: - still active, - expired (i.e., it's expiry time is in the past). In fact, that indicates (for this particular timer) that it has fired, and we are just about to handle the TIMER_SOFTIRQ (which will perform the timer deactivation and run its handler). Signed-off-by: Dario Faggioli Reviewed-by: Jan Beulich --- diff --git a/xen/common/rcupdate.c b/xen/common/rcupdate.c index 871936f11b..252e01b9c0 100644 --- a/xen/common/rcupdate.c +++ b/xen/common/rcupdate.c @@ -465,7 +465,24 @@ void rcu_idle_timer_stop() return; rdp->idle_timer_active = false; - stop_timer(&rdp->idle_timer); + + /* + * In general, as the CPU is becoming active again, we don't need the + * idle timer, and so we want to stop it. + * + * However, in case we are here because idle_timer has (just) fired and + * has woken up the CPU, we skip stop_timer() now. In fact, when a CPU + * wakes up from idle, this code always runs before do_softirq() has the + * chance to check and deal with TIMER_SOFTIRQ. And if we stop the timer + * now, the TIMER_SOFTIRQ handler will see it as inactive, and will not + * call rcu_idle_timer_handler(). + * + * Therefore, if we see that the timer is expired already, we leave it + * alone. The TIMER_SOFTIRQ handler will then run the timer routine, and + * deactivate it. + */ + if ( !timer_is_expired(&rdp->idle_timer) ) + stop_timer(&rdp->idle_timer); } static void rcu_idle_timer_handler(void* data) diff --git a/xen/common/timer.c b/xen/common/timer.c index d9ff669513..376581bd54 100644 --- a/xen/common/timer.c +++ b/xen/common/timer.c @@ -331,6 +331,20 @@ void stop_timer(struct timer *timer) timer_unlock_irqrestore(timer, flags); } +bool timer_expires_before(struct timer *timer, s_time_t t) +{ + unsigned long flags; + bool ret; + + if ( !timer_lock_irqsave(timer, flags) ) + return false; + + ret = active_timer(timer) && timer->expires <= t; + + timer_unlock_irqrestore(timer, flags); + + return ret; +} void migrate_timer(struct timer *timer, unsigned int new_cpu) { diff --git a/xen/include/xen/timer.h b/xen/include/xen/timer.h index 9531800def..4513260b0d 100644 --- a/xen/include/xen/timer.h +++ b/xen/include/xen/timer.h @@ -70,6 +70,11 @@ void set_timer(struct timer *timer, s_time_t expires); */ void stop_timer(struct timer *timer); +/* True if a timer is active, and its expiry time is earlier than t. */ +bool timer_expires_before(struct timer *timer, s_time_t t); + +#define timer_is_expired(t) timer_expires_before(t, NOW()) + /* Migrate a timer to a different CPU. The timer may be currently active. */ void migrate_timer(struct timer *timer, unsigned int new_cpu);