Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rw/uml
[firefly-linux-kernel-4.4.55.git] / kernel / time / clocksource.c
index c9583382141a439ce10980bfc399792e8c6f8423..50a8736757f3b470c5d0d393be03cfbf05f5df03 100644 (file)
@@ -31,6 +31,8 @@
 #include <linux/tick.h>
 #include <linux/kthread.h>
 
+#include "tick-internal.h"
+
 void timecounter_init(struct timecounter *tc,
                      const struct cyclecounter *cc,
                      u64 start_tstamp)
@@ -174,11 +176,12 @@ clocks_calc_mult_shift(u32 *mult, u32 *shift, u32 from, u32 to, u32 maxsec)
 static struct clocksource *curr_clocksource;
 static LIST_HEAD(clocksource_list);
 static DEFINE_MUTEX(clocksource_mutex);
-static char override_name[32];
+static char override_name[CS_NAME_LEN];
 static int finished_booting;
 
 #ifdef CONFIG_CLOCKSOURCE_WATCHDOG
 static void clocksource_watchdog_work(struct work_struct *work);
+static void clocksource_select(void);
 
 static LIST_HEAD(watchdog_list);
 static struct clocksource *watchdog;
@@ -299,13 +302,30 @@ static void clocksource_watchdog(unsigned long data)
                if (!(cs->flags & CLOCK_SOURCE_VALID_FOR_HRES) &&
                    (cs->flags & CLOCK_SOURCE_IS_CONTINUOUS) &&
                    (watchdog->flags & CLOCK_SOURCE_IS_CONTINUOUS)) {
+                       /* Mark it valid for high-res. */
                        cs->flags |= CLOCK_SOURCE_VALID_FOR_HRES;
+
+                       /*
+                        * clocksource_done_booting() will sort it if
+                        * finished_booting is not set yet.
+                        */
+                       if (!finished_booting)
+                               continue;
+
                        /*
-                        * We just marked the clocksource as highres-capable,
-                        * notify the rest of the system as well so that we
-                        * transition into high-res mode:
+                        * If this is not the current clocksource let
+                        * the watchdog thread reselect it. Due to the
+                        * change to high res this clocksource might
+                        * be preferred now. If it is the current
+                        * clocksource let the tick code know about
+                        * that change.
                         */
-                       tick_clock_notify();
+                       if (cs != curr_clocksource) {
+                               cs->flags |= CLOCK_SOURCE_RESELECT;
+                               schedule_work(&watchdog_work);
+                       } else {
+                               tick_clock_notify();
+                       }
                }
        }
 
@@ -388,44 +408,39 @@ static void clocksource_enqueue_watchdog(struct clocksource *cs)
 
 static void clocksource_dequeue_watchdog(struct clocksource *cs)
 {
-       struct clocksource *tmp;
        unsigned long flags;
 
        spin_lock_irqsave(&watchdog_lock, flags);
-       if (cs->flags & CLOCK_SOURCE_MUST_VERIFY) {
-               /* cs is a watched clocksource. */
-               list_del_init(&cs->wd_list);
-       } else if (cs == watchdog) {
-               /* Reset watchdog cycles */
-               clocksource_reset_watchdog();
-               /* Current watchdog is removed. Find an alternative. */
-               watchdog = NULL;
-               list_for_each_entry(tmp, &clocksource_list, list) {
-                       if (tmp == cs || tmp->flags & CLOCK_SOURCE_MUST_VERIFY)
-                               continue;
-                       if (!watchdog || tmp->rating > watchdog->rating)
-                               watchdog = tmp;
+       if (cs != watchdog) {
+               if (cs->flags & CLOCK_SOURCE_MUST_VERIFY) {
+                       /* cs is a watched clocksource. */
+                       list_del_init(&cs->wd_list);
+                       /* Check if the watchdog timer needs to be stopped. */
+                       clocksource_stop_watchdog();
                }
        }
-       cs->flags &= ~CLOCK_SOURCE_WATCHDOG;
-       /* Check if the watchdog timer needs to be stopped. */
-       clocksource_stop_watchdog();
        spin_unlock_irqrestore(&watchdog_lock, flags);
 }
 
-static int clocksource_watchdog_kthread(void *data)
+static int __clocksource_watchdog_kthread(void)
 {
        struct clocksource *cs, *tmp;
        unsigned long flags;
        LIST_HEAD(unstable);
+       int select = 0;
 
-       mutex_lock(&clocksource_mutex);
        spin_lock_irqsave(&watchdog_lock, flags);
-       list_for_each_entry_safe(cs, tmp, &watchdog_list, wd_list)
+       list_for_each_entry_safe(cs, tmp, &watchdog_list, wd_list) {
                if (cs->flags & CLOCK_SOURCE_UNSTABLE) {
                        list_del_init(&cs->wd_list);
                        list_add(&cs->wd_list, &unstable);
+                       select = 1;
                }
+               if (cs->flags & CLOCK_SOURCE_RESELECT) {
+                       cs->flags &= ~CLOCK_SOURCE_RESELECT;
+                       select = 1;
+               }
+       }
        /* Check if the watchdog timer needs to be stopped. */
        clocksource_stop_watchdog();
        spin_unlock_irqrestore(&watchdog_lock, flags);
@@ -435,10 +450,23 @@ static int clocksource_watchdog_kthread(void *data)
                list_del_init(&cs->wd_list);
                __clocksource_change_rating(cs, 0);
        }
+       return select;
+}
+
+static int clocksource_watchdog_kthread(void *data)
+{
+       mutex_lock(&clocksource_mutex);
+       if (__clocksource_watchdog_kthread())
+               clocksource_select();
        mutex_unlock(&clocksource_mutex);
        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)
@@ -449,7 +477,8 @@ 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 inline int __clocksource_watchdog_kthread(void) { return 0; }
+static bool clocksource_is_watchdog(struct clocksource *cs) { return false; }
 
 #endif /* CONFIG_CLOCKSOURCE_WATCHDOG */
 
@@ -553,24 +582,42 @@ static u64 clocksource_max_deferment(struct clocksource *cs)
 
 #ifndef CONFIG_ARCH_USES_GETTIMEOFFSET
 
-/**
- * clocksource_select - Select the best clocksource available
- *
- * Private function. Must hold clocksource_mutex when called.
- *
- * Select the clocksource with the best rating, or the clocksource,
- * which is selected by userspace override.
- */
-static void clocksource_select(void)
+static struct clocksource *clocksource_find_best(bool oneshot, bool skipcur)
 {
-       struct clocksource *best, *cs;
+       struct clocksource *cs;
 
        if (!finished_booting || list_empty(&clocksource_list))
+               return NULL;
+
+       /*
+        * We pick the clocksource with the highest rating. If oneshot
+        * mode is active, we pick the highres valid clocksource with
+        * the best rating.
+        */
+       list_for_each_entry(cs, &clocksource_list, list) {
+               if (skipcur && cs == curr_clocksource)
+                       continue;
+               if (oneshot && !(cs->flags & CLOCK_SOURCE_VALID_FOR_HRES))
+                       continue;
+               return cs;
+       }
+       return NULL;
+}
+
+static void __clocksource_select(bool skipcur)
+{
+       bool oneshot = tick_oneshot_mode_active();
+       struct clocksource *best, *cs;
+
+       /* Find the best suitable clocksource */
+       best = clocksource_find_best(oneshot, skipcur);
+       if (!best)
                return;
-       /* First clocksource on the list has the best rating. */
-       best = list_first_entry(&clocksource_list, struct clocksource, list);
+
        /* Check for the override clocksource. */
        list_for_each_entry(cs, &clocksource_list, list) {
+               if (skipcur && cs == curr_clocksource)
+                       continue;
                if (strcmp(cs->name, override_name) != 0)
                        continue;
                /*
@@ -578,8 +625,7 @@ static void clocksource_select(void)
                 * capable clocksource if the tick code is in oneshot
                 * mode (highres or nohz)
                 */
-               if (!(cs->flags & CLOCK_SOURCE_VALID_FOR_HRES) &&
-                   tick_oneshot_mode_active()) {
+               if (!(cs->flags & CLOCK_SOURCE_VALID_FOR_HRES) && oneshot) {
                        /* Override clocksource cannot be used. */
                        printk(KERN_WARNING "Override clocksource %s is not "
                               "HRT compatible. Cannot switch while in "
@@ -590,16 +636,35 @@ static void clocksource_select(void)
                        best = cs;
                break;
        }
-       if (curr_clocksource != best) {
-               printk(KERN_INFO "Switching to clocksource %s\n", best->name);
+
+       if (curr_clocksource != best && !timekeeping_notify(best)) {
+               pr_info("Switched to clocksource %s\n", best->name);
                curr_clocksource = best;
-               timekeeping_notify(curr_clocksource);
        }
 }
 
+/**
+ * clocksource_select - Select the best clocksource available
+ *
+ * Private function. Must hold clocksource_mutex when called.
+ *
+ * Select the clocksource with the best rating, or the clocksource,
+ * which is selected by userspace override.
+ */
+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) { }
+static inline void clocksource_select_fallback(void) { }
 
 #endif
 
@@ -614,16 +679,11 @@ static int __init clocksource_done_booting(void)
 {
        mutex_lock(&clocksource_mutex);
        curr_clocksource = clocksource_default_clock();
-       mutex_unlock(&clocksource_mutex);
-
        finished_booting = 1;
-
        /*
         * Run the watchdog first to eliminate unstable clock sources
         */
-       clocksource_watchdog_kthread(NULL);
-
-       mutex_lock(&clocksource_mutex);
+       __clocksource_watchdog_kthread();
        clocksource_select();
        mutex_unlock(&clocksource_mutex);
        return 0;
@@ -756,7 +816,6 @@ static void __clocksource_change_rating(struct clocksource *cs, int rating)
        list_del(&cs->list);
        cs->rating = rating;
        clocksource_enqueue(cs);
-       clocksource_select();
 }
 
 /**
@@ -768,21 +827,47 @@ void clocksource_change_rating(struct clocksource *cs, int rating)
 {
        mutex_lock(&clocksource_mutex);
        __clocksource_change_rating(cs, rating);
+       clocksource_select();
        mutex_unlock(&clocksource_mutex);
 }
 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
  */
-void clocksource_unregister(struct clocksource *cs)
+int clocksource_unregister(struct clocksource *cs)
 {
+       int ret = 0;
+
        mutex_lock(&clocksource_mutex);
-       clocksource_dequeue_watchdog(cs);
-       list_del(&cs->list);
-       clocksource_select();
+       if (!list_empty(&cs->list))
+               ret = clocksource_unbind(cs);
        mutex_unlock(&clocksource_mutex);
+       return ret;
 }
 EXPORT_SYMBOL(clocksource_unregister);
 
@@ -808,6 +893,23 @@ sysfs_show_current_clocksources(struct device *dev,
        return count;
 }
 
+size_t sysfs_get_uname(const char *buf, char *dst, size_t cnt)
+{
+       size_t ret = cnt;
+
+       /* strings from sysfs write are not 0 terminated! */
+       if (!cnt || cnt >= CS_NAME_LEN)
+               return -EINVAL;
+
+       /* strip of \n: */
+       if (buf[cnt-1] == '\n')
+               cnt--;
+       if (cnt > 0)
+               memcpy(dst, buf, cnt);
+       dst[cnt] = 0;
+       return ret;
+}
+
 /**
  * sysfs_override_clocksource - interface for manually overriding clocksource
  * @dev:       unused
@@ -822,28 +924,53 @@ static ssize_t sysfs_override_clocksource(struct device *dev,
                                          struct device_attribute *attr,
                                          const char *buf, size_t count)
 {
-       size_t ret = count;
-
-       /* strings from sysfs write are not 0 terminated! */
-       if (count >= sizeof(override_name))
-               return -EINVAL;
-
-       /* strip of \n: */
-       if (buf[count-1] == '\n')
-               count--;
+       size_t ret;
 
        mutex_lock(&clocksource_mutex);
 
-       if (count > 0)
-               memcpy(override_name, buf, count);
-       override_name[count] = 0;
-       clocksource_select();
+       ret = sysfs_get_uname(buf, override_name, count);
+       if (ret >= 0)
+               clocksource_select();
 
        mutex_unlock(&clocksource_mutex);
 
        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 = sysfs_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
@@ -886,6 +1013,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);
 
@@ -909,6 +1038,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,