diff options
Diffstat (limited to 'kernel/time/clocksource.c')
-rw-r--r-- | kernel/time/clocksource.c | 246 |
1 files changed, 184 insertions, 62 deletions
diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index d9ef176c4e09..193a0793af95 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include <linux/init.h> | 29 | #include <linux/init.h> |
30 | #include <linux/module.h> | 30 | #include <linux/module.h> |
31 | #include <linux/sched.h> /* for spin_unlock_irq() using preempt_count() m68k */ | 31 | #include <linux/sched.h> /* for spin_unlock_irq() using preempt_count() m68k */ |
32 | #include <linux/tick.h> | ||
32 | 33 | ||
33 | /* XXX - Would like a better way for initializing curr_clocksource */ | 34 | /* XXX - Would like a better way for initializing curr_clocksource */ |
34 | extern struct clocksource clocksource_jiffies; | 35 | extern struct clocksource clocksource_jiffies; |
@@ -48,6 +49,7 @@ extern struct clocksource clocksource_jiffies; | |||
48 | */ | 49 | */ |
49 | static struct clocksource *curr_clocksource = &clocksource_jiffies; | 50 | static struct clocksource *curr_clocksource = &clocksource_jiffies; |
50 | static struct clocksource *next_clocksource; | 51 | static struct clocksource *next_clocksource; |
52 | static struct clocksource *clocksource_override; | ||
51 | static LIST_HEAD(clocksource_list); | 53 | static LIST_HEAD(clocksource_list); |
52 | static DEFINE_SPINLOCK(clocksource_lock); | 54 | static DEFINE_SPINLOCK(clocksource_lock); |
53 | static char override_name[32]; | 55 | static char override_name[32]; |
@@ -62,9 +64,123 @@ static int __init clocksource_done_booting(void) | |||
62 | finished_booting = 1; | 64 | finished_booting = 1; |
63 | return 0; | 65 | return 0; |
64 | } | 66 | } |
65 | |||
66 | late_initcall(clocksource_done_booting); | 67 | late_initcall(clocksource_done_booting); |
67 | 68 | ||
69 | #ifdef CONFIG_CLOCKSOURCE_WATCHDOG | ||
70 | static LIST_HEAD(watchdog_list); | ||
71 | static struct clocksource *watchdog; | ||
72 | static struct timer_list watchdog_timer; | ||
73 | static DEFINE_SPINLOCK(watchdog_lock); | ||
74 | static cycle_t watchdog_last; | ||
75 | /* | ||
76 | * Interval: 0.5sec Treshold: 0.0625s | ||
77 | */ | ||
78 | #define WATCHDOG_INTERVAL (HZ >> 1) | ||
79 | #define WATCHDOG_TRESHOLD (NSEC_PER_SEC >> 4) | ||
80 | |||
81 | static void clocksource_ratewd(struct clocksource *cs, int64_t delta) | ||
82 | { | ||
83 | if (delta > -WATCHDOG_TRESHOLD && delta < WATCHDOG_TRESHOLD) | ||
84 | return; | ||
85 | |||
86 | printk(KERN_WARNING "Clocksource %s unstable (delta = %Ld ns)\n", | ||
87 | cs->name, delta); | ||
88 | cs->flags &= ~(CLOCK_SOURCE_VALID_FOR_HRES | CLOCK_SOURCE_WATCHDOG); | ||
89 | clocksource_change_rating(cs, 0); | ||
90 | cs->flags &= ~CLOCK_SOURCE_WATCHDOG; | ||
91 | list_del(&cs->wd_list); | ||
92 | } | ||
93 | |||
94 | static void clocksource_watchdog(unsigned long data) | ||
95 | { | ||
96 | struct clocksource *cs, *tmp; | ||
97 | cycle_t csnow, wdnow; | ||
98 | int64_t wd_nsec, cs_nsec; | ||
99 | |||
100 | spin_lock(&watchdog_lock); | ||
101 | |||
102 | wdnow = watchdog->read(); | ||
103 | wd_nsec = cyc2ns(watchdog, (wdnow - watchdog_last) & watchdog->mask); | ||
104 | watchdog_last = wdnow; | ||
105 | |||
106 | list_for_each_entry_safe(cs, tmp, &watchdog_list, wd_list) { | ||
107 | csnow = cs->read(); | ||
108 | /* Initialized ? */ | ||
109 | if (!(cs->flags & CLOCK_SOURCE_WATCHDOG)) { | ||
110 | if ((cs->flags & CLOCK_SOURCE_IS_CONTINUOUS) && | ||
111 | (watchdog->flags & CLOCK_SOURCE_IS_CONTINUOUS)) { | ||
112 | cs->flags |= CLOCK_SOURCE_VALID_FOR_HRES; | ||
113 | /* | ||
114 | * We just marked the clocksource as | ||
115 | * highres-capable, notify the rest of the | ||
116 | * system as well so that we transition | ||
117 | * into high-res mode: | ||
118 | */ | ||
119 | tick_clock_notify(); | ||
120 | } | ||
121 | cs->flags |= CLOCK_SOURCE_WATCHDOG; | ||
122 | cs->wd_last = csnow; | ||
123 | } else { | ||
124 | cs_nsec = cyc2ns(cs, (csnow - cs->wd_last) & cs->mask); | ||
125 | cs->wd_last = csnow; | ||
126 | /* Check the delta. Might remove from the list ! */ | ||
127 | clocksource_ratewd(cs, cs_nsec - wd_nsec); | ||
128 | } | ||
129 | } | ||
130 | |||
131 | if (!list_empty(&watchdog_list)) { | ||
132 | __mod_timer(&watchdog_timer, | ||
133 | watchdog_timer.expires + WATCHDOG_INTERVAL); | ||
134 | } | ||
135 | spin_unlock(&watchdog_lock); | ||
136 | } | ||
137 | static void clocksource_check_watchdog(struct clocksource *cs) | ||
138 | { | ||
139 | struct clocksource *cse; | ||
140 | unsigned long flags; | ||
141 | |||
142 | spin_lock_irqsave(&watchdog_lock, flags); | ||
143 | if (cs->flags & CLOCK_SOURCE_MUST_VERIFY) { | ||
144 | int started = !list_empty(&watchdog_list); | ||
145 | |||
146 | list_add(&cs->wd_list, &watchdog_list); | ||
147 | if (!started && watchdog) { | ||
148 | watchdog_last = watchdog->read(); | ||
149 | watchdog_timer.expires = jiffies + WATCHDOG_INTERVAL; | ||
150 | add_timer(&watchdog_timer); | ||
151 | } | ||
152 | } else if (cs->flags & CLOCK_SOURCE_IS_CONTINUOUS) { | ||
153 | cs->flags |= CLOCK_SOURCE_VALID_FOR_HRES; | ||
154 | |||
155 | if (!watchdog || cs->rating > watchdog->rating) { | ||
156 | if (watchdog) | ||
157 | del_timer(&watchdog_timer); | ||
158 | watchdog = cs; | ||
159 | init_timer(&watchdog_timer); | ||
160 | watchdog_timer.function = clocksource_watchdog; | ||
161 | |||
162 | /* Reset watchdog cycles */ | ||
163 | list_for_each_entry(cse, &watchdog_list, wd_list) | ||
164 | cse->flags &= ~CLOCK_SOURCE_WATCHDOG; | ||
165 | /* Start if list is not empty */ | ||
166 | if (!list_empty(&watchdog_list)) { | ||
167 | watchdog_last = watchdog->read(); | ||
168 | watchdog_timer.expires = | ||
169 | jiffies + WATCHDOG_INTERVAL; | ||
170 | add_timer(&watchdog_timer); | ||
171 | } | ||
172 | } | ||
173 | } | ||
174 | spin_unlock_irqrestore(&watchdog_lock, flags); | ||
175 | } | ||
176 | #else | ||
177 | static void clocksource_check_watchdog(struct clocksource *cs) | ||
178 | { | ||
179 | if (cs->flags & CLOCK_SOURCE_IS_CONTINUOUS) | ||
180 | cs->flags |= CLOCK_SOURCE_VALID_FOR_HRES; | ||
181 | } | ||
182 | #endif | ||
183 | |||
68 | /** | 184 | /** |
69 | * clocksource_get_next - Returns the selected clocksource | 185 | * clocksource_get_next - Returns the selected clocksource |
70 | * | 186 | * |
@@ -84,60 +200,54 @@ struct clocksource *clocksource_get_next(void) | |||
84 | } | 200 | } |
85 | 201 | ||
86 | /** | 202 | /** |
87 | * select_clocksource - Finds the best registered clocksource. | 203 | * select_clocksource - Selects the best registered clocksource. |
88 | * | 204 | * |
89 | * Private function. Must hold clocksource_lock when called. | 205 | * Private function. Must hold clocksource_lock when called. |
90 | * | 206 | * |
91 | * Looks through the list of registered clocksources, returning | 207 | * Select the clocksource with the best rating, or the clocksource, |
92 | * the one with the highest rating value. If there is a clocksource | 208 | * which is selected by userspace override. |
93 | * name that matches the override string, it returns that clocksource. | ||
94 | */ | 209 | */ |
95 | static struct clocksource *select_clocksource(void) | 210 | static struct clocksource *select_clocksource(void) |
96 | { | 211 | { |
97 | struct clocksource *best = NULL; | 212 | struct clocksource *next; |
98 | struct list_head *tmp; | ||
99 | 213 | ||
100 | list_for_each(tmp, &clocksource_list) { | 214 | if (list_empty(&clocksource_list)) |
101 | struct clocksource *src; | 215 | return NULL; |
102 | 216 | ||
103 | src = list_entry(tmp, struct clocksource, list); | 217 | if (clocksource_override) |
104 | if (!best) | 218 | next = clocksource_override; |
105 | best = src; | 219 | else |
106 | 220 | next = list_entry(clocksource_list.next, struct clocksource, | |
107 | /* check for override: */ | 221 | list); |
108 | if (strlen(src->name) == strlen(override_name) && | 222 | |
109 | !strcmp(src->name, override_name)) { | 223 | if (next == curr_clocksource) |
110 | best = src; | 224 | return NULL; |
111 | break; | ||
112 | } | ||
113 | /* pick the highest rating: */ | ||
114 | if (src->rating > best->rating) | ||
115 | best = src; | ||
116 | } | ||
117 | 225 | ||
118 | return best; | 226 | return next; |
119 | } | 227 | } |
120 | 228 | ||
121 | /** | 229 | /* |
122 | * is_registered_source - Checks if clocksource is registered | 230 | * Enqueue the clocksource sorted by rating |
123 | * @c: pointer to a clocksource | ||
124 | * | ||
125 | * Private helper function. Must hold clocksource_lock when called. | ||
126 | * | ||
127 | * Returns one if the clocksource is already registered, zero otherwise. | ||
128 | */ | 231 | */ |
129 | static int is_registered_source(struct clocksource *c) | 232 | static int clocksource_enqueue(struct clocksource *c) |
130 | { | 233 | { |
131 | int len = strlen(c->name); | 234 | struct list_head *tmp, *entry = &clocksource_list; |
132 | struct list_head *tmp; | ||
133 | 235 | ||
134 | list_for_each(tmp, &clocksource_list) { | 236 | list_for_each(tmp, &clocksource_list) { |
135 | struct clocksource *src; | 237 | struct clocksource *cs; |
136 | 238 | ||
137 | src = list_entry(tmp, struct clocksource, list); | 239 | cs = list_entry(tmp, struct clocksource, list); |
138 | if (strlen(src->name) == len && !strcmp(src->name, c->name)) | 240 | if (cs == c) |
139 | return 1; | 241 | return -EBUSY; |
242 | /* Keep track of the place, where to insert */ | ||
243 | if (cs->rating >= c->rating) | ||
244 | entry = tmp; | ||
140 | } | 245 | } |
246 | list_add(&c->list, entry); | ||
247 | |||
248 | if (strlen(c->name) == strlen(override_name) && | ||
249 | !strcmp(c->name, override_name)) | ||
250 | clocksource_override = c; | ||
141 | 251 | ||
142 | return 0; | 252 | return 0; |
143 | } | 253 | } |
@@ -150,42 +260,35 @@ static int is_registered_source(struct clocksource *c) | |||
150 | */ | 260 | */ |
151 | int clocksource_register(struct clocksource *c) | 261 | int clocksource_register(struct clocksource *c) |
152 | { | 262 | { |
153 | int ret = 0; | ||
154 | unsigned long flags; | 263 | unsigned long flags; |
264 | int ret; | ||
155 | 265 | ||
156 | spin_lock_irqsave(&clocksource_lock, flags); | 266 | spin_lock_irqsave(&clocksource_lock, flags); |
157 | /* check if clocksource is already registered */ | 267 | ret = clocksource_enqueue(c); |
158 | if (is_registered_source(c)) { | 268 | if (!ret) |
159 | printk("register_clocksource: Cannot register %s. " | ||
160 | "Already registered!", c->name); | ||
161 | ret = -EBUSY; | ||
162 | } else { | ||
163 | /* register it */ | ||
164 | list_add(&c->list, &clocksource_list); | ||
165 | /* scan the registered clocksources, and pick the best one */ | ||
166 | next_clocksource = select_clocksource(); | 269 | next_clocksource = select_clocksource(); |
167 | } | ||
168 | spin_unlock_irqrestore(&clocksource_lock, flags); | 270 | spin_unlock_irqrestore(&clocksource_lock, flags); |
271 | if (!ret) | ||
272 | clocksource_check_watchdog(c); | ||
169 | return ret; | 273 | return ret; |
170 | } | 274 | } |
171 | EXPORT_SYMBOL(clocksource_register); | 275 | EXPORT_SYMBOL(clocksource_register); |
172 | 276 | ||
173 | /** | 277 | /** |
174 | * clocksource_reselect - Rescan list for next clocksource | 278 | * clocksource_change_rating - Change the rating of a registered clocksource |
175 | * | 279 | * |
176 | * A quick helper function to be used if a clocksource changes its | ||
177 | * rating. Forces the clocksource list to be re-scanned for the best | ||
178 | * clocksource. | ||
179 | */ | 280 | */ |
180 | void clocksource_reselect(void) | 281 | void clocksource_change_rating(struct clocksource *cs, int rating) |
181 | { | 282 | { |
182 | unsigned long flags; | 283 | unsigned long flags; |
183 | 284 | ||
184 | spin_lock_irqsave(&clocksource_lock, flags); | 285 | spin_lock_irqsave(&clocksource_lock, flags); |
286 | list_del(&cs->list); | ||
287 | cs->rating = rating; | ||
288 | clocksource_enqueue(cs); | ||
185 | next_clocksource = select_clocksource(); | 289 | next_clocksource = select_clocksource(); |
186 | spin_unlock_irqrestore(&clocksource_lock, flags); | 290 | spin_unlock_irqrestore(&clocksource_lock, flags); |
187 | } | 291 | } |
188 | EXPORT_SYMBOL(clocksource_reselect); | ||
189 | 292 | ||
190 | #ifdef CONFIG_SYSFS | 293 | #ifdef CONFIG_SYSFS |
191 | /** | 294 | /** |
@@ -221,7 +324,11 @@ sysfs_show_current_clocksources(struct sys_device *dev, char *buf) | |||
221 | static ssize_t sysfs_override_clocksource(struct sys_device *dev, | 324 | static ssize_t sysfs_override_clocksource(struct sys_device *dev, |
222 | const char *buf, size_t count) | 325 | const char *buf, size_t count) |
223 | { | 326 | { |
327 | struct clocksource *ovr = NULL; | ||
328 | struct list_head *tmp; | ||
224 | size_t ret = count; | 329 | size_t ret = count; |
330 | int len; | ||
331 | |||
225 | /* strings from sysfs write are not 0 terminated! */ | 332 | /* strings from sysfs write are not 0 terminated! */ |
226 | if (count >= sizeof(override_name)) | 333 | if (count >= sizeof(override_name)) |
227 | return -EINVAL; | 334 | return -EINVAL; |
@@ -229,17 +336,32 @@ static ssize_t sysfs_override_clocksource(struct sys_device *dev, | |||
229 | /* strip of \n: */ | 336 | /* strip of \n: */ |
230 | if (buf[count-1] == '\n') | 337 | if (buf[count-1] == '\n') |
231 | count--; | 338 | count--; |
232 | if (count < 1) | ||
233 | return -EINVAL; | ||
234 | 339 | ||
235 | spin_lock_irq(&clocksource_lock); | 340 | spin_lock_irq(&clocksource_lock); |
236 | 341 | ||
237 | /* copy the name given: */ | 342 | if (count > 0) |
238 | memcpy(override_name, buf, count); | 343 | memcpy(override_name, buf, count); |
239 | override_name[count] = 0; | 344 | override_name[count] = 0; |
240 | 345 | ||
241 | /* try to select it: */ | 346 | len = strlen(override_name); |
242 | next_clocksource = select_clocksource(); | 347 | if (len) { |
348 | ovr = clocksource_override; | ||
349 | /* try to select it: */ | ||
350 | list_for_each(tmp, &clocksource_list) { | ||
351 | struct clocksource *cs; | ||
352 | |||
353 | cs = list_entry(tmp, struct clocksource, list); | ||
354 | if (strlen(cs->name) == len && | ||
355 | !strcmp(cs->name, override_name)) | ||
356 | ovr = cs; | ||
357 | } | ||
358 | } | ||
359 | |||
360 | /* Reselect, when the override name has changed */ | ||
361 | if (ovr != clocksource_override) { | ||
362 | clocksource_override = ovr; | ||
363 | next_clocksource = select_clocksource(); | ||
364 | } | ||
243 | 365 | ||
244 | spin_unlock_irq(&clocksource_lock); | 366 | spin_unlock_irq(&clocksource_lock); |
245 | 367 | ||