clocksource: sanity check sysfs clocksource changes
john stultz [Thu, 22 Jan 2009 05:53:22 +0000 (22:53 -0700)]
Thomas, Andrew and Ingo pointed out that we don't have any safety checks
in the clocksource sysfs entries to make sure sysadmins don't try to
change the clocksource to a non high-res timer capable clocksource (such
as jiffies) when high-res timers (HRT) is enabled.  Doing so will likely
hang a system.

Correct this by filtering non HRT clocksources from available_clocksources
and not accepting non HRT clocksources with HRT enabled.

Signed-off-by: John Stultz <johnstul@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

include/linux/hrtimer.h
kernel/hrtimer.c
kernel/time/clocksource.c

index 0d2f7c8..58021b0 100644 (file)
@@ -305,7 +305,7 @@ static inline int hrtimer_is_hres_active(struct hrtimer *timer)
 
 extern ktime_t ktime_get(void);
 extern ktime_t ktime_get_real(void);
-
+extern int hrtimer_hres_active(void);
 
 DECLARE_PER_CPU(struct tick_device, tick_cpu_device);
 
index cb8a15c..1a70c18 100644 (file)
@@ -476,7 +476,7 @@ static inline int hrtimer_is_hres_enabled(void)
 /*
  * Is the high resolution mode active ?
  */
-static inline int hrtimer_hres_active(void)
+int hrtimer_hres_active(void)
 {
        return __get_cpu_var(hrtimer_bases).hres_active;
 }
@@ -704,7 +704,7 @@ static int hrtimer_switch_to_hres(void)
 
 #else
 
-static inline int hrtimer_hres_active(void) { return 0; }
+int hrtimer_hres_active(void) { return 0; }
 static inline int hrtimer_is_hres_enabled(void) { return 0; }
 static inline int hrtimer_switch_to_hres(void) { return 0; }
 static inline void hrtimer_force_reprogram(struct hrtimer_cpu_base *base) { }
index 80189f6..18b9f5d 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/module.h>
 #include <linux/sched.h> /* for spin_unlock_irq() using preempt_count() m68k */
 #include <linux/tick.h>
+#include <linux/hrtimer.h>
 
 void timecounter_init(struct timecounter *tc,
                      const struct cyclecounter *cc,
@@ -509,6 +510,18 @@ static ssize_t sysfs_override_clocksource(struct sys_device *dev,
                }
        }
 
+       /*
+        * Check to make sure we don't switch to a non-HRT usable
+        * clocksource if HRT is enabled and running
+        */
+       if (hrtimer_hres_active() &&
+           !(ovr->flags & CLOCK_SOURCE_VALID_FOR_HRES)) {
+               printk(KERN_WARNING "%s clocksource is not HRT compatible. "
+                       "Cannot switch while in HRT mode\n", ovr->name);
+               ovr = NULL;
+               override_name[0] = 0;
+       }
+
        /* Reselect, when the override name has changed */
        if (ovr != clocksource_override) {
                clocksource_override = ovr;
@@ -537,7 +550,10 @@ sysfs_show_available_clocksources(struct sys_device *dev,
 
        spin_lock_irq(&clocksource_lock);
        list_for_each_entry(src, &clocksource_list, list) {
-               count += snprintf(buf + count,
+               /* Don't show non-HRES clocksource if HRES is enabled */
+               if (!hrtimer_hres_active() ||
+                               (src->flags & CLOCK_SOURCE_VALID_FOR_HRES))
+                       count += snprintf(buf + count,
                                  max((ssize_t)PAGE_SIZE - count, (ssize_t)0),
                                  "%s ", src->name);
        }