aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2013-04-25 16:31:46 -0400
committerThomas Gleixner <tglx@linutronix.de>2013-05-16 05:09:15 -0400
commit7eaeb34305dee26634f7c98ae62646da5cebe91d (patch)
tree90651274a29cbb0e17d7655ca63a15aa5e7edc30
parent29b5407819f59731c9423238fae03b756822708c (diff)
clocksource: Provide unbind interface in sysfs
With the module refcount held for the current clocksource there is no way to unload the module. Provide a sysfs interface which allows to unbind the clocksource. One could argue that the clocksource override could be (ab)used to do so, but the clocksource override cannot be used from the kernel itself, while an unbind function can be used to programmatically check whether a clocksource can be shutdown or not. The unbind functionality uses the new skip current feature of clocksource_select and verifies that a fallback clocksource has been installed. If the clocksource which should be unbound is the current clocksource and no fallback can be found, unbind returns -EBUSY. This does not support the unbinding of a clocksource which is used as the watchdog clocksource. No point in fostering crappy hardware. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Cc: John Stultz <john.stultz@linaro.org> Cc: Magnus Damm <magnus.damm@gmail.com> Link: http://lkml.kernel.org/r/20130425143435.964218245@linutronix.de Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-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);