[PATCH v2 05/10] PCI: Share the AER ratelimit sysfs accessors

From: Yazen Ghannam

Date: Fri Sep 18 2026 - 11:15:46 EST


The show and store paths for a ratelimit_state exposed through sysfs are
the same whichever capability owns the state. Only the expression that
reaches the ratelimit_state differs.

Move the bodies into pci-sysfs.c as four helpers taking a
ratelimit_state, and add PCI_RATELIMIT_INTERVAL_ATTR() and
PCI_RATELIMIT_BURST_ATTR() to generate the attribute pair around them.
Convert AER to use them.

The Makefile builds pci-sysfs.c only under CONFIG_SYSFS, so build the
AER attributes under it too. They were dead code without it, since
pci_dev_attr_groups[] is their only consumer, but they were still
emitted and now reference the shared helpers.

No functional change intended.

Assisted-by: LLM
Signed-off-by: Yazen Ghannam <yazen.ghannam@xxxxxxx>
---
drivers/pci/pci-sysfs.c | 68 ++++++++++++++++++++++++++++++
drivers/pci/pci.h | 49 ++++++++++++++++++++++
drivers/pci/pcie/aer.c | 93 +++++++----------------------------------
3 files changed, 131 insertions(+), 79 deletions(-)

diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index 1f21856aac8a..a111e62e39b3 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -29,6 +29,7 @@
#include <linux/pm_runtime.h>
#include <linux/msi.h>
#include <linux/of.h>
+#include <linux/ratelimit.h>
#include <linux/aperture.h>
#include <linux/unaligned.h>
#include "pci.h"
@@ -1892,6 +1893,73 @@ static const struct attribute_group pci_dev_group = {
.attrs = pci_dev_attrs,
};

+/*
+ * Accessors shared by the capabilities that expose a struct ratelimit_state
+ * through sysfs. Use them via PCI_RATELIMIT_INTERVAL_ATTR() and
+ * PCI_RATELIMIT_BURST_ATTR() rather than calling them directly.
+ */
+ssize_t pci_ratelimit_interval_show(struct ratelimit_state *rs, char *buf)
+{
+ unsigned long iv = rs->interval;
+
+ return sysfs_emit(buf, "%u\n", jiffies_to_msecs(iv));
+}
+
+/*
+ * Ratelimit interval, in milliseconds
+ * <=0: disabled with ratelimit.interval = 0
+ * >0: enabled, ratelimit.interval held in jiffies
+ */
+ssize_t pci_ratelimit_interval_store(struct ratelimit_state *rs,
+ const char *buf, size_t count)
+{
+ int interval;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (kstrtoint(buf, 0, &interval) < 0)
+ return -EINVAL;
+
+ if (interval <= 0)
+ interval = 0;
+ else
+ interval = msecs_to_jiffies(interval);
+
+ rs->interval = interval;
+
+ return count;
+}
+
+ssize_t pci_ratelimit_burst_show(struct ratelimit_state *rs, char *buf)
+{
+ return sysfs_emit(buf, "%d\n", rs->burst);
+}
+
+/*
+ * Ratelimit burst
+ * <=0: everything suppressed, unless the interval is also 0
+ * >0: messages allowed per interval
+ */
+ssize_t pci_ratelimit_burst_store(struct ratelimit_state *rs,
+ const char *buf, size_t count)
+{
+ int burst;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (kstrtoint(buf, 0, &burst) < 0)
+ return -EINVAL;
+
+ if (burst < 0)
+ burst = 0;
+
+ rs->burst = burst;
+
+ return count;
+}
+
const struct attribute_group *pci_dev_groups[] = {
&pci_dev_group,
#if defined(HAVE_PCI_MMAP) || defined(ARCH_GENERIC_PCI_MMAP_RESOURCE)
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index f5fdb5dffdb7..f43c5330fca3 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -9,6 +9,7 @@
#include <trace/events/pci.h>

struct pcie_tlp_log;
+struct ratelimit_state;

/* Number of possible devfns: 0.0 to 1f.7 inclusive */
#define MAX_NR_DEVFNS 256
@@ -74,6 +75,54 @@ struct pcie_tlp_log;
#define PCIE_LINK_WAIT_MAX_RETRIES 10
#define PCIE_LINK_WAIT_SLEEP_MS 90

+ssize_t pci_ratelimit_interval_show(struct ratelimit_state *rs, char *buf);
+ssize_t pci_ratelimit_interval_store(struct ratelimit_state *rs,
+ const char *buf, size_t count);
+ssize_t pci_ratelimit_burst_show(struct ratelimit_state *rs, char *buf);
+ssize_t pci_ratelimit_burst_store(struct ratelimit_state *rs,
+ const char *buf, size_t count);
+
+/*
+ * Define the "<name>" sysfs attribute for the interval or the burst of a
+ * struct ratelimit_state. @member names it within struct pci_dev, so it
+ * reads as e.g. aer_info->correctable_ratelimit.
+ */
+#define PCI_RATELIMIT_INTERVAL_ATTR(name, member) \
+ static ssize_t name##_show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+ { \
+ return pci_ratelimit_interval_show( \
+ &to_pci_dev(dev)->member, buf); \
+ } \
+ \
+ static ssize_t name##_store(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+ { \
+ return pci_ratelimit_interval_store( \
+ &to_pci_dev(dev)->member, buf, count); \
+ } \
+ static DEVICE_ATTR_RW(name)
+
+#define PCI_RATELIMIT_BURST_ATTR(name, member) \
+ static ssize_t name##_show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+ { \
+ return pci_ratelimit_burst_show( \
+ &to_pci_dev(dev)->member, buf); \
+ } \
+ \
+ static ssize_t name##_store(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+ { \
+ return pci_ratelimit_burst_store( \
+ &to_pci_dev(dev)->member, buf, count); \
+ } \
+ static DEVICE_ATTR_RW(name)
+
/* Format of TLP; PCIe r7.0, sec 2.2.1 */
#define PCIE_TLP_FMT_3DW_NO_DATA 0x00 /* 3DW header, no data */
#define PCIE_TLP_FMT_4DW_NO_DATA 0x01 /* 4DW header, no data */
diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c
index 91d73bd2ca7c..f42b57cac28a 100644
--- a/drivers/pci/pcie/aer.c
+++ b/drivers/pci/pcie/aer.c
@@ -599,6 +599,12 @@ static const char *aer_agent_string[] = {
"",
};

+/*
+ * pci_dev_attr_groups[] in pci-sysfs.c is the only consumer of the groups
+ * below, and the ratelimit accessors they are built from live there too.
+ */
+#ifdef CONFIG_SYSFS
+
#define aer_stats_dev_attr(name, stats_array, strings_array, \
total_string, total_field) \
static ssize_t \
@@ -687,87 +693,14 @@ const struct attribute_group aer_stats_attr_group = {
.is_visible = aer_stats_attrs_are_visible,
};

-/*
- * Ratelimit interval, in milliseconds
- * <=0: disabled with ratelimit.interval = 0
- * >0: enabled, ratelimit.interval held in jiffies
- */
-#define aer_ratelimit_interval_attr(name, ratelimit) \
- static ssize_t \
- name##_show(struct device *dev, struct device_attribute *attr, \
- char *buf) \
- { \
- struct pci_dev *pdev = to_pci_dev(dev); \
- unsigned long iv = pdev->aer_info->ratelimit.interval; \
- \
- return sysfs_emit(buf, "%u\n", jiffies_to_msecs(iv)); \
- } \
- \
- static ssize_t \
- name##_store(struct device *dev, struct device_attribute *attr, \
- const char *buf, size_t count) \
- { \
- struct pci_dev *pdev = to_pci_dev(dev); \
- int interval; \
- \
- if (!capable(CAP_SYS_ADMIN)) \
- return -EPERM; \
- \
- if (kstrtoint(buf, 0, &interval) < 0) \
- return -EINVAL; \
- \
- if (interval <= 0) \
- interval = 0; \
- else \
- interval = msecs_to_jiffies(interval); \
- \
- pdev->aer_info->ratelimit.interval = interval; \
- \
- return count; \
- } \
- static DEVICE_ATTR_RW(name);
-
-#define aer_ratelimit_burst_attr(name, ratelimit) \
- static ssize_t \
- name##_show(struct device *dev, struct device_attribute *attr, \
- char *buf) \
- { \
- struct pci_dev *pdev = to_pci_dev(dev); \
- \
- return sysfs_emit(buf, "%d\n", \
- pdev->aer_info->ratelimit.burst); \
- } \
- \
- static ssize_t \
- name##_store(struct device *dev, struct device_attribute *attr, \
- const char *buf, size_t count) \
- { \
- struct pci_dev *pdev = to_pci_dev(dev); \
- int burst; \
- \
- if (!capable(CAP_SYS_ADMIN)) \
- return -EPERM; \
- \
- if (kstrtoint(buf, 0, &burst) < 0) \
- return -EINVAL; \
- \
- if (burst < 0) \
- burst = 0; \
- \
- pdev->aer_info->ratelimit.burst = burst; \
- \
- return count; \
- } \
- static DEVICE_ATTR_RW(name);
-
#define aer_ratelimit_attrs(name) \
- aer_ratelimit_interval_attr(name##_ratelimit_interval_ms, \
- name##_ratelimit) \
- aer_ratelimit_burst_attr(name##_ratelimit_burst, \
- name##_ratelimit)
+ PCI_RATELIMIT_INTERVAL_ATTR(name##_ratelimit_interval_ms, \
+ aer_info->name##_ratelimit); \
+ PCI_RATELIMIT_BURST_ATTR(name##_ratelimit_burst, \
+ aer_info->name##_ratelimit)

-aer_ratelimit_attrs(correctable)
-aer_ratelimit_attrs(nonfatal)
+aer_ratelimit_attrs(correctable);
+aer_ratelimit_attrs(nonfatal);

static struct attribute *aer_attrs[] = {
&dev_attr_correctable_ratelimit_interval_ms.attr,
@@ -792,6 +725,8 @@ const struct attribute_group aer_attr_group = {
.is_visible = SYSFS_GROUP_VISIBLE(aer),
};

+#endif /* CONFIG_SYSFS */
+
static void pci_dev_aer_stats_incr(struct pci_dev *pdev,
struct aer_err_info *info)
{
--
2.43.0