mirror of
				git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
				synced 2025-10-31 08:44:41 +00:00 
			
		
		
		
	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
					
				
					 10 changed files with 101 additions and 19 deletions
				
			
		|  | @ -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,13 +450,71 @@ 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; | ||||
| 
 | ||||
| 	dev->aer_cap = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); | ||||
| 	if (!dev->aer_cap) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (dev->aer_cap) | ||||
| 	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,9 +744,7 @@ 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)) | ||||
| 	for_each_set_bit(i, &status, max) | ||||
| 		counter[i]++; | ||||
| } | ||||
| 
 | ||||
|  | @ -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…
	
	Add table
		
		Reference in a new issue
	
	 Bjorn Helgaas
						Bjorn Helgaas