timekeeping: Move TAI managment into timekeeping core from ntp
John Stultz [Thu, 3 May 2012 19:30:07 +0000 (12:30 -0700)]
Currently NTP manages the TAI offset. Since there's plans for a
CLOCK_TAI clockid, push the TAI management into the timekeeping
core.

CC: Thomas Gleixner <tglx@linutronix.de>
CC: Eric Dumazet <eric.dumazet@gmail.com>
CC: Richard Cochran <richardcochran@gmail.com>
Signed-off-by: John Stultz <john.stultz@linaro.org>

include/linux/time.h
include/linux/timekeeper_internal.h
kernel/time/ntp.c
kernel/time/timekeeping.c

index d4835df..47210a1 100644 (file)
@@ -181,6 +181,8 @@ extern struct timespec timespec_trunc(struct timespec t, unsigned gran);
 extern int timekeeping_valid_for_hres(void);
 extern u64 timekeeping_max_deferment(void);
 extern int timekeeping_inject_offset(struct timespec *ts);
+extern s32 timekeeping_get_tai_offset(void);
+extern void timekeeping_set_tai_offset(s32 tai_offset);
 
 struct tms;
 extern void do_sys_times(struct tms *);
index e1d558e..ff94f43 100644 (file)
@@ -62,6 +62,9 @@ struct timekeeper {
        ktime_t                 offs_boot;
        /* The raw monotonic time for the CLOCK_MONOTONIC_RAW posix clock. */
        struct timespec         raw_time;
+       /* The current UTC to TAI offset in seconds */
+       s32                     tai_offset;
+
        /* Seqlock for all timekeeper values */
        seqlock_t               lock;
 };
index 072bb06..59e2749 100644 (file)
@@ -53,9 +53,6 @@ static int                    time_state = TIME_OK;
 /* clock status bits:                                                  */
 static int                     time_status = STA_UNSYNC;
 
-/* TAI offset (secs):                                                  */
-static long                    time_tai;
-
 /* time adjustment (nsecs):                                            */
 static s64                     time_offset;
 
@@ -415,7 +412,6 @@ int second_overflow(unsigned long secs)
                else if (secs % 86400 == 0) {
                        leap = -1;
                        time_state = TIME_OOP;
-                       time_tai++;
                        printk(KERN_NOTICE
                                "Clock: inserting leap second 23:59:60 UTC\n");
                }
@@ -425,7 +421,6 @@ int second_overflow(unsigned long secs)
                        time_state = TIME_OK;
                else 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");
@@ -579,7 +574,9 @@ static inline void process_adj_status(struct timex *txc, struct timespec *ts)
  * Called with ntp_lock held, so we can access and modify
  * all the global NTP state:
  */
-static inline void process_adjtimex_modes(struct timex *txc, struct timespec *ts)
+static inline void process_adjtimex_modes(struct timex *txc,
+                                               struct timespec *ts,
+                                               s32 *time_tai)
 {
        if (txc->modes & ADJ_STATUS)
                process_adj_status(txc, ts);
@@ -613,7 +610,7 @@ static inline void process_adjtimex_modes(struct timex *txc, struct timespec *ts
        }
 
        if (txc->modes & ADJ_TAI && txc->constant > 0)
-               time_tai = txc->constant;
+               *time_tai = txc->constant;
 
        if (txc->modes & ADJ_OFFSET)
                ntp_update_offset(txc->offset);
@@ -632,6 +629,7 @@ static inline void process_adjtimex_modes(struct timex *txc, struct timespec *ts
 int do_adjtimex(struct timex *txc)
 {
        struct timespec ts;
+       u32 time_tai, orig_tai;
        int result;
 
        /* Validate the data before disabling interrupts */
@@ -671,6 +669,7 @@ int do_adjtimex(struct timex *txc)
        }
 
        getnstimeofday(&ts);
+       orig_tai = time_tai = timekeeping_get_tai_offset();
 
        raw_spin_lock_irq(&ntp_lock);
 
@@ -687,7 +686,7 @@ int do_adjtimex(struct timex *txc)
 
                /* If there are input parameters, then process them: */
                if (txc->modes)
-                       process_adjtimex_modes(txc, &ts);
+                       process_adjtimex_modes(txc, &ts, &time_tai);
 
                txc->offset = shift_right(time_offset * NTP_INTERVAL_FREQ,
                                  NTP_SCALE_SHIFT);
@@ -716,6 +715,9 @@ int do_adjtimex(struct timex *txc)
 
        raw_spin_unlock_irq(&ntp_lock);
 
+       if (time_tai != orig_tai)
+               timekeeping_set_tai_offset(time_tai);
+
        txc->time.tv_sec = ts.tv_sec;
        txc->time.tv_usec = ts.tv_nsec;
        if (!(time_status & STA_NANO))
index 0355f12..937098a 100644 (file)
@@ -513,6 +513,48 @@ error: /* even if we error out, we forwarded the time, so call update */
 }
 EXPORT_SYMBOL(timekeeping_inject_offset);
 
+
+/**
+ * timekeeping_get_tai_offset - Returns current TAI offset from UTC
+ *
+ */
+s32 timekeeping_get_tai_offset(void)
+{
+       struct timekeeper *tk = &timekeeper;
+       unsigned int seq;
+       s32 ret;
+
+       do {
+               seq = read_seqbegin(&tk->lock);
+               ret = tk->tai_offset;
+       } while (read_seqretry(&tk->lock, seq));
+
+       return ret;
+}
+
+/**
+ * __timekeeping_set_tai_offset - Lock free worker function
+ *
+ */
+void __timekeeping_set_tai_offset(struct timekeeper *tk, s32 tai_offset)
+{
+       tk->tai_offset = tai_offset;
+}
+
+/**
+ * timekeeping_set_tai_offset - Sets the current TAI offset from UTC
+ *
+ */
+void timekeeping_set_tai_offset(s32 tai_offset)
+{
+       struct timekeeper *tk = &timekeeper;
+       unsigned long flags;
+
+       write_seqlock_irqsave(&tk->lock, flags);
+       __timekeeping_set_tai_offset(tk, tai_offset);
+       write_sequnlock_irqrestore(&tk->lock, flags);
+}
+
 /**
  * change_clocksource - Swaps clocksources if a new one is available
  *
@@ -1143,6 +1185,8 @@ static inline void accumulate_nsecs_to_secs(struct timekeeper *tk)
                        tk_set_wall_to_mono(tk,
                                timespec_sub(tk->wall_to_monotonic, ts));
 
+                       __timekeeping_set_tai_offset(tk, tk->tai_offset - leap);
+
                        clock_was_set_delayed();
                }
        }