watchdog: add watchdog_cpumask sysctl to assist nohz
[cascardo/linux.git] / kernel / watchdog.c
index 581a68a..a6ffa43 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/sysctl.h>
 #include <linux/smpboot.h>
 #include <linux/sched/rt.h>
+#include <linux/tick.h>
 
 #include <asm/irq_regs.h>
 #include <linux/kvm_para.h>
@@ -58,6 +59,12 @@ int __read_mostly sysctl_softlockup_all_cpu_backtrace;
 #else
 #define sysctl_softlockup_all_cpu_backtrace 0
 #endif
+static struct cpumask watchdog_cpumask __read_mostly;
+unsigned long *watchdog_cpumask_bits = cpumask_bits(&watchdog_cpumask);
+
+/* Helper for online, unparked cpus. */
+#define for_each_watchdog_cpu(cpu) \
+       for_each_cpu_and((cpu), cpu_online_mask, &watchdog_cpumask)
 
 static int __read_mostly watchdog_running;
 static u64 __read_mostly sample_period;
@@ -207,7 +214,7 @@ void touch_all_softlockup_watchdogs(void)
         * do we care if a 0 races with a timestamp?
         * all it means is the softlock check starts one cycle later
         */
-       for_each_online_cpu(cpu)
+       for_each_watchdog_cpu(cpu)
                per_cpu(watchdog_touch_ts, cpu) = 0;
 }
 
@@ -616,7 +623,7 @@ void watchdog_nmi_enable_all(void)
                goto unlock;
 
        get_online_cpus();
-       for_each_online_cpu(cpu)
+       for_each_watchdog_cpu(cpu)
                watchdog_nmi_enable(cpu);
        put_online_cpus();
 
@@ -634,7 +641,7 @@ void watchdog_nmi_disable_all(void)
                goto unlock;
 
        get_online_cpus();
-       for_each_online_cpu(cpu)
+       for_each_watchdog_cpu(cpu)
                watchdog_nmi_disable(cpu);
        put_online_cpus();
 
@@ -696,7 +703,7 @@ static void update_watchdog_all_cpus(void)
        int cpu;
 
        get_online_cpus();
-       for_each_online_cpu(cpu)
+       for_each_watchdog_cpu(cpu)
                update_watchdog(cpu);
        put_online_cpus();
 }
@@ -709,8 +716,12 @@ static int watchdog_enable_all_cpus(void)
                err = smpboot_register_percpu_thread(&watchdog_threads);
                if (err)
                        pr_err("Failed to create watchdog threads, disabled\n");
-               else
+               else {
+                       if (smpboot_update_cpumask_percpu_thread(
+                                   &watchdog_threads, &watchdog_cpumask))
+                               pr_err("Failed to set cpumask for watchdog threads\n");
                        watchdog_running = 1;
+               }
        } else {
                /*
                 * Enable/disable the lockup detectors or
@@ -879,12 +890,58 @@ out:
        mutex_unlock(&watchdog_proc_mutex);
        return err;
 }
+
+/*
+ * The cpumask is the mask of possible cpus that the watchdog can run
+ * on, not the mask of cpus it is actually running on.  This allows the
+ * user to specify a mask that will include cpus that have not yet
+ * been brought online, if desired.
+ */
+int proc_watchdog_cpumask(struct ctl_table *table, int write,
+                         void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+       int err;
+
+       mutex_lock(&watchdog_proc_mutex);
+       err = proc_do_large_bitmap(table, write, buffer, lenp, ppos);
+       if (!err && write) {
+               /* Remove impossible cpus to keep sysctl output cleaner. */
+               cpumask_and(&watchdog_cpumask, &watchdog_cpumask,
+                           cpu_possible_mask);
+
+               if (watchdog_running) {
+                       /*
+                        * Failure would be due to being unable to allocate
+                        * a temporary cpumask, so we are likely not in a
+                        * position to do much else to make things better.
+                        */
+                       if (smpboot_update_cpumask_percpu_thread(
+                                   &watchdog_threads, &watchdog_cpumask) != 0)
+                               pr_err("cpumask update failed\n");
+               }
+       }
+       mutex_unlock(&watchdog_proc_mutex);
+       return err;
+}
+
 #endif /* CONFIG_SYSCTL */
 
 void __init lockup_detector_init(void)
 {
        set_sample_period();
 
+#ifdef CONFIG_NO_HZ_FULL
+       if (tick_nohz_full_enabled()) {
+               if (!cpumask_empty(tick_nohz_full_mask))
+                       pr_info("Disabling watchdog on nohz_full cores by default\n");
+               cpumask_andnot(&watchdog_cpumask, cpu_possible_mask,
+                              tick_nohz_full_mask);
+       } else
+               cpumask_copy(&watchdog_cpumask, cpu_possible_mask);
+#else
+       cpumask_copy(&watchdog_cpumask, cpu_possible_mask);
+#endif
+
        if (watchdog_enabled)
                watchdog_enable_all_cpus();
 }