aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/time/clocksource.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/time/clocksource.c')
-rw-r--r--kernel/time/clocksource.c120
1 files changed, 58 insertions, 62 deletions
diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c
index d9ef176c4e09..2f6a3d6e43bc 100644
--- a/kernel/time/clocksource.c
+++ b/kernel/time/clocksource.c
@@ -48,6 +48,7 @@ extern struct clocksource clocksource_jiffies;
48 */ 48 */
49static struct clocksource *curr_clocksource = &clocksource_jiffies; 49static struct clocksource *curr_clocksource = &clocksource_jiffies;
50static struct clocksource *next_clocksource; 50static struct clocksource *next_clocksource;
51static struct clocksource *clocksource_override;
51static LIST_HEAD(clocksource_list); 52static LIST_HEAD(clocksource_list);
52static DEFINE_SPINLOCK(clocksource_lock); 53static DEFINE_SPINLOCK(clocksource_lock);
53static char override_name[32]; 54static char override_name[32];
@@ -84,60 +85,46 @@ struct clocksource *clocksource_get_next(void)
84} 85}
85 86
86/** 87/**
87 * select_clocksource - Finds the best registered clocksource. 88 * select_clocksource - Selects the best registered clocksource.
88 * 89 *
89 * Private function. Must hold clocksource_lock when called. 90 * Private function. Must hold clocksource_lock when called.
90 * 91 *
91 * Looks through the list of registered clocksources, returning 92 * Select the clocksource with the best rating, or the clocksource,
92 * the one with the highest rating value. If there is a clocksource 93 * which is selected by userspace override.
93 * name that matches the override string, it returns that clocksource.
94 */ 94 */
95static struct clocksource *select_clocksource(void) 95static struct clocksource *select_clocksource(void)
96{ 96{
97 struct clocksource *best = NULL; 97 if (list_empty(&clocksource_list))
98 struct list_head *tmp; 98 return NULL;
99
100 list_for_each(tmp, &clocksource_list) {
101 struct clocksource *src;
102 99
103 src = list_entry(tmp, struct clocksource, list); 100 if (clocksource_override)
104 if (!best) 101 return clocksource_override;
105 best = src;
106
107 /* check for override: */
108 if (strlen(src->name) == strlen(override_name) &&
109 !strcmp(src->name, override_name)) {
110 best = src;
111 break;
112 }
113 /* pick the highest rating: */
114 if (src->rating > best->rating)
115 best = src;
116 }
117 102
118 return best; 103 return list_entry(clocksource_list.next, struct clocksource, list);
119} 104}
120 105
121/** 106/*
122 * is_registered_source - Checks if clocksource is registered 107 * Enqueue the clocksource sorted by rating
123 * @c: pointer to a clocksource
124 *
125 * Private helper function. Must hold clocksource_lock when called.
126 *
127 * Returns one if the clocksource is already registered, zero otherwise.
128 */ 108 */
129static int is_registered_source(struct clocksource *c) 109static int clocksource_enqueue(struct clocksource *c)
130{ 110{
131 int len = strlen(c->name); 111 struct list_head *tmp, *entry = &clocksource_list;
132 struct list_head *tmp;
133 112
134 list_for_each(tmp, &clocksource_list) { 113 list_for_each(tmp, &clocksource_list) {
135 struct clocksource *src; 114 struct clocksource *cs;
136 115
137 src = list_entry(tmp, struct clocksource, list); 116 cs = list_entry(tmp, struct clocksource, list);
138 if (strlen(src->name) == len && !strcmp(src->name, c->name)) 117 if (cs == c)
139 return 1; 118 return -EBUSY;
119 /* Keep track of the place, where to insert */
120 if (cs->rating >= c->rating)
121 entry = tmp;
140 } 122 }
123 list_add(&c->list, entry);
124
125 if (strlen(c->name) == strlen(override_name) &&
126 !strcmp(c->name, override_name))
127 clocksource_override = c;
141 128
142 return 0; 129 return 0;
143} 130}
@@ -150,42 +137,32 @@ static int is_registered_source(struct clocksource *c)
150 */ 137 */
151int clocksource_register(struct clocksource *c) 138int clocksource_register(struct clocksource *c)
152{ 139{
153 int ret = 0;
154 unsigned long flags; 140 unsigned long flags;
141 int ret = 0;
155 142
156 spin_lock_irqsave(&clocksource_lock, flags); 143 spin_lock_irqsave(&clocksource_lock, flags);
157 /* check if clocksource is already registered */ 144 ret = clocksource_enqueue(c);
158 if (is_registered_source(c)) { 145 if (!ret)
159 printk("register_clocksource: Cannot register %s. "
160 "Already registered!", c->name);
161 ret = -EBUSY;
162 } else {
163 /* register it */
164 list_add(&c->list, &clocksource_list);
165 /* scan the registered clocksources, and pick the best one */
166 next_clocksource = select_clocksource(); 146 next_clocksource = select_clocksource();
167 }
168 spin_unlock_irqrestore(&clocksource_lock, flags); 147 spin_unlock_irqrestore(&clocksource_lock, flags);
169 return ret; 148 return ret;
170} 149}
171EXPORT_SYMBOL(clocksource_register); 150EXPORT_SYMBOL(clocksource_register);
172 151
173/** 152/**
174 * clocksource_reselect - Rescan list for next clocksource 153 * clocksource_change_rating - Change the rating of a registered clocksource
175 * 154 *
176 * A quick helper function to be used if a clocksource changes its
177 * rating. Forces the clocksource list to be re-scanned for the best
178 * clocksource.
179 */ 155 */
180void clocksource_reselect(void) 156void clocksource_change_rating(struct clocksource *cs, int rating)
181{ 157{
182 unsigned long flags; 158 unsigned long flags;
183 159
184 spin_lock_irqsave(&clocksource_lock, flags); 160 spin_lock_irqsave(&clocksource_lock, flags);
161 list_del(&cs->list);
162 clocksource_enqueue(cs);
185 next_clocksource = select_clocksource(); 163 next_clocksource = select_clocksource();
186 spin_unlock_irqrestore(&clocksource_lock, flags); 164 spin_unlock_irqrestore(&clocksource_lock, flags);
187} 165}
188EXPORT_SYMBOL(clocksource_reselect);
189 166
190#ifdef CONFIG_SYSFS 167#ifdef CONFIG_SYSFS
191/** 168/**
@@ -221,7 +198,11 @@ sysfs_show_current_clocksources(struct sys_device *dev, char *buf)
221static ssize_t sysfs_override_clocksource(struct sys_device *dev, 198static ssize_t sysfs_override_clocksource(struct sys_device *dev,
222 const char *buf, size_t count) 199 const char *buf, size_t count)
223{ 200{
201 struct clocksource *ovr = NULL;
202 struct list_head *tmp;
224 size_t ret = count; 203 size_t ret = count;
204 int len;
205
225 /* strings from sysfs write are not 0 terminated! */ 206 /* strings from sysfs write are not 0 terminated! */
226 if (count >= sizeof(override_name)) 207 if (count >= sizeof(override_name))
227 return -EINVAL; 208 return -EINVAL;
@@ -229,17 +210,32 @@ static ssize_t sysfs_override_clocksource(struct sys_device *dev,
229 /* strip of \n: */ 210 /* strip of \n: */
230 if (buf[count-1] == '\n') 211 if (buf[count-1] == '\n')
231 count--; 212 count--;
232 if (count < 1)
233 return -EINVAL;
234 213
235 spin_lock_irq(&clocksource_lock); 214 spin_lock_irq(&clocksource_lock);
236 215
237 /* copy the name given: */ 216 if (count > 0)
238 memcpy(override_name, buf, count); 217 memcpy(override_name, buf, count);
239 override_name[count] = 0; 218 override_name[count] = 0;
240 219
241 /* try to select it: */ 220 len = strlen(override_name);
242 next_clocksource = select_clocksource(); 221 if (len) {
222 ovr = clocksource_override;
223 /* try to select it: */
224 list_for_each(tmp, &clocksource_list) {
225 struct clocksource *cs;
226
227 cs = list_entry(tmp, struct clocksource, list);
228 if (strlen(cs->name) == len &&
229 !strcmp(cs->name, override_name))
230 ovr = cs;
231 }
232 }
233
234 /* Reselect, when the override name has changed */
235 if (ovr != clocksource_override) {
236 clocksource_override = ovr;
237 next_clocksource = select_clocksource();
238 }
243 239
244 spin_unlock_irq(&clocksource_lock); 240 spin_unlock_irq(&clocksource_lock);
245 241