Skip to content

Commit

Permalink
From patchwork series 437156
Browse files Browse the repository at this point in the history
  • Loading branch information
Fox Snowpatch committed Dec 17, 2024
1 parent 2dd0957 commit 5b7295d
Show file tree
Hide file tree
Showing 11 changed files with 158 additions and 66 deletions.
2 changes: 1 addition & 1 deletion drivers/pci/ats.c
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ int pci_enable_pasid(struct pci_dev *pdev, int features)
if (WARN_ON(pdev->pasid_enabled))
return -EBUSY;

if (!pdev->eetlp_prefix_path && !pdev->pasid_no_tlp)
if (!pdev->eetlp_prefix_max && !pdev->pasid_no_tlp)
return -EINVAL;

if (!pasid)
Expand Down
28 changes: 0 additions & 28 deletions drivers/pci/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -1099,34 +1099,6 @@ static void pci_enable_acs(struct pci_dev *dev)
pci_write_config_word(dev, pos + PCI_ACS_CTRL, caps.ctrl);
}

/**
* pcie_read_tlp_log - read TLP Header Log
* @dev: PCIe device
* @where: PCI Config offset of TLP Header Log
* @tlp_log: TLP Log structure to fill
*
* Fill @tlp_log from TLP Header Log registers, e.g., AER or DPC.
*
* Return: 0 on success and filled TLP Log structure, <0 on error.
*/
int pcie_read_tlp_log(struct pci_dev *dev, int where,
struct pcie_tlp_log *tlp_log)
{
int i, ret;

memset(tlp_log, 0, sizeof(*tlp_log));

for (i = 0; i < 4; i++) {
ret = pci_read_config_dword(dev, where + i * 4,
&tlp_log->dw[i]);
if (ret)
return pcibios_err_to_errno(ret);
}

return 0;
}
EXPORT_SYMBOL_GPL(pcie_read_tlp_log);

/**
* pci_restore_bars - restore a device's BAR values (e.g. after wake-up)
* @dev: PCI device to have its BARs restored
Expand Down
9 changes: 9 additions & 0 deletions drivers/pci/pci.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

#include <linux/pci.h>

struct pcie_tlp_log;

/* Number of possible devfns: 0.0 to 1f.7 inclusive */
#define MAX_NR_DEVFNS 256

Expand Down Expand Up @@ -547,6 +549,12 @@ struct aer_err_info {

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 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);
#endif /* CONFIG_PCIEAER */

#ifdef CONFIG_PCIEPORTBUS
Expand All @@ -565,6 +573,7 @@ void pci_dpc_init(struct pci_dev *pdev);
void dpc_process_error(struct pci_dev *pdev);
pci_ers_result_t dpc_reset_link(struct pci_dev *pdev);
bool pci_dpc_recovered(struct pci_dev *pdev);
unsigned int dpc_tlp_log_len(struct pci_dev *dev);
#else
static inline void pci_save_dpc_state(struct pci_dev *dev) { }
static inline void pci_restore_dpc_state(struct pci_dev *dev) { }
Expand Down
2 changes: 1 addition & 1 deletion drivers/pci/pcie/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ pcieportdrv-y := portdrv.o rcec.o
obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o bwctrl.o

obj-y += aspm.o
obj-$(CONFIG_PCIEAER) += aer.o err.o
obj-$(CONFIG_PCIEAER) += aer.o err.o tlp.o
obj-$(CONFIG_PCIEAER_INJECT) += aer_inject.o
obj-$(CONFIG_PCIE_PME) += pme.o
obj-$(CONFIG_PCIE_DPC) += dpc.o
Expand Down
27 changes: 12 additions & 15 deletions drivers/pci/pcie/aer.c
Original file line number Diff line number Diff line change
Expand Up @@ -665,12 +665,6 @@ static void pci_rootport_aer_stats_incr(struct pci_dev *pdev,
}
}

static void __print_tlp_header(struct pci_dev *dev, struct pcie_tlp_log *t)
{
pci_err(dev, " TLP Header: %08x %08x %08x %08x\n",
t->dw[0], t->dw[1], t->dw[2], t->dw[3]);
}

static void __aer_print_error(struct pci_dev *dev,
struct aer_err_info *info)
{
Expand All @@ -692,8 +686,8 @@ 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,
info->first_error == i ? " (First)" : "");
pci_printk(level, dev, "%s [%2d] %-22s%s\n", dev_fmt(""), i,
errmsg, info->first_error == i ? " (First)" : "");
}
pci_dev_aer_stats_incr(dev, info);
}
Expand All @@ -715,17 +709,17 @@ 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_error_severity_string[info->severity],
pci_printk(level, dev, "%sPCIe Bus Error: severity=%s, type=%s, (%s)\n",
dev_fmt(""), 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",
dev->vendor, dev->device, info->status, info->mask);
pci_printk(level, dev, "%s device [%04x:%04x] error status/mask=%08x/%08x\n",
dev_fmt(""), dev->vendor, dev->device, info->status, info->mask);

__aer_print_error(dev, info);

if (info->tlp_header_valid)
__print_tlp_header(dev, &info->tlp);
pcie_print_tlp_log(dev, &info->tlp, dev_fmt(" "));

out:
if (info->id && info->error_dev_num > 1 && info->id == id)
Expand Down Expand Up @@ -797,7 +791,7 @@ void pci_print_aer(struct pci_dev *dev, int aer_severity,
aer->uncor_severity);

if (tlp_header_valid)
__print_tlp_header(dev, &aer->header_log);
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);
Expand Down Expand Up @@ -1248,7 +1242,10 @@ int aer_get_device_error_info(struct pci_dev *dev, struct aer_err_info *info)

if (info->status & AER_LOG_TLP_MASKS) {
info->tlp_header_valid = 1;
pcie_read_tlp_log(dev, aer + PCI_ERR_HEADER_LOG, &info->tlp);
pcie_read_tlp_log(dev, aer + PCI_ERR_HEADER_LOG,
aer + PCI_ERR_PREFIX_LOG,
aer_tlp_log_len(dev, aercc),
&info->tlp);
}
}

Expand Down
14 changes: 5 additions & 9 deletions drivers/pci/pcie/dpc.c
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ pci_ers_result_t dpc_reset_link(struct pci_dev *pdev)
static void dpc_process_rp_pio_error(struct pci_dev *pdev)
{
u16 cap = pdev->dpc_cap, dpc_status, first_error;
u32 status, mask, sev, syserr, exc, log, prefix;
u32 status, mask, sev, syserr, exc, log;
struct pcie_tlp_log tlp_log;
int i;

Expand All @@ -217,20 +217,16 @@ static void dpc_process_rp_pio_error(struct pci_dev *pdev)

if (pdev->dpc_rp_log_size < 4)
goto clear_status;
pcie_read_tlp_log(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG, &tlp_log);
pci_err(pdev, "TLP Header: %#010x %#010x %#010x %#010x\n",
tlp_log.dw[0], tlp_log.dw[1], tlp_log.dw[2], tlp_log.dw[3]);
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);
pcie_print_tlp_log(pdev, &tlp_log, dev_fmt(""));

if (pdev->dpc_rp_log_size < 5)
goto clear_status;
pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_IMPSPEC_LOG, &log);
pci_err(pdev, "RP PIO ImpSpec Log %#010x\n", log);

for (i = 0; i < pdev->dpc_rp_log_size - 5; i++) {
pci_read_config_dword(pdev,
cap + PCI_EXP_DPC_RP_PIO_TLPPREFIX_LOG + i * 4, &prefix);
pci_err(pdev, "TLP Prefix Header: dw%d, %#010x\n", i, prefix);
}
clear_status:
pci_write_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_STATUS, status);
}
Expand Down
112 changes: 112 additions & 0 deletions drivers/pci/pcie/tlp.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// SPDX-License-Identifier: GPL-2.0
/*
* PCIe TLP Log handling
*
* Copyright (C) 2024 Intel Corporation
*/

#include <linux/aer.h>
#include <linux/array_size.h>
#include <linux/pci.h>
#include <linux/string.h>

#include "../pci.h"

/**
* aer_tlp_log_len - Calculates AER Capability TLP Header/Prefix Log length
* @dev: PCIe device
*
* Return: TLP Header/Prefix Log length
*/
unsigned int aer_tlp_log_len(struct pci_dev *dev, u32 aercc)
{
return 4 + (aercc & PCI_ERR_CAP_PREFIX_LOG_PRESENT) ?
dev->eetlp_prefix_max : 0;
}

#ifdef CONFIG_PCIE_DPC
/**
* dpc_tlp_log_len - Calculates DPC RP PIO TLP Header/Prefix Log length
* @dev: PCIe device
*
* Return: TLP Header/Prefix Log length
*/
unsigned int dpc_tlp_log_len(struct pci_dev *dev)
{
/* Remove ImpSpec Log register from the count */
if (dev->dpc_rp_log_size >= 5)
return dev->dpc_rp_log_size - 1;

return dev->dpc_rp_log_size;
}
#endif

/**
* pcie_read_tlp_log - read TLP Header Log
* @dev: PCIe device
* @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)
* @log: TLP Log structure to fill
*
* Fill @log from TLP Header Log registers, e.g., AER or DPC.
*
* 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 i;
int off, ret;
u32 *to;

memset(log, 0, sizeof(*log));

for (i = 0; i < tlp_len; i++) {
if (i < 4) {
off = where + i * 4;
to = &log->dw[i];
} else {
off = where2 + (i - 4) * 4;
to = &log->prefix[i - 4];
}

ret = pci_read_config_dword(dev, off, to);
if (ret)
return pcibios_err_to_errno(ret);
}

return 0;
}

#define EE_PREFIX_STR " E-E Prefixes:"

/**
* pcie_print_tlp_log - Print TLP Header / Prefix Log contents
* @dev: PCIe device
* @log: TLP Log structure
* @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)
{
char buf[11 * (4 + ARRAY_SIZE(log->prefix)) + sizeof(EE_PREFIX_STR)];
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]);
}

pci_err(dev, "%sTLP Header: %s\n", pfx, buf);
}
14 changes: 9 additions & 5 deletions drivers/pci/probe.c
Original file line number Diff line number Diff line change
Expand Up @@ -2251,8 +2251,8 @@ static void pci_configure_relaxed_ordering(struct pci_dev *dev)

static void pci_configure_eetlp_prefix(struct pci_dev *dev)
{
#ifdef CONFIG_PCI_PASID
struct pci_dev *bridge;
unsigned int eetlp_max;
int pcie_type;
u32 cap;

Expand All @@ -2264,15 +2264,19 @@ static void pci_configure_eetlp_prefix(struct pci_dev *dev)
return;

pcie_type = pci_pcie_type(dev);

eetlp_max = FIELD_GET(PCI_EXP_DEVCAP2_EE_PREFIX_MAX, cap);
/* 00b means 4 */
eetlp_max = eetlp_max ?: 4;

if (pcie_type == PCI_EXP_TYPE_ROOT_PORT ||
pcie_type == PCI_EXP_TYPE_RC_END)
dev->eetlp_prefix_path = 1;
dev->eetlp_prefix_max = eetlp_max;
else {
bridge = pci_upstream_bridge(dev);
if (bridge && bridge->eetlp_prefix_path)
dev->eetlp_prefix_path = 1;
if (bridge && bridge->eetlp_prefix_max)
dev->eetlp_prefix_max = eetlp_max;
}
#endif
}

static void pci_configure_serr(struct pci_dev *dev)
Expand Down
3 changes: 1 addition & 2 deletions include/linux/aer.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ struct pci_dev;

struct pcie_tlp_log {
u32 dw[4];
u32 prefix[4];
};

struct aer_capability_regs {
Expand All @@ -37,8 +38,6 @@ struct aer_capability_regs {
u16 uncor_err_source;
};

int pcie_read_tlp_log(struct pci_dev *dev, int where, struct pcie_tlp_log *log);

#if defined(CONFIG_PCIEAER)
int pci_aer_clear_nonfatal_status(struct pci_dev *dev);
int pcie_aer_is_native(struct pci_dev *dev);
Expand Down
2 changes: 1 addition & 1 deletion include/linux/pci.h
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ struct pci_dev {
supported from root to here */
#endif
unsigned int pasid_no_tlp:1; /* PASID works without TLP Prefix */
unsigned int eetlp_prefix_path:1; /* End-to-End TLP Prefix */
unsigned int eetlp_prefix_max:3; /* Max # of End-End TLP Prefixes, 0=not supported */

pci_channel_state_t error_state; /* Current connectivity state */
struct device dev; /* Generic device interface */
Expand Down
11 changes: 7 additions & 4 deletions include/uapi/linux/pci_regs.h
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,7 @@
#define PCI_EXP_DEVCAP2_OBFF_MSG 0x00040000 /* New message signaling */
#define PCI_EXP_DEVCAP2_OBFF_WAKE 0x00080000 /* Re-use WAKE# for OBFF */
#define PCI_EXP_DEVCAP2_EE_PREFIX 0x00200000 /* End-End TLP Prefix */
#define PCI_EXP_DEVCAP2_EE_PREFIX_MAX 0x00c00000 /* Max End-End TLP Prefixes */
#define PCI_EXP_DEVCTL2 0x28 /* Device Control 2 */
#define PCI_EXP_DEVCTL2_COMP_TIMEOUT 0x000f /* Completion Timeout Value */
#define PCI_EXP_DEVCTL2_COMP_TMOUT_DIS 0x0010 /* Completion Timeout Disable */
Expand Down Expand Up @@ -789,10 +790,11 @@
/* Same bits as above */
#define PCI_ERR_CAP 0x18 /* Advanced Error Capabilities & Ctrl*/
#define PCI_ERR_CAP_FEP(x) ((x) & 0x1f) /* First Error Pointer */
#define PCI_ERR_CAP_ECRC_GENC 0x00000020 /* ECRC Generation Capable */
#define PCI_ERR_CAP_ECRC_GENE 0x00000040 /* ECRC Generation Enable */
#define PCI_ERR_CAP_ECRC_CHKC 0x00000080 /* ECRC Check Capable */
#define PCI_ERR_CAP_ECRC_CHKE 0x00000100 /* ECRC Check Enable */
#define PCI_ERR_CAP_ECRC_GENC 0x00000020 /* ECRC Generation Capable */
#define PCI_ERR_CAP_ECRC_GENE 0x00000040 /* ECRC Generation Enable */
#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_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 */
Expand All @@ -808,6 +810,7 @@
#define PCI_ERR_ROOT_FATAL_RCV 0x00000040 /* Fatal Received */
#define PCI_ERR_ROOT_AER_IRQ 0xf8000000 /* Advanced Error Interrupt Message Number */
#define PCI_ERR_ROOT_ERR_SRC 0x34 /* Error Source Identification */
#define PCI_ERR_PREFIX_LOG 0x38 /* TLP Prefix LOG Register (up to 16 bytes) */

/* Virtual Channel */
#define PCI_VC_PORT_CAP1 0x04
Expand Down

0 comments on commit 5b7295d

Please sign in to comment.