diff options
| author | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2009-08-14 15:47:21 +0200 | 
|---|---|---|
| committer | Thomas Gleixner <tglx@linutronix.de> | 2009-08-15 10:55:46 +0200 | 
| 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 d4c8e9c47c8..afefe514df0 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c @@ -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)  { diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index a1ef46f61c8..f263b3abf46 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -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); diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index 7466cb81125..e91662e87cd 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c @@ -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); diff --git a/kernel/time/jiffies.c b/kernel/time/jiffies.c index c3f6c30816e..5404a845690 100644 --- a/kernel/time/jiffies.c +++ b/kernel/time/jiffies.c @@ -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; +} diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index b5673016089..325a9b63265 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -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 */  | 
