Merge branches 'x86-ras-for-linus', 'x86-uv-for-linus' and 'x86-vdso-for-linus' of...
[firefly-linux-kernel-4.4.55.git] / kernel / time / tick-sched.c
index f654a8a298fad5cac36465bdcb23fddeb854f2ef..7c1412ea2d29836c54bff9ea841172e2f0827fec 100644 (file)
@@ -295,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;
@@ -349,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;
@@ -375,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);
 
@@ -982,6 +996,10 @@ static void tick_nohz_handler(struct clock_event_device *dev)
        tick_sched_do_timer(now);
        tick_sched_handle(ts, regs);
 
+       /* No need to reprogram if we are running tickless  */
+       if (unlikely(ts->tick_stopped))
+               return;
+
        while (tick_nohz_reprogram(ts, now)) {
                now = ktime_get();
                tick_do_update_jiffies64(now);
@@ -1109,6 +1127,10 @@ static enum hrtimer_restart tick_sched_timer(struct hrtimer *timer)
        if (regs)
                tick_sched_handle(ts, regs);
 
+       /* No need to reprogram if we are in idle or full dynticks mode */
+       if (unlikely(ts->tick_stopped))
+               return HRTIMER_NORESTART;
+
        hrtimer_forward(timer, now, tick_period);
 
        return HRTIMER_RESTART;