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:
Коммит
c2a3d213d1
|
@ -3540,6 +3540,8 @@
|
||||||
even if the platform doesn't give the OS permission to
|
even if the platform doesn't give the OS permission to
|
||||||
use them. This may cause conflicts if the platform
|
use them. This may cause conflicts if the platform
|
||||||
also tries to use these services.
|
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
|
compat Disable native PCIe services (PME, AER, DPC, PCIe
|
||||||
hotplug).
|
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;
|
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);
|
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_ltr_state(dev);
|
||||||
pci_save_dpc_state(dev);
|
pci_save_dpc_state(dev);
|
||||||
|
pci_save_aer_state(dev);
|
||||||
return pci_save_vc_state(dev);
|
return pci_save_vc_state(dev);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(pci_save_state);
|
EXPORT_SYMBOL(pci_save_state);
|
||||||
|
@ -1472,6 +1473,7 @@ void pci_restore_state(struct pci_dev *dev)
|
||||||
pci_restore_dpc_state(dev);
|
pci_restore_dpc_state(dev);
|
||||||
|
|
||||||
pci_cleanup_aer_error_status_regs(dev);
|
pci_cleanup_aer_error_status_regs(dev);
|
||||||
|
pci_restore_aer_state(dev);
|
||||||
|
|
||||||
pci_restore_config_space(dev);
|
pci_restore_config_space(dev);
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ extern const unsigned char pcie_link_speed[];
|
||||||
extern bool pci_early_dump;
|
extern bool pci_early_dump;
|
||||||
|
|
||||||
bool pcie_cap_has_lnkctl(const struct pci_dev *dev);
|
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 */
|
/* Functions internal to the PCI core code */
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#define pr_fmt(fmt) "AER: " fmt
|
#define pr_fmt(fmt) "AER: " fmt
|
||||||
#define dev_fmt pr_fmt
|
#define dev_fmt pr_fmt
|
||||||
|
|
||||||
|
#include <linux/bitops.h>
|
||||||
#include <linux/cper.h>
|
#include <linux/cper.h>
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include <linux/pci-acpi.h>
|
#include <linux/pci-acpi.h>
|
||||||
|
@ -36,7 +37,7 @@
|
||||||
#define AER_ERROR_SOURCES_MAX 128
|
#define AER_ERROR_SOURCES_MAX 128
|
||||||
|
|
||||||
#define AER_MAX_TYPEOF_COR_ERRS 16 /* as per PCI_ERR_COR_STATUS */
|
#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 {
|
struct aer_err_source {
|
||||||
unsigned int status;
|
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
|
* 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)
|
void pcie_ecrc_get_policy(char *str)
|
||||||
{
|
{
|
||||||
|
@ -448,12 +450,70 @@ int pci_cleanup_aer_error_status_regs(struct pci_dev *dev)
|
||||||
return 0;
|
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)
|
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_cap = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
|
||||||
dev->aer_stats = kzalloc(sizeof(struct aer_stats), GFP_KERNEL);
|
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);
|
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 */
|
"BlockedTLP", /* Bit Position 23 */
|
||||||
"AtomicOpBlocked", /* Bit Position 24 */
|
"AtomicOpBlocked", /* Bit Position 24 */
|
||||||
"TLPBlockedErr", /* Bit Position 25 */
|
"TLPBlockedErr", /* Bit Position 25 */
|
||||||
|
"PoisonTLPBlocked", /* Bit Position 26 */
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *aer_agent_string[] = {
|
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,
|
static void pci_dev_aer_stats_incr(struct pci_dev *pdev,
|
||||||
struct aer_err_info *info)
|
struct aer_err_info *info)
|
||||||
{
|
{
|
||||||
int status, i, max = -1;
|
unsigned long status = info->status & ~info->mask;
|
||||||
|
int i, max = -1;
|
||||||
u64 *counter = NULL;
|
u64 *counter = NULL;
|
||||||
struct aer_stats *aer_stats = pdev->aer_stats;
|
struct aer_stats *aer_stats = pdev->aer_stats;
|
||||||
|
|
||||||
|
@ -682,10 +744,8 @@ static void pci_dev_aer_stats_incr(struct pci_dev *pdev,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = (info->status & ~info->mask);
|
for_each_set_bit(i, &status, max)
|
||||||
for (i = 0; i < max; i++)
|
counter[i]++;
|
||||||
if (status & (1 << i))
|
|
||||||
counter[i]++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pci_rootport_aer_stats_incr(struct pci_dev *pdev,
|
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,
|
static void __aer_print_error(struct pci_dev *dev,
|
||||||
struct aer_err_info *info)
|
struct aer_err_info *info)
|
||||||
{
|
{
|
||||||
int i, status;
|
unsigned long status = info->status & ~info->mask;
|
||||||
const char *errmsg = NULL;
|
const char *errmsg = NULL;
|
||||||
status = (info->status & ~info->mask);
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < 32; i++) {
|
|
||||||
if (!(status & (1 << i)))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
|
for_each_set_bit(i, &status, 32) {
|
||||||
if (info->severity == AER_CORRECTABLE)
|
if (info->severity == AER_CORRECTABLE)
|
||||||
errmsg = i < ARRAY_SIZE(aer_correctable_error_string) ?
|
errmsg = i < ARRAY_SIZE(aer_correctable_error_string) ?
|
||||||
aer_correctable_error_string[i] : NULL;
|
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
|
* 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
|
* Invoked, as DPC, when root port records new detected error
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -291,7 +291,7 @@ static int dpc_probe(struct pcie_device *dev)
|
||||||
int status;
|
int status;
|
||||||
u16 ctl, cap;
|
u16 ctl, cap;
|
||||||
|
|
||||||
if (pcie_aer_get_firmware_first(pdev))
|
if (pcie_aer_get_firmware_first(pdev) && !pcie_ports_dpc_native)
|
||||||
return -ENOTSUPP;
|
return -ENOTSUPP;
|
||||||
|
|
||||||
dpc = devm_kzalloc(device, sizeof(*dpc), GFP_KERNEL);
|
dpc = devm_kzalloc(device, sizeof(*dpc), GFP_KERNEL);
|
||||||
|
|
|
@ -25,6 +25,8 @@
|
||||||
|
|
||||||
#define PCIE_PORT_DEVICE_MAXSERVICES 5
|
#define PCIE_PORT_DEVICE_MAXSERVICES 5
|
||||||
|
|
||||||
|
extern bool pcie_ports_dpc_native;
|
||||||
|
|
||||||
#ifdef CONFIG_PCIEAER
|
#ifdef CONFIG_PCIEAER
|
||||||
int pcie_aer_init(void);
|
int pcie_aer_init(void);
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -250,8 +250,13 @@ static int get_port_device_capability(struct pci_dev *dev)
|
||||||
pcie_pme_interrupt_enable(dev, false);
|
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) &&
|
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;
|
services |= PCIE_PORT_SERVICE_DPC;
|
||||||
|
|
||||||
if (pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM ||
|
if (pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM ||
|
||||||
|
|
|
@ -29,12 +29,20 @@ bool pcie_ports_disabled;
|
||||||
*/
|
*/
|
||||||
bool pcie_ports_native;
|
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)
|
static int __init pcie_port_setup(char *str)
|
||||||
{
|
{
|
||||||
if (!strncmp(str, "compat", 6))
|
if (!strncmp(str, "compat", 6))
|
||||||
pcie_ports_disabled = true;
|
pcie_ports_disabled = true;
|
||||||
else if (!strncmp(str, "native", 6))
|
else if (!strncmp(str, "native", 6))
|
||||||
pcie_ports_native = true;
|
pcie_ports_native = true;
|
||||||
|
else if (!strncmp(str, "dpc-native", 10))
|
||||||
|
pcie_ports_dpc_native = true;
|
||||||
|
|
||||||
return 1;
|
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_disable_pcie_error_reporting(struct pci_dev *dev);
|
||||||
int pci_cleanup_aer_uncorrect_error_status(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);
|
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
|
#else
|
||||||
static inline int pci_enable_pcie_error_reporting(struct pci_dev *dev)
|
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;
|
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
|
#endif
|
||||||
|
|
||||||
void cper_print_aer(struct pci_dev *dev, int aer_severity,
|
void cper_print_aer(struct pci_dev *dev, int aer_severity,
|
||||||
|
|
Загрузка…
Ссылка в новой задаче