When wall_to_monotonic become positive, then I get below two errors on an IMX6 development board without enable RTC device:
1:execute exportfs -a generate: "exportfs: /opt/nfs/arm does not support NFS export" 2:cat /proc/stat: "btime 4294967236" I can product the same issues on x86 with newest kernel in next tree with below c code: " int main(void) { struct timeval val; int ret; val.tv_sec = 0; val.tv_usec = 0; ret = settimeofday(&val, NULL); printf("ret:%d\n", ret); return 0; } " The reason is getboottime[64] return negative value, cause below codes don't work: nfs error:cache.h:get_expiry return negative value cause cache_flush always clear entries just added in ip_map_parse. proc/stat error: seq_printf use "unsigned long" to show a negative number return by getboottime. This patch fix it by validate new value of wall_to_monotonic before assign it. Signed-off-by: Wang YanQing <udkni...@gmail.com> --- kernel/time/timekeeping.c | 71 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 55 insertions(+), 16 deletions(-) diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 91db941..799e323 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -96,11 +96,29 @@ static void tk_xtime_add(struct timekeeper *tk, const struct timespec64 *ts) tk_normalize_xtime(tk); } -static void tk_set_wall_to_mono(struct timekeeper *tk, struct timespec64 wtm) +static inline bool timespec64_negative(struct timespec64 *ts) +{ + if (ts->tv_sec > 0) + return false; + if (ts->tv_sec == 0 && ts->tv_nsec > 0) + return false; + return true; +} + +static bool tk_set_wall_to_mono(struct timekeeper *tk, struct timespec64 wtm) { struct timespec64 tmp; /* + * The current time + * wall_to_monotonic is what we need to add to xtime (or xtime corrected + * for sub jiffie times) to get to monotonic time. Monotonic is pegged + * at zero at system boot time, so wall_to_monotonic will be negative. + */ + if (!timespec64_negative(&wtm)) + return false; + + /* * Verify consistency of: offset_real = -wall_to_monotonic * before modifying anything */ @@ -111,6 +129,7 @@ static void tk_set_wall_to_mono(struct timekeeper *tk, struct timespec64 wtm) set_normalized_timespec64(&tmp, -wtm.tv_sec, -wtm.tv_nsec); tk->offs_real = timespec64_to_ktime(tmp); tk->offs_tai = ktime_add(tk->offs_real, ktime_set(tk->tai_offset, 0)); + return true; } static inline void tk_update_sleep_time(struct timekeeper *tk, ktime_t delta) @@ -796,8 +815,9 @@ EXPORT_SYMBOL(do_gettimeofday); int do_settimeofday64(const struct timespec64 *ts) { struct timekeeper *tk = &tk_core.timekeeper; - struct timespec64 ts_delta, xt; + struct timespec64 ts_delta, tmp; unsigned long flags; + int ret = 0; if (!timespec64_valid_strict(ts)) return -EINVAL; @@ -807,23 +827,27 @@ int do_settimeofday64(const struct timespec64 *ts) timekeeping_forward_now(tk); - xt = tk_xtime(tk); - ts_delta.tv_sec = ts->tv_sec - xt.tv_sec; - ts_delta.tv_nsec = ts->tv_nsec - xt.tv_nsec; + tmp = tk_xtime(tk); + ts_delta.tv_sec = ts->tv_sec - tmp.tv_sec; + ts_delta.tv_nsec = ts->tv_nsec - tmp.tv_nsec; - tk_set_wall_to_mono(tk, timespec64_sub(tk->wall_to_monotonic, ts_delta)); + tmp = timespec64_sub(tk->wall_to_monotonic, ts_delta); + if (!tk_set_wall_to_mono(tk, tmp)) { + ret = -EINVAL; + goto out; + } tk_set_xtime(tk, ts); - timekeeping_update(tk, TK_CLEAR_NTP | TK_MIRROR | TK_CLOCK_WAS_SET); - +out: write_seqcount_end(&tk_core.seq); raw_spin_unlock_irqrestore(&timekeeper_lock, flags); /* signal hrtimers about time change */ - clock_was_set(); + if (!ret) + clock_was_set(); - return 0; + return ret; } EXPORT_SYMBOL(do_settimeofday64); @@ -857,8 +881,13 @@ int timekeeping_inject_offset(struct timespec *ts) goto error; } + tmp = timespec64_sub(tk->wall_to_monotonic, ts64); + if (!tk_set_wall_to_mono(tk, tmp)) { + ret = -EINVAL; + goto error; + } + tk_xtime_add(tk, &ts64); - tk_set_wall_to_mono(tk, timespec64_sub(tk->wall_to_monotonic, ts64)); error: /* even if we error out, we forwarded the time, so call update */ timekeeping_update(tk, TK_CLEAR_NTP | TK_MIRROR | TK_CLOCK_WAS_SET); @@ -1140,14 +1169,21 @@ static struct timespec64 timekeeping_suspend_time; static void __timekeeping_inject_sleeptime(struct timekeeper *tk, struct timespec64 *delta) { + struct timespec64 tmp; + if (!timespec64_valid_strict(delta)) { printk_deferred(KERN_WARNING "__timekeeping_inject_sleeptime: Invalid " "sleep delta value!\n"); return; } + + tmp = timespec64_sub(tk->wall_to_monotonic, *delta); + if (!tk_set_wall_to_mono(tk, tmp)) { + WARN_ON(1); + return; + } tk_xtime_add(tk, delta); - tk_set_wall_to_mono(tk, timespec64_sub(tk->wall_to_monotonic, *delta)); tk_update_sleep_time(tk, timespec64_to_ktime(*delta)); tk_debug_account_sleep_time(delta); } @@ -1539,14 +1575,17 @@ static inline unsigned int accumulate_nsecs_to_secs(struct timekeeper *tk) leap = second_overflow(tk->xtime_sec); if (unlikely(leap)) { struct timespec64 ts; - - tk->xtime_sec += leap; + bool ret; ts.tv_sec = leap; ts.tv_nsec = 0; - tk_set_wall_to_mono(tk, + ret = tk_set_wall_to_mono(tk, timespec64_sub(tk->wall_to_monotonic, ts)); - + if (!ret) { + WARN_ON_ONCE(1); + break; + } + tk->xtime_sec += leap; __timekeeping_set_tai_offset(tk, tk->tai_offset - leap); clock_set = TK_CLOCK_WAS_SET; -- 2.2.2.dirty -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/