We shouldn't hold dpm_list_mtx while executing
[disable|enable]_nonboot_cpus(), because theoretically this may lead
to a deadlock as shown by the following example (provided by Johannes
Berg):
CPU 3 CPU 2 CPU 1
suspend/hibernate
something:
rtnl_lock() device_pm_lock()
-> mutex_lock(&dpm_list_mtx)
mutex_lock(&dpm_list_mtx)
linkwatch_work
-> rtnl_lock()
disable_nonboot_cpus()
-> flush CPU 3 workqueue
Fortunately, device drivers are supposed to stop any activities that
might lead to the registration of new device objects way before
disable_nonboot_cpus() is called, so it shouldn't be necessary to
hold dpm_list_mtx over the entire late part of device suspend and
early part of device resume.
Thus, during the late suspend and the early resume of devices acquire
dpm_list_mtx only when dpm_list is going to be traversed and release
it right after that.
This patch is reported to fix the regressions tracked as
http://bugzilla.kernel.org/show_bug.cgi?id=13245.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Reported-by: Miles Lane <miles.lane@gmail.com>
Tested-by: Ming Lei <tom.leiming@gmail.com>
+ mutex_lock(&dpm_list_mtx);
list_for_each_entry(dev, &dpm_list, power.entry)
if (dev->power.status > DPM_OFF) {
int error;
list_for_each_entry(dev, &dpm_list, power.entry)
if (dev->power.status > DPM_OFF) {
int error;
if (error)
pm_dev_err(dev, state, " early", error);
}
if (error)
pm_dev_err(dev, state, " early", error);
}
+ mutex_unlock(&dpm_list_mtx);
int error = 0;
suspend_device_irqs();
int error = 0;
suspend_device_irqs();
+ mutex_lock(&dpm_list_mtx);
list_for_each_entry_reverse(dev, &dpm_list, power.entry) {
error = suspend_device_noirq(dev, state);
if (error) {
list_for_each_entry_reverse(dev, &dpm_list, power.entry) {
error = suspend_device_noirq(dev, state);
if (error) {
}
dev->power.status = DPM_OFF_IRQ;
}
}
dev->power.status = DPM_OFF_IRQ;
}
+ mutex_unlock(&dpm_list_mtx);
if (error)
device_power_up(resume_event(state));
return error;
if (error)
device_power_up(resume_event(state));
return error;
error = device_suspend(PMSG_FREEZE);
if (error)
goto Resume_console;
error = device_suspend(PMSG_FREEZE);
if (error)
goto Resume_console;
/* At this point, device_suspend() has been called,
* but *not* device_power_down(). We *must*
* device_power_down() now. Otherwise, drivers for
/* At this point, device_suspend() has been called,
* but *not* device_power_down(). We *must*
* device_power_down() now. Otherwise, drivers for
enable_nonboot_cpus();
device_power_up(PMSG_RESTORE);
Resume_devices:
enable_nonboot_cpus();
device_power_up(PMSG_RESTORE);
Resume_devices:
device_resume(PMSG_RESTORE);
Resume_console:
resume_console();
device_resume(PMSG_RESTORE);
Resume_console:
resume_console();
/* At this point, device_suspend() has been called, but *not*
* device_power_down(). We *must* call device_power_down() now.
* Otherwise, drivers for some devices (e.g. interrupt controllers)
/* At this point, device_suspend() has been called, but *not*
* device_power_down(). We *must* call device_power_down() now.
* Otherwise, drivers for some devices (e.g. interrupt controllers)
if (error) {
printk(KERN_ERR "PM: Some devices failed to power down, "
"aborting hibernation\n");
if (error) {
printk(KERN_ERR "PM: Some devices failed to power down, "
"aborting hibernation\n");
}
error = platform_pre_snapshot(platform_mode);
}
error = platform_pre_snapshot(platform_mode);
device_power_up(in_suspend ?
(error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE);
device_power_up(in_suspend ?
(error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE);
- Unlock:
- device_pm_unlock();
-
error = device_power_down(PMSG_QUIESCE);
if (error) {
printk(KERN_ERR "PM: Some devices failed to power down, "
"aborting resume\n");
error = device_power_down(PMSG_QUIESCE);
if (error) {
printk(KERN_ERR "PM: Some devices failed to power down, "
"aborting resume\n");
}
error = platform_pre_restore(platform_mode);
}
error = platform_pre_restore(platform_mode);
device_power_up(PMSG_RECOVER);
device_power_up(PMSG_RECOVER);
- Unlock:
- device_pm_unlock();
-
error = device_power_down(PMSG_HIBERNATE);
if (error)
error = device_power_down(PMSG_HIBERNATE);
if (error)
error = hibernation_ops->prepare();
if (error)
error = hibernation_ops->prepare();
if (error)
device_power_up(PMSG_RESTORE);
device_power_up(PMSG_RESTORE);
- Unlock:
- device_pm_unlock();
-
Resume_devices:
entering_platform_hibernation = false;
device_resume(PMSG_RESTORE);
Resume_devices:
entering_platform_hibernation = false;
device_resume(PMSG_RESTORE);
if (suspend_ops->prepare) {
error = suspend_ops->prepare();
if (error)
if (suspend_ops->prepare) {
error = suspend_ops->prepare();
if (error)
}
error = device_power_down(PMSG_SUSPEND);
}
error = device_power_down(PMSG_SUSPEND);
if (suspend_ops->finish)
suspend_ops->finish();
if (suspend_ops->finish)
suspend_ops->finish();
- Done:
- device_pm_unlock();
-