}
}
-static int collect_expired_timers(struct timer_base *base,
- struct hlist_head *heads)
+static int __collect_expired_timers(struct timer_base *base,
+ struct hlist_head *heads)
{
unsigned long clk = base->clk;
struct hlist_head *vec;
#ifdef CONFIG_NO_HZ_COMMON
/*
- * Find the next pending bucket of a level. Search from @offset + @clk upwards
- * and if nothing there, search from start of the level (@offset) up to
- * @offset + clk.
+ * Find the next pending bucket of a level. Search from level start (@offset)
+ * + @clk upwards and if nothing there, search from start of the level
+ * (@offset) up to @offset + clk.
*/
static int next_pending_bucket(struct timer_base *base, unsigned offset,
unsigned clk)
}
/*
- * Search the first expiring timer in the various clock levels.
+ * Search the first expiring timer in the various clock levels. Caller must
+ * hold base->lock.
*/
static unsigned long __next_timer_interrupt(struct timer_base *base)
{
unsigned long clk, next, adj;
unsigned lvl, offset = 0;
- spin_lock(&base->lock);
next = base->clk + NEXT_TIMER_MAX_DELTA;
clk = base->clk;
for (lvl = 0; lvl < LVL_DEPTH; lvl++, offset += LVL_SIZE) {
clk >>= LVL_CLK_SHIFT;
clk += adj;
}
- spin_unlock(&base->lock);
return next;
}
if (cpu_is_offline(smp_processor_id()))
return expires;
+ spin_lock(&base->lock);
nextevt = __next_timer_interrupt(base);
+ spin_unlock(&base->lock);
+
if (time_before_eq(nextevt, basej))
expires = basem;
else
return cmp_next_hrtimer_event(basem, expires);
}
+
+static int collect_expired_timers(struct timer_base *base,
+ struct hlist_head *heads)
+{
+ /*
+ * NOHZ optimization. After a long idle sleep we need to forward the
+ * base to current jiffies. Avoid a loop by searching the bitfield for
+ * the next expiring timer.
+ */
+ if ((long)(jiffies - base->clk) > 2) {
+ unsigned long next = __next_timer_interrupt(base);
+
+ /*
+ * If the next timer is ahead of time forward to current
+ * jiffies, otherwise forward to the next expiry time.
+ */
+ if (time_after(next, jiffies)) {
+ /* The call site will increment clock! */
+ base->clk = jiffies - 1;
+ return 0;
+ }
+ base->clk = next;
+ }
+ return __collect_expired_timers(base, heads);
+}
+#else
+static inline int collect_expired_timers(struct timer_base *base,
+ struct hlist_head *heads)
+{
+ return __collect_expired_timers(base, heads);
+}
#endif
/*