OSDN Git Service

nohz: Avoid duplication of code related to got_idle_tick
[uclinux-h8/linux.git] / kernel / time / tick-sched.c
index f5d3778..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)
@@ -465,7 +467,9 @@ __setup("nohz=", setup_tick_nohz);
 
 bool tick_nohz_tick_stopped(void)
 {
-       return __this_cpu_read(tick_cpu_sched.tick_stopped);
+       struct tick_sched *ts = this_cpu_ptr(&tick_cpu_sched);
+
+       return ts->tick_stopped;
 }
 
 bool tick_nohz_tick_stopped_cpu(int cpu)
@@ -641,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 {
@@ -656,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
@@ -700,47 +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;
        }
 
-       /* 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",
@@ -774,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);
@@ -783,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 */
@@ -827,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
@@ -853,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;
@@ -896,25 +920,33 @@ static void __tick_nohz_idle_stop_tick(struct tick_sched *ts)
        ktime_t expires;
        int cpu = smp_processor_id();
 
-       if (can_stop_idle_tick(cpu, 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;
+
+       ts->idle_calls++;
+
+       if (expires > 0LL) {
                int was_stopped = ts->tick_stopped;
 
-               ts->idle_calls++;
+               tick_nohz_stop_tick(ts, cpu);
 
-               /*
-                * The idle entry time should be a sufficient approximation of
-                * the current time at this point.
-                */
-               expires = tick_nohz_stop_sched_tick(ts, ts->idle_entrytime, 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);
        }
 }
 
@@ -928,6 +960,16 @@ 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
  *
@@ -942,6 +984,9 @@ void tick_nohz_idle_enter(void)
        local_irq_disable();
 
        ts = this_cpu_ptr(&tick_cpu_sched);
+
+       WARN_ON_ONCE(ts->timer_expires_base);
+
        ts->inidle = 1;
        tick_nohz_start_idle(ts);
 
@@ -967,15 +1012,56 @@ void tick_nohz_irq_exit(void)
 }
 
 /**
- * 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;
+
+       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 ts->sleep_length;
+       return ktime_sub(next_event, now);
 }
 
 /**
@@ -1053,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;
 
@@ -1079,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  */
@@ -1174,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