diff --git a/kernel/time.c b/kernel/time.c index f04791f694081d16dab799ae16f5b3f405683e4d..ffe19149d77006cb6c22db392b0a828962c655d3 100644 --- a/kernel/time.c +++ b/kernel/time.c @@ -57,14 +57,17 @@ EXPORT_SYMBOL(sys_tz); */ asmlinkage long sys_time(time_t __user * tloc) { - time_t i; - struct timeval tv; + /* + * We read xtime.tv_sec atomically - it's updated + * atomically by update_wall_time(), so no need to + * even read-lock the xtime seqlock: + */ + time_t i = xtime.tv_sec; - do_gettimeofday(&tv); - i = tv.tv_sec; + smp_rmb(); /* sys_time() results are coherent */ if (tloc) { - if (put_user(i,tloc)) + if (put_user(i, tloc)) i = -EFAULT; } return i; @@ -373,12 +376,25 @@ void do_gettimeofday (struct timeval *tv) tv->tv_sec = sec; tv->tv_usec = usec; -} + /* + * Make sure xtime.tv_sec [returned by sys_time()] always + * follows the gettimeofday() result precisely. This + * condition is extremely unlikely, it can hit at most + * once per second: + */ + if (unlikely(xtime.tv_sec != tv->tv_sec)) { + unsigned long flags; + + write_seqlock_irqsave(&xtime_lock, flags); + update_wall_time(); + write_sequnlock_irqrestore(&xtime_lock, flags); + } +} EXPORT_SYMBOL(do_gettimeofday); +#else /* CONFIG_TIME_INTERPOLATION */ -#else #ifndef CONFIG_GENERIC_TIME /* * Simulate gettimeofday using do_gettimeofday which only allows a timeval @@ -394,7 +410,7 @@ void getnstimeofday(struct timespec *tv) } EXPORT_SYMBOL_GPL(getnstimeofday); #endif -#endif +#endif /* CONFIG_TIME_INTERPOLATION */ /* Converts Gregorian date to seconds since 1970-01-01 00:00:00. * Assumes input in normal date format, i.e. 1980-12-31 23:59:59