* Arun R Bharadwaj <arun@xxxxxxxxxxxxxxxxxx> [2009-10-08 15:18:28]: This patch cleans up drivers/cpuidle/cpuidle.c Earlier cpuidle assumed pm_idle as the default idle loop. Break that assumption and make it more generic. cpuidle_idle_call() which is the main idle loop of cpuidle is to be called by architectures which have registered to cpuidle. Remove routines cpuidle_install/uninstall_idle_handler() which are not needed anymore. Signed-off-by: Arun R Bharadwaj <arun@xxxxxxxxxxxxxxxxxx> --- drivers/cpuidle/cpuidle.c | 62 +++++---------------------------------------- drivers/cpuidle/cpuidle.h | 6 +--- drivers/cpuidle/driver.c | 4 -- drivers/cpuidle/governor.c | 13 +++------ drivers/cpuidle/sysfs.c | 34 +++++++++++++----------- include/linux/cpuidle.h | 4 ++ 6 files changed, 37 insertions(+), 86 deletions(-) Index: linux.trees.git/drivers/cpuidle/cpuidle.c =================================================================== --- linux.trees.git.orig/drivers/cpuidle/cpuidle.c +++ linux.trees.git/drivers/cpuidle/cpuidle.c @@ -24,10 +24,6 @@ DEFINE_PER_CPU(struct cpuidle_device *, cpuidle_devices); DEFINE_MUTEX(cpuidle_lock); -LIST_HEAD(cpuidle_detected_devices); -static void (*pm_idle_old)(void); - -static int enabled_devices; #if defined(CONFIG_ARCH_HAS_CPU_IDLE_WAIT) static void cpuidle_kick_cpus(void) @@ -47,7 +43,7 @@ static int __cpuidle_register_device(str * * NOTE: no locks or semaphores should be used here */ -static void cpuidle_idle_call(void) +void cpuidle_idle_call(void) { struct cpuidle_device *dev = __get_cpu_var(cpuidle_devices); struct cpuidle_state *target_state; @@ -55,13 +51,10 @@ static void cpuidle_idle_call(void) /* check if the device is ready */ if (!dev || !dev->enabled) { - if (pm_idle_old) - pm_idle_old(); - else #if defined(CONFIG_ARCH_HAS_DEFAULT_IDLE) - default_idle(); + default_idle(); #else - local_irq_enable(); + local_irq_enable(); #endif return; } @@ -75,7 +68,11 @@ static void cpuidle_idle_call(void) hrtimer_peek_ahead_timers(); #endif /* ask the governor for the next state */ - next_state = cpuidle_curr_governor->select(dev); + if (dev->state_count > 1) + next_state = cpuidle_curr_governor->select(dev); + else + next_state = 0; + if (need_resched()) return; target_state = &dev->states[next_state]; @@ -96,35 +93,11 @@ static void cpuidle_idle_call(void) } /** - * cpuidle_install_idle_handler - installs the cpuidle idle loop handler - */ -void cpuidle_install_idle_handler(void) -{ - if (enabled_devices && (pm_idle != cpuidle_idle_call)) { - /* Make sure all changes finished before we switch to new idle */ - smp_wmb(); - pm_idle = cpuidle_idle_call; - } -} - -/** - * cpuidle_uninstall_idle_handler - uninstalls the cpuidle idle loop handler - */ -void cpuidle_uninstall_idle_handler(void) -{ - if (enabled_devices && pm_idle_old && (pm_idle != pm_idle_old)) { - pm_idle = pm_idle_old; - cpuidle_kick_cpus(); - } -} - -/** * cpuidle_pause_and_lock - temporarily disables CPUIDLE */ void cpuidle_pause_and_lock(void) { mutex_lock(&cpuidle_lock); - cpuidle_uninstall_idle_handler(); } EXPORT_SYMBOL_GPL(cpuidle_pause_and_lock); @@ -134,7 +107,6 @@ EXPORT_SYMBOL_GPL(cpuidle_pause_and_lock */ void cpuidle_resume_and_unlock(void) { - cpuidle_install_idle_handler(); mutex_unlock(&cpuidle_lock); } @@ -182,7 +154,6 @@ int cpuidle_enable_device(struct cpuidle dev->enabled = 1; - enabled_devices++; return 0; fail_sysfs: @@ -213,7 +184,6 @@ void cpuidle_disable_device(struct cpuid cpuidle_curr_governor->disable(dev); cpuidle_remove_state_sysfs(dev); - enabled_devices--; } EXPORT_SYMBOL_GPL(cpuidle_disable_device); @@ -266,7 +236,6 @@ static void poll_idle_init(struct cpuidl */ static int __cpuidle_register_device(struct cpuidle_device *dev) { - int ret; struct sys_device *sys_dev = get_cpu_sysdev((unsigned long)dev->cpu); if (!sys_dev) @@ -274,16 +243,9 @@ static int __cpuidle_register_device(str if (!try_module_get(cpuidle_curr_driver->owner)) return -EINVAL; - init_completion(&dev->kobj_unregister); - poll_idle_init(dev); per_cpu(cpuidle_devices, dev->cpu) = dev; - list_add(&dev->device_list, &cpuidle_detected_devices); - if ((ret = cpuidle_add_sysfs(sys_dev))) { - module_put(cpuidle_curr_driver->owner); - return ret; - } dev->registered = 1; return 0; @@ -305,7 +267,6 @@ int cpuidle_register_device(struct cpuid } cpuidle_enable_device(dev); - cpuidle_install_idle_handler(); mutex_unlock(&cpuidle_lock); @@ -321,8 +282,6 @@ EXPORT_SYMBOL_GPL(cpuidle_register_devic */ void cpuidle_unregister_device(struct cpuidle_device *dev) { - struct sys_device *sys_dev = get_cpu_sysdev((unsigned long)dev->cpu); - if (dev->registered == 0) return; @@ -330,9 +289,6 @@ void cpuidle_unregister_device(struct cp cpuidle_disable_device(dev); - cpuidle_remove_sysfs(sys_dev); - list_del(&dev->device_list); - wait_for_completion(&dev->kobj_unregister); per_cpu(cpuidle_devices, dev->cpu) = NULL; cpuidle_resume_and_unlock(); @@ -384,8 +340,6 @@ static int __init cpuidle_init(void) { int ret; - pm_idle_old = pm_idle; - ret = cpuidle_add_class_sysfs(&cpu_sysdev_class); if (ret) return ret; Index: linux.trees.git/drivers/cpuidle/governor.c =================================================================== --- linux.trees.git.orig/drivers/cpuidle/governor.c +++ linux.trees.git/drivers/cpuidle/governor.c @@ -43,16 +43,14 @@ static struct cpuidle_governor * __cpuid */ int cpuidle_switch_governor(struct cpuidle_governor *gov) { - struct cpuidle_device *dev; + int cpu; if (gov == cpuidle_curr_governor) return 0; - cpuidle_uninstall_idle_handler(); - if (cpuidle_curr_governor) { - list_for_each_entry(dev, &cpuidle_detected_devices, device_list) - cpuidle_disable_device(dev); + for_each_online_cpu(cpu) + cpuidle_disable_device(per_cpu(cpuidle_devices, cpu)); module_put(cpuidle_curr_governor->owner); } @@ -61,9 +59,8 @@ int cpuidle_switch_governor(struct cpuid if (gov) { if (!try_module_get(cpuidle_curr_governor->owner)) return -EINVAL; - list_for_each_entry(dev, &cpuidle_detected_devices, device_list) - cpuidle_enable_device(dev); - cpuidle_install_idle_handler(); + for_each_online_cpu(cpu) + cpuidle_enable_device(per_cpu(cpuidle_devices, cpu)); printk(KERN_INFO "cpuidle: using governor %s\n", gov->name); } Index: linux.trees.git/include/linux/cpuidle.h =================================================================== --- linux.trees.git.orig/include/linux/cpuidle.h +++ linux.trees.git/include/linux/cpuidle.h @@ -92,7 +92,6 @@ struct cpuidle_device { struct cpuidle_state_kobj *kobjs[CPUIDLE_STATE_MAX]; struct cpuidle_state *last_state; - struct list_head device_list; struct kobject kobj; struct completion kobj_unregister; void *governor_data; @@ -112,6 +111,9 @@ static inline int cpuidle_get_last_resid return dev->last_residency; } +extern void cpuidle_idle_call(void); +extern struct cpuidle_driver *cpuidle_curr_driver; + /**************************** * CPUIDLE DRIVER INTERFACE * Index: linux.trees.git/drivers/cpuidle/cpuidle.h =================================================================== --- linux.trees.git.orig/drivers/cpuidle/cpuidle.h +++ linux.trees.git/drivers/cpuidle/cpuidle.h @@ -9,9 +9,7 @@ /* For internal use only */ extern struct cpuidle_governor *cpuidle_curr_governor; -extern struct cpuidle_driver *cpuidle_curr_driver; extern struct list_head cpuidle_governors; -extern struct list_head cpuidle_detected_devices; extern struct mutex cpuidle_lock; extern spinlock_t cpuidle_driver_lock; @@ -27,7 +25,7 @@ extern int cpuidle_add_class_sysfs(struc extern void cpuidle_remove_class_sysfs(struct sysdev_class *cls); extern int cpuidle_add_state_sysfs(struct cpuidle_device *device); extern void cpuidle_remove_state_sysfs(struct cpuidle_device *device); -extern int cpuidle_add_sysfs(struct sys_device *sysdev); -extern void cpuidle_remove_sysfs(struct sys_device *sysdev); +extern int cpuidle_add_sysfs(struct cpuidle_device *device); +extern void cpuidle_remove_sysfs(struct cpuidle_device *device); #endif /* __DRIVER_CPUIDLE_H */ Index: linux.trees.git/drivers/cpuidle/sysfs.c =================================================================== --- linux.trees.git.orig/drivers/cpuidle/sysfs.c +++ linux.trees.git/drivers/cpuidle/sysfs.c @@ -311,6 +311,13 @@ int cpuidle_add_state_sysfs(struct cpuid int i, ret = -ENOMEM; struct cpuidle_state_kobj *kobj; + init_completion(&device->kobj_unregister); + + ret = cpuidle_add_sysfs(device); + if (ret) { + module_put(cpuidle_curr_driver->owner); + return ret; + } /* state statistics */ for (i = 0; i < device->state_count; i++) { kobj = kzalloc(sizeof(struct cpuidle_state_kobj), GFP_KERNEL); @@ -347,35 +354,32 @@ void cpuidle_remove_state_sysfs(struct c for (i = 0; i < device->state_count; i++) cpuidle_free_state_kobj(device, i); + + cpuidle_remove_sysfs(device); } /** * cpuidle_add_sysfs - creates a sysfs instance for the target device - * @sysdev: the target device + * @device: the target device */ -int cpuidle_add_sysfs(struct sys_device *sysdev) +int cpuidle_add_sysfs(struct cpuidle_device *device) { - int cpu = sysdev->id; - struct cpuidle_device *dev; int error; + struct sys_device *sysdev = get_cpu_sysdev((unsigned long)device->cpu); - dev = per_cpu(cpuidle_devices, cpu); - error = kobject_init_and_add(&dev->kobj, &ktype_cpuidle, &sysdev->kobj, - "cpuidle"); + error = kobject_init_and_add(&device->kobj, &ktype_cpuidle, + &sysdev->kobj, "cpuidle"); if (!error) - kobject_uevent(&dev->kobj, KOBJ_ADD); + kobject_uevent(&device->kobj, KOBJ_ADD); return error; } /** * cpuidle_remove_sysfs - deletes a sysfs instance on the target device - * @sysdev: the target device + * @device: the target device */ -void cpuidle_remove_sysfs(struct sys_device *sysdev) +void cpuidle_remove_sysfs(struct cpuidle_device *device) { - int cpu = sysdev->id; - struct cpuidle_device *dev; - - dev = per_cpu(cpuidle_devices, cpu); - kobject_put(&dev->kobj); + kobject_put(&device->kobj); + wait_for_completion(&device->kobj_unregister); } Index: linux.trees.git/drivers/cpuidle/driver.c =================================================================== --- linux.trees.git.orig/drivers/cpuidle/driver.c +++ linux.trees.git/drivers/cpuidle/driver.c @@ -27,10 +27,6 @@ int cpuidle_register_driver(struct cpuid return -EINVAL; spin_lock(&cpuidle_driver_lock); - if (cpuidle_curr_driver) { - spin_unlock(&cpuidle_driver_lock); - return -EBUSY; - } cpuidle_curr_driver = drv; spin_unlock(&cpuidle_driver_lock); -- To unsubscribe from this list: send the line "unsubscribe linux-arch" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html