mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-03 14:42:18 -04:00
Merge branch 'pci/aer'
- Implement local aer_printk() since AER is the only place that prints a message with level depending on the error severity (Ilpo Järvinen) * pci/aer: PCI/ERR: Handle TLP Log in Flit mode PCI: Track Flit Mode Status & print it with link status PCI/AER: Descope pci_printk() to aer_printk()
This commit is contained in:
@@ -292,7 +292,7 @@ int pciehp_check_link_status(struct controller *ctrl)
|
||||
{
|
||||
struct pci_dev *pdev = ctrl_dev(ctrl);
|
||||
bool found;
|
||||
u16 lnk_status;
|
||||
u16 lnk_status, linksta2;
|
||||
|
||||
if (!pcie_wait_for_link(pdev, true)) {
|
||||
ctrl_info(ctrl, "Slot(%s): No link\n", slot_name(ctrl));
|
||||
@@ -319,7 +319,8 @@ int pciehp_check_link_status(struct controller *ctrl)
|
||||
return -1;
|
||||
}
|
||||
|
||||
__pcie_update_link_speed(ctrl->pcie->port->subordinate, lnk_status);
|
||||
pcie_capability_read_word(pdev, PCI_EXP_LNKSTA2, &linksta2);
|
||||
__pcie_update_link_speed(ctrl->pcie->port->subordinate, lnk_status, linksta2);
|
||||
|
||||
if (!found) {
|
||||
ctrl_info(ctrl, "Slot(%s): No device found\n",
|
||||
|
||||
@@ -6198,21 +6198,25 @@ void __pcie_print_link_status(struct pci_dev *dev, bool verbose)
|
||||
enum pci_bus_speed speed, speed_cap;
|
||||
struct pci_dev *limiting_dev = NULL;
|
||||
u32 bw_avail, bw_cap;
|
||||
char *flit_mode = "";
|
||||
|
||||
bw_cap = pcie_bandwidth_capable(dev, &speed_cap, &width_cap);
|
||||
bw_avail = pcie_bandwidth_available(dev, &limiting_dev, &speed, &width);
|
||||
|
||||
if (dev->bus && dev->bus->flit_mode)
|
||||
flit_mode = ", in Flit mode";
|
||||
|
||||
if (bw_avail >= bw_cap && verbose)
|
||||
pci_info(dev, "%u.%03u Gb/s available PCIe bandwidth (%s x%d link)\n",
|
||||
pci_info(dev, "%u.%03u Gb/s available PCIe bandwidth (%s x%d link)%s\n",
|
||||
bw_cap / 1000, bw_cap % 1000,
|
||||
pci_speed_string(speed_cap), width_cap);
|
||||
pci_speed_string(speed_cap), width_cap, flit_mode);
|
||||
else if (bw_avail < bw_cap)
|
||||
pci_info(dev, "%u.%03u Gb/s available PCIe bandwidth, limited by %s x%d link at %s (capable of %u.%03u Gb/s with %s x%d link)\n",
|
||||
pci_info(dev, "%u.%03u Gb/s available PCIe bandwidth, limited by %s x%d link at %s (capable of %u.%03u Gb/s with %s x%d link)%s\n",
|
||||
bw_avail / 1000, bw_avail % 1000,
|
||||
pci_speed_string(speed), width,
|
||||
limiting_dev ? pci_name(limiting_dev) : "<unknown>",
|
||||
bw_cap / 1000, bw_cap % 1000,
|
||||
pci_speed_string(speed_cap), width_cap);
|
||||
pci_speed_string(speed_cap), width_cap, flit_mode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -406,9 +406,10 @@ const char *pci_speed_string(enum pci_bus_speed speed);
|
||||
void __pcie_print_link_status(struct pci_dev *dev, bool verbose);
|
||||
void pcie_report_downtraining(struct pci_dev *dev);
|
||||
|
||||
static inline void __pcie_update_link_speed(struct pci_bus *bus, u16 linksta)
|
||||
static inline void __pcie_update_link_speed(struct pci_bus *bus, u16 linksta, u16 linksta2)
|
||||
{
|
||||
bus->cur_bus_speed = pcie_link_speed[linksta & PCI_EXP_LNKSTA_CLS];
|
||||
bus->flit_mode = (linksta2 & PCI_EXP_LNKSTA2_FLIT) ? 1 : 0;
|
||||
}
|
||||
void pcie_update_link_speed(struct pci_bus *bus);
|
||||
|
||||
@@ -553,7 +554,8 @@ 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 pcie_read_tlp_log(struct pci_dev *dev, int where, int where2,
|
||||
unsigned int tlp_len, struct pcie_tlp_log *log);
|
||||
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);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/cper.h>
|
||||
#include <linux/dev_printk.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci-acpi.h>
|
||||
#include <linux/sched.h>
|
||||
@@ -35,6 +36,9 @@
|
||||
#include "../pci.h"
|
||||
#include "portdrv.h"
|
||||
|
||||
#define aer_printk(level, pdev, fmt, arg...) \
|
||||
dev_printk(level, &(pdev)->dev, fmt, ##arg)
|
||||
|
||||
#define AER_ERROR_SOURCES_MAX 128
|
||||
|
||||
#define AER_MAX_TYPEOF_COR_ERRS 16 /* as per PCI_ERR_COR_STATUS */
|
||||
@@ -686,7 +690,7 @@ static void __aer_print_error(struct pci_dev *dev,
|
||||
if (!errmsg)
|
||||
errmsg = "Unknown Error Bit";
|
||||
|
||||
pci_printk(level, dev, " [%2d] %-22s%s\n", i, errmsg,
|
||||
aer_printk(level, dev, " [%2d] %-22s%s\n", i, errmsg,
|
||||
info->first_error == i ? " (First)" : "");
|
||||
}
|
||||
pci_dev_aer_stats_incr(dev, info);
|
||||
@@ -709,11 +713,11 @@ void aer_print_error(struct pci_dev *dev, struct aer_err_info *info)
|
||||
|
||||
level = (info->severity == AER_CORRECTABLE) ? KERN_WARNING : KERN_ERR;
|
||||
|
||||
pci_printk(level, dev, "PCIe Bus Error: severity=%s, type=%s, (%s)\n",
|
||||
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]);
|
||||
|
||||
pci_printk(level, dev, " device [%04x:%04x] error status/mask=%08x/%08x\n",
|
||||
aer_printk(level, dev, " device [%04x:%04x] error status/mask=%08x/%08x\n",
|
||||
dev->vendor, dev->device, info->status, info->mask);
|
||||
|
||||
__aer_print_error(dev, info);
|
||||
@@ -1245,6 +1249,7 @@ int aer_get_device_error_info(struct pci_dev *dev, struct aer_err_info *info)
|
||||
pcie_read_tlp_log(dev, aer + PCI_ERR_HEADER_LOG,
|
||||
aer + PCI_ERR_PREFIX_LOG,
|
||||
aer_tlp_log_len(dev, aercc),
|
||||
aercc & PCI_ERR_CAP_TLP_LOG_FLIT,
|
||||
&info->tlp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,7 +219,9 @@ static void dpc_process_rp_pio_error(struct pci_dev *pdev)
|
||||
goto clear_status;
|
||||
pcie_read_tlp_log(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG,
|
||||
cap + PCI_EXP_DPC_RP_PIO_TLPPREFIX_LOG,
|
||||
dpc_tlp_log_len(pdev), &tlp_log);
|
||||
dpc_tlp_log_len(pdev),
|
||||
pdev->subordinate->flit_mode,
|
||||
&tlp_log);
|
||||
pcie_print_tlp_log(pdev, &tlp_log, dev_fmt(""));
|
||||
|
||||
if (pdev->dpc_rp_log_size < PCIE_STD_NUM_TLP_HEADERLOG + 1)
|
||||
@@ -398,11 +400,21 @@ void pci_dpc_init(struct pci_dev *pdev)
|
||||
|
||||
/* Quirks may set dpc_rp_log_size if device or firmware is buggy */
|
||||
if (!pdev->dpc_rp_log_size) {
|
||||
u16 flags;
|
||||
int ret;
|
||||
|
||||
ret = pcie_capability_read_word(pdev, PCI_EXP_FLAGS, &flags);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
pdev->dpc_rp_log_size =
|
||||
FIELD_GET(PCI_EXP_DPC_RP_PIO_LOG_SIZE, cap);
|
||||
if (FIELD_GET(PCI_EXP_FLAGS_FLIT, flags))
|
||||
pdev->dpc_rp_log_size += FIELD_GET(PCI_EXP_DPC_RP_PIO_LOG_SIZE4,
|
||||
cap) << 4;
|
||||
|
||||
if (pdev->dpc_rp_log_size < PCIE_STD_NUM_TLP_HEADERLOG ||
|
||||
pdev->dpc_rp_log_size > PCIE_STD_NUM_TLP_HEADERLOG + 1 +
|
||||
PCIE_STD_MAX_TLP_PREFIXLOG) {
|
||||
pdev->dpc_rp_log_size > PCIE_STD_MAX_TLP_HEADERLOG + 1) {
|
||||
pci_err(pdev, "RP PIO log size %u is invalid\n",
|
||||
pdev->dpc_rp_log_size);
|
||||
pdev->dpc_rp_log_size = 0;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <linux/aer.h>
|
||||
#include <linux/array_size.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
@@ -21,6 +22,9 @@
|
||||
*/
|
||||
unsigned int aer_tlp_log_len(struct pci_dev *dev, u32 aercc)
|
||||
{
|
||||
if (aercc & PCI_ERR_CAP_TLP_LOG_FLIT)
|
||||
return FIELD_GET(PCI_ERR_CAP_TLP_LOG_SIZE, aercc);
|
||||
|
||||
return PCIE_STD_NUM_TLP_HEADERLOG +
|
||||
((aercc & PCI_ERR_CAP_PREFIX_LOG_PRESENT) ?
|
||||
dev->eetlp_prefix_max : 0);
|
||||
@@ -49,6 +53,7 @@ unsigned int dpc_tlp_log_len(struct pci_dev *dev)
|
||||
* @where: PCI Config offset of TLP Header Log
|
||||
* @where2: PCI Config offset of TLP Prefix Log
|
||||
* @tlp_len: TLP Log length (Header Log + TLP Prefix Log in DWORDs)
|
||||
* @flit: TLP Logged in Flit mode
|
||||
* @log: TLP Log structure to fill
|
||||
*
|
||||
* Fill @log from TLP Header Log registers, e.g., AER or DPC.
|
||||
@@ -56,28 +61,34 @@ unsigned int dpc_tlp_log_len(struct pci_dev *dev)
|
||||
* Return: 0 on success and filled TLP Log structure, <0 on error.
|
||||
*/
|
||||
int pcie_read_tlp_log(struct pci_dev *dev, int where, int where2,
|
||||
unsigned int tlp_len, struct pcie_tlp_log *log)
|
||||
unsigned int tlp_len, bool flit, struct pcie_tlp_log *log)
|
||||
{
|
||||
unsigned int i;
|
||||
int off, ret;
|
||||
u32 *to;
|
||||
|
||||
if (tlp_len > ARRAY_SIZE(log->dw))
|
||||
tlp_len = ARRAY_SIZE(log->dw);
|
||||
|
||||
memset(log, 0, sizeof(*log));
|
||||
|
||||
for (i = 0; i < tlp_len; i++) {
|
||||
if (i < PCIE_STD_NUM_TLP_HEADERLOG) {
|
||||
if (i < PCIE_STD_NUM_TLP_HEADERLOG)
|
||||
off = where + i * 4;
|
||||
to = &log->dw[i];
|
||||
} else {
|
||||
else
|
||||
off = where2 + (i - PCIE_STD_NUM_TLP_HEADERLOG) * 4;
|
||||
to = &log->prefix[i - PCIE_STD_NUM_TLP_HEADERLOG];
|
||||
}
|
||||
|
||||
ret = pci_read_config_dword(dev, off, to);
|
||||
ret = pci_read_config_dword(dev, off, &log->dw[i]);
|
||||
if (ret)
|
||||
return pcibios_err_to_errno(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Hard-code non-Flit mode to 4 DWORDs, for now. The exact length
|
||||
* can only be known if the TLP is parsed.
|
||||
*/
|
||||
log->header_len = flit ? tlp_len : 4;
|
||||
log->flit = flit;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -94,22 +105,31 @@ int pcie_read_tlp_log(struct pci_dev *dev, int where, int where2,
|
||||
void pcie_print_tlp_log(const struct pci_dev *dev,
|
||||
const struct pcie_tlp_log *log, const char *pfx)
|
||||
{
|
||||
char buf[11 * (PCIE_STD_NUM_TLP_HEADERLOG + ARRAY_SIZE(log->prefix)) +
|
||||
sizeof(EE_PREFIX_STR)];
|
||||
/* EE_PREFIX_STR fits the extended DW space needed for the Flit mode */
|
||||
char buf[11 * PCIE_STD_MAX_TLP_HEADERLOG + 1];
|
||||
unsigned int i;
|
||||
int len;
|
||||
|
||||
len = scnprintf(buf, sizeof(buf), "%#010x %#010x %#010x %#010x",
|
||||
log->dw[0], log->dw[1], log->dw[2], log->dw[3]);
|
||||
|
||||
if (log->prefix[0])
|
||||
len += scnprintf(buf + len, sizeof(buf) - len, EE_PREFIX_STR);
|
||||
for (i = 0; i < ARRAY_SIZE(log->prefix); i++) {
|
||||
if (!log->prefix[i])
|
||||
break;
|
||||
len += scnprintf(buf + len, sizeof(buf) - len,
|
||||
" %#010x", log->prefix[i]);
|
||||
if (log->flit) {
|
||||
for (i = PCIE_STD_NUM_TLP_HEADERLOG; i < log->header_len; i++) {
|
||||
len += scnprintf(buf + len, sizeof(buf) - len,
|
||||
" %#010x", log->dw[i]);
|
||||
}
|
||||
} else {
|
||||
if (log->prefix[0])
|
||||
len += scnprintf(buf + len, sizeof(buf) - len,
|
||||
EE_PREFIX_STR);
|
||||
for (i = 0; i < ARRAY_SIZE(log->prefix); i++) {
|
||||
if (!log->prefix[i])
|
||||
break;
|
||||
len += scnprintf(buf + len, sizeof(buf) - len,
|
||||
" %#010x", log->prefix[i]);
|
||||
}
|
||||
}
|
||||
|
||||
pci_err(dev, "%sTLP Header: %s\n", pfx, buf);
|
||||
pci_err(dev, "%sTLP Header%s: %s\n", pfx,
|
||||
log->flit ? " (Flit)" : "", buf);
|
||||
}
|
||||
|
||||
@@ -788,10 +788,11 @@ EXPORT_SYMBOL_GPL(pci_speed_string);
|
||||
void pcie_update_link_speed(struct pci_bus *bus)
|
||||
{
|
||||
struct pci_dev *bridge = bus->self;
|
||||
u16 linksta;
|
||||
u16 linksta, linksta2;
|
||||
|
||||
pcie_capability_read_word(bridge, PCI_EXP_LNKSTA, &linksta);
|
||||
__pcie_update_link_speed(bus, linksta);
|
||||
pcie_capability_read_word(bridge, PCI_EXP_LNKSTA2, &linksta2);
|
||||
__pcie_update_link_speed(bus, linksta, linksta2);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pcie_update_link_speed);
|
||||
|
||||
|
||||
@@ -22,12 +22,20 @@
|
||||
*/
|
||||
#define PCIE_STD_NUM_TLP_HEADERLOG 4
|
||||
#define PCIE_STD_MAX_TLP_PREFIXLOG 4
|
||||
#define PCIE_STD_MAX_TLP_HEADERLOG (PCIE_STD_NUM_TLP_HEADERLOG + 10)
|
||||
|
||||
struct pci_dev;
|
||||
|
||||
struct pcie_tlp_log {
|
||||
u32 dw[PCIE_STD_NUM_TLP_HEADERLOG];
|
||||
u32 prefix[PCIE_STD_MAX_TLP_PREFIXLOG];
|
||||
union {
|
||||
u32 dw[PCIE_STD_MAX_TLP_HEADERLOG];
|
||||
struct {
|
||||
u32 _do_not_use[PCIE_STD_NUM_TLP_HEADERLOG];
|
||||
u32 prefix[PCIE_STD_MAX_TLP_PREFIXLOG];
|
||||
};
|
||||
};
|
||||
u8 header_len; /* Length of the Logged TLP Header in DWORDs */
|
||||
bool flit; /* TLP was logged when in Flit mode */
|
||||
};
|
||||
|
||||
struct aer_capability_regs {
|
||||
|
||||
@@ -681,6 +681,7 @@ struct pci_bus {
|
||||
struct bin_attribute *legacy_mem; /* Legacy mem */
|
||||
unsigned int is_added:1;
|
||||
unsigned int unsafe_warn:1; /* warned about RW1C config write */
|
||||
unsigned int flit_mode:1; /* Link in Flit mode */
|
||||
};
|
||||
|
||||
#define to_pci_bus(n) container_of(n, struct pci_bus, dev)
|
||||
|
||||
@@ -309,7 +309,7 @@ TRACE_EVENT(aer_event,
|
||||
__field( u32, status )
|
||||
__field( u8, severity )
|
||||
__field( u8, tlp_header_valid)
|
||||
__array( u32, tlp_header, 4 )
|
||||
__array( u32, tlp_header, PCIE_STD_MAX_TLP_HEADERLOG)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
@@ -318,10 +318,10 @@ TRACE_EVENT(aer_event,
|
||||
__entry->severity = severity;
|
||||
__entry->tlp_header_valid = tlp_header_valid;
|
||||
if (tlp_header_valid) {
|
||||
__entry->tlp_header[0] = tlp->dw[0];
|
||||
__entry->tlp_header[1] = tlp->dw[1];
|
||||
__entry->tlp_header[2] = tlp->dw[2];
|
||||
__entry->tlp_header[3] = tlp->dw[3];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PCIE_STD_MAX_TLP_HEADERLOG; i++)
|
||||
__entry->tlp_header[i] = tlp->dw[i];
|
||||
}
|
||||
),
|
||||
|
||||
@@ -334,7 +334,7 @@ TRACE_EVENT(aer_event,
|
||||
__print_flags(__entry->status, "|", aer_correctable_errors) :
|
||||
__print_flags(__entry->status, "|", aer_uncorrectable_errors),
|
||||
__entry->tlp_header_valid ?
|
||||
__print_array(__entry->tlp_header, 4, 4) :
|
||||
__print_array(__entry->tlp_header, PCIE_STD_MAX_TLP_HEADERLOG, 4) :
|
||||
"Not available")
|
||||
);
|
||||
|
||||
|
||||
@@ -486,6 +486,7 @@
|
||||
#define PCI_EXP_TYPE_RC_EC 0xa /* Root Complex Event Collector */
|
||||
#define PCI_EXP_FLAGS_SLOT 0x0100 /* Slot implemented */
|
||||
#define PCI_EXP_FLAGS_IRQ 0x3e00 /* Interrupt message number */
|
||||
#define PCI_EXP_FLAGS_FLIT 0x8000 /* Flit Mode Supported */
|
||||
#define PCI_EXP_DEVCAP 0x04 /* Device capabilities */
|
||||
#define PCI_EXP_DEVCAP_PAYLOAD 0x00000007 /* Max_Payload_Size */
|
||||
#define PCI_EXP_DEVCAP_PHANTOM 0x00000018 /* Phantom functions */
|
||||
@@ -795,6 +796,8 @@
|
||||
#define PCI_ERR_CAP_ECRC_CHKC 0x00000080 /* ECRC Check Capable */
|
||||
#define PCI_ERR_CAP_ECRC_CHKE 0x00000100 /* ECRC Check Enable */
|
||||
#define PCI_ERR_CAP_PREFIX_LOG_PRESENT 0x00000800 /* TLP Prefix Log Present */
|
||||
#define PCI_ERR_CAP_TLP_LOG_FLIT 0x00040000 /* TLP was logged in Flit Mode */
|
||||
#define PCI_ERR_CAP_TLP_LOG_SIZE 0x00f80000 /* Logged TLP Size (only in Flit mode) */
|
||||
#define PCI_ERR_HEADER_LOG 0x1c /* Header Log Register (16 bytes) */
|
||||
#define PCI_ERR_ROOT_COMMAND 0x2c /* Root Error Command */
|
||||
#define PCI_ERR_ROOT_CMD_COR_EN 0x00000001 /* Correctable Err Reporting Enable */
|
||||
@@ -1061,8 +1064,9 @@
|
||||
#define PCI_EXP_DPC_CAP_RP_EXT 0x0020 /* Root Port Extensions */
|
||||
#define PCI_EXP_DPC_CAP_POISONED_TLP 0x0040 /* Poisoned TLP Egress Blocking Supported */
|
||||
#define PCI_EXP_DPC_CAP_SW_TRIGGER 0x0080 /* Software Triggering Supported */
|
||||
#define PCI_EXP_DPC_RP_PIO_LOG_SIZE 0x0F00 /* RP PIO Log Size */
|
||||
#define PCI_EXP_DPC_RP_PIO_LOG_SIZE 0x0F00 /* RP PIO Log Size [3:0] */
|
||||
#define PCI_EXP_DPC_CAP_DL_ACTIVE 0x1000 /* ERR_COR signal on DL_Active supported */
|
||||
#define PCI_EXP_DPC_RP_PIO_LOG_SIZE4 0x2000 /* RP PIO Log Size [4] */
|
||||
|
||||
#define PCI_EXP_DPC_CTL 0x06 /* DPC control */
|
||||
#define PCI_EXP_DPC_CTL_EN_FATAL 0x0001 /* Enable trigger on ERR_FATAL message */
|
||||
|
||||
Reference in New Issue
Block a user