diff options
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/time/clocksource.c | 73 |
1 files changed, 73 insertions, 0 deletions
diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index d7f1a45c2fa5..791d1aeb17ac 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c | |||
| @@ -440,6 +440,11 @@ static int clocksource_watchdog_kthread(void *data) | |||
| 440 | return 0; | 440 | return 0; |
| 441 | } | 441 | } |
| 442 | 442 | ||
| 443 | static bool clocksource_is_watchdog(struct clocksource *cs) | ||
| 444 | { | ||
| 445 | return cs == watchdog; | ||
| 446 | } | ||
| 447 | |||
| 443 | #else /* CONFIG_CLOCKSOURCE_WATCHDOG */ | 448 | #else /* CONFIG_CLOCKSOURCE_WATCHDOG */ |
| 444 | 449 | ||
| 445 | static void clocksource_enqueue_watchdog(struct clocksource *cs) | 450 | static void clocksource_enqueue_watchdog(struct clocksource *cs) |
| @@ -451,6 +456,7 @@ static void clocksource_enqueue_watchdog(struct clocksource *cs) | |||
| 451 | static inline void clocksource_dequeue_watchdog(struct clocksource *cs) { } | 456 | static inline void clocksource_dequeue_watchdog(struct clocksource *cs) { } |
| 452 | static inline void clocksource_resume_watchdog(void) { } | 457 | static inline void clocksource_resume_watchdog(void) { } |
| 453 | static inline int clocksource_watchdog_kthread(void *data) { return 0; } | 458 | static inline int clocksource_watchdog_kthread(void *data) { return 0; } |
| 459 | static bool clocksource_is_watchdog(struct clocksource *cs) { return false; } | ||
| 454 | 460 | ||
| 455 | #endif /* CONFIG_CLOCKSOURCE_WATCHDOG */ | 461 | #endif /* CONFIG_CLOCKSOURCE_WATCHDOG */ |
| 456 | 462 | ||
| @@ -628,6 +634,11 @@ static void clocksource_select(void) | |||
| 628 | return __clocksource_select(false); | 634 | return __clocksource_select(false); |
| 629 | } | 635 | } |
| 630 | 636 | ||
| 637 | static void clocksource_select_fallback(void) | ||
| 638 | { | ||
| 639 | return __clocksource_select(true); | ||
| 640 | } | ||
| 641 | |||
| 631 | #else /* !CONFIG_ARCH_USES_GETTIMEOFFSET */ | 642 | #else /* !CONFIG_ARCH_USES_GETTIMEOFFSET */ |
| 632 | 643 | ||
| 633 | static inline void clocksource_select(void) { } | 644 | static inline void clocksource_select(void) { } |
| @@ -803,6 +814,29 @@ void clocksource_change_rating(struct clocksource *cs, int rating) | |||
| 803 | } | 814 | } |
| 804 | EXPORT_SYMBOL(clocksource_change_rating); | 815 | EXPORT_SYMBOL(clocksource_change_rating); |
| 805 | 816 | ||
| 817 | /* | ||
| 818 | * Unbind clocksource @cs. Called with clocksource_mutex held | ||
| 819 | */ | ||
| 820 | static int clocksource_unbind(struct clocksource *cs) | ||
| 821 | { | ||
| 822 | /* | ||
| 823 | * I really can't convince myself to support this on hardware | ||
| 824 | * designed by lobotomized monkeys. | ||
| 825 | */ | ||
| 826 | if (clocksource_is_watchdog(cs)) | ||
| 827 | return -EBUSY; | ||
| 828 | |||
| 829 | if (cs == curr_clocksource) { | ||
| 830 | /* Select and try to install a replacement clock source */ | ||
| 831 | clocksource_select_fallback(); | ||
| 832 | if (curr_clocksource == cs) | ||
| 833 | return -EBUSY; | ||
| 834 | } | ||
| 835 | clocksource_dequeue_watchdog(cs); | ||
| 836 | list_del_init(&cs->list); | ||
| 837 | return 0; | ||
| 838 | } | ||
| 839 | |||
| 806 | /** | 840 | /** |
| 807 | * clocksource_unregister - remove a registered clocksource | 841 | * clocksource_unregister - remove a registered clocksource |
| 808 | * @cs: clocksource to be unregistered | 842 | * @cs: clocksource to be unregistered |
| @@ -884,6 +918,40 @@ static ssize_t sysfs_override_clocksource(struct device *dev, | |||
| 884 | } | 918 | } |
| 885 | 919 | ||
| 886 | /** | 920 | /** |
| 921 | * sysfs_unbind_current_clocksource - interface for manually unbinding clocksource | ||
| 922 | * @dev: unused | ||
| 923 | * @attr: unused | ||
| 924 | * @buf: unused | ||
| 925 | * @count: length of buffer | ||
| 926 | * | ||
| 927 | * Takes input from sysfs interface for manually unbinding a clocksource. | ||
| 928 | */ | ||
| 929 | static ssize_t sysfs_unbind_clocksource(struct device *dev, | ||
| 930 | struct device_attribute *attr, | ||
| 931 | const char *buf, size_t count) | ||
| 932 | { | ||
| 933 | struct clocksource *cs; | ||
| 934 | char name[CS_NAME_LEN]; | ||
| 935 | size_t ret; | ||
| 936 | |||
| 937 | ret = clocksource_get_uname(buf, name, count); | ||
| 938 | if (ret < 0) | ||
| 939 | return ret; | ||
| 940 | |||
| 941 | ret = -ENODEV; | ||
| 942 | mutex_lock(&clocksource_mutex); | ||
| 943 | list_for_each_entry(cs, &clocksource_list, list) { | ||
| 944 | if (strcmp(cs->name, name)) | ||
| 945 | continue; | ||
| 946 | ret = clocksource_unbind(cs); | ||
| 947 | break; | ||
| 948 | } | ||
| 949 | mutex_unlock(&clocksource_mutex); | ||
| 950 | |||
| 951 | return ret ? ret : count; | ||
| 952 | } | ||
| 953 | |||
| 954 | /** | ||
| 887 | * sysfs_show_available_clocksources - sysfs interface for listing clocksource | 955 | * sysfs_show_available_clocksources - sysfs interface for listing clocksource |
| 888 | * @dev: unused | 956 | * @dev: unused |
| 889 | * @attr: unused | 957 | * @attr: unused |
| @@ -925,6 +993,8 @@ sysfs_show_available_clocksources(struct device *dev, | |||
| 925 | static DEVICE_ATTR(current_clocksource, 0644, sysfs_show_current_clocksources, | 993 | static DEVICE_ATTR(current_clocksource, 0644, sysfs_show_current_clocksources, |
| 926 | sysfs_override_clocksource); | 994 | sysfs_override_clocksource); |
| 927 | 995 | ||
| 996 | static DEVICE_ATTR(unbind_clocksource, 0200, NULL, sysfs_unbind_clocksource); | ||
| 997 | |||
| 928 | static DEVICE_ATTR(available_clocksource, 0444, | 998 | static DEVICE_ATTR(available_clocksource, 0444, |
| 929 | sysfs_show_available_clocksources, NULL); | 999 | sysfs_show_available_clocksources, NULL); |
| 930 | 1000 | ||
| @@ -949,6 +1019,9 @@ static int __init init_clocksource_sysfs(void) | |||
| 949 | &device_clocksource, | 1019 | &device_clocksource, |
| 950 | &dev_attr_current_clocksource); | 1020 | &dev_attr_current_clocksource); |
| 951 | if (!error) | 1021 | if (!error) |
| 1022 | error = device_create_file(&device_clocksource, | ||
| 1023 | &dev_attr_unbind_clocksource); | ||
| 1024 | if (!error) | ||
| 952 | error = device_create_file( | 1025 | error = device_create_file( |
| 953 | &device_clocksource, | 1026 | &device_clocksource, |
| 954 | &dev_attr_available_clocksource); | 1027 | &dev_attr_available_clocksource); |
