[RFC/RFT][PATCH v2 6/6] time: tick-sched: Avoid running the same code twice in a row
From: Rafael J. WysockiTo avoid running the same piece of code twice in a row, move the tick stopping part of __tick_nohz_next_event() into a new function called __tick_nohz_stop_tick() and invoke them both separately. Make __tick_nohz_idle_enter() avoid calling __tick_nohz_next_event() if it has been called already by tick_nohz_get_sleep_length() and use the new next_idle_tick field in struct tick_sched to pass the next event time value between tick_nohz_get_sleep_length() and __tick_nohz_idle_enter(). Signed-off-by: Rafael J. Wysocki --- -> v2: No changes. --- kernel/time/tick-sched.c | 130 ++- kernel/time/tick-sched.h |1 2 files changed, 73 insertions(+), 58 deletions(-) Index: linux-pm/kernel/time/tick-sched.c === --- linux-pm.orig/kernel/time/tick-sched.c +++ linux-pm/kernel/time/tick-sched.c @@ -655,13 +655,10 @@ static inline bool local_timer_softirq_p return local_softirq_pending() & TIMER_SOFTIRQ; } -static ktime_t __tick_nohz_next_event(struct tick_sched *ts, int cpu, - bool stop_tick) +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 { @@ -714,34 +711,23 @@ static ktime_t __tick_nohz_next_event(st * We've not stopped the tick yet, and there's a timer in the * next period, so no point in stopping it either, bail. */ - if (!ts->tick_stopped) { - tick = 0; - goto out; - } + if (!ts->tick_stopped) + return 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. +* 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) { - if (stop_tick) { - tick_do_timer_cpu = TICK_DO_TIMER_NONE; - ts->do_timer_last = 1; + if (cpu != tick_do_timer_cpu) { + 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; } - } 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 @@ -756,24 +742,37 @@ static ktime_t __tick_nohz_next_event(st else expires = KTIME_MAX; - expires = min_t(u64, expires, next_tick); - tick = expires; + ts->next_idle_tick = min_t(u64, expires, next_tick); + return ts->next_idle_tick; +} - if (!stop_tick) { - /* Undo the effect of get_next_timer_interrupt(). */ - timer_clear_idle(); - goto out; +static void __tick_nohz_stop_tick(struct tick_sched *ts, int cpu, u64 expires) +{ + struct clock_event_device *dev = __this_cpu_read(tick_cpu_device.evtdev); + ktime_t tick = expires; + + /* +* 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 (cpu == tick_do_timer_cpu) { + tick_do_timer_cpu = TICK_DO_TIMER_NONE; + ts->do_timer_last = 1; } /* Skip reprogram of event if its not changed */ if (ts->tick_stopped && (expires == ts->next_tick)) { /* Sanity check: make sure clockevent is
[RFC/RFT][PATCH v2 6/6] time: tick-sched: Avoid running the same code twice in a row
From: Rafael J. Wysocki To avoid running the same piece of code twice in a row, move the tick stopping part of __tick_nohz_next_event() into a new function called __tick_nohz_stop_tick() and invoke them both separately. Make __tick_nohz_idle_enter() avoid calling __tick_nohz_next_event() if it has been called already by tick_nohz_get_sleep_length() and use the new next_idle_tick field in struct tick_sched to pass the next event time value between tick_nohz_get_sleep_length() and __tick_nohz_idle_enter(). Signed-off-by: Rafael J. Wysocki --- -> v2: No changes. --- kernel/time/tick-sched.c | 130 ++- kernel/time/tick-sched.h |1 2 files changed, 73 insertions(+), 58 deletions(-) Index: linux-pm/kernel/time/tick-sched.c === --- linux-pm.orig/kernel/time/tick-sched.c +++ linux-pm/kernel/time/tick-sched.c @@ -655,13 +655,10 @@ static inline bool local_timer_softirq_p return local_softirq_pending() & TIMER_SOFTIRQ; } -static ktime_t __tick_nohz_next_event(struct tick_sched *ts, int cpu, - bool stop_tick) +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 { @@ -714,34 +711,23 @@ static ktime_t __tick_nohz_next_event(st * We've not stopped the tick yet, and there's a timer in the * next period, so no point in stopping it either, bail. */ - if (!ts->tick_stopped) { - tick = 0; - goto out; - } + if (!ts->tick_stopped) + return 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. +* 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) { - if (stop_tick) { - tick_do_timer_cpu = TICK_DO_TIMER_NONE; - ts->do_timer_last = 1; + if (cpu != tick_do_timer_cpu) { + 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; } - } 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 @@ -756,24 +742,37 @@ static ktime_t __tick_nohz_next_event(st else expires = KTIME_MAX; - expires = min_t(u64, expires, next_tick); - tick = expires; + ts->next_idle_tick = min_t(u64, expires, next_tick); + return ts->next_idle_tick; +} - if (!stop_tick) { - /* Undo the effect of get_next_timer_interrupt(). */ - timer_clear_idle(); - goto out; +static void __tick_nohz_stop_tick(struct tick_sched *ts, int cpu, u64 expires) +{ + struct clock_event_device *dev = __this_cpu_read(tick_cpu_device.evtdev); + ktime_t tick = expires; + + /* +* 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 (cpu == tick_do_timer_cpu) { + tick_do_timer_cpu = TICK_DO_TIMER_NONE; + ts->do_timer_last = 1; } /* 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 ==