Merge branch 'pci/aer'

- Initialize struct aer_err_info before using it to avoid depending on
  stack garbage (Bjorn Helgaas)

- Log the DPC Error Source ID only when it's actually valid (when ERR_FATAL
  or ERR_NONFATAL was received from a downstream device) and decode into
  bus/device/function (Bjorn Helgaas)

- Consolidate AER Error Source ID in one place for message consistency
  (Bjorn Helgaas)

- Update statistics and emit trace events early in AER logging paths,
  before any potential ratelimiting (Bjorn Helgaas)

- Determine AER log level once and save it so all related messages use the
  same level (Karolina Stolarek)

- Use KERN_WARNING, not KERN_ERR, when logging PCIe Correctable Errors.

- Ratelimit PCIe Correctable and Non-Fatal error logging, with sysfs
  controls on interval and burst count, to avoid flooding logs and RCU
  stall warnings (Jon Pan-Doh)

* pci/aer:
  PCI/ERR: Remove misleading TODO regarding kernel panic
  PCI/AER: Add sysfs attributes for log ratelimits
  PCI/AER: Add ratelimits to PCI AER Documentation
  PCI/AER: Ratelimit correctable and non-fatal error logging
  PCI/AER: Simplify add_error_device()
  PCI/AER: Convert aer_get_device_error_info(), aer_print_error() to index
  PCI/AER: Rename struct aer_stats to aer_info
  PCI/AER: Reduce pci_print_aer() correctable error level to KERN_WARNING
  PCI/ERR: Add printk level to pcie_print_tlp_log()
  PCI/AER: Check log level once and remember it
  PCI/AER: Trace error event before ratelimiting
  PCI/AER: Update statistics before ratelimiting
  PCI/AER: Simplify pci_print_aer()
  PCI/AER: Initialize aer_err_info before using it
  PCI/AER: Move aer_print_source() earlier in file
  PCI/AER: Rename aer_print_port_info() to aer_print_source()
  PCI/AER: Extract bus/dev/fn in aer_print_port_info() with PCI_BUS_NUM(), etc
  PCI/AER: Consolidate Error Source ID logging in aer_isr_one_error_type()
  PCI/AER: Factor COR/UNCOR error handling out from aer_isr_one_error()
  PCI/DPC: Log Error Source ID only when valid
  PCI/DPC: Initialize aer_err_info before using it
This commit is contained in:
Bjorn Helgaas
2025-06-04 10:49:49 -05:00
9 changed files with 427 additions and 168 deletions

View File

@@ -117,3 +117,47 @@ Date: July 2018
KernelVersion: 4.19.0
Contact: linux-pci@vger.kernel.org, rajatja@google.com
Description: Total number of ERR_NONFATAL messages reported to rootport.
PCIe AER ratelimits
-------------------
These attributes show up under all the devices that are AER capable.
They represent configurable ratelimits of logs per error type.
See Documentation/PCI/pcieaer-howto.rst for more info on ratelimits.
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).
What: /sys/bus/pci/devices/<dev>/aer/correctable_ratelimit_burst
Date: May 2025
KernelVersion: 6.16.0
Contact: linux-pci@vger.kernel.org
Description: Ratelimit burst for correctable error logs. Writing a value
changes the number of errors (burst) allowed per interval
before ratelimiting. Reading gets the current ratelimit
burst. Default is DEFAULT_RATELIMIT_BURST (10).
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).
What: /sys/bus/pci/devices/<dev>/aer/nonfatal_ratelimit_burst
Date: May 2025
KernelVersion: 6.16.0
Contact: linux-pci@vger.kernel.org
Description: Ratelimit burst for non-fatal uncorrectable error logs.
Writing a value changes the number of errors (burst)
allowed per interval before ratelimiting. Reading gets the
current ratelimit burst. Default is DEFAULT_RATELIMIT_BURST
(10).

View File

@@ -85,12 +85,27 @@ In the example, 'Requester ID' means the ID of the device that sent
the error message to the Root Port. Please refer to PCIe specs for other
fields.
AER Ratelimits
--------------
Since error messages can be generated for each transaction, we may see
large volumes of errors reported. To prevent spammy devices from flooding
the console/stalling execution, messages are throttled by device and error
type (correctable vs. non-fatal uncorrectable). Fatal errors, including
DPC errors, are not ratelimited.
AER uses the default ratelimit of DEFAULT_RATELIMIT_BURST (10 events) over
DEFAULT_RATELIMIT_INTERVAL (5 seconds).
Ratelimits are exposed in the form of sysfs attributes and configurable.
See Documentation/ABI/testing/sysfs-bus-pci-devices-aer.
AER Statistics / Counters
-------------------------
When PCIe AER errors are captured, the counters / statistics are also exposed
in the form of sysfs attributes which are documented at
Documentation/ABI/testing/sysfs-bus-pci-devices-aer_stats
Documentation/ABI/testing/sysfs-bus-pci-devices-aer.
Developer Guide
===============

View File

@@ -1805,6 +1805,7 @@ const struct attribute_group *pci_dev_attr_groups[] = {
&pcie_dev_attr_group,
#ifdef CONFIG_PCIEAER
&aer_stats_attr_group,
&aer_attr_group,
#endif
#ifdef CONFIG_PCIEASPM
&aspm_ctrl_attr_group,

View File

@@ -587,12 +587,15 @@ static inline bool pci_dev_test_and_set_removed(struct pci_dev *dev)
struct aer_err_info {
struct pci_dev *dev[AER_MAX_MULTI_ERR_DEVICES];
int ratelimit_print[AER_MAX_MULTI_ERR_DEVICES];
int error_dev_num;
const char *level; /* printk level */
unsigned int id:16;
unsigned int severity:2; /* 0:NONFATAL | 1:FATAL | 2:COR */
unsigned int __pad1:5;
unsigned int root_ratelimit_print:1; /* 0=skip, 1=print */
unsigned int __pad1:4;
unsigned int multi_error_valid:1;
unsigned int first_error:5;
@@ -604,15 +607,16 @@ struct aer_err_info {
struct pcie_tlp_log tlp; /* TLP Header */
};
int aer_get_device_error_info(struct pci_dev *dev, struct aer_err_info *info);
void aer_print_error(struct pci_dev *dev, struct aer_err_info *info);
int aer_get_device_error_info(struct aer_err_info *info, int i);
void aer_print_error(struct aer_err_info *info, int i);
int pcie_read_tlp_log(struct pci_dev *dev, int where, int where2,
unsigned int tlp_len, bool flit,
struct pcie_tlp_log *log);
unsigned int aer_tlp_log_len(struct pci_dev *dev, u32 aercc);
void pcie_print_tlp_log(const struct pci_dev *dev,
const struct pcie_tlp_log *log, const char *pfx);
const struct pcie_tlp_log *log, const char *level,
const char *pfx);
#endif /* CONFIG_PCIEAER */
#ifdef CONFIG_PCIEPORTBUS
@@ -961,6 +965,7 @@ void pci_no_aer(void);
void pci_aer_init(struct pci_dev *dev);
void pci_aer_exit(struct pci_dev *dev);
extern const struct attribute_group aer_stats_attr_group;
extern const struct attribute_group aer_attr_group;
void pci_aer_clear_fatal_status(struct pci_dev *dev);
int pci_aer_clear_status(struct pci_dev *dev);
int pci_aer_raw_clear_status(struct pci_dev *dev);

View File

@@ -28,6 +28,7 @@
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/kfifo.h>
#include <linux/ratelimit.h>
#include <linux/slab.h>
#include <acpi/apei.h>
#include <acpi/ghes.h>
@@ -54,8 +55,8 @@ struct aer_rpc {
DECLARE_KFIFO(aer_fifo, struct aer_err_source, AER_ERROR_SOURCES_MAX);
};
/* AER stats for the device */
struct aer_stats {
/* AER info for the device */
struct aer_info {
/*
* Fields for all AER capable devices. They indicate the errors
@@ -88,6 +89,10 @@ struct aer_stats {
u64 rootport_total_cor_errs;
u64 rootport_total_fatal_errs;
u64 rootport_total_nonfatal_errs;
/* Ratelimits for errors */
struct ratelimit_state correctable_ratelimit;
struct ratelimit_state nonfatal_ratelimit;
};
#define AER_LOG_TLP_MASKS (PCI_ERR_UNC_POISON_TLP| \
@@ -377,7 +382,12 @@ void pci_aer_init(struct pci_dev *dev)
if (!dev->aer_cap)
return;
dev->aer_stats = kzalloc(sizeof(struct aer_stats), GFP_KERNEL);
dev->aer_info = kzalloc(sizeof(*dev->aer_info), GFP_KERNEL);
ratelimit_state_init(&dev->aer_info->correctable_ratelimit,
DEFAULT_RATELIMIT_INTERVAL, DEFAULT_RATELIMIT_BURST);
ratelimit_state_init(&dev->aer_info->nonfatal_ratelimit,
DEFAULT_RATELIMIT_INTERVAL, DEFAULT_RATELIMIT_BURST);
/*
* We save/restore PCI_ERR_UNCOR_MASK, PCI_ERR_UNCOR_SEVER,
@@ -398,8 +408,8 @@ void pci_aer_init(struct pci_dev *dev)
void pci_aer_exit(struct pci_dev *dev)
{
kfree(dev->aer_stats);
dev->aer_stats = NULL;
kfree(dev->aer_info);
dev->aer_info = NULL;
}
#define AER_AGENT_RECEIVER 0
@@ -537,10 +547,10 @@ static const char *aer_agent_string[] = {
{ \
unsigned int i; \
struct pci_dev *pdev = to_pci_dev(dev); \
u64 *stats = pdev->aer_stats->stats_array; \
u64 *stats = pdev->aer_info->stats_array; \
size_t len = 0; \
\
for (i = 0; i < ARRAY_SIZE(pdev->aer_stats->stats_array); i++) {\
for (i = 0; i < ARRAY_SIZE(pdev->aer_info->stats_array); i++) { \
if (strings_array[i]) \
len += sysfs_emit_at(buf, len, "%s %llu\n", \
strings_array[i], \
@@ -551,7 +561,7 @@ static const char *aer_agent_string[] = {
i, stats[i]); \
} \
len += sysfs_emit_at(buf, len, "TOTAL_%s %llu\n", total_string, \
pdev->aer_stats->total_field); \
pdev->aer_info->total_field); \
return len; \
} \
static DEVICE_ATTR_RO(name)
@@ -572,7 +582,7 @@ aer_stats_dev_attr(aer_dev_nonfatal, dev_nonfatal_errs,
char *buf) \
{ \
struct pci_dev *pdev = to_pci_dev(dev); \
return sysfs_emit(buf, "%llu\n", pdev->aer_stats->field); \
return sysfs_emit(buf, "%llu\n", pdev->aer_info->field); \
} \
static DEVICE_ATTR_RO(name)
@@ -599,7 +609,7 @@ static umode_t aer_stats_attrs_are_visible(struct kobject *kobj,
struct device *dev = kobj_to_dev(kobj);
struct pci_dev *pdev = to_pci_dev(dev);
if (!pdev->aer_stats)
if (!pdev->aer_info)
return 0;
if ((a == &dev_attr_aer_rootport_total_err_cor.attr ||
@@ -617,31 +627,136 @@ const struct attribute_group aer_stats_attr_group = {
.is_visible = aer_stats_attrs_are_visible,
};
/*
* Ratelimit interval
* <=0: disabled with ratelimit.interval = 0
* >0: enabled with ratelimit.interval in ms
*/
#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); \
\
return sysfs_emit(buf, "%d\n", \
pdev->aer_info->ratelimit.interval); \
} \
\
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; \
\
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)
aer_ratelimit_attrs(correctable)
aer_ratelimit_attrs(nonfatal)
static struct attribute *aer_attrs[] = {
&dev_attr_correctable_ratelimit_interval_ms.attr,
&dev_attr_correctable_ratelimit_burst.attr,
&dev_attr_nonfatal_ratelimit_interval_ms.attr,
&dev_attr_nonfatal_ratelimit_burst.attr,
NULL
};
static umode_t aer_attrs_are_visible(struct kobject *kobj,
struct attribute *a, int n)
{
struct device *dev = kobj_to_dev(kobj);
struct pci_dev *pdev = to_pci_dev(dev);
if (!pdev->aer_info)
return 0;
return a->mode;
}
const struct attribute_group aer_attr_group = {
.name = "aer",
.attrs = aer_attrs,
.is_visible = aer_attrs_are_visible,
};
static void pci_dev_aer_stats_incr(struct pci_dev *pdev,
struct aer_err_info *info)
{
unsigned long status = info->status & ~info->mask;
int i, max = -1;
u64 *counter = NULL;
struct aer_stats *aer_stats = pdev->aer_stats;
struct aer_info *aer_info = pdev->aer_info;
if (!aer_stats)
if (!aer_info)
return;
switch (info->severity) {
case AER_CORRECTABLE:
aer_stats->dev_total_cor_errs++;
counter = &aer_stats->dev_cor_errs[0];
aer_info->dev_total_cor_errs++;
counter = &aer_info->dev_cor_errs[0];
max = AER_MAX_TYPEOF_COR_ERRS;
break;
case AER_NONFATAL:
aer_stats->dev_total_nonfatal_errs++;
counter = &aer_stats->dev_nonfatal_errs[0];
aer_info->dev_total_nonfatal_errs++;
counter = &aer_info->dev_nonfatal_errs[0];
max = AER_MAX_TYPEOF_UNCOR_ERRS;
break;
case AER_FATAL:
aer_stats->dev_total_fatal_errs++;
counter = &aer_stats->dev_fatal_errs[0];
aer_info->dev_total_fatal_errs++;
counter = &aer_info->dev_fatal_errs[0];
max = AER_MAX_TYPEOF_UNCOR_ERRS;
break;
}
@@ -653,37 +768,46 @@ static void pci_dev_aer_stats_incr(struct pci_dev *pdev,
static void pci_rootport_aer_stats_incr(struct pci_dev *pdev,
struct aer_err_source *e_src)
{
struct aer_stats *aer_stats = pdev->aer_stats;
struct aer_info *aer_info = pdev->aer_info;
if (!aer_stats)
if (!aer_info)
return;
if (e_src->status & PCI_ERR_ROOT_COR_RCV)
aer_stats->rootport_total_cor_errs++;
aer_info->rootport_total_cor_errs++;
if (e_src->status & PCI_ERR_ROOT_UNCOR_RCV) {
if (e_src->status & PCI_ERR_ROOT_FATAL_RCV)
aer_stats->rootport_total_fatal_errs++;
aer_info->rootport_total_fatal_errs++;
else
aer_stats->rootport_total_nonfatal_errs++;
aer_info->rootport_total_nonfatal_errs++;
}
}
static void __aer_print_error(struct pci_dev *dev,
struct aer_err_info *info)
static int aer_ratelimit(struct pci_dev *dev, unsigned int severity)
{
switch (severity) {
case AER_NONFATAL:
return __ratelimit(&dev->aer_info->nonfatal_ratelimit);
case AER_CORRECTABLE:
return __ratelimit(&dev->aer_info->correctable_ratelimit);
default:
return 1; /* Don't ratelimit fatal errors */
}
}
static void __aer_print_error(struct pci_dev *dev, struct aer_err_info *info)
{
const char **strings;
unsigned long status = info->status & ~info->mask;
const char *level, *errmsg;
const char *level = info->level;
const char *errmsg;
int i;
if (info->severity == AER_CORRECTABLE) {
if (info->severity == AER_CORRECTABLE)
strings = aer_correctable_error_string;
level = KERN_WARNING;
} else {
else
strings = aer_uncorrectable_error_string;
level = KERN_ERR;
}
for_each_set_bit(i, &status, 32) {
errmsg = strings[i];
@@ -693,14 +817,39 @@ static void __aer_print_error(struct pci_dev *dev,
aer_printk(level, dev, " [%2d] %-22s%s\n", i, errmsg,
info->first_error == i ? " (First)" : "");
}
pci_dev_aer_stats_incr(dev, info);
}
void aer_print_error(struct pci_dev *dev, struct aer_err_info *info)
static void aer_print_source(struct pci_dev *dev, struct aer_err_info *info,
bool found)
{
int layer, agent;
int id = pci_dev_id(dev);
const char *level;
u16 source = info->id;
pci_info(dev, "%s%s error message received from %04x:%02x:%02x.%d%s\n",
info->multi_error_valid ? "Multiple " : "",
aer_error_severity_string[info->severity],
pci_domain_nr(dev->bus), PCI_BUS_NUM(source),
PCI_SLOT(source), PCI_FUNC(source),
found ? "" : " (no details found");
}
void aer_print_error(struct aer_err_info *info, int i)
{
struct pci_dev *dev;
int layer, agent, id;
const char *level = info->level;
if (WARN_ON_ONCE(i >= AER_MAX_MULTI_ERR_DEVICES))
return;
dev = info->dev[i];
id = pci_dev_id(dev);
pci_dev_aer_stats_incr(dev, info);
trace_aer_event(pci_name(dev), (info->status & ~info->mask),
info->severity, info->tlp_header_valid, &info->tlp);
if (!info->ratelimit_print[i])
return;
if (!info->status) {
pci_err(dev, "PCIe Bus Error: severity=%s, type=Inaccessible, (Unregistered Agent ID)\n",
@@ -711,8 +860,6 @@ void aer_print_error(struct pci_dev *dev, struct aer_err_info *info)
layer = AER_GET_LAYER_ERROR(info->severity, info->status);
agent = AER_GET_AGENT(info->severity, info->status);
level = (info->severity == AER_CORRECTABLE) ? KERN_WARNING : KERN_ERR;
aer_printk(level, dev, "PCIe Bus Error: severity=%s, type=%s, (%s)\n",
aer_error_severity_string[info->severity],
aer_error_layer[layer], aer_agent_string[agent]);
@@ -723,26 +870,11 @@ void aer_print_error(struct pci_dev *dev, struct aer_err_info *info)
__aer_print_error(dev, info);
if (info->tlp_header_valid)
pcie_print_tlp_log(dev, &info->tlp, dev_fmt(" "));
pcie_print_tlp_log(dev, &info->tlp, level, dev_fmt(" "));
out:
if (info->id && info->error_dev_num > 1 && info->id == id)
pci_err(dev, " Error of this Agent is reported first\n");
trace_aer_event(dev_name(&dev->dev), (info->status & ~info->mask),
info->severity, info->tlp_header_valid, &info->tlp);
}
static void aer_print_port_info(struct pci_dev *dev, struct aer_err_info *info)
{
u8 bus = info->id >> 8;
u8 devfn = info->id & 0xff;
pci_info(dev, "%s%s error message received from %04x:%02x:%02x.%d\n",
info->multi_error_valid ? "Multiple " : "",
aer_error_severity_string[info->severity],
pci_domain_nr(dev->bus), bus, PCI_SLOT(devfn),
PCI_FUNC(devfn));
}
#ifdef CONFIG_ACPI_APEI_PCIEAER
@@ -765,40 +897,48 @@ void pci_print_aer(struct pci_dev *dev, int aer_severity,
{
int layer, agent, tlp_header_valid = 0;
u32 status, mask;
struct aer_err_info info;
struct aer_err_info info = {
.severity = aer_severity,
.first_error = PCI_ERR_CAP_FEP(aer->cap_control),
};
if (aer_severity == AER_CORRECTABLE) {
status = aer->cor_status;
mask = aer->cor_mask;
info.level = KERN_WARNING;
} else {
status = aer->uncor_status;
mask = aer->uncor_mask;
info.level = KERN_ERR;
tlp_header_valid = status & AER_LOG_TLP_MASKS;
}
info.status = status;
info.mask = mask;
pci_dev_aer_stats_incr(dev, &info);
trace_aer_event(pci_name(dev), (status & ~mask),
aer_severity, tlp_header_valid, &aer->header_log);
if (!aer_ratelimit(dev, info.severity))
return;
layer = AER_GET_LAYER_ERROR(aer_severity, status);
agent = AER_GET_AGENT(aer_severity, status);
memset(&info, 0, sizeof(info));
info.severity = aer_severity;
info.status = status;
info.mask = mask;
info.first_error = PCI_ERR_CAP_FEP(aer->cap_control);
pci_err(dev, "aer_status: 0x%08x, aer_mask: 0x%08x\n", status, mask);
aer_printk(info.level, dev, "aer_status: 0x%08x, aer_mask: 0x%08x\n",
status, mask);
__aer_print_error(dev, &info);
pci_err(dev, "aer_layer=%s, aer_agent=%s\n",
aer_error_layer[layer], aer_agent_string[agent]);
aer_printk(info.level, dev, "aer_layer=%s, aer_agent=%s\n",
aer_error_layer[layer], aer_agent_string[agent]);
if (aer_severity != AER_CORRECTABLE)
pci_err(dev, "aer_uncor_severity: 0x%08x\n",
aer->uncor_severity);
aer_printk(info.level, dev, "aer_uncor_severity: 0x%08x\n",
aer->uncor_severity);
if (tlp_header_valid)
pcie_print_tlp_log(dev, &aer->header_log, dev_fmt(" "));
trace_aer_event(dev_name(&dev->dev), (status & ~mask),
aer_severity, tlp_header_valid, &aer->header_log);
pcie_print_tlp_log(dev, &aer->header_log, info.level,
dev_fmt(" "));
}
EXPORT_SYMBOL_NS_GPL(pci_print_aer, "CXL");
@@ -809,12 +949,27 @@ EXPORT_SYMBOL_NS_GPL(pci_print_aer, "CXL");
*/
static int add_error_device(struct aer_err_info *e_info, struct pci_dev *dev)
{
if (e_info->error_dev_num < AER_MAX_MULTI_ERR_DEVICES) {
e_info->dev[e_info->error_dev_num] = pci_dev_get(dev);
e_info->error_dev_num++;
return 0;
int i = e_info->error_dev_num;
if (i >= AER_MAX_MULTI_ERR_DEVICES)
return -ENOSPC;
e_info->dev[i] = pci_dev_get(dev);
e_info->error_dev_num++;
/*
* Ratelimit AER log messages. "dev" is either the source
* identified by the root's Error Source ID or it has an unmasked
* error logged in its own AER Capability. Messages are emitted
* when "ratelimit_print[i]" is non-zero. If we will print detail
* for a downstream device, make sure we print the Error Source ID
* from the root as well.
*/
if (aer_ratelimit(dev, e_info->severity)) {
e_info->ratelimit_print[i] = 1;
e_info->root_ratelimit_print = 1;
}
return -ENOSPC;
return 0;
}
/**
@@ -908,7 +1063,7 @@ static int find_device_iter(struct pci_dev *dev, void *data)
* e_info->error_dev_num and e_info->dev[], based on the given information.
*/
static bool find_source_device(struct pci_dev *parent,
struct aer_err_info *e_info)
struct aer_err_info *e_info)
{
struct pci_dev *dev = parent;
int result;
@@ -926,15 +1081,8 @@ static bool find_source_device(struct pci_dev *parent,
else
pci_walk_bus(parent->subordinate, find_device_iter, e_info);
if (!e_info->error_dev_num) {
u8 bus = e_info->id >> 8;
u8 devfn = e_info->id & 0xff;
pci_info(parent, "found no error details for %04x:%02x:%02x.%d\n",
pci_domain_nr(parent->bus), bus, PCI_SLOT(devfn),
PCI_FUNC(devfn));
if (!e_info->error_dev_num)
return false;
}
return true;
}
@@ -1141,9 +1289,10 @@ static void aer_recover_work_func(struct work_struct *work)
pdev = pci_get_domain_bus_and_slot(entry.domain, entry.bus,
entry.devfn);
if (!pdev) {
pr_err("no pci_dev for %04x:%02x:%02x.%x\n",
entry.domain, entry.bus,
PCI_SLOT(entry.devfn), PCI_FUNC(entry.devfn));
pr_err_ratelimited("%04x:%02x:%02x.%x: no pci_dev found\n",
entry.domain, entry.bus,
PCI_SLOT(entry.devfn),
PCI_FUNC(entry.devfn));
continue;
}
pci_print_aer(pdev, entry.severity, entry.regs);
@@ -1199,19 +1348,26 @@ EXPORT_SYMBOL_GPL(aer_recover_queue);
/**
* aer_get_device_error_info - read error status from dev and store it to info
* @dev: pointer to the device expected to have an error record
* @info: pointer to structure to store the error record
* @i: index into info->dev[]
*
* Return: 1 on success, 0 on error.
*
* Note that @info is reused among all error devices. Clear fields properly.
*/
int aer_get_device_error_info(struct pci_dev *dev, struct aer_err_info *info)
int aer_get_device_error_info(struct aer_err_info *info, int i)
{
int type = pci_pcie_type(dev);
int aer = dev->aer_cap;
struct pci_dev *dev;
int type, aer;
u32 aercc;
if (i >= AER_MAX_MULTI_ERR_DEVICES)
return 0;
dev = info->dev[i];
aer = dev->aer_cap;
type = pci_pcie_type(dev);
/* Must reset in this function */
info->status = 0;
info->tlp_header_valid = 0;
@@ -1263,63 +1419,87 @@ static inline void aer_process_err_devices(struct aer_err_info *e_info)
/* Report all before handling them, to not lose records by reset etc. */
for (i = 0; i < e_info->error_dev_num && e_info->dev[i]; i++) {
if (aer_get_device_error_info(e_info->dev[i], e_info))
aer_print_error(e_info->dev[i], e_info);
if (aer_get_device_error_info(e_info, i))
aer_print_error(e_info, i);
}
for (i = 0; i < e_info->error_dev_num && e_info->dev[i]; i++) {
if (aer_get_device_error_info(e_info->dev[i], e_info))
if (aer_get_device_error_info(e_info, i))
handle_error_source(e_info->dev[i], e_info);
}
}
/**
* aer_isr_one_error - consume an error detected by Root Port
* @rpc: pointer to the Root Port which holds an error
* aer_isr_one_error_type - consume a Correctable or Uncorrectable Error
* detected by Root Port or RCEC
* @root: pointer to Root Port or RCEC that signaled AER interrupt
* @info: pointer to AER error info
*/
static void aer_isr_one_error_type(struct pci_dev *root,
struct aer_err_info *info)
{
bool found;
found = find_source_device(root, info);
/*
* If we're going to log error messages, we've already set
* "info->root_ratelimit_print" and "info->ratelimit_print[i]" to
* non-zero (which enables printing) because this is either an
* ERR_FATAL or we found a device with an error logged in its AER
* Capability.
*
* If we didn't find the Error Source device, at least log the
* Requester ID from the ERR_* Message received by the Root Port or
* RCEC, ratelimited by the RP or RCEC.
*/
if (info->root_ratelimit_print ||
(!found && aer_ratelimit(root, info->severity)))
aer_print_source(root, info, found);
if (found)
aer_process_err_devices(info);
}
/**
* aer_isr_one_error - consume error(s) signaled by an AER interrupt from
* Root Port or RCEC
* @root: pointer to Root Port or RCEC that signaled AER interrupt
* @e_src: pointer to an error source
*/
static void aer_isr_one_error(struct aer_rpc *rpc,
struct aer_err_source *e_src)
static void aer_isr_one_error(struct pci_dev *root,
struct aer_err_source *e_src)
{
struct pci_dev *pdev = rpc->rpd;
struct aer_err_info e_info;
u32 status = e_src->status;
pci_rootport_aer_stats_incr(pdev, e_src);
pci_rootport_aer_stats_incr(root, e_src);
/*
* There is a possibility that both correctable error and
* uncorrectable error being logged. Report correctable error first.
*/
if (e_src->status & PCI_ERR_ROOT_COR_RCV) {
e_info.id = ERR_COR_ID(e_src->id);
e_info.severity = AER_CORRECTABLE;
if (status & PCI_ERR_ROOT_COR_RCV) {
int multi = status & PCI_ERR_ROOT_MULTI_COR_RCV;
struct aer_err_info e_info = {
.id = ERR_COR_ID(e_src->id),
.severity = AER_CORRECTABLE,
.level = KERN_WARNING,
.multi_error_valid = multi ? 1 : 0,
};
if (e_src->status & PCI_ERR_ROOT_MULTI_COR_RCV)
e_info.multi_error_valid = 1;
else
e_info.multi_error_valid = 0;
aer_print_port_info(pdev, &e_info);
if (find_source_device(pdev, &e_info))
aer_process_err_devices(&e_info);
aer_isr_one_error_type(root, &e_info);
}
if (e_src->status & PCI_ERR_ROOT_UNCOR_RCV) {
e_info.id = ERR_UNCOR_ID(e_src->id);
if (status & PCI_ERR_ROOT_UNCOR_RCV) {
int fatal = status & PCI_ERR_ROOT_FATAL_RCV;
int multi = status & PCI_ERR_ROOT_MULTI_UNCOR_RCV;
struct aer_err_info e_info = {
.id = ERR_UNCOR_ID(e_src->id),
.severity = fatal ? AER_FATAL : AER_NONFATAL,
.level = KERN_ERR,
.multi_error_valid = multi ? 1 : 0,
};
if (e_src->status & PCI_ERR_ROOT_FATAL_RCV)
e_info.severity = AER_FATAL;
else
e_info.severity = AER_NONFATAL;
if (e_src->status & PCI_ERR_ROOT_MULTI_UNCOR_RCV)
e_info.multi_error_valid = 1;
else
e_info.multi_error_valid = 0;
aer_print_port_info(pdev, &e_info);
if (find_source_device(pdev, &e_info))
aer_process_err_devices(&e_info);
aer_isr_one_error_type(root, &e_info);
}
}
@@ -1340,7 +1520,7 @@ static irqreturn_t aer_isr(int irq, void *context)
return IRQ_NONE;
while (kfifo_get(&rpc->aer_fifo, &e_src))
aer_isr_one_error(rpc, &e_src);
aer_isr_one_error(rpc->rpd, &e_src);
return IRQ_HANDLED;
}

View File

@@ -222,7 +222,7 @@ static void dpc_process_rp_pio_error(struct pci_dev *pdev)
dpc_tlp_log_len(pdev),
pdev->subordinate->flit_mode,
&tlp_log);
pcie_print_tlp_log(pdev, &tlp_log, dev_fmt(""));
pcie_print_tlp_log(pdev, &tlp_log, KERN_ERR, dev_fmt(""));
if (pdev->dpc_rp_log_size < PCIE_STD_NUM_TLP_HEADERLOG + 1)
goto clear_status;
@@ -252,46 +252,59 @@ static int dpc_get_aer_uncorrect_severity(struct pci_dev *dev,
else
info->severity = AER_NONFATAL;
info->level = KERN_ERR;
info->dev[0] = dev;
info->error_dev_num = 1;
return 1;
}
void dpc_process_error(struct pci_dev *pdev)
{
u16 cap = pdev->dpc_cap, status, source, reason, ext_reason;
struct aer_err_info info;
struct aer_err_info info = {};
pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status);
pci_read_config_word(pdev, cap + PCI_EXP_DPC_SOURCE_ID, &source);
pci_info(pdev, "containment event, status:%#06x source:%#06x\n",
status, source);
reason = status & PCI_EXP_DPC_STATUS_TRIGGER_RSN;
ext_reason = status & PCI_EXP_DPC_STATUS_TRIGGER_RSN_EXT;
pci_warn(pdev, "%s detected\n",
(reason == PCI_EXP_DPC_STATUS_TRIGGER_RSN_UNCOR) ?
"unmasked uncorrectable error" :
(reason == PCI_EXP_DPC_STATUS_TRIGGER_RSN_NFE) ?
"ERR_NONFATAL" :
(reason == PCI_EXP_DPC_STATUS_TRIGGER_RSN_FE) ?
"ERR_FATAL" :
(ext_reason == PCI_EXP_DPC_STATUS_TRIGGER_RSN_RP_PIO) ?
"RP PIO error" :
(ext_reason == PCI_EXP_DPC_STATUS_TRIGGER_RSN_SW_TRIGGER) ?
"software trigger" :
"reserved error");
/* show RP PIO error detail information */
if (pdev->dpc_rp_extensions &&
reason == PCI_EXP_DPC_STATUS_TRIGGER_RSN_IN_EXT &&
ext_reason == PCI_EXP_DPC_STATUS_TRIGGER_RSN_RP_PIO)
dpc_process_rp_pio_error(pdev);
else if (reason == PCI_EXP_DPC_STATUS_TRIGGER_RSN_UNCOR &&
dpc_get_aer_uncorrect_severity(pdev, &info) &&
aer_get_device_error_info(pdev, &info)) {
aer_print_error(pdev, &info);
pci_aer_clear_nonfatal_status(pdev);
pci_aer_clear_fatal_status(pdev);
switch (reason) {
case PCI_EXP_DPC_STATUS_TRIGGER_RSN_UNCOR:
pci_warn(pdev, "containment event, status:%#06x: unmasked uncorrectable error detected\n",
status);
if (dpc_get_aer_uncorrect_severity(pdev, &info) &&
aer_get_device_error_info(&info, 0)) {
aer_print_error(&info, 0);
pci_aer_clear_nonfatal_status(pdev);
pci_aer_clear_fatal_status(pdev);
}
break;
case PCI_EXP_DPC_STATUS_TRIGGER_RSN_NFE:
case PCI_EXP_DPC_STATUS_TRIGGER_RSN_FE:
pci_read_config_word(pdev, cap + PCI_EXP_DPC_SOURCE_ID,
&source);
pci_warn(pdev, "containment event, status:%#06x, %s received from %04x:%02x:%02x.%d\n",
status,
(reason == PCI_EXP_DPC_STATUS_TRIGGER_RSN_FE) ?
"ERR_FATAL" : "ERR_NONFATAL",
pci_domain_nr(pdev->bus), PCI_BUS_NUM(source),
PCI_SLOT(source), PCI_FUNC(source));
break;
case PCI_EXP_DPC_STATUS_TRIGGER_RSN_IN_EXT:
ext_reason = status & PCI_EXP_DPC_STATUS_TRIGGER_RSN_EXT;
pci_warn(pdev, "containment event, status:%#06x: %s detected\n",
status,
(ext_reason == PCI_EXP_DPC_STATUS_TRIGGER_RSN_RP_PIO) ?
"RP PIO error" :
(ext_reason == PCI_EXP_DPC_STATUS_TRIGGER_RSN_SW_TRIGGER) ?
"software trigger" :
"reserved error");
/* show RP PIO error detail information */
if (ext_reason == PCI_EXP_DPC_STATUS_TRIGGER_RSN_RP_PIO &&
pdev->dpc_rp_extensions)
dpc_process_rp_pio_error(pdev);
break;
}
}

View File

@@ -271,7 +271,6 @@ pci_ers_result_t pcie_do_recovery(struct pci_dev *dev,
pci_uevent_ers(bridge, PCI_ERS_RESULT_DISCONNECT);
/* TODO: Should kernel panic here? */
pci_info(bridge, "device recovery failed\n");
return status;

View File

@@ -98,12 +98,14 @@ int pcie_read_tlp_log(struct pci_dev *dev, int where, int where2,
* pcie_print_tlp_log - Print TLP Header / Prefix Log contents
* @dev: PCIe device
* @log: TLP Log structure
* @level: Printk log level
* @pfx: String prefix
*
* Prints TLP Header and Prefix Log information held by @log.
*/
void pcie_print_tlp_log(const struct pci_dev *dev,
const struct pcie_tlp_log *log, const char *pfx)
const struct pcie_tlp_log *log, const char *level,
const char *pfx)
{
/* EE_PREFIX_STR fits the extended DW space needed for the Flit mode */
char buf[11 * PCIE_STD_MAX_TLP_HEADERLOG + 1];
@@ -130,6 +132,6 @@ void pcie_print_tlp_log(const struct pci_dev *dev,
}
}
pci_err(dev, "%sTLP Header%s: %s\n", pfx,
dev_printk(level, &dev->dev, "%sTLP Header%s: %s\n", pfx,
log->flit ? " (Flit)" : "", buf);
}

View File

@@ -346,7 +346,7 @@ struct pci_dev {
u8 hdr_type; /* PCI header type (`multi' flag masked out) */
#ifdef CONFIG_PCIEAER
u16 aer_cap; /* AER capability offset */
struct aer_stats *aer_stats; /* AER stats for this device */
struct aer_info *aer_info; /* AER info for this device */
#endif
#ifdef CONFIG_PCIEPORTBUS
struct rcec_ea *rcec_ea; /* RCEC cached endpoint association */