OSDN Git Service

nohz: Avoid duplication of code related to got_idle_tick
[uclinux-h8/linux.git] / kernel / time / tick-sched.c
index 29a5733..956831c 100644 (file)
@@ -113,8 +113,7 @@ static ktime_t tick_init_jiffy_update(void)
        return period;
 }
 
-
-static void tick_sched_do_timer(ktime_t now)
+static void tick_sched_do_timer(struct tick_sched *ts, ktime_t now)
 {
        int cpu = smp_processor_id();
 
@@ -134,6 +133,9 @@ static void tick_sched_do_timer(ktime_t now)
        /* Check, if the jiffies need an update */
        if (tick_do_timer_cpu == cpu)
                tick_do_update_jiffies64(now);
+
+       if (ts->inidle)
+               ts->got_idle_tick = 1;
 }
 
 static void tick_sched_handle(struct tick_sched *ts, struct pt_regs *regs)
@@ -405,30 +407,12 @@ static int tick_nohz_cpu_down(unsigned int cpu)
        return 0;
 }
 
-static int tick_nohz_init_all(void)
-{
-       int err = -1;
-
-#ifdef CONFIG_NO_HZ_FULL_ALL
-       if (!alloc_cpumask_var(&tick_nohz_full_mask, GFP_KERNEL)) {
-               WARN(1, "NO_HZ: Can't allocate full dynticks cpumask\n");
-               return err;
-       }
-       err = 0;
-       cpumask_setall(tick_nohz_full_mask);
-       tick_nohz_full_running = true;
-#endif
-       return err;
-}
-
 void __init tick_nohz_init(void)
 {
        int cpu, ret;
 
-       if (!tick_nohz_full_running) {
-               if (tick_nohz_init_all() < 0)
-                       return;
-       }
+       if (!tick_nohz_full_running)
+               return;
 
        /*
         * Full dynticks uses irq work to drive the tick rescheduling on safe
@@ -481,9 +465,18 @@ static int __init setup_tick_nohz(char *str)
 
 __setup("nohz=", setup_tick_nohz);
 
-int tick_nohz_tick_stopped(void)
+bool tick_nohz_tick_stopped(void)
+{
+       struct tick_sched *ts = this_cpu_ptr(&tick_cpu_sched);
+
+       return ts->tick_stopped;
+}
+
+bool tick_nohz_tick_stopped_cpu(int cpu)
 {
-       return __this_cpu_read(tick_cpu_sched.tick_stopped);
+       struct tick_sched *ts = per_cpu_ptr(&tick_cpu_sched, cpu);
+
+       return ts->tick_stopped;
 }
 
 /**
@@ -539,14 +532,11 @@ static void tick_nohz_stop_idle(struct tick_sched *ts, ktime_t now)
        sched_clock_idle_wakeup_event();
 }
 
-static ktime_t tick_nohz_start_idle(struct tick_sched *ts)
+static void tick_nohz_start_idle(struct tick_sched *ts)
 {
-       ktime_t now = ktime_get();
-
-       ts->idle_entrytime = now;
+       ts->idle_entrytime = ktime_get();
        ts->idle_active = 1;
        sched_clock_idle_sleep_event();
-       return now;
 }
 
 /**
@@ -655,13 +645,10 @@ static inline bool local_timer_softirq_pending(void)
        return local_softirq_pending() & TIMER_SOFTIRQ;
 }
 
-static ktime_t tick_nohz_stop_sched_tick(struct tick_sched *ts,
-                                        ktime_t now, int cpu)
+static ktime_t tick_nohz_next_event(struct tick_sched *ts, int cpu)
 {
-       struct clock_event_device *dev = __this_cpu_read(tick_cpu_device.evtdev);
        u64 basemono, next_tick, next_tmr, next_rcu, delta, expires;
        unsigned long seq, basejiff;
-       ktime_t tick;
 
        /* Read jiffies and the time when jiffies were updated last */
        do {
@@ -670,6 +657,7 @@ static ktime_t tick_nohz_stop_sched_tick(struct tick_sched *ts,
                basejiff = jiffies;
        } while (read_seqretry(&jiffies_lock, seq));
        ts->last_jiffies = basejiff;
+       ts->timer_expires_base = basemono;
 
        /*
         * Keep the periodic tick, when RCU, architecture or irq_work
@@ -714,53 +702,63 @@ static ktime_t tick_nohz_stop_sched_tick(struct tick_sched *ts,
                 * next period, so no point in stopping it either, bail.
                 */
                if (!ts->tick_stopped) {
-                       tick = 0;
+                       ts->timer_expires = 0;
                        goto out;
                }
        }
 
        /*
+        * If this CPU is the one which had the do_timer() duty last, we limit
+        * the sleep time to the timekeeping max_deferment value.
+        * Otherwise we can sleep as long as we want.
+        */
+       delta = timekeeping_max_deferment();
+       if (cpu != tick_do_timer_cpu &&
+           (tick_do_timer_cpu != TICK_DO_TIMER_NONE || !ts->do_timer_last))
+               delta = KTIME_MAX;
+
+       /* Calculate the next expiry time */
+       if (delta < (KTIME_MAX - basemono))
+               expires = basemono + delta;
+       else
+               expires = KTIME_MAX;
+
+       ts->timer_expires = min_t(u64, expires, next_tick);
+
+out:
+       return ts->timer_expires;
+}
+
+static void tick_nohz_stop_tick(struct tick_sched *ts, int cpu)
+{
+       struct clock_event_device *dev = __this_cpu_read(tick_cpu_device.evtdev);
+       u64 basemono = ts->timer_expires_base;
+       u64 expires = ts->timer_expires;
+       ktime_t tick = expires;
+
+       /* Make sure we won't be trying to stop it twice in a row. */
+       ts->timer_expires_base = 0;
+
+       /*
         * If this CPU 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, which might be this CPU as well. If we
         * don't drop this here the jiffies might be stale and
         * do_timer() never invoked. Keep track of the fact that it
-        * was the one which had the do_timer() duty last. If this CPU
-        * is the one which had the do_timer() duty last, we limit the
-        * sleep time to the timekeeping max_deferment value.
-        * Otherwise we can sleep as long as we want.
+        * was the one which had the do_timer() duty last.
         */
-       delta = timekeeping_max_deferment();
        if (cpu == tick_do_timer_cpu) {
                tick_do_timer_cpu = TICK_DO_TIMER_NONE;
                ts->do_timer_last = 1;
        } else if (tick_do_timer_cpu != TICK_DO_TIMER_NONE) {
-               delta = KTIME_MAX;
                ts->do_timer_last = 0;
-       } else if (!ts->do_timer_last) {
-               delta = KTIME_MAX;
        }
 
-#ifdef CONFIG_NO_HZ_FULL
-       /* Limit the tick delta to the maximum scheduler deferment */
-       if (!ts->inidle)
-               delta = min(delta, scheduler_tick_max_deferment());
-#endif
-
-       /* Calculate the next expiry time */
-       if (delta < (KTIME_MAX - basemono))
-               expires = basemono + delta;
-       else
-               expires = KTIME_MAX;
-
-       expires = min_t(u64, expires, next_tick);
-       tick = expires;
-
        /* Skip reprogram of event if its not changed */
        if (ts->tick_stopped && (expires == ts->next_tick)) {
                /* Sanity check: make sure clockevent is actually programmed */
                if (tick == KTIME_MAX || ts->next_tick == hrtimer_get_expires(&ts->sched_timer))
-                       goto out;
+                       return;
 
                WARN_ON_ONCE(1);
                printk_once("basemono: %llu ts->next_tick: %llu dev->next_event: %llu timer->active: %d timer->expires: %llu\n",
@@ -794,7 +792,7 @@ static ktime_t tick_nohz_stop_sched_tick(struct tick_sched *ts,
        if (unlikely(expires == KTIME_MAX)) {
                if (ts->nohz_mode == NOHZ_MODE_HIGHRES)
                        hrtimer_cancel(&ts->sched_timer);
-               goto out;
+               return;
        }
 
        hrtimer_set_expires(&ts->sched_timer, tick);
@@ -803,15 +801,23 @@ static ktime_t tick_nohz_stop_sched_tick(struct tick_sched *ts,
                hrtimer_start_expires(&ts->sched_timer, HRTIMER_MODE_ABS_PINNED);
        else
                tick_program_event(tick, 1);
-out:
-       /*
-        * Update the estimated sleep length until the next timer
-        * (not only the tick).
-        */
-       ts->sleep_length = ktime_sub(dev->next_event, now);
-       return tick;
 }
 
+static void tick_nohz_retain_tick(struct tick_sched *ts)
+{
+       ts->timer_expires_base = 0;
+}
+
+#ifdef CONFIG_NO_HZ_FULL
+static void tick_nohz_stop_sched_tick(struct tick_sched *ts, int cpu)
+{
+       if (tick_nohz_next_event(ts, cpu))
+               tick_nohz_stop_tick(ts, cpu);
+       else
+               tick_nohz_retain_tick(ts);
+}
+#endif /* CONFIG_NO_HZ_FULL */
+
 static void tick_nohz_restart_sched_tick(struct tick_sched *ts, ktime_t now)
 {
        /* Update jiffies first */
@@ -847,7 +853,7 @@ static void tick_nohz_full_update_tick(struct tick_sched *ts)
                return;
 
        if (can_stop_full_tick(cpu, ts))
-               tick_nohz_stop_sched_tick(ts, ktime_get(), cpu);
+               tick_nohz_stop_sched_tick(ts, cpu);
        else if (ts->tick_stopped)
                tick_nohz_restart_sched_tick(ts, ktime_get());
 #endif
@@ -873,10 +879,8 @@ static bool can_stop_idle_tick(int cpu, struct tick_sched *ts)
                return false;
        }
 
-       if (unlikely(ts->nohz_mode == NOHZ_MODE_INACTIVE)) {
-               ts->sleep_length = NSEC_PER_SEC / HZ;
+       if (unlikely(ts->nohz_mode == NOHZ_MODE_INACTIVE))
                return false;
-       }
 
        if (need_resched())
                return false;
@@ -911,61 +915,80 @@ static bool can_stop_idle_tick(int cpu, struct tick_sched *ts)
        return true;
 }
 
-static void __tick_nohz_idle_enter(struct tick_sched *ts)
+static void __tick_nohz_idle_stop_tick(struct tick_sched *ts)
 {
-       ktime_t now, expires;
+       ktime_t expires;
        int cpu = smp_processor_id();
 
-       now = tick_nohz_start_idle(ts);
+       /*
+        * If tick_nohz_get_sleep_length() ran tick_nohz_next_event(), the
+        * tick timer expiration time is known already.
+        */
+       if (ts->timer_expires_base)
+               expires = ts->timer_expires;
+       else if (can_stop_idle_tick(cpu, ts))
+               expires = tick_nohz_next_event(ts, cpu);
+       else
+               return;
 
-       if (can_stop_idle_tick(cpu, ts)) {
+       ts->idle_calls++;
+
+       if (expires > 0LL) {
                int was_stopped = ts->tick_stopped;
 
-               ts->idle_calls++;
+               tick_nohz_stop_tick(ts, cpu);
 
-               expires = tick_nohz_stop_sched_tick(ts, now, cpu);
-               if (expires > 0LL) {
-                       ts->idle_sleeps++;
-                       ts->idle_expires = expires;
-               }
+               ts->idle_sleeps++;
+               ts->idle_expires = expires;
 
                if (!was_stopped && ts->tick_stopped) {
                        ts->idle_jiffies = ts->last_jiffies;
                        nohz_balance_enter_idle(cpu);
                }
+       } else {
+               tick_nohz_retain_tick(ts);
        }
 }
 
 /**
- * tick_nohz_idle_enter - stop the idle tick from the idle task
+ * tick_nohz_idle_stop_tick - stop the idle tick from the idle task
  *
  * When the next event is more than a tick into the future, stop the idle tick
- * Called when we start the idle loop.
- *
- * The arch is responsible of calling:
+ */
+void tick_nohz_idle_stop_tick(void)
+{
+       __tick_nohz_idle_stop_tick(this_cpu_ptr(&tick_cpu_sched));
+}
+
+void tick_nohz_idle_retain_tick(void)
+{
+       tick_nohz_retain_tick(this_cpu_ptr(&tick_cpu_sched));
+       /*
+        * Undo the effect of get_next_timer_interrupt() called from
+        * tick_nohz_next_event().
+        */
+       timer_clear_idle();
+}
+
+/**
+ * tick_nohz_idle_enter - prepare for entering idle on the current CPU
  *
- * - rcu_idle_enter() after its last use of RCU before the CPU is put
- *  to sleep.
- * - rcu_idle_exit() before the first use of RCU after the CPU is woken up.
+ * Called when we start the idle loop.
  */
 void tick_nohz_idle_enter(void)
 {
        struct tick_sched *ts;
 
        lockdep_assert_irqs_enabled();
-       /*
-        * Update the idle state in the scheduler domain hierarchy
-        * when tick_nohz_stop_sched_tick() is called from the idle loop.
-        * State will be updated to busy during the first busy tick after
-        * exiting idle.
-        */
-       set_cpu_sd_state_idle();
 
        local_irq_disable();
 
        ts = this_cpu_ptr(&tick_cpu_sched);
+
+       WARN_ON_ONCE(ts->timer_expires_base);
+
        ts->inidle = 1;
-       __tick_nohz_idle_enter(ts);
+       tick_nohz_start_idle(ts);
 
        local_irq_enable();
 }
@@ -983,21 +1006,62 @@ void tick_nohz_irq_exit(void)
        struct tick_sched *ts = this_cpu_ptr(&tick_cpu_sched);
 
        if (ts->inidle)
-               __tick_nohz_idle_enter(ts);
+               tick_nohz_start_idle(ts);
        else
                tick_nohz_full_update_tick(ts);
 }
 
 /**
- * tick_nohz_get_sleep_length - return the length of the current sleep
+ * tick_nohz_idle_got_tick - Check whether or not the tick handler has run
+ */
+bool tick_nohz_idle_got_tick(void)
+{
+       struct tick_sched *ts = this_cpu_ptr(&tick_cpu_sched);
+
+       if (ts->got_idle_tick) {
+               ts->got_idle_tick = 0;
+               return true;
+       }
+       return false;
+}
+
+/**
+ * tick_nohz_get_sleep_length - return the expected length of the current sleep
+ * @delta_next: duration until the next event if the tick cannot be stopped
  *
  * Called from power state control code with interrupts disabled
  */
-ktime_t tick_nohz_get_sleep_length(void)
+ktime_t tick_nohz_get_sleep_length(ktime_t *delta_next)
 {
+       struct clock_event_device *dev = __this_cpu_read(tick_cpu_device.evtdev);
        struct tick_sched *ts = this_cpu_ptr(&tick_cpu_sched);
+       int cpu = smp_processor_id();
+       /*
+        * The idle entry time is expected to be a sufficient approximation of
+        * the current time at this point.
+        */
+       ktime_t now = ts->idle_entrytime;
+       ktime_t next_event;
+
+       WARN_ON_ONCE(!ts->inidle);
+
+       *delta_next = ktime_sub(dev->next_event, now);
+
+       if (!can_stop_idle_tick(cpu, ts))
+               return *delta_next;
 
-       return ts->sleep_length;
+       next_event = tick_nohz_next_event(ts, cpu);
+       if (!next_event)
+               return *delta_next;
+
+       /*
+        * If the next highres timer to expire is earlier than next_event, the
+        * idle governor needs to know that.
+        */
+       next_event = min_t(u64, next_event,
+                          hrtimer_next_event_without(&ts->sched_timer));
+
+       return ktime_sub(next_event, now);
 }
 
 /**
@@ -1046,6 +1110,20 @@ static void tick_nohz_account_idle_ticks(struct tick_sched *ts)
 #endif
 }
 
+static void __tick_nohz_idle_restart_tick(struct tick_sched *ts, ktime_t now)
+{
+       tick_nohz_restart_sched_tick(ts, now);
+       tick_nohz_account_idle_ticks(ts);
+}
+
+void tick_nohz_idle_restart_tick(void)
+{
+       struct tick_sched *ts = this_cpu_ptr(&tick_cpu_sched);
+
+       if (ts->tick_stopped)
+               __tick_nohz_idle_restart_tick(ts, ktime_get());
+}
+
 /**
  * tick_nohz_idle_exit - restart the idle tick from the idle task
  *
@@ -1061,6 +1139,7 @@ void tick_nohz_idle_exit(void)
        local_irq_disable();
 
        WARN_ON_ONCE(!ts->inidle);
+       WARN_ON_ONCE(ts->timer_expires_base);
 
        ts->inidle = 0;
 
@@ -1070,10 +1149,8 @@ void tick_nohz_idle_exit(void)
        if (ts->idle_active)
                tick_nohz_stop_idle(ts, now);
 
-       if (ts->tick_stopped) {
-               tick_nohz_restart_sched_tick(ts, now);
-               tick_nohz_account_idle_ticks(ts);
-       }
+       if (ts->tick_stopped)
+               __tick_nohz_idle_restart_tick(ts, now);
 
        local_irq_enable();
 }
@@ -1089,7 +1166,7 @@ static void tick_nohz_handler(struct clock_event_device *dev)
 
        dev->next_event = KTIME_MAX;
 
-       tick_sched_do_timer(now);
+       tick_sched_do_timer(ts, now);
        tick_sched_handle(ts, regs);
 
        /* No need to reprogram if we are running tickless  */
@@ -1184,7 +1261,7 @@ static enum hrtimer_restart tick_sched_timer(struct hrtimer *timer)
        struct pt_regs *regs = get_irq_regs();
        ktime_t now = ktime_get();
 
-       tick_sched_do_timer(now);
+       tick_sched_do_timer(ts, now);
 
        /*
         * Do not call, when we are not in irq context and have