- 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:
Bjorn Helgaas 2019-11-28 08:54:28 -06:00
Родитель 7d194c2100 35a0b2378c
Коммит c2a3d213d1
10 изменённых файлов: 101 добавлений и 19 удалений

Просмотреть файл

@ -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,