]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
Revert "ntp: Fix leap-second hrtimer livelock"
authorGuangyu Sun <guangyu.sun@oracle.com>
Thu, 4 Oct 2012 22:47:00 +0000 (15:47 -0700)
committerGuangyu Sun <guangyu.sun@oracle.com>
Thu, 4 Oct 2012 22:47:00 +0000 (15:47 -0700)
This reverts commit 7defe04c0fb3e600f3688673c4d3206ee809fce4.

Signed-off-by: Guangyu Sun <guangyu.sun@oracle.com>
include/linux/timex.h
kernel/time/ntp.c
kernel/time/timekeeping.c

index 99bc88b1fc02734a30619038d2f9a7f430b4a951..b75e1864ed19c9e151fb0b17f9ebc7bdf4a39b09 100644 (file)
@@ -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 *);
 
index 3d17ebd47fa252044b69a2b339e340148dcf9f44..6e039b144daf0359ecaffcbc00227a7233fd0dd3 100644 (file)
@@ -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;
 }
index bd056f01ccf9bbf6c2ea1f222120a35ac1fd79dd..c0af8aa9976eeeb40d040b86cad1a99395b5b728 100644 (file)
@@ -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 */