PM: Do not hold dpm_list_mtx while disabling/enabling nonboot CPUs
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>
This commit is contained in:
@@ -357,6 +357,7 @@ static void dpm_power_up(pm_message_t state)
|
||||
{
|
||||
struct device *dev;
|
||||
|
||||
mutex_lock(&dpm_list_mtx);
|
||||
list_for_each_entry(dev, &dpm_list, power.entry)
|
||||
if (dev->power.status > DPM_OFF) {
|
||||
int error;
|
||||
@@ -366,6 +367,7 @@ static void dpm_power_up(pm_message_t state)
|
||||
if (error)
|
||||
pm_dev_err(dev, state, " early", error);
|
||||
}
|
||||
mutex_unlock(&dpm_list_mtx);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -614,6 +616,7 @@ int device_power_down(pm_message_t state)
|
||||
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) {
|
||||
@@ -622,6 +625,7 @@ int device_power_down(pm_message_t state)
|
||||
}
|
||||
dev->power.status = DPM_OFF_IRQ;
|
||||
}
|
||||
mutex_unlock(&dpm_list_mtx);
|
||||
if (error)
|
||||
device_power_up(resume_event(state));
|
||||
return error;
|
||||
|
Reference in New Issue
Block a user