From 883e3ea07e3b725c90e57bbf1200856d07b389ff Mon Sep 17 00:00:00 2001 From: Prasad Sodagudi Date: Wed, 5 Apr 2017 10:17:19 -0700 Subject: [PATCH] sched: Add a check for cpu unbound deferrable timers Add a check for cpu unbound deferrable timer expiry and raise softirq for handling the expired timers so that the CPU can process the cpu unbound deferrable times as early as possible when a cpu tries to enter/exit idle loop. Change-Id: Ieffa74fa22a4d25493f5590b5ac1e0d784fcbbad Signed-off-by: Prasad Sodagudi --- include/linux/timer.h | 3 +++ kernel/time/tick-sched.c | 6 ++++++ kernel/time/timer.c | 34 +++++++++++++++++++++++++++++++--- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/include/linux/timer.h b/include/linux/timer.h index b1617e8932b2..1239c6ef949e 100644 --- a/include/linux/timer.h +++ b/include/linux/timer.h @@ -173,6 +173,9 @@ extern int mod_timer_pending(struct timer_list *timer, unsigned long expires); extern int mod_timer_pinned(struct timer_list *timer, unsigned long expires); extern void set_timer_slack(struct timer_list *time, int slack_hz); +#ifdef CONFIG_SMP +extern bool check_pending_deferrable_timers(int cpu); +#endif #define TIMER_NOT_PINNED 0 #define TIMER_PINNED 1 diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index 651ff1a3a306..ec2102104cb8 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -809,6 +810,11 @@ static void __tick_nohz_idle_enter(struct tick_sched *ts) now = tick_nohz_start_idle(ts); +#ifdef CONFIG_SMP + if (check_pending_deferrable_timers(cpu)) + raise_softirq_irqoff(TIMER_SOFTIRQ); +#endif + if (can_stop_idle_tick(cpu, ts)) { int was_stopped = ts->tick_stopped; diff --git a/kernel/time/timer.c b/kernel/time/timer.c index 2bde2c2b1cb3..8315d4d72cc3 100644 --- a/kernel/time/timer.c +++ b/kernel/time/timer.c @@ -102,6 +102,7 @@ static DEFINE_PER_CPU(struct tvec_base, tvec_bases); unsigned int sysctl_timer_migration = 1; struct tvec_base tvec_base_deferrable; +static atomic_t deferrable_pending; void timers_update_migration(bool update_nohz) { @@ -150,9 +151,12 @@ static inline struct tvec_base *get_target_base(struct tvec_base *base, static inline void __run_deferrable_timers(void) { - if (smp_processor_id() == tick_do_timer_cpu && - time_after_eq(jiffies, tvec_base_deferrable.timer_jiffies)) - __run_timers(&tvec_base_deferrable); + if (time_after_eq(jiffies, tvec_base_deferrable.timer_jiffies)) { + if ((atomic_cmpxchg(&deferrable_pending, 1, 0) && + tick_do_timer_cpu == TICK_DO_TIMER_NONE) || + tick_do_timer_cpu == smp_processor_id()) + __run_timers(&tvec_base_deferrable); + } } static inline void init_timer_deferrable_global(void) @@ -1428,6 +1432,30 @@ static u64 cmp_next_hrtimer_event(u64 basem, u64 expires) return DIV_ROUND_UP_ULL(nextevt, TICK_NSEC) * TICK_NSEC; } +#ifdef CONFIG_SMP +/* + * check_pending_deferrable_timers - Check for unbound deferrable timer expiry. + * @cpu - Current CPU + * + * The function checks whether any global deferrable pending timers + * are exipired or not. This function does not check cpu bounded + * diferrable pending timers expiry. + * + * The function returns true when a cpu unbounded deferrable timer is expired. + */ +bool check_pending_deferrable_timers(int cpu) +{ + if (cpu == tick_do_timer_cpu || + tick_do_timer_cpu == TICK_DO_TIMER_NONE) { + if (time_after_eq(jiffies, tvec_base_deferrable.timer_jiffies) + && !atomic_cmpxchg(&deferrable_pending, 0, 1)) { + return true; + } + } + return false; +} +#endif + /** * get_next_timer_interrupt - return the time (clock mono) of the next timer * @basej: base time jiffies