diff options
| author | Thomas Gleixner <tglx@linutronix.de> | 2007-02-16 04:27:43 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-02-16 11:13:57 -0500 |
| commit | 5d8b34fdcb384161552d01ee8f34af5ff11f9684 (patch) | |
| tree | 7052d50574d747b7ec2172051adf8126074d6982 | |
| parent | 7e69f2b1ead2a4c51c12817f18263ff0e59335a6 (diff) | |
[PATCH] clocksource: Add verification (watchdog) helper
The TSC needs to be verified against another clocksource. Instead of using
hardwired assumptions of available hardware, provide a generic verification
mechanism. The verification uses the best available clocksource and handles
the usability for high resolution timers / dynticks of the clocksource which
needs to be verified.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Cc: john stultz <johnstul@us.ibm.com>
Cc: Roman Zippel <zippel@linux-m68k.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
| -rw-r--r-- | arch/i386/Kconfig | 4 | ||||
| -rw-r--r-- | arch/i386/kernel/tsc.c | 49 | ||||
| -rw-r--r-- | include/linux/clocksource.h | 15 | ||||
| -rw-r--r-- | kernel/time/clocksource.c | 126 | ||||
| -rw-r--r-- | kernel/timer.c | 45 |
5 files changed, 161 insertions, 78 deletions
diff --git a/arch/i386/Kconfig b/arch/i386/Kconfig index 4ea31c327d1f..458b3aad3eb3 100644 --- a/arch/i386/Kconfig +++ b/arch/i386/Kconfig | |||
| @@ -18,6 +18,10 @@ config GENERIC_TIME | |||
| 18 | bool | 18 | bool |
| 19 | default y | 19 | default y |
| 20 | 20 | ||
| 21 | config CLOCKSOURCE_WATCHDOG | ||
| 22 | bool | ||
| 23 | default y | ||
| 24 | |||
| 21 | config LOCKDEP_SUPPORT | 25 | config LOCKDEP_SUPPORT |
| 22 | bool | 26 | bool |
| 23 | default y | 27 | default y |
diff --git a/arch/i386/kernel/tsc.c b/arch/i386/kernel/tsc.c index b4b2be21d1c7..22931d24027c 100644 --- a/arch/i386/kernel/tsc.c +++ b/arch/i386/kernel/tsc.c | |||
| @@ -344,49 +344,6 @@ static struct dmi_system_id __initdata bad_tsc_dmi_table[] = { | |||
| 344 | {} | 344 | {} |
| 345 | }; | 345 | }; |
| 346 | 346 | ||
| 347 | #define TSC_FREQ_CHECK_INTERVAL (10*MSEC_PER_SEC) /* 10sec in MS */ | ||
| 348 | static struct timer_list verify_tsc_freq_timer; | ||
| 349 | |||
| 350 | /* XXX - Probably should add locking */ | ||
| 351 | static void verify_tsc_freq(unsigned long unused) | ||
| 352 | { | ||
| 353 | static u64 last_tsc; | ||
| 354 | static unsigned long last_jiffies; | ||
| 355 | |||
| 356 | u64 now_tsc, interval_tsc; | ||
| 357 | unsigned long now_jiffies, interval_jiffies; | ||
| 358 | |||
| 359 | |||
| 360 | if (check_tsc_unstable()) | ||
| 361 | return; | ||
| 362 | |||
| 363 | rdtscll(now_tsc); | ||
| 364 | now_jiffies = jiffies; | ||
| 365 | |||
| 366 | if (!last_jiffies) { | ||
| 367 | goto out; | ||
| 368 | } | ||
| 369 | |||
| 370 | interval_jiffies = now_jiffies - last_jiffies; | ||
| 371 | interval_tsc = now_tsc - last_tsc; | ||
| 372 | interval_tsc *= HZ; | ||
| 373 | do_div(interval_tsc, cpu_khz*1000); | ||
| 374 | |||
| 375 | if (interval_tsc < (interval_jiffies * 3 / 4)) { | ||
| 376 | printk("TSC appears to be running slowly. " | ||
| 377 | "Marking it as unstable\n"); | ||
| 378 | mark_tsc_unstable(); | ||
| 379 | return; | ||
| 380 | } | ||
| 381 | |||
| 382 | out: | ||
| 383 | last_tsc = now_tsc; | ||
| 384 | last_jiffies = now_jiffies; | ||
| 385 | /* set us up to go off on the next interval: */ | ||
| 386 | mod_timer(&verify_tsc_freq_timer, | ||
| 387 | jiffies + msecs_to_jiffies(TSC_FREQ_CHECK_INTERVAL)); | ||
| 388 | } | ||
| 389 | |||
| 390 | /* | 347 | /* |
| 391 | * Make an educated guess if the TSC is trustworthy and synchronized | 348 | * Make an educated guess if the TSC is trustworthy and synchronized |
| 392 | * over all CPUs. | 349 | * over all CPUs. |
| @@ -424,12 +381,6 @@ static int __init init_tsc_clocksource(void) | |||
| 424 | clocksource_tsc.flags &= ~CLOCK_SOURCE_IS_CONTINUOUS; | 381 | clocksource_tsc.flags &= ~CLOCK_SOURCE_IS_CONTINUOUS; |
| 425 | } | 382 | } |
| 426 | 383 | ||
| 427 | init_timer(&verify_tsc_freq_timer); | ||
| 428 | verify_tsc_freq_timer.function = verify_tsc_freq; | ||
| 429 | verify_tsc_freq_timer.expires = | ||
| 430 | jiffies + msecs_to_jiffies(TSC_FREQ_CHECK_INTERVAL); | ||
| 431 | add_timer(&verify_tsc_freq_timer); | ||
| 432 | |||
| 433 | return clocksource_register(&clocksource_tsc); | 384 | return clocksource_register(&clocksource_tsc); |
| 434 | } | 385 | } |
| 435 | 386 | ||
diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index a585a29fe7c4..830a250ecf94 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h | |||
| @@ -12,11 +12,13 @@ | |||
| 12 | #include <linux/timex.h> | 12 | #include <linux/timex.h> |
| 13 | #include <linux/time.h> | 13 | #include <linux/time.h> |
| 14 | #include <linux/list.h> | 14 | #include <linux/list.h> |
| 15 | #include <linux/timer.h> | ||
| 15 | #include <asm/div64.h> | 16 | #include <asm/div64.h> |
| 16 | #include <asm/io.h> | 17 | #include <asm/io.h> |
| 17 | 18 | ||
| 18 | /* clocksource cycle base type */ | 19 | /* clocksource cycle base type */ |
| 19 | typedef u64 cycle_t; | 20 | typedef u64 cycle_t; |
| 21 | struct clocksource; | ||
| 20 | 22 | ||
| 21 | /** | 23 | /** |
| 22 | * struct clocksource - hardware abstraction for a free running counter | 24 | * struct clocksource - hardware abstraction for a free running counter |
| @@ -62,13 +64,22 @@ struct clocksource { | |||
| 62 | cycle_t cycle_last, cycle_interval; | 64 | cycle_t cycle_last, cycle_interval; |
| 63 | u64 xtime_nsec, xtime_interval; | 65 | u64 xtime_nsec, xtime_interval; |
| 64 | s64 error; | 66 | s64 error; |
| 67 | |||
| 68 | #ifdef CONFIG_CLOCKSOURCE_WATCHDOG | ||
| 69 | /* Watchdog related data, used by the framework */ | ||
| 70 | struct list_head wd_list; | ||
| 71 | cycle_t wd_last; | ||
| 72 | #endif | ||
| 65 | }; | 73 | }; |
| 66 | 74 | ||
| 67 | /* | 75 | /* |
| 68 | * Clock source flags bits:: | 76 | * Clock source flags bits:: |
| 69 | */ | 77 | */ |
| 70 | #define CLOCK_SOURCE_IS_CONTINUOUS 0x01 | 78 | #define CLOCK_SOURCE_IS_CONTINUOUS 0x01 |
| 71 | #define CLOCK_SOURCE_MUST_VERIFY 0x02 | 79 | #define CLOCK_SOURCE_MUST_VERIFY 0x02 |
| 80 | |||
| 81 | #define CLOCK_SOURCE_WATCHDOG 0x10 | ||
| 82 | #define CLOCK_SOURCE_VALID_FOR_HRES 0x20 | ||
| 72 | 83 | ||
| 73 | /* simplify initialization of mask field */ | 84 | /* simplify initialization of mask field */ |
| 74 | #define CLOCKSOURCE_MASK(bits) (cycle_t)(bits<64 ? ((1ULL<<bits)-1) : -1) | 85 | #define CLOCKSOURCE_MASK(bits) (cycle_t)(bits<64 ? ((1ULL<<bits)-1) : -1) |
diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index 2f6a3d6e43bc..3cb8ac978270 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c | |||
| @@ -63,9 +63,116 @@ static int __init clocksource_done_booting(void) | |||
| 63 | finished_booting = 1; | 63 | finished_booting = 1; |
| 64 | return 0; | 64 | return 0; |
| 65 | } | 65 | } |
| 66 | |||
| 67 | late_initcall(clocksource_done_booting); | 66 | late_initcall(clocksource_done_booting); |
| 68 | 67 | ||
| 68 | #ifdef CONFIG_CLOCKSOURCE_WATCHDOG | ||
| 69 | static LIST_HEAD(watchdog_list); | ||
| 70 | static struct clocksource *watchdog; | ||
| 71 | static struct timer_list watchdog_timer; | ||
| 72 | static DEFINE_SPINLOCK(watchdog_lock); | ||
| 73 | static cycle_t watchdog_last; | ||
| 74 | /* | ||
| 75 | * Interval: 0.5sec Treshold: 0.0625s | ||
| 76 | */ | ||
| 77 | #define WATCHDOG_INTERVAL (HZ >> 1) | ||
| 78 | #define WATCHDOG_TRESHOLD (NSEC_PER_SEC >> 4) | ||
| 79 | |||
| 80 | static void clocksource_ratewd(struct clocksource *cs, int64_t delta) | ||
| 81 | { | ||
| 82 | if (delta > -WATCHDOG_TRESHOLD && delta < WATCHDOG_TRESHOLD) | ||
| 83 | return; | ||
| 84 | |||
| 85 | printk(KERN_WARNING "Clocksource %s unstable (delta = %Ld ns)\n", | ||
| 86 | cs->name, delta); | ||
| 87 | cs->flags &= ~(CLOCK_SOURCE_VALID_FOR_HRES | CLOCK_SOURCE_WATCHDOG); | ||
| 88 | clocksource_change_rating(cs, 0); | ||
| 89 | cs->flags &= ~CLOCK_SOURCE_WATCHDOG; | ||
| 90 | list_del(&cs->wd_list); | ||
| 91 | } | ||
| 92 | |||
| 93 | static void clocksource_watchdog(unsigned long data) | ||
| 94 | { | ||
| 95 | struct clocksource *cs, *tmp; | ||
| 96 | cycle_t csnow, wdnow; | ||
| 97 | int64_t wd_nsec, cs_nsec; | ||
| 98 | |||
| 99 | spin_lock(&watchdog_lock); | ||
| 100 | |||
| 101 | wdnow = watchdog->read(); | ||
| 102 | wd_nsec = cyc2ns(watchdog, (wdnow - watchdog_last) & watchdog->mask); | ||
| 103 | watchdog_last = wdnow; | ||
| 104 | |||
| 105 | list_for_each_entry_safe(cs, tmp, &watchdog_list, wd_list) { | ||
| 106 | csnow = cs->read(); | ||
| 107 | /* Initialized ? */ | ||
| 108 | if (!(cs->flags & CLOCK_SOURCE_WATCHDOG)) { | ||
| 109 | if ((cs->flags & CLOCK_SOURCE_IS_CONTINUOUS) && | ||
| 110 | (watchdog->flags & CLOCK_SOURCE_IS_CONTINUOUS)) { | ||
| 111 | cs->flags |= CLOCK_SOURCE_VALID_FOR_HRES; | ||
| 112 | } | ||
| 113 | cs->flags |= CLOCK_SOURCE_WATCHDOG; | ||
| 114 | cs->wd_last = csnow; | ||
| 115 | } else { | ||
| 116 | cs_nsec = cyc2ns(cs, (csnow - cs->wd_last) & cs->mask); | ||
| 117 | cs->wd_last = csnow; | ||
| 118 | /* Check the delta. Might remove from the list ! */ | ||
| 119 | clocksource_ratewd(cs, cs_nsec - wd_nsec); | ||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 123 | if (!list_empty(&watchdog_list)) { | ||
| 124 | __mod_timer(&watchdog_timer, | ||
| 125 | watchdog_timer.expires + WATCHDOG_INTERVAL); | ||
| 126 | } | ||
| 127 | spin_unlock(&watchdog_lock); | ||
| 128 | } | ||
| 129 | static void clocksource_check_watchdog(struct clocksource *cs) | ||
| 130 | { | ||
| 131 | struct clocksource *cse; | ||
| 132 | unsigned long flags; | ||
| 133 | |||
| 134 | spin_lock_irqsave(&watchdog_lock, flags); | ||
| 135 | if (cs->flags & CLOCK_SOURCE_MUST_VERIFY) { | ||
| 136 | int started = !list_empty(&watchdog_list); | ||
| 137 | |||
| 138 | list_add(&cs->wd_list, &watchdog_list); | ||
| 139 | if (!started && watchdog) { | ||
| 140 | watchdog_last = watchdog->read(); | ||
| 141 | watchdog_timer.expires = jiffies + WATCHDOG_INTERVAL; | ||
| 142 | add_timer(&watchdog_timer); | ||
| 143 | } | ||
| 144 | } else if (cs->flags & CLOCK_SOURCE_IS_CONTINUOUS) { | ||
| 145 | cs->flags |= CLOCK_SOURCE_VALID_FOR_HRES; | ||
| 146 | |||
| 147 | if (!watchdog || cs->rating > watchdog->rating) { | ||
| 148 | if (watchdog) | ||
| 149 | del_timer(&watchdog_timer); | ||
| 150 | watchdog = cs; | ||
| 151 | init_timer(&watchdog_timer); | ||
| 152 | watchdog_timer.function = clocksource_watchdog; | ||
| 153 | |||
| 154 | /* Reset watchdog cycles */ | ||
| 155 | list_for_each_entry(cse, &watchdog_list, wd_list) | ||
| 156 | cse->flags &= ~CLOCK_SOURCE_WATCHDOG; | ||
| 157 | /* Start if list is not empty */ | ||
| 158 | if (!list_empty(&watchdog_list)) { | ||
| 159 | watchdog_last = watchdog->read(); | ||
| 160 | watchdog_timer.expires = | ||
| 161 | jiffies + WATCHDOG_INTERVAL; | ||
| 162 | add_timer(&watchdog_timer); | ||
| 163 | } | ||
| 164 | } | ||
| 165 | } | ||
| 166 | spin_unlock_irqrestore(&watchdog_lock, flags); | ||
| 167 | } | ||
| 168 | #else | ||
| 169 | static void clocksource_check_watchdog(struct clocksource *cs) | ||
| 170 | { | ||
| 171 | if (cs->flags & CLOCK_SOURCE_IS_CONTINUOUS) | ||
| 172 | cs->flags |= CLOCK_SOURCE_VALID_FOR_HRES; | ||
| 173 | } | ||
| 174 | #endif | ||
| 175 | |||
| 69 | /** | 176 | /** |
| 70 | * clocksource_get_next - Returns the selected clocksource | 177 | * clocksource_get_next - Returns the selected clocksource |
| 71 | * | 178 | * |
| @@ -94,13 +201,21 @@ struct clocksource *clocksource_get_next(void) | |||
| 94 | */ | 201 | */ |
| 95 | static struct clocksource *select_clocksource(void) | 202 | static struct clocksource *select_clocksource(void) |
| 96 | { | 203 | { |
| 204 | struct clocksource *next; | ||
| 205 | |||
| 97 | if (list_empty(&clocksource_list)) | 206 | if (list_empty(&clocksource_list)) |
| 98 | return NULL; | 207 | return NULL; |
| 99 | 208 | ||
| 100 | if (clocksource_override) | 209 | if (clocksource_override) |
| 101 | return clocksource_override; | 210 | next = clocksource_override; |
| 211 | else | ||
| 212 | next = list_entry(clocksource_list.next, struct clocksource, | ||
| 213 | list); | ||
| 102 | 214 | ||
| 103 | return list_entry(clocksource_list.next, struct clocksource, list); | 215 | if (next == curr_clocksource) |
| 216 | return NULL; | ||
| 217 | |||
| 218 | return next; | ||
| 104 | } | 219 | } |
| 105 | 220 | ||
| 106 | /* | 221 | /* |
| @@ -138,13 +253,15 @@ static int clocksource_enqueue(struct clocksource *c) | |||
| 138 | int clocksource_register(struct clocksource *c) | 253 | int clocksource_register(struct clocksource *c) |
| 139 | { | 254 | { |
| 140 | unsigned long flags; | 255 | unsigned long flags; |
| 141 | int ret = 0; | 256 | int ret; |
| 142 | 257 | ||
| 143 | spin_lock_irqsave(&clocksource_lock, flags); | 258 | spin_lock_irqsave(&clocksource_lock, flags); |
| 144 | ret = clocksource_enqueue(c); | 259 | ret = clocksource_enqueue(c); |
| 145 | if (!ret) | 260 | if (!ret) |
| 146 | next_clocksource = select_clocksource(); | 261 | next_clocksource = select_clocksource(); |
| 147 | spin_unlock_irqrestore(&clocksource_lock, flags); | 262 | spin_unlock_irqrestore(&clocksource_lock, flags); |
| 263 | if (!ret) | ||
| 264 | clocksource_check_watchdog(c); | ||
| 148 | return ret; | 265 | return ret; |
| 149 | } | 266 | } |
| 150 | EXPORT_SYMBOL(clocksource_register); | 267 | EXPORT_SYMBOL(clocksource_register); |
| @@ -159,6 +276,7 @@ void clocksource_change_rating(struct clocksource *cs, int rating) | |||
| 159 | 276 | ||
| 160 | spin_lock_irqsave(&clocksource_lock, flags); | 277 | spin_lock_irqsave(&clocksource_lock, flags); |
| 161 | list_del(&cs->list); | 278 | list_del(&cs->list); |
| 279 | cs->rating = rating; | ||
| 162 | clocksource_enqueue(cs); | 280 | clocksource_enqueue(cs); |
| 163 | next_clocksource = select_clocksource(); | 281 | next_clocksource = select_clocksource(); |
| 164 | spin_unlock_irqrestore(&clocksource_lock, flags); | 282 | spin_unlock_irqrestore(&clocksource_lock, flags); |
diff --git a/kernel/timer.c b/kernel/timer.c index 4b088fcadb3f..b68a21a82e17 100644 --- a/kernel/timer.c +++ b/kernel/timer.c | |||
| @@ -832,30 +832,33 @@ EXPORT_SYMBOL(do_settimeofday); | |||
| 832 | * | 832 | * |
| 833 | * Accumulates current time interval and initializes new clocksource | 833 | * Accumulates current time interval and initializes new clocksource |
| 834 | */ | 834 | */ |
| 835 | static int change_clocksource(void) | 835 | static void change_clocksource(void) |
| 836 | { | 836 | { |
| 837 | struct clocksource *new; | 837 | struct clocksource *new; |
| 838 | cycle_t now; | 838 | cycle_t now; |
| 839 | u64 nsec; | 839 | u64 nsec; |
| 840 | |||
| 840 | new = clocksource_get_next(); | 841 | new = clocksource_get_next(); |
| 841 | if (clock != new) { | 842 | |
| 842 | now = clocksource_read(new); | 843 | if (clock == new) |
| 843 | nsec = __get_nsec_offset(); | 844 | return; |
| 844 | timespec_add_ns(&xtime, nsec); | 845 | |
| 845 | 846 | now = clocksource_read(new); | |
| 846 | clock = new; | 847 | nsec = __get_nsec_offset(); |
| 847 | clock->cycle_last = now; | 848 | timespec_add_ns(&xtime, nsec); |
| 848 | printk(KERN_INFO "Time: %s clocksource has been installed.\n", | 849 | |
| 849 | clock->name); | 850 | clock = new; |
| 850 | return 1; | 851 | clock->cycle_last = now; |
| 851 | } | 852 | |
| 852 | return 0; | 853 | clock->error = 0; |
| 854 | clock->xtime_nsec = 0; | ||
| 855 | clocksource_calculate_interval(clock, NTP_INTERVAL_LENGTH); | ||
| 856 | |||
| 857 | printk(KERN_INFO "Time: %s clocksource has been installed.\n", | ||
| 858 | clock->name); | ||
| 853 | } | 859 | } |
| 854 | #else | 860 | #else |
| 855 | static inline int change_clocksource(void) | 861 | static inline void change_clocksource(void) { } |
| 856 | { | ||
| 857 | return 0; | ||
| 858 | } | ||
| 859 | #endif | 862 | #endif |
| 860 | 863 | ||
| 861 | /** | 864 | /** |
| @@ -869,7 +872,7 @@ int timekeeping_is_continuous(void) | |||
| 869 | do { | 872 | do { |
| 870 | seq = read_seqbegin(&xtime_lock); | 873 | seq = read_seqbegin(&xtime_lock); |
| 871 | 874 | ||
| 872 | ret = clock->flags & CLOCK_SOURCE_IS_CONTINUOUS; | 875 | ret = clock->flags & CLOCK_SOURCE_VALID_FOR_HRES; |
| 873 | 876 | ||
| 874 | } while (read_seqretry(&xtime_lock, seq)); | 877 | } while (read_seqretry(&xtime_lock, seq)); |
| 875 | 878 | ||
| @@ -1124,11 +1127,7 @@ static void update_wall_time(void) | |||
| 1124 | clock->xtime_nsec -= (s64)xtime.tv_nsec << clock->shift; | 1127 | clock->xtime_nsec -= (s64)xtime.tv_nsec << clock->shift; |
| 1125 | 1128 | ||
| 1126 | /* check to see if there is a new clocksource to use */ | 1129 | /* check to see if there is a new clocksource to use */ |
| 1127 | if (change_clocksource()) { | 1130 | change_clocksource(); |
| 1128 | clock->error = 0; | ||
| 1129 | clock->xtime_nsec = 0; | ||
| 1130 | clocksource_calculate_interval(clock, NTP_INTERVAL_LENGTH); | ||
| 1131 | } | ||
| 1132 | } | 1131 | } |
| 1133 | 1132 | ||
| 1134 | /* | 1133 | /* |
