clocksource: introduce CLOCK_MONOTONIC_RAW
[safe/jmp/linux-2.6] / kernel / time / timekeeping.c
index 671af61..5099c95 100644 (file)
@@ -53,32 +53,31 @@ void update_xtime_cache(u64 nsec)
        timespec_add_ns(&xtime_cache, nsec);
 }
 
-static struct clocksource *clock; /* pointer to current clocksource */
+struct clocksource *clock;
 
 
 #ifdef CONFIG_GENERIC_TIME
 /**
- * __get_nsec_offset - Returns nanoseconds since last call to periodic_hook
+ * clocksource_forward_now - update clock to the current time
  *
- * private function, must hold xtime_lock lock when being
- * called. Returns the number of nanoseconds since the
- * last call to update_wall_time() (adjusted by NTP scaling)
+ * Forward the current clock to update its state since the last call to
+ * update_wall_time(). This is useful before significant clock changes,
+ * as it avoids having to deal with this time offset explicitly.
  */
-static inline s64 __get_nsec_offset(void)
+static void clocksource_forward_now(void)
 {
        cycle_t cycle_now, cycle_delta;
-       s64 ns_offset;
+       s64 nsec;
 
-       /* read clocksource: */
        cycle_now = clocksource_read(clock);
-
-       /* calculate the delta since the last update_wall_time: */
        cycle_delta = (cycle_now - clock->cycle_last) & clock->mask;
+       clock->cycle_last = cycle_now;
 
-       /* convert to nanoseconds: */
-       ns_offset = cyc2ns(clock, cycle_delta);
+       nsec = cyc2ns(clock, cycle_delta);
+       timespec_add_ns(&xtime, nsec);
 
-       return ns_offset;
+       nsec = ((s64)cycle_delta * clock->mult_orig) >> clock->shift;
+       clock->raw_time.tv_nsec += nsec;
 }
 
 /**
@@ -89,6 +88,7 @@ static inline s64 __get_nsec_offset(void)
  */
 void getnstimeofday(struct timespec *ts)
 {
+       cycle_t cycle_now, cycle_delta;
        unsigned long seq;
        s64 nsecs;
 
@@ -96,7 +96,15 @@ void getnstimeofday(struct timespec *ts)
                seq = read_seqbegin(&xtime_lock);
 
                *ts = xtime;
-               nsecs = __get_nsec_offset();
+
+               /* read clocksource: */
+               cycle_now = clocksource_read(clock);
+
+               /* calculate the delta since the last update_wall_time: */
+               cycle_delta = (cycle_now - clock->cycle_last) & clock->mask;
+
+               /* convert to nanoseconds: */
+               nsecs = cyc2ns(clock, cycle_delta);
 
        } while (read_seqretry(&xtime_lock, seq));
 
@@ -129,22 +137,22 @@ EXPORT_SYMBOL(do_gettimeofday);
  */
 int do_settimeofday(struct timespec *tv)
 {
+       struct timespec ts_delta;
        unsigned long flags;
-       time_t wtm_sec, sec = tv->tv_sec;
-       long wtm_nsec, nsec = tv->tv_nsec;
 
        if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
                return -EINVAL;
 
        write_seqlock_irqsave(&xtime_lock, flags);
 
-       nsec -= __get_nsec_offset();
+       clocksource_forward_now();
 
-       wtm_sec  = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec);
-       wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec);
+       ts_delta.tv_sec = tv->tv_sec - xtime.tv_sec;
+       ts_delta.tv_nsec = tv->tv_nsec - xtime.tv_nsec;
+       wall_to_monotonic = timespec_sub(wall_to_monotonic, ts_delta);
+
+       xtime = *tv;
 
-       set_normalized_timespec(&xtime, sec, nsec);
-       set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec);
        update_xtime_cache(0);
 
        clock->error = 0;
@@ -170,36 +178,71 @@ EXPORT_SYMBOL(do_settimeofday);
 static void change_clocksource(void)
 {
        struct clocksource *new;
-       cycle_t now;
-       u64 nsec;
 
        new = clocksource_get_next();
 
        if (clock == new)
                return;
 
-       now = clocksource_read(new);
-       nsec =  __get_nsec_offset();
-       timespec_add_ns(&xtime, nsec);
+       clocksource_forward_now();
 
-       clock = new;
-       clock->cycle_last = now;
+       new->raw_time = clock->raw_time;
 
+       clock = new;
+       clock->cycle_last = 0;
+       clock->cycle_last = clocksource_read(new);
        clock->error = 0;
        clock->xtime_nsec = 0;
        clocksource_calculate_interval(clock, NTP_INTERVAL_LENGTH);
 
        tick_clock_notify();
 
+       /*
+        * We're holding xtime lock and waking up klogd would deadlock
+        * us on enqueue.  So no printing!
        printk(KERN_INFO "Time: %s clocksource has been installed.\n",
               clock->name);
+        */
 }
 #else
+static inline void clocksource_forward_now(void) { }
 static inline void change_clocksource(void) { }
-static inline s64 __get_nsec_offset(void) { return 0; }
 #endif
 
 /**
+ * getrawmonotonic - Returns the raw monotonic time in a timespec
+ * @ts:                pointer to the timespec to be set
+ *
+ * Returns the raw monotonic time (completely un-modified by ntp)
+ */
+void getrawmonotonic(struct timespec *ts)
+{
+       unsigned long seq;
+       s64 nsecs;
+       cycle_t cycle_now, cycle_delta;
+
+       do {
+               seq = read_seqbegin(&xtime_lock);
+
+               /* read clocksource: */
+               cycle_now = clocksource_read(clock);
+
+               /* calculate the delta since the last update_wall_time: */
+               cycle_delta = (cycle_now - clock->cycle_last) & clock->mask;
+
+               /* convert to nanoseconds: */
+               nsecs = ((s64)cycle_delta * clock->mult_orig) >> clock->shift;
+
+               *ts = clock->raw_time;
+
+       } while (read_seqretry(&xtime_lock, seq));
+
+       timespec_add_ns(ts, nsecs);
+}
+EXPORT_SYMBOL(getrawmonotonic);
+
+
+/**
  * timekeeping_valid_for_hres - Check if timekeeping is suitable for hres
  */
 int timekeeping_valid_for_hres(void)
@@ -241,7 +284,7 @@ void __init timekeeping_init(void)
 
        write_seqlock_irqsave(&xtime_lock, flags);
 
-       ntp_clear();
+       ntp_init();
 
        clock = clocksource_get_next();
        clocksource_calculate_interval(clock, NTP_INTERVAL_LENGTH);
@@ -260,8 +303,6 @@ void __init timekeeping_init(void)
 static int timekeeping_suspended;
 /* time in seconds when suspend began */
 static unsigned long timekeeping_suspend_time;
-/* xtime offset when we went into suspend */
-static s64 timekeeping_suspend_nsecs;
 
 /**
  * timekeeping_resume - Resumes the generic timekeeping subsystem.
@@ -287,10 +328,9 @@ static int timekeeping_resume(struct sys_device *dev)
                wall_to_monotonic.tv_sec -= sleep_length;
                total_sleep_time += sleep_length;
        }
-       /* Make sure that we have the correct xtime reference */
-       timespec_add_ns(&xtime, timekeeping_suspend_nsecs);
        update_xtime_cache(0);
        /* re-base the last cycle value */
+       clock->cycle_last = 0;
        clock->cycle_last = clocksource_read(clock);
        clock->error = 0;
        timekeeping_suspended = 0;
@@ -313,8 +353,7 @@ static int timekeeping_suspend(struct sys_device *dev, pm_message_t state)
        timekeeping_suspend_time = read_persistent_clock();
 
        write_seqlock_irqsave(&xtime_lock, flags);
-       /* Get the current xtime offset */
-       timekeeping_suspend_nsecs = __get_nsec_offset();
+       clocksource_forward_now();
        timekeeping_suspended = 1;
        write_sequnlock_irqrestore(&xtime_lock, flags);
 
@@ -365,7 +404,7 @@ static __always_inline int clocksource_bigadjust(s64 error, s64 *interval,
         * here.  This is tuned so that an error of about 1 msec is adjusted
         * within about 1 sec (or 2^20 nsec in 2^SHIFT_HZ ticks).
         */
-       error2 = clock->error >> (TICK_LENGTH_SHIFT + 22 - 2 * SHIFT_HZ);
+       error2 = clock->error >> (NTP_SCALE_SHIFT + 22 - 2 * SHIFT_HZ);
        error2 = abs(error2);
        for (look_ahead = 0; error2 > 0; look_ahead++)
                error2 >>= 2;
@@ -374,8 +413,7 @@ static __always_inline int clocksource_bigadjust(s64 error, s64 *interval,
         * Now calculate the error in (1 << look_ahead) ticks, but first
         * remove the single look ahead already included in the error.
         */
-       tick_error = current_tick_length() >>
-               (TICK_LENGTH_SHIFT - clock->shift + 1);
+       tick_error = tick_length >> (NTP_SCALE_SHIFT - clock->shift + 1);
        tick_error -= clock->xtime_interval >> 1;
        error = ((error - tick_error) >> look_ahead) + tick_error;
 
@@ -406,7 +444,7 @@ static void clocksource_adjust(s64 offset)
        s64 error, interval = clock->cycle_interval;
        int adj;
 
-       error = clock->error >> (TICK_LENGTH_SHIFT - clock->shift - 1);
+       error = clock->error >> (NTP_SCALE_SHIFT - clock->shift - 1);
        if (error > interval) {
                error >>= 2;
                if (likely(error <= interval))
@@ -428,7 +466,7 @@ static void clocksource_adjust(s64 offset)
        clock->xtime_interval += interval;
        clock->xtime_nsec -= offset;
        clock->error -= (interval - offset) <<
-                       (TICK_LENGTH_SHIFT - clock->shift);
+                       (NTP_SCALE_SHIFT - clock->shift);
 }
 
 /**
@@ -456,19 +494,25 @@ void update_wall_time(void)
         */
        while (offset >= clock->cycle_interval) {
                /* accumulate one interval */
-               clock->xtime_nsec += clock->xtime_interval;
-               clock->cycle_last += clock->cycle_interval;
                offset -= clock->cycle_interval;
+               clock->cycle_last += clock->cycle_interval;
 
+               clock->xtime_nsec += clock->xtime_interval;
                if (clock->xtime_nsec >= (u64)NSEC_PER_SEC << clock->shift) {
                        clock->xtime_nsec -= (u64)NSEC_PER_SEC << clock->shift;
                        xtime.tv_sec++;
                        second_overflow();
                }
 
+               clock->raw_time.tv_nsec += clock->raw_interval;
+               if (clock->raw_time.tv_nsec >= NSEC_PER_SEC) {
+                       clock->raw_time.tv_nsec -= NSEC_PER_SEC;
+                       clock->raw_time.tv_sec++;
+               }
+
                /* accumulate error between NTP and clock interval */
-               clock->error += current_tick_length();
-               clock->error -= clock->xtime_interval << (TICK_LENGTH_SHIFT - clock->shift);
+               clock->error += tick_length;
+               clock->error -= clock->xtime_interval << (NTP_SCALE_SHIFT - clock->shift);
        }
 
        /* correct the clock when NTP error is too big */