nohz: nohz full depends on irq work self IPI support
[sfrench/cifs-2.6.git] / kernel / time / tick-sched.c
index 99aa6ee3908fbbff83923b2a8bb4c72b06e3f281..5a9ff243588cc20a4019d4af496f9a9893d6cb8a 100644 (file)
@@ -224,6 +224,20 @@ static DEFINE_PER_CPU(struct irq_work, nohz_full_kick_work) = {
        .func = nohz_full_kick_work_func,
 };
 
+/*
+ * Kick this CPU if it's full dynticks in order to force it to
+ * re-evaluate its dependency on the tick and restart it if necessary.
+ * This kick, unlike tick_nohz_full_kick_cpu() and tick_nohz_full_kick_all(),
+ * is NMI safe.
+ */
+void tick_nohz_full_kick(void)
+{
+       if (!tick_nohz_full_cpu(smp_processor_id()))
+               return;
+
+       irq_work_queue(&__get_cpu_var(nohz_full_kick_work));
+}
+
 /*
  * Kick the CPU if it's full dynticks in order to force it to
  * re-evaluate its dependency on the tick and restart it if necessary.
@@ -281,22 +295,12 @@ out:
 /* Parse the boot-time nohz CPU list from the kernel parameters. */
 static int __init tick_nohz_full_setup(char *str)
 {
-       int cpu;
-
        alloc_bootmem_cpumask_var(&tick_nohz_full_mask);
-       alloc_bootmem_cpumask_var(&housekeeping_mask);
        if (cpulist_parse(str, tick_nohz_full_mask) < 0) {
                pr_warning("NOHZ: Incorrect nohz_full cpumask\n");
+               free_bootmem_cpumask_var(tick_nohz_full_mask);
                return 1;
        }
-
-       cpu = smp_processor_id();
-       if (cpumask_test_cpu(cpu, tick_nohz_full_mask)) {
-               pr_warning("NO_HZ: Clearing %d from nohz_full range for timekeeping\n", cpu);
-               cpumask_clear_cpu(cpu, tick_nohz_full_mask);
-       }
-       cpumask_andnot(housekeeping_mask,
-                      cpu_possible_mask, tick_nohz_full_mask);
        tick_nohz_full_running = true;
 
        return 1;
@@ -335,18 +339,11 @@ static int tick_nohz_init_all(void)
 
 #ifdef CONFIG_NO_HZ_FULL_ALL
        if (!alloc_cpumask_var(&tick_nohz_full_mask, GFP_KERNEL)) {
-               pr_err("NO_HZ: Can't allocate full dynticks cpumask\n");
-               return err;
-       }
-       if (!alloc_cpumask_var(&housekeeping_mask, GFP_KERNEL)) {
-               pr_err("NO_HZ: Can't allocate not-full dynticks cpumask\n");
+               WARN(1, "NO_HZ: Can't allocate full dynticks cpumask\n");
                return err;
        }
        err = 0;
        cpumask_setall(tick_nohz_full_mask);
-       cpumask_clear_cpu(smp_processor_id(), tick_nohz_full_mask);
-       cpumask_clear(housekeeping_mask);
-       cpumask_set_cpu(smp_processor_id(), housekeeping_mask);
        tick_nohz_full_running = true;
 #endif
        return err;
@@ -361,6 +358,37 @@ void __init tick_nohz_init(void)
                        return;
        }
 
+       if (!alloc_cpumask_var(&housekeeping_mask, GFP_KERNEL)) {
+               WARN(1, "NO_HZ: Can't allocate not-full dynticks cpumask\n");
+               cpumask_clear(tick_nohz_full_mask);
+               tick_nohz_full_running = false;
+               return;
+       }
+
+       /*
+        * Full dynticks uses irq work to drive the tick rescheduling on safe
+        * locking contexts. But then we need irq work to raise its own
+        * interrupts to avoid circular dependency on the tick
+        */
+       if (!arch_irq_work_has_interrupt()) {
+               pr_warning("NO_HZ: Can't run full dynticks because arch doesn't "
+                          "support irq work self-IPIs\n");
+               cpumask_clear(tick_nohz_full_mask);
+               cpumask_copy(housekeeping_mask, cpu_possible_mask);
+               tick_nohz_full_running = false;
+               return;
+       }
+
+       cpu = smp_processor_id();
+
+       if (cpumask_test_cpu(cpu, tick_nohz_full_mask)) {
+               pr_warning("NO_HZ: Clearing %d from nohz_full range for timekeeping\n", cpu);
+               cpumask_clear_cpu(cpu, tick_nohz_full_mask);
+       }
+
+       cpumask_andnot(housekeeping_mask,
+                      cpu_possible_mask, tick_nohz_full_mask);
+
        for_each_cpu(cpu, tick_nohz_full_mask)
                context_tracking_cpu_set(cpu);