Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+108 -17
+14
Documentation/power/pci.txt
··· 980 980 driver of the device returns a positive value. That allows the driver to opt 981 981 out from using the direct-complete mechanism dynamically. 982 982 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 + 983 997 3.2. Device Runtime Power Management 984 998 ------------------------------------ 985 999 In addition to providing device power management callbacks PCI device drivers
+6
drivers/base/power/main.c
··· 1861 1861 !dev->driver->suspend && !dev->driver->resume)); 1862 1862 spin_unlock_irq(&dev->power.lock); 1863 1863 } 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 + }
+86 -17
drivers/pci/pci-driver.c
··· 734 734 735 735 if (!pm) { 736 736 pci_pm_default_suspend(pci_dev); 737 - goto Fixup; 737 + return 0; 738 738 } 739 739 740 740 /* 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. 747 752 */ 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); 749 756 750 757 pci_dev->state_saved = false; 751 758 if (pm->suspend) { ··· 772 765 } 773 766 } 774 767 775 - Fixup: 776 - pci_fixup_device(pci_fixup_suspend, pci_dev); 777 - 778 768 return 0; 769 + } 770 + 771 + static int pci_pm_suspend_late(struct device *dev) 772 + { 773 + if (dev_pm_smart_suspend_and_suspended(dev)) 774 + return 0; 775 + 776 + pci_fixup_device(pci_fixup_suspend, to_pci_dev(dev)); 777 + 778 + return pm_generic_suspend_late(dev); 779 779 } 780 780 781 781 static int pci_pm_suspend_noirq(struct device *dev) 782 782 { 783 783 struct pci_dev *pci_dev = to_pci_dev(dev); 784 784 const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; 785 + 786 + if (dev_pm_smart_suspend_and_suspended(dev)) 787 + return 0; 785 788 786 789 if (pci_has_legacy_pm_support(pci_dev)) 787 790 return pci_legacy_suspend_late(dev, PMSG_SUSPEND); ··· 851 834 struct device_driver *drv = dev->driver; 852 835 int error = 0; 853 836 837 + /* 838 + * Devices with DPM_FLAG_SMART_SUSPEND may be left in runtime suspend 839 + * during system suspend, so update their runtime PM status to "active" 840 + * as they are going to be put into D0 shortly. 841 + */ 842 + if (dev_pm_smart_suspend_and_suspended(dev)) 843 + pm_runtime_set_active(dev); 844 + 854 845 pci_pm_default_resume_early(pci_dev); 855 846 856 847 if (pci_has_legacy_pm_support(pci_dev)) ··· 901 876 #else /* !CONFIG_SUSPEND */ 902 877 903 878 #define pci_pm_suspend NULL 879 + #define pci_pm_suspend_late NULL 904 880 #define pci_pm_suspend_noirq NULL 905 881 #define pci_pm_resume NULL 906 882 #define pci_pm_resume_noirq NULL ··· 936 910 * devices should not be touched during freeze/thaw transitions, 937 911 * however. 938 912 */ 939 - pm_runtime_resume(dev); 913 + if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND)) 914 + pm_runtime_resume(dev); 940 915 941 916 pci_dev->state_saved = false; 942 917 if (pm->freeze) { ··· 952 925 return 0; 953 926 } 954 927 928 + static int pci_pm_freeze_late(struct device *dev) 929 + { 930 + if (dev_pm_smart_suspend_and_suspended(dev)) 931 + return 0; 932 + 933 + return pm_generic_freeze_late(dev);; 934 + } 935 + 955 936 static int pci_pm_freeze_noirq(struct device *dev) 956 937 { 957 938 struct pci_dev *pci_dev = to_pci_dev(dev); 958 939 struct device_driver *drv = dev->driver; 940 + 941 + if (dev_pm_smart_suspend_and_suspended(dev)) 942 + return 0; 959 943 960 944 if (pci_has_legacy_pm_support(pci_dev)) 961 945 return pci_legacy_suspend_late(dev, PMSG_FREEZE); ··· 996 958 struct pci_dev *pci_dev = to_pci_dev(dev); 997 959 struct device_driver *drv = dev->driver; 998 960 int error = 0; 961 + 962 + /* 963 + * If the device is in runtime suspend, the code below may not work 964 + * correctly with it, so skip that code and make the PM core skip all of 965 + * the subsequent "thaw" callbacks for the device. 966 + */ 967 + if (dev_pm_smart_suspend_and_suspended(dev)) { 968 + dev->power.direct_complete = true; 969 + return 0; 970 + } 999 971 1000 972 if (pcibios_pm_ops.thaw_noirq) { 1001 973 error = pcibios_pm_ops.thaw_noirq(dev); ··· 1056 1008 1057 1009 if (!pm) { 1058 1010 pci_pm_default_suspend(pci_dev); 1059 - goto Fixup; 1011 + return 0; 1060 1012 } 1061 1013 1062 1014 /* The reason to do that is the same as in pci_pm_suspend(). */ 1063 - pm_runtime_resume(dev); 1015 + if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) || 1016 + !pci_dev_keep_suspended(pci_dev)) 1017 + pm_runtime_resume(dev); 1064 1018 1065 1019 pci_dev->state_saved = false; 1066 1020 if (pm->poweroff) { ··· 1074 1024 return error; 1075 1025 } 1076 1026 1077 - Fixup: 1078 - pci_fixup_device(pci_fixup_suspend, pci_dev); 1079 - 1080 1027 return 0; 1028 + } 1029 + 1030 + static int pci_pm_poweroff_late(struct device *dev) 1031 + { 1032 + if (dev_pm_smart_suspend_and_suspended(dev)) 1033 + return 0; 1034 + 1035 + pci_fixup_device(pci_fixup_suspend, to_pci_dev(dev)); 1036 + 1037 + return pm_generic_poweroff_late(dev); 1081 1038 } 1082 1039 1083 1040 static int pci_pm_poweroff_noirq(struct device *dev) 1084 1041 { 1085 1042 struct pci_dev *pci_dev = to_pci_dev(dev); 1086 1043 struct device_driver *drv = dev->driver; 1044 + 1045 + if (dev_pm_smart_suspend_and_suspended(dev)) 1046 + return 0; 1087 1047 1088 1048 if (pci_has_legacy_pm_support(to_pci_dev(dev))) 1089 1049 return pci_legacy_suspend_late(dev, PMSG_HIBERNATE); ··· 1135 1075 struct pci_dev *pci_dev = to_pci_dev(dev); 1136 1076 struct device_driver *drv = dev->driver; 1137 1077 int error = 0; 1078 + 1079 + /* This is analogous to the pci_pm_resume_noirq() case. */ 1080 + if (dev_pm_smart_suspend_and_suspended(dev)) 1081 + pm_runtime_set_active(dev); 1138 1082 1139 1083 if (pcibios_pm_ops.restore_noirq) { 1140 1084 error = pcibios_pm_ops.restore_noirq(dev); ··· 1188 1124 #else /* !CONFIG_HIBERNATE_CALLBACKS */ 1189 1125 1190 1126 #define pci_pm_freeze NULL 1127 + #define pci_pm_freeze_late NULL 1191 1128 #define pci_pm_freeze_noirq NULL 1192 1129 #define pci_pm_thaw NULL 1193 1130 #define pci_pm_thaw_noirq NULL 1194 1131 #define pci_pm_poweroff NULL 1132 + #define pci_pm_poweroff_late NULL 1195 1133 #define pci_pm_poweroff_noirq NULL 1196 1134 #define pci_pm_restore NULL 1197 1135 #define pci_pm_restore_noirq NULL ··· 1309 1243 .prepare = pci_pm_prepare, 1310 1244 .complete = pci_pm_complete, 1311 1245 .suspend = pci_pm_suspend, 1246 + .suspend_late = pci_pm_suspend_late, 1312 1247 .resume = pci_pm_resume, 1313 1248 .freeze = pci_pm_freeze, 1249 + .freeze_late = pci_pm_freeze_late, 1314 1250 .thaw = pci_pm_thaw, 1315 1251 .poweroff = pci_pm_poweroff, 1252 + .poweroff_late = pci_pm_poweroff_late, 1316 1253 .restore = pci_pm_restore, 1317 1254 .suspend_noirq = pci_pm_suspend_noirq, 1318 1255 .resume_noirq = pci_pm_resume_noirq,
+2
include/linux/pm.h
··· 765 765 extern int pm_generic_poweroff(struct device *dev); 766 766 extern void pm_generic_complete(struct device *dev); 767 767 768 + extern bool dev_pm_smart_suspend_and_suspended(struct device *dev); 769 + 768 770 #else /* !CONFIG_PM_SLEEP */ 769 771 770 772 #define device_pm_lock() do {} while (0)