diff options
Diffstat (limited to 'kernel/hrtimer.c')
-rw-r--r-- | kernel/hrtimer.c | 48 |
1 files changed, 44 insertions, 4 deletions
diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index 263b663fb242..8af69b688fe8 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -591,7 +591,22 @@ hrtimer_force_reprogram(struct hrtimer_cpu_base *cpu_base, int skip_equal) if (cpu_base->hang_detected) return; - tick_program_event(cpu_base->expires_next, 1); + if (cpu_base->expires_next.tv64 != KTIME_MAX) { + tick_program_event(cpu_base->expires_next, 1); + } else { + /* + * Don't need clockevent device anymore, stop it. + * + * We reach here only for NOHZ_MODE_HIGHRES mode and we are + * guaranteed that no timers/hrtimers are enqueued on this cpu. + * + * We will reach here: + * - when tick-sched hrtimer is cancelled to enter into idle or + * full dynticks mode + * - or a hrtimer is cancelled on a tickless cpu + */ + tick_stop_event(); + } } /* @@ -1377,9 +1392,34 @@ retry: cpu_base->expires_next = expires_next; raw_spin_unlock(&cpu_base->lock); - /* Reprogramming necessary ? */ - if (expires_next.tv64 == KTIME_MAX || - !tick_program_event(expires_next, 0)) { + if (expires_next.tv64 == KTIME_MAX) { + cpu_base->hang_detected = 0; + + /* + * Don't need clockevent device anymore, stop it. + * + * We reach here only for NOHZ_MODE_HIGHRES and we are + * guaranteed to not have any pending timers/hrtimers on this + * cpu. Most of the scenarios will be covered by similar code + * present in hrtimer_force_reprogram(), as we always try to + * evaluate tick requirement on idle/irq exit and cancel + * tick-sched hrtimer when tick isn't required anymore. + * + * It is required here as well for the extreme corner case. + * + * Suppose last hrtimer fires on a tickless CPU, we reach + * tick_nohz_irq_exit(). While reevaluating we will get expires + * == KTIME_MAX and try to cancel tick-sched hrtimer. BUT, the + * tick-sched hrtimer was never queued on a tickless CPU and so + * we wouldn't reach hrtimer_force_reprogram(). + * + * So, disable Clockevent device here as well. + */ + tick_stop_event(); + return; + } + + if (!tick_program_event(expires_next, 0)) { cpu_base->hang_detected = 0; return; } |