]> git.proxmox.com Git - mirror_ubuntu-focal-kernel.git/commitdiff
PCI/ASPM: Add sysfs attributes for controlling ASPM link states
authorHeiner Kallweit <hkallweit1@gmail.com>
Tue, 10 Mar 2020 01:48:34 +0000 (09:48 +0800)
committerSeth Forshee <seth.forshee@canonical.com>
Fri, 20 Mar 2020 22:24:35 +0000 (17:24 -0500)
BugLink: https://bugs.launchpad.net/ubuntu/bugs/1836030
Add sysfs attributes to Endpoints and other Upstream Ports to control ASPM,
Clock PM, and L1 PM Substates.  The new attributes are:

  /sys/devices/pci*/.../link/clkpm
  /sys/devices/pci*/.../link/l0s_aspm
  /sys/devices/pci*/.../link/l1_aspm
  /sys/devices/pci*/.../link/l1_1_aspm
  /sys/devices/pci*/.../link/l1_2_aspm
  /sys/devices/pci*/.../link/l1_1_pcipm
  /sys/devices/pci*/.../link/l1_2_pcipm

An attribute is only visible if both ends of the Link leading to the device
support the state.  Writing y/1/on to the file enables the state; n/0/off
disables it.

These attributes can be used to tune the power/performance tradeoff for
individual devices.

[bhelgaas: commit log, rename directory to "link"]
Link: https://lore.kernel.org/r/b1c83f8a-9bf6-eac5-82d0-cf5b90128fbf@gmail.com
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
(cherry picked from commit 72ea91afbfb08619696ccde610ee4d0d29cf4a1d)
Signed-off-by: AceLan Kao <acelan.kao@canonical.com>
Signed-off-by: Seth Forshee <seth.forshee@canonical.com>
Documentation/ABI/testing/sysfs-bus-pci
drivers/pci/pci-sysfs.c
drivers/pci/pci.h
drivers/pci/pcie/aspm.c

index 8bfee557e50eab1fc66cbd3bc790731f1719bd04..450296cc7948ecbaab3e70446bcba5b0d1e8807b 100644 (file)
@@ -347,3 +347,16 @@ Description:
                If the device has any Peer-to-Peer memory registered, this
                file contains a '1' if the memory has been published for
                use outside the driver that owns the device.
+
+What:          /sys/bus/pci/devices/.../link/clkpm
+               /sys/bus/pci/devices/.../link/l0s_aspm
+               /sys/bus/pci/devices/.../link/l1_aspm
+               /sys/bus/pci/devices/.../link/l1_1_aspm
+               /sys/bus/pci/devices/.../link/l1_2_aspm
+               /sys/bus/pci/devices/.../link/l1_1_pcipm
+               /sys/bus/pci/devices/.../link/l1_2_pcipm
+Date:          October 2019
+Contact:       Heiner Kallweit <hkallweit1@gmail.com>
+Description:   If ASPM is supported for an endpoint, these files can be
+               used to disable or enable the individual power management
+               states. Write y/1/on to enable, n/0/off to disable.
index 7934129545290480b573f03976e8d5e5e220ef01..0e76c02e0b7d46f8ec95fef45b54533c8c3df159 100644 (file)
@@ -1587,6 +1587,9 @@ static const struct attribute_group *pci_dev_attr_groups[] = {
        &pcie_dev_attr_group,
 #ifdef CONFIG_PCIEAER
        &aer_stats_attr_group,
+#endif
+#ifdef CONFIG_PCIEASPM
+       &aspm_ctrl_attr_group,
 #endif
        NULL,
 };
index a5adc2e2c351d23ac3dc833c361a214b8b9f72c8..317a796a723ee41ee66294d02f349351271ba088 100644 (file)
@@ -671,4 +671,8 @@ static inline int pci_acpi_program_hp_params(struct pci_dev *dev)
 }
 #endif
 
+#ifdef CONFIG_PCIEASPM
+extern const struct attribute_group aspm_ctrl_attr_group;
+#endif
+
 #endif /* DRIVERS_PCI_H */
index 2d5ecede8fa365e900393eac6ad1a99aaad56a30..9cd251827a67ffb58ae8f5e6d8059e727daa1b78 100644 (file)
@@ -899,6 +899,14 @@ static struct pcie_link_state *alloc_pcie_link_state(struct pci_dev *pdev)
        return link;
 }
 
+static void pcie_aspm_update_sysfs_visibility(struct pci_dev *pdev)
+{
+       struct pci_dev *child;
+
+       list_for_each_entry(child, &pdev->subordinate->devices, bus_list)
+               sysfs_update_group(&child->dev.kobj, &aspm_ctrl_attr_group);
+}
+
 /*
  * pcie_aspm_init_link_state: Initiate PCI express link state.
  * It is called after the pcie and its children devices are scanned.
@@ -960,6 +968,8 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev)
                pcie_set_clkpm(link, policy_to_clkpm_state(link));
        }
 
+       pcie_aspm_update_sysfs_visibility(pdev);
+
 unlock:
        mutex_unlock(&aspm_lock);
 out:
@@ -1313,6 +1323,145 @@ void pcie_aspm_remove_sysfs_dev_files(struct pci_dev *pdev)
 }
 #endif
 
+static ssize_t aspm_attr_show_common(struct device *dev,
+                                    struct device_attribute *attr,
+                                    char *buf, u8 state)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct pcie_link_state *link = pcie_aspm_get_link(pdev);
+
+       return sprintf(buf, "%d\n", (link->aspm_enabled & state) ? 1 : 0);
+}
+
+static ssize_t aspm_attr_store_common(struct device *dev,
+                                     struct device_attribute *attr,
+                                     const char *buf, size_t len, u8 state)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct pcie_link_state *link = pcie_aspm_get_link(pdev);
+       bool state_enable;
+
+       if (strtobool(buf, &state_enable) < 0)
+               return -EINVAL;
+
+       down_read(&pci_bus_sem);
+       mutex_lock(&aspm_lock);
+
+       if (state_enable) {
+               link->aspm_disable &= ~state;
+               /* need to enable L1 for substates */
+               if (state & ASPM_STATE_L1SS)
+                       link->aspm_disable &= ~ASPM_STATE_L1;
+       } else {
+               link->aspm_disable |= state;
+       }
+
+       pcie_config_aspm_link(link, policy_to_aspm_state(link));
+
+       mutex_unlock(&aspm_lock);
+       up_read(&pci_bus_sem);
+
+       return len;
+}
+
+#define ASPM_ATTR(_f, _s)                                              \
+static ssize_t _f##_show(struct device *dev,                           \
+                        struct device_attribute *attr, char *buf)      \
+{ return aspm_attr_show_common(dev, attr, buf, ASPM_STATE_##_s); }     \
+                                                                       \
+static ssize_t _f##_store(struct device *dev,                          \
+                         struct device_attribute *attr,                \
+                         const char *buf, size_t len)                  \
+{ return aspm_attr_store_common(dev, attr, buf, len, ASPM_STATE_##_s); }
+
+ASPM_ATTR(l0s_aspm, L0S)
+ASPM_ATTR(l1_aspm, L1)
+ASPM_ATTR(l1_1_aspm, L1_1)
+ASPM_ATTR(l1_2_aspm, L1_2)
+ASPM_ATTR(l1_1_pcipm, L1_1_PCIPM)
+ASPM_ATTR(l1_2_pcipm, L1_2_PCIPM)
+
+static ssize_t clkpm_show(struct device *dev,
+                         struct device_attribute *attr, char *buf)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct pcie_link_state *link = pcie_aspm_get_link(pdev);
+
+       return sprintf(buf, "%d\n", link->clkpm_enabled);
+}
+
+static ssize_t clkpm_store(struct device *dev,
+                          struct device_attribute *attr,
+                          const char *buf, size_t len)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct pcie_link_state *link = pcie_aspm_get_link(pdev);
+       bool state_enable;
+
+       if (strtobool(buf, &state_enable) < 0)
+               return -EINVAL;
+
+       down_read(&pci_bus_sem);
+       mutex_lock(&aspm_lock);
+
+       link->clkpm_disable = !state_enable;
+       pcie_set_clkpm(link, policy_to_clkpm_state(link));
+
+       mutex_unlock(&aspm_lock);
+       up_read(&pci_bus_sem);
+
+       return len;
+}
+
+static DEVICE_ATTR_RW(clkpm);
+static DEVICE_ATTR_RW(l0s_aspm);
+static DEVICE_ATTR_RW(l1_aspm);
+static DEVICE_ATTR_RW(l1_1_aspm);
+static DEVICE_ATTR_RW(l1_2_aspm);
+static DEVICE_ATTR_RW(l1_1_pcipm);
+static DEVICE_ATTR_RW(l1_2_pcipm);
+
+static struct attribute *aspm_ctrl_attrs[] = {
+       &dev_attr_clkpm.attr,
+       &dev_attr_l0s_aspm.attr,
+       &dev_attr_l1_aspm.attr,
+       &dev_attr_l1_1_aspm.attr,
+       &dev_attr_l1_2_aspm.attr,
+       &dev_attr_l1_1_pcipm.attr,
+       &dev_attr_l1_2_pcipm.attr,
+       NULL
+};
+
+static umode_t aspm_ctrl_attrs_are_visible(struct kobject *kobj,
+                                          struct attribute *a, int n)
+{
+       struct device *dev = kobj_to_dev(kobj);
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct pcie_link_state *link = pcie_aspm_get_link(pdev);
+       static const u8 aspm_state_map[] = {
+               ASPM_STATE_L0S,
+               ASPM_STATE_L1,
+               ASPM_STATE_L1_1,
+               ASPM_STATE_L1_2,
+               ASPM_STATE_L1_1_PCIPM,
+               ASPM_STATE_L1_2_PCIPM,
+       };
+
+       if (aspm_disabled || !link)
+               return 0;
+
+       if (n == 0)
+               return link->clkpm_capable ? a->mode : 0;
+
+       return link->aspm_capable & aspm_state_map[n - 1] ? a->mode : 0;
+}
+
+const struct attribute_group aspm_ctrl_attr_group = {
+       .name = "link",
+       .attrs = aspm_ctrl_attrs,
+       .is_visible = aspm_ctrl_attrs_are_visible,
+};
+
 static int __init pcie_aspm_disable(char *str)
 {
        if (!strcmp(str, "off")) {