|
@@ -440,6 +440,11 @@ static int clocksource_watchdog_kthread(void *data)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static bool clocksource_is_watchdog(struct clocksource *cs)
|
|
|
+{
|
|
|
+ return cs == watchdog;
|
|
|
+}
|
|
|
+
|
|
|
#else /* CONFIG_CLOCKSOURCE_WATCHDOG */
|
|
|
|
|
|
static void clocksource_enqueue_watchdog(struct clocksource *cs)
|
|
@@ -451,6 +456,7 @@ static void clocksource_enqueue_watchdog(struct clocksource *cs)
|
|
|
static inline void clocksource_dequeue_watchdog(struct clocksource *cs) { }
|
|
|
static inline void clocksource_resume_watchdog(void) { }
|
|
|
static inline int clocksource_watchdog_kthread(void *data) { return 0; }
|
|
|
+static bool clocksource_is_watchdog(struct clocksource *cs) { return false; }
|
|
|
|
|
|
#endif /* CONFIG_CLOCKSOURCE_WATCHDOG */
|
|
|
|
|
@@ -628,6 +634,11 @@ static void clocksource_select(void)
|
|
|
return __clocksource_select(false);
|
|
|
}
|
|
|
|
|
|
+static void clocksource_select_fallback(void)
|
|
|
+{
|
|
|
+ return __clocksource_select(true);
|
|
|
+}
|
|
|
+
|
|
|
#else /* !CONFIG_ARCH_USES_GETTIMEOFFSET */
|
|
|
|
|
|
static inline void clocksource_select(void) { }
|
|
@@ -803,6 +814,29 @@ void clocksource_change_rating(struct clocksource *cs, int rating)
|
|
|
}
|
|
|
EXPORT_SYMBOL(clocksource_change_rating);
|
|
|
|
|
|
+/*
|
|
|
+ * Unbind clocksource @cs. Called with clocksource_mutex held
|
|
|
+ */
|
|
|
+static int clocksource_unbind(struct clocksource *cs)
|
|
|
+{
|
|
|
+ /*
|
|
|
+ * I really can't convince myself to support this on hardware
|
|
|
+ * designed by lobotomized monkeys.
|
|
|
+ */
|
|
|
+ if (clocksource_is_watchdog(cs))
|
|
|
+ return -EBUSY;
|
|
|
+
|
|
|
+ if (cs == curr_clocksource) {
|
|
|
+ /* Select and try to install a replacement clock source */
|
|
|
+ clocksource_select_fallback();
|
|
|
+ if (curr_clocksource == cs)
|
|
|
+ return -EBUSY;
|
|
|
+ }
|
|
|
+ clocksource_dequeue_watchdog(cs);
|
|
|
+ list_del_init(&cs->list);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* clocksource_unregister - remove a registered clocksource
|
|
|
* @cs: clocksource to be unregistered
|
|
@@ -883,6 +917,40 @@ static ssize_t sysfs_override_clocksource(struct device *dev,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * sysfs_unbind_current_clocksource - interface for manually unbinding clocksource
|
|
|
+ * @dev: unused
|
|
|
+ * @attr: unused
|
|
|
+ * @buf: unused
|
|
|
+ * @count: length of buffer
|
|
|
+ *
|
|
|
+ * Takes input from sysfs interface for manually unbinding a clocksource.
|
|
|
+ */
|
|
|
+static ssize_t sysfs_unbind_clocksource(struct device *dev,
|
|
|
+ struct device_attribute *attr,
|
|
|
+ const char *buf, size_t count)
|
|
|
+{
|
|
|
+ struct clocksource *cs;
|
|
|
+ char name[CS_NAME_LEN];
|
|
|
+ size_t ret;
|
|
|
+
|
|
|
+ ret = clocksource_get_uname(buf, name, count);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ ret = -ENODEV;
|
|
|
+ mutex_lock(&clocksource_mutex);
|
|
|
+ list_for_each_entry(cs, &clocksource_list, list) {
|
|
|
+ if (strcmp(cs->name, name))
|
|
|
+ continue;
|
|
|
+ ret = clocksource_unbind(cs);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ mutex_unlock(&clocksource_mutex);
|
|
|
+
|
|
|
+ return ret ? ret : count;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* sysfs_show_available_clocksources - sysfs interface for listing clocksource
|
|
|
* @dev: unused
|
|
@@ -925,6 +993,8 @@ sysfs_show_available_clocksources(struct device *dev,
|
|
|
static DEVICE_ATTR(current_clocksource, 0644, sysfs_show_current_clocksources,
|
|
|
sysfs_override_clocksource);
|
|
|
|
|
|
+static DEVICE_ATTR(unbind_clocksource, 0200, NULL, sysfs_unbind_clocksource);
|
|
|
+
|
|
|
static DEVICE_ATTR(available_clocksource, 0444,
|
|
|
sysfs_show_available_clocksources, NULL);
|
|
|
|
|
@@ -948,6 +1018,9 @@ static int __init init_clocksource_sysfs(void)
|
|
|
error = device_create_file(
|
|
|
&device_clocksource,
|
|
|
&dev_attr_current_clocksource);
|
|
|
+ if (!error)
|
|
|
+ error = device_create_file(&device_clocksource,
|
|
|
+ &dev_attr_unbind_clocksource);
|
|
|
if (!error)
|
|
|
error = device_create_file(
|
|
|
&device_clocksource,
|