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)
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)
{
*/
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);