diff options
author | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2009-08-14 09:47:21 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2009-08-15 04:55:46 -0400 |
commit | f1b82746c1e93daf24e1ab9bfbd39bcdb2e7018b (patch) | |
tree | 1403e3662dd3b32cb8b17218bfdd9a640061f654 | |
parent | 1be396794897f80bfc8774719ba60309a9e3d374 (diff) |
clocksource: Cleanup clocksource selection
If a non high-resolution clocksource is first set as override clock
and then registered it becomes active even if the system is in one-shot
mode. Move the override check from sysfs_override_clocksource to the
clocksource selection. That fixes the bug and simplifies the code. The
check in clocksource_register for double registration of the same
clocksource is removed without replacement.
To find the initial clocksource a new weak function in jiffies.c is
defined that returns the jiffies clocksource. The architecture code
can then override the weak function with a more suitable clocksource,
e.g. the TOD clock on s390.
[ tglx: Folded in a fix from John Stultz ]
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Acked-by: John Stultz <johnstul@us.ibm.com>
Cc: Daniel Walker <dwalker@fifo99.com>
LKML-Reference: <20090814134808.388024160@de.ibm.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r-- | arch/s390/kernel/time.c | 4 | ||||
-rw-r--r-- | include/linux/clocksource.h | 2 | ||||
-rw-r--r-- | kernel/time/clocksource.c | 134 | ||||
-rw-r--r-- | kernel/time/jiffies.c | 6 | ||||
-rw-r--r-- | kernel/time/timekeeping.c | 4 |
5 files changed, 64 insertions, 86 deletions
diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index d4c8e9c47c81..afefe514df0f 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c | |||
@@ -205,6 +205,10 @@ static struct clocksource clocksource_tod = { | |||
205 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | 205 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, |
206 | }; | 206 | }; |
207 | 207 | ||
208 | struct clocksource * __init clocksource_default_clock(void) | ||
209 | { | ||
210 | return &clocksource_tod; | ||
211 | } | ||
208 | 212 | ||
209 | void update_vsyscall(struct timespec *wall_time, struct clocksource *clock) | 213 | void update_vsyscall(struct timespec *wall_time, struct clocksource *clock) |
210 | { | 214 | { |
diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index a1ef46f61c81..f263b3abf46e 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <linux/list.h> | 14 | #include <linux/list.h> |
15 | #include <linux/cache.h> | 15 | #include <linux/cache.h> |
16 | #include <linux/timer.h> | 16 | #include <linux/timer.h> |
17 | #include <linux/init.h> | ||
17 | #include <asm/div64.h> | 18 | #include <asm/div64.h> |
18 | #include <asm/io.h> | 19 | #include <asm/io.h> |
19 | 20 | ||
@@ -322,6 +323,7 @@ extern void clocksource_touch_watchdog(void); | |||
322 | extern struct clocksource* clocksource_get_next(void); | 323 | extern struct clocksource* clocksource_get_next(void); |
323 | extern void clocksource_change_rating(struct clocksource *cs, int rating); | 324 | extern void clocksource_change_rating(struct clocksource *cs, int rating); |
324 | extern void clocksource_resume(void); | 325 | extern void clocksource_resume(void); |
326 | extern struct clocksource * __init __weak clocksource_default_clock(void); | ||
325 | 327 | ||
326 | #ifdef CONFIG_GENERIC_TIME_VSYSCALL | 328 | #ifdef CONFIG_GENERIC_TIME_VSYSCALL |
327 | extern void update_vsyscall(struct timespec *ts, struct clocksource *c); | 329 | extern void update_vsyscall(struct timespec *ts, struct clocksource *c); |
diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index 7466cb811251..e91662e87cde 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c | |||
@@ -21,7 +21,6 @@ | |||
21 | * | 21 | * |
22 | * TODO WishList: | 22 | * TODO WishList: |
23 | * o Allow clocksource drivers to be unregistered | 23 | * o Allow clocksource drivers to be unregistered |
24 | * o get rid of clocksource_jiffies extern | ||
25 | */ | 24 | */ |
26 | 25 | ||
27 | #include <linux/clocksource.h> | 26 | #include <linux/clocksource.h> |
@@ -107,12 +106,9 @@ u64 timecounter_cyc2time(struct timecounter *tc, | |||
107 | } | 106 | } |
108 | EXPORT_SYMBOL(timecounter_cyc2time); | 107 | EXPORT_SYMBOL(timecounter_cyc2time); |
109 | 108 | ||
110 | /* XXX - Would like a better way for initializing curr_clocksource */ | ||
111 | extern struct clocksource clocksource_jiffies; | ||
112 | |||
113 | /*[Clocksource internal variables]--------- | 109 | /*[Clocksource internal variables]--------- |
114 | * curr_clocksource: | 110 | * curr_clocksource: |
115 | * currently selected clocksource. Initialized to clocksource_jiffies. | 111 | * currently selected clocksource. |
116 | * next_clocksource: | 112 | * next_clocksource: |
117 | * pending next selected clocksource. | 113 | * pending next selected clocksource. |
118 | * clocksource_list: | 114 | * clocksource_list: |
@@ -123,9 +119,8 @@ extern struct clocksource clocksource_jiffies; | |||
123 | * override_name: | 119 | * override_name: |
124 | * Name of the user-specified clocksource. | 120 | * Name of the user-specified clocksource. |
125 | */ | 121 | */ |
126 | static struct clocksource *curr_clocksource = &clocksource_jiffies; | 122 | static struct clocksource *curr_clocksource; |
127 | static struct clocksource *next_clocksource; | 123 | static struct clocksource *next_clocksource; |
128 | static struct clocksource *clocksource_override; | ||
129 | static LIST_HEAD(clocksource_list); | 124 | static LIST_HEAD(clocksource_list); |
130 | static DEFINE_SPINLOCK(clocksource_lock); | 125 | static DEFINE_SPINLOCK(clocksource_lock); |
131 | static char override_name[32]; | 126 | static char override_name[32]; |
@@ -320,6 +315,7 @@ void clocksource_touch_watchdog(void) | |||
320 | clocksource_resume_watchdog(); | 315 | clocksource_resume_watchdog(); |
321 | } | 316 | } |
322 | 317 | ||
318 | #ifdef CONFIG_GENERIC_TIME | ||
323 | /** | 319 | /** |
324 | * clocksource_get_next - Returns the selected clocksource | 320 | * clocksource_get_next - Returns the selected clocksource |
325 | * | 321 | * |
@@ -339,56 +335,65 @@ struct clocksource *clocksource_get_next(void) | |||
339 | } | 335 | } |
340 | 336 | ||
341 | /** | 337 | /** |
342 | * select_clocksource - Selects the best registered clocksource. | 338 | * clocksource_select - Select the best clocksource available |
343 | * | 339 | * |
344 | * Private function. Must hold clocksource_lock when called. | 340 | * Private function. Must hold clocksource_lock when called. |
345 | * | 341 | * |
346 | * Select the clocksource with the best rating, or the clocksource, | 342 | * Select the clocksource with the best rating, or the clocksource, |
347 | * which is selected by userspace override. | 343 | * which is selected by userspace override. |
348 | */ | 344 | */ |
349 | static struct clocksource *select_clocksource(void) | 345 | static void clocksource_select(void) |
350 | { | 346 | { |
351 | struct clocksource *next; | 347 | struct clocksource *best, *cs; |
352 | 348 | ||
353 | if (list_empty(&clocksource_list)) | 349 | if (list_empty(&clocksource_list)) |
354 | return NULL; | 350 | return; |
351 | /* First clocksource on the list has the best rating. */ | ||
352 | best = list_first_entry(&clocksource_list, struct clocksource, list); | ||
353 | /* Check for the override clocksource. */ | ||
354 | list_for_each_entry(cs, &clocksource_list, list) { | ||
355 | if (strcmp(cs->name, override_name) != 0) | ||
356 | continue; | ||
357 | /* | ||
358 | * Check to make sure we don't switch to a non-highres | ||
359 | * capable clocksource if the tick code is in oneshot | ||
360 | * mode (highres or nohz) | ||
361 | */ | ||
362 | if (!(cs->flags & CLOCK_SOURCE_VALID_FOR_HRES) && | ||
363 | tick_oneshot_mode_active()) { | ||
364 | /* Override clocksource cannot be used. */ | ||
365 | printk(KERN_WARNING "Override clocksource %s is not " | ||
366 | "HRT compatible. Cannot switch while in " | ||
367 | "HRT/NOHZ mode\n", cs->name); | ||
368 | override_name[0] = 0; | ||
369 | } else | ||
370 | /* Override clocksource can be used. */ | ||
371 | best = cs; | ||
372 | break; | ||
373 | } | ||
374 | if (curr_clocksource != best) | ||
375 | next_clocksource = best; | ||
376 | } | ||
355 | 377 | ||
356 | if (clocksource_override) | 378 | #else /* CONFIG_GENERIC_TIME */ |
357 | next = clocksource_override; | ||
358 | else | ||
359 | next = list_entry(clocksource_list.next, struct clocksource, | ||
360 | list); | ||
361 | 379 | ||
362 | if (next == curr_clocksource) | 380 | static void clocksource_select(void) { } |
363 | return NULL; | ||
364 | 381 | ||
365 | return next; | 382 | #endif |
366 | } | ||
367 | 383 | ||
368 | /* | 384 | /* |
369 | * Enqueue the clocksource sorted by rating | 385 | * Enqueue the clocksource sorted by rating |
370 | */ | 386 | */ |
371 | static int clocksource_enqueue(struct clocksource *c) | 387 | static void clocksource_enqueue(struct clocksource *cs) |
372 | { | 388 | { |
373 | struct list_head *tmp, *entry = &clocksource_list; | 389 | struct list_head *entry = &clocksource_list; |
374 | 390 | struct clocksource *tmp; | |
375 | list_for_each(tmp, &clocksource_list) { | ||
376 | struct clocksource *cs; | ||
377 | 391 | ||
378 | cs = list_entry(tmp, struct clocksource, list); | 392 | list_for_each_entry(tmp, &clocksource_list, list) |
379 | if (cs == c) | ||
380 | return -EBUSY; | ||
381 | /* Keep track of the place, where to insert */ | 393 | /* Keep track of the place, where to insert */ |
382 | if (cs->rating >= c->rating) | 394 | if (tmp->rating >= cs->rating) |
383 | entry = tmp; | 395 | entry = &tmp->list; |
384 | } | 396 | list_add(&cs->list, entry); |
385 | list_add(&c->list, entry); | ||
386 | |||
387 | if (strlen(c->name) == strlen(override_name) && | ||
388 | !strcmp(c->name, override_name)) | ||
389 | clocksource_override = c; | ||
390 | |||
391 | return 0; | ||
392 | } | 397 | } |
393 | 398 | ||
394 | /** | 399 | /** |
@@ -397,19 +402,16 @@ static int clocksource_enqueue(struct clocksource *c) | |||
397 | * | 402 | * |
398 | * Returns -EBUSY if registration fails, zero otherwise. | 403 | * Returns -EBUSY if registration fails, zero otherwise. |
399 | */ | 404 | */ |
400 | int clocksource_register(struct clocksource *c) | 405 | int clocksource_register(struct clocksource *cs) |
401 | { | 406 | { |
402 | unsigned long flags; | 407 | unsigned long flags; |
403 | int ret; | ||
404 | 408 | ||
405 | spin_lock_irqsave(&clocksource_lock, flags); | 409 | spin_lock_irqsave(&clocksource_lock, flags); |
406 | ret = clocksource_enqueue(c); | 410 | clocksource_enqueue(cs); |
407 | if (!ret) | 411 | clocksource_select(); |
408 | next_clocksource = select_clocksource(); | ||
409 | spin_unlock_irqrestore(&clocksource_lock, flags); | 412 | spin_unlock_irqrestore(&clocksource_lock, flags); |
410 | if (!ret) | 413 | clocksource_check_watchdog(cs); |
411 | clocksource_check_watchdog(c); | 414 | return 0; |
412 | return ret; | ||
413 | } | 415 | } |
414 | EXPORT_SYMBOL(clocksource_register); | 416 | EXPORT_SYMBOL(clocksource_register); |
415 | 417 | ||
@@ -425,7 +427,7 @@ void clocksource_change_rating(struct clocksource *cs, int rating) | |||
425 | list_del(&cs->list); | 427 | list_del(&cs->list); |
426 | cs->rating = rating; | 428 | cs->rating = rating; |
427 | clocksource_enqueue(cs); | 429 | clocksource_enqueue(cs); |
428 | next_clocksource = select_clocksource(); | 430 | clocksource_select(); |
429 | spin_unlock_irqrestore(&clocksource_lock, flags); | 431 | spin_unlock_irqrestore(&clocksource_lock, flags); |
430 | } | 432 | } |
431 | 433 | ||
@@ -438,9 +440,7 @@ void clocksource_unregister(struct clocksource *cs) | |||
438 | 440 | ||
439 | spin_lock_irqsave(&clocksource_lock, flags); | 441 | spin_lock_irqsave(&clocksource_lock, flags); |
440 | list_del(&cs->list); | 442 | list_del(&cs->list); |
441 | if (clocksource_override == cs) | 443 | clocksource_select(); |
442 | clocksource_override = NULL; | ||
443 | next_clocksource = select_clocksource(); | ||
444 | spin_unlock_irqrestore(&clocksource_lock, flags); | 444 | spin_unlock_irqrestore(&clocksource_lock, flags); |
445 | } | 445 | } |
446 | 446 | ||
@@ -478,9 +478,7 @@ static ssize_t sysfs_override_clocksource(struct sys_device *dev, | |||
478 | struct sysdev_attribute *attr, | 478 | struct sysdev_attribute *attr, |
479 | const char *buf, size_t count) | 479 | const char *buf, size_t count) |
480 | { | 480 | { |
481 | struct clocksource *ovr = NULL; | ||
482 | size_t ret = count; | 481 | size_t ret = count; |
483 | int len; | ||
484 | 482 | ||
485 | /* strings from sysfs write are not 0 terminated! */ | 483 | /* strings from sysfs write are not 0 terminated! */ |
486 | if (count >= sizeof(override_name)) | 484 | if (count >= sizeof(override_name)) |
@@ -495,37 +493,7 @@ static ssize_t sysfs_override_clocksource(struct sys_device *dev, | |||
495 | if (count > 0) | 493 | if (count > 0) |
496 | memcpy(override_name, buf, count); | 494 | memcpy(override_name, buf, count); |
497 | override_name[count] = 0; | 495 | override_name[count] = 0; |
498 | 496 | clocksource_select(); | |
499 | len = strlen(override_name); | ||
500 | if (len) { | ||
501 | struct clocksource *cs; | ||
502 | |||
503 | ovr = clocksource_override; | ||
504 | /* try to select it: */ | ||
505 | list_for_each_entry(cs, &clocksource_list, list) { | ||
506 | if (strlen(cs->name) == len && | ||
507 | !strcmp(cs->name, override_name)) | ||
508 | ovr = cs; | ||
509 | } | ||
510 | } | ||
511 | |||
512 | /* | ||
513 | * Check to make sure we don't switch to a non-highres capable | ||
514 | * clocksource if the tick code is in oneshot mode (highres or nohz) | ||
515 | */ | ||
516 | if (tick_oneshot_mode_active() && ovr && | ||
517 | !(ovr->flags & CLOCK_SOURCE_VALID_FOR_HRES)) { | ||
518 | printk(KERN_WARNING "%s clocksource is not HRT compatible. " | ||
519 | "Cannot switch while in HRT/NOHZ mode\n", ovr->name); | ||
520 | ovr = NULL; | ||
521 | override_name[0] = 0; | ||
522 | } | ||
523 | |||
524 | /* Reselect, when the override name has changed */ | ||
525 | if (ovr != clocksource_override) { | ||
526 | clocksource_override = ovr; | ||
527 | next_clocksource = select_clocksource(); | ||
528 | } | ||
529 | 497 | ||
530 | spin_unlock_irq(&clocksource_lock); | 498 | spin_unlock_irq(&clocksource_lock); |
531 | 499 | ||
diff --git a/kernel/time/jiffies.c b/kernel/time/jiffies.c index c3f6c30816e3..5404a8456909 100644 --- a/kernel/time/jiffies.c +++ b/kernel/time/jiffies.c | |||
@@ -61,7 +61,6 @@ struct clocksource clocksource_jiffies = { | |||
61 | .read = jiffies_read, | 61 | .read = jiffies_read, |
62 | .mask = 0xffffffff, /*32bits*/ | 62 | .mask = 0xffffffff, /*32bits*/ |
63 | .mult = NSEC_PER_JIFFY << JIFFIES_SHIFT, /* details above */ | 63 | .mult = NSEC_PER_JIFFY << JIFFIES_SHIFT, /* details above */ |
64 | .mult_orig = NSEC_PER_JIFFY << JIFFIES_SHIFT, | ||
65 | .shift = JIFFIES_SHIFT, | 64 | .shift = JIFFIES_SHIFT, |
66 | }; | 65 | }; |
67 | 66 | ||
@@ -71,3 +70,8 @@ static int __init init_jiffies_clocksource(void) | |||
71 | } | 70 | } |
72 | 71 | ||
73 | core_initcall(init_jiffies_clocksource); | 72 | core_initcall(init_jiffies_clocksource); |
73 | |||
74 | struct clocksource * __init __weak clocksource_default_clock(void) | ||
75 | { | ||
76 | return &clocksource_jiffies; | ||
77 | } | ||
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index b5673016089f..325a9b63265a 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c | |||
@@ -269,7 +269,7 @@ static void change_clocksource(void) | |||
269 | 269 | ||
270 | new = clocksource_get_next(); | 270 | new = clocksource_get_next(); |
271 | 271 | ||
272 | if (clock == new) | 272 | if (!new || clock == new) |
273 | return; | 273 | return; |
274 | 274 | ||
275 | clocksource_forward_now(); | 275 | clocksource_forward_now(); |
@@ -446,7 +446,7 @@ void __init timekeeping_init(void) | |||
446 | 446 | ||
447 | ntp_init(); | 447 | ntp_init(); |
448 | 448 | ||
449 | clock = clocksource_get_next(); | 449 | clock = clocksource_default_clock(); |
450 | if (clock->enable) | 450 | if (clock->enable) |
451 | clock->enable(clock); | 451 | clock->enable(clock); |
452 | /* set mult_orig on enable */ | 452 | /* set mult_orig on enable */ |