|
@@ -582,39 +582,46 @@ static void tick_nohz_restart(struct tick_sched *ts, ktime_t now)
|
|
static ktime_t tick_nohz_stop_sched_tick(struct tick_sched *ts,
|
|
static ktime_t tick_nohz_stop_sched_tick(struct tick_sched *ts,
|
|
ktime_t now, int cpu)
|
|
ktime_t now, int cpu)
|
|
{
|
|
{
|
|
- unsigned long seq, last_jiffies, next_jiffies, delta_jiffies;
|
|
|
|
- ktime_t last_update, expires, ret = { .tv64 = 0 };
|
|
|
|
- unsigned long rcu_delta_jiffies;
|
|
|
|
struct clock_event_device *dev = __this_cpu_read(tick_cpu_device.evtdev);
|
|
struct clock_event_device *dev = __this_cpu_read(tick_cpu_device.evtdev);
|
|
- u64 time_delta;
|
|
|
|
-
|
|
|
|
- time_delta = timekeeping_max_deferment();
|
|
|
|
|
|
+ u64 basemono, next_tick, next_tmr, next_rcu, delta, expires;
|
|
|
|
+ unsigned long seq, basejiff;
|
|
|
|
+ ktime_t tick;
|
|
|
|
|
|
/* Read jiffies and the time when jiffies were updated last */
|
|
/* Read jiffies and the time when jiffies were updated last */
|
|
do {
|
|
do {
|
|
seq = read_seqbegin(&jiffies_lock);
|
|
seq = read_seqbegin(&jiffies_lock);
|
|
- last_update = last_jiffies_update;
|
|
|
|
- last_jiffies = jiffies;
|
|
|
|
|
|
+ basemono = last_jiffies_update.tv64;
|
|
|
|
+ basejiff = jiffies;
|
|
} while (read_seqretry(&jiffies_lock, seq));
|
|
} while (read_seqretry(&jiffies_lock, seq));
|
|
|
|
+ ts->last_jiffies = basejiff;
|
|
|
|
|
|
- if (rcu_needs_cpu(&rcu_delta_jiffies) ||
|
|
|
|
|
|
+ if (rcu_needs_cpu(basemono, &next_rcu) ||
|
|
arch_needs_cpu() || irq_work_needs_cpu()) {
|
|
arch_needs_cpu() || irq_work_needs_cpu()) {
|
|
- next_jiffies = last_jiffies + 1;
|
|
|
|
- delta_jiffies = 1;
|
|
|
|
|
|
+ next_tick = basemono + TICK_NSEC;
|
|
} else {
|
|
} else {
|
|
- /* Get the next timer wheel timer */
|
|
|
|
- next_jiffies = get_next_timer_interrupt(last_jiffies);
|
|
|
|
- delta_jiffies = next_jiffies - last_jiffies;
|
|
|
|
- if (rcu_delta_jiffies < delta_jiffies) {
|
|
|
|
- next_jiffies = last_jiffies + rcu_delta_jiffies;
|
|
|
|
- delta_jiffies = rcu_delta_jiffies;
|
|
|
|
- }
|
|
|
|
|
|
+ /*
|
|
|
|
+ * Get the next pending timer. If high resolution
|
|
|
|
+ * timers are enabled this only takes the timer wheel
|
|
|
|
+ * timers into account. If high resolution timers are
|
|
|
|
+ * disabled this also looks at the next expiring
|
|
|
|
+ * hrtimer.
|
|
|
|
+ */
|
|
|
|
+ next_tmr = get_next_timer_interrupt(basejiff, basemono);
|
|
|
|
+ ts->next_timer = next_tmr;
|
|
|
|
+ /* Take the next rcu event into account */
|
|
|
|
+ next_tick = next_rcu < next_tmr ? next_rcu : next_tmr;
|
|
}
|
|
}
|
|
|
|
|
|
- if ((long)delta_jiffies <= 1) {
|
|
|
|
|
|
+ /*
|
|
|
|
+ * If the tick is due in the next period, keep it ticking or
|
|
|
|
+ * restart it proper.
|
|
|
|
+ */
|
|
|
|
+ delta = next_tick - basemono;
|
|
|
|
+ if (delta <= (u64)TICK_NSEC) {
|
|
|
|
+ tick.tv64 = 0;
|
|
if (!ts->tick_stopped)
|
|
if (!ts->tick_stopped)
|
|
goto out;
|
|
goto out;
|
|
- if (delta_jiffies == 0) {
|
|
|
|
|
|
+ if (delta == 0) {
|
|
/* Tick is stopped, but required now. Enforce it */
|
|
/* Tick is stopped, but required now. Enforce it */
|
|
tick_nohz_restart(ts, now);
|
|
tick_nohz_restart(ts, now);
|
|
goto out;
|
|
goto out;
|
|
@@ -629,54 +636,39 @@ static ktime_t tick_nohz_stop_sched_tick(struct tick_sched *ts,
|
|
* do_timer() never invoked. Keep track of the fact that it
|
|
* do_timer() never invoked. Keep track of the fact that it
|
|
* was the one which had the do_timer() duty last. If this cpu
|
|
* was the one which had the do_timer() duty last. If this cpu
|
|
* is the one which had the do_timer() duty last, we limit the
|
|
* is the one which had the do_timer() duty last, we limit the
|
|
- * sleep time to the timekeeping max_deferement value which we
|
|
|
|
- * retrieved above. Otherwise we can sleep as long as we want.
|
|
|
|
|
|
+ * sleep time to the timekeeping max_deferement value.
|
|
|
|
+ * Otherwise we can sleep as long as we want.
|
|
*/
|
|
*/
|
|
|
|
+ delta = timekeeping_max_deferment();
|
|
if (cpu == tick_do_timer_cpu) {
|
|
if (cpu == tick_do_timer_cpu) {
|
|
tick_do_timer_cpu = TICK_DO_TIMER_NONE;
|
|
tick_do_timer_cpu = TICK_DO_TIMER_NONE;
|
|
ts->do_timer_last = 1;
|
|
ts->do_timer_last = 1;
|
|
} else if (tick_do_timer_cpu != TICK_DO_TIMER_NONE) {
|
|
} else if (tick_do_timer_cpu != TICK_DO_TIMER_NONE) {
|
|
- time_delta = KTIME_MAX;
|
|
|
|
|
|
+ delta = KTIME_MAX;
|
|
ts->do_timer_last = 0;
|
|
ts->do_timer_last = 0;
|
|
} else if (!ts->do_timer_last) {
|
|
} else if (!ts->do_timer_last) {
|
|
- time_delta = KTIME_MAX;
|
|
|
|
|
|
+ delta = KTIME_MAX;
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_NO_HZ_FULL
|
|
#ifdef CONFIG_NO_HZ_FULL
|
|
|
|
+ /* Limit the tick delta to the maximum scheduler deferment */
|
|
if (!ts->inidle)
|
|
if (!ts->inidle)
|
|
- time_delta = min(time_delta, scheduler_tick_max_deferment());
|
|
|
|
|
|
+ delta = min(delta, scheduler_tick_max_deferment());
|
|
#endif
|
|
#endif
|
|
|
|
|
|
- /*
|
|
|
|
- * calculate the expiry time for the next timer wheel
|
|
|
|
- * timer. delta_jiffies >= NEXT_TIMER_MAX_DELTA signals that
|
|
|
|
- * there is no timer pending or at least extremely far into
|
|
|
|
- * the future (12 days for HZ=1000). In this case we set the
|
|
|
|
- * expiry to the end of time.
|
|
|
|
- */
|
|
|
|
- if (likely(delta_jiffies < NEXT_TIMER_MAX_DELTA)) {
|
|
|
|
- /*
|
|
|
|
- * Calculate the time delta for the next timer event.
|
|
|
|
- * If the time delta exceeds the maximum time delta
|
|
|
|
- * permitted by the current clocksource then adjust
|
|
|
|
- * the time delta accordingly to ensure the
|
|
|
|
- * clocksource does not wrap.
|
|
|
|
- */
|
|
|
|
- time_delta = min_t(u64, time_delta,
|
|
|
|
- tick_period.tv64 * delta_jiffies);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (time_delta < KTIME_MAX)
|
|
|
|
- expires = ktime_add_ns(last_update, time_delta);
|
|
|
|
|
|
+ /* Calculate the next expiry time */
|
|
|
|
+ if (delta < (KTIME_MAX - basemono))
|
|
|
|
+ expires = basemono + delta;
|
|
else
|
|
else
|
|
- expires.tv64 = KTIME_MAX;
|
|
|
|
|
|
+ expires = KTIME_MAX;
|
|
|
|
+
|
|
|
|
+ expires = min_t(u64, expires, next_tick);
|
|
|
|
+ tick.tv64 = expires;
|
|
|
|
|
|
/* Skip reprogram of event if its not changed */
|
|
/* Skip reprogram of event if its not changed */
|
|
- if (ts->tick_stopped && ktime_equal(expires, dev->next_event))
|
|
|
|
|
|
+ if (ts->tick_stopped && (expires == dev->next_event.tv64))
|
|
goto out;
|
|
goto out;
|
|
|
|
|
|
- ret = expires;
|
|
|
|
-
|
|
|
|
/*
|
|
/*
|
|
* nohz_stop_sched_tick can be called several times before
|
|
* nohz_stop_sched_tick can be called several times before
|
|
* the nohz_restart_sched_tick is called. This happens when
|
|
* the nohz_restart_sched_tick is called. This happens when
|
|
@@ -694,26 +686,23 @@ static ktime_t tick_nohz_stop_sched_tick(struct tick_sched *ts,
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
- * If the expiration time == KTIME_MAX, then
|
|
|
|
- * in this case we simply stop the tick timer.
|
|
|
|
|
|
+ * If the expiration time == KTIME_MAX, then we simply stop
|
|
|
|
+ * the tick timer.
|
|
*/
|
|
*/
|
|
- if (unlikely(expires.tv64 == KTIME_MAX)) {
|
|
|
|
|
|
+ if (unlikely(expires == KTIME_MAX)) {
|
|
if (ts->nohz_mode == NOHZ_MODE_HIGHRES)
|
|
if (ts->nohz_mode == NOHZ_MODE_HIGHRES)
|
|
hrtimer_cancel(&ts->sched_timer);
|
|
hrtimer_cancel(&ts->sched_timer);
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
|
|
if (ts->nohz_mode == NOHZ_MODE_HIGHRES)
|
|
if (ts->nohz_mode == NOHZ_MODE_HIGHRES)
|
|
- hrtimer_start(&ts->sched_timer, expires,
|
|
|
|
- HRTIMER_MODE_ABS_PINNED);
|
|
|
|
|
|
+ hrtimer_start(&ts->sched_timer, tick, HRTIMER_MODE_ABS_PINNED);
|
|
else
|
|
else
|
|
- tick_program_event(expires, 1);
|
|
|
|
|
|
+ tick_program_event(tick, 1);
|
|
out:
|
|
out:
|
|
- ts->next_jiffies = next_jiffies;
|
|
|
|
- ts->last_jiffies = last_jiffies;
|
|
|
|
|
|
+ /* Update the estimated sleep length */
|
|
ts->sleep_length = ktime_sub(dev->next_event, now);
|
|
ts->sleep_length = ktime_sub(dev->next_event, now);
|
|
-
|
|
|
|
- return ret;
|
|
|
|
|
|
+ return tick;
|
|
}
|
|
}
|
|
|
|
|
|
static void tick_nohz_full_stop_tick(struct tick_sched *ts)
|
|
static void tick_nohz_full_stop_tick(struct tick_sched *ts)
|