]> Pileus Git - ~andy/linux/blobdiff - kernel/time/timekeeping.c
Merge branch 'timers/posix-cpu-timers-for-tglx' of
[~andy/linux] / kernel / time / timekeeping.c
index baeeb5c87cf142a818122fac50140a0657f027bd..48b9fffabdc294a4bea16b9b78083a161e8eec42 100644 (file)
 
 #include "tick-internal.h"
 #include "ntp_internal.h"
+#include "timekeeping_internal.h"
+
+#define TK_CLEAR_NTP           (1 << 0)
+#define TK_MIRROR              (1 << 1)
+#define TK_CLOCK_WAS_SET       (1 << 2)
 
 static struct timekeeper timekeeper;
 static DEFINE_RAW_SPINLOCK(timekeeper_lock);
@@ -200,9 +205,9 @@ static inline s64 timekeeping_get_ns_raw(struct timekeeper *tk)
 
 static RAW_NOTIFIER_HEAD(pvclock_gtod_chain);
 
-static void update_pvclock_gtod(struct timekeeper *tk)
+static void update_pvclock_gtod(struct timekeeper *tk, bool was_set)
 {
-       raw_notifier_call_chain(&pvclock_gtod_chain, 0, tk);
+       raw_notifier_call_chain(&pvclock_gtod_chain, was_set, tk);
 }
 
 /**
@@ -216,7 +221,7 @@ int pvclock_gtod_register_notifier(struct notifier_block *nb)
 
        raw_spin_lock_irqsave(&timekeeper_lock, flags);
        ret = raw_notifier_chain_register(&pvclock_gtod_chain, nb);
-       update_pvclock_gtod(tk);
+       update_pvclock_gtod(tk, true);
        raw_spin_unlock_irqrestore(&timekeeper_lock, flags);
 
        return ret;
@@ -241,16 +246,16 @@ int pvclock_gtod_unregister_notifier(struct notifier_block *nb)
 EXPORT_SYMBOL_GPL(pvclock_gtod_unregister_notifier);
 
 /* must hold timekeeper_lock */
-static void timekeeping_update(struct timekeeper *tk, bool clearntp, bool mirror)
+static void timekeeping_update(struct timekeeper *tk, unsigned int action)
 {
-       if (clearntp) {
+       if (action & TK_CLEAR_NTP) {
                tk->ntp_error = 0;
                ntp_clear();
        }
        update_vsyscall(tk);
-       update_pvclock_gtod(tk);
+       update_pvclock_gtod(tk, action & TK_CLOCK_WAS_SET);
 
-       if (mirror)
+       if (action & TK_MIRROR)
                memcpy(&shadow_timekeeper, &timekeeper, sizeof(timekeeper));
 }
 
@@ -508,7 +513,7 @@ int do_settimeofday(const struct timespec *tv)
 
        tk_set_xtime(tk, tv);
 
-       timekeeping_update(tk, true, true);
+       timekeeping_update(tk, TK_CLEAR_NTP | TK_MIRROR | TK_CLOCK_WAS_SET);
 
        write_seqcount_end(&timekeeper_seq);
        raw_spin_unlock_irqrestore(&timekeeper_lock, flags);
@@ -552,7 +557,7 @@ int timekeeping_inject_offset(struct timespec *ts)
        tk_set_wall_to_mono(tk, timespec_sub(tk->wall_to_monotonic, *ts));
 
 error: /* even if we error out, we forwarded the time, so call update */
-       timekeeping_update(tk, true, true);
+       timekeeping_update(tk, TK_CLEAR_NTP | TK_MIRROR | TK_CLOCK_WAS_SET);
 
        write_seqcount_end(&timekeeper_seq);
        raw_spin_unlock_irqrestore(&timekeeper_lock, flags);
@@ -627,13 +632,22 @@ static int change_clocksource(void *data)
        write_seqcount_begin(&timekeeper_seq);
 
        timekeeping_forward_now(tk);
-       if (!new->enable || new->enable(new) == 0) {
-               old = tk->clock;
-               tk_setup_internals(tk, new);
-               if (old->disable)
-                       old->disable(old);
+       /*
+        * If the cs is in module, get a module reference. Succeeds
+        * for built-in code (owner == NULL) as well.
+        */
+       if (try_module_get(new->owner)) {
+               if (!new->enable || new->enable(new) == 0) {
+                       old = tk->clock;
+                       tk_setup_internals(tk, new);
+                       if (old->disable)
+                               old->disable(old);
+                       module_put(old->owner);
+               } else {
+                       module_put(new->owner);
+               }
        }
-       timekeeping_update(tk, true, true);
+       timekeeping_update(tk, TK_CLEAR_NTP | TK_MIRROR | TK_CLOCK_WAS_SET);
 
        write_seqcount_end(&timekeeper_seq);
        raw_spin_unlock_irqrestore(&timekeeper_lock, flags);
@@ -648,14 +662,15 @@ static int change_clocksource(void *data)
  * This function is called from clocksource.c after a new, better clock
  * source has been registered. The caller holds the clocksource_mutex.
  */
-void timekeeping_notify(struct clocksource *clock)
+int timekeeping_notify(struct clocksource *clock)
 {
        struct timekeeper *tk = &timekeeper;
 
        if (tk->clock == clock)
-               return;
+               return 0;
        stop_machine(change_clocksource, clock, NULL);
        tick_clock_notify();
+       return tk->clock == clock ? 0 : -1;
 }
 
 /**
@@ -841,6 +856,7 @@ static void __timekeeping_inject_sleeptime(struct timekeeper *tk,
        tk_xtime_add(tk, delta);
        tk_set_wall_to_mono(tk, timespec_sub(tk->wall_to_monotonic, *delta));
        tk_set_sleep_time(tk, timespec_add(tk->total_sleep_time, *delta));
+       tk_debug_account_sleep_time(delta);
 }
 
 /**
@@ -872,7 +888,7 @@ void timekeeping_inject_sleeptime(struct timespec *delta)
 
        __timekeeping_inject_sleeptime(tk, delta);
 
-       timekeeping_update(tk, true, true);
+       timekeeping_update(tk, TK_CLEAR_NTP | TK_MIRROR | TK_CLOCK_WAS_SET);
 
        write_seqcount_end(&timekeeper_seq);
        raw_spin_unlock_irqrestore(&timekeeper_lock, flags);
@@ -954,7 +970,7 @@ static void timekeeping_resume(void)
        tk->cycle_last = clock->cycle_last = cycle_now;
        tk->ntp_error = 0;
        timekeeping_suspended = 0;
-       timekeeping_update(tk, false, true);
+       timekeeping_update(tk, TK_MIRROR | TK_CLOCK_WAS_SET);
        write_seqcount_end(&timekeeper_seq);
        raw_spin_unlock_irqrestore(&timekeeper_lock, flags);
 
@@ -1236,9 +1252,10 @@ out_adjust:
  * It also calls into the NTP code to handle leapsecond processing.
  *
  */
-static inline void accumulate_nsecs_to_secs(struct timekeeper *tk)
+static inline unsigned int accumulate_nsecs_to_secs(struct timekeeper *tk)
 {
        u64 nsecps = (u64)NSEC_PER_SEC << tk->shift;
+       unsigned int action = 0;
 
        while (tk->xtime_nsec >= nsecps) {
                int leap;
@@ -1261,8 +1278,10 @@ static inline void accumulate_nsecs_to_secs(struct timekeeper *tk)
                        __timekeeping_set_tai_offset(tk, tk->tai_offset - leap);
 
                        clock_was_set_delayed();
+                       action = TK_CLOCK_WAS_SET;
                }
        }
+       return action;
 }
 
 /**
@@ -1347,6 +1366,7 @@ static void update_wall_time(void)
        struct timekeeper *tk = &shadow_timekeeper;
        cycle_t offset;
        int shift = 0, maxshift;
+       unsigned int action;
        unsigned long flags;
 
        raw_spin_lock_irqsave(&timekeeper_lock, flags);
@@ -1399,7 +1419,7 @@ static void update_wall_time(void)
         * Finally, make sure that after the rounding
         * xtime_nsec isn't larger than NSEC_PER_SEC
         */
-       accumulate_nsecs_to_secs(tk);
+       action = accumulate_nsecs_to_secs(tk);
 
        write_seqcount_begin(&timekeeper_seq);
        /* Update clock->cycle_last with the new value */
@@ -1415,7 +1435,7 @@ static void update_wall_time(void)
         * updating.
         */
        memcpy(real_tk, tk, sizeof(*tk));
-       timekeeping_update(real_tk, false, false);
+       timekeeping_update(real_tk, action);
        write_seqcount_end(&timekeeper_seq);
 out:
        raw_spin_unlock_irqrestore(&timekeeper_lock, flags);
@@ -1677,6 +1697,7 @@ int do_adjtimex(struct timex *txc)
 
        if (tai != orig_tai) {
                __timekeeping_set_tai_offset(tk, tai);
+               update_pvclock_gtod(tk, true);
                clock_was_set_delayed();
        }
        write_seqcount_end(&timekeeper_seq);