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>
This commit is contained in:
		
					parent
					
						
							
								1be3967948
							
						
					
				
			
			
				commit
				
					
						f1b82746c1
					
				
			
		
					 5 changed files with 66 additions and 88 deletions
				
			
		|  | @ -205,6 +205,10 @@ static struct clocksource clocksource_tod = { | ||||||
| 	.flags		= CLOCK_SOURCE_IS_CONTINUOUS, | 	.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) | void update_vsyscall(struct timespec *wall_time, struct clocksource *clock) | ||||||
| { | { | ||||||
|  |  | ||||||
|  | @ -14,6 +14,7 @@ | ||||||
| #include <linux/list.h> | #include <linux/list.h> | ||||||
| #include <linux/cache.h> | #include <linux/cache.h> | ||||||
| #include <linux/timer.h> | #include <linux/timer.h> | ||||||
|  | #include <linux/init.h> | ||||||
| #include <asm/div64.h> | #include <asm/div64.h> | ||||||
| #include <asm/io.h> | #include <asm/io.h> | ||||||
| 
 | 
 | ||||||
|  | @ -322,6 +323,7 @@ extern void clocksource_touch_watchdog(void); | ||||||
| extern struct clocksource* clocksource_get_next(void); | extern struct clocksource* clocksource_get_next(void); | ||||||
| extern void clocksource_change_rating(struct clocksource *cs, int rating); | extern void clocksource_change_rating(struct clocksource *cs, int rating); | ||||||
| extern void clocksource_resume(void); | extern void clocksource_resume(void); | ||||||
|  | extern struct clocksource * __init __weak clocksource_default_clock(void); | ||||||
| 
 | 
 | ||||||
| #ifdef CONFIG_GENERIC_TIME_VSYSCALL | #ifdef CONFIG_GENERIC_TIME_VSYSCALL | ||||||
| extern void update_vsyscall(struct timespec *ts, struct clocksource *c); | extern void update_vsyscall(struct timespec *ts, struct clocksource *c); | ||||||
|  |  | ||||||
|  | @ -21,7 +21,6 @@ | ||||||
|  * |  * | ||||||
|  * TODO WishList: |  * TODO WishList: | ||||||
|  *   o Allow clocksource drivers to be unregistered |  *   o Allow clocksource drivers to be unregistered | ||||||
|  *   o get rid of clocksource_jiffies extern |  | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| #include <linux/clocksource.h> | #include <linux/clocksource.h> | ||||||
|  | @ -107,12 +106,9 @@ u64 timecounter_cyc2time(struct timecounter *tc, | ||||||
| } | } | ||||||
| EXPORT_SYMBOL(timecounter_cyc2time); | EXPORT_SYMBOL(timecounter_cyc2time); | ||||||
| 
 | 
 | ||||||
| /* XXX - Would like a better way for initializing curr_clocksource */ |  | ||||||
| extern struct clocksource clocksource_jiffies; |  | ||||||
| 
 |  | ||||||
| /*[Clocksource internal variables]---------
 | /*[Clocksource internal variables]---------
 | ||||||
|  * curr_clocksource: |  * curr_clocksource: | ||||||
|  *	currently selected clocksource. Initialized to clocksource_jiffies. |  *	currently selected clocksource. | ||||||
|  * next_clocksource: |  * next_clocksource: | ||||||
|  *	pending next selected clocksource. |  *	pending next selected clocksource. | ||||||
|  * clocksource_list: |  * clocksource_list: | ||||||
|  | @ -123,9 +119,8 @@ extern struct clocksource clocksource_jiffies; | ||||||
|  * override_name: |  * override_name: | ||||||
|  *	Name of the user-specified clocksource. |  *	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 *next_clocksource; | ||||||
| static struct clocksource *clocksource_override; |  | ||||||
| static LIST_HEAD(clocksource_list); | static LIST_HEAD(clocksource_list); | ||||||
| static DEFINE_SPINLOCK(clocksource_lock); | static DEFINE_SPINLOCK(clocksource_lock); | ||||||
| static char override_name[32]; | static char override_name[32]; | ||||||
|  | @ -320,6 +315,7 @@ void clocksource_touch_watchdog(void) | ||||||
| 	clocksource_resume_watchdog(); | 	clocksource_resume_watchdog(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #ifdef CONFIG_GENERIC_TIME | ||||||
| /**
 | /**
 | ||||||
|  * clocksource_get_next - Returns the selected clocksource |  * 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. |  * Private function. Must hold clocksource_lock when called. | ||||||
|  * |  * | ||||||
|  * Select the clocksource with the best rating, or the clocksource, |  * Select the clocksource with the best rating, or the clocksource, | ||||||
|  * which is selected by userspace override. |  * 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)) | 	if (list_empty(&clocksource_list)) | ||||||
| 		return NULL; | 		return; | ||||||
| 
 | 	/* First clocksource on the list has the best rating. */ | ||||||
| 	if (clocksource_override) | 	best = list_first_entry(&clocksource_list, struct clocksource, list); | ||||||
| 		next = clocksource_override; | 	/* Check for the override clocksource. */ | ||||||
| 	else | 	list_for_each_entry(cs, &clocksource_list, list) { | ||||||
| 		next = list_entry(clocksource_list.next, struct clocksource, | 		if (strcmp(cs->name, override_name) != 0) | ||||||
| 				  list); | 			continue; | ||||||
| 
 | 		/*
 | ||||||
| 	if (next == curr_clocksource) | 		 * Check to make sure we don't switch to a non-highres | ||||||
| 		return NULL; | 		 * capable clocksource if the tick code is in oneshot | ||||||
| 
 | 		 * mode (highres or nohz) | ||||||
| 	return next; | 		 */ | ||||||
|  | 		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; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #else /* CONFIG_GENERIC_TIME */ | ||||||
|  | 
 | ||||||
|  | static void clocksource_select(void) { } | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| /*
 | /*
 | ||||||
|  * Enqueue the clocksource sorted by rating |  * 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; | 	struct list_head *entry = &clocksource_list; | ||||||
|  | 	struct clocksource *tmp; | ||||||
| 
 | 
 | ||||||
| 	list_for_each(tmp, &clocksource_list) { | 	list_for_each_entry(tmp, &clocksource_list, list) | ||||||
| 		struct clocksource *cs; |  | ||||||
| 
 |  | ||||||
| 		cs = list_entry(tmp, struct clocksource, list); |  | ||||||
| 		if (cs == c) |  | ||||||
| 			return -EBUSY; |  | ||||||
| 		/* Keep track of the place, where to insert */ | 		/* Keep track of the place, where to insert */ | ||||||
| 		if (cs->rating >= c->rating) | 		if (tmp->rating >= cs->rating) | ||||||
| 			entry = tmp; | 			entry = &tmp->list; | ||||||
| 	} | 	list_add(&cs->list, entry); | ||||||
| 	list_add(&c->list, entry); |  | ||||||
| 
 |  | ||||||
| 	if (strlen(c->name) == strlen(override_name) && |  | ||||||
| 	    !strcmp(c->name, override_name)) |  | ||||||
| 		clocksource_override = c; |  | ||||||
| 
 |  | ||||||
| 	return 0; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  | @ -397,19 +402,16 @@ static int clocksource_enqueue(struct clocksource *c) | ||||||
|  * |  * | ||||||
|  * Returns -EBUSY if registration fails, zero otherwise. |  * Returns -EBUSY if registration fails, zero otherwise. | ||||||
|  */ |  */ | ||||||
| int clocksource_register(struct clocksource *c) | int clocksource_register(struct clocksource *cs) | ||||||
| { | { | ||||||
| 	unsigned long flags; | 	unsigned long flags; | ||||||
| 	int ret; |  | ||||||
| 
 | 
 | ||||||
| 	spin_lock_irqsave(&clocksource_lock, flags); | 	spin_lock_irqsave(&clocksource_lock, flags); | ||||||
| 	ret = clocksource_enqueue(c); | 	clocksource_enqueue(cs); | ||||||
| 	if (!ret) | 	clocksource_select(); | ||||||
| 		next_clocksource = select_clocksource(); |  | ||||||
| 	spin_unlock_irqrestore(&clocksource_lock, flags); | 	spin_unlock_irqrestore(&clocksource_lock, flags); | ||||||
| 	if (!ret) | 	clocksource_check_watchdog(cs); | ||||||
| 		clocksource_check_watchdog(c); | 	return 0; | ||||||
| 	return ret; |  | ||||||
| } | } | ||||||
| EXPORT_SYMBOL(clocksource_register); | EXPORT_SYMBOL(clocksource_register); | ||||||
| 
 | 
 | ||||||
|  | @ -425,7 +427,7 @@ void clocksource_change_rating(struct clocksource *cs, int rating) | ||||||
| 	list_del(&cs->list); | 	list_del(&cs->list); | ||||||
| 	cs->rating = rating; | 	cs->rating = rating; | ||||||
| 	clocksource_enqueue(cs); | 	clocksource_enqueue(cs); | ||||||
| 	next_clocksource = select_clocksource(); | 	clocksource_select(); | ||||||
| 	spin_unlock_irqrestore(&clocksource_lock, flags); | 	spin_unlock_irqrestore(&clocksource_lock, flags); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -438,9 +440,7 @@ void clocksource_unregister(struct clocksource *cs) | ||||||
| 
 | 
 | ||||||
| 	spin_lock_irqsave(&clocksource_lock, flags); | 	spin_lock_irqsave(&clocksource_lock, flags); | ||||||
| 	list_del(&cs->list); | 	list_del(&cs->list); | ||||||
| 	if (clocksource_override == cs) | 	clocksource_select(); | ||||||
| 		clocksource_override = NULL; |  | ||||||
| 	next_clocksource = select_clocksource(); |  | ||||||
| 	spin_unlock_irqrestore(&clocksource_lock, flags); | 	spin_unlock_irqrestore(&clocksource_lock, flags); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -478,9 +478,7 @@ static ssize_t sysfs_override_clocksource(struct sys_device *dev, | ||||||
| 					  struct sysdev_attribute *attr, | 					  struct sysdev_attribute *attr, | ||||||
| 					  const char *buf, size_t count) | 					  const char *buf, size_t count) | ||||||
| { | { | ||||||
| 	struct clocksource *ovr = NULL; |  | ||||||
| 	size_t ret = count; | 	size_t ret = count; | ||||||
| 	int len; |  | ||||||
| 
 | 
 | ||||||
| 	/* strings from sysfs write are not 0 terminated! */ | 	/* strings from sysfs write are not 0 terminated! */ | ||||||
| 	if (count >= sizeof(override_name)) | 	if (count >= sizeof(override_name)) | ||||||
|  | @ -495,37 +493,7 @@ static ssize_t sysfs_override_clocksource(struct sys_device *dev, | ||||||
| 	if (count > 0) | 	if (count > 0) | ||||||
| 		memcpy(override_name, buf, count); | 		memcpy(override_name, buf, count); | ||||||
| 	override_name[count] = 0; | 	override_name[count] = 0; | ||||||
| 
 | 	clocksource_select(); | ||||||
| 	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(); |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	spin_unlock_irq(&clocksource_lock); | 	spin_unlock_irq(&clocksource_lock); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -61,7 +61,6 @@ struct clocksource clocksource_jiffies = { | ||||||
| 	.read		= jiffies_read, | 	.read		= jiffies_read, | ||||||
| 	.mask		= 0xffffffff, /*32bits*/ | 	.mask		= 0xffffffff, /*32bits*/ | ||||||
| 	.mult		= NSEC_PER_JIFFY << JIFFIES_SHIFT, /* details above */ | 	.mult		= NSEC_PER_JIFFY << JIFFIES_SHIFT, /* details above */ | ||||||
| 	.mult_orig	= NSEC_PER_JIFFY << JIFFIES_SHIFT, |  | ||||||
| 	.shift		= JIFFIES_SHIFT, | 	.shift		= JIFFIES_SHIFT, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -71,3 +70,8 @@ static int __init init_jiffies_clocksource(void) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| core_initcall(init_jiffies_clocksource); | core_initcall(init_jiffies_clocksource); | ||||||
|  | 
 | ||||||
|  | struct clocksource * __init __weak clocksource_default_clock(void) | ||||||
|  | { | ||||||
|  | 	return &clocksource_jiffies; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -269,7 +269,7 @@ static void change_clocksource(void) | ||||||
| 
 | 
 | ||||||
| 	new = clocksource_get_next(); | 	new = clocksource_get_next(); | ||||||
| 
 | 
 | ||||||
| 	if (clock == new) | 	if (!new || clock == new) | ||||||
| 		return; | 		return; | ||||||
| 
 | 
 | ||||||
| 	clocksource_forward_now(); | 	clocksource_forward_now(); | ||||||
|  | @ -446,7 +446,7 @@ void __init timekeeping_init(void) | ||||||
| 
 | 
 | ||||||
| 	ntp_init(); | 	ntp_init(); | ||||||
| 
 | 
 | ||||||
| 	clock = clocksource_get_next(); | 	clock = clocksource_default_clock(); | ||||||
| 	if (clock->enable) | 	if (clock->enable) | ||||||
| 		clock->enable(clock); | 		clock->enable(clock); | ||||||
| 	/* set mult_orig on enable */ | 	/* set mult_orig on enable */ | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Martin Schwidefsky
				Martin Schwidefsky