From: Heiner Kallweit <hkallweit1@gmail.com>
To: Frederick Lawler <fred@fredlawl.com>,
Bjorn Helgaas <bhelgaas@google.com>
Cc: "linux-pci@vger.kernel.org" <linux-pci@vger.kernel.org>
Subject: [PATCH RFC v3 3/3] PCI/ASPM: add sysfs attribute for controlling ASPM
Date: Sun, 12 May 2019 15:54:55 +0200 [thread overview]
Message-ID: <061c2def-8998-d62e-a268-c5d1426b14f9@gmail.com> (raw)
In-Reply-To: <bde6db67-b432-f23c-5a44-d2cbb794ad40@gmail.com>
Background of this extension is a problem with the r8169 network driver.
Several combinations of board chipsets and network chip versions have
problems if ASPM is enabled, therefore we have to disable ASPM per
default. However especially on notebooks ASPM can provide significant
power-saving, therefore we want to give users the option to enable
ASPM. With the new sysfs attribute users can control which ASPM
link-states are disabled.
This is a RFC version, therefore documentation of attribute is
still missing. The attribute handling was inspired by the protocol
attribute handling in drivers/media/rc/rc-main.c.
Attribute syntax in a few words:
none: disable all ASPM states
all: allow all supported ASPM states
-<state>: add state to list of disabled ASPM states
+<state>: re-enable ASPM state if supported
v2:
- bind attribute to the endpoint
v3:
- reverse semantics of attribute
- change attribute name to reflect changed semantics
---
drivers/pci/pci.h | 8 +-
drivers/pci/pcie/aspm.c | 180 +++++++++++++++++++++++++++++++++++++++-
2 files changed, 180 insertions(+), 8 deletions(-)
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 9cb99380c..06642b7de 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -499,17 +499,13 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev);
void pcie_aspm_exit_link_state(struct pci_dev *pdev);
void pcie_aspm_pm_state_change(struct pci_dev *pdev);
void pcie_aspm_powersave_config_link(struct pci_dev *pdev);
+void pcie_aspm_create_sysfs_dev_files(struct pci_dev *pdev);
+void pcie_aspm_remove_sysfs_dev_files(struct pci_dev *pdev);
#else
static inline void pcie_aspm_init_link_state(struct pci_dev *pdev) { }
static inline void pcie_aspm_exit_link_state(struct pci_dev *pdev) { }
static inline void pcie_aspm_pm_state_change(struct pci_dev *pdev) { }
static inline void pcie_aspm_powersave_config_link(struct pci_dev *pdev) { }
-#endif
-
-#ifdef CONFIG_PCIEASPM_DEBUG
-void pcie_aspm_create_sysfs_dev_files(struct pci_dev *pdev);
-void pcie_aspm_remove_sysfs_dev_files(struct pci_dev *pdev);
-#else
static inline void pcie_aspm_create_sysfs_dev_files(struct pci_dev *pdev) { }
static inline void pcie_aspm_remove_sysfs_dev_files(struct pci_dev *pdev) { }
#endif
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index 7847be38e..f3822accb 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -42,6 +42,8 @@
#define ASPM_STATE_ALL (ASPM_STATE_L0S | ASPM_STATE_L1 | \
ASPM_STATE_L1SS)
+static const char power_group[] = "power";
+
struct aspm_latency {
u32 l0s; /* L0s latency (nsec) */
u32 l1; /* L1 latency (nsec) */
@@ -1251,38 +1253,212 @@ static ssize_t clk_ctl_store(struct device *dev,
static DEVICE_ATTR_RW(link_state);
static DEVICE_ATTR_RW(clk_ctl);
+#endif
+
+struct aspm_sysfs_state {
+ const char *name;
+ int mask;
+};
+
+static const struct aspm_sysfs_state aspm_sysfs_states[] = {
+ { "L0S", ASPM_STATE_L0S },
+ { "L1", ASPM_STATE_L1 },
+ { "L1.1", ASPM_STATE_L1_1_MASK },
+ { "L1.2", ASPM_STATE_L1_2_MASK },
+};
+
+static struct pcie_link_state *aspm_get_parent_link(struct pci_dev *pdev)
+{
+ struct pci_dev *parent = pdev->bus->self;
+
+ if (pdev->has_secondary_link)
+ parent = pdev;
+
+ return parent ? parent->link_state : NULL;
+}
+
+static bool pcie_check_aspm_endpoint(struct pci_dev *pdev)
+{
+ struct pcie_link_state *link;
+
+ if (pci_pcie_type(pdev) != PCI_EXP_TYPE_ENDPOINT)
+ return false;
+
+ link = aspm_get_parent_link(pdev);
+
+ return link && link->aspm_support;
+}
+
+static ssize_t aspm_link_states_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct pcie_link_state *link;
+ int len = 0, i;
+
+ link = aspm_get_parent_link(pdev);
+ if (!link)
+ return -EOPNOTSUPP;
+
+ mutex_lock(&aspm_lock);
+
+ for (i = 0; i < ARRAY_SIZE(aspm_sysfs_states); i++) {
+ const struct aspm_sysfs_state *st = aspm_sysfs_states + i;
+
+ if (link->aspm_enabled & st->mask)
+ len += scnprintf(buf + len, PAGE_SIZE - len, "[%s] ",
+ st->name);
+ else
+ len += scnprintf(buf + len, PAGE_SIZE - len, "%s ",
+ st->name);
+ }
+
+ if (link->clkpm_enabled)
+ len += scnprintf(buf + len, PAGE_SIZE - len, "[CLKPM] ");
+ else
+ len += scnprintf(buf + len, PAGE_SIZE - len, "CLKPM ");
+
+ mutex_unlock(&aspm_lock);
+
+ len += scnprintf(buf + len, PAGE_SIZE - len, "\n");
+
+ return len;
+}
+
+static ssize_t aspm_link_states_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;
+ char *buftmp = (char *)buf, *tok;
+ unsigned int disable_aspm, disable_clkpm;
+ bool first = true, add;
+ int err = 0, i;
+
+ if (aspm_disabled)
+ return -EPERM;
+
+ link = aspm_get_parent_link(pdev);
+ if (!link)
+ return -EOPNOTSUPP;
+
+ down_read(&pci_bus_sem);
+ mutex_lock(&aspm_lock);
+
+ disable_aspm = link->aspm_disable;
+ disable_clkpm = link->clkpm_disable;
+
+ while ((tok = strsep(&buftmp, " \n")) != NULL) {
+ bool found = false;
+
+ if (!*tok)
+ continue;
+
+ if (first) {
+ if (!strcasecmp(tok, "none")) {
+ disable_aspm = ASPM_STATE_ALL;
+ disable_clkpm = 1;
+ break;
+ }
+ if (!strcasecmp(tok, "all")) {
+ disable_aspm = 0;
+ disable_clkpm = 0;
+ break;
+ }
+ first = false;
+ }
+
+ if (*tok != '+' && *tok != '-') {
+ err = -EINVAL;
+ goto out;
+ }
+
+ add = *tok++ == '+';
+
+ for (i = 0; i < ARRAY_SIZE(aspm_sysfs_states); i++) {
+ const struct aspm_sysfs_state *st =
+ aspm_sysfs_states + i;
+
+ if (!strcasecmp(tok, st->name)) {
+ if (add)
+ disable_aspm &= ~st->mask;
+ else
+ disable_aspm |= st->mask;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found && !strcasecmp(tok, "clkpm")) {
+ disable_clkpm = add ? 0 : 1;
+ found = true;
+ }
+
+ if (!found) {
+ err = -EINVAL;
+ goto out;
+ }
+ }
+
+ if (disable_aspm & ASPM_STATE_L1)
+ disable_aspm |= ASPM_STATE_L1SS;
+
+ link->aspm_disable = disable_aspm;
+ link->clkpm_disable = disable_clkpm;
+
+ pcie_config_aspm_link(link, policy_to_aspm_state(link));
+ pcie_set_clkpm(link, policy_to_clkpm_state(link));
+out:
+ mutex_unlock(&aspm_lock);
+ up_read(&pci_bus_sem);
+
+ return err ?: len;
+}
+
+static DEVICE_ATTR_RW(aspm_link_states);
-static char power_group[] = "power";
void pcie_aspm_create_sysfs_dev_files(struct pci_dev *pdev)
{
struct pcie_link_state *link_state = pdev->link_state;
+ if (pcie_check_aspm_endpoint(pdev))
+ sysfs_add_file_to_group(&pdev->dev.kobj,
+ &dev_attr_aspm_link_states.attr, power_group);
+
if (!link_state)
return;
+#ifdef CONFIG_PCIEASPM_DEBUG
if (link_state->aspm_support)
sysfs_add_file_to_group(&pdev->dev.kobj,
&dev_attr_link_state.attr, power_group);
if (link_state->clkpm_capable)
sysfs_add_file_to_group(&pdev->dev.kobj,
&dev_attr_clk_ctl.attr, power_group);
+#endif
}
void pcie_aspm_remove_sysfs_dev_files(struct pci_dev *pdev)
{
struct pcie_link_state *link_state = pdev->link_state;
+ if (pcie_check_aspm_endpoint(pdev))
+ sysfs_remove_file_from_group(&pdev->dev.kobj,
+ &dev_attr_aspm_link_states.attr, power_group);
+
if (!link_state)
return;
+#ifdef CONFIG_PCIEASPM_DEBUG
if (link_state->aspm_support)
sysfs_remove_file_from_group(&pdev->dev.kobj,
&dev_attr_link_state.attr, power_group);
if (link_state->clkpm_capable)
sysfs_remove_file_from_group(&pdev->dev.kobj,
&dev_attr_clk_ctl.attr, power_group);
-}
#endif
+}
static int __init pcie_aspm_disable(char *str)
{
--
2.21.0
next prev parent reply other threads:[~2019-05-12 13:55 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-05-12 13:52 [PATCH RFC v3 0/3] PCI/ASPM: add sysfs attribute for controlling ASPM Heiner Kallweit
2019-05-12 13:53 ` [PATCH RFC v3 1/3] PCI/ASPM: add L1 sub-state support to pci_disable_link_state Heiner Kallweit
2019-05-12 13:54 ` [PATCH RFC v3 2/3] PCI/ASPM: allow to re-enable Clock PM Heiner Kallweit
2019-05-12 13:54 ` Heiner Kallweit [this message]
2019-05-17 0:12 ` [PATCH RFC v3 3/3] PCI/ASPM: add sysfs attribute for controlling ASPM Frederick Lawler
2019-05-20 19:16 ` Heiner Kallweit
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=061c2def-8998-d62e-a268-c5d1426b14f9@gmail.com \
--to=hkallweit1@gmail.com \
--cc=bhelgaas@google.com \
--cc=fred@fredlawl.com \
--cc=linux-pci@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).