|
@@ -557,7 +557,7 @@ hrtimer_force_reprogram(struct hrtimer_cpu_base *cpu_base, int skip_equal)
|
|
|
static int hrtimer_reprogram(struct hrtimer *timer,
|
|
|
struct hrtimer_clock_base *base)
|
|
|
{
|
|
|
- ktime_t *expires_next = &__get_cpu_var(hrtimer_bases).expires_next;
|
|
|
+ struct hrtimer_cpu_base *cpu_base = &__get_cpu_var(hrtimer_bases);
|
|
|
ktime_t expires = ktime_sub(hrtimer_get_expires(timer), base->offset);
|
|
|
int res;
|
|
|
|
|
@@ -582,7 +582,16 @@ static int hrtimer_reprogram(struct hrtimer *timer,
|
|
|
if (expires.tv64 < 0)
|
|
|
return -ETIME;
|
|
|
|
|
|
- if (expires.tv64 >= expires_next->tv64)
|
|
|
+ if (expires.tv64 >= cpu_base->expires_next.tv64)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If a hang was detected in the last timer interrupt then we
|
|
|
+ * do not schedule a timer which is earlier than the expiry
|
|
|
+ * which we enforced in the hang detection. We want the system
|
|
|
+ * to make progress.
|
|
|
+ */
|
|
|
+ if (cpu_base->hang_detected)
|
|
|
return 0;
|
|
|
|
|
|
/*
|
|
@@ -590,7 +599,7 @@ static int hrtimer_reprogram(struct hrtimer *timer,
|
|
|
*/
|
|
|
res = tick_program_event(expires, 0);
|
|
|
if (!IS_ERR_VALUE(res))
|
|
|
- *expires_next = expires;
|
|
|
+ cpu_base->expires_next = expires;
|
|
|
return res;
|
|
|
}
|
|
|
|
|
@@ -747,17 +756,33 @@ static inline void hrtimer_init_timer_hres(struct hrtimer *timer) { }
|
|
|
|
|
|
#endif /* CONFIG_HIGH_RES_TIMERS */
|
|
|
|
|
|
-#ifdef CONFIG_TIMER_STATS
|
|
|
-void __timer_stats_hrtimer_set_start_info(struct hrtimer *timer, void *addr)
|
|
|
+static inline void timer_stats_hrtimer_set_start_info(struct hrtimer *timer)
|
|
|
{
|
|
|
+#ifdef CONFIG_TIMER_STATS
|
|
|
if (timer->start_site)
|
|
|
return;
|
|
|
-
|
|
|
- timer->start_site = addr;
|
|
|
+ timer->start_site = __builtin_return_address(0);
|
|
|
memcpy(timer->start_comm, current->comm, TASK_COMM_LEN);
|
|
|
timer->start_pid = current->pid;
|
|
|
+#endif
|
|
|
}
|
|
|
+
|
|
|
+static inline void timer_stats_hrtimer_clear_start_info(struct hrtimer *timer)
|
|
|
+{
|
|
|
+#ifdef CONFIG_TIMER_STATS
|
|
|
+ timer->start_site = NULL;
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
+static inline void timer_stats_account_hrtimer(struct hrtimer *timer)
|
|
|
+{
|
|
|
+#ifdef CONFIG_TIMER_STATS
|
|
|
+ if (likely(!timer_stats_active))
|
|
|
+ return;
|
|
|
+ timer_stats_update_stats(timer, timer->start_pid, timer->start_site,
|
|
|
+ timer->function, timer->start_comm, 0);
|
|
|
#endif
|
|
|
+}
|
|
|
|
|
|
/*
|
|
|
* Counterpart to lock_hrtimer_base above:
|
|
@@ -1217,30 +1242,6 @@ static void __run_hrtimer(struct hrtimer *timer, ktime_t *now)
|
|
|
|
|
|
#ifdef CONFIG_HIGH_RES_TIMERS
|
|
|
|
|
|
-static int force_clock_reprogram;
|
|
|
-
|
|
|
-/*
|
|
|
- * After 5 iteration's attempts, we consider that hrtimer_interrupt()
|
|
|
- * is hanging, which could happen with something that slows the interrupt
|
|
|
- * such as the tracing. Then we force the clock reprogramming for each future
|
|
|
- * hrtimer interrupts to avoid infinite loops and use the min_delta_ns
|
|
|
- * threshold that we will overwrite.
|
|
|
- * The next tick event will be scheduled to 3 times we currently spend on
|
|
|
- * hrtimer_interrupt(). This gives a good compromise, the cpus will spend
|
|
|
- * 1/4 of their time to process the hrtimer interrupts. This is enough to
|
|
|
- * let it running without serious starvation.
|
|
|
- */
|
|
|
-
|
|
|
-static inline void
|
|
|
-hrtimer_interrupt_hanging(struct clock_event_device *dev,
|
|
|
- ktime_t try_time)
|
|
|
-{
|
|
|
- force_clock_reprogram = 1;
|
|
|
- dev->min_delta_ns = (unsigned long)try_time.tv64 * 3;
|
|
|
- printk(KERN_WARNING "hrtimer: interrupt too slow, "
|
|
|
- "forcing clock min delta to %llu ns\n",
|
|
|
- (unsigned long long) dev->min_delta_ns);
|
|
|
-}
|
|
|
/*
|
|
|
* High resolution timer interrupt
|
|
|
* Called with interrupts disabled
|
|
@@ -1249,21 +1250,15 @@ void hrtimer_interrupt(struct clock_event_device *dev)
|
|
|
{
|
|
|
struct hrtimer_cpu_base *cpu_base = &__get_cpu_var(hrtimer_bases);
|
|
|
struct hrtimer_clock_base *base;
|
|
|
- ktime_t expires_next, now;
|
|
|
- int nr_retries = 0;
|
|
|
- int i;
|
|
|
+ ktime_t expires_next, now, entry_time, delta;
|
|
|
+ int i, retries = 0;
|
|
|
|
|
|
BUG_ON(!cpu_base->hres_active);
|
|
|
cpu_base->nr_events++;
|
|
|
dev->next_event.tv64 = KTIME_MAX;
|
|
|
|
|
|
- retry:
|
|
|
- /* 5 retries is enough to notice a hang */
|
|
|
- if (!(++nr_retries % 5))
|
|
|
- hrtimer_interrupt_hanging(dev, ktime_sub(ktime_get(), now));
|
|
|
-
|
|
|
- now = ktime_get();
|
|
|
-
|
|
|
+ entry_time = now = ktime_get();
|
|
|
+retry:
|
|
|
expires_next.tv64 = KTIME_MAX;
|
|
|
|
|
|
spin_lock(&cpu_base->lock);
|
|
@@ -1325,10 +1320,48 @@ void hrtimer_interrupt(struct clock_event_device *dev)
|
|
|
spin_unlock(&cpu_base->lock);
|
|
|
|
|
|
/* Reprogramming necessary ? */
|
|
|
- if (expires_next.tv64 != KTIME_MAX) {
|
|
|
- if (tick_program_event(expires_next, force_clock_reprogram))
|
|
|
- goto retry;
|
|
|
+ if (expires_next.tv64 == KTIME_MAX ||
|
|
|
+ !tick_program_event(expires_next, 0)) {
|
|
|
+ cpu_base->hang_detected = 0;
|
|
|
+ return;
|
|
|
}
|
|
|
+
|
|
|
+ /*
|
|
|
+ * The next timer was already expired due to:
|
|
|
+ * - tracing
|
|
|
+ * - long lasting callbacks
|
|
|
+ * - being scheduled away when running in a VM
|
|
|
+ *
|
|
|
+ * We need to prevent that we loop forever in the hrtimer
|
|
|
+ * interrupt routine. We give it 3 attempts to avoid
|
|
|
+ * overreacting on some spurious event.
|
|
|
+ */
|
|
|
+ now = ktime_get();
|
|
|
+ cpu_base->nr_retries++;
|
|
|
+ if (++retries < 3)
|
|
|
+ goto retry;
|
|
|
+ /*
|
|
|
+ * Give the system a chance to do something else than looping
|
|
|
+ * here. We stored the entry time, so we know exactly how long
|
|
|
+ * we spent here. We schedule the next event this amount of
|
|
|
+ * time away.
|
|
|
+ */
|
|
|
+ cpu_base->nr_hangs++;
|
|
|
+ cpu_base->hang_detected = 1;
|
|
|
+ delta = ktime_sub(now, entry_time);
|
|
|
+ if (delta.tv64 > cpu_base->max_hang_time.tv64)
|
|
|
+ cpu_base->max_hang_time = delta;
|
|
|
+ /*
|
|
|
+ * Limit it to a sensible value as we enforce a longer
|
|
|
+ * delay. Give the CPU at least 100ms to catch up.
|
|
|
+ */
|
|
|
+ if (delta.tv64 > 100 * NSEC_PER_MSEC)
|
|
|
+ expires_next = ktime_add_ns(now, 100 * NSEC_PER_MSEC);
|
|
|
+ else
|
|
|
+ expires_next = ktime_add(now, delta);
|
|
|
+ tick_program_event(expires_next, 1);
|
|
|
+ printk_once(KERN_WARNING "hrtimer: interrupt took %llu ns\n",
|
|
|
+ ktime_to_ns(delta));
|
|
|
}
|
|
|
|
|
|
/*
|