]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/commitdiff
Merge branch 'core-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
authorLinus Torvalds <torvalds@linux-foundation.org>
Sun, 20 Aug 2017 15:54:30 +0000 (08:54 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sun, 20 Aug 2017 15:54:30 +0000 (08:54 -0700)
Pull watchdog fix from Thomas Gleixner:
 "A fix for the hardlockup watchdog to prevent false positives with
  extreme Turbo-Modes which make the perf/NMI watchdog fire faster than
  the hrtimer which is used to verify.

  Slightly larger than the minimal fix, which just would increase the
  hrtimer frequency, but comes with extra overhead of more watchdog
  timer interrupts and thread wakeups for all users.

  With this change we restrict the overhead to the extreme Turbo-Mode
  systems"

* 'core-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  kernel/watchdog: Prevent false positives with turbo modes

arch/x86/Kconfig
include/linux/nmi.h
kernel/watchdog.c
kernel/watchdog_hld.c
lib/Kconfig.debug

index 29a1bf85e5077f5cc179555f231986c85af8af9c..323cb065be5eda120b44dac79618a13301ece231 100644 (file)
@@ -100,6 +100,7 @@ config X86
        select GENERIC_STRNCPY_FROM_USER
        select GENERIC_STRNLEN_USER
        select GENERIC_TIME_VSYSCALL
+       select HARDLOCKUP_CHECK_TIMESTAMP       if X86_64
        select HAVE_ACPI_APEI                   if ACPI
        select HAVE_ACPI_APEI_NMI               if ACPI
        select HAVE_ALIGNED_STRUCT_PAGE         if SLUB
index 8aa01fd859fb84e5f64e9d7ffb0c20957afdfd2d..a36abe2da13e1a23edc8aa152205af143f658990 100644 (file)
@@ -168,6 +168,14 @@ extern int sysctl_hardlockup_all_cpu_backtrace;
 #define sysctl_softlockup_all_cpu_backtrace 0
 #define sysctl_hardlockup_all_cpu_backtrace 0
 #endif
+
+#if defined(CONFIG_HARDLOCKUP_CHECK_TIMESTAMP) && \
+    defined(CONFIG_HARDLOCKUP_DETECTOR)
+void watchdog_update_hrtimer_threshold(u64 period);
+#else
+static inline void watchdog_update_hrtimer_threshold(u64 period) { }
+#endif
+
 extern bool is_hardlockup(void);
 struct ctl_table;
 extern int proc_watchdog(struct ctl_table *, int ,
index 06d3389bca0df01c8a730f7b270767f6ed35a8a9..f5d52024f6b72a9d1354b1a44c12c3e3b6af06a9 100644 (file)
@@ -240,6 +240,7 @@ static void set_sample_period(void)
         * hardlockup detector generates a warning
         */
        sample_period = get_softlockup_thresh() * ((u64)NSEC_PER_SEC / 5);
+       watchdog_update_hrtimer_threshold(sample_period);
 }
 
 /* Commands for resetting the watchdog */
index 295a0d84934cb1d3a9a87abd4a8ac0f1d38656a5..3a09ea1b1d3d5e6e284d058052403ac1396804ca 100644 (file)
@@ -37,6 +37,62 @@ void arch_touch_nmi_watchdog(void)
 }
 EXPORT_SYMBOL(arch_touch_nmi_watchdog);
 
+#ifdef CONFIG_HARDLOCKUP_CHECK_TIMESTAMP
+static DEFINE_PER_CPU(ktime_t, last_timestamp);
+static DEFINE_PER_CPU(unsigned int, nmi_rearmed);
+static ktime_t watchdog_hrtimer_sample_threshold __read_mostly;
+
+void watchdog_update_hrtimer_threshold(u64 period)
+{
+       /*
+        * The hrtimer runs with a period of (watchdog_threshold * 2) / 5
+        *
+        * So it runs effectively with 2.5 times the rate of the NMI
+        * watchdog. That means the hrtimer should fire 2-3 times before
+        * the NMI watchdog expires. The NMI watchdog on x86 is based on
+        * unhalted CPU cycles, so if Turbo-Mode is enabled the CPU cycles
+        * might run way faster than expected and the NMI fires in a
+        * smaller period than the one deduced from the nominal CPU
+        * frequency. Depending on the Turbo-Mode factor this might be fast
+        * enough to get the NMI period smaller than the hrtimer watchdog
+        * period and trigger false positives.
+        *
+        * The sample threshold is used to check in the NMI handler whether
+        * the minimum time between two NMI samples has elapsed. That
+        * prevents false positives.
+        *
+        * Set this to 4/5 of the actual watchdog threshold period so the
+        * hrtimer is guaranteed to fire at least once within the real
+        * watchdog threshold.
+        */
+       watchdog_hrtimer_sample_threshold = period * 2;
+}
+
+static bool watchdog_check_timestamp(void)
+{
+       ktime_t delta, now = ktime_get_mono_fast_ns();
+
+       delta = now - __this_cpu_read(last_timestamp);
+       if (delta < watchdog_hrtimer_sample_threshold) {
+               /*
+                * If ktime is jiffies based, a stalled timer would prevent
+                * jiffies from being incremented and the filter would look
+                * at a stale timestamp and never trigger.
+                */
+               if (__this_cpu_inc_return(nmi_rearmed) < 10)
+                       return false;
+       }
+       __this_cpu_write(nmi_rearmed, 0);
+       __this_cpu_write(last_timestamp, now);
+       return true;
+}
+#else
+static inline bool watchdog_check_timestamp(void)
+{
+       return true;
+}
+#endif
+
 static struct perf_event_attr wd_hw_attr = {
        .type           = PERF_TYPE_HARDWARE,
        .config         = PERF_COUNT_HW_CPU_CYCLES,
@@ -61,6 +117,9 @@ static void watchdog_overflow_callback(struct perf_event *event,
                return;
        }
 
+       if (!watchdog_check_timestamp())
+               return;
+
        /* check for a hardlockup
         * This is done by making sure our timer interrupt
         * is incrementing.  The timer interrupt should have
index 98fe715522e8d1834083e608d32a78ed0600deb9..c617b9d1d6cb687c93fb0c65e8d05424f5dd3c43 100644 (file)
@@ -797,6 +797,13 @@ config HARDLOCKUP_DETECTOR_PERF
        bool
        select SOFTLOCKUP_DETECTOR
 
+#
+# Enables a timestamp based low pass filter to compensate for perf based
+# hard lockup detection which runs too fast due to turbo modes.
+#
+config HARDLOCKUP_CHECK_TIMESTAMP
+       bool
+
 #
 # arch/ can define HAVE_HARDLOCKUP_DETECTOR_ARCH to provide their own hard
 # lockup detector rather than the perf based detector.