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