* [PATCH v2 00/10] PCIe Flit Logging Ext Capability Support
@ 2026-09-18 14:56 Yazen Ghannam
2026-09-18 14:56 ` [PATCH v2 01/10] MAINTAINERS: Add the AER sysfs ABI file to PCI SUBSYSTEM Yazen Ghannam
` (9 more replies)
0 siblings, 10 replies; 12+ messages in thread
From: Yazen Ghannam @ 2026-09-18 14:56 UTC (permalink / raw)
To: linux-pci; +Cc: linux-kernel, linux-acpi, linux-edac, bhelgaas
Hi all,
This set adds basic support for using the Flit Logging capability
introduced in PCIe v6.0.
Patches 1-4 are fixes for AER sysfs issues.
Patches 5-6 are refactor prep.
Patches 7-10 are the Flit Logging support.
I tried to reduce the verbosity in code comments and commit messages.
But I found that review bots would either get confused or pose redundant
questions if comments, etc. were too terse. In any case, please let me
know if more can be trimmed.
Thanks,
Yazen
Link:
https://lkml.kernel.org/r/20260714183950.1620016-1-yazen.ghannam@amd.com
v1->v2:
* Address Sashiko bot feedback.
* Fix sysfs issues found during development.
* Add sysfs controls for Flit logging parameters.
Yazen Ghannam (10):
MAINTAINERS: Add the AER sysfs ABI file to PCI SUBSYSTEM
PCI/AER: Clamp negative ratelimit burst to zero
PCI/AER: Report the ratelimit interval in milliseconds
PCI/AER: Hide the "aer" sysfs directory when AER is unavailable
PCI: Share the AER ratelimit sysfs accessors
PCI/portdrv: Group PCI Exp Cap services into a single mask
PCI/FLIT: Add support for Flit Logging Extended Capability
PCI/FLIT: Add trace event for flit errors
PCI/FLIT: Ratelimit flit error logging
PCI/FLIT: Expose the error counter controls through sysfs
.../ABI/testing/sysfs-bus-pci-devices-aer | 27 +-
.../ABI/testing/sysfs-bus-pci-devices-flit | 70 +++
MAINTAINERS | 2 +
drivers/acpi/pci_root.c | 11 +
drivers/pci/pci-sysfs.c | 71 +++
drivers/pci/pci.c | 2 +
drivers/pci/pci.h | 62 +++
drivers/pci/pcie/Kconfig | 9 +
drivers/pci/pcie/Makefile | 1 +
drivers/pci/pcie/aer.c | 105 +---
drivers/pci/pcie/flit.c | 511 ++++++++++++++++++
drivers/pci/pcie/portdrv.c | 23 +-
drivers/pci/pcie/portdrv.h | 16 +-
drivers/pci/probe.c | 3 +
include/linux/acpi.h | 1 +
include/linux/pci.h | 6 +
include/ras/ras_event.h | 38 ++
include/uapi/linux/pci_regs.h | 20 +-
18 files changed, 876 insertions(+), 102 deletions(-)
create mode 100644 Documentation/ABI/testing/sysfs-bus-pci-devices-flit
create mode 100644 drivers/pci/pcie/flit.c
base-commit: c6d17c3a542095046bd3ae187e82fc5aeaa6e0fc
--
2.43.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v2 01/10] MAINTAINERS: Add the AER sysfs ABI file to PCI SUBSYSTEM
2026-09-18 14:56 [PATCH v2 00/10] PCIe Flit Logging Ext Capability Support Yazen Ghannam
@ 2026-09-18 14:56 ` Yazen Ghannam
2026-09-18 14:56 ` [PATCH v2 02/10] PCI/AER: Clamp negative ratelimit burst to zero Yazen Ghannam
` (8 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Yazen Ghannam @ 2026-09-18 14:56 UTC (permalink / raw)
To: linux-pci; +Cc: linux-kernel, linux-acpi, linux-edac, bhelgaas, Yazen Ghannam
get_maintainer.pl returns only the LKML catch-all for
Documentation/ABI/testing/sysfs-bus-pci-devices-aer, so changes to the
documented AER sysfs interface do not reach linux-pci.
Add the file to the PCI SUBSYSTEM entry.
The generic Documentation/ABI/testing/sysfs-bus-pci is left out on
purpose. file_match_pattern() matches on a prefix, so that path would
also claim eight sibling files describing individual drivers. Covering
it needs matching X: lines, which is left for a separate change.
Assisted-by: LLM
Signed-off-by: Yazen Ghannam <yazen.ghannam@amd.com>
---
MAINTAINERS | 1 +
1 file changed, 1 insertion(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 3a19da74d00c..3df47851869e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21110,6 +21110,7 @@ Q: https://patchwork.kernel.org/project/linux-pci/list/
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-devices-pci-host-bridge
F: Documentation/PCI/
F: Documentation/devicetree/bindings/pci/
--
2.43.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v2 02/10] PCI/AER: Clamp negative ratelimit burst to zero
2026-09-18 14:56 [PATCH v2 00/10] PCIe Flit Logging Ext Capability Support Yazen Ghannam
2026-09-18 14:56 ` [PATCH v2 01/10] MAINTAINERS: Add the AER sysfs ABI file to PCI SUBSYSTEM Yazen Ghannam
@ 2026-09-18 14:56 ` Yazen Ghannam
2026-09-18 14:56 ` [PATCH v2 03/10] PCI/AER: Report the ratelimit interval in milliseconds Yazen Ghannam
` (7 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Yazen Ghannam @ 2026-09-18 14:56 UTC (permalink / raw)
To: linux-pci; +Cc: linux-kernel, linux-acpi, linux-edac, bhelgaas, Yazen Ghannam
The correctable and nonfatal "_ratelimit_burst" attributes store
whatever kstrtoint() parses, so a negative value lands directly in
ratelimit_state.burst.
___ratelimit() reads a negative burst as an uninitialized
ratelimit_state and splats a WARN_ONCE. A CAP_SYS_ADMIN write of "-1"
then warns on the next AER error and suppresses every later message,
with nothing in the attribute to show that logging is off.
Clamp negative values to zero, as the companion interval attribute
already does. Zero still suppresses everything, so a negative value
keeps its old meaning without the warning.
Document that in the ABI description of both burst attributes. Document
it for the interval attributes too, where clamping negatives is existing
behaviour that was never written down.
Fixes: b4fe7398def6 ("PCI/AER: Add sysfs attributes for log ratelimits")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/linux-pci/20260714191826.BA81E1F000E9@smtp.kernel.org/
Assisted-by: LLM
Signed-off-by: Yazen Ghannam <yazen.ghannam@amd.com>
---
.../ABI/testing/sysfs-bus-pci-devices-aer | 21 ++++++++++++-------
drivers/pci/pcie/aer.c | 3 +++
2 files changed, 17 insertions(+), 7 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-bus-pci-devices-aer b/Documentation/ABI/testing/sysfs-bus-pci-devices-aer
index 5ed284523956..215cf8bd4c30 100644
--- a/Documentation/ABI/testing/sysfs-bus-pci-devices-aer
+++ b/Documentation/ABI/testing/sysfs-bus-pci-devices-aer
@@ -130,9 +130,10 @@ What: /sys/bus/pci/devices/<dev>/aer/correctable_ratelimit_interval_ms
Date: May 2025
KernelVersion: 6.16.0
Contact: linux-pci@vger.kernel.org
-Description: Writing 0 disables AER correctable error log ratelimiting.
- Writing a positive value sets the ratelimit interval in ms.
- Default is DEFAULT_RATELIMIT_INTERVAL (5000 ms).
+Description: Writing 0, or any negative value, disables AER correctable
+ error log ratelimiting. Writing a positive value sets the
+ ratelimit interval in ms. Default is
+ DEFAULT_RATELIMIT_INTERVAL (5000 ms).
What: /sys/bus/pci/devices/<dev>/aer/correctable_ratelimit_burst
Date: May 2025
@@ -143,14 +144,17 @@ Description: Ratelimit burst for correctable error logs. Writing a value
before ratelimiting. Reading gets the current ratelimit
burst. Default is DEFAULT_RATELIMIT_BURST (10).
+ Writing 0, or any negative value, suppresses all logs
+ unless the interval attribute is also 0.
+
What: /sys/bus/pci/devices/<dev>/aer/nonfatal_ratelimit_interval_ms
Date: May 2025
KernelVersion: 6.16.0
Contact: linux-pci@vger.kernel.org
-Description: Writing 0 disables AER non-fatal uncorrectable error log
- ratelimiting. Writing a positive value sets the ratelimit
- interval in ms. Default is DEFAULT_RATELIMIT_INTERVAL
- (5000 ms).
+Description: Writing 0, or any negative value, disables AER non-fatal
+ uncorrectable error log ratelimiting. Writing a positive
+ value sets the ratelimit interval in ms. Default is
+ DEFAULT_RATELIMIT_INTERVAL (5000 ms).
What: /sys/bus/pci/devices/<dev>/aer/nonfatal_ratelimit_burst
Date: May 2025
@@ -161,3 +165,6 @@ Description: Ratelimit burst for non-fatal uncorrectable error logs.
allowed per interval before ratelimiting. Reading gets the
current ratelimit burst. Default is DEFAULT_RATELIMIT_BURST
(10).
+
+ Writing 0, or any negative value, suppresses all logs
+ unless the interval attribute is also 0.
diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c
index d8dcd238fda1..350b57e5ccb1 100644
--- a/drivers/pci/pcie/aer.c
+++ b/drivers/pci/pcie/aer.c
@@ -751,6 +751,9 @@ const struct attribute_group aer_stats_attr_group = {
if (kstrtoint(buf, 0, &burst) < 0) \
return -EINVAL; \
\
+ if (burst < 0) \
+ burst = 0; \
+ \
pdev->aer_info->ratelimit.burst = burst; \
\
return count; \
--
2.43.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v2 03/10] PCI/AER: Report the ratelimit interval in milliseconds
2026-09-18 14:56 [PATCH v2 00/10] PCIe Flit Logging Ext Capability Support Yazen Ghannam
2026-09-18 14:56 ` [PATCH v2 01/10] MAINTAINERS: Add the AER sysfs ABI file to PCI SUBSYSTEM Yazen Ghannam
2026-09-18 14:56 ` [PATCH v2 02/10] PCI/AER: Clamp negative ratelimit burst to zero Yazen Ghannam
@ 2026-09-18 14:56 ` Yazen Ghannam
2026-09-18 14:56 ` [PATCH v2 04/10] PCI/AER: Hide the "aer" sysfs directory when AER is unavailable Yazen Ghannam
` (6 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Yazen Ghannam @ 2026-09-18 14:56 UTC (permalink / raw)
To: linux-pci; +Cc: linux-kernel, linux-acpi, linux-edac, bhelgaas, Yazen Ghannam
The "*_ratelimit_interval_ms" attributes convert on the way in but not on
the way out. The store path runs msecs_to_jiffies() before assigning to
ratelimit_state.interval, and the show path prints that field raw.
The field therefore reads back in jiffies while the attribute name and
the ABI documentation both say milliseconds. The two only agree when
CONFIG_HZ is 1000. With CONFIG_HZ=250 the default reads as 1250 rather
than the documented 5000.
Convert back with jiffies_to_msecs() so the attribute reports the unit
it is named for. msecs_to_jiffies() rounds up, so a written value that
is not a whole number of jiffies reads back rounded up. Document what a
read returns, which the burst attributes beside it already do.
Fixes: b4fe7398def6 ("PCI/AER: Add sysfs attributes for log ratelimits")
Assisted-by: LLM
Signed-off-by: Yazen Ghannam <yazen.ghannam@amd.com>
---
Documentation/ABI/testing/sysfs-bus-pci-devices-aer | 6 ++++++
drivers/pci/pcie/aer.c | 8 ++++----
2 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-bus-pci-devices-aer b/Documentation/ABI/testing/sysfs-bus-pci-devices-aer
index 215cf8bd4c30..732fc03ef5fb 100644
--- a/Documentation/ABI/testing/sysfs-bus-pci-devices-aer
+++ b/Documentation/ABI/testing/sysfs-bus-pci-devices-aer
@@ -135,6 +135,9 @@ Description: Writing 0, or any negative value, disables AER correctable
ratelimit interval in ms. Default is
DEFAULT_RATELIMIT_INTERVAL (5000 ms).
+ Reading gets the current ratelimit interval in ms, rounded
+ up to a whole jiffy.
+
What: /sys/bus/pci/devices/<dev>/aer/correctable_ratelimit_burst
Date: May 2025
KernelVersion: 6.16.0
@@ -156,6 +159,9 @@ Description: Writing 0, or any negative value, disables AER non-fatal
value sets the ratelimit interval in ms. Default is
DEFAULT_RATELIMIT_INTERVAL (5000 ms).
+ Reading gets the current ratelimit interval in ms, rounded
+ up to a whole jiffy.
+
What: /sys/bus/pci/devices/<dev>/aer/nonfatal_ratelimit_burst
Date: May 2025
KernelVersion: 6.16.0
diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c
index 350b57e5ccb1..5498fc605556 100644
--- a/drivers/pci/pcie/aer.c
+++ b/drivers/pci/pcie/aer.c
@@ -688,9 +688,9 @@ const struct attribute_group aer_stats_attr_group = {
};
/*
- * Ratelimit interval
+ * Ratelimit interval, in milliseconds
* <=0: disabled with ratelimit.interval = 0
- * >0: enabled with ratelimit.interval in ms
+ * >0: enabled, ratelimit.interval held in jiffies
*/
#define aer_ratelimit_interval_attr(name, ratelimit) \
static ssize_t \
@@ -698,9 +698,9 @@ const struct attribute_group aer_stats_attr_group = {
char *buf) \
{ \
struct pci_dev *pdev = to_pci_dev(dev); \
+ unsigned long iv = pdev->aer_info->ratelimit.interval; \
\
- return sysfs_emit(buf, "%d\n", \
- pdev->aer_info->ratelimit.interval); \
+ return sysfs_emit(buf, "%u\n", jiffies_to_msecs(iv)); \
} \
\
static ssize_t \
--
2.43.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v2 04/10] PCI/AER: Hide the "aer" sysfs directory when AER is unavailable
2026-09-18 14:56 [PATCH v2 00/10] PCIe Flit Logging Ext Capability Support Yazen Ghannam
` (2 preceding siblings ...)
2026-09-18 14:56 ` [PATCH v2 03/10] PCI/AER: Report the ratelimit interval in milliseconds Yazen Ghannam
@ 2026-09-18 14:56 ` Yazen Ghannam
2026-09-18 14:56 ` [PATCH v2 05/10] PCI: Share the AER ratelimit sysfs accessors Yazen Ghannam
` (5 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Yazen Ghannam @ 2026-09-18 14:56 UTC (permalink / raw)
To: linux-pci; +Cc: linux-kernel, linux-acpi, linux-edac, bhelgaas, Yazen Ghannam
aer_attr_group is a named group, so sysfs creates an "aer" directory for
every PCI device. aer_attrs_are_visible() hides the attributes when the
device has no aer_info, but internal_create_group() only skips the
directory when is_visible() returns SYSFS_GROUP_INVISIBLE for the first
attribute. The result is an empty /sys/bus/pci/devices/*/aer/ on every
device without AER.
Switch to DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(), which returns
SYSFS_GROUP_INVISIBLE when the group predicate is false. Drop the
per-attribute callback, since all four attributes share one condition.
aer_stats_attr_group is unnamed and creates no directory, so it is left
alone.
Fixes: b4fe7398def6 ("PCI/AER: Add sysfs attributes for log ratelimits")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/linux-pci/20260714191826.BA81E1F000E9@smtp.kernel.org/
Assisted-by: LLM
Signed-off-by: Yazen Ghannam <yazen.ghannam@amd.com>
---
drivers/pci/pcie/aer.c | 15 ++++++---------
1 file changed, 6 insertions(+), 9 deletions(-)
diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c
index 5498fc605556..91d73bd2ca7c 100644
--- a/drivers/pci/pcie/aer.c
+++ b/drivers/pci/pcie/aer.c
@@ -777,22 +777,19 @@ static struct attribute *aer_attrs[] = {
NULL
};
-static umode_t aer_attrs_are_visible(struct kobject *kobj,
- struct attribute *a, int n)
+static bool aer_group_visible(struct kobject *kobj)
{
- struct device *dev = kobj_to_dev(kobj);
- struct pci_dev *pdev = to_pci_dev(dev);
+ struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
- if (!pdev->aer_info)
- return 0;
-
- return a->mode;
+ return !!pdev->aer_info;
}
+DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(aer);
+
const struct attribute_group aer_attr_group = {
.name = "aer",
.attrs = aer_attrs,
- .is_visible = aer_attrs_are_visible,
+ .is_visible = SYSFS_GROUP_VISIBLE(aer),
};
static void pci_dev_aer_stats_incr(struct pci_dev *pdev,
--
2.43.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v2 05/10] PCI: Share the AER ratelimit sysfs accessors
2026-09-18 14:56 [PATCH v2 00/10] PCIe Flit Logging Ext Capability Support Yazen Ghannam
` (3 preceding siblings ...)
2026-09-18 14:56 ` [PATCH v2 04/10] PCI/AER: Hide the "aer" sysfs directory when AER is unavailable Yazen Ghannam
@ 2026-09-18 14:56 ` Yazen Ghannam
2026-09-18 14:56 ` [PATCH v2 06/10] PCI/portdrv: Group PCI Exp Cap services into a single mask Yazen Ghannam
` (4 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Yazen Ghannam @ 2026-09-18 14:56 UTC (permalink / raw)
To: linux-pci; +Cc: linux-kernel, linux-acpi, linux-edac, bhelgaas, Yazen Ghannam
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@amd.com>
---
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
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v2 06/10] PCI/portdrv: Group PCI Exp Cap services into a single mask
2026-09-18 14:56 [PATCH v2 00/10] PCIe Flit Logging Ext Capability Support Yazen Ghannam
` (4 preceding siblings ...)
2026-09-18 14:56 ` [PATCH v2 05/10] PCI: Share the AER ratelimit sysfs accessors Yazen Ghannam
@ 2026-09-18 14:56 ` Yazen Ghannam
2026-09-18 14:56 ` [PATCH v2 07/10] PCI/FLIT: Add support for Flit Logging Extended Capability Yazen Ghannam
` (3 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Yazen Ghannam @ 2026-09-18 14:56 UTC (permalink / raw)
To: linux-pci; +Cc: linux-kernel, linux-acpi, linux-edac, bhelgaas, Yazen Ghannam
PME, hotplug, and bandwidth notification are all services of the PCI
Express Capability and share a single MSI/MSI-X interrupt vector. The
set of these services is open-coded as a bitmask in more than one place.
That is repetitive and easy to get out of sync as services are added.
Define a single PCIE_PORT_SERVICES_EXPCAP mask beside the service bits
it groups, and use it in place of the open-coded expressions. Assign the
shared vector by iterating the mask rather than naming each service
again, so adding one is a single edit.
Iterating narrows which irqs[] entries get written. The open-coded
version filled the slot of every service in the group as soon as one of
them was in the mask. Those extra entries were never read, since
pcie_init_service_irqs() presets the array to -1 and
pcie_port_device_register() only reads the slot of a service it found.
No functional change intended.
Assisted-by: LLM
Signed-off-by: Yazen Ghannam <yazen.ghannam@amd.com>
---
drivers/pci/pcie/portdrv.c | 16 ++++++++--------
drivers/pci/pcie/portdrv.h | 5 +++++
2 files changed, 13 insertions(+), 8 deletions(-)
diff --git a/drivers/pci/pcie/portdrv.c b/drivers/pci/pcie/portdrv.c
index a9cbfc1d2bc7..ca1b9dbb8b08 100644
--- a/drivers/pci/pcie/portdrv.c
+++ b/drivers/pci/pcie/portdrv.c
@@ -67,8 +67,7 @@ static int pcie_message_numbers(struct pci_dev *dev, int mask,
* 7.8.2, 7.10.10, 7.31.2.
*/
- if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP |
- PCIE_PORT_SERVICE_BWCTRL)) {
+ if (mask & PCIE_PORT_SERVICES_EXPCAP) {
pcie_capability_read_word(dev, PCI_EXP_FLAGS, ®16);
*pme = FIELD_GET(PCI_EXP_FLAGS_IRQ, reg16);
nvec = *pme + 1;
@@ -114,6 +113,8 @@ static int pcie_port_enable_irq_vec(struct pci_dev *dev, int *irqs, int mask)
{
int nr_entries, nvec, pcie_irq;
u32 pme = 0, aer = 0, dpc = 0;
+ unsigned long expcap;
+ unsigned int i;
/* Allocate the maximum possible number of MSI/MSI-X vectors */
nr_entries = pci_alloc_irq_vectors(dev, 1, PCIE_PORT_MAX_MSI_ENTRIES,
@@ -148,13 +149,12 @@ static int pcie_port_enable_irq_vec(struct pci_dev *dev, int *irqs, int mask)
return nr_entries;
}
- /* PME, hotplug and bandwidth notification share an MSI/MSI-X vector */
- if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP |
- PCIE_PORT_SERVICE_BWCTRL)) {
+ /* These services share the PCIe Capability Interrupt Message Number */
+ expcap = mask & PCIE_PORT_SERVICES_EXPCAP;
+ if (expcap) {
pcie_irq = pci_irq_vector(dev, pme);
- irqs[PCIE_PORT_SERVICE_PME_SHIFT] = pcie_irq;
- irqs[PCIE_PORT_SERVICE_HP_SHIFT] = pcie_irq;
- irqs[PCIE_PORT_SERVICE_BWCTRL_SHIFT] = pcie_irq;
+ for_each_set_bit(i, &expcap, PCIE_PORT_DEVICE_MAXSERVICES)
+ irqs[i] = pcie_irq;
}
if (mask & PCIE_PORT_SERVICE_AER)
diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h
index cc58bf2f2c84..bf18ca415990 100644
--- a/drivers/pci/pcie/portdrv.h
+++ b/drivers/pci/pcie/portdrv.h
@@ -23,6 +23,11 @@
#define PCIE_PORT_SERVICE_BWCTRL_SHIFT 4 /* Bandwidth Controller (notifications) */
#define PCIE_PORT_SERVICE_BWCTRL (1 << PCIE_PORT_SERVICE_BWCTRL_SHIFT)
+/* Services sharing the PCI Express Capability Interrupt Message Number */
+#define PCIE_PORT_SERVICES_EXPCAP (PCIE_PORT_SERVICE_PME | \
+ PCIE_PORT_SERVICE_HP | \
+ PCIE_PORT_SERVICE_BWCTRL)
+
#define PCIE_PORT_DEVICE_MAXSERVICES 5
extern bool pcie_ports_dpc_native;
--
2.43.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v2 07/10] PCI/FLIT: Add support for Flit Logging Extended Capability
2026-09-18 14:56 [PATCH v2 00/10] PCIe Flit Logging Ext Capability Support Yazen Ghannam
` (5 preceding siblings ...)
2026-09-18 14:56 ` [PATCH v2 06/10] PCI/portdrv: Group PCI Exp Cap services into a single mask Yazen Ghannam
@ 2026-09-18 14:56 ` Yazen Ghannam
2026-09-18 16:38 ` Ilpo Järvinen
2026-09-18 14:56 ` [PATCH v2 08/10] PCI/FLIT: Add trace event for flit errors Yazen Ghannam
` (2 subsequent siblings)
9 siblings, 1 reply; 12+ messages in thread
From: Yazen Ghannam @ 2026-09-18 14:56 UTC (permalink / raw)
To: linux-pci; +Cc: linux-kernel, linux-acpi, linux-edac, bhelgaas, Yazen Ghannam
PCIe r6.4 defines the Flit Logging Extended Capability for logging
erroneous flits, and requires it on ports and RCRBs that support PCIe
Flit Mode.[1]
Add a port service driver for it. Include _OSC negotiation for control
of the capability.
The driver claims only the port types the PCIe port bus driver binds to.
An Endpoint implements the capability on its Upstream Port too, and _OSC
is negotiated per host bridge, so accepting control leaves those
instances with neither firmware nor the OS managing them. Covering them
needs a home outside the port bus driver.
[1] PCI Express® Base Specification Revision 6.4, section 7.7.8
Link: https://pcisig.com/specification/extend-osc-negotiate-control-pcie-flit-logging-extended-capability
Originally-by: Avadhut Naik <avadhut.naik@amd.com>
Assisted-by: LLM
Signed-off-by: Yazen Ghannam <yazen.ghannam@amd.com>
---
drivers/acpi/pci_root.c | 11 ++
drivers/pci/pci.c | 2 +
drivers/pci/pci.h | 12 ++
drivers/pci/pcie/Kconfig | 9 +
drivers/pci/pcie/Makefile | 1 +
drivers/pci/pcie/flit.c | 324 ++++++++++++++++++++++++++++++++++
drivers/pci/pcie/portdrv.c | 7 +
drivers/pci/pcie/portdrv.h | 13 +-
drivers/pci/probe.c | 3 +
include/linux/acpi.h | 1 +
include/linux/pci.h | 6 +
include/uapi/linux/pci_regs.h | 19 +-
12 files changed, 405 insertions(+), 3 deletions(-)
create mode 100644 drivers/pci/pcie/flit.c
diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
index 88c65f34e305..402686a9ca4c 100644
--- a/drivers/acpi/pci_root.c
+++ b/drivers/acpi/pci_root.c
@@ -137,6 +137,7 @@ static struct pci_osc_bit_struct pci_osc_control_bit[] = {
{ OSC_PCI_EXPRESS_CAPABILITY_CONTROL, "PCIeCapability" },
{ OSC_PCI_EXPRESS_LTR_CONTROL, "LTR" },
{ OSC_PCI_EXPRESS_DPC_CONTROL, "DPC" },
+ { OSC_PCI_EXPRESS_FLIT_CONTROL, "FlitLogging" },
};
static struct pci_osc_bit_struct cxl_osc_support_bit[] = {
@@ -520,6 +521,14 @@ static u32 calculate_control(void)
if (IS_ENABLED(CONFIG_PCIE_DPC) && IS_ENABLED(CONFIG_PCIE_EDR))
control |= OSC_PCI_EXPRESS_DPC_CONTROL;
+ /*
+ * An OS that requests Flit Logging control must request AER control
+ * as well, so pair it with the bit that was actually requested above.
+ */
+ if (IS_ENABLED(CONFIG_PCIE_FLIT) &&
+ (control & OSC_PCI_EXPRESS_AER_CONTROL))
+ control |= OSC_PCI_EXPRESS_FLIT_CONTROL;
+
return control;
}
@@ -1040,6 +1049,8 @@ struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root,
host_bridge->native_ltr = 0;
if (!(root->osc_control_set & OSC_PCI_EXPRESS_DPC_CONTROL))
host_bridge->native_dpc = 0;
+ if (!(root->osc_control_set & OSC_PCI_EXPRESS_FLIT_CONTROL))
+ host_bridge->native_flit = 0;
if (!(root->osc_ext_control_set & OSC_CXL_ERROR_REPORTING_CONTROL))
host_bridge->native_cxl_error = 0;
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index b2879a6be5f8..c1cfa0e3d75c 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -1798,6 +1798,7 @@ int pci_save_state(struct pci_dev *dev)
pci_save_dpc_state(dev);
pci_save_aer_state(dev);
+ pci_save_flit_state(dev);
pci_save_ptm_state(dev);
pci_save_tph_state(dev);
return pci_save_vc_state(dev);
@@ -1870,6 +1871,7 @@ void pci_restore_state(struct pci_dev *dev)
pci_aer_clear_status(dev);
pci_restore_aer_state(dev);
+ pci_restore_flit_state(dev);
pci_restore_config_space(dev);
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index f43c5330fca3..53a90f75cd76 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -1000,6 +1000,18 @@ static inline void pci_dpc_init(struct pci_dev *pdev) { }
static inline bool pci_dpc_recovered(struct pci_dev *pdev) { return false; }
#endif
+#ifdef CONFIG_PCIE_FLIT
+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);
+#else
+static inline void pci_flit_init(struct pci_dev *pdev) { }
+static inline void pci_flit_exit(struct pci_dev *pdev) { }
+static inline void pci_save_flit_state(struct pci_dev *pdev) { }
+static inline void pci_restore_flit_state(struct pci_dev *pdev) { }
+#endif
+
#ifdef CONFIG_PCIEPORTBUS
void pci_rcec_init(struct pci_dev *dev);
void pci_rcec_exit(struct pci_dev *dev);
diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig
index 207c2deae35f..7ddadad57d3f 100644
--- a/drivers/pci/pcie/Kconfig
+++ b/drivers/pci/pcie/Kconfig
@@ -146,3 +146,12 @@ config PCIE_EDR
the PCI Firmware Specification r3.2. Enable this if you want to
support hybrid DPC model which uses both firmware and OS to
implement DPC.
+
+config PCIE_FLIT
+ bool "PCI Express Flit Logging support"
+ depends on PCIEPORTBUS && PCIEAER
+ help
+ This enables support for the PCI Express Flit Logging Extended
+ Capability, which logs errors encountered by a port operating
+ in PCIe Flit Mode. If your system doesn't have this capability
+ or you do not want to use this feature, it is safe to answer N.
diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile
index b0b43a18c304..14a5222920b9 100644
--- a/drivers/pci/pcie/Makefile
+++ b/drivers/pci/pcie/Makefile
@@ -14,3 +14,4 @@ obj-$(CONFIG_PCIE_PME) += pme.o
obj-$(CONFIG_PCIE_DPC) += dpc.o
obj-$(CONFIG_PCIE_PTM) += ptm.o
obj-$(CONFIG_PCIE_EDR) += edr.o
+obj-$(CONFIG_PCIE_FLIT) += flit.o
diff --git a/drivers/pci/pcie/flit.c b/drivers/pci/pcie/flit.c
new file mode 100644
index 000000000000..7c0b9c8dd481
--- /dev/null
+++ b/drivers/pci/pcie/flit.c
@@ -0,0 +1,324 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Service driver for PCIe Flit Logging Extended Capability
+ *
+ * Copyright (c) 2026, Advanced Micro Devices, Inc.
+ * All Rights Reserved.
+ *
+ * Authors: Avadhut Naik <Avadhut.Naik@amd.com>
+ * Yazen Ghannam <Yazen.Ghannam@amd.com>
+ */
+
+#define pr_fmt(fmt) "Flit: " fmt
+#define dev_fmt pr_fmt
+
+#include <linux/bitfield.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include "portdrv.h"
+#include "../pci.h"
+
+/*
+ * Report only a link accumulating errors far faster than the counter drains.
+ * 0xff never fires, since an event needs the counter to exceed the trigger.
+ */
+#define FLIT_DEFAULT_TRIGGER 0xfe
+
+/*
+ * Per PCIe r6.4, sec 7.7.8.2, table 7-93, More Entries can be set again
+ * mid-drain, so bound the loop.
+ */
+#define FLIT_MAX_LOG_ENTRIES 0xff
+
+/*
+ * Per PCIe r6.4, sec 7.7.8.2, table 7-92, an entry with none of these set
+ * and Flit Error Log 2 zero is Reserved.
+ */
+#define FLIT_ERR_LOG1_ERROR (PCI_FLIT_ERR_LOG1_UNRECOG | \
+ PCI_FLIT_ERR_LOG1_FEC_UNCOR | \
+ PCI_FLIT_ERR_LOG1_SYND_PARITY0 | \
+ PCI_FLIT_ERR_LOG1_SYND_CHECK0)
+
+/* Only the port types pcie_portdrv_probe() claims get a Flit service. */
+static bool flit_is_port(struct pci_dev *pdev)
+{
+ if (!pci_is_pcie(pdev))
+ return false;
+
+ switch (pci_pcie_type(pdev)) {
+ case PCI_EXP_TYPE_ROOT_PORT:
+ case PCI_EXP_TYPE_UPSTREAM:
+ case PCI_EXP_TYPE_DOWNSTREAM:
+ case PCI_EXP_TYPE_RC_EC:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/*
+ * Without _OSC control the OS must not touch the capability. Flit Logging
+ * control is only meaningful alongside AER control, so require both.
+ */
+static bool flit_is_native(struct pci_dev *pdev)
+{
+ struct pci_host_bridge *host = pci_find_host_bridge(pdev->bus);
+
+ if (pcie_ports_native)
+ return true;
+
+ return host->native_flit && host->native_aer;
+}
+
+struct flit_info {
+ spinlock_t lock; /* serializes Counter Control read-modify-write */
+ bool cntr_enabled; /* Counter Enable as the driver intends it */
+};
+
+void pci_flit_init(struct pci_dev *pdev)
+{
+ if (!flit_is_port(pdev) || !flit_is_native(pdev))
+ return;
+
+ pdev->flit_cap = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_FLIT);
+ if (!pdev->flit_cap)
+ return;
+
+ pdev->flit_info = kzalloc_obj(*pdev->flit_info);
+ if (!pdev->flit_info) {
+ pdev->flit_cap = 0;
+ return;
+ }
+
+ spin_lock_init(&pdev->flit_info->lock);
+
+ pci_add_ext_cap_save_buffer(pdev, PCI_EXT_CAP_ID_FLIT, sizeof(u16));
+
+ pci_dbg(pdev, "Flit Logging Extended Capability present.\n");
+}
+
+void pci_flit_exit(struct pci_dev *pdev)
+{
+ kfree(pdev->flit_info);
+ pdev->flit_info = NULL;
+ pdev->flit_cap = 0;
+}
+
+void pci_save_flit_state(struct pci_dev *pdev)
+{
+ struct pci_cap_saved_state *save_state;
+ u16 *cap;
+
+ if (!pdev->flit_cap)
+ return;
+
+ save_state = pci_find_saved_ext_cap(pdev, PCI_EXT_CAP_ID_FLIT);
+ if (!save_state)
+ return;
+
+ /*
+ * Take Counter Enable from the driver rather than the register.
+ * flit_isr() clears it and only flit_ist() sets it again once the log
+ * is drained, and a save landing in between would capture a zero that
+ * the next restore writes back for good.
+ */
+ cap = (u16 *)&save_state->cap.data[0];
+ pci_read_config_word(pdev, pdev->flit_cap + PCI_FLIT_ERR_CNTR_CTRL, cap);
+ if (pdev->flit_info->cntr_enabled)
+ *cap |= PCI_FLIT_ERR_CNTR_CTRL_EN;
+ else
+ *cap &= ~PCI_FLIT_ERR_CNTR_CTRL_EN;
+}
+
+void pci_restore_flit_state(struct pci_dev *pdev)
+{
+ struct pci_cap_saved_state *save_state;
+ u16 *cap;
+
+ if (!pdev->flit_cap)
+ return;
+
+ save_state = pci_find_saved_ext_cap(pdev, PCI_EXT_CAP_ID_FLIT);
+ if (!save_state)
+ return;
+
+ cap = (u16 *)&save_state->cap.data[0];
+ pci_write_config_word(pdev, pdev->flit_cap + PCI_FLIT_ERR_CNTR_CTRL, *cap);
+}
+
+/*
+ * Serializes the Counter Control read-modify-write against flit_isr(), which
+ * runs in hard irq context on the vector this port shares with PME, hotplug
+ * and bandwidth notification. A lost update here leaves Trigger Event on Error
+ * Count at zero, and per PCIe r6.4, sec 7.7.8.4, table 7-95, a zero trigger
+ * never generates an event.
+ */
+static void flit_cntr_ctrl_update(struct pci_dev *pdev, u16 clear, u16 set)
+{
+ unsigned long flags;
+ u16 ctrl;
+
+ spin_lock_irqsave(&pdev->flit_info->lock, flags);
+ pci_read_config_word(pdev, pdev->flit_cap + PCI_FLIT_ERR_CNTR_CTRL, &ctrl);
+ ctrl &= ~clear;
+ ctrl |= set;
+ pci_write_config_word(pdev, pdev->flit_cap + PCI_FLIT_ERR_CNTR_CTRL, ctrl);
+ spin_unlock_irqrestore(&pdev->flit_info->lock, flags);
+}
+
+static void flit_cntr_enable(struct pci_dev *pdev)
+{
+ u16 flit = pdev->flit_cap;
+ unsigned long flags;
+ u16 reg;
+
+ pci_read_config_word(pdev, flit + PCI_FLIT_ERR_CNTR_STA, ®);
+ pci_write_config_word(pdev, flit + PCI_FLIT_ERR_CNTR_STA, reg);
+
+ pdev->flit_info->cntr_enabled = true;
+
+ spin_lock_irqsave(&pdev->flit_info->lock, flags);
+ pci_read_config_word(pdev, flit + PCI_FLIT_ERR_CNTR_CTRL, ®);
+
+ /* Set default trigger count if not set by platform, since zero never fires */
+ if (!(reg & PCI_FLIT_ERR_CNTR_CTRL_TRIGGER))
+ reg |= FIELD_PREP(PCI_FLIT_ERR_CNTR_CTRL_TRIGGER, FLIT_DEFAULT_TRIGGER);
+
+ reg |= PCI_FLIT_ERR_CNTR_CTRL_EN | PCI_FLIT_ERR_CNTR_CTRL_INTR_EN;
+ pci_write_config_word(pdev, flit + PCI_FLIT_ERR_CNTR_CTRL, reg);
+ spin_unlock_irqrestore(&pdev->flit_info->lock, flags);
+}
+
+static void flit_cntr_disable(struct pci_dev *pdev)
+{
+ pdev->flit_info->cntr_enabled = false;
+ flit_cntr_ctrl_update(pdev, PCI_FLIT_ERR_CNTR_CTRL_EN | PCI_FLIT_ERR_CNTR_CTRL_INTR_EN, 0);
+}
+
+static void flit_report(struct pci_dev *pdev, u32 err_log1, u32 err_log2)
+{
+ /* Software should silently discard a Reserved entry */
+ if (!(err_log1 & FLIT_ERR_LOG1_ERROR) && !err_log2)
+ return;
+
+ pci_warn(pdev, "ErrLog1: 0x%08x ErrLog2: 0x%08x\n", err_log1, err_log2);
+}
+
+static irqreturn_t flit_ist(int irq, void *context)
+{
+ struct pcie_device *dev = (struct pcie_device *)context;
+ struct pci_dev *pdev = dev->port;
+ u16 flit = pdev->flit_cap;
+ u32 err_log1, err_log2;
+ unsigned int i;
+
+ for (i = 0; i < FLIT_MAX_LOG_ENTRIES; i++) {
+ pci_read_config_dword(pdev, flit + PCI_FLIT_ERR_LOG1, &err_log1);
+
+ if (PCI_POSSIBLE_ERROR(err_log1) || !(err_log1 & PCI_FLIT_ERR_LOG1_VALID))
+ break;
+
+ pci_read_config_dword(pdev, flit + PCI_FLIT_ERR_LOG2, &err_log2);
+
+ flit_report(pdev, err_log1, err_log2);
+
+ pci_write_config_dword(pdev, flit + PCI_FLIT_ERR_LOG1, err_log1);
+
+ if (!(err_log1 & PCI_FLIT_ERR_LOG1_MORE))
+ break;
+ }
+
+ /*
+ * Re-enable the counter. Per PCIe r6.4, sec 7.7.8.5, table 7-96, the
+ * status bit is "Cleared on 0b to 1b transition of Flit Error Counter
+ * Enable", which also clears the counter and re-arms interrupt
+ * generation.
+ */
+ flit_cntr_ctrl_update(pdev, 0, PCI_FLIT_ERR_CNTR_CTRL_EN);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t flit_isr(int irq, void *context)
+{
+ struct pcie_device *dev = (struct pcie_device *)context;
+ struct pci_dev *pdev = dev->port;
+ u16 flit = pdev->flit_cap;
+ u16 cntr_sta;
+
+ pci_read_config_word(pdev, flit + PCI_FLIT_ERR_CNTR_STA, &cntr_sta);
+ if (PCI_POSSIBLE_ERROR(cntr_sta) || !(cntr_sta & PCI_FLIT_ERR_CNTR_STA_INTR_GEN))
+ return IRQ_NONE;
+
+ /*
+ * Clear Flit Error Counter Enable first, so the counter cannot reach
+ * the trigger again while the thread runs, then clear the status bit.
+ * A message-signaled vector needs no more than that, but this service
+ * can land on INTx, where the line stays asserted until the status is
+ * cleared. flit_ist() transitions Counter Enable back 0->1.
+ */
+ flit_cntr_ctrl_update(pdev, PCI_FLIT_ERR_CNTR_CTRL_EN, 0);
+ pci_write_config_word(pdev, flit + PCI_FLIT_ERR_CNTR_STA,
+ PCI_FLIT_ERR_CNTR_STA_INTR_GEN);
+ return IRQ_WAKE_THREAD;
+}
+
+static int flit_probe(struct pcie_device *dev)
+{
+ struct pci_dev *pdev = dev->port;
+ int status;
+
+ /*
+ * Not devm_request_threaded_irq(): devres would free the IRQ only
+ * after .remove() runs, too late to stop flit_ist() re-enabling the
+ * counter.
+ */
+ status = request_threaded_irq(dev->irq, flit_isr, flit_ist,
+ IRQF_SHARED, "pcie-flit", dev);
+ if (status) {
+ pci_warn(pdev, "request Flit IRQ %d failed: %d\n", dev->irq, status);
+ return status;
+ }
+
+ flit_cntr_enable(pdev);
+ pci_info(pdev, "enabled with IRQ %d\n", dev->irq);
+
+ return 0;
+}
+
+/*
+ * No .suspend or .runtime_suspend counterpart. pci_save_state() runs after
+ * both, so disabling the counter there would put a transient value in the
+ * save buffer, and pcie_portdrv_slot_reset() restores that buffer after
+ * calling .slot_reset.
+ */
+static int flit_cntr_restore(struct pcie_device *dev)
+{
+ flit_cntr_enable(dev->port);
+ return 0;
+}
+
+static void flit_cntr_remove(struct pcie_device *dev)
+{
+ /* Drop the IRQ first: free_irq() waits for the threaded handler. */
+ free_irq(dev->irq, dev);
+ flit_cntr_disable(dev->port);
+}
+
+static struct pcie_port_service_driver flitdriver = {
+ .name = "flit",
+ .port_type = PCIE_ANY_PORT,
+ .service = PCIE_PORT_SERVICE_FLIT,
+ .probe = flit_probe,
+ .resume = flit_cntr_restore,
+ .runtime_resume = flit_cntr_restore,
+ .slot_reset = flit_cntr_restore,
+ .remove = flit_cntr_remove,
+};
+
+int __init pcie_flit_init(void)
+{
+ return pcie_port_service_register(&flitdriver);
+}
diff --git a/drivers/pci/pcie/portdrv.c b/drivers/pci/pcie/portdrv.c
index ca1b9dbb8b08..6328233c75d7 100644
--- a/drivers/pci/pcie/portdrv.c
+++ b/drivers/pci/pcie/portdrv.c
@@ -278,6 +278,12 @@ static int get_port_device_capability(struct pci_dev *dev)
services |= PCIE_PORT_SERVICE_BWCTRL;
}
+#ifdef CONFIG_PCIE_FLIT
+ /* flit_cap is set only when the capability is OS-managed */
+ if (dev->flit_cap)
+ services |= PCIE_PORT_SERVICE_FLIT;
+#endif
+
return services;
}
@@ -834,6 +840,7 @@ static void __init pcie_init_services(void)
pcie_dpc_init();
pcie_bwctrl_init();
pcie_hp_init();
+ pcie_flit_init();
}
static int __init pcie_portdrv_init(void)
diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h
index bf18ca415990..975f83134a5b 100644
--- a/drivers/pci/pcie/portdrv.h
+++ b/drivers/pci/pcie/portdrv.h
@@ -22,13 +22,16 @@
#define PCIE_PORT_SERVICE_DPC (1 << PCIE_PORT_SERVICE_DPC_SHIFT)
#define PCIE_PORT_SERVICE_BWCTRL_SHIFT 4 /* Bandwidth Controller (notifications) */
#define PCIE_PORT_SERVICE_BWCTRL (1 << PCIE_PORT_SERVICE_BWCTRL_SHIFT)
+#define PCIE_PORT_SERVICE_FLIT_SHIFT 5 /* Flit Logging */
+#define PCIE_PORT_SERVICE_FLIT (1 << PCIE_PORT_SERVICE_FLIT_SHIFT)
/* Services sharing the PCI Express Capability Interrupt Message Number */
#define PCIE_PORT_SERVICES_EXPCAP (PCIE_PORT_SERVICE_PME | \
PCIE_PORT_SERVICE_HP | \
- PCIE_PORT_SERVICE_BWCTRL)
+ PCIE_PORT_SERVICE_BWCTRL | \
+ PCIE_PORT_SERVICE_FLIT)
-#define PCIE_PORT_DEVICE_MAXSERVICES 5
+#define PCIE_PORT_DEVICE_MAXSERVICES 6
extern bool pcie_ports_dpc_native;
@@ -56,6 +59,12 @@ int pcie_dpc_init(void);
static inline int pcie_dpc_init(void) { return 0; }
#endif
+#ifdef CONFIG_PCIE_FLIT
+int pcie_flit_init(void);
+#else
+static inline int pcie_flit_init(void) { return 0; }
+#endif
+
int pcie_bwctrl_init(void);
/* Port Type */
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 27008e2ea5af..6bf62bdadc8d 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -674,6 +674,7 @@ static void pci_init_host_bridge(struct pci_host_bridge *bridge)
bridge->native_pme = 1;
bridge->native_ltr = 1;
bridge->native_dpc = 1;
+ bridge->native_flit = 1;
bridge->domain_nr = PCI_DOMAIN_NR_NOT_SET;
bridge->native_cxl_error = 1;
bridge->dev.type = &pci_host_bridge_type;
@@ -2465,6 +2466,7 @@ static void pci_configure_device(struct pci_dev *dev)
static void pci_release_capabilities(struct pci_dev *dev)
{
+ pci_flit_exit(dev);
pci_aer_exit(dev);
pci_rcec_exit(dev);
pci_iov_release(dev);
@@ -2666,6 +2668,7 @@ static void pci_init_capabilities(struct pci_dev *dev)
pci_pasid_init(dev); /* Process Address Space ID */
pci_acs_init(dev); /* Access Control Services */
pci_ptm_init(dev); /* Precision Time Measurement */
+ pci_flit_init(dev); /* Flit Logging */
pci_aer_init(dev); /* Advanced Error Reporting */
pci_dpc_init(dev); /* Downstream Port Containment */
pci_rcec_init(dev); /* Root Complex Event Collector */
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index ddacac812094..b3d8a5bb79e9 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -667,6 +667,7 @@ extern u32 osc_sb_native_usb4_control;
#define OSC_PCI_EXPRESS_CAPABILITY_CONTROL 0x00000010
#define OSC_PCI_EXPRESS_LTR_CONTROL 0x00000020
#define OSC_PCI_EXPRESS_DPC_CONTROL 0x00000080
+#define OSC_PCI_EXPRESS_FLIT_CONTROL 0x00000800
/* CXL _OSC: Capabilities DWORD 4: Support Field */
#define OSC_CXL_1_1_PORT_REG_ACCESS_SUPPORT 0x00000001
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 8650c627bebb..37d01fab50bb 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -598,6 +598,11 @@ struct pci_dev {
u8 tph_mode; /* TPH mode */
u8 tph_req_type; /* TPH requester type */
#endif
+
+#ifdef CONFIG_PCIE_FLIT
+ u16 flit_cap; /* Flit Logging Capability offset */
+ struct flit_info *flit_info; /* Flit Logging state */
+#endif
};
static inline struct pci_dev *pci_physfn(struct pci_dev *dev)
@@ -663,6 +668,7 @@ struct pci_host_bridge {
unsigned int native_pme:1; /* OS may use PCIe PME */
unsigned int native_ltr:1; /* OS may use PCIe LTR */
unsigned int native_dpc:1; /* OS may use PCIe DPC */
+ unsigned int native_flit:1; /* OS may use PCIe Flit logging */
unsigned int native_cxl_error:1; /* OS may use CXL RAS/Events */
unsigned int preserve_config:1; /* Preserve FW resource setup */
unsigned int size_windows:1; /* Enable root bus sizing */
diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h
index facaa324bd86..73482024359b 100644
--- a/include/uapi/linux/pci_regs.h
+++ b/include/uapi/linux/pci_regs.h
@@ -763,7 +763,8 @@
#define PCI_EXT_CAP_ID_DEV3 0x2F /* Device 3 Capability/Control/Status */
#define PCI_EXT_CAP_ID_IDE 0x30 /* Integrity and Data Encryption */
#define PCI_EXT_CAP_ID_PL_64GT 0x31 /* Physical Layer 64.0 GT/s */
-#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_PL_64GT
+#define PCI_EXT_CAP_ID_FLIT 0x32 /* Flit Logging */
+#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_FLIT
#define PCI_EXT_CAP_DSN_SIZEOF 12
#define PCI_EXT_CAP_MCAST_ENDPOINT_SIZEOF 40
@@ -1187,6 +1188,22 @@
/* Physical Layer 64.0 GT/s */
#define PCI_PL_64GT_LE_CTRL 0x20 /* Lane Equalization Control Register */
+/* Flit Logging Extended Capability */
+#define PCI_FLIT_ERR_LOG1 0x04 /* Flit Error Log 1 Register */
+#define PCI_FLIT_ERR_LOG1_VALID 0x00000001 /* Flit Error Log Valid */
+#define PCI_FLIT_ERR_LOG1_MORE 0x00002000 /* More Entries Valid */
+#define PCI_FLIT_ERR_LOG1_UNRECOG 0x00004000 /* Unrecognized Flit */
+#define PCI_FLIT_ERR_LOG1_FEC_UNCOR 0x00008000 /* FEC Uncorrectable Error in Flit */
+#define PCI_FLIT_ERR_LOG1_SYND_PARITY0 0x00ff0000 /* Syndrome Parity for ECC Group 0 */
+#define PCI_FLIT_ERR_LOG1_SYND_CHECK0 0xff000000 /* Syndrome Check for ECC Group 0 */
+#define PCI_FLIT_ERR_LOG2 0x08 /* Flit Error Log 2 Register */
+#define PCI_FLIT_ERR_CNTR_CTRL 0x0c /* Flit Error Counter Control Register */
+#define PCI_FLIT_ERR_CNTR_CTRL_EN 0x0001 /* Flit Error Counter Enable */
+#define PCI_FLIT_ERR_CNTR_CTRL_INTR_EN 0x0002 /* Flit Error Counter Interrupt Enable */
+#define PCI_FLIT_ERR_CNTR_CTRL_TRIGGER 0x0ff0 /* Trigger Event on Error Count */
+#define PCI_FLIT_ERR_CNTR_STA 0x0e /* Flit Error Counter Status Register */
+#define PCI_FLIT_ERR_CNTR_STA_INTR_GEN 0x0008 /* Interrupt Generated based on Trigger */
+
/* Native PCIe Enclosure Management */
#define PCI_NPEM_CAP 0x04 /* NPEM capability register */
#define PCI_NPEM_CAP_CAPABLE 0x00000001 /* NPEM Capable */
--
2.43.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v2 08/10] PCI/FLIT: Add trace event for flit errors
2026-09-18 14:56 [PATCH v2 00/10] PCIe Flit Logging Ext Capability Support Yazen Ghannam
` (6 preceding siblings ...)
2026-09-18 14:56 ` [PATCH v2 07/10] PCI/FLIT: Add support for Flit Logging Extended Capability Yazen Ghannam
@ 2026-09-18 14:56 ` Yazen Ghannam
2026-09-18 14:56 ` [PATCH v2 09/10] PCI/FLIT: Ratelimit flit error logging Yazen Ghannam
2026-09-18 14:56 ` [PATCH v2 10/10] PCI/FLIT: Expose the error counter controls through sysfs Yazen Ghannam
9 siblings, 0 replies; 12+ messages in thread
From: Yazen Ghannam @ 2026-09-18 14:56 UTC (permalink / raw)
To: linux-pci; +Cc: linux-kernel, linux-acpi, linux-edac, bhelgaas, Yazen Ghannam
Emit a trace event for each flit error logged from the Flit Error Log.
The event carries both Error Log registers.
Assisted-by: LLM
Signed-off-by: Yazen Ghannam <yazen.ghannam@amd.com>
---
drivers/pci/pcie/flit.c | 3 +++
include/ras/ras_event.h | 38 ++++++++++++++++++++++++++++++++++++++
2 files changed, 41 insertions(+)
diff --git a/drivers/pci/pcie/flit.c b/drivers/pci/pcie/flit.c
index 7c0b9c8dd481..9eb804035997 100644
--- a/drivers/pci/pcie/flit.c
+++ b/drivers/pci/pcie/flit.c
@@ -18,6 +18,7 @@
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
+#include <ras/ras_event.h>
#include "portdrv.h"
#include "../pci.h"
@@ -205,6 +206,8 @@ static void flit_report(struct pci_dev *pdev, u32 err_log1, u32 err_log2)
return;
pci_warn(pdev, "ErrLog1: 0x%08x ErrLog2: 0x%08x\n", err_log1, err_log2);
+
+ trace_flit_event(pci_name(pdev), err_log1, err_log2);
}
static irqreturn_t flit_ist(int irq, void *context)
diff --git a/include/ras/ras_event.h b/include/ras/ras_event.h
index fdb785fa4613..427585505dd6 100644
--- a/include/ras/ras_event.h
+++ b/include/ras/ras_event.h
@@ -381,6 +381,44 @@ TRACE_EVENT(aer_event,
"Not available")
);
#endif /* CONFIG_PCIEAER */
+
+/*
+ * PCIe Flit Logging trace event
+ *
+ * One event is generated for each flit error the driver logs from the Flit
+ * Error Log of a PCIe Flit Mode port.
+ *
+ * char * dev_name - The name of the slot where the device resides
+ * ([domain:]bus:device.function).
+ * u32 err_log1 - Flit Error Log 1 register value.
+ * u32 err_log2 - Flit Error Log 2 register value.
+ */
+#ifdef CONFIG_PCIE_FLIT
+TRACE_EVENT(flit_event,
+ TP_PROTO(const char *dev_name,
+ const u32 err_log1,
+ const u32 err_log2),
+
+ TP_ARGS(dev_name, err_log1, err_log2),
+
+ TP_STRUCT__entry(
+ __string( dev_name, dev_name )
+ __field( u32, err_log1 )
+ __field( u32, err_log2 )
+ ),
+
+ TP_fast_assign(
+ __assign_str(dev_name);
+ __entry->err_log1 = err_log1;
+ __entry->err_log2 = err_log2;
+ ),
+
+ TP_printk("%s err_log1:0x%08x err_log2:0x%08x",
+ __get_str(dev_name),
+ __entry->err_log1,
+ __entry->err_log2)
+);
+#endif /* CONFIG_PCIE_FLIT */
#endif /* _TRACE_HW_EVENT_MC_H */
/* This part must be outside protection */
--
2.43.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v2 09/10] PCI/FLIT: Ratelimit flit error logging
2026-09-18 14:56 [PATCH v2 00/10] PCIe Flit Logging Ext Capability Support Yazen Ghannam
` (7 preceding siblings ...)
2026-09-18 14:56 ` [PATCH v2 08/10] PCI/FLIT: Add trace event for flit errors Yazen Ghannam
@ 2026-09-18 14:56 ` Yazen Ghannam
2026-09-18 14:56 ` [PATCH v2 10/10] PCI/FLIT: Expose the error counter controls through sysfs Yazen Ghannam
9 siblings, 0 replies; 12+ messages in thread
From: Yazen Ghannam @ 2026-09-18 14:56 UTC (permalink / raw)
To: linux-pci; +Cc: linux-kernel, linux-acpi, linux-edac, bhelgaas, Yazen Ghannam
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@amd.com>
---
.../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@vger.kernel.org
+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@vger.kernel.org
+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
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v2 10/10] PCI/FLIT: Expose the error counter controls through sysfs
2026-09-18 14:56 [PATCH v2 00/10] PCIe Flit Logging Ext Capability Support Yazen Ghannam
` (8 preceding siblings ...)
2026-09-18 14:56 ` [PATCH v2 09/10] PCI/FLIT: Ratelimit flit error logging Yazen Ghannam
@ 2026-09-18 14:56 ` Yazen Ghannam
9 siblings, 0 replies; 12+ messages in thread
From: Yazen Ghannam @ 2026-09-18 14:56 UTC (permalink / raw)
To: linux-pci; +Cc: linux-kernel, linux-acpi, linux-edac, bhelgaas, Yazen Ghannam
The Flit Error Counter only generates an event when the Flit Error
Counter exceeds Trigger Event on Error Count, and the counter drains on
its own at a rate set by the link width and encoding. The driver picks a
default for that field when the platform leaves it at zero, but the
right threshold depends on the link and on how noisy an error rate is
worth reporting. Which events the counter counts at all is likewise a
policy choice, not something the driver can pick for every user.
Expose both fields of the Flit Error Counter Control register through
the per-device "flit" sysfs group, gated on CAP_SYS_ADMIN like the
ratelimit attributes beside them.
Track the trigger in struct flit_info rather than rereading it from the
register on every enable. Sampling the register only works while nothing
but the platform writes it. Once the value can come from sysfs, a zero
means "the user asked for no events" rather than "the platform left it
alone", and rederiving the default would put it back on the next resume.
Apply each write to the extended capability save buffer as well.
pcie_portdrv_slot_reset() restores that buffer after running the
.slot_reset callbacks, so it is the last writer on the recovery path and
would otherwise put the probe-time settings back.
These attributes are the first user-context config accesses in this
driver, so hold a runtime PM reference across them.
Assisted-by: LLM
Signed-off-by: Yazen Ghannam <yazen.ghannam@amd.com>
---
.../ABI/testing/sysfs-bus-pci-devices-flit | 35 ++++
drivers/pci/pcie/flit.c | 156 +++++++++++++++++-
include/uapi/linux/pci_regs.h | 1 +
3 files changed, 186 insertions(+), 6 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-bus-pci-devices-flit b/Documentation/ABI/testing/sysfs-bus-pci-devices-flit
index 43d7c7aacf81..01f22ac9ebf6 100644
--- a/Documentation/ABI/testing/sysfs-bus-pci-devices-flit
+++ b/Documentation/ABI/testing/sysfs-bus-pci-devices-flit
@@ -33,3 +33,38 @@ Description: Ratelimit burst for flit error logs. Writing a value changes
Writing 0, or any negative value, suppresses all logs
unless the interval attribute is also 0. Default is
DEFAULT_RATELIMIT_BURST (10).
+
+What: /sys/bus/pci/devices/<dev>/flit/trigger_count
+Date: September 2026
+KernelVersion: 7.4.0
+Contact: linux-pci@vger.kernel.org
+Description: Trigger Event on Error Count field of the Flit Error Counter
+ Control register. An event is generated when the Flit Error
+ Counter exceeds this value. Accepts 0 to 255. Reading gets
+ the current field value.
+
+ The counter is eight bits and decrements on its own at a
+ rate set by the link width and encoding, so a high value
+ reports only a link whose error rate greatly exceeds that
+ drain rate. Writing 0 disables event generation, and 255
+ can never be exceeded, so neither value generates an event.
+
+ A value set by the platform is left alone. Default is 254
+ where the platform left the field at zero.
+
+What: /sys/bus/pci/devices/<dev>/flit/events_to_count
+Date: September 2026
+KernelVersion: 7.4.0
+Contact: linux-pci@vger.kernel.org
+Description: Events to Count field of the Flit Error Counter Control
+ register, selecting which events the Flit Error Counter
+ counts. Reading gets the current field value::
+
+ 0 FEC-correctable Flit, Invalid Flit, or Framing Error
+ 1 FEC-correctable Flit
+ 2 Invalid Flit
+ 3 the events selected by 0, plus a 1b/1b TS Ordered Set
+ with only one valid half, and an invalid Ordered Set
+
+ The driver never writes this field on its own, so it
+ defaults to the value set by the platform.
diff --git a/drivers/pci/pcie/flit.c b/drivers/pci/pcie/flit.c
index 252bbb54e617..33ec1b9c385c 100644
--- a/drivers/pci/pcie/flit.c
+++ b/drivers/pci/pcie/flit.c
@@ -13,6 +13,7 @@
#define dev_fmt pr_fmt
#include <linux/bitfield.h>
+#include <linux/capability.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
@@ -79,11 +80,14 @@ 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;
+ u16 trigger; /* Trigger Event on Error Count to program */
bool cntr_enabled; /* Counter Enable as the driver intends it */
};
void pci_flit_init(struct pci_dev *pdev)
{
+ u16 ctrl, trigger;
+
if (!flit_is_port(pdev) || !flit_is_native(pdev))
return;
@@ -103,6 +107,17 @@ void pci_flit_init(struct pci_dev *pdev)
DEFAULT_RATELIMIT_INTERVAL,
DEFAULT_RATELIMIT_BURST);
+ /*
+ * Sample the trigger before anything here writes the register. A zero
+ * field means the platform left it alone, so fall back to the default.
+ * From here on flit_info->trigger is what gets programmed, so a zero
+ * written through sysfs stays zero.
+ */
+ pci_read_config_word(pdev, pdev->flit_cap + PCI_FLIT_ERR_CNTR_CTRL,
+ &ctrl);
+ trigger = FIELD_GET(PCI_FLIT_ERR_CNTR_CTRL_TRIGGER, ctrl);
+ pdev->flit_info->trigger = trigger ? trigger : FLIT_DEFAULT_TRIGGER;
+
pci_add_ext_cap_save_buffer(pdev, PCI_EXT_CAP_ID_FLIT, sizeof(u16));
pci_dbg(pdev, "Flit Logging Extended Capability present.\n");
@@ -164,16 +179,24 @@ void pci_restore_flit_state(struct pci_dev *pdev)
* Count at zero, and per PCIe r6.4, sec 7.7.8.4, table 7-95, a zero trigger
* never generates an event.
*/
-static void flit_cntr_ctrl_update(struct pci_dev *pdev, u16 clear, u16 set)
+static void __flit_cntr_ctrl_update(struct pci_dev *pdev, u16 clear, u16 set)
{
- unsigned long flags;
u16 ctrl;
- spin_lock_irqsave(&pdev->flit_info->lock, flags);
+ lockdep_assert_held(&pdev->flit_info->lock);
+
pci_read_config_word(pdev, pdev->flit_cap + PCI_FLIT_ERR_CNTR_CTRL, &ctrl);
ctrl &= ~clear;
ctrl |= set;
pci_write_config_word(pdev, pdev->flit_cap + PCI_FLIT_ERR_CNTR_CTRL, ctrl);
+}
+
+static void flit_cntr_ctrl_update(struct pci_dev *pdev, u16 clear, u16 set)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdev->flit_info->lock, flags);
+ __flit_cntr_ctrl_update(pdev, clear, set);
spin_unlock_irqrestore(&pdev->flit_info->lock, flags);
}
@@ -186,9 +209,130 @@ static void flit_cntr_ctrl_update(struct pci_dev *pdev, u16 clear, u16 set)
PCI_RATELIMIT_INTERVAL_ATTR(ratelimit_interval_ms, flit_info->ratelimit);
PCI_RATELIMIT_BURST_ATTR(ratelimit_burst, flit_info->ratelimit);
+/*
+ * Program a durable setting into the register and the save buffer.
+ * pcie_portdrv_slot_reset() restores that buffer after running .slot_reset, so
+ * it is the last writer on the recovery path and would otherwise put the
+ * probe-time word back.
+ *
+ * Both writes happen under the lock, so the register and the buffer cannot
+ * disagree if trigger_count and events_to_count are written at once. The
+ * buffer gets the same clear and set rather than a fresh sample, which would
+ * pick up the Counter Enable that flit_isr() leaves clear for flit_ist().
+ */
+static void flit_cntr_ctrl_set(struct pci_dev *pdev, u16 clear, u16 set)
+{
+ struct pci_cap_saved_state *save_state;
+ unsigned long flags;
+ u16 *cap;
+
+ save_state = pci_find_saved_ext_cap(pdev, PCI_EXT_CAP_ID_FLIT);
+ cap = save_state ? (u16 *)&save_state->cap.data[0] : NULL;
+
+ pci_config_pm_runtime_get(pdev);
+
+ spin_lock_irqsave(&pdev->flit_info->lock, flags);
+ __flit_cntr_ctrl_update(pdev, clear, set);
+ if (cap) {
+ *cap &= ~clear;
+ *cap |= set;
+ }
+ spin_unlock_irqrestore(&pdev->flit_info->lock, flags);
+
+ pci_config_pm_runtime_put(pdev);
+}
+
+/* A port in D3cold answers config reads with all ones. */
+static u16 flit_cntr_ctrl_read(struct pci_dev *pdev)
+{
+ u16 ctrl;
+
+ pci_config_pm_runtime_get(pdev);
+ pci_read_config_word(pdev, pdev->flit_cap + PCI_FLIT_ERR_CNTR_CTRL,
+ &ctrl);
+ pci_config_pm_runtime_put(pdev);
+
+ return ctrl;
+}
+
+static ssize_t trigger_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ u16 ctrl, trigger;
+
+ ctrl = flit_cntr_ctrl_read(pdev);
+ trigger = FIELD_GET(PCI_FLIT_ERR_CNTR_CTRL_TRIGGER, ctrl);
+
+ return sysfs_emit(buf, "%u\n", trigger);
+}
+
+static ssize_t trigger_count_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ u16 val;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (kstrtou16(buf, 0, &val) < 0)
+ return -EINVAL;
+
+ if (val > FIELD_MAX(PCI_FLIT_ERR_CNTR_CTRL_TRIGGER))
+ return -EINVAL;
+
+ pdev->flit_info->trigger = val;
+ flit_cntr_ctrl_set(pdev, PCI_FLIT_ERR_CNTR_CTRL_TRIGGER,
+ FIELD_PREP(PCI_FLIT_ERR_CNTR_CTRL_TRIGGER, val));
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(trigger_count);
+
+static ssize_t events_to_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ u16 ctrl, events;
+
+ ctrl = flit_cntr_ctrl_read(pdev);
+ events = FIELD_GET(PCI_FLIT_ERR_CNTR_CTRL_EVENTS, ctrl);
+
+ return sysfs_emit(buf, "%u\n", events);
+}
+
+static ssize_t events_to_count_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ u16 val;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (kstrtou16(buf, 0, &val) < 0)
+ return -EINVAL;
+
+ if (val > FIELD_MAX(PCI_FLIT_ERR_CNTR_CTRL_EVENTS))
+ return -EINVAL;
+
+ flit_cntr_ctrl_set(pdev, PCI_FLIT_ERR_CNTR_CTRL_EVENTS,
+ FIELD_PREP(PCI_FLIT_ERR_CNTR_CTRL_EVENTS, val));
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(events_to_count);
+
static struct attribute *flit_attrs[] = {
&dev_attr_ratelimit_interval_ms.attr,
&dev_attr_ratelimit_burst.attr,
+ &dev_attr_trigger_count.attr,
+ &dev_attr_events_to_count.attr,
NULL
};
@@ -223,9 +367,9 @@ static void flit_cntr_enable(struct pci_dev *pdev)
spin_lock_irqsave(&pdev->flit_info->lock, flags);
pci_read_config_word(pdev, flit + PCI_FLIT_ERR_CNTR_CTRL, ®);
- /* Set default trigger count if not set by platform, since zero never fires */
- if (!(reg & PCI_FLIT_ERR_CNTR_CTRL_TRIGGER))
- reg |= FIELD_PREP(PCI_FLIT_ERR_CNTR_CTRL_TRIGGER, FLIT_DEFAULT_TRIGGER);
+ reg &= ~PCI_FLIT_ERR_CNTR_CTRL_TRIGGER;
+ reg |= FIELD_PREP(PCI_FLIT_ERR_CNTR_CTRL_TRIGGER,
+ pdev->flit_info->trigger);
reg |= PCI_FLIT_ERR_CNTR_CTRL_EN | PCI_FLIT_ERR_CNTR_CTRL_INTR_EN;
pci_write_config_word(pdev, flit + PCI_FLIT_ERR_CNTR_CTRL, reg);
diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h
index 73482024359b..bf55b2e42ce3 100644
--- a/include/uapi/linux/pci_regs.h
+++ b/include/uapi/linux/pci_regs.h
@@ -1200,6 +1200,7 @@
#define PCI_FLIT_ERR_CNTR_CTRL 0x0c /* Flit Error Counter Control Register */
#define PCI_FLIT_ERR_CNTR_CTRL_EN 0x0001 /* Flit Error Counter Enable */
#define PCI_FLIT_ERR_CNTR_CTRL_INTR_EN 0x0002 /* Flit Error Counter Interrupt Enable */
+#define PCI_FLIT_ERR_CNTR_CTRL_EVENTS 0x000c /* Events to count */
#define PCI_FLIT_ERR_CNTR_CTRL_TRIGGER 0x0ff0 /* Trigger Event on Error Count */
#define PCI_FLIT_ERR_CNTR_STA 0x0e /* Flit Error Counter Status Register */
#define PCI_FLIT_ERR_CNTR_STA_INTR_GEN 0x0008 /* Interrupt Generated based on Trigger */
--
2.43.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 07/10] PCI/FLIT: Add support for Flit Logging Extended Capability
2026-09-18 14:56 ` [PATCH v2 07/10] PCI/FLIT: Add support for Flit Logging Extended Capability Yazen Ghannam
@ 2026-09-18 16:38 ` Ilpo Järvinen
0 siblings, 0 replies; 12+ messages in thread
From: Ilpo Järvinen @ 2026-09-18 16:38 UTC (permalink / raw)
To: Yazen Ghannam; +Cc: linux-pci, LKML, linux-acpi, linux-edac, bhelgaas
[-- Attachment #1: Type: text/plain, Size: 23907 bytes --]
On Fri, 18 Sep 2026, Yazen Ghannam wrote:
> PCIe r6.4 defines the Flit Logging Extended Capability for logging
> erroneous flits, and requires it on ports and RCRBs that support PCIe
> Flit Mode.[1]
>
> Add a port service driver for it. Include _OSC negotiation for control
> of the capability.
>
> The driver claims only the port types the PCIe port bus driver binds to.
> An Endpoint implements the capability on its Upstream Port too, and _OSC
> is negotiated per host bridge, so accepting control leaves those
> instances with neither firmware nor the OS managing them. Covering them
> needs a home outside the port bus driver.
>
> [1] PCI Express® Base Specification Revision 6.4, section 7.7.8
>
> Link: https://pcisig.com/specification/extend-osc-negotiate-control-pcie-flit-logging-extended-capability
> Originally-by: Avadhut Naik <avadhut.naik@amd.com>
> Assisted-by: LLM
> Signed-off-by: Yazen Ghannam <yazen.ghannam@amd.com>
> ---
> drivers/acpi/pci_root.c | 11 ++
> drivers/pci/pci.c | 2 +
> drivers/pci/pci.h | 12 ++
> drivers/pci/pcie/Kconfig | 9 +
> drivers/pci/pcie/Makefile | 1 +
> drivers/pci/pcie/flit.c | 324 ++++++++++++++++++++++++++++++++++
> drivers/pci/pcie/portdrv.c | 7 +
> drivers/pci/pcie/portdrv.h | 13 +-
> drivers/pci/probe.c | 3 +
> include/linux/acpi.h | 1 +
> include/linux/pci.h | 6 +
> include/uapi/linux/pci_regs.h | 19 +-
> 12 files changed, 405 insertions(+), 3 deletions(-)
> create mode 100644 drivers/pci/pcie/flit.c
>
> diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
> index 88c65f34e305..402686a9ca4c 100644
> --- a/drivers/acpi/pci_root.c
> +++ b/drivers/acpi/pci_root.c
> @@ -137,6 +137,7 @@ static struct pci_osc_bit_struct pci_osc_control_bit[] = {
> { OSC_PCI_EXPRESS_CAPABILITY_CONTROL, "PCIeCapability" },
> { OSC_PCI_EXPRESS_LTR_CONTROL, "LTR" },
> { OSC_PCI_EXPRESS_DPC_CONTROL, "DPC" },
> + { OSC_PCI_EXPRESS_FLIT_CONTROL, "FlitLogging" },
> };
>
> static struct pci_osc_bit_struct cxl_osc_support_bit[] = {
> @@ -520,6 +521,14 @@ static u32 calculate_control(void)
> if (IS_ENABLED(CONFIG_PCIE_DPC) && IS_ENABLED(CONFIG_PCIE_EDR))
> control |= OSC_PCI_EXPRESS_DPC_CONTROL;
>
> + /*
> + * An OS that requests Flit Logging control must request AER control
> + * as well, so pair it with the bit that was actually requested above.
> + */
> + if (IS_ENABLED(CONFIG_PCIE_FLIT) &&
> + (control & OSC_PCI_EXPRESS_AER_CONTROL))
> + control |= OSC_PCI_EXPRESS_FLIT_CONTROL;
> +
> return control;
> }
>
> @@ -1040,6 +1049,8 @@ struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root,
> host_bridge->native_ltr = 0;
> if (!(root->osc_control_set & OSC_PCI_EXPRESS_DPC_CONTROL))
> host_bridge->native_dpc = 0;
> + if (!(root->osc_control_set & OSC_PCI_EXPRESS_FLIT_CONTROL))
> + host_bridge->native_flit = 0;
>
> if (!(root->osc_ext_control_set & OSC_CXL_ERROR_REPORTING_CONTROL))
> host_bridge->native_cxl_error = 0;
> diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
> index b2879a6be5f8..c1cfa0e3d75c 100644
> --- a/drivers/pci/pci.c
> +++ b/drivers/pci/pci.c
> @@ -1798,6 +1798,7 @@ int pci_save_state(struct pci_dev *dev)
>
> pci_save_dpc_state(dev);
> pci_save_aer_state(dev);
> + pci_save_flit_state(dev);
> pci_save_ptm_state(dev);
> pci_save_tph_state(dev);
> return pci_save_vc_state(dev);
> @@ -1870,6 +1871,7 @@ void pci_restore_state(struct pci_dev *dev)
>
> pci_aer_clear_status(dev);
> pci_restore_aer_state(dev);
> + pci_restore_flit_state(dev);
>
> pci_restore_config_space(dev);
>
> diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
> index f43c5330fca3..53a90f75cd76 100644
> --- a/drivers/pci/pci.h
> +++ b/drivers/pci/pci.h
> @@ -1000,6 +1000,18 @@ static inline void pci_dpc_init(struct pci_dev *pdev) { }
> static inline bool pci_dpc_recovered(struct pci_dev *pdev) { return false; }
> #endif
>
> +#ifdef CONFIG_PCIE_FLIT
> +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);
> +#else
> +static inline void pci_flit_init(struct pci_dev *pdev) { }
> +static inline void pci_flit_exit(struct pci_dev *pdev) { }
> +static inline void pci_save_flit_state(struct pci_dev *pdev) { }
> +static inline void pci_restore_flit_state(struct pci_dev *pdev) { }
> +#endif
> +
> #ifdef CONFIG_PCIEPORTBUS
> void pci_rcec_init(struct pci_dev *dev);
> void pci_rcec_exit(struct pci_dev *dev);
> diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig
> index 207c2deae35f..7ddadad57d3f 100644
> --- a/drivers/pci/pcie/Kconfig
> +++ b/drivers/pci/pcie/Kconfig
> @@ -146,3 +146,12 @@ config PCIE_EDR
> the PCI Firmware Specification r3.2. Enable this if you want to
> support hybrid DPC model which uses both firmware and OS to
> implement DPC.
> +
> +config PCIE_FLIT
> + bool "PCI Express Flit Logging support"
> + depends on PCIEPORTBUS && PCIEAER
> + help
> + This enables support for the PCI Express Flit Logging Extended
> + Capability, which logs errors encountered by a port operating
> + in PCIe Flit Mode. If your system doesn't have this capability
> + or you do not want to use this feature, it is safe to answer N.
> diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile
> index b0b43a18c304..14a5222920b9 100644
> --- a/drivers/pci/pcie/Makefile
> +++ b/drivers/pci/pcie/Makefile
> @@ -14,3 +14,4 @@ obj-$(CONFIG_PCIE_PME) += pme.o
> obj-$(CONFIG_PCIE_DPC) += dpc.o
> obj-$(CONFIG_PCIE_PTM) += ptm.o
> obj-$(CONFIG_PCIE_EDR) += edr.o
> +obj-$(CONFIG_PCIE_FLIT) += flit.o
> diff --git a/drivers/pci/pcie/flit.c b/drivers/pci/pcie/flit.c
> new file mode 100644
> index 000000000000..7c0b9c8dd481
> --- /dev/null
> +++ b/drivers/pci/pcie/flit.c
> @@ -0,0 +1,324 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Service driver for PCIe Flit Logging Extended Capability
> + *
> + * Copyright (c) 2026, Advanced Micro Devices, Inc.
> + * All Rights Reserved.
> + *
> + * Authors: Avadhut Naik <Avadhut.Naik@amd.com>
> + * Yazen Ghannam <Yazen.Ghannam@amd.com>
> + */
> +
> +#define pr_fmt(fmt) "Flit: " fmt
> +#define dev_fmt pr_fmt
But you lack the actual include for printing??
> +#include <linux/bitfield.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/pci.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
Add empty line.
> +#include "portdrv.h"
> +#include "../pci.h"
> +
> +/*
> + * Report only a link accumulating errors far faster than the counter drains.
> + * 0xff never fires, since an event needs the counter to exceed the trigger.
> + */
> +#define FLIT_DEFAULT_TRIGGER 0xfe
> +
> +/*
> + * Per PCIe r6.4, sec 7.7.8.2, table 7-93, More Entries can be set again
> + * mid-drain, so bound the loop.
> + */
> +#define FLIT_MAX_LOG_ENTRIES 0xff
> +
> +/*
> + * Per PCIe r6.4, sec 7.7.8.2, table 7-92, an entry with none of these set
> + * and Flit Error Log 2 zero is Reserved.
> + */
> +#define FLIT_ERR_LOG1_ERROR (PCI_FLIT_ERR_LOG1_UNRECOG | \
> + PCI_FLIT_ERR_LOG1_FEC_UNCOR | \
> + PCI_FLIT_ERR_LOG1_SYND_PARITY0 | \
> + PCI_FLIT_ERR_LOG1_SYND_CHECK0)
> +
> +/* Only the port types pcie_portdrv_probe() claims get a Flit service. */
> +static bool flit_is_port(struct pci_dev *pdev)
> +{
> + if (!pci_is_pcie(pdev))
> + return false;
> +
> + switch (pci_pcie_type(pdev)) {
> + case PCI_EXP_TYPE_ROOT_PORT:
> + case PCI_EXP_TYPE_UPSTREAM:
> + case PCI_EXP_TYPE_DOWNSTREAM:
> + case PCI_EXP_TYPE_RC_EC:
> + return true;
> + default:
> + return false;
> + }
> +}
> +
> +/*
> + * Without _OSC control the OS must not touch the capability. Flit Logging
> + * control is only meaningful alongside AER control, so require both.
> + */
> +static bool flit_is_native(struct pci_dev *pdev)
> +{
> + struct pci_host_bridge *host = pci_find_host_bridge(pdev->bus);
> +
> + if (pcie_ports_native)
> + return true;
> +
> + return host->native_flit && host->native_aer;
> +}
> +
> +struct flit_info {
> + spinlock_t lock; /* serializes Counter Control read-modify-write */
> + bool cntr_enabled; /* Counter Enable as the driver intends it */
> +};
Move before functions.
> +
> +void pci_flit_init(struct pci_dev *pdev)
> +{
> + if (!flit_is_port(pdev) || !flit_is_native(pdev))
> + return;
> +
> + pdev->flit_cap = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_FLIT);
> + if (!pdev->flit_cap)
> + return;
> +
> + pdev->flit_info = kzalloc_obj(*pdev->flit_info);
> + if (!pdev->flit_info) {
> + pdev->flit_cap = 0;
> + return;
> + }
> +
> + spin_lock_init(&pdev->flit_info->lock);
> +
> + pci_add_ext_cap_save_buffer(pdev, PCI_EXT_CAP_ID_FLIT, sizeof(u16));
> +
> + pci_dbg(pdev, "Flit Logging Extended Capability present.\n");
> +}
> +
> +void pci_flit_exit(struct pci_dev *pdev)
> +{
> + kfree(pdev->flit_info);
> + pdev->flit_info = NULL;
> + pdev->flit_cap = 0;
> +}
> +
> +void pci_save_flit_state(struct pci_dev *pdev)
> +{
> + struct pci_cap_saved_state *save_state;
> + u16 *cap;
> +
> + if (!pdev->flit_cap)
> + return;
> +
> + save_state = pci_find_saved_ext_cap(pdev, PCI_EXT_CAP_ID_FLIT);
> + if (!save_state)
> + return;
> +
> + /*
> + * Take Counter Enable from the driver rather than the register.
> + * flit_isr() clears it and only flit_ist() sets it again once the log
> + * is drained, and a save landing in between would capture a zero that
> + * the next restore writes back for good.
> + */
> + cap = (u16 *)&save_state->cap.data[0];
> + pci_read_config_word(pdev, pdev->flit_cap + PCI_FLIT_ERR_CNTR_CTRL, cap);
> + if (pdev->flit_info->cntr_enabled)
> + *cap |= PCI_FLIT_ERR_CNTR_CTRL_EN;
> + else
> + *cap &= ~PCI_FLIT_ERR_CNTR_CTRL_EN;
> +}
> +
> +void pci_restore_flit_state(struct pci_dev *pdev)
> +{
> + struct pci_cap_saved_state *save_state;
> + u16 *cap;
> +
> + if (!pdev->flit_cap)
> + return;
> +
> + save_state = pci_find_saved_ext_cap(pdev, PCI_EXT_CAP_ID_FLIT);
> + if (!save_state)
> + return;
> +
> + cap = (u16 *)&save_state->cap.data[0];
> + pci_write_config_word(pdev, pdev->flit_cap + PCI_FLIT_ERR_CNTR_CTRL, *cap);
> +}
> +
> +/*
> + * Serializes the Counter Control read-modify-write against flit_isr(), which
> + * runs in hard irq context on the vector this port shares with PME, hotplug
> + * and bandwidth notification. A lost update here leaves Trigger Event on Error
> + * Count at zero, and per PCIe r6.4, sec 7.7.8.4, table 7-95, a zero trigger
> + * never generates an event.
> + */
> +static void flit_cntr_ctrl_update(struct pci_dev *pdev, u16 clear, u16 set)
> +{
> + unsigned long flags;
> + u16 ctrl;
> +
> + spin_lock_irqsave(&pdev->flit_info->lock, flags);
> + pci_read_config_word(pdev, pdev->flit_cap + PCI_FLIT_ERR_CNTR_CTRL, &ctrl);
> + ctrl &= ~clear;
> + ctrl |= set;
> + pci_write_config_word(pdev, pdev->flit_cap + PCI_FLIT_ERR_CNTR_CTRL, ctrl);
I suggest adding the generic clear+set accessor into access.c, there's
already one for dword but not for config word.
> + spin_unlock_irqrestore(&pdev->flit_info->lock, flags);
> +}
> +
> +static void flit_cntr_enable(struct pci_dev *pdev)
> +{
> + u16 flit = pdev->flit_cap;
> + unsigned long flags;
> + u16 reg;
> +
> + pci_read_config_word(pdev, flit + PCI_FLIT_ERR_CNTR_STA, ®);
> + pci_write_config_word(pdev, flit + PCI_FLIT_ERR_CNTR_STA, reg);
> +
> + pdev->flit_info->cntr_enabled = true;
> +
> + spin_lock_irqsave(&pdev->flit_info->lock, flags);
> + pci_read_config_word(pdev, flit + PCI_FLIT_ERR_CNTR_CTRL, ®);
> +
> + /* Set default trigger count if not set by platform, since zero never fires */
> + if (!(reg & PCI_FLIT_ERR_CNTR_CTRL_TRIGGER))
> + reg |= FIELD_PREP(PCI_FLIT_ERR_CNTR_CTRL_TRIGGER, FLIT_DEFAULT_TRIGGER);
> +
> + reg |= PCI_FLIT_ERR_CNTR_CTRL_EN | PCI_FLIT_ERR_CNTR_CTRL_INTR_EN;
> + pci_write_config_word(pdev, flit + PCI_FLIT_ERR_CNTR_CTRL, reg);
> + spin_unlock_irqrestore(&pdev->flit_info->lock, flags);
> +}
> +
> +static void flit_cntr_disable(struct pci_dev *pdev)
> +{
> + pdev->flit_info->cntr_enabled = false;
> + flit_cntr_ctrl_update(pdev, PCI_FLIT_ERR_CNTR_CTRL_EN | PCI_FLIT_ERR_CNTR_CTRL_INTR_EN, 0);
> +}
> +
> +static void flit_report(struct pci_dev *pdev, u32 err_log1, u32 err_log2)
> +{
> + /* Software should silently discard a Reserved entry */
> + if (!(err_log1 & FLIT_ERR_LOG1_ERROR) && !err_log2)
> + return;
> +
> + pci_warn(pdev, "ErrLog1: 0x%08x ErrLog2: 0x%08x\n", err_log1, err_log2);
> +}
> +
> +static irqreturn_t flit_ist(int irq, void *context)
> +{
> + struct pcie_device *dev = (struct pcie_device *)context;
> + struct pci_dev *pdev = dev->port;
> + u16 flit = pdev->flit_cap;
> + u32 err_log1, err_log2;
> + unsigned int i;
> +
> + for (i = 0; i < FLIT_MAX_LOG_ENTRIES; i++) {
> + pci_read_config_dword(pdev, flit + PCI_FLIT_ERR_LOG1, &err_log1);
> +
> + if (PCI_POSSIBLE_ERROR(err_log1) || !(err_log1 & PCI_FLIT_ERR_LOG1_VALID))
> + break;
> +
> + pci_read_config_dword(pdev, flit + PCI_FLIT_ERR_LOG2, &err_log2);
> +
> + flit_report(pdev, err_log1, err_log2);
> +
> + pci_write_config_dword(pdev, flit + PCI_FLIT_ERR_LOG1, err_log1);
> +
> + if (!(err_log1 & PCI_FLIT_ERR_LOG1_MORE))
> + break;
> + }
> +
> + /*
> + * Re-enable the counter. Per PCIe r6.4, sec 7.7.8.5, table 7-96, the
> + * status bit is "Cleared on 0b to 1b transition of Flit Error Counter
> + * Enable", which also clears the counter and re-arms interrupt
> + * generation.
> + */
> + flit_cntr_ctrl_update(pdev, 0, PCI_FLIT_ERR_CNTR_CTRL_EN);
> + return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t flit_isr(int irq, void *context)
> +{
> + struct pcie_device *dev = (struct pcie_device *)context;
> + struct pci_dev *pdev = dev->port;
> + u16 flit = pdev->flit_cap;
> + u16 cntr_sta;
> +
> + pci_read_config_word(pdev, flit + PCI_FLIT_ERR_CNTR_STA, &cntr_sta);
> + if (PCI_POSSIBLE_ERROR(cntr_sta) || !(cntr_sta & PCI_FLIT_ERR_CNTR_STA_INTR_GEN))
> + return IRQ_NONE;
> +
> + /*
> + * Clear Flit Error Counter Enable first, so the counter cannot reach
> + * the trigger again while the thread runs, then clear the status bit.
> + * A message-signaled vector needs no more than that, but this service
> + * can land on INTx, where the line stays asserted until the status is
> + * cleared. flit_ist() transitions Counter Enable back 0->1.
> + */
> + flit_cntr_ctrl_update(pdev, PCI_FLIT_ERR_CNTR_CTRL_EN, 0);
> + pci_write_config_word(pdev, flit + PCI_FLIT_ERR_CNTR_STA,
> + PCI_FLIT_ERR_CNTR_STA_INTR_GEN);
> + return IRQ_WAKE_THREAD;
> +}
> +
> +static int flit_probe(struct pcie_device *dev)
> +{
> + struct pci_dev *pdev = dev->port;
> + int status;
> +
> + /*
> + * Not devm_request_threaded_irq(): devres would free the IRQ only
> + * after .remove() runs, too late to stop flit_ist() re-enabling the
> + * counter.
> + */
> + status = request_threaded_irq(dev->irq, flit_isr, flit_ist,
> + IRQF_SHARED, "pcie-flit", dev);
> + if (status) {
> + pci_warn(pdev, "request Flit IRQ %d failed: %d\n", dev->irq, status);
> + return status;
> + }
> +
> + flit_cntr_enable(pdev);
> + pci_info(pdev, "enabled with IRQ %d\n", dev->irq);
Success path should be silent.
--
i.
> +
> + return 0;
> +}
> +
> +/*
> + * No .suspend or .runtime_suspend counterpart. pci_save_state() runs after
> + * both, so disabling the counter there would put a transient value in the
> + * save buffer, and pcie_portdrv_slot_reset() restores that buffer after
> + * calling .slot_reset.
> + */
> +static int flit_cntr_restore(struct pcie_device *dev)
> +{
> + flit_cntr_enable(dev->port);
> + return 0;
> +}
> +
> +static void flit_cntr_remove(struct pcie_device *dev)
> +{
> + /* Drop the IRQ first: free_irq() waits for the threaded handler. */
> + free_irq(dev->irq, dev);
> + flit_cntr_disable(dev->port);
> +}
> +
> +static struct pcie_port_service_driver flitdriver = {
> + .name = "flit",
> + .port_type = PCIE_ANY_PORT,
> + .service = PCIE_PORT_SERVICE_FLIT,
> + .probe = flit_probe,
> + .resume = flit_cntr_restore,
> + .runtime_resume = flit_cntr_restore,
> + .slot_reset = flit_cntr_restore,
> + .remove = flit_cntr_remove,
> +};
> +
> +int __init pcie_flit_init(void)
> +{
> + return pcie_port_service_register(&flitdriver);
> +}
> diff --git a/drivers/pci/pcie/portdrv.c b/drivers/pci/pcie/portdrv.c
> index ca1b9dbb8b08..6328233c75d7 100644
> --- a/drivers/pci/pcie/portdrv.c
> +++ b/drivers/pci/pcie/portdrv.c
> @@ -278,6 +278,12 @@ static int get_port_device_capability(struct pci_dev *dev)
> services |= PCIE_PORT_SERVICE_BWCTRL;
> }
>
> +#ifdef CONFIG_PCIE_FLIT
> + /* flit_cap is set only when the capability is OS-managed */
> + if (dev->flit_cap)
> + services |= PCIE_PORT_SERVICE_FLIT;
> +#endif
> +
> return services;
> }
>
> @@ -834,6 +840,7 @@ static void __init pcie_init_services(void)
> pcie_dpc_init();
> pcie_bwctrl_init();
> pcie_hp_init();
> + pcie_flit_init();
> }
>
> static int __init pcie_portdrv_init(void)
> diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h
> index bf18ca415990..975f83134a5b 100644
> --- a/drivers/pci/pcie/portdrv.h
> +++ b/drivers/pci/pcie/portdrv.h
> @@ -22,13 +22,16 @@
> #define PCIE_PORT_SERVICE_DPC (1 << PCIE_PORT_SERVICE_DPC_SHIFT)
> #define PCIE_PORT_SERVICE_BWCTRL_SHIFT 4 /* Bandwidth Controller (notifications) */
> #define PCIE_PORT_SERVICE_BWCTRL (1 << PCIE_PORT_SERVICE_BWCTRL_SHIFT)
> +#define PCIE_PORT_SERVICE_FLIT_SHIFT 5 /* Flit Logging */
> +#define PCIE_PORT_SERVICE_FLIT (1 << PCIE_PORT_SERVICE_FLIT_SHIFT)
>
> /* Services sharing the PCI Express Capability Interrupt Message Number */
> #define PCIE_PORT_SERVICES_EXPCAP (PCIE_PORT_SERVICE_PME | \
> PCIE_PORT_SERVICE_HP | \
> - PCIE_PORT_SERVICE_BWCTRL)
> + PCIE_PORT_SERVICE_BWCTRL | \
> + PCIE_PORT_SERVICE_FLIT)
>
> -#define PCIE_PORT_DEVICE_MAXSERVICES 5
> +#define PCIE_PORT_DEVICE_MAXSERVICES 6
>
> extern bool pcie_ports_dpc_native;
>
> @@ -56,6 +59,12 @@ int pcie_dpc_init(void);
> static inline int pcie_dpc_init(void) { return 0; }
> #endif
>
> +#ifdef CONFIG_PCIE_FLIT
> +int pcie_flit_init(void);
> +#else
> +static inline int pcie_flit_init(void) { return 0; }
> +#endif
> +
> int pcie_bwctrl_init(void);
>
> /* Port Type */
> diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
> index 27008e2ea5af..6bf62bdadc8d 100644
> --- a/drivers/pci/probe.c
> +++ b/drivers/pci/probe.c
> @@ -674,6 +674,7 @@ static void pci_init_host_bridge(struct pci_host_bridge *bridge)
> bridge->native_pme = 1;
> bridge->native_ltr = 1;
> bridge->native_dpc = 1;
> + bridge->native_flit = 1;
> bridge->domain_nr = PCI_DOMAIN_NR_NOT_SET;
> bridge->native_cxl_error = 1;
> bridge->dev.type = &pci_host_bridge_type;
> @@ -2465,6 +2466,7 @@ static void pci_configure_device(struct pci_dev *dev)
>
> static void pci_release_capabilities(struct pci_dev *dev)
> {
> + pci_flit_exit(dev);
> pci_aer_exit(dev);
> pci_rcec_exit(dev);
> pci_iov_release(dev);
> @@ -2666,6 +2668,7 @@ static void pci_init_capabilities(struct pci_dev *dev)
> pci_pasid_init(dev); /* Process Address Space ID */
> pci_acs_init(dev); /* Access Control Services */
> pci_ptm_init(dev); /* Precision Time Measurement */
> + pci_flit_init(dev); /* Flit Logging */
> pci_aer_init(dev); /* Advanced Error Reporting */
> pci_dpc_init(dev); /* Downstream Port Containment */
> pci_rcec_init(dev); /* Root Complex Event Collector */
> diff --git a/include/linux/acpi.h b/include/linux/acpi.h
> index ddacac812094..b3d8a5bb79e9 100644
> --- a/include/linux/acpi.h
> +++ b/include/linux/acpi.h
> @@ -667,6 +667,7 @@ extern u32 osc_sb_native_usb4_control;
> #define OSC_PCI_EXPRESS_CAPABILITY_CONTROL 0x00000010
> #define OSC_PCI_EXPRESS_LTR_CONTROL 0x00000020
> #define OSC_PCI_EXPRESS_DPC_CONTROL 0x00000080
> +#define OSC_PCI_EXPRESS_FLIT_CONTROL 0x00000800
>
> /* CXL _OSC: Capabilities DWORD 4: Support Field */
> #define OSC_CXL_1_1_PORT_REG_ACCESS_SUPPORT 0x00000001
> diff --git a/include/linux/pci.h b/include/linux/pci.h
> index 8650c627bebb..37d01fab50bb 100644
> --- a/include/linux/pci.h
> +++ b/include/linux/pci.h
> @@ -598,6 +598,11 @@ struct pci_dev {
> u8 tph_mode; /* TPH mode */
> u8 tph_req_type; /* TPH requester type */
> #endif
> +
> +#ifdef CONFIG_PCIE_FLIT
> + u16 flit_cap; /* Flit Logging Capability offset */
> + struct flit_info *flit_info; /* Flit Logging state */
> +#endif
> };
>
> static inline struct pci_dev *pci_physfn(struct pci_dev *dev)
> @@ -663,6 +668,7 @@ struct pci_host_bridge {
> unsigned int native_pme:1; /* OS may use PCIe PME */
> unsigned int native_ltr:1; /* OS may use PCIe LTR */
> unsigned int native_dpc:1; /* OS may use PCIe DPC */
> + unsigned int native_flit:1; /* OS may use PCIe Flit logging */
> unsigned int native_cxl_error:1; /* OS may use CXL RAS/Events */
> unsigned int preserve_config:1; /* Preserve FW resource setup */
> unsigned int size_windows:1; /* Enable root bus sizing */
> diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h
> index facaa324bd86..73482024359b 100644
> --- a/include/uapi/linux/pci_regs.h
> +++ b/include/uapi/linux/pci_regs.h
> @@ -763,7 +763,8 @@
> #define PCI_EXT_CAP_ID_DEV3 0x2F /* Device 3 Capability/Control/Status */
> #define PCI_EXT_CAP_ID_IDE 0x30 /* Integrity and Data Encryption */
> #define PCI_EXT_CAP_ID_PL_64GT 0x31 /* Physical Layer 64.0 GT/s */
> -#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_PL_64GT
> +#define PCI_EXT_CAP_ID_FLIT 0x32 /* Flit Logging */
> +#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_FLIT
>
> #define PCI_EXT_CAP_DSN_SIZEOF 12
> #define PCI_EXT_CAP_MCAST_ENDPOINT_SIZEOF 40
> @@ -1187,6 +1188,22 @@
> /* Physical Layer 64.0 GT/s */
> #define PCI_PL_64GT_LE_CTRL 0x20 /* Lane Equalization Control Register */
>
> +/* Flit Logging Extended Capability */
> +#define PCI_FLIT_ERR_LOG1 0x04 /* Flit Error Log 1 Register */
> +#define PCI_FLIT_ERR_LOG1_VALID 0x00000001 /* Flit Error Log Valid */
> +#define PCI_FLIT_ERR_LOG1_MORE 0x00002000 /* More Entries Valid */
> +#define PCI_FLIT_ERR_LOG1_UNRECOG 0x00004000 /* Unrecognized Flit */
> +#define PCI_FLIT_ERR_LOG1_FEC_UNCOR 0x00008000 /* FEC Uncorrectable Error in Flit */
> +#define PCI_FLIT_ERR_LOG1_SYND_PARITY0 0x00ff0000 /* Syndrome Parity for ECC Group 0 */
> +#define PCI_FLIT_ERR_LOG1_SYND_CHECK0 0xff000000 /* Syndrome Check for ECC Group 0 */
> +#define PCI_FLIT_ERR_LOG2 0x08 /* Flit Error Log 2 Register */
> +#define PCI_FLIT_ERR_CNTR_CTRL 0x0c /* Flit Error Counter Control Register */
> +#define PCI_FLIT_ERR_CNTR_CTRL_EN 0x0001 /* Flit Error Counter Enable */
> +#define PCI_FLIT_ERR_CNTR_CTRL_INTR_EN 0x0002 /* Flit Error Counter Interrupt Enable */
> +#define PCI_FLIT_ERR_CNTR_CTRL_TRIGGER 0x0ff0 /* Trigger Event on Error Count */
> +#define PCI_FLIT_ERR_CNTR_STA 0x0e /* Flit Error Counter Status Register */
> +#define PCI_FLIT_ERR_CNTR_STA_INTR_GEN 0x0008 /* Interrupt Generated based on Trigger */
> +
> /* Native PCIe Enclosure Management */
> #define PCI_NPEM_CAP 0x04 /* NPEM capability register */
> #define PCI_NPEM_CAP_CAPABLE 0x00000001 /* NPEM Capable */
>
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-09-18 16:38 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-18 14:56 [PATCH v2 00/10] PCIe Flit Logging Ext Capability Support Yazen Ghannam
2026-09-18 14:56 ` [PATCH v2 01/10] MAINTAINERS: Add the AER sysfs ABI file to PCI SUBSYSTEM Yazen Ghannam
2026-09-18 14:56 ` [PATCH v2 02/10] PCI/AER: Clamp negative ratelimit burst to zero Yazen Ghannam
2026-09-18 14:56 ` [PATCH v2 03/10] PCI/AER: Report the ratelimit interval in milliseconds Yazen Ghannam
2026-09-18 14:56 ` [PATCH v2 04/10] PCI/AER: Hide the "aer" sysfs directory when AER is unavailable Yazen Ghannam
2026-09-18 14:56 ` [PATCH v2 05/10] PCI: Share the AER ratelimit sysfs accessors Yazen Ghannam
2026-09-18 14:56 ` [PATCH v2 06/10] PCI/portdrv: Group PCI Exp Cap services into a single mask Yazen Ghannam
2026-09-18 14:56 ` [PATCH v2 07/10] PCI/FLIT: Add support for Flit Logging Extended Capability Yazen Ghannam
2026-09-18 16:38 ` Ilpo Järvinen
2026-09-18 14:56 ` [PATCH v2 08/10] PCI/FLIT: Add trace event for flit errors Yazen Ghannam
2026-09-18 14:56 ` [PATCH v2 09/10] PCI/FLIT: Ratelimit flit error logging Yazen Ghannam
2026-09-18 14:56 ` [PATCH v2 10/10] PCI/FLIT: Expose the error counter controls through sysfs Yazen Ghannam
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®