aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorMartin Schwidefsky <schwidefsky@de.ibm.com>2009-08-14 09:47:21 -0400
committerThomas Gleixner <tglx@linutronix.de>2009-08-15 04:55:46 -0400
commitf1b82746c1e93daf24e1ab9bfbd39bcdb2e7018b (patch)
tree1403e3662dd3b32cb8b17218bfdd9a640061f654 /kernel
parent1be396794897f80bfc8774719ba60309a9e3d374 (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>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/time/clocksource.c134
-rw-r--r--kernel/time/jiffies.c6
-rw-r--r--kernel/time/timekeeping.c4
3 files changed, 58 insertions, 86 deletions
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}
108EXPORT_SYMBOL(timecounter_cyc2time); 107EXPORT_SYMBOL(timecounter_cyc2time);
109 108
110/* XXX - Would like a better way for initializing curr_clocksource */
111extern 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 */
126static struct clocksource *curr_clocksource = &clocksource_jiffies; 122static struct clocksource *curr_clocksource;
127static struct clocksource *next_clocksource; 123static struct clocksource *next_clocksource;
128static struct clocksource *clocksource_override;
129static LIST_HEAD(clocksource_list); 124static LIST_HEAD(clocksource_list);
130static DEFINE_SPINLOCK(clocksource_lock); 125static DEFINE_SPINLOCK(clocksource_lock);
131static char override_name[32]; 126static 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 */
349static struct clocksource *select_clocksource(void) 345static 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) 380static 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 */
371static int clocksource_enqueue(struct clocksource *c) 387static 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 */
400int clocksource_register(struct clocksource *c) 405int 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}
414EXPORT_SYMBOL(clocksource_register); 416EXPORT_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
73core_initcall(init_jiffies_clocksource); 72core_initcall(init_jiffies_clocksource);
73
74struct 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 */