diff options
Diffstat (limited to 'kernel/time/clocksource.c')
-rw-r--r-- | kernel/time/clocksource.c | 266 |
1 files changed, 199 insertions, 67 deletions
diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index c9583382141a..50a8736757f3 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c | |||
@@ -31,6 +31,8 @@ | |||
31 | #include <linux/tick.h> | 31 | #include <linux/tick.h> |
32 | #include <linux/kthread.h> | 32 | #include <linux/kthread.h> |
33 | 33 | ||
34 | #include "tick-internal.h" | ||
35 | |||
34 | void timecounter_init(struct timecounter *tc, | 36 | void timecounter_init(struct timecounter *tc, |
35 | const struct cyclecounter *cc, | 37 | const struct cyclecounter *cc, |
36 | u64 start_tstamp) | 38 | u64 start_tstamp) |
@@ -174,11 +176,12 @@ clocks_calc_mult_shift(u32 *mult, u32 *shift, u32 from, u32 to, u32 maxsec) | |||
174 | static struct clocksource *curr_clocksource; | 176 | static struct clocksource *curr_clocksource; |
175 | static LIST_HEAD(clocksource_list); | 177 | static LIST_HEAD(clocksource_list); |
176 | static DEFINE_MUTEX(clocksource_mutex); | 178 | static DEFINE_MUTEX(clocksource_mutex); |
177 | static char override_name[32]; | 179 | static char override_name[CS_NAME_LEN]; |
178 | static int finished_booting; | 180 | static int finished_booting; |
179 | 181 | ||
180 | #ifdef CONFIG_CLOCKSOURCE_WATCHDOG | 182 | #ifdef CONFIG_CLOCKSOURCE_WATCHDOG |
181 | static void clocksource_watchdog_work(struct work_struct *work); | 183 | static void clocksource_watchdog_work(struct work_struct *work); |
184 | static void clocksource_select(void); | ||
182 | 185 | ||
183 | static LIST_HEAD(watchdog_list); | 186 | static LIST_HEAD(watchdog_list); |
184 | static struct clocksource *watchdog; | 187 | static struct clocksource *watchdog; |
@@ -299,13 +302,30 @@ static void clocksource_watchdog(unsigned long data) | |||
299 | if (!(cs->flags & CLOCK_SOURCE_VALID_FOR_HRES) && | 302 | if (!(cs->flags & CLOCK_SOURCE_VALID_FOR_HRES) && |
300 | (cs->flags & CLOCK_SOURCE_IS_CONTINUOUS) && | 303 | (cs->flags & CLOCK_SOURCE_IS_CONTINUOUS) && |
301 | (watchdog->flags & CLOCK_SOURCE_IS_CONTINUOUS)) { | 304 | (watchdog->flags & CLOCK_SOURCE_IS_CONTINUOUS)) { |
305 | /* Mark it valid for high-res. */ | ||
302 | cs->flags |= CLOCK_SOURCE_VALID_FOR_HRES; | 306 | cs->flags |= CLOCK_SOURCE_VALID_FOR_HRES; |
307 | |||
308 | /* | ||
309 | * clocksource_done_booting() will sort it if | ||
310 | * finished_booting is not set yet. | ||
311 | */ | ||
312 | if (!finished_booting) | ||
313 | continue; | ||
314 | |||
303 | /* | 315 | /* |
304 | * We just marked the clocksource as highres-capable, | 316 | * If this is not the current clocksource let |
305 | * notify the rest of the system as well so that we | 317 | * the watchdog thread reselect it. Due to the |
306 | * transition into high-res mode: | 318 | * change to high res this clocksource might |
319 | * be preferred now. If it is the current | ||
320 | * clocksource let the tick code know about | ||
321 | * that change. | ||
307 | */ | 322 | */ |
308 | tick_clock_notify(); | 323 | if (cs != curr_clocksource) { |
324 | cs->flags |= CLOCK_SOURCE_RESELECT; | ||
325 | schedule_work(&watchdog_work); | ||
326 | } else { | ||
327 | tick_clock_notify(); | ||
328 | } | ||
309 | } | 329 | } |
310 | } | 330 | } |
311 | 331 | ||
@@ -388,44 +408,39 @@ static void clocksource_enqueue_watchdog(struct clocksource *cs) | |||
388 | 408 | ||
389 | static void clocksource_dequeue_watchdog(struct clocksource *cs) | 409 | static void clocksource_dequeue_watchdog(struct clocksource *cs) |
390 | { | 410 | { |
391 | struct clocksource *tmp; | ||
392 | unsigned long flags; | 411 | unsigned long flags; |
393 | 412 | ||
394 | spin_lock_irqsave(&watchdog_lock, flags); | 413 | spin_lock_irqsave(&watchdog_lock, flags); |
395 | if (cs->flags & CLOCK_SOURCE_MUST_VERIFY) { | 414 | if (cs != watchdog) { |
396 | /* cs is a watched clocksource. */ | 415 | if (cs->flags & CLOCK_SOURCE_MUST_VERIFY) { |
397 | list_del_init(&cs->wd_list); | 416 | /* cs is a watched clocksource. */ |
398 | } else if (cs == watchdog) { | 417 | list_del_init(&cs->wd_list); |
399 | /* Reset watchdog cycles */ | 418 | /* Check if the watchdog timer needs to be stopped. */ |
400 | clocksource_reset_watchdog(); | 419 | clocksource_stop_watchdog(); |
401 | /* Current watchdog is removed. Find an alternative. */ | ||
402 | watchdog = NULL; | ||
403 | list_for_each_entry(tmp, &clocksource_list, list) { | ||
404 | if (tmp == cs || tmp->flags & CLOCK_SOURCE_MUST_VERIFY) | ||
405 | continue; | ||
406 | if (!watchdog || tmp->rating > watchdog->rating) | ||
407 | watchdog = tmp; | ||
408 | } | 420 | } |
409 | } | 421 | } |
410 | cs->flags &= ~CLOCK_SOURCE_WATCHDOG; | ||
411 | /* Check if the watchdog timer needs to be stopped. */ | ||
412 | clocksource_stop_watchdog(); | ||
413 | spin_unlock_irqrestore(&watchdog_lock, flags); | 422 | spin_unlock_irqrestore(&watchdog_lock, flags); |
414 | } | 423 | } |
415 | 424 | ||
416 | static int clocksource_watchdog_kthread(void *data) | 425 | static int __clocksource_watchdog_kthread(void) |
417 | { | 426 | { |
418 | struct clocksource *cs, *tmp; | 427 | struct clocksource *cs, *tmp; |
419 | unsigned long flags; | 428 | unsigned long flags; |
420 | LIST_HEAD(unstable); | 429 | LIST_HEAD(unstable); |
430 | int select = 0; | ||
421 | 431 | ||
422 | mutex_lock(&clocksource_mutex); | ||
423 | spin_lock_irqsave(&watchdog_lock, flags); | 432 | spin_lock_irqsave(&watchdog_lock, flags); |
424 | list_for_each_entry_safe(cs, tmp, &watchdog_list, wd_list) | 433 | list_for_each_entry_safe(cs, tmp, &watchdog_list, wd_list) { |
425 | if (cs->flags & CLOCK_SOURCE_UNSTABLE) { | 434 | if (cs->flags & CLOCK_SOURCE_UNSTABLE) { |
426 | list_del_init(&cs->wd_list); | 435 | list_del_init(&cs->wd_list); |
427 | list_add(&cs->wd_list, &unstable); | 436 | list_add(&cs->wd_list, &unstable); |
437 | select = 1; | ||
428 | } | 438 | } |
439 | if (cs->flags & CLOCK_SOURCE_RESELECT) { | ||
440 | cs->flags &= ~CLOCK_SOURCE_RESELECT; | ||
441 | select = 1; | ||
442 | } | ||
443 | } | ||
429 | /* Check if the watchdog timer needs to be stopped. */ | 444 | /* Check if the watchdog timer needs to be stopped. */ |
430 | clocksource_stop_watchdog(); | 445 | clocksource_stop_watchdog(); |
431 | spin_unlock_irqrestore(&watchdog_lock, flags); | 446 | spin_unlock_irqrestore(&watchdog_lock, flags); |
@@ -435,10 +450,23 @@ static int clocksource_watchdog_kthread(void *data) | |||
435 | list_del_init(&cs->wd_list); | 450 | list_del_init(&cs->wd_list); |
436 | __clocksource_change_rating(cs, 0); | 451 | __clocksource_change_rating(cs, 0); |
437 | } | 452 | } |
453 | return select; | ||
454 | } | ||
455 | |||
456 | static int clocksource_watchdog_kthread(void *data) | ||
457 | { | ||
458 | mutex_lock(&clocksource_mutex); | ||
459 | if (__clocksource_watchdog_kthread()) | ||
460 | clocksource_select(); | ||
438 | mutex_unlock(&clocksource_mutex); | 461 | mutex_unlock(&clocksource_mutex); |
439 | return 0; | 462 | return 0; |
440 | } | 463 | } |
441 | 464 | ||
465 | static bool clocksource_is_watchdog(struct clocksource *cs) | ||
466 | { | ||
467 | return cs == watchdog; | ||
468 | } | ||
469 | |||
442 | #else /* CONFIG_CLOCKSOURCE_WATCHDOG */ | 470 | #else /* CONFIG_CLOCKSOURCE_WATCHDOG */ |
443 | 471 | ||
444 | static void clocksource_enqueue_watchdog(struct clocksource *cs) | 472 | static void clocksource_enqueue_watchdog(struct clocksource *cs) |
@@ -449,7 +477,8 @@ static void clocksource_enqueue_watchdog(struct clocksource *cs) | |||
449 | 477 | ||
450 | static inline void clocksource_dequeue_watchdog(struct clocksource *cs) { } | 478 | static inline void clocksource_dequeue_watchdog(struct clocksource *cs) { } |
451 | static inline void clocksource_resume_watchdog(void) { } | 479 | static inline void clocksource_resume_watchdog(void) { } |
452 | static inline int clocksource_watchdog_kthread(void *data) { return 0; } | 480 | static inline int __clocksource_watchdog_kthread(void) { return 0; } |
481 | static bool clocksource_is_watchdog(struct clocksource *cs) { return false; } | ||
453 | 482 | ||
454 | #endif /* CONFIG_CLOCKSOURCE_WATCHDOG */ | 483 | #endif /* CONFIG_CLOCKSOURCE_WATCHDOG */ |
455 | 484 | ||
@@ -553,24 +582,42 @@ static u64 clocksource_max_deferment(struct clocksource *cs) | |||
553 | 582 | ||
554 | #ifndef CONFIG_ARCH_USES_GETTIMEOFFSET | 583 | #ifndef CONFIG_ARCH_USES_GETTIMEOFFSET |
555 | 584 | ||
556 | /** | 585 | static struct clocksource *clocksource_find_best(bool oneshot, bool skipcur) |
557 | * clocksource_select - Select the best clocksource available | ||
558 | * | ||
559 | * Private function. Must hold clocksource_mutex when called. | ||
560 | * | ||
561 | * Select the clocksource with the best rating, or the clocksource, | ||
562 | * which is selected by userspace override. | ||
563 | */ | ||
564 | static void clocksource_select(void) | ||
565 | { | 586 | { |
566 | struct clocksource *best, *cs; | 587 | struct clocksource *cs; |
567 | 588 | ||
568 | if (!finished_booting || list_empty(&clocksource_list)) | 589 | if (!finished_booting || list_empty(&clocksource_list)) |
590 | return NULL; | ||
591 | |||
592 | /* | ||
593 | * We pick the clocksource with the highest rating. If oneshot | ||
594 | * mode is active, we pick the highres valid clocksource with | ||
595 | * the best rating. | ||
596 | */ | ||
597 | list_for_each_entry(cs, &clocksource_list, list) { | ||
598 | if (skipcur && cs == curr_clocksource) | ||
599 | continue; | ||
600 | if (oneshot && !(cs->flags & CLOCK_SOURCE_VALID_FOR_HRES)) | ||
601 | continue; | ||
602 | return cs; | ||
603 | } | ||
604 | return NULL; | ||
605 | } | ||
606 | |||
607 | static void __clocksource_select(bool skipcur) | ||
608 | { | ||
609 | bool oneshot = tick_oneshot_mode_active(); | ||
610 | struct clocksource *best, *cs; | ||
611 | |||
612 | /* Find the best suitable clocksource */ | ||
613 | best = clocksource_find_best(oneshot, skipcur); | ||
614 | if (!best) | ||
569 | return; | 615 | return; |
570 | /* First clocksource on the list has the best rating. */ | 616 | |
571 | best = list_first_entry(&clocksource_list, struct clocksource, list); | ||
572 | /* Check for the override clocksource. */ | 617 | /* Check for the override clocksource. */ |
573 | list_for_each_entry(cs, &clocksource_list, list) { | 618 | list_for_each_entry(cs, &clocksource_list, list) { |
619 | if (skipcur && cs == curr_clocksource) | ||
620 | continue; | ||
574 | if (strcmp(cs->name, override_name) != 0) | 621 | if (strcmp(cs->name, override_name) != 0) |
575 | continue; | 622 | continue; |
576 | /* | 623 | /* |
@@ -578,8 +625,7 @@ static void clocksource_select(void) | |||
578 | * capable clocksource if the tick code is in oneshot | 625 | * capable clocksource if the tick code is in oneshot |
579 | * mode (highres or nohz) | 626 | * mode (highres or nohz) |
580 | */ | 627 | */ |
581 | if (!(cs->flags & CLOCK_SOURCE_VALID_FOR_HRES) && | 628 | if (!(cs->flags & CLOCK_SOURCE_VALID_FOR_HRES) && oneshot) { |
582 | tick_oneshot_mode_active()) { | ||
583 | /* Override clocksource cannot be used. */ | 629 | /* Override clocksource cannot be used. */ |
584 | printk(KERN_WARNING "Override clocksource %s is not " | 630 | printk(KERN_WARNING "Override clocksource %s is not " |
585 | "HRT compatible. Cannot switch while in " | 631 | "HRT compatible. Cannot switch while in " |
@@ -590,16 +636,35 @@ static void clocksource_select(void) | |||
590 | best = cs; | 636 | best = cs; |
591 | break; | 637 | break; |
592 | } | 638 | } |
593 | if (curr_clocksource != best) { | 639 | |
594 | printk(KERN_INFO "Switching to clocksource %s\n", best->name); | 640 | if (curr_clocksource != best && !timekeeping_notify(best)) { |
641 | pr_info("Switched to clocksource %s\n", best->name); | ||
595 | curr_clocksource = best; | 642 | curr_clocksource = best; |
596 | timekeeping_notify(curr_clocksource); | ||
597 | } | 643 | } |
598 | } | 644 | } |
599 | 645 | ||
646 | /** | ||
647 | * clocksource_select - Select the best clocksource available | ||
648 | * | ||
649 | * Private function. Must hold clocksource_mutex when called. | ||
650 | * | ||
651 | * Select the clocksource with the best rating, or the clocksource, | ||
652 | * which is selected by userspace override. | ||
653 | */ | ||
654 | static void clocksource_select(void) | ||
655 | { | ||
656 | return __clocksource_select(false); | ||
657 | } | ||
658 | |||
659 | static void clocksource_select_fallback(void) | ||
660 | { | ||
661 | return __clocksource_select(true); | ||
662 | } | ||
663 | |||
600 | #else /* !CONFIG_ARCH_USES_GETTIMEOFFSET */ | 664 | #else /* !CONFIG_ARCH_USES_GETTIMEOFFSET */ |
601 | 665 | ||
602 | static inline void clocksource_select(void) { } | 666 | static inline void clocksource_select(void) { } |
667 | static inline void clocksource_select_fallback(void) { } | ||
603 | 668 | ||
604 | #endif | 669 | #endif |
605 | 670 | ||
@@ -614,16 +679,11 @@ static int __init clocksource_done_booting(void) | |||
614 | { | 679 | { |
615 | mutex_lock(&clocksource_mutex); | 680 | mutex_lock(&clocksource_mutex); |
616 | curr_clocksource = clocksource_default_clock(); | 681 | curr_clocksource = clocksource_default_clock(); |
617 | mutex_unlock(&clocksource_mutex); | ||
618 | |||
619 | finished_booting = 1; | 682 | finished_booting = 1; |
620 | |||
621 | /* | 683 | /* |
622 | * Run the watchdog first to eliminate unstable clock sources | 684 | * Run the watchdog first to eliminate unstable clock sources |
623 | */ | 685 | */ |
624 | clocksource_watchdog_kthread(NULL); | 686 | __clocksource_watchdog_kthread(); |
625 | |||
626 | mutex_lock(&clocksource_mutex); | ||
627 | clocksource_select(); | 687 | clocksource_select(); |
628 | mutex_unlock(&clocksource_mutex); | 688 | mutex_unlock(&clocksource_mutex); |
629 | return 0; | 689 | return 0; |
@@ -756,7 +816,6 @@ static void __clocksource_change_rating(struct clocksource *cs, int rating) | |||
756 | list_del(&cs->list); | 816 | list_del(&cs->list); |
757 | cs->rating = rating; | 817 | cs->rating = rating; |
758 | clocksource_enqueue(cs); | 818 | clocksource_enqueue(cs); |
759 | clocksource_select(); | ||
760 | } | 819 | } |
761 | 820 | ||
762 | /** | 821 | /** |
@@ -768,21 +827,47 @@ void clocksource_change_rating(struct clocksource *cs, int rating) | |||
768 | { | 827 | { |
769 | mutex_lock(&clocksource_mutex); | 828 | mutex_lock(&clocksource_mutex); |
770 | __clocksource_change_rating(cs, rating); | 829 | __clocksource_change_rating(cs, rating); |
830 | clocksource_select(); | ||
771 | mutex_unlock(&clocksource_mutex); | 831 | mutex_unlock(&clocksource_mutex); |
772 | } | 832 | } |
773 | EXPORT_SYMBOL(clocksource_change_rating); | 833 | EXPORT_SYMBOL(clocksource_change_rating); |
774 | 834 | ||
835 | /* | ||
836 | * Unbind clocksource @cs. Called with clocksource_mutex held | ||
837 | */ | ||
838 | static int clocksource_unbind(struct clocksource *cs) | ||
839 | { | ||
840 | /* | ||
841 | * I really can't convince myself to support this on hardware | ||
842 | * designed by lobotomized monkeys. | ||
843 | */ | ||
844 | if (clocksource_is_watchdog(cs)) | ||
845 | return -EBUSY; | ||
846 | |||
847 | if (cs == curr_clocksource) { | ||
848 | /* Select and try to install a replacement clock source */ | ||
849 | clocksource_select_fallback(); | ||
850 | if (curr_clocksource == cs) | ||
851 | return -EBUSY; | ||
852 | } | ||
853 | clocksource_dequeue_watchdog(cs); | ||
854 | list_del_init(&cs->list); | ||
855 | return 0; | ||
856 | } | ||
857 | |||
775 | /** | 858 | /** |
776 | * clocksource_unregister - remove a registered clocksource | 859 | * clocksource_unregister - remove a registered clocksource |
777 | * @cs: clocksource to be unregistered | 860 | * @cs: clocksource to be unregistered |
778 | */ | 861 | */ |
779 | void clocksource_unregister(struct clocksource *cs) | 862 | int clocksource_unregister(struct clocksource *cs) |
780 | { | 863 | { |
864 | int ret = 0; | ||
865 | |||
781 | mutex_lock(&clocksource_mutex); | 866 | mutex_lock(&clocksource_mutex); |
782 | clocksource_dequeue_watchdog(cs); | 867 | if (!list_empty(&cs->list)) |
783 | list_del(&cs->list); | 868 | ret = clocksource_unbind(cs); |
784 | clocksource_select(); | ||
785 | mutex_unlock(&clocksource_mutex); | 869 | mutex_unlock(&clocksource_mutex); |
870 | return ret; | ||
786 | } | 871 | } |
787 | EXPORT_SYMBOL(clocksource_unregister); | 872 | EXPORT_SYMBOL(clocksource_unregister); |
788 | 873 | ||
@@ -808,6 +893,23 @@ sysfs_show_current_clocksources(struct device *dev, | |||
808 | return count; | 893 | return count; |
809 | } | 894 | } |
810 | 895 | ||
896 | size_t sysfs_get_uname(const char *buf, char *dst, size_t cnt) | ||
897 | { | ||
898 | size_t ret = cnt; | ||
899 | |||
900 | /* strings from sysfs write are not 0 terminated! */ | ||
901 | if (!cnt || cnt >= CS_NAME_LEN) | ||
902 | return -EINVAL; | ||
903 | |||
904 | /* strip of \n: */ | ||
905 | if (buf[cnt-1] == '\n') | ||
906 | cnt--; | ||
907 | if (cnt > 0) | ||
908 | memcpy(dst, buf, cnt); | ||
909 | dst[cnt] = 0; | ||
910 | return ret; | ||
911 | } | ||
912 | |||
811 | /** | 913 | /** |
812 | * sysfs_override_clocksource - interface for manually overriding clocksource | 914 | * sysfs_override_clocksource - interface for manually overriding clocksource |
813 | * @dev: unused | 915 | * @dev: unused |
@@ -822,22 +924,13 @@ static ssize_t sysfs_override_clocksource(struct device *dev, | |||
822 | struct device_attribute *attr, | 924 | struct device_attribute *attr, |
823 | const char *buf, size_t count) | 925 | const char *buf, size_t count) |
824 | { | 926 | { |
825 | size_t ret = count; | 927 | size_t ret; |
826 | |||
827 | /* strings from sysfs write are not 0 terminated! */ | ||
828 | if (count >= sizeof(override_name)) | ||
829 | return -EINVAL; | ||
830 | |||
831 | /* strip of \n: */ | ||
832 | if (buf[count-1] == '\n') | ||
833 | count--; | ||
834 | 928 | ||
835 | mutex_lock(&clocksource_mutex); | 929 | mutex_lock(&clocksource_mutex); |
836 | 930 | ||
837 | if (count > 0) | 931 | ret = sysfs_get_uname(buf, override_name, count); |
838 | memcpy(override_name, buf, count); | 932 | if (ret >= 0) |
839 | override_name[count] = 0; | 933 | clocksource_select(); |
840 | clocksource_select(); | ||
841 | 934 | ||
842 | mutex_unlock(&clocksource_mutex); | 935 | mutex_unlock(&clocksource_mutex); |
843 | 936 | ||
@@ -845,6 +938,40 @@ static ssize_t sysfs_override_clocksource(struct device *dev, | |||
845 | } | 938 | } |
846 | 939 | ||
847 | /** | 940 | /** |
941 | * sysfs_unbind_current_clocksource - interface for manually unbinding clocksource | ||
942 | * @dev: unused | ||
943 | * @attr: unused | ||
944 | * @buf: unused | ||
945 | * @count: length of buffer | ||
946 | * | ||
947 | * Takes input from sysfs interface for manually unbinding a clocksource. | ||
948 | */ | ||
949 | static ssize_t sysfs_unbind_clocksource(struct device *dev, | ||
950 | struct device_attribute *attr, | ||
951 | const char *buf, size_t count) | ||
952 | { | ||
953 | struct clocksource *cs; | ||
954 | char name[CS_NAME_LEN]; | ||
955 | size_t ret; | ||
956 | |||
957 | ret = sysfs_get_uname(buf, name, count); | ||
958 | if (ret < 0) | ||
959 | return ret; | ||
960 | |||
961 | ret = -ENODEV; | ||
962 | mutex_lock(&clocksource_mutex); | ||
963 | list_for_each_entry(cs, &clocksource_list, list) { | ||
964 | if (strcmp(cs->name, name)) | ||
965 | continue; | ||
966 | ret = clocksource_unbind(cs); | ||
967 | break; | ||
968 | } | ||
969 | mutex_unlock(&clocksource_mutex); | ||
970 | |||
971 | return ret ? ret : count; | ||
972 | } | ||
973 | |||
974 | /** | ||
848 | * sysfs_show_available_clocksources - sysfs interface for listing clocksource | 975 | * sysfs_show_available_clocksources - sysfs interface for listing clocksource |
849 | * @dev: unused | 976 | * @dev: unused |
850 | * @attr: unused | 977 | * @attr: unused |
@@ -886,6 +1013,8 @@ sysfs_show_available_clocksources(struct device *dev, | |||
886 | static DEVICE_ATTR(current_clocksource, 0644, sysfs_show_current_clocksources, | 1013 | static DEVICE_ATTR(current_clocksource, 0644, sysfs_show_current_clocksources, |
887 | sysfs_override_clocksource); | 1014 | sysfs_override_clocksource); |
888 | 1015 | ||
1016 | static DEVICE_ATTR(unbind_clocksource, 0200, NULL, sysfs_unbind_clocksource); | ||
1017 | |||
889 | static DEVICE_ATTR(available_clocksource, 0444, | 1018 | static DEVICE_ATTR(available_clocksource, 0444, |
890 | sysfs_show_available_clocksources, NULL); | 1019 | sysfs_show_available_clocksources, NULL); |
891 | 1020 | ||
@@ -910,6 +1039,9 @@ static int __init init_clocksource_sysfs(void) | |||
910 | &device_clocksource, | 1039 | &device_clocksource, |
911 | &dev_attr_current_clocksource); | 1040 | &dev_attr_current_clocksource); |
912 | if (!error) | 1041 | if (!error) |
1042 | error = device_create_file(&device_clocksource, | ||
1043 | &dev_attr_unbind_clocksource); | ||
1044 | if (!error) | ||
913 | error = device_create_file( | 1045 | error = device_create_file( |
914 | &device_clocksource, | 1046 | &device_clocksource, |
915 | &dev_attr_available_clocksource); | 1047 | &dev_attr_available_clocksource); |