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>

+30 -4
+30 -4
drivers/base/power/main.c
··· 475 */ 476 void dpm_resume_noirq(pm_message_t state) 477 { 478 - struct device *dev; 479 ktime_t starttime = ktime_get(); 480 481 mutex_lock(&dpm_list_mtx); 482 transition_started = false; 483 - list_for_each_entry(dev, &dpm_list, power.entry) 484 if (dev->power.status > DPM_OFF) { 485 int error; 486 487 dev->power.status = DPM_OFF; 488 error = device_resume_noirq(dev, state); 489 if (error) 490 pm_dev_err(dev, state, " early", error); 491 } 492 mutex_unlock(&dpm_list_mtx); 493 dpm_show_time(starttime, state, "early"); 494 resume_device_irqs(); ··· 802 */ 803 int dpm_suspend_noirq(pm_message_t state) 804 { 805 - struct device *dev; 806 ktime_t starttime = ktime_get(); 807 int error = 0; 808 809 suspend_device_irqs(); 810 mutex_lock(&dpm_list_mtx); 811 - list_for_each_entry_reverse(dev, &dpm_list, power.entry) { 812 error = device_suspend_noirq(dev, state); 813 if (error) { 814 pm_dev_err(dev, state, " late", error); 815 break; 816 } 817 dev->power.status = DPM_OFF_IRQ; 818 } 819 mutex_unlock(&dpm_list_mtx); 820 if (error) 821 dpm_resume_noirq(resume_event(state));
··· 475 */ 476 void dpm_resume_noirq(pm_message_t state) 477 { 478 + struct list_head list; 479 ktime_t starttime = ktime_get(); 480 481 + INIT_LIST_HEAD(&list); 482 mutex_lock(&dpm_list_mtx); 483 transition_started = false; 484 + while (!list_empty(&dpm_list)) { 485 + struct device *dev = to_device(dpm_list.next); 486 + 487 + get_device(dev); 488 if (dev->power.status > DPM_OFF) { 489 int error; 490 491 dev->power.status = DPM_OFF; 492 + mutex_unlock(&dpm_list_mtx); 493 + 494 error = device_resume_noirq(dev, state); 495 + 496 + mutex_lock(&dpm_list_mtx); 497 if (error) 498 pm_dev_err(dev, state, " early", error); 499 } 500 + if (!list_empty(&dev->power.entry)) 501 + list_move_tail(&dev->power.entry, &list); 502 + put_device(dev); 503 + } 504 + list_splice(&list, &dpm_list); 505 mutex_unlock(&dpm_list_mtx); 506 dpm_show_time(starttime, state, "early"); 507 resume_device_irqs(); ··· 789 */ 790 int dpm_suspend_noirq(pm_message_t state) 791 { 792 + struct list_head list; 793 ktime_t starttime = ktime_get(); 794 int error = 0; 795 796 + INIT_LIST_HEAD(&list); 797 suspend_device_irqs(); 798 mutex_lock(&dpm_list_mtx); 799 + while (!list_empty(&dpm_list)) { 800 + struct device *dev = to_device(dpm_list.prev); 801 + 802 + get_device(dev); 803 + mutex_unlock(&dpm_list_mtx); 804 + 805 error = device_suspend_noirq(dev, state); 806 + 807 + mutex_lock(&dpm_list_mtx); 808 if (error) { 809 pm_dev_err(dev, state, " late", error); 810 + put_device(dev); 811 break; 812 } 813 dev->power.status = DPM_OFF_IRQ; 814 + if (!list_empty(&dev->power.entry)) 815 + list_move(&dev->power.entry, &list); 816 + put_device(dev); 817 } 818 + list_splice_tail(&list, &dpm_list); 819 mutex_unlock(&dpm_list_mtx); 820 if (error) 821 dpm_resume_noirq(resume_event(state));