diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2009-05-24 15:15:07 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rjw@sisk.pl> | 2009-05-24 15:15:07 -0400 |
commit | 32bdfac5462d777f35b00838893c4f87baf23efe (patch) | |
tree | 92e4ef3af7b68007e8004eaca978865a29e543b0 /drivers | |
parent | 59a3759d0fe8d969888c741bb33f4946e4d3750d (diff) |
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>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/base/power/main.c | 4 |
1 files changed, 4 insertions, 0 deletions
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 69b4ddb7de3b..3e4bc699bc0f 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c | |||
@@ -357,6 +357,7 @@ static void dpm_power_up(pm_message_t state) | |||
357 | { | 357 | { |
358 | struct device *dev; | 358 | struct device *dev; |
359 | 359 | ||
360 | mutex_lock(&dpm_list_mtx); | ||
360 | list_for_each_entry(dev, &dpm_list, power.entry) | 361 | list_for_each_entry(dev, &dpm_list, power.entry) |
361 | if (dev->power.status > DPM_OFF) { | 362 | if (dev->power.status > DPM_OFF) { |
362 | int error; | 363 | int error; |
@@ -366,6 +367,7 @@ static void dpm_power_up(pm_message_t state) | |||
366 | if (error) | 367 | if (error) |
367 | pm_dev_err(dev, state, " early", error); | 368 | pm_dev_err(dev, state, " early", error); |
368 | } | 369 | } |
370 | mutex_unlock(&dpm_list_mtx); | ||
369 | } | 371 | } |
370 | 372 | ||
371 | /** | 373 | /** |
@@ -614,6 +616,7 @@ int device_power_down(pm_message_t state) | |||
614 | int error = 0; | 616 | int error = 0; |
615 | 617 | ||
616 | suspend_device_irqs(); | 618 | suspend_device_irqs(); |
619 | mutex_lock(&dpm_list_mtx); | ||
617 | list_for_each_entry_reverse(dev, &dpm_list, power.entry) { | 620 | list_for_each_entry_reverse(dev, &dpm_list, power.entry) { |
618 | error = suspend_device_noirq(dev, state); | 621 | error = suspend_device_noirq(dev, state); |
619 | if (error) { | 622 | if (error) { |
@@ -622,6 +625,7 @@ int device_power_down(pm_message_t state) | |||
622 | } | 625 | } |
623 | dev->power.status = DPM_OFF_IRQ; | 626 | dev->power.status = DPM_OFF_IRQ; |
624 | } | 627 | } |
628 | mutex_unlock(&dpm_list_mtx); | ||
625 | if (error) | 629 | if (error) |
626 | device_power_up(resume_event(state)); | 630 | device_power_up(resume_event(state)); |
627 | return error; | 631 | return error; |