Skip to content

Commit c4b6515

Browse files
committed
PCI / PM: Take SMART_SUSPEND driver flag into account
Make the PCI bus type take DPM_FLAG_SMART_SUSPEND into account in its system-wide PM callbacks and make sure that all code that should not run in parallel with pci_pm_runtime_resume() is executed in the "late" phases of system suspend, freeze and poweroff transitions. [Note that the pm_runtime_suspended() check in pci_dev_keep_suspended() is an optimization, because if is not passed, all of the subsequent checks may be skipped and some of them are much more overhead in general.] Also use the observation that if the device is in runtime suspend at the beginning of the "late" phase of a system-wide suspend-like transition, its state cannot change going forward (runtime PM is disabled for it at that time) until the transition is over and the subsequent system-wide PM callbacks should be skipped for it (as they generally assume the device to not be suspended), so add checks for that in pci_pm_suspend_late/noirq(), pci_pm_freeze_late/noirq() and pci_pm_poweroff_late/noirq(). Moreover, if pci_pm_resume_noirq() or pci_pm_restore_noirq() is called during the subsequent system-wide resume transition and if the device was left in runtime suspend previously, its runtime PM status needs to be changed to "active" as it is going to be put into the full-power state, so add checks for that too to these functions. In turn, if pci_pm_thaw_noirq() runs after the device has been left in runtime suspend, the subsequent "thaw" callbacks need to be skipped for it (as they may not work correctly with a suspended device), so set the power.direct_complete flag for the device then to make the PM core skip those callbacks. In addition to the above add a core helper for checking if DPM_FLAG_SMART_SUSPEND is set and the device runtime PM status is "suspended" at the same time, which is done quite often in the new code (and will be done elsewhere going forward too). Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Acked-by: Bjorn Helgaas <bhelgaas@google.com>
1 parent 302666d commit c4b6515

File tree

4 files changed

+108
-17
lines changed

4 files changed

+108
-17
lines changed

Documentation/power/pci.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -980,6 +980,20 @@ positive value from pci_pm_prepare() if the ->prepare callback provided by the
980980
driver of the device returns a positive value. That allows the driver to opt
981981
out from using the direct-complete mechanism dynamically.
982982

983+
The DPM_FLAG_SMART_SUSPEND flag tells the PCI bus type that from the driver's
984+
perspective the device can be safely left in runtime suspend during system
985+
suspend. That causes pci_pm_suspend(), pci_pm_freeze() and pci_pm_poweroff()
986+
to skip resuming the device from runtime suspend unless there are PCI-specific
987+
reasons for doing that. Also, it causes pci_pm_suspend_late/noirq(),
988+
pci_pm_freeze_late/noirq() and pci_pm_poweroff_late/noirq() to return early
989+
if the device remains in runtime suspend in the beginning of the "late" phase
990+
of the system-wide transition under way. Moreover, if the device is in
991+
runtime suspend in pci_pm_resume_noirq() or pci_pm_restore_noirq(), its runtime
992+
power management status will be changed to "active" (as it is going to be put
993+
into D0 going forward), but if it is in runtime suspend in pci_pm_thaw_noirq(),
994+
the function will set the power.direct_complete flag for it (to make the PM core
995+
skip the subsequent "thaw" callbacks for it) and return.
996+
983997
3.2. Device Runtime Power Management
984998
------------------------------------
985999
In addition to providing device power management callbacks PCI device drivers

drivers/base/power/main.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1861,3 +1861,9 @@ void device_pm_check_callbacks(struct device *dev)
18611861
!dev->driver->suspend && !dev->driver->resume));
18621862
spin_unlock_irq(&dev->power.lock);
18631863
}
1864+
1865+
bool dev_pm_smart_suspend_and_suspended(struct device *dev)
1866+
{
1867+
return dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) &&
1868+
pm_runtime_status_suspended(dev);
1869+
}

drivers/pci/pci-driver.c

Lines changed: 86 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -734,18 +734,25 @@ static int pci_pm_suspend(struct device *dev)
734734

735735
if (!pm) {
736736
pci_pm_default_suspend(pci_dev);
737-
goto Fixup;
737+
return 0;
738738
}
739739

740740
/*
741-
* PCI devices suspended at run time need to be resumed at this point,
742-
* because in general it is necessary to reconfigure them for system
743-
* suspend. Namely, if the device is supposed to wake up the system
744-
* from the sleep state, we may need to reconfigure it for this purpose.
745-
* In turn, if the device is not supposed to wake up the system from the
746-
* sleep state, we'll have to prevent it from signaling wake-up.
741+
* PCI devices suspended at run time may need to be resumed at this
742+
* point, because in general it may be necessary to reconfigure them for
743+
* system suspend. Namely, if the device is expected to wake up the
744+
* system from the sleep state, it may have to be reconfigured for this
745+
* purpose, or if the device is not expected to wake up the system from
746+
* the sleep state, it should be prevented from signaling wakeup events
747+
* going forward.
748+
*
749+
* Also if the driver of the device does not indicate that its system
750+
* suspend callbacks can cope with runtime-suspended devices, it is
751+
* better to resume the device from runtime suspend here.
747752
*/
748-
pm_runtime_resume(dev);
753+
if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) ||
754+
!pci_dev_keep_suspended(pci_dev))
755+
pm_runtime_resume(dev);
749756

750757
pci_dev->state_saved = false;
751758
if (pm->suspend) {
@@ -765,17 +772,27 @@ static int pci_pm_suspend(struct device *dev)
765772
}
766773
}
767774

768-
Fixup:
769-
pci_fixup_device(pci_fixup_suspend, pci_dev);
770-
771775
return 0;
772776
}
773777

778+
static int pci_pm_suspend_late(struct device *dev)
779+
{
780+
if (dev_pm_smart_suspend_and_suspended(dev))
781+
return 0;
782+
783+
pci_fixup_device(pci_fixup_suspend, to_pci_dev(dev));
784+
785+
return pm_generic_suspend_late(dev);
786+
}
787+
774788
static int pci_pm_suspend_noirq(struct device *dev)
775789
{
776790
struct pci_dev *pci_dev = to_pci_dev(dev);
777791
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
778792

793+
if (dev_pm_smart_suspend_and_suspended(dev))
794+
return 0;
795+
779796
if (pci_has_legacy_pm_support(pci_dev))
780797
return pci_legacy_suspend_late(dev, PMSG_SUSPEND);
781798

@@ -834,6 +851,14 @@ static int pci_pm_resume_noirq(struct device *dev)
834851
struct device_driver *drv = dev->driver;
835852
int error = 0;
836853

854+
/*
855+
* Devices with DPM_FLAG_SMART_SUSPEND may be left in runtime suspend
856+
* during system suspend, so update their runtime PM status to "active"
857+
* as they are going to be put into D0 shortly.
858+
*/
859+
if (dev_pm_smart_suspend_and_suspended(dev))
860+
pm_runtime_set_active(dev);
861+
837862
pci_pm_default_resume_early(pci_dev);
838863

839864
if (pci_has_legacy_pm_support(pci_dev))
@@ -876,6 +901,7 @@ static int pci_pm_resume(struct device *dev)
876901
#else /* !CONFIG_SUSPEND */
877902

878903
#define pci_pm_suspend NULL
904+
#define pci_pm_suspend_late NULL
879905
#define pci_pm_suspend_noirq NULL
880906
#define pci_pm_resume NULL
881907
#define pci_pm_resume_noirq NULL
@@ -910,7 +936,8 @@ static int pci_pm_freeze(struct device *dev)
910936
* devices should not be touched during freeze/thaw transitions,
911937
* however.
912938
*/
913-
pm_runtime_resume(dev);
939+
if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND))
940+
pm_runtime_resume(dev);
914941

915942
pci_dev->state_saved = false;
916943
if (pm->freeze) {
@@ -925,11 +952,22 @@ static int pci_pm_freeze(struct device *dev)
925952
return 0;
926953
}
927954

955+
static int pci_pm_freeze_late(struct device *dev)
956+
{
957+
if (dev_pm_smart_suspend_and_suspended(dev))
958+
return 0;
959+
960+
return pm_generic_freeze_late(dev);;
961+
}
962+
928963
static int pci_pm_freeze_noirq(struct device *dev)
929964
{
930965
struct pci_dev *pci_dev = to_pci_dev(dev);
931966
struct device_driver *drv = dev->driver;
932967

968+
if (dev_pm_smart_suspend_and_suspended(dev))
969+
return 0;
970+
933971
if (pci_has_legacy_pm_support(pci_dev))
934972
return pci_legacy_suspend_late(dev, PMSG_FREEZE);
935973

@@ -959,6 +997,16 @@ static int pci_pm_thaw_noirq(struct device *dev)
959997
struct device_driver *drv = dev->driver;
960998
int error = 0;
961999

1000+
/*
1001+
* If the device is in runtime suspend, the code below may not work
1002+
* correctly with it, so skip that code and make the PM core skip all of
1003+
* the subsequent "thaw" callbacks for the device.
1004+
*/
1005+
if (dev_pm_smart_suspend_and_suspended(dev)) {
1006+
dev->power.direct_complete = true;
1007+
return 0;
1008+
}
1009+
9621010
if (pcibios_pm_ops.thaw_noirq) {
9631011
error = pcibios_pm_ops.thaw_noirq(dev);
9641012
if (error)
@@ -1008,11 +1056,13 @@ static int pci_pm_poweroff(struct device *dev)
10081056

10091057
if (!pm) {
10101058
pci_pm_default_suspend(pci_dev);
1011-
goto Fixup;
1059+
return 0;
10121060
}
10131061

10141062
/* The reason to do that is the same as in pci_pm_suspend(). */
1015-
pm_runtime_resume(dev);
1063+
if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) ||
1064+
!pci_dev_keep_suspended(pci_dev))
1065+
pm_runtime_resume(dev);
10161066

10171067
pci_dev->state_saved = false;
10181068
if (pm->poweroff) {
@@ -1024,17 +1074,27 @@ static int pci_pm_poweroff(struct device *dev)
10241074
return error;
10251075
}
10261076

1027-
Fixup:
1028-
pci_fixup_device(pci_fixup_suspend, pci_dev);
1029-
10301077
return 0;
10311078
}
10321079

1080+
static int pci_pm_poweroff_late(struct device *dev)
1081+
{
1082+
if (dev_pm_smart_suspend_and_suspended(dev))
1083+
return 0;
1084+
1085+
pci_fixup_device(pci_fixup_suspend, to_pci_dev(dev));
1086+
1087+
return pm_generic_poweroff_late(dev);
1088+
}
1089+
10331090
static int pci_pm_poweroff_noirq(struct device *dev)
10341091
{
10351092
struct pci_dev *pci_dev = to_pci_dev(dev);
10361093
struct device_driver *drv = dev->driver;
10371094

1095+
if (dev_pm_smart_suspend_and_suspended(dev))
1096+
return 0;
1097+
10381098
if (pci_has_legacy_pm_support(to_pci_dev(dev)))
10391099
return pci_legacy_suspend_late(dev, PMSG_HIBERNATE);
10401100

@@ -1076,6 +1136,10 @@ static int pci_pm_restore_noirq(struct device *dev)
10761136
struct device_driver *drv = dev->driver;
10771137
int error = 0;
10781138

1139+
/* This is analogous to the pci_pm_resume_noirq() case. */
1140+
if (dev_pm_smart_suspend_and_suspended(dev))
1141+
pm_runtime_set_active(dev);
1142+
10791143
if (pcibios_pm_ops.restore_noirq) {
10801144
error = pcibios_pm_ops.restore_noirq(dev);
10811145
if (error)
@@ -1124,10 +1188,12 @@ static int pci_pm_restore(struct device *dev)
11241188
#else /* !CONFIG_HIBERNATE_CALLBACKS */
11251189

11261190
#define pci_pm_freeze NULL
1191+
#define pci_pm_freeze_late NULL
11271192
#define pci_pm_freeze_noirq NULL
11281193
#define pci_pm_thaw NULL
11291194
#define pci_pm_thaw_noirq NULL
11301195
#define pci_pm_poweroff NULL
1196+
#define pci_pm_poweroff_late NULL
11311197
#define pci_pm_poweroff_noirq NULL
11321198
#define pci_pm_restore NULL
11331199
#define pci_pm_restore_noirq NULL
@@ -1243,10 +1309,13 @@ static const struct dev_pm_ops pci_dev_pm_ops = {
12431309
.prepare = pci_pm_prepare,
12441310
.complete = pci_pm_complete,
12451311
.suspend = pci_pm_suspend,
1312+
.suspend_late = pci_pm_suspend_late,
12461313
.resume = pci_pm_resume,
12471314
.freeze = pci_pm_freeze,
1315+
.freeze_late = pci_pm_freeze_late,
12481316
.thaw = pci_pm_thaw,
12491317
.poweroff = pci_pm_poweroff,
1318+
.poweroff_late = pci_pm_poweroff_late,
12501319
.restore = pci_pm_restore,
12511320
.suspend_noirq = pci_pm_suspend_noirq,
12521321
.resume_noirq = pci_pm_resume_noirq,

include/linux/pm.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -765,6 +765,8 @@ extern int pm_generic_poweroff_late(struct device *dev);
765765
extern int pm_generic_poweroff(struct device *dev);
766766
extern void pm_generic_complete(struct device *dev);
767767

768+
extern bool dev_pm_smart_suspend_and_suspended(struct device *dev);
769+
768770
#else /* !CONFIG_PM_SLEEP */
769771

770772
#define device_pm_lock() do {} while (0)

0 commit comments

Comments
 (0)