clocksource: Cleanup clocksource selection
Martin Schwidefsky [Fri, 14 Aug 2009 13:47:21 +0000 (15:47 +0200)]
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>

arch/s390/kernel/time.c
include/linux/clocksource.h
kernel/time/clocksource.c
kernel/time/jiffies.c
kernel/time/timekeeping.c

index d4c8e9c..afefe51 100644 (file)
@@ -205,6 +205,10 @@ static struct clocksource clocksource_tod = {
        .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
 };
 
+struct clocksource * __init clocksource_default_clock(void)
+{
+       return &clocksource_tod;
+}
 
 void update_vsyscall(struct timespec *wall_time, struct clocksource *clock)
 {
index a1ef46f..f263b3a 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/list.h>
 #include <linux/cache.h>
 #include <linux/timer.h>
+#include <linux/init.h>
 #include <asm/div64.h>
 #include <asm/io.h>
 
@@ -322,6 +323,7 @@ extern void clocksource_touch_watchdog(void);
 extern struct clocksource* clocksource_get_next(void);
 extern void clocksource_change_rating(struct clocksource *cs, int rating);
 extern void clocksource_resume(void);
+extern struct clocksource * __init __weak clocksource_default_clock(void);
 
 #ifdef CONFIG_GENERIC_TIME_VSYSCALL
 extern void update_vsyscall(struct timespec *ts, struct clocksource *c);
index 7466cb8..e91662e 100644 (file)
@@ -21,7 +21,6 @@
  *
  * TODO WishList:
  *   o Allow clocksource drivers to be unregistered
- *   o get rid of clocksource_jiffies extern
  */
 
 #include <linux/clocksource.h>
@@ -107,12 +106,9 @@ u64 timecounter_cyc2time(struct timecounter *tc,
 }
 EXPORT_SYMBOL(timecounter_cyc2time);
 
-/* XXX - Would like a better way for initializing curr_clocksource */
-extern struct clocksource clocksource_jiffies;
-
 /*[Clocksource internal variables]---------
  * curr_clocksource:
- *     currently selected clocksource. Initialized to clocksource_jiffies.
+ *     currently selected clocksource.
  * next_clocksource:
  *     pending next selected clocksource.
  * clocksource_list:
@@ -123,9 +119,8 @@ extern struct clocksource clocksource_jiffies;
  * override_name:
  *     Name of the user-specified clocksource.
  */
-static struct clocksource *curr_clocksource = &clocksource_jiffies;
+static struct clocksource *curr_clocksource;
 static struct clocksource *next_clocksource;
-static struct clocksource *clocksource_override;
 static LIST_HEAD(clocksource_list);
 static DEFINE_SPINLOCK(clocksource_lock);
 static char override_name[32];
@@ -320,6 +315,7 @@ void clocksource_touch_watchdog(void)
        clocksource_resume_watchdog();
 }
 
+#ifdef CONFIG_GENERIC_TIME
 /**
  * clocksource_get_next - Returns the selected clocksource
  *
@@ -339,56 +335,65 @@ struct clocksource *clocksource_get_next(void)
 }
 
 /**
- * select_clocksource - Selects the best registered clocksource.
+ * clocksource_select - Select the best clocksource available
  *
  * Private function. Must hold clocksource_lock when called.
  *
  * Select the clocksource with the best rating, or the clocksource,
  * which is selected by userspace override.
  */
-static struct clocksource *select_clocksource(void)
+static void clocksource_select(void)
 {
-       struct clocksource *next;
+       struct clocksource *best, *cs;
 
        if (list_empty(&clocksource_list))
-               return NULL;
+               return;
+       /* First clocksource on the list has the best rating. */
+       best = list_first_entry(&clocksource_list, struct clocksource, list);
+       /* Check for the override clocksource. */
+       list_for_each_entry(cs, &clocksource_list, list) {
+               if (strcmp(cs->name, override_name) != 0)
+                       continue;
+               /*
+                * Check to make sure we don't switch to a non-highres
+                * capable clocksource if the tick code is in oneshot
+                * mode (highres or nohz)
+                */
+               if (!(cs->flags & CLOCK_SOURCE_VALID_FOR_HRES) &&
+                   tick_oneshot_mode_active()) {
+                       /* Override clocksource cannot be used. */
+                       printk(KERN_WARNING "Override clocksource %s is not "
+                              "HRT compatible. Cannot switch while in "
+                              "HRT/NOHZ mode\n", cs->name);
+                       override_name[0] = 0;
+               } else
+                       /* Override clocksource can be used. */
+                       best = cs;
+               break;
+       }
+       if (curr_clocksource != best)
+               next_clocksource = best;
+}
 
-       if (clocksource_override)
-               next = clocksource_override;
-       else
-               next = list_entry(clocksource_list.next, struct clocksource,
-                                 list);
+#else /* CONFIG_GENERIC_TIME */
 
-       if (next == curr_clocksource)
-               return NULL;
+static void clocksource_select(void) { }
 
-       return next;
-}
+#endif
 
 /*
  * Enqueue the clocksource sorted by rating
  */
-static int clocksource_enqueue(struct clocksource *c)
+static void clocksource_enqueue(struct clocksource *cs)
 {
-       struct list_head *tmp, *entry = &clocksource_list;
-
-       list_for_each(tmp, &clocksource_list) {
-               struct clocksource *cs;
+       struct list_head *entry = &clocksource_list;
+       struct clocksource *tmp;
 
-               cs = list_entry(tmp, struct clocksource, list);
-               if (cs == c)
-                       return -EBUSY;
+       list_for_each_entry(tmp, &clocksource_list, list)
                /* Keep track of the place, where to insert */
-               if (cs->rating >= c->rating)
-                       entry = tmp;
-       }
-       list_add(&c->list, entry);
-
-       if (strlen(c->name) == strlen(override_name) &&
-           !strcmp(c->name, override_name))
-               clocksource_override = c;
-
-       return 0;
+               if (tmp->rating >= cs->rating)
+                       entry = &tmp->list;
+       list_add(&cs->list, entry);
 }
 
 /**
@@ -397,19 +402,16 @@ static int clocksource_enqueue(struct clocksource *c)
  *
  * Returns -EBUSY if registration fails, zero otherwise.
  */
-int clocksource_register(struct clocksource *c)
+int clocksource_register(struct clocksource *cs)
 {
        unsigned long flags;
-       int ret;
 
        spin_lock_irqsave(&clocksource_lock, flags);
-       ret = clocksource_enqueue(c);
-       if (!ret)
-               next_clocksource = select_clocksource();
+       clocksource_enqueue(cs);
+       clocksource_select();
        spin_unlock_irqrestore(&clocksource_lock, flags);
-       if (!ret)
-               clocksource_check_watchdog(c);
-       return ret;
+       clocksource_check_watchdog(cs);
+       return 0;
 }
 EXPORT_SYMBOL(clocksource_register);
 
@@ -425,7 +427,7 @@ void clocksource_change_rating(struct clocksource *cs, int rating)
        list_del(&cs->list);
        cs->rating = rating;
        clocksource_enqueue(cs);
-       next_clocksource = select_clocksource();
+       clocksource_select();
        spin_unlock_irqrestore(&clocksource_lock, flags);
 }
 
@@ -438,9 +440,7 @@ void clocksource_unregister(struct clocksource *cs)
 
        spin_lock_irqsave(&clocksource_lock, flags);
        list_del(&cs->list);
-       if (clocksource_override == cs)
-               clocksource_override = NULL;
-       next_clocksource = select_clocksource();
+       clocksource_select();
        spin_unlock_irqrestore(&clocksource_lock, flags);
 }
 
@@ -478,9 +478,7 @@ static ssize_t sysfs_override_clocksource(struct sys_device *dev,
                                          struct sysdev_attribute *attr,
                                          const char *buf, size_t count)
 {
-       struct clocksource *ovr = NULL;
        size_t ret = count;
-       int len;
 
        /* strings from sysfs write are not 0 terminated! */
        if (count >= sizeof(override_name))
@@ -495,37 +493,7 @@ static ssize_t sysfs_override_clocksource(struct sys_device *dev,
        if (count > 0)
                memcpy(override_name, buf, count);
        override_name[count] = 0;
-
-       len = strlen(override_name);
-       if (len) {
-               struct clocksource *cs;
-
-               ovr = clocksource_override;
-               /* try to select it: */
-               list_for_each_entry(cs, &clocksource_list, list) {
-                       if (strlen(cs->name) == len &&
-                           !strcmp(cs->name, override_name))
-                               ovr = cs;
-               }
-       }
-
-       /*
-        * Check to make sure we don't switch to a non-highres capable
-        * clocksource if the tick code is in oneshot mode (highres or nohz)
-        */
-       if (tick_oneshot_mode_active() && ovr &&
-           !(ovr->flags & CLOCK_SOURCE_VALID_FOR_HRES)) {
-               printk(KERN_WARNING "%s clocksource is not HRT compatible. "
-                       "Cannot switch while in HRT/NOHZ mode\n", ovr->name);
-               ovr = NULL;
-               override_name[0] = 0;
-       }
-
-       /* Reselect, when the override name has changed */
-       if (ovr != clocksource_override) {
-               clocksource_override = ovr;
-               next_clocksource = select_clocksource();
-       }
+       clocksource_select();
 
        spin_unlock_irq(&clocksource_lock);
 
index c3f6c30..5404a84 100644 (file)
@@ -61,7 +61,6 @@ struct clocksource clocksource_jiffies = {
        .read           = jiffies_read,
        .mask           = 0xffffffff, /*32bits*/
        .mult           = NSEC_PER_JIFFY << JIFFIES_SHIFT, /* details above */
-       .mult_orig      = NSEC_PER_JIFFY << JIFFIES_SHIFT,
        .shift          = JIFFIES_SHIFT,
 };
 
@@ -71,3 +70,8 @@ static int __init init_jiffies_clocksource(void)
 }
 
 core_initcall(init_jiffies_clocksource);
+
+struct clocksource * __init __weak clocksource_default_clock(void)
+{
+       return &clocksource_jiffies;
+}
index b567301..325a9b6 100644 (file)
@@ -269,7 +269,7 @@ static void change_clocksource(void)
 
        new = clocksource_get_next();
 
-       if (clock == new)
+       if (!new || clock == new)
                return;
 
        clocksource_forward_now();
@@ -446,7 +446,7 @@ void __init timekeeping_init(void)
 
        ntp_init();
 
-       clock = clocksource_get_next();
+       clock = clocksource_default_clock();
        if (clock->enable)
                clock->enable(clock);
        /* set mult_orig on enable */