* Arun R Bharadwaj <a...@linux.vnet.ibm.com> [2009-10-08 15:18:28]:

This patch cleans up x86 of all instances of pm_idle.

pm_idle which was earlier called from cpu_idle() idle loop
is replaced by cpuidle_idle_call.

x86 also registers to cpuidle when the idle routine is selected,
by populating the cpuidle_device data structure for each cpu.

This is replicated for apm module and for xen, which also used pm_idle.


Signed-off-by: Arun R Bharadwaj <a...@linux.vnet.ibm.com>
---
 arch/x86/kernel/apm_32.c      |   55 +++++++++++++++++++++++-
 arch/x86/kernel/process.c     |   93 ++++++++++++++++++++++++++----------------
 arch/x86/kernel/process_32.c  |    3 -
 arch/x86/kernel/process_64.c  |    3 -
 arch/x86/xen/setup.c          |   40 +++++++++++++++++-
 drivers/acpi/processor_core.c |    9 ++--
 drivers/cpuidle/cpuidle.c     |   16 ++++---
 include/linux/cpuidle.h       |    1 
 8 files changed, 172 insertions(+), 48 deletions(-)

Index: linux.trees.git/arch/x86/kernel/process.c
===================================================================
--- linux.trees.git.orig/arch/x86/kernel/process.c
+++ linux.trees.git/arch/x86/kernel/process.c
@@ -9,6 +9,7 @@
 #include <linux/pm.h>
 #include <linux/clockchips.h>
 #include <linux/random.h>
+#include <linux/cpuidle.h>
 #include <trace/events/power.h>
 #include <asm/system.h>
 #include <asm/apic.h>
@@ -244,12 +245,6 @@ int sys_vfork(struct pt_regs *regs)
 unsigned long boot_option_idle_override = 0;
 EXPORT_SYMBOL(boot_option_idle_override);
 
-/*
- * Powermanagement idle function, if any..
- */
-void (*pm_idle)(void);
-EXPORT_SYMBOL(pm_idle);
-
 #ifdef CONFIG_X86_32
 /*
  * This halt magic was a workaround for ancient floppy DMA
@@ -329,17 +324,15 @@ static void do_nothing(void *unused)
 }
 
 /*
- * cpu_idle_wait - Used to ensure that all the CPUs discard old value of
- * pm_idle and update to new pm_idle value. Required while changing pm_idle
- * handler on SMP systems.
+ * cpu_idle_wait - Required while changing idle routine handler on SMP systems.
  *
- * Caller must have changed pm_idle to the new value before the call. Old
- * pm_idle value will not be used by any CPU after the return of this function.
+ * Caller must have changed idle routine to the new value before the call. Old
+ * value will not be used by any CPU after the return of this function.
  */
 void cpu_idle_wait(void)
 {
        smp_mb();
-       /* kick all the CPUs so that they exit out of pm_idle */
+       /* kick all the CPUs so that they exit out of idle loop */
        smp_call_function(do_nothing, NULL, 1);
 }
 EXPORT_SYMBOL_GPL(cpu_idle_wait);
@@ -387,20 +380,6 @@ static void mwait_idle(void)
 }
 
 /*
- * 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)
-{
-       trace_power_start(POWER_CSTATE, 0);
-       local_irq_enable();
-       while (!need_resched())
-               cpu_relax();
-       trace_power_end(0);
-}
-
-/*
  * mwait selection logic:
  *
  * It depends on the CPU. For AMD CPUs that support MWAIT this is
@@ -518,15 +497,59 @@ static void c1e_idle(void)
                default_idle();
 }
 
+static void (*local_idle)(void);
+DEFINE_PER_CPU(struct cpuidle_device, idle_devices);
+
+struct cpuidle_driver cpuidle_default_driver = {
+       .name =         "cpuidle_default",
+};
+
+static int local_idle_loop(struct cpuidle_device *dev, struct cpuidle_state 
*st)
+{
+       ktime_t t1, t2;
+       s64 diff;
+       int ret;
+
+       t1 = ktime_get();
+       local_idle();
+       t2 = ktime_get();
+
+       diff = ktime_to_us(ktime_sub(t2, t1));
+       if (diff > INT_MAX)
+               diff = INT_MAX;
+       ret = (int) diff;
+
+       return ret;
+}
+
+static int setup_cpuidle_simple(void)
+{
+       struct cpuidle_device *dev;
+       int cpu;
+
+       if (!cpuidle_curr_driver)
+               cpuidle_register_driver(&cpuidle_default_driver);
+
+       for_each_online_cpu(cpu) {
+               dev = &per_cpu(idle_devices, cpu);
+               dev->cpu = cpu;
+               dev->states[0].enter = local_idle_loop;
+               dev->state_count = 1;
+               cpuidle_register_device(dev);
+       }
+       return 0;
+}
+device_initcall(setup_cpuidle_simple);
+
 void __cpuinit select_idle_routine(const struct cpuinfo_x86 *c)
 {
 #ifdef CONFIG_SMP
-       if (pm_idle == poll_idle && smp_num_siblings > 1) {
+       if (local_idle == poll_idle && smp_num_siblings > 1) {
                printk(KERN_WARNING "WARNING: polling idle and HT enabled,"
                        " performance may degrade.\n");
        }
 #endif
-       if (pm_idle)
+       if (local_idle)
                return;
 
        if (cpu_has(c, X86_FEATURE_MWAIT) && mwait_usable(c)) {
@@ -534,18 +557,20 @@ void __cpuinit select_idle_routine(const
                 * One CPU supports mwait => All CPUs supports mwait
                 */
                printk(KERN_INFO "using mwait in idle threads.\n");
-               pm_idle = mwait_idle;
+               local_idle = mwait_idle;
        } else if (check_c1e_idle(c)) {
                printk(KERN_INFO "using C1E aware idle routine\n");
-               pm_idle = c1e_idle;
+               local_idle = c1e_idle;
        } else
-               pm_idle = default_idle;
+               local_idle = default_idle;
+
+       return;
 }
 
 void __init init_c1e_mask(void)
 {
        /* If we're using c1e_idle, we need to allocate c1e_mask. */
-       if (pm_idle == c1e_idle)
+       if (local_idle == c1e_idle)
                zalloc_cpumask_var(&c1e_mask, GFP_KERNEL);
 }
 
@@ -556,7 +581,7 @@ static int __init idle_setup(char *str)
 
        if (!strcmp(str, "poll")) {
                printk("using polling idle threads.\n");
-               pm_idle = poll_idle;
+               local_idle = poll_idle;
        } else if (!strcmp(str, "mwait"))
                force_mwait = 1;
        else if (!strcmp(str, "halt")) {
@@ -567,7 +592,7 @@ static int __init idle_setup(char *str)
                 * To continue to load the CPU idle driver, don't touch
                 * the boot_option_idle_override.
                 */
-               pm_idle = default_idle;
+               local_idle = default_idle;
                idle_halt = 1;
                return 0;
        } else if (!strcmp(str, "nomwait")) {
Index: linux.trees.git/arch/x86/kernel/process_32.c
===================================================================
--- linux.trees.git.orig/arch/x86/kernel/process_32.c
+++ linux.trees.git/arch/x86/kernel/process_32.c
@@ -40,6 +40,7 @@
 #include <linux/uaccess.h>
 #include <linux/io.h>
 #include <linux/kdebug.h>
+#include <linux/cpuidle.h>
 
 #include <asm/pgtable.h>
 #include <asm/system.h>
@@ -113,7 +114,7 @@ void cpu_idle(void)
                        local_irq_disable();
                        /* Don't trace irqs off for idle */
                        stop_critical_timings();
-                       pm_idle();
+                       cpuidle_idle_call();
                        start_critical_timings();
                }
                tick_nohz_restart_sched_tick();
Index: linux.trees.git/arch/x86/kernel/process_64.c
===================================================================
--- linux.trees.git.orig/arch/x86/kernel/process_64.c
+++ linux.trees.git/arch/x86/kernel/process_64.c
@@ -39,6 +39,7 @@
 #include <linux/io.h>
 #include <linux/ftrace.h>
 #include <linux/dmi.h>
+#include <linux/cpuidle.h>
 
 #include <asm/pgtable.h>
 #include <asm/system.h>
@@ -142,7 +143,7 @@ void cpu_idle(void)
                        enter_idle();
                        /* Don't trace irqs off for idle */
                        stop_critical_timings();
-                       pm_idle();
+                       cpuidle_idle_call();
                        start_critical_timings();
                        /* In many cases the interrupt that ended idle
                           has already called exit_idle. But some idle
Index: linux.trees.git/arch/x86/kernel/apm_32.c
===================================================================
--- linux.trees.git.orig/arch/x86/kernel/apm_32.c
+++ linux.trees.git/arch/x86/kernel/apm_32.c
@@ -2257,6 +2257,56 @@ static struct dmi_system_id __initdata a
        { }
 };
 
+DEFINE_PER_CPU(struct cpuidle_device, apm_idle_devices);
+
+struct cpuidle_driver cpuidle_apm_driver = {
+       .name =         "cpuidle_apm",
+};
+
+static int apm_idle_loop(struct cpuidle_device *dev, struct cpuidle_state *st)
+{
+       ktime_t t1, t2;
+       s64 diff;
+       int ret;
+
+       t1 = ktime_get();
+       apm_cpu_idle();
+       t2 = ktime_get();
+
+       diff = ktime_to_us(ktime_sub(t2, t1));
+       if (diff > INT_MAX)
+               diff = INT_MAX;
+       ret = (int) diff;
+
+       return ret;
+}
+
+void __cpuinit setup_cpuidle_apm(void)
+{
+       struct cpuidle_device *dev;
+
+       if (!cpuidle_curr_driver)
+               cpuidle_register_driver(&cpuidle_apm_driver);
+
+       dev = &per_cpu(apm_idle_devices, smp_processor_id());
+       dev->cpu = smp_processor_id();
+       dev->states[0].enter = apm_idle_loop;
+       dev->state_count = 1;
+       cpuidle_register_device(dev);
+}
+
+void exit_cpuidle_apm(void)
+{
+       struct cpuidle_device *dev;
+       int cpu;
+
+       for_each_online_cpu(cpu) {
+               dev = &per_cpu(apm_idle_devices, cpu);
+               cpuidle_unregister_device(dev);
+       }
+}
+
+
 /*
  * Just start the APM thread. We do NOT want to do APM BIOS
  * calls from anything but the APM thread, if for no other reason
@@ -2394,8 +2444,7 @@ static int __init apm_init(void)
        if (HZ != 100)
                idle_period = (idle_period * HZ) / 100;
        if (idle_threshold < 100) {
-               original_pm_idle = pm_idle;
-               pm_idle  = apm_cpu_idle;
+               setup_cpuidle_apm();
                set_pm_idle = 1;
        }
 
@@ -2407,7 +2456,7 @@ static void __exit apm_exit(void)
        int error;
 
        if (set_pm_idle) {
-               pm_idle = original_pm_idle;
+               exit_cpuidle_apm();
                /*
                 * We are about to unload the current idle thread pm callback
                 * (pm_idle), Wait for all processors to update cached/local
Index: linux.trees.git/arch/x86/xen/setup.c
===================================================================
--- linux.trees.git.orig/arch/x86/xen/setup.c
+++ linux.trees.git/arch/x86/xen/setup.c
@@ -8,6 +8,7 @@
 #include <linux/sched.h>
 #include <linux/mm.h>
 #include <linux/pm.h>
+#include <linux/cpuidle.h>
 
 #include <asm/elf.h>
 #include <asm/vdso.h>
@@ -151,6 +152,43 @@ void __cpuinit xen_enable_syscall(void)
 #endif /* CONFIG_X86_64 */
 }
 
+DEFINE_PER_CPU(struct cpuidle_device, xen_idle_devices);
+struct cpuidle_driver cpuidle_xen_driver = {
+       .name =         "cpuidle_xen",
+};
+
+static int xen_idle_loop(struct cpuidle_device *dev, struct cpuidle_state *st)
+{
+       ktime_t t1, t2;
+       s64 diff;
+       int ret;
+
+       t1 = ktime_get();
+       xen_idle();
+       t2 = ktime_get();
+
+       diff = ktime_to_us(ktime_sub(t2, t1));
+       if (diff > INT_MAX)
+               diff = INT_MAX;
+       ret = (int) diff;
+
+       return ret;
+}
+
+void __cpuinit setup_cpuidle_xen(void)
+{
+       struct cpuidle_device *dev;
+
+       if (!cpuidle_curr_driver)
+               cpuidle_register_driver(&cpuidle_xen_driver);
+
+       dev = &per_cpu(xen_idle_devices, smp_processor_id());
+       dev->cpu = smp_processor_id();
+       dev->states[0].enter = xen_idle_loop;
+       dev->state_count = 1;
+       cpuidle_register_device(dev);
+}
+
 void __init xen_arch_setup(void)
 {
        struct physdev_set_iopl set_iopl;
@@ -186,7 +224,7 @@ void __init xen_arch_setup(void)
               MAX_GUEST_CMDLINE > COMMAND_LINE_SIZE ?
               COMMAND_LINE_SIZE : MAX_GUEST_CMDLINE);
 
-       pm_idle = xen_idle;
+       setup_cpuidle_xen();
 
        paravirt_disable_iospace();
 
Index: linux.trees.git/drivers/acpi/processor_core.c
===================================================================
--- linux.trees.git.orig/drivers/acpi/processor_core.c
+++ linux.trees.git/drivers/acpi/processor_core.c
@@ -1150,9 +1150,12 @@ static int __init acpi_processor_init(vo
         * should not use mwait for CPU-states.
         */
        dmi_check_system(processor_idle_dmi_table);
-       result = cpuidle_register_driver(&acpi_idle_driver);
-       if (result < 0)
-               goto out_proc;
+
+       if (!boot_option_idle_override) {
+               result = cpuidle_register_driver(&acpi_idle_driver);
+               if (result < 0)
+                       goto out_proc;
+       }
 
        result = acpi_bus_register_driver(&acpi_processor_driver);
        if (result < 0)
Index: linux.trees.git/drivers/cpuidle/cpuidle.c
===================================================================
--- linux.trees.git.orig/drivers/cpuidle/cpuidle.c
+++ linux.trees.git/drivers/cpuidle/cpuidle.c
@@ -224,16 +224,22 @@ void cpuidle_disable_device(struct cpuid
 EXPORT_SYMBOL_GPL(cpuidle_disable_device);
 
 #ifdef CONFIG_ARCH_HAS_CPU_RELAX
-static int poll_idle(struct cpuidle_device *dev, struct cpuidle_state *st)
+void poll_idle(void)
+{
+       local_irq_enable();
+       while (!need_resched())
+               cpu_relax();
+}
+
+static int poll_idle_loop(struct cpuidle_device *dev, struct cpuidle_state *st)
 {
        ktime_t t1, t2;
        s64 diff;
        int ret;
 
        t1 = ktime_get();
-       local_irq_enable();
-       while (!need_resched())
-               cpu_relax();
+
+       poll_idle();
 
        t2 = ktime_get();
        diff = ktime_to_us(ktime_sub(t2, t1));
@@ -256,7 +262,7 @@ static void poll_idle_init(struct cpuidl
        state->target_residency = 0;
        state->power_usage = -1;
        state->flags = CPUIDLE_FLAG_POLL;
-       state->enter = poll_idle;
+       state->enter = poll_idle_loop;
 }
 #else
 static void poll_idle_init(struct cpuidle_device *dev) {}
Index: linux.trees.git/include/linux/cpuidle.h
===================================================================
--- linux.trees.git.orig/include/linux/cpuidle.h
+++ linux.trees.git/include/linux/cpuidle.h
@@ -187,6 +187,7 @@ static inline void cpuidle_unregister_go
 
 #ifdef CONFIG_ARCH_HAS_CPU_RELAX
 #define CPUIDLE_DRIVER_STATE_START     1
+extern void poll_idle(void);
 #else
 #define CPUIDLE_DRIVER_STATE_START     0
 #endif
_______________________________________________
Linuxppc-dev mailing list
Linuxppc-dev@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/linuxppc-dev

Reply via email to