[PATCH v19 13/14] PCI/CXL: Mask/Unmask CXL protocol errors
Terry Bowman <[email protected]> Mon, 3 Aug 2026 17:18:09 -0500
| Newsgroups | gmane.linux.acpi.devel,gmane.linux.kernel.pci,gmane.linux.documentation,gmane.linux.kernel |
|---|---|
| Message-ID | <[email protected]> |
CXL protocol errors must be unmasked to be reported. Add pci_aer_mask_internal_errors() as the symmetric counterpart to pci_aer_unmask_internal_errors() and export both for cxl_core. Unmask CXL internal errors in cxl_dport_map_ras() and devm_cxl_port_ras_setup() after the RAS register block is successfully mapped. Register a devm action to restore the mask on teardown. The unmask/mask helpers gate on dev_is_pci() and pcie_aer_is_native() internally so callers need no special-casing. Remove the dev_is_pci(dport->dport_dev) guard in devm_cxl_dport_rch_ras_setup(). On RCH systems dport->dport_dev is the pci_host_bridge device which is not on pci_bus_type, so this guard blocked real hardware. The caller already gates on dport->rch. Co-developed-by: Dan Williams <[email protected]> Signed-off-by: Dan Williams <[email protected]> Signed-off-by: Terry Bowman <[email protected]> Reviewed-by: Dave Jiang <[email protected]> Reviewed-by: Jonathan Cameron <[email protected]> --- Changes in v18 -> v19: - Reworded commit message to be more concise. - Use pci_clear_and_set_config_dword() in pci_aer_mask_internal_errors() - Add review-by for DaveJ and Jonathan Changes in v17->v18: - Make cxl_unmask_proto_interrupts() and cxl_mask_proto_interrupts() static - Remove dev_is_pci() guard from devm_cxl_dport_rch_ras_setup(); the guard blocked real RCH hardware because pci_host_bridge is not on pci_bus_type Changes in v16->v17: - Drop redundant cxl_mask_proto_interrupts() calls from unregister_port() and cxl_dport_remove(); the devres action registered alongside the unmask is the sole mask path. - Update title - Remove unnecessary check for aer_capabilities - Gate cxl_unmask_proto_interrupts() on pcie_aer_is_native() - Add pci_aer_mask_internal_errors() and cxl_mask_proto_interrupts() - Only unmask on successful cxl_map_component_regs() - NULL-check @dev in cxl_{un,}mask_proto_interrupts() - Drop static and declare in core/core.h Change in v15 -> v16: - None Change in v14 -> v15: - None Changes in v13->v14: - Update commit title's prefix (Bjorn) Changes in v12->v13: - Add dev and dev_is_pci() NULL checks in cxl_unmask_proto_interrupts() (Terry) - Add Dave Jiang's and Ben's review-by Changes in v11->v12: - None --- drivers/cxl/core/ras.c | 73 +++++++++++++++++++++++++++++++---- drivers/pci/pcie/aer.c | 22 +++++++++-- include/linux/aer.h | 2 + tools/testing/cxl/Kbuild | 1 + tools/testing/cxl/test/mock.c | 12 ++++++ 5 files changed, 99 insertions(+), 11 deletions(-) diff --git a/drivers/cxl/core/ras.c b/drivers/cxl/core/ras.c index 0254b7ec64c30..40a8a03070652 100644 --- a/drivers/cxl/core/ras.c +++ b/drivers/cxl/core/ras.c @@ -114,16 +114,64 @@ static void cxl_cper_prot_err_work_fn(struct work_struct *work) } static DECLARE_WORK(cxl_cper_prot_err_work, cxl_cper_prot_err_work_fn); +static void cxl_unmask_proto_interrupts(struct device *dev) +{ + struct pci_dev *pdev; + + if (!dev || !dev_is_pci(dev)) + return; + + pdev = to_pci_dev(dev); + if (!pcie_aer_is_native(pdev)) + return; + + pci_aer_unmask_internal_errors(pdev); +} + +static void cxl_mask_proto_interrupts(struct device *dev) +{ + struct pci_dev *pdev; + + if (!dev || !dev_is_pci(dev)) + return; + + pdev = to_pci_dev(dev); + if (!pcie_aer_is_native(pdev)) + return; + + pci_aer_mask_internal_errors(pdev); +} + +static void cxl_mask_proto_irqs(void *dev) +{ + cxl_mask_proto_interrupts(dev); +} + static void cxl_dport_map_ras(struct cxl_dport *dport) { struct cxl_register_map *map = &dport->reg_map; struct device *dev = dport->dport_dev; - if (!map->component_map.ras.valid) + if (!map->component_map.ras.valid) { dev_dbg(dev, "RAS registers not found\n"); - else if (cxl_map_component_regs(map, &dport->regs.component, - BIT(CXL_CM_CAP_CAP_ID_RAS))) + return; + } + + if (cxl_map_component_regs(map, &dport->regs.component, + BIT(CXL_CM_CAP_CAP_ID_RAS))) { dev_dbg(dev, "Failed to map RAS capability.\n"); + return; + } + + if (!dev_is_pci(dev)) + return; + + cxl_unmask_proto_interrupts(dev); + if (devm_add_action_or_reset(dport_to_host(dport), + cxl_mask_proto_irqs, dev)) { + dev_warn(dev, "failed to defer CXL proto-irq mask; CXL protocol error reporting disabled\n"); + dport->regs.component.ras = NULL; + } } /** @@ -140,9 +188,6 @@ void devm_cxl_dport_rch_ras_setup(struct cxl_dport *dport) { struct pci_host_bridge *host_bridge; - if (!dev_is_pci(dport->dport_dev)) - return; - devm_cxl_dport_ras_setup(dport); host_bridge = to_pci_host_bridge(dport->dport_dev); @@ -157,6 +202,7 @@ EXPORT_SYMBOL_NS_GPL(devm_cxl_dport_rch_ras_setup, "CXL"); void devm_cxl_port_ras_setup(struct cxl_port *port) { struct cxl_register_map *map = &port->reg_map; + struct device *dev; if (!map->component_map.ras.valid) { dev_dbg(&port->dev, "RAS registers not found\n"); @@ -165,8 +211,21 @@ void devm_cxl_port_ras_setup(struct cxl_port *port) map->host = &port->dev; if (cxl_map_component_regs(map, &port->regs, - BIT(CXL_CM_CAP_CAP_ID_RAS))) + BIT(CXL_CM_CAP_CAP_ID_RAS))) { dev_dbg(&port->dev, "Failed to map RAS capability\n"); + return; + } + + dev = is_cxl_endpoint(port) ? port->uport_dev->parent : port->uport_dev; + if (!dev_is_pci(dev)) + return; + + cxl_unmask_proto_interrupts(dev); + if (devm_add_action_or_reset(&port->dev, cxl_mask_proto_irqs, dev)) { + dev_warn(&port->dev, + "failed to defer CXL proto-irq mask; CXL protocol error reporting disabled\n"); + port->regs.ras = NULL; + } } EXPORT_SYMBOL_NS_GPL(devm_cxl_port_ras_setup, "CXL"); diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index 0155f3923c1fd..d461f281e5372 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -1143,12 +1143,26 @@ void pci_aer_unmask_internal_errors(struct pci_dev *dev) mask &= ~PCI_ERR_COR_INTERNAL; pci_write_config_dword(dev, aer + PCI_ERR_COR_MASK, mask); } +EXPORT_SYMBOL_FOR_MODULES(pci_aer_unmask_internal_errors, "cxl_core"); -/* - * Internal errors are too device-specific to enable generally, however for CXL - * their behavior is standardized for conveying CXL protocol errors. +/** + * pci_aer_mask_internal_errors - mask internal errors + * @dev: pointer to the pci_dev data structure + * + * Mask internal errors in the Uncorrectable and Correctable Error + * Mask registers. + * + * Note: AER must be enabled and supported by the device which must be + * checked in advance, e.g. with pcie_aer_is_native(). */ -EXPORT_SYMBOL_FOR_MODULES(pci_aer_unmask_internal_errors, "cxl_core"); +void pci_aer_mask_internal_errors(struct pci_dev *dev) +{ + pci_clear_and_set_config_dword(dev, dev->aer_cap + PCI_ERR_UNCOR_MASK, + 0, PCI_ERR_UNC_INTN); + pci_clear_and_set_config_dword(dev, dev->aer_cap + PCI_ERR_COR_MASK, + 0, PCI_ERR_COR_INTERNAL); +} +EXPORT_SYMBOL_FOR_MODULES(pci_aer_mask_internal_errors, "cxl_core"); /** * pci_aer_handle_error - handle logging error into an event log diff --git a/include/linux/aer.h b/include/linux/aer.h index 8eba3192e2d15..b3657b80564b9 100644 --- a/include/linux/aer.h +++ b/include/linux/aer.h @@ -58,6 +58,7 @@ struct aer_capability_regs { int pci_aer_clear_nonfatal_status(struct pci_dev *dev); int pcie_aer_is_native(struct pci_dev *dev); void pci_aer_unmask_internal_errors(struct pci_dev *dev); +void pci_aer_mask_internal_errors(struct pci_dev *dev); #else static inline int pci_aer_clear_nonfatal_status(struct pci_dev *dev) { @@ -65,6 +66,7 @@ static inline int pci_aer_clear_nonfatal_status(struct pci_dev *dev) } static inline int pcie_aer_is_native(struct pci_dev *dev) { return 0; } static inline void pci_aer_unmask_internal_errors(struct pci_dev *dev) { } +static inline void pci_aer_mask_internal_errors(struct pci_dev *dev) { } #endif #ifdef CONFIG_CXL_RAS diff --git a/tools/testing/cxl/Kbuild b/tools/testing/cxl/Kbuild index 2be1df80fcc93..957945201f04d 100644 --- a/tools/testing/cxl/Kbuild +++ b/tools/testing/cxl/Kbuild @@ -6,6 +6,7 @@ ldflags-y += --wrap=acpi_pci_find_root ldflags-y += --wrap=nvdimm_bus_register ldflags-y += --wrap=cxl_await_media_ready ldflags-y += --wrap=devm_cxl_add_rch_dport +ldflags-y += --wrap=devm_cxl_dport_rch_ras_setup ldflags-y += --wrap=cxl_endpoint_parse_cdat ldflags-y += --wrap=devm_cxl_endpoint_decoders_setup ldflags-y += --wrap=hmat_get_extended_linear_cache_size diff --git a/tools/testing/cxl/test/mock.c b/tools/testing/cxl/test/mock.c index 6454b868b122c..5ad3243da8d29 100644 --- a/tools/testing/cxl/test/mock.c +++ b/tools/testing/cxl/test/mock.c @@ -220,6 +220,18 @@ struct cxl_dport *__wrap_devm_cxl_add_rch_dport(struct cxl_port *port, } EXPORT_SYMBOL_NS_GPL(__wrap_devm_cxl_add_rch_dport, "CXL"); +void __wrap_devm_cxl_dport_rch_ras_setup(struct cxl_dport *dport) +{ + int index; + struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); + + if (!ops || !ops->is_mock_port(dport->dport_dev)) + devm_cxl_dport_rch_ras_setup(dport); + + put_cxl_mock_ops(index); +} +EXPORT_SYMBOL_NS_GPL(__wrap_devm_cxl_dport_rch_ras_setup, "CXL"); + void __wrap_cxl_endpoint_parse_cdat(struct cxl_port *port) { int index; -- 2.34.1