Merge branch 'pci/aer'
- Restore AER capability after resume (Mayurkumar Patel) - Add PoisonTLPBlocked AER counter (Rajat Jain) - Use for_each_set_bit() to simplify AER code (Andy Shevchenko) - Fix AER kernel-doc (Andy Shevchenko) - Add "pcie_ports=dpc-native" parameter to allow native use of DPC even if platform didn't grant control over AER (Olof Johansson) * pci/aer: PCI/DPC: Add "pcie_ports=dpc-native" to allow DPC without AER control PCI/AER: Fix kernel-doc warnings PCI/AER: Use for_each_set_bit() to simplify code PCI/AER: Add PoisonTLPBlocked to Uncorrectable error counters PCI/AER: Save AER Capability for suspend/resume
This commit is contained in:
commit
c2a3d213d1
@ -3540,6 +3540,8 @@
|
||||
even if the platform doesn't give the OS permission to
|
||||
use them. This may cause conflicts if the platform
|
||||
also tries to use these services.
|
||||
dpc-native Use native PCIe service for DPC only. May
|
||||
cause conflicts if firmware uses AER or DPC.
|
||||
compat Disable native PCIe services (PME, AER, DPC, PCIe
|
||||
hotplug).
|
||||
|
||||
|
@ -355,7 +355,7 @@ static inline bool pcie_cap_has_sltctl(const struct pci_dev *dev)
|
||||
pcie_caps_reg(dev) & PCI_EXP_FLAGS_SLOT;
|
||||
}
|
||||
|
||||
static inline bool pcie_cap_has_rtctl(const struct pci_dev *dev)
|
||||
bool pcie_cap_has_rtctl(const struct pci_dev *dev)
|
||||
{
|
||||
int type = pci_pcie_type(dev);
|
||||
|
||||
|
@ -1359,6 +1359,7 @@ int pci_save_state(struct pci_dev *dev)
|
||||
|
||||
pci_save_ltr_state(dev);
|
||||
pci_save_dpc_state(dev);
|
||||
pci_save_aer_state(dev);
|
||||
return pci_save_vc_state(dev);
|
||||
}
|
||||
EXPORT_SYMBOL(pci_save_state);
|
||||
@ -1472,6 +1473,7 @@ void pci_restore_state(struct pci_dev *dev)
|
||||
pci_restore_dpc_state(dev);
|
||||
|
||||
pci_cleanup_aer_error_status_regs(dev);
|
||||
pci_restore_aer_state(dev);
|
||||
|
||||
pci_restore_config_space(dev);
|
||||
|
||||
|
@ -12,6 +12,7 @@ extern const unsigned char pcie_link_speed[];
|
||||
extern bool pci_early_dump;
|
||||
|
||||
bool pcie_cap_has_lnkctl(const struct pci_dev *dev);
|
||||
bool pcie_cap_has_rtctl(const struct pci_dev *dev);
|
||||
|
||||
/* Functions internal to the PCI core code */
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
#define pr_fmt(fmt) "AER: " fmt
|
||||
#define dev_fmt pr_fmt
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/cper.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci-acpi.h>
|
||||
@ -36,7 +37,7 @@
|
||||
#define AER_ERROR_SOURCES_MAX 128
|
||||
|
||||
#define AER_MAX_TYPEOF_COR_ERRS 16 /* as per PCI_ERR_COR_STATUS */
|
||||
#define AER_MAX_TYPEOF_UNCOR_ERRS 26 /* as per PCI_ERR_UNCOR_STATUS*/
|
||||
#define AER_MAX_TYPEOF_UNCOR_ERRS 27 /* as per PCI_ERR_UNCOR_STATUS*/
|
||||
|
||||
struct aer_err_source {
|
||||
unsigned int status;
|
||||
@ -201,6 +202,7 @@ void pcie_set_ecrc_checking(struct pci_dev *dev)
|
||||
|
||||
/**
|
||||
* pcie_ecrc_get_policy - parse kernel command-line ecrc option
|
||||
* @str: ECRC policy from kernel command line to use
|
||||
*/
|
||||
void pcie_ecrc_get_policy(char *str)
|
||||
{
|
||||
@ -448,12 +450,70 @@ int pci_cleanup_aer_error_status_regs(struct pci_dev *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pci_save_aer_state(struct pci_dev *dev)
|
||||
{
|
||||
struct pci_cap_saved_state *save_state;
|
||||
u32 *cap;
|
||||
int pos;
|
||||
|
||||
pos = dev->aer_cap;
|
||||
if (!pos)
|
||||
return;
|
||||
|
||||
save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_ERR);
|
||||
if (!save_state)
|
||||
return;
|
||||
|
||||
cap = &save_state->cap.data[0];
|
||||
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, cap++);
|
||||
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, cap++);
|
||||
pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, cap++);
|
||||
pci_read_config_dword(dev, pos + PCI_ERR_CAP, cap++);
|
||||
if (pcie_cap_has_rtctl(dev))
|
||||
pci_read_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, cap++);
|
||||
}
|
||||
|
||||
void pci_restore_aer_state(struct pci_dev *dev)
|
||||
{
|
||||
struct pci_cap_saved_state *save_state;
|
||||
u32 *cap;
|
||||
int pos;
|
||||
|
||||
pos = dev->aer_cap;
|
||||
if (!pos)
|
||||
return;
|
||||
|
||||
save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_ERR);
|
||||
if (!save_state)
|
||||
return;
|
||||
|
||||
cap = &save_state->cap.data[0];
|
||||
pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, *cap++);
|
||||
pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, *cap++);
|
||||
pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, *cap++);
|
||||
pci_write_config_dword(dev, pos + PCI_ERR_CAP, *cap++);
|
||||
if (pcie_cap_has_rtctl(dev))
|
||||
pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, *cap++);
|
||||
}
|
||||
|
||||
void pci_aer_init(struct pci_dev *dev)
|
||||
{
|
||||
dev->aer_cap = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
|
||||
int n;
|
||||
|
||||
if (dev->aer_cap)
|
||||
dev->aer_stats = kzalloc(sizeof(struct aer_stats), GFP_KERNEL);
|
||||
dev->aer_cap = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
|
||||
if (!dev->aer_cap)
|
||||
return;
|
||||
|
||||
dev->aer_stats = kzalloc(sizeof(struct aer_stats), GFP_KERNEL);
|
||||
|
||||
/*
|
||||
* We save/restore PCI_ERR_UNCOR_MASK, PCI_ERR_UNCOR_SEVER,
|
||||
* PCI_ERR_COR_MASK, and PCI_ERR_CAP. Root and Root Complex Event
|
||||
* Collectors also implement PCI_ERR_ROOT_COMMAND (PCIe r5.0, sec
|
||||
* 7.8.4).
|
||||
*/
|
||||
n = pcie_cap_has_rtctl(dev) ? 5 : 4;
|
||||
pci_add_ext_cap_save_buffer(dev, PCI_EXT_CAP_ID_ERR, sizeof(u32) * n);
|
||||
|
||||
pci_cleanup_aer_error_status_regs(dev);
|
||||
}
|
||||
@ -560,6 +620,7 @@ static const char *aer_uncorrectable_error_string[AER_MAX_TYPEOF_UNCOR_ERRS] = {
|
||||
"BlockedTLP", /* Bit Position 23 */
|
||||
"AtomicOpBlocked", /* Bit Position 24 */
|
||||
"TLPBlockedErr", /* Bit Position 25 */
|
||||
"PoisonTLPBlocked", /* Bit Position 26 */
|
||||
};
|
||||
|
||||
static const char *aer_agent_string[] = {
|
||||
@ -657,7 +718,8 @@ const struct attribute_group aer_stats_attr_group = {
|
||||
static void pci_dev_aer_stats_incr(struct pci_dev *pdev,
|
||||
struct aer_err_info *info)
|
||||
{
|
||||
int status, i, max = -1;
|
||||
unsigned long status = info->status & ~info->mask;
|
||||
int i, max = -1;
|
||||
u64 *counter = NULL;
|
||||
struct aer_stats *aer_stats = pdev->aer_stats;
|
||||
|
||||
@ -682,10 +744,8 @@ static void pci_dev_aer_stats_incr(struct pci_dev *pdev,
|
||||
break;
|
||||
}
|
||||
|
||||
status = (info->status & ~info->mask);
|
||||
for (i = 0; i < max; i++)
|
||||
if (status & (1 << i))
|
||||
counter[i]++;
|
||||
for_each_set_bit(i, &status, max)
|
||||
counter[i]++;
|
||||
}
|
||||
|
||||
static void pci_rootport_aer_stats_incr(struct pci_dev *pdev,
|
||||
@ -717,14 +777,11 @@ static void __print_tlp_header(struct pci_dev *dev,
|
||||
static void __aer_print_error(struct pci_dev *dev,
|
||||
struct aer_err_info *info)
|
||||
{
|
||||
int i, status;
|
||||
unsigned long status = info->status & ~info->mask;
|
||||
const char *errmsg = NULL;
|
||||
status = (info->status & ~info->mask);
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
if (!(status & (1 << i)))
|
||||
continue;
|
||||
int i;
|
||||
|
||||
for_each_set_bit(i, &status, 32) {
|
||||
if (info->severity == AER_CORRECTABLE)
|
||||
errmsg = i < ARRAY_SIZE(aer_correctable_error_string) ?
|
||||
aer_correctable_error_string[i] : NULL;
|
||||
@ -1204,7 +1261,8 @@ static void aer_isr_one_error(struct aer_rpc *rpc,
|
||||
|
||||
/**
|
||||
* aer_isr - consume errors detected by root port
|
||||
* @work: definition of this work item
|
||||
* @irq: IRQ assigned to Root Port
|
||||
* @context: pointer to Root Port data structure
|
||||
*
|
||||
* Invoked, as DPC, when root port records new detected error
|
||||
*/
|
||||
|
@ -291,7 +291,7 @@ static int dpc_probe(struct pcie_device *dev)
|
||||
int status;
|
||||
u16 ctl, cap;
|
||||
|
||||
if (pcie_aer_get_firmware_first(pdev))
|
||||
if (pcie_aer_get_firmware_first(pdev) && !pcie_ports_dpc_native)
|
||||
return -ENOTSUPP;
|
||||
|
||||
dpc = devm_kzalloc(device, sizeof(*dpc), GFP_KERNEL);
|
||||
|
@ -25,6 +25,8 @@
|
||||
|
||||
#define PCIE_PORT_DEVICE_MAXSERVICES 5
|
||||
|
||||
extern bool pcie_ports_dpc_native;
|
||||
|
||||
#ifdef CONFIG_PCIEAER
|
||||
int pcie_aer_init(void);
|
||||
#else
|
||||
|
@ -250,8 +250,13 @@ static int get_port_device_capability(struct pci_dev *dev)
|
||||
pcie_pme_interrupt_enable(dev, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* With dpc-native, allow Linux to use DPC even if it doesn't have
|
||||
* permission to use AER.
|
||||
*/
|
||||
if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC) &&
|
||||
pci_aer_available() && services & PCIE_PORT_SERVICE_AER)
|
||||
pci_aer_available() &&
|
||||
(pcie_ports_dpc_native || (services & PCIE_PORT_SERVICE_AER)))
|
||||
services |= PCIE_PORT_SERVICE_DPC;
|
||||
|
||||
if (pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM ||
|
||||
|
@ -29,12 +29,20 @@ bool pcie_ports_disabled;
|
||||
*/
|
||||
bool pcie_ports_native;
|
||||
|
||||
/*
|
||||
* If the user specified "pcie_ports=dpc-native", use the Linux DPC PCIe
|
||||
* service even if the platform hasn't given us permission.
|
||||
*/
|
||||
bool pcie_ports_dpc_native;
|
||||
|
||||
static int __init pcie_port_setup(char *str)
|
||||
{
|
||||
if (!strncmp(str, "compat", 6))
|
||||
pcie_ports_disabled = true;
|
||||
else if (!strncmp(str, "native", 6))
|
||||
pcie_ports_native = true;
|
||||
else if (!strncmp(str, "dpc-native", 10))
|
||||
pcie_ports_dpc_native = true;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -46,6 +46,8 @@ int pci_enable_pcie_error_reporting(struct pci_dev *dev);
|
||||
int pci_disable_pcie_error_reporting(struct pci_dev *dev);
|
||||
int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev);
|
||||
int pci_cleanup_aer_error_status_regs(struct pci_dev *dev);
|
||||
void pci_save_aer_state(struct pci_dev *dev);
|
||||
void pci_restore_aer_state(struct pci_dev *dev);
|
||||
#else
|
||||
static inline int pci_enable_pcie_error_reporting(struct pci_dev *dev)
|
||||
{
|
||||
@ -63,6 +65,8 @@ static inline int pci_cleanup_aer_error_status_regs(struct pci_dev *dev)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
static inline void pci_save_aer_state(struct pci_dev *dev) {}
|
||||
static inline void pci_restore_aer_state(struct pci_dev *dev) {}
|
||||
#endif
|
||||
|
||||
void cper_print_aer(struct pci_dev *dev, int aer_severity,
|
||||
|
Loading…
Reference in New Issue
Block a user