kcsan: Fix debugfs initcall return type
[linux-2.6-microblaze.git] / kernel / watchdog.c
index 107bc38..7c39790 100644 (file)
@@ -154,7 +154,11 @@ static void lockup_detector_update_enable(void)
 
 #ifdef CONFIG_SOFTLOCKUP_DETECTOR
 
-#define SOFTLOCKUP_RESET       ULONG_MAX
+/*
+ * Delay the soflockup report when running a known slow code.
+ * It does _not_ affect the timestamp of the last successdul reschedule.
+ */
+#define SOFTLOCKUP_DELAY_REPORT        ULONG_MAX
 
 #ifdef CONFIG_SMP
 int __read_mostly sysctl_softlockup_all_cpu_backtrace;
@@ -169,10 +173,12 @@ unsigned int __read_mostly softlockup_panic =
 static bool softlockup_initialized __read_mostly;
 static u64 __read_mostly sample_period;
 
+/* Timestamp taken after the last successful reschedule. */
 static DEFINE_PER_CPU(unsigned long, watchdog_touch_ts);
+/* Timestamp of the last softlockup report. */
+static DEFINE_PER_CPU(unsigned long, watchdog_report_ts);
 static DEFINE_PER_CPU(struct hrtimer, watchdog_hrtimer);
 static DEFINE_PER_CPU(bool, softlockup_touch_sync);
-static DEFINE_PER_CPU(bool, soft_watchdog_warn);
 static DEFINE_PER_CPU(unsigned long, hrtimer_interrupts);
 static DEFINE_PER_CPU(unsigned long, hrtimer_interrupts_saved);
 static unsigned long soft_lockup_nmi_warn;
@@ -235,10 +241,16 @@ static void set_sample_period(void)
        watchdog_update_hrtimer_threshold(sample_period);
 }
 
+static void update_report_ts(void)
+{
+       __this_cpu_write(watchdog_report_ts, get_timestamp());
+}
+
 /* Commands for resetting the watchdog */
-static void __touch_watchdog(void)
+static void update_touch_ts(void)
 {
        __this_cpu_write(watchdog_touch_ts, get_timestamp());
+       update_report_ts();
 }
 
 /**
@@ -252,10 +264,10 @@ static void __touch_watchdog(void)
 notrace void touch_softlockup_watchdog_sched(void)
 {
        /*
-        * Preemption can be enabled.  It doesn't matter which CPU's timestamp
-        * gets zeroed here, so use the raw_ operation.
+        * Preemption can be enabled.  It doesn't matter which CPU's watchdog
+        * report period gets restarted here, so use the raw_ operation.
         */
-       raw_cpu_write(watchdog_touch_ts, SOFTLOCKUP_RESET);
+       raw_cpu_write(watchdog_report_ts, SOFTLOCKUP_DELAY_REPORT);
 }
 
 notrace void touch_softlockup_watchdog(void)
@@ -279,7 +291,7 @@ void touch_all_softlockup_watchdogs(void)
         * the softlockup check.
         */
        for_each_cpu(cpu, &watchdog_allowed_mask) {
-               per_cpu(watchdog_touch_ts, cpu) = SOFTLOCKUP_RESET;
+               per_cpu(watchdog_report_ts, cpu) = SOFTLOCKUP_DELAY_REPORT;
                wq_watchdog_touch(cpu);
        }
 }
@@ -287,16 +299,16 @@ void touch_all_softlockup_watchdogs(void)
 void touch_softlockup_watchdog_sync(void)
 {
        __this_cpu_write(softlockup_touch_sync, true);
-       __this_cpu_write(watchdog_touch_ts, SOFTLOCKUP_RESET);
+       __this_cpu_write(watchdog_report_ts, SOFTLOCKUP_DELAY_REPORT);
 }
 
-static int is_softlockup(unsigned long touch_ts)
+static int is_softlockup(unsigned long touch_ts, unsigned long period_ts)
 {
        unsigned long now = get_timestamp();
 
        if ((watchdog_enabled & SOFT_WATCHDOG_ENABLED) && watchdog_thresh){
                /* Warn about unreasonable delays. */
-               if (time_after(now, touch_ts + get_softlockup_thresh()))
+               if (time_after(now, period_ts + get_softlockup_thresh()))
                        return now - touch_ts;
        }
        return 0;
@@ -332,7 +344,7 @@ static DEFINE_PER_CPU(struct cpu_stop_work, softlockup_stop_work);
  */
 static int softlockup_fn(void *data)
 {
-       __touch_watchdog();
+       update_touch_ts();
        complete(this_cpu_ptr(&softlockup_completion));
 
        return 0;
@@ -342,6 +354,7 @@ static int softlockup_fn(void *data)
 static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer)
 {
        unsigned long touch_ts = __this_cpu_read(watchdog_touch_ts);
+       unsigned long period_ts = __this_cpu_read(watchdog_report_ts);
        struct pt_regs *regs = get_irq_regs();
        int duration;
        int softlockup_all_cpu_backtrace = sysctl_softlockup_all_cpu_backtrace;
@@ -363,7 +376,15 @@ static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer)
        /* .. and repeat */
        hrtimer_forward_now(hrtimer, ns_to_ktime(sample_period));
 
-       if (touch_ts == SOFTLOCKUP_RESET) {
+       /*
+        * If a virtual machine is stopped by the host it can look to
+        * the watchdog like a soft lockup. Check to see if the host
+        * stopped the vm before we process the timestamps.
+        */
+       kvm_check_and_clear_guest_paused();
+
+       /* Reset the interval when touched by known problematic code. */
+       if (period_ts == SOFTLOCKUP_DELAY_REPORT) {
                if (unlikely(__this_cpu_read(softlockup_touch_sync))) {
                        /*
                         * If the time stamp was touched atomically
@@ -373,9 +394,7 @@ static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer)
                        sched_clock_tick();
                }
 
-               /* Clear the guest paused flag on watchdog reset */
-               kvm_check_and_clear_guest_paused();
-               __touch_watchdog();
+               update_report_ts();
                return HRTIMER_RESTART;
        }
 
@@ -385,31 +404,20 @@ static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer)
         * indicate it is getting cpu time.  If it hasn't then
         * this is a good indication some task is hogging the cpu
         */
-       duration = is_softlockup(touch_ts);
+       duration = is_softlockup(touch_ts, period_ts);
        if (unlikely(duration)) {
                /*
-                * If a virtual machine is stopped by the host it can look to
-                * the watchdog like a soft lockup, check to see if the host
-                * stopped the vm before we issue the warning
+                * Prevent multiple soft-lockup reports if one cpu is already
+                * engaged in dumping all cpu back traces.
                 */
-               if (kvm_check_and_clear_guest_paused())
-                       return HRTIMER_RESTART;
-
-               /* only warn once */
-               if (__this_cpu_read(soft_watchdog_warn) == true)
-                       return HRTIMER_RESTART;
-
                if (softlockup_all_cpu_backtrace) {
-                       /* Prevent multiple soft-lockup reports if one cpu is already
-                        * engaged in dumping cpu back traces
-                        */
-                       if (test_and_set_bit(0, &soft_lockup_nmi_warn)) {
-                               /* Someone else will report us. Let's give up */
-                               __this_cpu_write(soft_watchdog_warn, true);
+                       if (test_and_set_bit_lock(0, &soft_lockup_nmi_warn))
                                return HRTIMER_RESTART;
-                       }
                }
 
+               /* Start period for the next softlockup warning. */
+               update_report_ts();
+
                pr_emerg("BUG: soft lockup - CPU#%d stuck for %us! [%s:%d]\n",
                        smp_processor_id(), duration,
                        current->comm, task_pid_nr(current));
@@ -421,22 +429,14 @@ static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer)
                        dump_stack();
 
                if (softlockup_all_cpu_backtrace) {
-                       /* Avoid generating two back traces for current
-                        * given that one is already made above
-                        */
                        trigger_allbutself_cpu_backtrace();
-
-                       clear_bit(0, &soft_lockup_nmi_warn);
-                       /* Barrier to sync with other cpus */
-                       smp_mb__after_atomic();
+                       clear_bit_unlock(0, &soft_lockup_nmi_warn);
                }
 
                add_taint(TAINT_SOFTLOCKUP, LOCKDEP_STILL_OK);
                if (softlockup_panic)
                        panic("softlockup: hung tasks");
-               __this_cpu_write(soft_watchdog_warn, true);
-       } else
-               __this_cpu_write(soft_watchdog_warn, false);
+       }
 
        return HRTIMER_RESTART;
 }
@@ -461,7 +461,7 @@ static void watchdog_enable(unsigned int cpu)
                      HRTIMER_MODE_REL_PINNED_HARD);
 
        /* Initialize timestamp */
-       __touch_watchdog();
+       update_touch_ts();
        /* Enable the perf event */
        if (watchdog_enabled & NMI_WATCHDOG_ENABLED)
                watchdog_nmi_enable(cpu);