Skip to content

Commit 2369683

Browse files
anna-marialxIngo Molnar
authored andcommitted
timers: Optimize collect_expired_timers() for NOHZ
After a NOHZ idle sleep the timer wheel must be forwarded to current jiffies. There might be expired timers so the current code loops and checks the expired buckets for timers. This can take quite some time for long NOHZ idle periods. The pending bitmask in the timer base allows us to do a quick search for the next expiring timer and therefore a fast forward of the base time which prevents pointless long lasting loops. For a 3 seconds idle sleep this reduces the catchup time from ~1ms to 5us. Signed-off-by: Anna-Maria Gleixner <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Cc: Arjan van de Ven <[email protected]> Cc: Chris Mason <[email protected]> Cc: Eric Dumazet <[email protected]> Cc: Frederic Weisbecker <[email protected]> Cc: George Spelvin <[email protected]> Cc: Josh Triplett <[email protected]> Cc: Len Brown <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Paul E. McKenney <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Rik van Riel <[email protected]> Cc: [email protected] Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Ingo Molnar <[email protected]>
1 parent 73420fe commit 2369683

File tree

1 file changed

+41
-8
lines changed

1 file changed

+41
-8
lines changed

kernel/time/timer.c

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,8 +1252,8 @@ static void expire_timers(struct timer_base *base, struct hlist_head *head)
12521252
}
12531253
}
12541254

1255-
static int collect_expired_timers(struct timer_base *base,
1256-
struct hlist_head *heads)
1255+
static int __collect_expired_timers(struct timer_base *base,
1256+
struct hlist_head *heads)
12571257
{
12581258
unsigned long clk = base->clk;
12591259
struct hlist_head *vec;
@@ -1279,9 +1279,9 @@ static int collect_expired_timers(struct timer_base *base,
12791279

12801280
#ifdef CONFIG_NO_HZ_COMMON
12811281
/*
1282-
* Find the next pending bucket of a level. Search from @offset + @clk upwards
1283-
* and if nothing there, search from start of the level (@offset) up to
1284-
* @offset + clk.
1282+
* Find the next pending bucket of a level. Search from level start (@offset)
1283+
* + @clk upwards and if nothing there, search from start of the level
1284+
* (@offset) up to @offset + clk.
12851285
*/
12861286
static int next_pending_bucket(struct timer_base *base, unsigned offset,
12871287
unsigned clk)
@@ -1298,14 +1298,14 @@ static int next_pending_bucket(struct timer_base *base, unsigned offset,
12981298
}
12991299

13001300
/*
1301-
* Search the first expiring timer in the various clock levels.
1301+
* Search the first expiring timer in the various clock levels. Caller must
1302+
* hold base->lock.
13021303
*/
13031304
static unsigned long __next_timer_interrupt(struct timer_base *base)
13041305
{
13051306
unsigned long clk, next, adj;
13061307
unsigned lvl, offset = 0;
13071308

1308-
spin_lock(&base->lock);
13091309
next = base->clk + NEXT_TIMER_MAX_DELTA;
13101310
clk = base->clk;
13111311
for (lvl = 0; lvl < LVL_DEPTH; lvl++, offset += LVL_SIZE) {
@@ -1358,7 +1358,6 @@ static unsigned long __next_timer_interrupt(struct timer_base *base)
13581358
clk >>= LVL_CLK_SHIFT;
13591359
clk += adj;
13601360
}
1361-
spin_unlock(&base->lock);
13621361
return next;
13631362
}
13641363

@@ -1416,14 +1415,48 @@ u64 get_next_timer_interrupt(unsigned long basej, u64 basem)
14161415
if (cpu_is_offline(smp_processor_id()))
14171416
return expires;
14181417

1418+
spin_lock(&base->lock);
14191419
nextevt = __next_timer_interrupt(base);
1420+
spin_unlock(&base->lock);
1421+
14201422
if (time_before_eq(nextevt, basej))
14211423
expires = basem;
14221424
else
14231425
expires = basem + (nextevt - basej) * TICK_NSEC;
14241426

14251427
return cmp_next_hrtimer_event(basem, expires);
14261428
}
1429+
1430+
static int collect_expired_timers(struct timer_base *base,
1431+
struct hlist_head *heads)
1432+
{
1433+
/*
1434+
* NOHZ optimization. After a long idle sleep we need to forward the
1435+
* base to current jiffies. Avoid a loop by searching the bitfield for
1436+
* the next expiring timer.
1437+
*/
1438+
if ((long)(jiffies - base->clk) > 2) {
1439+
unsigned long next = __next_timer_interrupt(base);
1440+
1441+
/*
1442+
* If the next timer is ahead of time forward to current
1443+
* jiffies, otherwise forward to the next expiry time.
1444+
*/
1445+
if (time_after(next, jiffies)) {
1446+
/* The call site will increment clock! */
1447+
base->clk = jiffies - 1;
1448+
return 0;
1449+
}
1450+
base->clk = next;
1451+
}
1452+
return __collect_expired_timers(base, heads);
1453+
}
1454+
#else
1455+
static inline int collect_expired_timers(struct timer_base *base,
1456+
struct hlist_head *heads)
1457+
{
1458+
return __collect_expired_timers(base, heads);
1459+
}
14271460
#endif
14281461

14291462
/*

0 commit comments

Comments
 (0)