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 /kernel | |
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>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/time/clocksource.c | 126 | ||||
-rw-r--r-- | kernel/timer.c | 45 |
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 | |||
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 | /* |