cpufreq: Avoid cpufreq_suspend() deadlock on system shutdown
It is incorrect to set the cpufreq syscore shutdown callback pointer to cpufreq_suspend(), because that function cannot be run in the syscore stage of system shutdown for two reasons: (a) it may attempt to carry out actions depending on devices that have already been shut down at that point and (b) the RCU synchronization carried out by it may not be able to make progress then. The latter issue has been present since commit45975c7d21
("rcu: Define RCU-sched API in terms of RCU for Tree RCU PREEMPT builds"), but the former one has been there since commit90de2a4aa9
("cpufreq: suspend cpufreq governors on shutdown") regardless. Fix that by dropping cpufreq_syscore_ops altogether and making device_shutdown() call cpufreq_suspend() directly before shutting down devices, which is along the lines of what system-wide power management does. Fixes:45975c7d21
("rcu: Define RCU-sched API in terms of RCU for Tree RCU PREEMPT builds") Fixes:90de2a4aa9
("cpufreq: suspend cpufreq governors on shutdown") Reported-by: Ville Syrjälä <ville.syrjala@linux.intel.com> Tested-by: Ville Syrjälä <ville.syrjala@linux.intel.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Acked-by: Viresh Kumar <viresh.kumar@linaro.org> Cc: 4.0+ <stable@vger.kernel.org> # 4.0+
This commit is contained in:
parent
da0c9ea146
commit
65650b3513
|
@ -9,6 +9,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
|
#include <linux/cpufreq.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/fwnode.h>
|
#include <linux/fwnode.h>
|
||||||
|
@ -3179,6 +3180,8 @@ void device_shutdown(void)
|
||||||
wait_for_device_probe();
|
wait_for_device_probe();
|
||||||
device_block_probing();
|
device_block_probing();
|
||||||
|
|
||||||
|
cpufreq_suspend();
|
||||||
|
|
||||||
spin_lock(&devices_kset->list_lock);
|
spin_lock(&devices_kset->list_lock);
|
||||||
/*
|
/*
|
||||||
* Walk the devices list backward, shutting down each in turn.
|
* Walk the devices list backward, shutting down each in turn.
|
||||||
|
|
|
@ -2737,14 +2737,6 @@ int cpufreq_unregister_driver(struct cpufreq_driver *driver)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(cpufreq_unregister_driver);
|
EXPORT_SYMBOL_GPL(cpufreq_unregister_driver);
|
||||||
|
|
||||||
/*
|
|
||||||
* Stop cpufreq at shutdown to make sure it isn't holding any locks
|
|
||||||
* or mutexes when secondary CPUs are halted.
|
|
||||||
*/
|
|
||||||
static struct syscore_ops cpufreq_syscore_ops = {
|
|
||||||
.shutdown = cpufreq_suspend,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct kobject *cpufreq_global_kobject;
|
struct kobject *cpufreq_global_kobject;
|
||||||
EXPORT_SYMBOL(cpufreq_global_kobject);
|
EXPORT_SYMBOL(cpufreq_global_kobject);
|
||||||
|
|
||||||
|
@ -2756,8 +2748,6 @@ static int __init cpufreq_core_init(void)
|
||||||
cpufreq_global_kobject = kobject_create_and_add("cpufreq", &cpu_subsys.dev_root->kobj);
|
cpufreq_global_kobject = kobject_create_and_add("cpufreq", &cpu_subsys.dev_root->kobj);
|
||||||
BUG_ON(!cpufreq_global_kobject);
|
BUG_ON(!cpufreq_global_kobject);
|
||||||
|
|
||||||
register_syscore_ops(&cpufreq_syscore_ops);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
module_param(off, int, 0444);
|
module_param(off, int, 0444);
|
||||||
|
|
Loading…
Reference in New Issue