time: don't touch an offlined CPU's ts->tick_stopped in tick_cancel_sched_timer()
[sfrench/cifs-2.6.git] / kernel / time / tick-sched.c
index d36ee2fd1a3b97f4bf418ea1162e050d5660a57e..686da821d376b01dd21d379bfd8baf39d6b106f1 100644 (file)
@@ -9,7 +9,7 @@
  *
  *  Started by: Thomas Gleixner and Ingo Molnar
  *
- *  For licencing details see kernel-base/COPYING
+ *  Distribute under GPLv2.
  */
 #include <linux/cpu.h>
 #include <linux/err.h>
@@ -137,12 +137,51 @@ void tick_nohz_update_jiffies(void)
 
        cpu_clear(cpu, nohz_cpu_mask);
        now = ktime_get();
+       ts->idle_waketime = now;
 
        local_irq_save(flags);
        tick_do_update_jiffies64(now);
        local_irq_restore(flags);
 }
 
+void tick_nohz_stop_idle(int cpu)
+{
+       struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu);
+
+       if (ts->idle_active) {
+               ktime_t now, delta;
+               now = ktime_get();
+               delta = ktime_sub(now, ts->idle_entrytime);
+               ts->idle_lastupdate = now;
+               ts->idle_sleeptime = ktime_add(ts->idle_sleeptime, delta);
+               ts->idle_active = 0;
+       }
+}
+
+static ktime_t tick_nohz_start_idle(int cpu)
+{
+       struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu);
+       ktime_t now, delta;
+
+       now = ktime_get();
+       if (ts->idle_active) {
+               delta = ktime_sub(now, ts->idle_entrytime);
+               ts->idle_lastupdate = now;
+               ts->idle_sleeptime = ktime_add(ts->idle_sleeptime, delta);
+       }
+       ts->idle_entrytime = now;
+       ts->idle_active = 1;
+       return now;
+}
+
+u64 get_cpu_idle_time_us(int cpu, u64 *last_update_time)
+{
+       struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu);
+
+       *last_update_time = ktime_to_us(ts->idle_lastupdate);
+       return ktime_to_us(ts->idle_sleeptime);
+}
+
 /**
  * tick_nohz_stop_sched_tick - stop the idle tick from the idle task
  *
@@ -155,13 +194,14 @@ void tick_nohz_stop_sched_tick(void)
        unsigned long seq, last_jiffies, next_jiffies, delta_jiffies, flags;
        unsigned long rt_jiffies;
        struct tick_sched *ts;
-       ktime_t last_update, expires, now, delta;
+       ktime_t last_update, expires, now;
        struct clock_event_device *dev = __get_cpu_var(tick_cpu_device).evtdev;
        int cpu;
 
        local_irq_save(flags);
 
        cpu = smp_processor_id();
+       now = tick_nohz_start_idle(cpu);
        ts = &per_cpu(tick_cpu_sched, cpu);
 
        /*
@@ -193,19 +233,7 @@ void tick_nohz_stop_sched_tick(void)
                }
        }
 
-       now = ktime_get();
-       /*
-        * When called from irq_exit we need to account the idle sleep time
-        * correctly.
-        */
-       if (ts->tick_stopped) {
-               delta = ktime_sub(now, ts->idle_entrytime);
-               ts->idle_sleeptime = ktime_add(ts->idle_sleeptime, delta);
-       }
-
-       ts->idle_entrytime = now;
        ts->idle_calls++;
-
        /* Read jiffies and the time when jiffies were updated last */
        do {
                seq = read_seqbegin(&xtime_lock);
@@ -254,6 +282,7 @@ void tick_nohz_stop_sched_tick(void)
                        ts->idle_tick = ts->sched_timer.expires;
                        ts->tick_stopped = 1;
                        ts->idle_jiffies = last_jiffies;
+                       rcu_enter_nohz();
                }
 
                /*
@@ -337,23 +366,24 @@ void tick_nohz_restart_sched_tick(void)
        int cpu = smp_processor_id();
        struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu);
        unsigned long ticks;
-       ktime_t now, delta;
+       ktime_t now;
 
-       if (!ts->tick_stopped)
+       local_irq_disable();
+       tick_nohz_stop_idle(cpu);
+
+       if (!ts->tick_stopped) {
+               local_irq_enable();
                return;
+       }
 
-       /* Update jiffies first */
-       now = ktime_get();
+       rcu_exit_nohz();
 
-       local_irq_disable();
+       /* Update jiffies first */
        select_nohz_load_balancer(0);
+       now = ktime_get();
        tick_do_update_jiffies64(now);
        cpu_clear(cpu, nohz_cpu_mask);
 
-       /* Account the idle time */
-       delta = ktime_sub(now, ts->idle_entrytime);
-       ts->idle_sleeptime = ktime_add(ts->idle_sleeptime, delta);
-
        /*
         * We stopped the tick in idle. Update process times would miss the
         * time we slept as update_process_times does only a 1 tick
@@ -374,6 +404,7 @@ void tick_nohz_restart_sched_tick(void)
         * Cancel the scheduled timer and restore the tick
         */
        ts->tick_stopped  = 0;
+       ts->idle_exittime = now;
        hrtimer_cancel(&ts->sched_timer);
        ts->sched_timer.expires = ts->idle_tick;
 
@@ -609,7 +640,7 @@ void tick_cancel_sched_timer(int cpu)
 
        if (ts->sched_timer.base)
                hrtimer_cancel(&ts->sched_timer);
-       ts->tick_stopped = 0;
+
        ts->nohz_mode = NOHZ_MODE_INACTIVE;
 }
 #endif /* HIGH_RES_TIMERS */
@@ -653,7 +684,7 @@ int tick_check_oneshot_change(int allow_nohz)
        if (ts->nohz_mode != NOHZ_MODE_INACTIVE)
                return 0;
 
-       if (!timekeeping_is_continuous() || !tick_is_oneshot_available())
+       if (!timekeeping_valid_for_hres() || !tick_is_oneshot_available())
                return 0;
 
        if (!allow_nohz)