diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/time/clocksource.c | 120 |
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 | */ |
49 | static struct clocksource *curr_clocksource = &clocksource_jiffies; | 49 | static struct clocksource *curr_clocksource = &clocksource_jiffies; |
50 | static struct clocksource *next_clocksource; | 50 | static struct clocksource *next_clocksource; |
51 | static struct clocksource *clocksource_override; | ||
51 | static LIST_HEAD(clocksource_list); | 52 | static LIST_HEAD(clocksource_list); |
52 | static DEFINE_SPINLOCK(clocksource_lock); | 53 | static DEFINE_SPINLOCK(clocksource_lock); |
53 | static char override_name[32]; | 54 | static 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 | */ |
95 | static struct clocksource *select_clocksource(void) | 95 | static 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 | */ |
129 | static int is_registered_source(struct clocksource *c) | 109 | static 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 | */ |
151 | int clocksource_register(struct clocksource *c) | 138 | int 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 | } |
171 | EXPORT_SYMBOL(clocksource_register); | 150 | EXPORT_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 | */ |
180 | void clocksource_reselect(void) | 156 | void 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 | } |
188 | EXPORT_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) | |||
221 | static ssize_t sysfs_override_clocksource(struct sys_device *dev, | 198 | static 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 | ||