[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH v2 1/3] xen: 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 <dario.faggioli@xxxxxxxxxx> --- Cc: Andrew Cooper <andrew.cooper3@xxxxxxxxxx> Cc: George Dunlap <George.Dunlap@xxxxxxxxxxxxx> Cc: Jan Beulich <jbeulich@xxxxxxxx> Cc: Stefano Stabellini <sstabellini@xxxxxxxxxx> Cc: Julien Grall <julien.grall@xxxxxxx> Cc: Tim Deegan <tim@xxxxxxx> --- Changes from v1: - logic changed completely: instead of avoiding deactivate_timer() in stop_timer(), we avoid stop_timer() in rcu_idle_timer_stop() (if appropriate, of course). --- xen/common/rcupdate.c | 16 +++++++++++++++- xen/common/timer.c | 17 +++++++++++++++++ xen/include/xen/timer.h | 3 +++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/xen/common/rcupdate.c b/xen/common/rcupdate.c index 871936f..4a02cdd 100644 --- a/xen/common/rcupdate.c +++ b/xen/common/rcupdate.c @@ -465,7 +465,21 @@ 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, if we stop + * it, then the TIMER_SOFTIRQ handler wouldn't find idle_timer among the + * active timers any longer, and hence won't call rcu_idle_timer_handler(). + * + * Therefore, if we see that the timer is expired already, leave it alone. + * It will be finally deactiveted by the TIMER_SOFTIRQ handler. + */ + if ( !timer_is_expired(&rdp->idle_timer, NOW()) ) + 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 d9ff669..a768aa3 100644 --- a/xen/common/timer.c +++ b/xen/common/timer.c @@ -332,6 +332,23 @@ void stop_timer(struct timer *timer) } +bool timer_is_expired(struct timer *timer, s_time_t now) +{ + unsigned long flags; + bool ret = false; + + if ( !timer_lock_irqsave(timer, flags) ) + return ret; + + if ( active_timer(timer) && timer->expires <= now ) + ret = true; + + timer_unlock_irqrestore(timer, flags); + + return ret; +} + + void migrate_timer(struct timer *timer, unsigned int new_cpu) { unsigned int old_cpu; diff --git a/xen/include/xen/timer.h b/xen/include/xen/timer.h index 9531800..a095007 100644 --- a/xen/include/xen/timer.h +++ b/xen/include/xen/timer.h @@ -70,6 +70,9 @@ void set_timer(struct timer *timer, s_time_t expires); */ void stop_timer(struct timer *timer); +/* True if a timer is active, but with its expiry time before now. */ +bool timer_is_expired(struct timer *timer, s_time_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); _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx https://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |