diff options
Diffstat (limited to 'kernel/time')
-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); |