aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2007-02-16 04:27:43 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-02-16 11:13:57 -0500
commit5d8b34fdcb384161552d01ee8f34af5ff11f9684 (patch)
tree7052d50574d747b7ec2172051adf8126074d6982
parent7e69f2b1ead2a4c51c12817f18263ff0e59335a6 (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/Kconfig4
-rw-r--r--arch/i386/kernel/tsc.c49
-rw-r--r--include/linux/clocksource.h15
-rw-r--r--kernel/time/clocksource.c126
-rw-r--r--kernel/timer.c45
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
21config CLOCKSOURCE_WATCHDOG
22 bool
23 default y
24
21config LOCKDEP_SUPPORT 25config 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 */
348static struct timer_list verify_tsc_freq_timer;
349
350/* XXX - Probably should add locking */
351static 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
382out:
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 */
19typedef u64 cycle_t; 20typedef u64 cycle_t;
21struct 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
67late_initcall(clocksource_done_booting); 66late_initcall(clocksource_done_booting);
68 67
68#ifdef CONFIG_CLOCKSOURCE_WATCHDOG
69static LIST_HEAD(watchdog_list);
70static struct clocksource *watchdog;
71static struct timer_list watchdog_timer;
72static DEFINE_SPINLOCK(watchdog_lock);
73static 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
80static 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
93static 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}
129static 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
169static 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 */
95static struct clocksource *select_clocksource(void) 202static 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)
138int clocksource_register(struct clocksource *c) 253int 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}
150EXPORT_SYMBOL(clocksource_register); 267EXPORT_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 */
835static int change_clocksource(void) 835static 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
855static inline int change_clocksource(void) 861static 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/*