Commit 280f0677 authored by Frederic Weisbecker's avatar Frederic Weisbecker Committed by Paul E. McKenney

nohz: Separate out irq exit and idle loop dyntick logic

The tick_nohz_stop_sched_tick() function, which tries to delay
the next timer tick as long as possible, can be called from two
places:

- From the idle loop to start the dytick idle mode
- From interrupt exit if we have interrupted the dyntick
idle mode, so that we reprogram the next tick event in
case the irq changed some internal state that requires this
action.

There are only few minor differences between both that
are handled by that function, driven by the ts->inidle
cpu variable and the inidle parameter. The whole guarantees
that we only update the dyntick mode on irq exit if we actually
interrupted the dyntick idle mode, and that we enter in RCU extended
quiescent state from idle loop entry only.

Split this function into:

- tick_nohz_idle_enter(), which sets ts->inidle to 1, enters
dynticks idle mode unconditionally if it can, and enters into RCU
extended quiescent state.

- tick_nohz_irq_exit() which only updates the dynticks idle mode
when ts->inidle is set (ie: if tick_nohz_idle_enter() has been called).

To maintain symmetry, tick_nohz_restart_sched_tick() has been renamed
into tick_nohz_idle_exit().

This simplifies the code and micro-optimize the irq exit path (no need
for local_irq_save there). This also prepares for the split between
dynticks and rcu extended quiescent state logics. We'll need this split to
further fix illegal uses of RCU in extended quiescent states in the idle
loop.
Signed-off-by: default avatarFrederic Weisbecker <fweisbec@gmail.com>
Cc: Mike Frysinger <vapier@gentoo.org>
Cc: Guan Xuetao <gxt@mprc.pku.edu.cn>
Cc: David Miller <davem@davemloft.net>
Cc: Chris Metcalf <cmetcalf@tilera.com>
Cc: Hans-Christian Egtvedt <hans-christian.egtvedt@atmel.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Russell King <linux@arm.linux.org.uk>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Heiko Carstens <heiko.carstens@de.ibm.com>
Cc: Paul Mundt <lethal@linux-sh.org>
Signed-off-by: default avatarPaul E. McKenney <paulmck@linux.vnet.ibm.com>
Reviewed-by: default avatarJosh Triplett <josh@joshtriplett.org>
parent 867f236b
......@@ -183,7 +183,7 @@ void cpu_idle(void)
/* endless idle loop with no priority at all */
while (1) {
tick_nohz_stop_sched_tick(1);
tick_nohz_idle_enter();
leds_event(led_idle_start);
while (!need_resched()) {
#ifdef CONFIG_HOTPLUG_CPU
......@@ -213,7 +213,7 @@ void cpu_idle(void)
}
}
leds_event(led_idle_end);
tick_nohz_restart_sched_tick();
tick_nohz_idle_exit();
preempt_enable_no_resched();
schedule();
preempt_disable();
......
......@@ -34,10 +34,10 @@ void cpu_idle(void)
{
/* endless idle loop with no priority at all */
while (1) {
tick_nohz_stop_sched_tick(1);
tick_nohz_idle_enter();
while (!need_resched())
cpu_idle_sleep();
tick_nohz_restart_sched_tick();
tick_nohz_idle_exit();
preempt_enable_no_resched();
schedule();
preempt_disable();
......
......@@ -88,10 +88,10 @@ void cpu_idle(void)
#endif
if (!idle)
idle = default_idle;
tick_nohz_stop_sched_tick(1);
tick_nohz_idle_enter();
while (!need_resched())
idle();
tick_nohz_restart_sched_tick();
tick_nohz_idle_exit();
preempt_enable_no_resched();
schedule();
preempt_disable();
......
......@@ -103,10 +103,10 @@ void cpu_idle(void)
if (!idle)
idle = default_idle;
tick_nohz_stop_sched_tick(1);
tick_nohz_idle_enter();
while (!need_resched())
idle();
tick_nohz_restart_sched_tick();
tick_nohz_idle_exit();
preempt_enable_no_resched();
schedule();
......
......@@ -56,7 +56,7 @@ void __noreturn cpu_idle(void)
/* endless idle loop with no priority at all */
while (1) {
tick_nohz_stop_sched_tick(1);
tick_nohz_idle_enter();
while (!need_resched() && cpu_online(cpu)) {
#ifdef CONFIG_MIPS_MT_SMTC
extern void smtc_idle_loop_hook(void);
......@@ -77,7 +77,7 @@ void __noreturn cpu_idle(void)
system_state == SYSTEM_BOOTING))
play_dead();
#endif
tick_nohz_restart_sched_tick();
tick_nohz_idle_exit();
preempt_enable_no_resched();
schedule();
preempt_disable();
......
......@@ -51,7 +51,7 @@ void cpu_idle(void)
/* endless idle loop with no priority at all */
while (1) {
tick_nohz_stop_sched_tick(1);
tick_nohz_idle_enter();
while (!need_resched()) {
check_pgt_cache();
......@@ -69,7 +69,7 @@ void cpu_idle(void)
set_thread_flag(TIF_POLLING_NRFLAG);
}
tick_nohz_restart_sched_tick();
tick_nohz_idle_exit();
preempt_enable_no_resched();
schedule();
preempt_disable();
......
......@@ -56,7 +56,7 @@ void cpu_idle(void)
set_thread_flag(TIF_POLLING_NRFLAG);
while (1) {
tick_nohz_stop_sched_tick(1);
tick_nohz_idle_enter();
while (!need_resched() && !cpu_should_die()) {
ppc64_runlatch_off();
......@@ -93,7 +93,7 @@ void cpu_idle(void)
HMT_medium();
ppc64_runlatch_on();
tick_nohz_restart_sched_tick();
tick_nohz_idle_exit();
preempt_enable_no_resched();
if (cpu_should_die())
cpu_die();
......
......@@ -563,7 +563,7 @@ static void yield_shared_processor(void)
static void iseries_shared_idle(void)
{
while (1) {
tick_nohz_stop_sched_tick(1);
tick_nohz_idle_enter();
while (!need_resched() && !hvlpevent_is_pending()) {
local_irq_disable();
ppc64_runlatch_off();
......@@ -577,7 +577,7 @@ static void iseries_shared_idle(void)
}
ppc64_runlatch_on();
tick_nohz_restart_sched_tick();
tick_nohz_idle_exit();
if (hvlpevent_is_pending())
process_iSeries_events();
......@@ -593,7 +593,7 @@ static void iseries_dedicated_idle(void)
set_thread_flag(TIF_POLLING_NRFLAG);
while (1) {
tick_nohz_stop_sched_tick(1);
tick_nohz_idle_enter();
if (!need_resched()) {
while (!need_resched()) {
ppc64_runlatch_off();
......@@ -610,7 +610,7 @@ static void iseries_dedicated_idle(void)
}
ppc64_runlatch_on();
tick_nohz_restart_sched_tick();
tick_nohz_idle_exit();
preempt_enable_no_resched();
schedule();
preempt_disable();
......
......@@ -91,10 +91,10 @@ static void default_idle(void)
void cpu_idle(void)
{
for (;;) {
tick_nohz_stop_sched_tick(1);
tick_nohz_idle_enter();
while (!need_resched())
default_idle();
tick_nohz_restart_sched_tick();
tick_nohz_idle_exit();
preempt_enable_no_resched();
schedule();
preempt_disable();
......
......@@ -89,7 +89,7 @@ void cpu_idle(void)
/* endless idle loop with no priority at all */
while (1) {
tick_nohz_stop_sched_tick(1);
tick_nohz_idle_enter();
while (!need_resched()) {
check_pgt_cache();
......@@ -111,7 +111,7 @@ void cpu_idle(void)
start_critical_timings();
}
tick_nohz_restart_sched_tick();
tick_nohz_idle_exit();
preempt_enable_no_resched();
schedule();
preempt_disable();
......
......@@ -95,12 +95,12 @@ void cpu_idle(void)
set_thread_flag(TIF_POLLING_NRFLAG);
while(1) {
tick_nohz_stop_sched_tick(1);
tick_nohz_idle_enter();
while (!need_resched() && !cpu_is_offline(cpu))
sparc64_yield(cpu);
tick_nohz_restart_sched_tick();
tick_nohz_idle_exit();
preempt_enable_no_resched();
......
......@@ -85,7 +85,7 @@ void cpu_idle(void)
/* endless idle loop with no priority at all */
while (1) {
tick_nohz_stop_sched_tick(1);
tick_nohz_idle_enter();
while (!need_resched()) {
if (cpu_is_offline(cpu))
BUG(); /* no HOTPLUG_CPU */
......@@ -105,7 +105,7 @@ void cpu_idle(void)
local_irq_enable();
current_thread_info()->status |= TS_POLLING;
}
tick_nohz_restart_sched_tick();
tick_nohz_idle_exit();
preempt_enable_no_resched();
schedule();
preempt_disable();
......
......@@ -246,10 +246,10 @@ void default_idle(void)
if (need_resched())
schedule();
tick_nohz_stop_sched_tick(1);
tick_nohz_idle_enter();
nsecs = disable_timer();
idle_sleep(nsecs);
tick_nohz_restart_sched_tick();
tick_nohz_idle_exit();
}
}
......
......@@ -55,7 +55,7 @@ void cpu_idle(void)
{
/* endless idle loop with no priority at all */
while (1) {
tick_nohz_stop_sched_tick(1);
tick_nohz_idle_enter();
while (!need_resched()) {
local_irq_disable();
stop_critical_timings();
......@@ -63,7 +63,7 @@ void cpu_idle(void)
local_irq_enable();
start_critical_timings();
}
tick_nohz_restart_sched_tick();
tick_nohz_idle_exit();
preempt_enable_no_resched();
schedule();
preempt_disable();
......
......@@ -99,7 +99,7 @@ void cpu_idle(void)
/* endless idle loop with no priority at all */
while (1) {
tick_nohz_stop_sched_tick(1);
tick_nohz_idle_enter();
while (!need_resched()) {
check_pgt_cache();
......@@ -116,7 +116,7 @@ void cpu_idle(void)
pm_idle();
start_critical_timings();
}
tick_nohz_restart_sched_tick();
tick_nohz_idle_exit();
preempt_enable_no_resched();
schedule();
preempt_disable();
......
......@@ -122,7 +122,7 @@ void cpu_idle(void)
/* endless idle loop with no priority at all */
while (1) {
tick_nohz_stop_sched_tick(1);
tick_nohz_idle_enter();
while (!need_resched()) {
rmb();
......@@ -149,7 +149,7 @@ void cpu_idle(void)
__exit_idle();
}
tick_nohz_restart_sched_tick();
tick_nohz_idle_exit();
preempt_enable_no_resched();
schedule();
preempt_disable();
......
......@@ -121,21 +121,22 @@ static inline int tick_oneshot_mode_active(void) { return 0; }
#endif /* !CONFIG_GENERIC_CLOCKEVENTS */
# ifdef CONFIG_NO_HZ
extern void tick_nohz_stop_sched_tick(int inidle);
extern void tick_nohz_restart_sched_tick(void);
extern void tick_nohz_idle_enter(void);
extern void tick_nohz_idle_exit(void);
extern void tick_nohz_irq_exit(void);
extern ktime_t tick_nohz_get_sleep_length(void);
extern u64 get_cpu_idle_time_us(int cpu, u64 *last_update_time);
extern u64 get_cpu_iowait_time_us(int cpu, u64 *last_update_time);
# else
static inline void tick_nohz_stop_sched_tick(int inidle)
static inline void tick_nohz_idle_enter(void)
{
if (inidle)
rcu_idle_enter();
}
static inline void tick_nohz_restart_sched_tick(void)
static inline void tick_nohz_idle_exit(void)
{
rcu_idle_exit();
}
static inline ktime_t tick_nohz_get_sleep_length(void)
{
ktime_t len = { .tv64 = NSEC_PER_SEC/HZ };
......
......@@ -351,7 +351,7 @@ void irq_exit(void)
#ifdef CONFIG_NO_HZ
/* Make sure that timer wheel updates are propagated */
if (idle_cpu(smp_processor_id()) && !in_interrupt() && !need_resched())
tick_nohz_stop_sched_tick(0);
tick_nohz_irq_exit();
#endif
preempt_enable_no_resched();
}
......
......@@ -275,42 +275,17 @@ u64 get_cpu_iowait_time_us(int cpu, u64 *last_update_time)
}
EXPORT_SYMBOL_GPL(get_cpu_iowait_time_us);
/**
* tick_nohz_stop_sched_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 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(int inidle)
static void tick_nohz_stop_sched_tick(struct tick_sched *ts)
{
unsigned long seq, last_jiffies, next_jiffies, delta_jiffies, flags;
struct tick_sched *ts;
unsigned long seq, last_jiffies, next_jiffies, delta_jiffies;
ktime_t last_update, expires, now;
struct clock_event_device *dev = __get_cpu_var(tick_cpu_device).evtdev;
u64 time_delta;
int cpu;
local_irq_save(flags);
cpu = smp_processor_id();
ts = &per_cpu(tick_cpu_sched, cpu);
/*
* Call to tick_nohz_start_idle stops the last_update_time from being
* updated. Thus, it must not be called in the event we are called from
* irq_exit() with the prior state different than idle.
*/
if (!inidle && !ts->inidle)
goto end;
/*
* Set ts->inidle unconditionally. Even if the system did not
* switch to NOHZ mode the cpu frequency governers rely on the
* update of the idle time accounting in tick_nohz_start_idle().
*/
ts->inidle = 1;
now = tick_nohz_start_idle(cpu, ts);
/*
......@@ -326,10 +301,10 @@ void tick_nohz_stop_sched_tick(int inidle)
}
if (unlikely(ts->nohz_mode == NOHZ_MODE_INACTIVE))
goto end;
return;
if (need_resched())
goto end;
return;
if (unlikely(local_softirq_pending() && cpu_online(cpu))) {
static int ratelimit;
......@@ -339,7 +314,7 @@ void tick_nohz_stop_sched_tick(int inidle)
(unsigned int) local_softirq_pending());
ratelimit++;
}
goto end;
return;
}
ts->idle_calls++;
......@@ -471,10 +446,54 @@ void tick_nohz_stop_sched_tick(int inidle)
ts->next_jiffies = next_jiffies;
ts->last_jiffies = last_jiffies;
ts->sleep_length = ktime_sub(dev->next_event, now);
end:
if (inidle)
}
/**
* tick_nohz_idle_enter - 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.
* This also enters into RCU extended quiescent state so that this CPU doesn't
* need anymore to be part of any global grace period completion. This way
* the tick can be stopped safely as we don't need to report quiescent states.
*/
void tick_nohz_idle_enter(void)
{
struct tick_sched *ts;
WARN_ON_ONCE(irqs_disabled());
local_irq_disable();
ts = &__get_cpu_var(tick_cpu_sched);
/*
* set ts->inidle unconditionally. even if the system did not
* switch to nohz mode the cpu frequency governers rely on the
* update of the idle time accounting in tick_nohz_start_idle().
*/
ts->inidle = 1;
tick_nohz_stop_sched_tick(ts);
rcu_idle_enter();
local_irq_restore(flags);
local_irq_enable();
}
/**
* tick_nohz_irq_exit - update next tick event from interrupt exit
*
* When an interrupt fires while we are idle and it doesn't cause
* a reschedule, it may still add, modify or delete a timer, enqueue
* an RCU callback, etc...
* So we need to re-calculate and reprogram the next tick event.
*/
void tick_nohz_irq_exit(void)
{
struct tick_sched *ts = &__get_cpu_var(tick_cpu_sched);
if (!ts->inidle)
return;
tick_nohz_stop_sched_tick(ts);
}
/**
......@@ -516,11 +535,13 @@ static void tick_nohz_restart(struct tick_sched *ts, ktime_t now)
}
/**
* tick_nohz_restart_sched_tick - restart the idle tick from the idle task
* tick_nohz_idle_exit - restart the idle tick from the idle task
*
* Restart the idle tick when the CPU is woken up from idle
* This also exit the RCU extended quiescent state. The CPU
* can use RCU again after this function is called.
*/
void tick_nohz_restart_sched_tick(void)
void tick_nohz_idle_exit(void)
{
int cpu = smp_processor_id();
struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment