]> Pileus Git - ~andy/linux/blobdiff - kernel/time/tick-sched.c
tick/timekeeping: Call update_wall_time outside the jiffies lock
[~andy/linux] / kernel / time / tick-sched.c
index e8a1516cc0a36d3c247d2bd4a18ef6d69f17b42c..c58b03d89951fe72b923b7713b644fe2b0bb81d9 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/irq_work.h>
 #include <linux/posix-timers.h>
 #include <linux/perf_event.h>
+#include <linux/context_tracking.h>
 
 #include <asm/irq_regs.h>
 
@@ -85,6 +86,7 @@ static void tick_do_update_jiffies64(ktime_t now)
                tick_next_period = ktime_add(last_jiffies_update, tick_period);
        }
        write_sequnlock(&jiffies_lock);
+       update_wall_time();
 }
 
 /*
@@ -148,8 +150,8 @@ static void tick_sched_handle(struct tick_sched *ts, struct pt_regs *regs)
 }
 
 #ifdef CONFIG_NO_HZ_FULL
-static cpumask_var_t nohz_full_mask;
-bool have_nohz_full_mask;
+cpumask_var_t tick_nohz_full_mask;
+bool tick_nohz_full_running;
 
 static bool can_stop_full_tick(void)
 {
@@ -182,7 +184,7 @@ static bool can_stop_full_tick(void)
                 * Don't allow the user to think they can get
                 * full NO_HZ with this machine.
                 */
-               WARN_ONCE(have_nohz_full_mask,
+               WARN_ONCE(tick_nohz_full_running,
                          "NO_HZ FULL will not work with unstable sched clock");
                return false;
        }
@@ -197,7 +199,7 @@ static void tick_nohz_restart_sched_tick(struct tick_sched *ts, ktime_t now);
  * Re-evaluate the need for the tick on the current CPU
  * and restart it if necessary.
  */
-void tick_nohz_full_check(void)
+void __tick_nohz_full_check(void)
 {
        struct tick_sched *ts = &__get_cpu_var(tick_cpu_sched);
 
@@ -211,7 +213,7 @@ void tick_nohz_full_check(void)
 
 static void nohz_full_kick_work_func(struct irq_work *work)
 {
-       tick_nohz_full_check();
+       __tick_nohz_full_check();
 }
 
 static DEFINE_PER_CPU(struct irq_work, nohz_full_kick_work) = {
@@ -230,7 +232,7 @@ void tick_nohz_full_kick(void)
 
 static void nohz_full_kick_ipi(void *info)
 {
-       tick_nohz_full_check();
+       __tick_nohz_full_check();
 }
 
 /*
@@ -239,12 +241,13 @@ static void nohz_full_kick_ipi(void *info)
  */
 void tick_nohz_full_kick_all(void)
 {
-       if (!have_nohz_full_mask)
+       if (!tick_nohz_full_running)
                return;
 
        preempt_disable();
-       smp_call_function_many(nohz_full_mask,
+       smp_call_function_many(tick_nohz_full_mask,
                               nohz_full_kick_ipi, NULL, false);
+       tick_nohz_full_kick();
        preempt_enable();
 }
 
@@ -253,7 +256,7 @@ void tick_nohz_full_kick_all(void)
  * It might need the tick due to per task/process properties:
  * perf events, posix cpu timers, ...
  */
-void tick_nohz_task_switch(struct task_struct *tsk)
+void __tick_nohz_task_switch(struct task_struct *tsk)
 {
        unsigned long flags;
 
@@ -269,31 +272,23 @@ out:
        local_irq_restore(flags);
 }
 
-int tick_nohz_full_cpu(int cpu)
-{
-       if (!have_nohz_full_mask)
-               return 0;
-
-       return cpumask_test_cpu(cpu, nohz_full_mask);
-}
-
 /* 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(&nohz_full_mask);
-       if (cpulist_parse(str, nohz_full_mask) < 0) {
+       alloc_bootmem_cpumask_var(&tick_nohz_full_mask);
+       if (cpulist_parse(str, tick_nohz_full_mask) < 0) {
                pr_warning("NOHZ: Incorrect nohz_full cpumask\n");
                return 1;
        }
 
        cpu = smp_processor_id();
-       if (cpumask_test_cpu(cpu, nohz_full_mask)) {
+       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, nohz_full_mask);
+               cpumask_clear_cpu(cpu, tick_nohz_full_mask);
        }
-       have_nohz_full_mask = true;
+       tick_nohz_full_running = true;
 
        return 1;
 }
@@ -311,7 +306,7 @@ static int tick_nohz_cpu_down_callback(struct notifier_block *nfb,
                 * If we handle the timekeeping duty for full dynticks CPUs,
                 * we can't safely shutdown that CPU.
                 */
-               if (have_nohz_full_mask && tick_do_timer_cpu == cpu)
+               if (tick_nohz_full_running && tick_do_timer_cpu == cpu)
                        return NOTIFY_BAD;
                break;
        }
@@ -330,31 +325,34 @@ static int tick_nohz_init_all(void)
        int err = -1;
 
 #ifdef CONFIG_NO_HZ_FULL_ALL
-       if (!alloc_cpumask_var(&nohz_full_mask, GFP_KERNEL)) {
+       if (!alloc_cpumask_var(&tick_nohz_full_mask, GFP_KERNEL)) {
                pr_err("NO_HZ: Can't allocate full dynticks cpumask\n");
                return err;
        }
        err = 0;
-       cpumask_setall(nohz_full_mask);
-       cpumask_clear_cpu(smp_processor_id(), nohz_full_mask);
-       have_nohz_full_mask = true;
+       cpumask_setall(tick_nohz_full_mask);
+       cpumask_clear_cpu(smp_processor_id(), tick_nohz_full_mask);
+       tick_nohz_full_running = true;
 #endif
        return err;
 }
 
 void __init tick_nohz_init(void)
 {
-       if (!have_nohz_full_mask) {
+       int cpu;
+
+       if (!tick_nohz_full_running) {
                if (tick_nohz_init_all() < 0)
                        return;
        }
 
+       for_each_cpu(cpu, tick_nohz_full_mask)
+               context_tracking_cpu_set(cpu);
+
        cpu_notifier(tick_nohz_cpu_down_callback, 0);
-       cpulist_scnprintf(nohz_full_buf, sizeof(nohz_full_buf), nohz_full_mask);
+       cpulist_scnprintf(nohz_full_buf, sizeof(nohz_full_buf), tick_nohz_full_mask);
        pr_info("NO_HZ: Full dynticks CPUs: %s.\n", nohz_full_buf);
 }
-#else
-#define have_nohz_full_mask (0)
 #endif
 
 /*
@@ -394,11 +392,9 @@ __setup("nohz=", setup_tick_nohz);
  */
 static void tick_nohz_update_jiffies(ktime_t now)
 {
-       int cpu = smp_processor_id();
-       struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu);
        unsigned long flags;
 
-       ts->idle_waketime = now;
+       __this_cpu_write(tick_cpu_sched.idle_waketime, now);
 
        local_irq_save(flags);
        tick_do_update_jiffies64(now);
@@ -429,17 +425,15 @@ update_ts_time_stats(int cpu, struct tick_sched *ts, ktime_t now, u64 *last_upda
 
 }
 
-static void tick_nohz_stop_idle(int cpu, ktime_t now)
+static void tick_nohz_stop_idle(struct tick_sched *ts, ktime_t now)
 {
-       struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu);
-
-       update_ts_time_stats(cpu, ts, now, NULL);
+       update_ts_time_stats(smp_processor_id(), ts, now, NULL);
        ts->idle_active = 0;
 
        sched_clock_idle_wakeup_event(0);
 }
 
-static ktime_t tick_nohz_start_idle(int cpu, struct tick_sched *ts)
+static ktime_t tick_nohz_start_idle(struct tick_sched *ts)
 {
        ktime_t now = ktime_get();
 
@@ -732,7 +726,7 @@ static bool can_stop_idle_tick(int cpu, struct tick_sched *ts)
                return false;
        }
 
-       if (have_nohz_full_mask) {
+       if (tick_nohz_full_enabled()) {
                /*
                 * Keep the tick alive to guarantee timekeeping progression
                 * if there are full dynticks CPUs around
@@ -755,7 +749,7 @@ static void __tick_nohz_idle_enter(struct tick_sched *ts)
        ktime_t now, expires;
        int cpu = smp_processor_id();
 
-       now = tick_nohz_start_idle(cpu, ts);
+       now = tick_nohz_start_idle(ts);
 
        if (can_stop_idle_tick(cpu, ts)) {
                int was_stopped = ts->tick_stopped;
@@ -917,8 +911,7 @@ static void tick_nohz_account_idle_ticks(struct tick_sched *ts)
  */
 void tick_nohz_idle_exit(void)
 {
-       int cpu = smp_processor_id();
-       struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu);
+       struct tick_sched *ts = &__get_cpu_var(tick_cpu_sched);
        ktime_t now;
 
        local_irq_disable();
@@ -931,7 +924,7 @@ void tick_nohz_idle_exit(void)
                now = ktime_get();
 
        if (ts->idle_active)
-               tick_nohz_stop_idle(cpu, now);
+               tick_nohz_stop_idle(ts, now);
 
        if (ts->tick_stopped) {
                tick_nohz_restart_sched_tick(ts, now);
@@ -1015,12 +1008,10 @@ static void tick_nohz_switch_to_nohz(void)
  * timer and do not touch the other magic bits which need to be done
  * when idle is left.
  */
-static void tick_nohz_kick_tick(int cpu, ktime_t now)
+static void tick_nohz_kick_tick(struct tick_sched *ts, ktime_t now)
 {
 #if 0
        /* Switch back to 2.6.27 behaviour */
-
-       struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu);
        ktime_t delta;
 
        /*
@@ -1035,36 +1026,36 @@ static void tick_nohz_kick_tick(int cpu, ktime_t now)
 #endif
 }
 
-static inline void tick_check_nohz(int cpu)
+static inline void tick_check_nohz_this_cpu(void)
 {
-       struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu);
+       struct tick_sched *ts = &__get_cpu_var(tick_cpu_sched);
        ktime_t now;
 
        if (!ts->idle_active && !ts->tick_stopped)
                return;
        now = ktime_get();
        if (ts->idle_active)
-               tick_nohz_stop_idle(cpu, now);
+               tick_nohz_stop_idle(ts, now);
        if (ts->tick_stopped) {
                tick_nohz_update_jiffies(now);
-               tick_nohz_kick_tick(cpu, now);
+               tick_nohz_kick_tick(ts, now);
        }
 }
 
 #else
 
 static inline void tick_nohz_switch_to_nohz(void) { }
-static inline void tick_check_nohz(int cpu) { }
+static inline void tick_check_nohz_this_cpu(void) { }
 
 #endif /* CONFIG_NO_HZ_COMMON */
 
 /*
  * Called from irq_enter to notify about the possible interruption of idle()
  */
-void tick_check_idle(int cpu)
+void tick_check_idle(void)
 {
-       tick_check_oneshot_broadcast(cpu);
-       tick_check_nohz(cpu);
+       tick_check_oneshot_broadcast_this_cpu();
+       tick_check_nohz_this_cpu();
 }
 
 /*