[PATCH v2 09/10] PCI/FLIT: Ratelimit flit error logging
From: Yazen Ghannam
Date: Fri Sep 18 2026 - 11:56:20 EST
A high rate of flit errors can flood the kernel log. Ratelimit it.
Keep the limit per port, so a storm on one port cannot suppress logging
on another, and take the decision per logged error rather than per
interrupt, so the configured burst counts console lines. AER does both
for the same reasons.
The trace event is left unthrottled, so tooling records every erroneous
flit even while the console log is limited.
Expose the interval and burst through a per-device "flit" sysfs group,
gated on CAP_SYS_ADMIN, so the limit can be tuned or disabled at
runtime. Document the attributes and list the new file in the PCI
SUBSYSTEM entry.
Assisted-by: LLM
Signed-off-by: Yazen Ghannam <yazen.ghannam@xxxxxxx>
---
.../ABI/testing/sysfs-bus-pci-devices-flit | 35 ++++++++++++++++
MAINTAINERS | 1 +
drivers/pci/pci-sysfs.c | 3 ++
drivers/pci/pci.h | 1 +
drivers/pci/pcie/flit.c | 42 ++++++++++++++++++-
5 files changed, 81 insertions(+), 1 deletion(-)
create mode 100644 Documentation/ABI/testing/sysfs-bus-pci-devices-flit
diff --git a/Documentation/ABI/testing/sysfs-bus-pci-devices-flit b/Documentation/ABI/testing/sysfs-bus-pci-devices-flit
new file mode 100644
index 000000000000..43d7c7aacf81
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-pci-devices-flit
@@ -0,0 +1,35 @@
+PCIe Flit error log ratelimits
+------------------------------
+
+These attributes show up under the Root Ports, Switch Ports and Root Complex
+Event Collectors that implement the PCI Express Flit Logging Extended
+Capability, where the OS was granted control of it. They represent configurable
+ratelimits of the flit error logs written to the kernel log.
+
+The ratelimit applies only to the kernel log. The flit_event trace event is
+emitted for every flit error the driver drains from the Flit Error Log,
+regardless of these settings. The log is a FIFO of implementation specific
+depth, so errors the hardware could not log are not reported either way.
+
+Writing these attributes requires CAP_SYS_ADMIN.
+
+What: /sys/bus/pci/devices/<dev>/flit/ratelimit_interval_ms
+Date: September 2026
+KernelVersion: 7.4.0
+Contact: linux-pci@xxxxxxxxxxxxxxx
+Description: Writing 0, or any negative value, disables flit error log
+ ratelimiting. Writing a positive value sets the ratelimit
+ interval in ms. Reading gets the current ratelimit interval
+ in ms, rounded up to a whole jiffy. Default is
+ DEFAULT_RATELIMIT_INTERVAL (5000 ms).
+
+What: /sys/bus/pci/devices/<dev>/flit/ratelimit_burst
+Date: September 2026
+KernelVersion: 7.4.0
+Contact: linux-pci@xxxxxxxxxxxxxxx
+Description: Ratelimit burst for flit error logs. Writing a value changes
+ the number of logs (burst) allowed per interval before
+ ratelimiting. Reading gets the current ratelimit burst.
+ Writing 0, or any negative value, suppresses all logs
+ unless the interval attribute is also 0. Default is
+ DEFAULT_RATELIMIT_BURST (10).
diff --git a/MAINTAINERS b/MAINTAINERS
index 3df47851869e..2faa74c190f9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21111,6 +21111,7 @@ B: https://bugzilla.kernel.org
C: irc://irc.oftc.net/linux-pci
T: git git://git.kernel.org/pub/scm/linux/kernel/git/pci/pci.git
F: Documentation/ABI/testing/sysfs-bus-pci-devices-aer
+F: Documentation/ABI/testing/sysfs-bus-pci-devices-flit
F: Documentation/ABI/testing/sysfs-devices-pci-host-bridge
F: Documentation/PCI/
F: Documentation/devicetree/bindings/pci/
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index a111e62e39b3..e53e81ef5d91 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -2016,6 +2016,9 @@ const struct attribute_group *pci_dev_attr_groups[] = {
&aer_stats_attr_group,
&aer_attr_group,
#endif
+#ifdef CONFIG_PCIE_FLIT
+ &flit_attr_group,
+#endif
#ifdef CONFIG_PCIEASPM
&aspm_ctrl_attr_group,
#endif
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 53a90f75cd76..733f0db70413 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -1005,6 +1005,7 @@ void pci_flit_init(struct pci_dev *pdev);
void pci_flit_exit(struct pci_dev *pdev);
void pci_save_flit_state(struct pci_dev *pdev);
void pci_restore_flit_state(struct pci_dev *pdev);
+extern const struct attribute_group flit_attr_group;
#else
static inline void pci_flit_init(struct pci_dev *pdev) { }
static inline void pci_flit_exit(struct pci_dev *pdev) { }
diff --git a/drivers/pci/pcie/flit.c b/drivers/pci/pcie/flit.c
index 9eb804035997..252bbb54e617 100644
--- a/drivers/pci/pcie/flit.c
+++ b/drivers/pci/pcie/flit.c
@@ -16,8 +16,10 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
+#include <linux/ratelimit.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
+#include <linux/sysfs.h>
#include <ras/ras_event.h>
#include "portdrv.h"
#include "../pci.h"
@@ -76,6 +78,7 @@ static bool flit_is_native(struct pci_dev *pdev)
struct flit_info {
spinlock_t lock; /* serializes Counter Control read-modify-write */
+ struct ratelimit_state ratelimit;
bool cntr_enabled; /* Counter Enable as the driver intends it */
};
@@ -96,6 +99,10 @@ void pci_flit_init(struct pci_dev *pdev)
spin_lock_init(&pdev->flit_info->lock);
+ ratelimit_state_init(&pdev->flit_info->ratelimit,
+ DEFAULT_RATELIMIT_INTERVAL,
+ DEFAULT_RATELIMIT_BURST);
+
pci_add_ext_cap_save_buffer(pdev, PCI_EXT_CAP_ID_FLIT, sizeof(u16));
pci_dbg(pdev, "Flit Logging Extended Capability present.\n");
@@ -170,6 +177,38 @@ static void flit_cntr_ctrl_update(struct pci_dev *pdev, u16 clear, u16 set)
spin_unlock_irqrestore(&pdev->flit_info->lock, flags);
}
+/*
+ * pci_dev_attr_groups[] in pci-sysfs.c is the only consumer of the group
+ * below, and the ratelimit accessors it is built from live there too.
+ */
+#ifdef CONFIG_SYSFS
+
+PCI_RATELIMIT_INTERVAL_ATTR(ratelimit_interval_ms, flit_info->ratelimit);
+PCI_RATELIMIT_BURST_ATTR(ratelimit_burst, flit_info->ratelimit);
+
+static struct attribute *flit_attrs[] = {
+ &dev_attr_ratelimit_interval_ms.attr,
+ &dev_attr_ratelimit_burst.attr,
+ NULL
+};
+
+static bool flit_group_visible(struct kobject *kobj)
+{
+ struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
+
+ return !!pdev->flit_info;
+}
+
+DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(flit);
+
+const struct attribute_group flit_attr_group = {
+ .name = "flit",
+ .attrs = flit_attrs,
+ .is_visible = SYSFS_GROUP_VISIBLE(flit),
+};
+
+#endif /* CONFIG_SYSFS */
+
static void flit_cntr_enable(struct pci_dev *pdev)
{
u16 flit = pdev->flit_cap;
@@ -205,7 +244,8 @@ static void flit_report(struct pci_dev *pdev, u32 err_log1, u32 err_log2)
if (!(err_log1 & FLIT_ERR_LOG1_ERROR) && !err_log2)
return;
- pci_warn(pdev, "ErrLog1: 0x%08x ErrLog2: 0x%08x\n", err_log1, err_log2);
+ if (__ratelimit(&pdev->flit_info->ratelimit))
+ pci_warn(pdev, "ErrLog1: 0x%08x ErrLog2: 0x%08x\n", err_log1, err_log2);
trace_flit_event(pci_name(pdev), err_log1, err_log2);
}
--
2.43.0