sched_clock: fix NOHZ interaction
[safe/jmp/linux-2.6] / kernel / time / tick-sched.c
index 3e7ebc4..a87b046 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>
@@ -48,6 +48,13 @@ static void tick_do_update_jiffies64(ktime_t now)
        unsigned long ticks = 0;
        ktime_t delta;
 
+       /*
+        * Do a quick check without holding xtime_lock:
+        */
+       delta = ktime_sub(now, last_jiffies_update);
+       if (delta.tv64 < tick_period.tv64)
+               return;
+
        /* Reevalute with xtime_lock held */
        write_seqlock(&xtime_lock);
 
@@ -135,10 +142,53 @@ 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);
+
+       touch_softlockup_watchdog();
+}
+
+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;
+
+               sched_clock_idle_wakeup_event(0);
+       }
+}
+
+static ktime_t tick_nohz_start_idle(struct tick_sched *ts)
+{
+       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;
+       sched_clock_idle_sleep_event();
+       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);
 }
 
 /**
@@ -148,25 +198,43 @@ void tick_nohz_update_jiffies(void)
  * Called either from the idle loop or from irq_exit() when an idle period was
  * just interrupted by an interrupt which did not cause a reschedule.
  */
-void tick_nohz_stop_sched_tick(void)
+void tick_nohz_stop_sched_tick(int inidle)
 {
        unsigned long seq, last_jiffies, next_jiffies, delta_jiffies, flags;
        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();
        ts = &per_cpu(tick_cpu_sched, cpu);
+       now = tick_nohz_start_idle(ts);
+
+       /*
+        * If this cpu is offline and it is the one which updates
+        * jiffies, then give up the assignment and let it be taken by
+        * the cpu which runs the tick timer next. If we don't drop
+        * this here the jiffies might be stale and do_timer() never
+        * invoked.
+        */
+       if (unlikely(!cpu_online(cpu))) {
+               if (cpu == tick_do_timer_cpu)
+                       tick_do_timer_cpu = -1;
+       }
 
        if (unlikely(ts->nohz_mode == NOHZ_MODE_INACTIVE))
                goto end;
 
+       if (!inidle && !ts->inidle)
+               goto end;
+
+       ts->inidle = 1;
+
        if (need_resched())
                goto end;
 
-       cpu = smp_processor_id();
        if (unlikely(local_softirq_pending())) {
                static int ratelimit;
 
@@ -175,21 +243,10 @@ void tick_nohz_stop_sched_tick(void)
                               local_softirq_pending());
                        ratelimit++;
                }
+               goto end;
        }
 
-       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);
@@ -234,6 +291,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();
                }
 
                /*
@@ -247,6 +305,21 @@ void tick_nohz_stop_sched_tick(void)
                if (cpu == tick_do_timer_cpu)
                        tick_do_timer_cpu = -1;
 
+               ts->idle_sleeps++;
+
+               /*
+                * delta_jiffies >= NEXT_TIMER_MAX_DELTA signals that
+                * there is no timer pending or at least extremly far
+                * into the future (12 days for HZ=1000). In this case
+                * we simply stop the tick timer:
+                */
+               if (unlikely(delta_jiffies >= NEXT_TIMER_MAX_DELTA)) {
+                       ts->idle_expires.tv64 = KTIME_MAX;
+                       if (ts->nohz_mode == NOHZ_MODE_HIGHRES)
+                               hrtimer_cancel(&ts->sched_timer);
+                       goto out;
+               }
+
                /*
                 * calculate the expiry time for the next timer wheel
                 * timer
@@ -254,7 +327,6 @@ void tick_nohz_stop_sched_tick(void)
                expires = ktime_add_ns(last_update, tick_period.tv64 *
                                       delta_jiffies);
                ts->idle_expires = expires;
-               ts->idle_sleeps++;
 
                if (ts->nohz_mode == NOHZ_MODE_HIGHRES) {
                        hrtimer_start(&ts->sched_timer, expires,
@@ -262,7 +334,7 @@ void tick_nohz_stop_sched_tick(void)
                        /* Check, if the timer was already in the past */
                        if (hrtimer_active(&ts->sched_timer))
                                goto out;
-               } else if(!tick_program_event(expires, 0))
+               } else if (!tick_program_event(expires, 0))
                                goto out;
                /*
                 * We are past the event already. So we crossed a
@@ -276,12 +348,25 @@ void tick_nohz_stop_sched_tick(void)
 out:
        ts->next_jiffies = next_jiffies;
        ts->last_jiffies = last_jiffies;
+       ts->sleep_length = ktime_sub(dev->next_event, now);
 end:
        local_irq_restore(flags);
 }
 
 /**
- * nohz_restart_sched_tick - restart the idle tick from the idle task
+ * tick_nohz_get_sleep_length - return the length of the current sleep
+ *
+ * Called from power state control code with interrupts disabled
+ */
+ktime_t tick_nohz_get_sleep_length(void)
+{
+       struct tick_sched *ts = &__get_cpu_var(tick_cpu_sched);
+
+       return ts->sleep_length;
+}
+
+/**
+ * tick_nohz_restart_sched_tick - restart the idle tick from the idle task
  *
  * Restart the idle tick when the CPU is woken up from idle
  */
@@ -290,23 +375,27 @@ 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->inidle || !ts->tick_stopped) {
+               ts->inidle = 0;
+               local_irq_enable();
                return;
+       }
 
-       /* Update jiffies first */
-       now = ktime_get();
+       ts->inidle = 0;
 
-       local_irq_disable();
+       rcu_exit_nohz();
+
+       /* 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
@@ -323,10 +412,12 @@ void tick_nohz_restart_sched_tick(void)
                sub_preempt_count(HARDIRQ_OFFSET);
        }
 
+       touch_softlockup_watchdog();
        /*
         * 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;
 
@@ -460,14 +551,13 @@ static inline void tick_nohz_switch_to_nohz(void) { }
  */
 #ifdef CONFIG_HIGH_RES_TIMERS
 /*
- * We rearm the timer until we get disabled by the idle code
+ * We rearm the timer until we get disabled by the idle code.
  * Called with interrupts disabled and timer->base->cpu_base->lock held.
  */
 static enum hrtimer_restart tick_sched_timer(struct hrtimer *timer)
 {
        struct tick_sched *ts =
                container_of(timer, struct tick_sched, sched_timer);
-       struct hrtimer_cpu_base *base = timer->base->cpu_base;
        struct pt_regs *regs = get_irq_regs();
        ktime_t now = ktime_get();
        int cpu = smp_processor_id();
@@ -505,15 +595,8 @@ static enum hrtimer_restart tick_sched_timer(struct hrtimer *timer)
                        touch_softlockup_watchdog();
                        ts->idle_jiffies++;
                }
-               /*
-                * update_process_times() might take tasklist_lock, hence
-                * drop the base lock. sched-tick hrtimers are per-CPU and
-                * never accessible by userspace APIs, so this is safe to do.
-                */
-               spin_unlock(&base->lock);
                update_process_times(user_mode(regs));
                profile_tick(CPU_PROFILING);
-               spin_lock(&base->lock);
        }
 
        /* Do not restart, when we are in the idle loop */
@@ -532,6 +615,7 @@ void tick_setup_sched_timer(void)
 {
        struct tick_sched *ts = &__get_cpu_var(tick_cpu_sched);
        ktime_t now = ktime_get();
+       u64 offset;
 
        /*
         * Emulate tick processing via per-CPU hrtimers:
@@ -540,8 +624,12 @@ void tick_setup_sched_timer(void)
        ts->sched_timer.function = tick_sched_timer;
        ts->sched_timer.cb_mode = HRTIMER_CB_IRQSAFE_NO_SOFTIRQ;
 
-       /* Get the next period */
+       /* Get the next period (per cpu) */
        ts->sched_timer.expires = tick_init_jiffy_update();
+       offset = ktime_to_ns(tick_period) >> 1;
+       do_div(offset, num_possible_cpus());
+       offset *= smp_processor_id();
+       ts->sched_timer.expires = ktime_add_ns(ts->sched_timer.expires, offset);
 
        for (;;) {
                hrtimer_forward(&ts->sched_timer, now, tick_period);
@@ -558,17 +646,21 @@ void tick_setup_sched_timer(void)
                ts->nohz_mode = NOHZ_MODE_HIGHRES;
 #endif
 }
+#endif /* HIGH_RES_TIMERS */
 
+#if defined CONFIG_NO_HZ || defined CONFIG_HIGH_RES_TIMERS
 void tick_cancel_sched_timer(int cpu)
 {
        struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu);
 
+# ifdef CONFIG_HIGH_RES_TIMERS
        if (ts->sched_timer.base)
                hrtimer_cancel(&ts->sched_timer);
-       ts->tick_stopped = 0;
+# endif
+
        ts->nohz_mode = NOHZ_MODE_INACTIVE;
 }
-#endif /* HIGH_RES_TIMERS */
+#endif
 
 /**
  * Async notification about clocksource changes
@@ -609,7 +701,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)