mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-30 15:44:13 +08:00
cpuidle acpi driver: fix oops on AC<->DC
cpuidle and acpi driver interaction bug with the way cpuidle_register_driver() is called. Due to this bug, there will be oops on AC<->DC on some systems, where they support C-states in one DC and not in AC. The current code does ON BOOT: Look at CST and other C-state info to see whether more than C1 is supported. If it is, then acpi processor_idle does a cpuidle_register_driver() call, which internally enables the device. ON CST change notification (AC<->DC) and on suspend-resume: acpi driver temporarily disables device, updates the device with any new C-states, and reenables the device. The problem is is on boot, there are no C2, C3 states supported and we skip the register. Later on AC<->DC, we may get a CST notification and we try to reevaluate CST and enabled the device, without actually registering it. This causes breakage as we try to create /sys fs sub directory, without the parent directory which is created at register time. Thanks to Sanjeev for reporting the problem here. http://bugzilla.kernel.org/show_bug.cgi?id=10394 Signed-off-by: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com> Signed-off-by: Len Brown <len.brown@intel.com>
This commit is contained in:
parent
e1094bfa26
commit
dcb84f335b
@ -1669,6 +1669,7 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dev->cpu = pr->id;
|
||||||
for (i = 0; i < CPUIDLE_STATE_MAX; i++) {
|
for (i = 0; i < CPUIDLE_STATE_MAX; i++) {
|
||||||
dev->states[i].name[0] = '\0';
|
dev->states[i].name[0] = '\0';
|
||||||
dev->states[i].desc[0] = '\0';
|
dev->states[i].desc[0] = '\0';
|
||||||
@ -1738,7 +1739,7 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr)
|
|||||||
|
|
||||||
int acpi_processor_cst_has_changed(struct acpi_processor *pr)
|
int acpi_processor_cst_has_changed(struct acpi_processor *pr)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret = 0;
|
||||||
|
|
||||||
if (boot_option_idle_override)
|
if (boot_option_idle_override)
|
||||||
return 0;
|
return 0;
|
||||||
@ -1756,8 +1757,10 @@ int acpi_processor_cst_has_changed(struct acpi_processor *pr)
|
|||||||
cpuidle_pause_and_lock();
|
cpuidle_pause_and_lock();
|
||||||
cpuidle_disable_device(&pr->power.dev);
|
cpuidle_disable_device(&pr->power.dev);
|
||||||
acpi_processor_get_power_info(pr);
|
acpi_processor_get_power_info(pr);
|
||||||
acpi_processor_setup_cpuidle(pr);
|
if (pr->flags.power) {
|
||||||
ret = cpuidle_enable_device(&pr->power.dev);
|
acpi_processor_setup_cpuidle(pr);
|
||||||
|
ret = cpuidle_enable_device(&pr->power.dev);
|
||||||
|
}
|
||||||
cpuidle_resume_and_unlock();
|
cpuidle_resume_and_unlock();
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -1813,7 +1816,6 @@ int __cpuinit acpi_processor_power_init(struct acpi_processor *pr,
|
|||||||
if (pr->flags.power) {
|
if (pr->flags.power) {
|
||||||
#ifdef CONFIG_CPU_IDLE
|
#ifdef CONFIG_CPU_IDLE
|
||||||
acpi_processor_setup_cpuidle(pr);
|
acpi_processor_setup_cpuidle(pr);
|
||||||
pr->power.dev.cpu = pr->id;
|
|
||||||
if (cpuidle_register_device(&pr->power.dev))
|
if (cpuidle_register_device(&pr->power.dev))
|
||||||
return -EIO;
|
return -EIO;
|
||||||
#endif
|
#endif
|
||||||
@ -1850,8 +1852,7 @@ int acpi_processor_power_exit(struct acpi_processor *pr,
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
#ifdef CONFIG_CPU_IDLE
|
#ifdef CONFIG_CPU_IDLE
|
||||||
if (pr->flags.power)
|
cpuidle_unregister_device(&pr->power.dev);
|
||||||
cpuidle_unregister_device(&pr->power.dev);
|
|
||||||
#endif
|
#endif
|
||||||
pr->flags.power_setup_done = 0;
|
pr->flags.power_setup_done = 0;
|
||||||
|
|
||||||
|
@ -38,6 +38,8 @@ static void cpuidle_kick_cpus(void)
|
|||||||
static void cpuidle_kick_cpus(void) {}
|
static void cpuidle_kick_cpus(void) {}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static int __cpuidle_register_device(struct cpuidle_device *dev);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* cpuidle_idle_call - the main idle loop
|
* cpuidle_idle_call - the main idle loop
|
||||||
*
|
*
|
||||||
@ -138,6 +140,12 @@ int cpuidle_enable_device(struct cpuidle_device *dev)
|
|||||||
if (!dev->state_count)
|
if (!dev->state_count)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (dev->registered == 0) {
|
||||||
|
ret = __cpuidle_register_device(dev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
if ((ret = cpuidle_add_state_sysfs(dev)))
|
if ((ret = cpuidle_add_state_sysfs(dev)))
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@ -232,10 +240,13 @@ static void poll_idle_init(struct cpuidle_device *dev) {}
|
|||||||
#endif /* CONFIG_ARCH_HAS_CPU_RELAX */
|
#endif /* CONFIG_ARCH_HAS_CPU_RELAX */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* cpuidle_register_device - registers a CPU's idle PM feature
|
* __cpuidle_register_device - internal register function called before register
|
||||||
|
* and enable routines
|
||||||
* @dev: the cpu
|
* @dev: the cpu
|
||||||
|
*
|
||||||
|
* cpuidle_lock mutex must be held before this is called
|
||||||
*/
|
*/
|
||||||
int cpuidle_register_device(struct cpuidle_device *dev)
|
static int __cpuidle_register_device(struct cpuidle_device *dev)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
struct sys_device *sys_dev = get_cpu_sysdev((unsigned long)dev->cpu);
|
struct sys_device *sys_dev = get_cpu_sysdev((unsigned long)dev->cpu);
|
||||||
@ -247,18 +258,34 @@ int cpuidle_register_device(struct cpuidle_device *dev)
|
|||||||
|
|
||||||
init_completion(&dev->kobj_unregister);
|
init_completion(&dev->kobj_unregister);
|
||||||
|
|
||||||
mutex_lock(&cpuidle_lock);
|
|
||||||
|
|
||||||
poll_idle_init(dev);
|
poll_idle_init(dev);
|
||||||
|
|
||||||
per_cpu(cpuidle_devices, dev->cpu) = dev;
|
per_cpu(cpuidle_devices, dev->cpu) = dev;
|
||||||
list_add(&dev->device_list, &cpuidle_detected_devices);
|
list_add(&dev->device_list, &cpuidle_detected_devices);
|
||||||
if ((ret = cpuidle_add_sysfs(sys_dev))) {
|
if ((ret = cpuidle_add_sysfs(sys_dev))) {
|
||||||
mutex_unlock(&cpuidle_lock);
|
|
||||||
module_put(cpuidle_curr_driver->owner);
|
module_put(cpuidle_curr_driver->owner);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dev->registered = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cpuidle_register_device - registers a CPU's idle PM feature
|
||||||
|
* @dev: the cpu
|
||||||
|
*/
|
||||||
|
int cpuidle_register_device(struct cpuidle_device *dev)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
mutex_lock(&cpuidle_lock);
|
||||||
|
|
||||||
|
if ((ret = __cpuidle_register_device(dev))) {
|
||||||
|
mutex_unlock(&cpuidle_lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
cpuidle_enable_device(dev);
|
cpuidle_enable_device(dev);
|
||||||
cpuidle_install_idle_handler();
|
cpuidle_install_idle_handler();
|
||||||
|
|
||||||
@ -278,6 +305,9 @@ void cpuidle_unregister_device(struct cpuidle_device *dev)
|
|||||||
{
|
{
|
||||||
struct sys_device *sys_dev = get_cpu_sysdev((unsigned long)dev->cpu);
|
struct sys_device *sys_dev = get_cpu_sysdev((unsigned long)dev->cpu);
|
||||||
|
|
||||||
|
if (dev->registered == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
cpuidle_pause_and_lock();
|
cpuidle_pause_and_lock();
|
||||||
|
|
||||||
cpuidle_disable_device(dev);
|
cpuidle_disable_device(dev);
|
||||||
|
@ -82,6 +82,7 @@ struct cpuidle_state_kobj {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct cpuidle_device {
|
struct cpuidle_device {
|
||||||
|
unsigned int registered:1;
|
||||||
unsigned int enabled:1;
|
unsigned int enabled:1;
|
||||||
unsigned int cpu;
|
unsigned int cpu;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user