diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index c3a93fece819..fdc432f18022 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -466,7 +466,7 @@ void cpuidle_unregister(struct cpuidle_driver *drv) int cpu; struct cpuidle_device *device; - for_each_possible_cpu(cpu) { + for_each_cpu(cpu, drv->cpumask) { device = &per_cpu(cpuidle_dev, cpu); cpuidle_unregister_device(device); } @@ -498,7 +498,7 @@ int cpuidle_register(struct cpuidle_driver *drv, return ret; } - for_each_possible_cpu(cpu) { + for_each_cpu(cpu, drv->cpumask) { device = &per_cpu(cpuidle_dev, cpu); device->cpu = cpu; diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c index 8dfaaae94444..e75fa5472a91 100644 --- a/drivers/cpuidle/driver.c +++ b/drivers/cpuidle/driver.c @@ -18,8 +18,71 @@ DEFINE_SPINLOCK(cpuidle_driver_lock); -static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu); -static struct cpuidle_driver * __cpuidle_get_cpu_driver(int cpu); +#ifdef CONFIG_CPU_IDLE_MULTIPLE_DRIVERS + +static DEFINE_PER_CPU(struct cpuidle_driver *, cpuidle_drivers); + +static struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) +{ + return per_cpu(cpuidle_drivers, cpu); +} + +static inline void __cpuidle_unset_driver(struct cpuidle_driver *drv) +{ + int cpu; + + for_each_cpu(cpu, drv->cpumask) { + + if (drv != __cpuidle_get_cpu_driver(cpu)) + continue; + + per_cpu(cpuidle_drivers, cpu) = NULL; + } +} + +static inline int __cpuidle_set_driver(struct cpuidle_driver *drv) +{ + int cpu; + + for_each_cpu(cpu, drv->cpumask) { + + if (__cpuidle_get_cpu_driver(cpu)) { + __cpuidle_unset_driver(drv); + return -EBUSY; + } + + per_cpu(cpuidle_drivers, cpu) = drv; + } + + return 0; +} + +#else + +static struct cpuidle_driver *cpuidle_curr_driver; + +static inline struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) +{ + return cpuidle_curr_driver; +} + +static inline int __cpuidle_set_driver(struct cpuidle_driver *drv) +{ + if (cpuidle_curr_driver) + return -EBUSY; + + cpuidle_curr_driver = drv; + + return 0; +} + +static inline void __cpuidle_unset_driver(struct cpuidle_driver *drv) +{ + if (drv == cpuidle_curr_driver) + cpuidle_curr_driver = NULL; +} + +#endif static void cpuidle_setup_broadcast_timer(void *arg) { @@ -27,116 +90,68 @@ static void cpuidle_setup_broadcast_timer(void *arg) clockevents_notify((long)(arg), &cpu); } -static void __cpuidle_driver_init(struct cpuidle_driver *drv, int cpu) +static int __cpuidle_driver_init(struct cpuidle_driver *drv) { int i; drv->refcnt = 0; + if (!drv->cpumask) + drv->cpumask = (struct cpumask *)cpu_possible_mask; + for (i = drv->state_count - 1; i >= 0 ; i--) { if (!(drv->states[i].flags & CPUIDLE_FLAG_TIMER_STOP)) continue; drv->bctimer = 1; - on_each_cpu_mask(get_cpu_mask(cpu), cpuidle_setup_broadcast_timer, - (void *)CLOCK_EVT_NOTIFY_BROADCAST_ON, 1); break; } + + return 0; } -static int __cpuidle_register_driver(struct cpuidle_driver *drv, int cpu) +static int __cpuidle_register_driver(struct cpuidle_driver *drv) { + int ret; + if (!drv || !drv->state_count) return -EINVAL; if (cpuidle_disabled()) return -ENODEV; - if (__cpuidle_get_cpu_driver(cpu)) - return -EBUSY; + ret = __cpuidle_driver_init(drv); + if (ret) + return ret; - __cpuidle_driver_init(drv, cpu); + ret = __cpuidle_set_driver(drv); + if (ret) + return ret; - __cpuidle_set_cpu_driver(drv, cpu); + if (drv->bctimer) + on_each_cpu_mask(drv->cpumask, cpuidle_setup_broadcast_timer, + (void *)CLOCK_EVT_NOTIFY_BROADCAST_ON, 1); return 0; } -static void __cpuidle_unregister_driver(struct cpuidle_driver *drv, int cpu) +/** + * cpuidle_unregister_driver - unregisters a driver + * @drv: the driver + */ +static void __cpuidle_unregister_driver(struct cpuidle_driver *drv) { - if (drv != __cpuidle_get_cpu_driver(cpu)) + if (WARN_ON(drv->refcnt > 0)) return; - if (!WARN_ON(drv->refcnt > 0)) - __cpuidle_set_cpu_driver(NULL, cpu); - if (drv->bctimer) { drv->bctimer = 0; - on_each_cpu_mask(get_cpu_mask(cpu), cpuidle_setup_broadcast_timer, + on_each_cpu_mask(drv->cpumask, cpuidle_setup_broadcast_timer, (void *)CLOCK_EVT_NOTIFY_BROADCAST_OFF, 1); } -} -#ifdef CONFIG_CPU_IDLE_MULTIPLE_DRIVERS - -static DEFINE_PER_CPU(struct cpuidle_driver *, cpuidle_drivers); - -static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu) -{ - per_cpu(cpuidle_drivers, cpu) = drv; -} - -static struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) -{ - return per_cpu(cpuidle_drivers, cpu); -} - -static void __cpuidle_unregister_all_cpu_driver(struct cpuidle_driver *drv) -{ - int cpu; - for_each_present_cpu(cpu) - __cpuidle_unregister_driver(drv, cpu); -} - -static int __cpuidle_register_all_cpu_driver(struct cpuidle_driver *drv) -{ - int ret = 0; - int i, cpu; - - for_each_present_cpu(cpu) { - ret = __cpuidle_register_driver(drv, cpu); - if (ret) - break; - } - - if (ret) - for_each_present_cpu(i) { - if (i == cpu) - break; - __cpuidle_unregister_driver(drv, i); - } - - - return ret; -} - -int cpuidle_register_cpu_driver(struct cpuidle_driver *drv, int cpu) -{ - int ret; - - spin_lock(&cpuidle_driver_lock); - ret = __cpuidle_register_driver(drv, cpu); - spin_unlock(&cpuidle_driver_lock); - - return ret; -} - -void cpuidle_unregister_cpu_driver(struct cpuidle_driver *drv, int cpu) -{ - spin_lock(&cpuidle_driver_lock); - __cpuidle_unregister_driver(drv, cpu); - spin_unlock(&cpuidle_driver_lock); + __cpuidle_unset_driver(drv); } /** @@ -148,7 +163,7 @@ int cpuidle_register_driver(struct cpuidle_driver *drv) int ret; spin_lock(&cpuidle_driver_lock); - ret = __cpuidle_register_all_cpu_driver(drv); + ret = __cpuidle_register_driver(drv); spin_unlock(&cpuidle_driver_lock); return ret; @@ -162,60 +177,11 @@ EXPORT_SYMBOL_GPL(cpuidle_register_driver); void cpuidle_unregister_driver(struct cpuidle_driver *drv) { spin_lock(&cpuidle_driver_lock); - __cpuidle_unregister_all_cpu_driver(drv); + __cpuidle_unregister_driver(drv); spin_unlock(&cpuidle_driver_lock); } EXPORT_SYMBOL_GPL(cpuidle_unregister_driver); -#else - -static struct cpuidle_driver *cpuidle_curr_driver; - -static inline void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu) -{ - cpuidle_curr_driver = drv; -} - -static inline struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) -{ - return cpuidle_curr_driver; -} - -/** - * cpuidle_register_driver - registers a driver - * @drv: the driver - */ -int cpuidle_register_driver(struct cpuidle_driver *drv) -{ - int ret, cpu; - - cpu = get_cpu(); - spin_lock(&cpuidle_driver_lock); - ret = __cpuidle_register_driver(drv, cpu); - spin_unlock(&cpuidle_driver_lock); - put_cpu(); - - return ret; -} -EXPORT_SYMBOL_GPL(cpuidle_register_driver); - -/** - * cpuidle_unregister_driver - unregisters a driver - * @drv: the driver - */ -void cpuidle_unregister_driver(struct cpuidle_driver *drv) -{ - int cpu; - - cpu = get_cpu(); - spin_lock(&cpuidle_driver_lock); - __cpuidle_unregister_driver(drv, cpu); - spin_unlock(&cpuidle_driver_lock); - put_cpu(); -} -EXPORT_SYMBOL_GPL(cpuidle_unregister_driver); -#endif - /** * cpuidle_get_driver - return the current driver */ diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 8f0406230a0a..0bc4b74668e9 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -111,6 +111,9 @@ struct cpuidle_driver { struct cpuidle_state states[CPUIDLE_STATE_MAX]; int state_count; int safe_state_index; + + /* the driver handles the cpus in cpumask */ + struct cpumask *cpumask; }; #ifdef CONFIG_CPU_IDLE @@ -135,9 +138,6 @@ extern void cpuidle_disable_device(struct cpuidle_device *dev); extern int cpuidle_play_dead(void); extern struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev); -extern int cpuidle_register_cpu_driver(struct cpuidle_driver *drv, int cpu); -extern void cpuidle_unregister_cpu_driver(struct cpuidle_driver *drv, int cpu); - #else static inline void disable_cpuidle(void) { } static inline int cpuidle_idle_call(void) { return -ENODEV; }