]> err.no Git - linux-2.6/blobdiff - kernel/time/ntp.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/roland...
[linux-2.6] / kernel / time / ntp.c
index b5e352597cbb628091718d2ac486b59564a95cfc..c88b5910e7abb9c630106fcbf21b6b7a0d363faf 100644 (file)
@@ -10,6 +10,7 @@
 
 #include <linux/mm.h>
 #include <linux/time.h>
+#include <linux/timer.h>
 #include <linux/timex.h>
 #include <linux/jiffies.h>
 #include <linux/hrtimer.h>
@@ -42,10 +43,6 @@ long time_freq;                              /* frequency offset (scaled ppm)*/
 static long time_reftime;              /* time at last adjustment (s)  */
 long time_adjust;
 
-#define CLOCK_TICK_OVERFLOW    (LATCH * HZ - CLOCK_TICK_RATE)
-#define CLOCK_TICK_ADJUST      (((s64)CLOCK_TICK_OVERFLOW * NSEC_PER_SEC) / \
-                                       (s64)CLOCK_TICK_RATE)
-
 static void ntp_update_frequency(void)
 {
        u64 second_length = (u64)(tick_usec * NSEC_PER_USEC * USER_HZ)
@@ -175,12 +172,64 @@ u64 current_tick_length(void)
        return tick_length;
 }
 
+#ifdef CONFIG_GENERIC_CMOS_UPDATE
+
+/* Disable the cmos update - used by virtualization and embedded */
+int no_sync_cmos_clock  __read_mostly;
+
+static void sync_cmos_clock(unsigned long dummy);
+
+static DEFINE_TIMER(sync_cmos_timer, sync_cmos_clock, 0, 0);
+
+static void sync_cmos_clock(unsigned long dummy)
+{
+       struct timespec now, next;
+       int fail = 1;
+
+       /*
+        * If we have an externally synchronized Linux clock, then update
+        * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
+        * called as close as possible to 500 ms before the new second starts.
+        * This code is run on a timer.  If the clock is set, that timer
+        * may not expire at the correct time.  Thus, we adjust...
+        */
+       if (!ntp_synced())
+               /*
+                * Not synced, exit, do not restart a timer (if one is
+                * running, let it run out).
+                */
+               return;
+
+       getnstimeofday(&now);
+       if (abs(now.tv_nsec - (NSEC_PER_SEC / 2)) <= tick_nsec / 2)
+               fail = update_persistent_clock(now);
+
+       next.tv_nsec = (NSEC_PER_SEC / 2) - now.tv_nsec;
+       if (next.tv_nsec <= 0)
+               next.tv_nsec += NSEC_PER_SEC;
+
+       if (!fail)
+               next.tv_sec = 659;
+       else
+               next.tv_sec = 0;
+
+       if (next.tv_nsec >= NSEC_PER_SEC) {
+               next.tv_sec++;
+               next.tv_nsec -= NSEC_PER_SEC;
+       }
+       mod_timer(&sync_cmos_timer, jiffies + timespec_to_jiffies(&next));
+}
 
-void __attribute__ ((weak)) notify_arch_cmos_timer(void)
+static void notify_cmos_timer(void)
 {
-       return;
+       if (!no_sync_cmos_clock)
+               mod_timer(&sync_cmos_timer, jiffies + 1);
 }
 
+#else
+static inline void notify_cmos_timer(void) { }
+#endif
+
 /* adjtimex mainly allows reading (and writing, if superuser) of
  * kernel time-keeping variables. used by xntpd.
  */
@@ -196,10 +245,12 @@ int do_adjtimex(struct timex *txc)
 
        /* Now we validate the data before disabling interrupts */
 
-       if ((txc->modes & ADJ_OFFSET_SINGLESHOT) == ADJ_OFFSET_SINGLESHOT)
+       if ((txc->modes & ADJ_OFFSET_SINGLESHOT) == ADJ_OFFSET_SINGLESHOT) {
          /* singleshot must not be used with any other mode bits */
-               if (txc->modes != ADJ_OFFSET_SINGLESHOT)
+               if (txc->modes != ADJ_OFFSET_SINGLESHOT &&
+                                       txc->modes != ADJ_OFFSET_SS_READ)
                        return -EINVAL;
+       }
 
        if (txc->modes != ADJ_OFFSET_SINGLESHOT && (txc->modes & ADJ_OFFSET))
          /* adjustment Offset limited to +- .512 seconds */
@@ -319,7 +370,8 @@ int do_adjtimex(struct timex *txc)
 leave: if ((time_status & (STA_UNSYNC|STA_CLOCKERR)) != 0)
                result = TIME_ERROR;
 
-       if ((txc->modes & ADJ_OFFSET_SINGLESHOT) == ADJ_OFFSET_SINGLESHOT)
+       if ((txc->modes == ADJ_OFFSET_SINGLESHOT) ||
+                       (txc->modes == ADJ_OFFSET_SS_READ))
                txc->offset = save_adjust;
        else
                txc->offset = ((long)shift_right(time_offset, SHIFT_UPDATE)) *
@@ -345,6 +397,6 @@ leave:      if ((time_status & (STA_UNSYNC|STA_CLOCKERR)) != 0)
        txc->stbcnt        = 0;
        write_sequnlock_irq(&xtime_lock);
        do_gettimeofday(&txc->time);
-       notify_arch_cmos_timer();
+       notify_cmos_timer();
        return(result);
 }