include/linux/timekeeper_internal.h | 1 + kernel/time/timekeeping.c | 25 ++++++++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/include/linux/timekeeper_internal.h b/include/linux/timekeeper_internal.h index 05af9a334893..0fcb60d77079 100644 --- a/include/linux/timekeeper_internal.h +++ b/include/linux/timekeeper_internal.h @@ -32,6 +32,7 @@ struct tk_read_base { cycle_t (*read)(struct clocksource *cs); cycle_t mask; cycle_t cycle_last; + cycle_t cycle_error; u32 mult; u32 shift; u64 xtime_nsec; diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 6a931852082f..1c842ddd567f 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -140,6 +140,7 @@ static void tk_setup_internals(struct timekeeper *tk, struct clocksource *clock) tk->tkr.read = clock->read; tk->tkr.mask = clock->mask; tk->tkr.cycle_last = tk->tkr.read(clock); + tk->tkr.cycle_error = 0; /* Do the ns -> cycle conversion first, using original mult */ tmp = NTP_INTERVAL_LENGTH; @@ -197,11 +198,17 @@ static inline s64 timekeeping_get_ns(struct tk_read_base *tkr) s64 nsec; /* read clocksource: */ - cycle_now = tkr->read(tkr->clock); + cycle_now = tkr->read(tkr->clock) + tkr->cycle_error; /* calculate the delta since the last update_wall_time: */ delta = clocksource_delta(cycle_now, tkr->cycle_last, tkr->mask); + /* Hmm? This is really not good, we're too close to overflowing */ + if (unlikely(delta > (tkr->mask >> 3))) { + tkr->cycle_error = delta; + delta = 0; + } + nsec = delta * tkr->mult + tkr->xtime_nsec; nsec >>= tkr->shift; @@ -465,6 +472,16 @@ static void timekeeping_update(struct timekeeper *tk, unsigned int action) update_fast_timekeeper(tk); } +static void check_cycle_error(struct tk_read_base *tkr) +{ + cycle_t error = tkr->cycle_error; + + if (unlikely(error)) { + tkr->cycle_error = 0; + pr_err("Clocksource %s had cycles off by %llu\n", tkr->clock->name, error); + } +} + /** * timekeeping_forward_now - update clock to the current time * @@ -481,6 +498,7 @@ static void timekeeping_forward_now(struct timekeeper *tk) cycle_now = tk->tkr.read(clock); delta = clocksource_delta(cycle_now, tk->tkr.cycle_last, tk->tkr.mask); tk->tkr.cycle_last = cycle_now; + check_cycle_error(&tk->tkr); tk->tkr.xtime_nsec += delta * tk->tkr.mult; @@ -1237,6 +1255,7 @@ static void timekeeping_resume(void) /* Re-base the last cycle value */ tk->tkr.cycle_last = cycle_now; + tk->tkr.cycle_error = 0; tk->ntp_error = 0; timekeeping_suspended = 0; timekeeping_update(tk, TK_MIRROR | TK_CLOCK_WAS_SET); @@ -1591,11 +1610,15 @@ void update_wall_time(void) if (unlikely(timekeeping_suspended)) goto out; + check_cycle_error(&real_tk->tkr); + #ifdef CONFIG_ARCH_USES_GETTIMEOFFSET offset = real_tk->cycle_interval; #else offset = clocksource_delta(tk->tkr.read(tk->tkr.clock), tk->tkr.cycle_last, tk->tkr.mask); + if (unlikely(offset > (tk->tkr.mask >> 3))) + pr_err("Cutting it too close for %s in in update_wall_time (offset = %llu)\n", tk->tkr.clock->name, offset); #endif /* Check if there's really nothing to do */