From: Guangyu Sun Date: Thu, 4 Oct 2012 22:47:00 +0000 (-0700) Subject: Revert "ntp: Fix leap-second hrtimer livelock" X-Git-Tag: v2.6.39-400.9.0~303^2~15 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=8ffda5b6d85b018be5c64f2547bcba141eb45a22;p=users%2Fjedix%2Flinux-maple.git Revert "ntp: Fix leap-second hrtimer livelock" This reverts commit 7defe04c0fb3e600f3688673c4d3206ee809fce4. Signed-off-by: Guangyu Sun --- diff --git a/include/linux/timex.h b/include/linux/timex.h index 99bc88b1fc027..b75e1864ed19c 100644 --- a/include/linux/timex.h +++ b/include/linux/timex.h @@ -252,7 +252,7 @@ extern void ntp_clear(void); /* Returns how long ticks are at present, in ns / 2^NTP_SCALE_SHIFT. */ extern u64 ntp_tick_length(void); -extern int second_overflow(unsigned long secs); +extern void second_overflow(void); extern int do_adjtimex(struct timex *); extern void hardpps(const struct timespec *, const struct timespec *); diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c index 3d17ebd47fa25..6e039b144daf0 100644 --- a/kernel/time/ntp.c +++ b/kernel/time/ntp.c @@ -34,6 +34,8 @@ unsigned long tick_nsec; static u64 tick_length; static u64 tick_length_base; +static struct hrtimer leap_timer; + #define MAX_TICKADJ 500LL /* usecs */ #define MAX_TICKADJ_SCALED \ (((MAX_TICKADJ * NSEC_PER_USEC) << NTP_SCALE_SHIFT) / NTP_INTERVAL_FREQ) @@ -379,63 +381,70 @@ u64 ntp_tick_length(void) /* - * this routine handles the overflow of the microsecond field - * - * The tricky bits of code to handle the accurate clock support - * were provided by Dave Mills (Mills@UDEL.EDU) of NTP fame. - * They were originally developed for SUN and DEC kernels. - * All the kudos should go to Dave for this stuff. - * - * Also handles leap second processing, and returns leap offset + * Leap second processing. If in leap-insert state at the end of the + * day, the system clock is set back one second; if in leap-delete + * state, the system clock is set ahead one second. */ -int second_overflow(unsigned long secs) +static enum hrtimer_restart ntp_leap_second(struct hrtimer *timer) { - s64 delta; - int leap = 0; + enum hrtimer_restart res = HRTIMER_NORESTART; unsigned long flags; + int leap = 0; spin_lock_irqsave(&ntp_lock, flags); - - /* - * Leap second processing. If in leap-insert state at the end of the - * day, the system clock is set back one second; if in leap-delete - * state, the system clock is set ahead one second. - */ switch (time_state) { case TIME_OK: - if (time_status & STA_INS) - time_state = TIME_INS; - else if (time_status & STA_DEL) - time_state = TIME_DEL; break; case TIME_INS: - if (secs % 86400 == 0) { - leap = -1; - time_state = TIME_OOP; - printk(KERN_NOTICE - "Clock: inserting leap second 23:59:60 UTC\n"); - } + leap = -1; + time_state = TIME_OOP; + printk(KERN_NOTICE + "Clock: inserting leap second 23:59:60 UTC\n"); + hrtimer_add_expires_ns(&leap_timer, NSEC_PER_SEC); + res = HRTIMER_RESTART; break; case TIME_DEL: - if ((secs + 1) % 86400 == 0) { - leap = 1; - time_tai--; - time_state = TIME_WAIT; - printk(KERN_NOTICE - "Clock: deleting leap second 23:59:59 UTC\n"); - } + leap = 1; + time_tai--; + time_state = TIME_WAIT; + printk(KERN_NOTICE + "Clock: deleting leap second 23:59:59 UTC\n"); break; case TIME_OOP: time_tai++; time_state = TIME_WAIT; - break; - + /* fall through */ case TIME_WAIT: if (!(time_status & (STA_INS | STA_DEL))) time_state = TIME_OK; break; } + spin_unlock_irqrestore(&ntp_lock, flags); + + /* + * We have to call this outside of the ntp_lock to keep + * the proper locking hierarchy + */ + if (leap) + timekeeping_leap_insert(leap); + + return res; +} + +/* + * this routine handles the overflow of the microsecond field + * + * The tricky bits of code to handle the accurate clock support + * were provided by Dave Mills (Mills@UDEL.EDU) of NTP fame. + * They were originally developed for SUN and DEC kernels. + * All the kudos should go to Dave for this stuff. + */ +void second_overflow(void) +{ + s64 delta; + unsigned long flags; + spin_lock_irqsave(&ntp_lock, flags); /* Bump the maxerror field */ time_maxerror += MAXFREQ / NSEC_PER_USEC; @@ -472,13 +481,8 @@ int second_overflow(unsigned long secs) tick_length += (s64)(time_adjust * NSEC_PER_USEC / NTP_INTERVAL_FREQ) << NTP_SCALE_SHIFT; time_adjust = 0; - - - out: spin_unlock_irqrestore(&ntp_lock, flags); - - return leap; } #ifdef CONFIG_GENERIC_CMOS_UPDATE @@ -540,6 +544,27 @@ static void notify_cmos_timer(void) static inline void notify_cmos_timer(void) { } #endif +/* + * Start the leap seconds timer: + */ +static inline void ntp_start_leap_timer(struct timespec *ts) +{ + long now = ts->tv_sec; + + if (time_status & STA_INS) { + time_state = TIME_INS; + now += 86400 - now % 86400; + hrtimer_start(&leap_timer, ktime_set(now, 0), HRTIMER_MODE_ABS); + + return; + } + + if (time_status & STA_DEL) { + time_state = TIME_DEL; + now += 86400 - (now + 1) % 86400; + hrtimer_start(&leap_timer, ktime_set(now, 0), HRTIMER_MODE_ABS); + } +} /* * Propagate a new txc->status value into the NTP state: @@ -564,6 +589,22 @@ static inline void process_adj_status(struct timex *txc, struct timespec *ts) time_status &= STA_RONLY; time_status |= txc->status & ~STA_RONLY; + switch (time_state) { + case TIME_OK: + ntp_start_leap_timer(ts); + break; + case TIME_INS: + case TIME_DEL: + time_state = TIME_OK; + ntp_start_leap_timer(ts); + case TIME_WAIT: + if (!(time_status & (STA_INS | STA_DEL))) + time_state = TIME_OK; + break; + case TIME_OOP: + hrtimer_restart(&leap_timer); + break; + } } /* * Called with the xtime lock held, so we can access and modify @@ -645,6 +686,9 @@ int do_adjtimex(struct timex *txc) (txc->tick < 900000/USER_HZ || txc->tick > 1100000/USER_HZ)) return -EINVAL; + + if (txc->modes & ADJ_STATUS && time_state != TIME_OK) + hrtimer_cancel(&leap_timer); } if (txc->modes & ADJ_SETOFFSET) { @@ -966,4 +1010,6 @@ __setup("ntp_tick_adj=", ntp_tick_adj_setup); void __init ntp_init(void) { ntp_clear(); + hrtimer_init(&leap_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS); + leap_timer.function = ntp_leap_second; } diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index bd056f01ccf9b..c0af8aa9976ee 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -828,11 +828,9 @@ static cycle_t logarithmic_accumulation(cycle_t offset, int shift) timekeeper.xtime_nsec += timekeeper.xtime_interval << shift; while (timekeeper.xtime_nsec >= nsecps) { - int leap; timekeeper.xtime_nsec -= nsecps; xtime.tv_sec++; - leap = second_overflow(xtime.tv_sec); - xtime.tv_sec += leap; + second_overflow(); } /* Accumulate raw time */ @@ -938,11 +936,9 @@ static void update_wall_time(void) * xtime.tv_nsec isn't larger then NSEC_PER_SEC */ if (unlikely(xtime.tv_nsec >= NSEC_PER_SEC)) { - int leap; xtime.tv_nsec -= NSEC_PER_SEC; xtime.tv_sec++; - leap = second_overflow(xtime.tv_sec); - xtime.tv_sec += leap; + second_overflow(); } /* check to see if there is a new clocksource to use */