aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/time
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/time')
-rw-r--r--kernel/time/clocksource.c73
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
443static 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
445static void clocksource_enqueue_watchdog(struct clocksource *cs) 450static void clocksource_enqueue_watchdog(struct clocksource *cs)
@@ -451,6 +456,7 @@ static void clocksource_enqueue_watchdog(struct clocksource *cs)
451static inline void clocksource_dequeue_watchdog(struct clocksource *cs) { } 456static inline void clocksource_dequeue_watchdog(struct clocksource *cs) { }
452static inline void clocksource_resume_watchdog(void) { } 457static inline void clocksource_resume_watchdog(void) { }
453static inline int clocksource_watchdog_kthread(void *data) { return 0; } 458static inline int clocksource_watchdog_kthread(void *data) { return 0; }
459static 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
637static 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
633static inline void clocksource_select(void) { } 644static inline void clocksource_select(void) { }
@@ -803,6 +814,29 @@ void clocksource_change_rating(struct clocksource *cs, int rating)
803} 814}
804EXPORT_SYMBOL(clocksource_change_rating); 815EXPORT_SYMBOL(clocksource_change_rating);
805 816
817/*
818 * Unbind clocksource @cs. Called with clocksource_mutex held
819 */
820static 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 */
929static 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,
925static DEVICE_ATTR(current_clocksource, 0644, sysfs_show_current_clocksources, 993static DEVICE_ATTR(current_clocksource, 0644, sysfs_show_current_clocksources,
926 sysfs_override_clocksource); 994 sysfs_override_clocksource);
927 995
996static DEVICE_ATTR(unbind_clocksource, 0200, NULL, sysfs_unbind_clocksource);
997
928static DEVICE_ATTR(available_clocksource, 0444, 998static 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);