aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
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 /kernel
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>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/time/clocksource.c126
-rw-r--r--kernel/timer.c45
2 files changed, 144 insertions, 27 deletions
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/*