From ad98d75f2bf82f8cfef1e628e329c722be6c5ad1 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 16 Oct 2009 17:20:58 +0900 Subject: [PATCH] --- yaml --- r: 173411 b: refs/heads/master c: f533c3d340536198a4889a42a68d6c0d79a504e7 h: refs/heads/master i: 173409: 10b88224759dc3a8307b85196cab0d48b49be87a 173407: b038091660546f6e1cd338555411b282e6f104db v: v3 --- [refs] | 2 +- trunk/arch/sh/include/asm/bugs.h | 4 ++ trunk/arch/sh/kernel/idle.c | 73 +++++++++++++++++++++++++------- 3 files changed, 62 insertions(+), 17 deletions(-) diff --git a/[refs] b/[refs] index d39799e5c57b..065a269e3676 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: 94eab0bb206443dd7480349804f64e2bba8dc6e1 +refs/heads/master: f533c3d340536198a4889a42a68d6c0d79a504e7 diff --git a/trunk/arch/sh/include/asm/bugs.h b/trunk/arch/sh/include/asm/bugs.h index 46260fcbdf4b..02a19a1c033a 100644 --- a/trunk/arch/sh/include/asm/bugs.h +++ b/trunk/arch/sh/include/asm/bugs.h @@ -14,11 +14,15 @@ #include +extern void select_idle_routine(void); + static void __init check_bugs(void) { extern unsigned long loops_per_jiffy; char *p = &init_utsname()->machine[2]; /* "sh" */ + select_idle_routine(); + current_cpu_data.loops_per_jiffy = loops_per_jiffy; switch (current_cpu_data.family) { diff --git a/trunk/arch/sh/kernel/idle.c b/trunk/arch/sh/kernel/idle.c index 27ff2dc093c7..8e61241230cb 100644 --- a/trunk/arch/sh/kernel/idle.c +++ b/trunk/arch/sh/kernel/idle.c @@ -21,7 +21,7 @@ #include static int hlt_counter; -void (*pm_idle)(void); +void (*pm_idle)(void) = NULL; void (*pm_power_off)(void); EXPORT_SYMBOL(pm_power_off); @@ -39,41 +39,68 @@ static int __init hlt_setup(char *__unused) } __setup("hlt", hlt_setup); +static inline int hlt_works(void) +{ + return !hlt_counter; +} + +/* + * On SMP it's slightly faster (but much more power-consuming!) + * to poll the ->work.need_resched flag instead of waiting for the + * cross-CPU IPI to arrive. Use this option with caution. + */ +static void poll_idle(void) +{ + local_irq_enable(); + while (!need_resched()) + cpu_relax(); +} + void default_idle(void) { - if (!hlt_counter) { + if (hlt_works()) { clear_thread_flag(TIF_POLLING_NRFLAG); smp_mb__after_clear_bit(); - set_bl_bit(); - stop_critical_timings(); - while (!need_resched()) + if (!need_resched()) { + local_irq_enable(); cpu_sleep(); + } - start_critical_timings(); - clear_bl_bit(); set_thread_flag(TIF_POLLING_NRFLAG); } else - while (!need_resched()) - cpu_relax(); + poll_idle(); } +/* + * The idle thread. There's no useful work to be done, so just try to conserve + * power and have a low exit latency (ie sit in a loop waiting for somebody to + * say that they'd like to reschedule) + */ void cpu_idle(void) { + unsigned int cpu = smp_processor_id(); + set_thread_flag(TIF_POLLING_NRFLAG); /* endless idle loop with no priority at all */ while (1) { - void (*idle)(void) = pm_idle; + tick_nohz_stop_sched_tick(1); - if (!idle) - idle = default_idle; + while (!need_resched() && cpu_online(cpu)) { + local_irq_disable(); + /* Don't trace irqs off for idle */ + stop_critical_timings(); + pm_idle(); + /* + * Sanity check to ensure that pm_idle() returns + * with IRQs enabled + */ + WARN_ON(irqs_disabled()); + start_critical_timings(); + } - tick_nohz_stop_sched_tick(1); - while (!need_resched()) - idle(); tick_nohz_restart_sched_tick(); - preempt_enable_no_resched(); schedule(); preempt_disable(); @@ -81,6 +108,20 @@ void cpu_idle(void) } } +void __cpuinit select_idle_routine(void) +{ + /* + * If a platform has set its own idle routine, leave it alone. + */ + if (pm_idle) + return; + + if (hlt_works()) + pm_idle = default_idle; + else + pm_idle = poll_idle; +} + static void do_nothing(void *unused) { }