diff options
-rw-r--r-- | kernel/time/ntp.c | 45 | ||||
-rw-r--r-- | kernel/time/timekeeping.c | 37 |
2 files changed, 73 insertions, 9 deletions
diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c index e32587e7a6ca..ea8d82ee797d 100644 --- a/kernel/time/ntp.c +++ b/kernel/time/ntp.c @@ -34,6 +34,7 @@ unsigned long tick_nsec; static u64 tick_length; static u64 tick_length_base; +#define SECS_PER_DAY 86400 #define MAX_TICKADJ 500LL /* usecs */ #define MAX_TICKADJ_SCALED \ (((MAX_TICKADJ * NSEC_PER_USEC) << NTP_SCALE_SHIFT) / NTP_INTERVAL_FREQ) @@ -78,6 +79,9 @@ static long time_adjust; /* constant (boot-param configurable) NTP tick adjustment (upscaled) */ static s64 ntp_tick_adj; +/* second value of the next pending leapsecond, or KTIME_MAX if no leap */ +static s64 ntp_next_leap_sec = KTIME_MAX; + #ifdef CONFIG_NTP_PPS /* @@ -354,6 +358,8 @@ void ntp_clear(void) time_maxerror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT; + ntp_next_leap_sec = KTIME_MAX; + ntp_update_frequency(); tick_length = tick_length_base; @@ -377,6 +383,21 @@ u64 ntp_tick_length(void) return ret; } +/** + * ntp_get_next_leap - Returns the next leapsecond in CLOCK_REALTIME ktime_t + * + * Provides the time of the next leapsecond against CLOCK_REALTIME in + * a ktime_t format. Returns KTIME_MAX if no leapsecond is pending. + */ +ktime_t ntp_get_next_leap(void) +{ + ktime_t ret; + + if ((time_state == TIME_INS) && (time_status & STA_INS)) + return ktime_set(ntp_next_leap_sec, 0); + ret.tv64 = KTIME_MAX; + return ret; +} /* * this routine handles the overflow of the microsecond field @@ -403,15 +424,21 @@ int second_overflow(unsigned long secs) */ switch (time_state) { case TIME_OK: - if (time_status & STA_INS) + if (time_status & STA_INS) { time_state = TIME_INS; - else if (time_status & STA_DEL) + ntp_next_leap_sec = secs + SECS_PER_DAY - + (secs % SECS_PER_DAY); + } else if (time_status & STA_DEL) { time_state = TIME_DEL; + ntp_next_leap_sec = secs + SECS_PER_DAY - + ((secs+1) % SECS_PER_DAY); + } break; case TIME_INS: - if (!(time_status & STA_INS)) + if (!(time_status & STA_INS)) { + ntp_next_leap_sec = KTIME_MAX; time_state = TIME_OK; - else if (secs % 86400 == 0) { + } else if (secs % SECS_PER_DAY == 0) { leap = -1; time_state = TIME_OOP; time_tai++; @@ -420,10 +447,12 @@ int second_overflow(unsigned long secs) } break; case TIME_DEL: - if (!(time_status & STA_DEL)) + if (!(time_status & STA_DEL)) { + ntp_next_leap_sec = KTIME_MAX; time_state = TIME_OK; - else if ((secs + 1) % 86400 == 0) { + } else if ((secs + 1) % SECS_PER_DAY == 0) { leap = 1; + ntp_next_leap_sec = KTIME_MAX; time_tai--; time_state = TIME_WAIT; printk(KERN_NOTICE @@ -431,6 +460,7 @@ int second_overflow(unsigned long secs) } break; case TIME_OOP: + ntp_next_leap_sec = KTIME_MAX; time_state = TIME_WAIT; break; @@ -549,6 +579,7 @@ static inline void process_adj_status(struct timex *txc, struct timespec *ts) if ((time_status & STA_PLL) && !(txc->status & STA_PLL)) { time_state = TIME_OK; time_status = STA_UNSYNC; + ntp_next_leap_sec = KTIME_MAX; /* restart PPS frequency calibration */ pps_reset_freq_interval(); } @@ -619,7 +650,7 @@ static inline void process_adjtimex_modes(struct timex *txc, struct timespec *ts * adjtimex mainly allows reading (and writing, if superuser) of * kernel time-keeping variables. used by xntpd. */ -int do_adjtimex(struct timex *txc) +int __do_adjtimex(struct timex *txc) { struct timespec ts; int result; diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 32f0cb8f1fe8..a72f63e2f285 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -21,6 +21,9 @@ #include <linux/tick.h> #include <linux/stop_machine.h> +extern ktime_t ntp_get_next_leap(void); +extern int __do_adjtimex(struct timex *); + /* Structure holding internal timekeeping values. */ struct timekeeper { /* Current clocksource used for timekeeping. */ @@ -30,6 +33,8 @@ struct timekeeper { /* The shift value of the current clocksource. */ int shift; + /* CLOCK_MONOTONIC time value of a pending leap-second*/ + ktime_t next_leap_ktime; /* Number of clock cycles in one NTP interval. */ cycle_t cycle_interval; /* Number of clock shifted nano seconds in one NTP interval. */ @@ -186,6 +191,17 @@ static void update_rt_offset(void) timekeeper.offs_real = timespec_to_ktime(tmp); } +/* + * tk_update_leap_state - helper to update the next_leap_ktime + */ +static inline void tk_update_leap_state(struct timekeeper *tk) +{ + tk->next_leap_ktime = ntp_get_next_leap(); + if (tk->next_leap_ktime.tv64 != KTIME_MAX) + /* Convert to monotonic time */ + tk->next_leap_ktime = ktime_sub(tk->next_leap_ktime, tk->offs_real); +} + /* must hold write on timekeeper.lock */ static void timekeeping_update(bool clearntp) { @@ -193,6 +209,7 @@ static void timekeeping_update(bool clearntp) timekeeper.ntp_error = 0; ntp_clear(); } + tk_update_leap_state(&timekeeper); update_rt_offset(); update_vsyscall(&timekeeper.xtime, &timekeeper.wall_to_monotonic, timekeeper.clock, timekeeper.mult); @@ -1329,10 +1346,16 @@ ktime_t ktime_get_update_offsets(ktime_t *offs_real, ktime_t *offs_boot) *offs_real = timekeeper.offs_real; *offs_boot = timekeeper.offs_boot; + + now = ktime_add_ns(ktime_set(secs, 0), nsecs); + now = ktime_sub(now, *offs_real); + + /* Handle leapsecond insertion adjustments */ + if (unlikely(now.tv64 >= timekeeper.next_leap_ktime.tv64)) + *offs_real = ktime_sub(timekeeper.offs_real, ktime_set(1, 0)); + } while (read_seqretry(&timekeeper.lock, seq)); - now = ktime_add_ns(ktime_set(secs, 0), nsecs); - now = ktime_sub(now, *offs_real); return now; } #endif @@ -1354,6 +1377,16 @@ ktime_t ktime_get_monotonic_offset(void) } EXPORT_SYMBOL_GPL(ktime_get_monotonic_offset); +/* + * do_adjtimex() - Accessor function to NTP __do_adjtimex function + */ +int do_adjtimex(struct timex *txc) +{ + int ret; + ret = __do_adjtimex(txc); + tk_update_leap_state(&timekeeper); + return ret; +} /** * xtime_update() - advances the timekeeping infrastructure |