Commit 84e345e4 authored by Prarit Bhargava's avatar Prarit Bhargava Committed by John Stultz

time, Fix setting of hardware clock in NTP code

At init time, if the system time is "warped" forward in warp_clock()
it will differ from the hardware clock by sys_tz.tz_minuteswest.  This time
difference is not taken into account when ntp updates the hardware clock,
and this causes the system time to jump forward by this offset every reboot.

The kernel must take this offset into account when writing the system time
to the hardware clock in the ntp code.  This patch adds
persistent_clock_is_local which indicates that an offset has been applied
in warp_clock() and accounts for the "warp" before writing the hardware
clock.

x86 does not have this problem as rtc writes are software limited to a
+/-15 minute window relative to the current rtc time.  Other arches, such
as powerpc, however do a full synchronization of the system time to the
rtc and will see this problem.

[v2]: generated against tip/timers/core
Signed-off-by: default avatarPrarit Bhargava <prarit@redhat.com>
Cc: John Stultz <john.stultz@linaro.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: default avatarJohn Stultz <john.stultz@linaro.org>
parent 6f16eebe
...@@ -128,6 +128,7 @@ static inline bool has_persistent_clock(void) ...@@ -128,6 +128,7 @@ static inline bool has_persistent_clock(void)
extern void read_persistent_clock(struct timespec *ts); extern void read_persistent_clock(struct timespec *ts);
extern void read_boot_clock(struct timespec *ts); extern void read_boot_clock(struct timespec *ts);
extern int persistent_clock_is_local;
extern int update_persistent_clock(struct timespec now); extern int update_persistent_clock(struct timespec now);
void timekeeping_init(void); void timekeeping_init(void);
extern int timekeeping_suspended; extern int timekeeping_suspended;
......
...@@ -114,6 +114,12 @@ SYSCALL_DEFINE2(gettimeofday, struct timeval __user *, tv, ...@@ -114,6 +114,12 @@ SYSCALL_DEFINE2(gettimeofday, struct timeval __user *, tv,
return 0; return 0;
} }
/*
* Indicates if there is an offset between the system clock and the hardware
* clock/persistent clock/rtc.
*/
int persistent_clock_is_local;
/* /*
* Adjust the time obtained from the CMOS to be UTC time instead of * Adjust the time obtained from the CMOS to be UTC time instead of
* local time. * local time.
...@@ -135,6 +141,8 @@ static inline void warp_clock(void) ...@@ -135,6 +141,8 @@ static inline void warp_clock(void)
struct timespec adjust; struct timespec adjust;
adjust = current_kernel_time(); adjust = current_kernel_time();
if (sys_tz.tz_minuteswest != 0)
persistent_clock_is_local = 1;
adjust.tv_sec += sys_tz.tz_minuteswest * 60; adjust.tv_sec += sys_tz.tz_minuteswest * 60;
do_settimeofday(&adjust); do_settimeofday(&adjust);
} }
......
...@@ -511,13 +511,17 @@ static void sync_cmos_clock(struct work_struct *work) ...@@ -511,13 +511,17 @@ static void sync_cmos_clock(struct work_struct *work)
getnstimeofday(&now); getnstimeofday(&now);
if (abs(now.tv_nsec - (NSEC_PER_SEC / 2)) <= tick_nsec / 2) { if (abs(now.tv_nsec - (NSEC_PER_SEC / 2)) <= tick_nsec / 2) {
struct timespec adjust = now;
fail = -ENODEV; fail = -ENODEV;
if (persistent_clock_is_local)
adjust.tv_sec -= (sys_tz.tz_minuteswest * 60);
#ifdef CONFIG_GENERIC_CMOS_UPDATE #ifdef CONFIG_GENERIC_CMOS_UPDATE
fail = update_persistent_clock(now); fail = update_persistent_clock(adjust);
#endif #endif
#ifdef CONFIG_RTC_SYSTOHC #ifdef CONFIG_RTC_SYSTOHC
if (fail == -ENODEV) if (fail == -ENODEV)
fail = rtc_set_ntp_time(now); fail = rtc_set_ntp_time(adjust);
#endif #endif
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment