PM: Allow devices to be removed during late suspend and early resume

Holding dpm_list_mtx across late suspend and early resume of devices
is problematic for the PCMCIA subsystem and doesn't allow device
objects to be removed by late suspend and early resume driver
callbacks.  This appears to be overly restrictive, as drivers are
generally allowed to remove device objects in other phases of suspend
and resume.  Therefore rework dpm_{suspend|resume}_noirq() so that
they don't have to hold dpm_list_mtx all the time.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
This commit is contained in:
Rafael J. Wysocki 2010-11-11 01:50:53 +01:00
Родитель f6614b7bb4
Коммит d08a5ace18
1 изменённых файлов: 30 добавлений и 4 удалений

Просмотреть файл

@ -475,20 +475,33 @@ End:
*/
void dpm_resume_noirq(pm_message_t state)
{
struct device *dev;
struct list_head list;
ktime_t starttime = ktime_get();
INIT_LIST_HEAD(&list);
mutex_lock(&dpm_list_mtx);
transition_started = false;
list_for_each_entry(dev, &dpm_list, power.entry)
while (!list_empty(&dpm_list)) {
struct device *dev = to_device(dpm_list.next);
get_device(dev);
if (dev->power.status > DPM_OFF) {
int error;
dev->power.status = DPM_OFF;
mutex_unlock(&dpm_list_mtx);
error = device_resume_noirq(dev, state);
mutex_lock(&dpm_list_mtx);
if (error)
pm_dev_err(dev, state, " early", error);
}
if (!list_empty(&dev->power.entry))
list_move_tail(&dev->power.entry, &list);
put_device(dev);
}
list_splice(&list, &dpm_list);
mutex_unlock(&dpm_list_mtx);
dpm_show_time(starttime, state, "early");
resume_device_irqs();
@ -789,20 +802,33 @@ End:
*/
int dpm_suspend_noirq(pm_message_t state)
{
struct device *dev;
struct list_head list;
ktime_t starttime = ktime_get();
int error = 0;
INIT_LIST_HEAD(&list);
suspend_device_irqs();
mutex_lock(&dpm_list_mtx);
list_for_each_entry_reverse(dev, &dpm_list, power.entry) {
while (!list_empty(&dpm_list)) {
struct device *dev = to_device(dpm_list.prev);
get_device(dev);
mutex_unlock(&dpm_list_mtx);
error = device_suspend_noirq(dev, state);
mutex_lock(&dpm_list_mtx);
if (error) {
pm_dev_err(dev, state, " late", error);
put_device(dev);
break;
}
dev->power.status = DPM_OFF_IRQ;
if (!list_empty(&dev->power.entry))
list_move(&dev->power.entry, &list);
put_device(dev);
}
list_splice_tail(&list, &dpm_list);
mutex_unlock(&dpm_list_mtx);
if (error)
dpm_resume_noirq(resume_event(state));