diff options
Diffstat (limited to 'kernel/time/clocksource.c')
-rw-r--r-- | kernel/time/clocksource.c | 134 |
1 files changed, 51 insertions, 83 deletions
diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index 7466cb811251..e91662e87cde 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c | |||
@@ -21,7 +21,6 @@ | |||
21 | * | 21 | * |
22 | * TODO WishList: | 22 | * TODO WishList: |
23 | * o Allow clocksource drivers to be unregistered | 23 | * o Allow clocksource drivers to be unregistered |
24 | * o get rid of clocksource_jiffies extern | ||
25 | */ | 24 | */ |
26 | 25 | ||
27 | #include <linux/clocksource.h> | 26 | #include <linux/clocksource.h> |
@@ -107,12 +106,9 @@ u64 timecounter_cyc2time(struct timecounter *tc, | |||
107 | } | 106 | } |
108 | EXPORT_SYMBOL(timecounter_cyc2time); | 107 | EXPORT_SYMBOL(timecounter_cyc2time); |
109 | 108 | ||
110 | /* XXX - Would like a better way for initializing curr_clocksource */ | ||
111 | extern struct clocksource clocksource_jiffies; | ||
112 | |||
113 | /*[Clocksource internal variables]--------- | 109 | /*[Clocksource internal variables]--------- |
114 | * curr_clocksource: | 110 | * curr_clocksource: |
115 | * currently selected clocksource. Initialized to clocksource_jiffies. | 111 | * currently selected clocksource. |
116 | * next_clocksource: | 112 | * next_clocksource: |
117 | * pending next selected clocksource. | 113 | * pending next selected clocksource. |
118 | * clocksource_list: | 114 | * clocksource_list: |
@@ -123,9 +119,8 @@ extern struct clocksource clocksource_jiffies; | |||
123 | * override_name: | 119 | * override_name: |
124 | * Name of the user-specified clocksource. | 120 | * Name of the user-specified clocksource. |
125 | */ | 121 | */ |
126 | static struct clocksource *curr_clocksource = &clocksource_jiffies; | 122 | static struct clocksource *curr_clocksource; |
127 | static struct clocksource *next_clocksource; | 123 | static struct clocksource *next_clocksource; |
128 | static struct clocksource *clocksource_override; | ||
129 | static LIST_HEAD(clocksource_list); | 124 | static LIST_HEAD(clocksource_list); |
130 | static DEFINE_SPINLOCK(clocksource_lock); | 125 | static DEFINE_SPINLOCK(clocksource_lock); |
131 | static char override_name[32]; | 126 | static char override_name[32]; |
@@ -320,6 +315,7 @@ void clocksource_touch_watchdog(void) | |||
320 | clocksource_resume_watchdog(); | 315 | clocksource_resume_watchdog(); |
321 | } | 316 | } |
322 | 317 | ||
318 | #ifdef CONFIG_GENERIC_TIME | ||
323 | /** | 319 | /** |
324 | * clocksource_get_next - Returns the selected clocksource | 320 | * clocksource_get_next - Returns the selected clocksource |
325 | * | 321 | * |
@@ -339,56 +335,65 @@ struct clocksource *clocksource_get_next(void) | |||
339 | } | 335 | } |
340 | 336 | ||
341 | /** | 337 | /** |
342 | * select_clocksource - Selects the best registered clocksource. | 338 | * clocksource_select - Select the best clocksource available |
343 | * | 339 | * |
344 | * Private function. Must hold clocksource_lock when called. | 340 | * Private function. Must hold clocksource_lock when called. |
345 | * | 341 | * |
346 | * Select the clocksource with the best rating, or the clocksource, | 342 | * Select the clocksource with the best rating, or the clocksource, |
347 | * which is selected by userspace override. | 343 | * which is selected by userspace override. |
348 | */ | 344 | */ |
349 | static struct clocksource *select_clocksource(void) | 345 | static void clocksource_select(void) |
350 | { | 346 | { |
351 | struct clocksource *next; | 347 | struct clocksource *best, *cs; |
352 | 348 | ||
353 | if (list_empty(&clocksource_list)) | 349 | if (list_empty(&clocksource_list)) |
354 | return NULL; | 350 | return; |
351 | /* First clocksource on the list has the best rating. */ | ||
352 | best = list_first_entry(&clocksource_list, struct clocksource, list); | ||
353 | /* Check for the override clocksource. */ | ||
354 | list_for_each_entry(cs, &clocksource_list, list) { | ||
355 | if (strcmp(cs->name, override_name) != 0) | ||
356 | continue; | ||
357 | /* | ||
358 | * Check to make sure we don't switch to a non-highres | ||
359 | * capable clocksource if the tick code is in oneshot | ||
360 | * mode (highres or nohz) | ||
361 | */ | ||
362 | if (!(cs->flags & CLOCK_SOURCE_VALID_FOR_HRES) && | ||
363 | tick_oneshot_mode_active()) { | ||
364 | /* Override clocksource cannot be used. */ | ||
365 | printk(KERN_WARNING "Override clocksource %s is not " | ||
366 | "HRT compatible. Cannot switch while in " | ||
367 | "HRT/NOHZ mode\n", cs->name); | ||
368 | override_name[0] = 0; | ||
369 | } else | ||
370 | /* Override clocksource can be used. */ | ||
371 | best = cs; | ||
372 | break; | ||
373 | } | ||
374 | if (curr_clocksource != best) | ||
375 | next_clocksource = best; | ||
376 | } | ||
355 | 377 | ||
356 | if (clocksource_override) | 378 | #else /* CONFIG_GENERIC_TIME */ |
357 | next = clocksource_override; | ||
358 | else | ||
359 | next = list_entry(clocksource_list.next, struct clocksource, | ||
360 | list); | ||
361 | 379 | ||
362 | if (next == curr_clocksource) | 380 | static void clocksource_select(void) { } |
363 | return NULL; | ||
364 | 381 | ||
365 | return next; | 382 | #endif |
366 | } | ||
367 | 383 | ||
368 | /* | 384 | /* |
369 | * Enqueue the clocksource sorted by rating | 385 | * Enqueue the clocksource sorted by rating |
370 | */ | 386 | */ |
371 | static int clocksource_enqueue(struct clocksource *c) | 387 | static void clocksource_enqueue(struct clocksource *cs) |
372 | { | 388 | { |
373 | struct list_head *tmp, *entry = &clocksource_list; | 389 | struct list_head *entry = &clocksource_list; |
374 | 390 | struct clocksource *tmp; | |
375 | list_for_each(tmp, &clocksource_list) { | ||
376 | struct clocksource *cs; | ||
377 | 391 | ||
378 | cs = list_entry(tmp, struct clocksource, list); | 392 | list_for_each_entry(tmp, &clocksource_list, list) |
379 | if (cs == c) | ||
380 | return -EBUSY; | ||
381 | /* Keep track of the place, where to insert */ | 393 | /* Keep track of the place, where to insert */ |
382 | if (cs->rating >= c->rating) | 394 | if (tmp->rating >= cs->rating) |
383 | entry = tmp; | 395 | entry = &tmp->list; |
384 | } | 396 | list_add(&cs->list, entry); |
385 | list_add(&c->list, entry); | ||
386 | |||
387 | if (strlen(c->name) == strlen(override_name) && | ||
388 | !strcmp(c->name, override_name)) | ||
389 | clocksource_override = c; | ||
390 | |||
391 | return 0; | ||
392 | } | 397 | } |
393 | 398 | ||
394 | /** | 399 | /** |
@@ -397,19 +402,16 @@ static int clocksource_enqueue(struct clocksource *c) | |||
397 | * | 402 | * |
398 | * Returns -EBUSY if registration fails, zero otherwise. | 403 | * Returns -EBUSY if registration fails, zero otherwise. |
399 | */ | 404 | */ |
400 | int clocksource_register(struct clocksource *c) | 405 | int clocksource_register(struct clocksource *cs) |
401 | { | 406 | { |
402 | unsigned long flags; | 407 | unsigned long flags; |
403 | int ret; | ||
404 | 408 | ||
405 | spin_lock_irqsave(&clocksource_lock, flags); | 409 | spin_lock_irqsave(&clocksource_lock, flags); |
406 | ret = clocksource_enqueue(c); | 410 | clocksource_enqueue(cs); |
407 | if (!ret) | 411 | clocksource_select(); |
408 | next_clocksource = select_clocksource(); | ||
409 | spin_unlock_irqrestore(&clocksource_lock, flags); | 412 | spin_unlock_irqrestore(&clocksource_lock, flags); |
410 | if (!ret) | 413 | clocksource_check_watchdog(cs); |
411 | clocksource_check_watchdog(c); | 414 | return 0; |
412 | return ret; | ||
413 | } | 415 | } |
414 | EXPORT_SYMBOL(clocksource_register); | 416 | EXPORT_SYMBOL(clocksource_register); |
415 | 417 | ||
@@ -425,7 +427,7 @@ void clocksource_change_rating(struct clocksource *cs, int rating) | |||
425 | list_del(&cs->list); | 427 | list_del(&cs->list); |
426 | cs->rating = rating; | 428 | cs->rating = rating; |
427 | clocksource_enqueue(cs); | 429 | clocksource_enqueue(cs); |
428 | next_clocksource = select_clocksource(); | 430 | clocksource_select(); |
429 | spin_unlock_irqrestore(&clocksource_lock, flags); | 431 | spin_unlock_irqrestore(&clocksource_lock, flags); |
430 | } | 432 | } |
431 | 433 | ||
@@ -438,9 +440,7 @@ void clocksource_unregister(struct clocksource *cs) | |||
438 | 440 | ||
439 | spin_lock_irqsave(&clocksource_lock, flags); | 441 | spin_lock_irqsave(&clocksource_lock, flags); |
440 | list_del(&cs->list); | 442 | list_del(&cs->list); |
441 | if (clocksource_override == cs) | 443 | clocksource_select(); |
442 | clocksource_override = NULL; | ||
443 | next_clocksource = select_clocksource(); | ||
444 | spin_unlock_irqrestore(&clocksource_lock, flags); | 444 | spin_unlock_irqrestore(&clocksource_lock, flags); |
445 | } | 445 | } |
446 | 446 | ||
@@ -478,9 +478,7 @@ static ssize_t sysfs_override_clocksource(struct sys_device *dev, | |||
478 | struct sysdev_attribute *attr, | 478 | struct sysdev_attribute *attr, |
479 | const char *buf, size_t count) | 479 | const char *buf, size_t count) |
480 | { | 480 | { |
481 | struct clocksource *ovr = NULL; | ||
482 | size_t ret = count; | 481 | size_t ret = count; |
483 | int len; | ||
484 | 482 | ||
485 | /* strings from sysfs write are not 0 terminated! */ | 483 | /* strings from sysfs write are not 0 terminated! */ |
486 | if (count >= sizeof(override_name)) | 484 | if (count >= sizeof(override_name)) |
@@ -495,37 +493,7 @@ static ssize_t sysfs_override_clocksource(struct sys_device *dev, | |||
495 | if (count > 0) | 493 | if (count > 0) |
496 | memcpy(override_name, buf, count); | 494 | memcpy(override_name, buf, count); |
497 | override_name[count] = 0; | 495 | override_name[count] = 0; |
498 | 496 | clocksource_select(); | |
499 | len = strlen(override_name); | ||
500 | if (len) { | ||
501 | struct clocksource *cs; | ||
502 | |||
503 | ovr = clocksource_override; | ||
504 | /* try to select it: */ | ||
505 | list_for_each_entry(cs, &clocksource_list, list) { | ||
506 | if (strlen(cs->name) == len && | ||
507 | !strcmp(cs->name, override_name)) | ||
508 | ovr = cs; | ||
509 | } | ||
510 | } | ||
511 | |||
512 | /* | ||
513 | * Check to make sure we don't switch to a non-highres capable | ||
514 | * clocksource if the tick code is in oneshot mode (highres or nohz) | ||
515 | */ | ||
516 | if (tick_oneshot_mode_active() && ovr && | ||
517 | !(ovr->flags & CLOCK_SOURCE_VALID_FOR_HRES)) { | ||
518 | printk(KERN_WARNING "%s clocksource is not HRT compatible. " | ||
519 | "Cannot switch while in HRT/NOHZ mode\n", ovr->name); | ||
520 | ovr = NULL; | ||
521 | override_name[0] = 0; | ||
522 | } | ||
523 | |||
524 | /* Reselect, when the override name has changed */ | ||
525 | if (ovr != clocksource_override) { | ||
526 | clocksource_override = ovr; | ||
527 | next_clocksource = select_clocksource(); | ||
528 | } | ||
529 | 497 | ||
530 | spin_unlock_irq(&clocksource_lock); | 498 | spin_unlock_irq(&clocksource_lock); |
531 | 499 | ||