Re: [RFC PATCH 7/9] cxl/pci: Add atomic notifier callback for CXL PCIe port AER internal errors

Li, Ming4
2024-06-26T06:23:13Z

On 6/18/2024 4:04 AM, Terry Bowman wrote:
> CXL root ports, CXL downstream switch ports, and CXL upstream switch
> ports are bound to the PCIe port bus driver, portdrv. portdrv provides
> an atomic notifier chain for reporting PCIe port device AER
> correctable internal errors (CIE) and AER uncorrectable internal
> errors (UIE).
>
> CXL PCIe port devices use AER CIE/UIE to report CXL RAS.[1]
>
> Add a cxl_pci atomic notification callback for handling the portdrv's
> AER UIE/CIE notifications.
>
> Register the atomic notification callback in the cxl_pci module's
> load. Unregister the callback in the cxl_pci driver's unload.
>
> Implement the callback to check if the device parameter is a valid
> CXL PCIe port. If it is valid then make the notification callback call
> __cxl_handle_cor_ras() or __cxl_handle_ras() depending on the AER
> type.
>
> [1] CXL3.1 - 12.2.2 CXL Root Ports, Downstream Switch Ports, and
> Upstream Switch Ports
>
> Signed-off-by: Terry Bowman <terry.bowman@amd.com>
> ---
> drivers/cxl/core/core.h | 4 ++
> drivers/cxl/core/pci.c | 97 ++++++++++++++++++++++++++++++++++++++---
> drivers/cxl/core/port.c | 6 +--
> drivers/cxl/cxl.h | 5 +++
> drivers/cxl/cxlpci.h | 2 +
> drivers/cxl/pci.c | 19 +++++++-
> 6 files changed, 123 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
> index bc5a95665aa0..69bef1db6ee0 100644
> --- a/drivers/cxl/core/core.h
> +++ b/drivers/cxl/core/core.h
> @@ -94,4 +94,8 @@ int cxl_update_hmat_access_coordinates(int nid, struct cxl_region *cxlr,
> enum access_coordinate_class access);
> bool cxl_need_node_perf_attrs_update(int nid);
>
> +struct cxl_dport *find_dport(struct cxl_port *port, int id);
> +struct cxl_port *find_cxl_port(struct device *dport_dev,
> + struct cxl_dport **dport);
> +
> #endif /* __CXL_CORE_H__ */
> diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c
> index 59a317ab84bb..e630eccb733d 100644
> --- a/drivers/cxl/core/pci.c
> +++ b/drivers/cxl/core/pci.c
> @@ -689,7 +689,6 @@ EXPORT_SYMBOL_NS_GPL(read_cdat_data, CXL);
> static void __cxl_handle_cor_ras(struct device *dev,
> void __iomem *ras_base)
> {
> - struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
> void __iomem *addr;
> u32 status;
>
> @@ -698,10 +697,17 @@ static void __cxl_handle_cor_ras(struct device *dev,
>
> addr = ras_base + CXL_RAS_CORRECTABLE_STATUS_OFFSET;
> status = readl(addr);
> - if (status & CXL_RAS_CORRECTABLE_STATUS_MASK) {
> - writel(status & CXL_RAS_CORRECTABLE_STATUS_MASK, addr);
> +
> + if (!(status & CXL_RAS_CORRECTABLE_STATUS_MASK))
> + return;
> +
> + writel(status & CXL_RAS_CORRECTABLE_STATUS_MASK, addr);
> + if (is_cxl_memdev(dev)) {
> + struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
> +
> trace_cxl_aer_correctable_error(cxlmd, status);
> - }
> + } else if (dev_is_pci(dev))
> + trace_cxl_port_aer_correctable_error(dev, status);
> }
>
> static void cxl_handle_endpoint_cor_ras(struct cxl_dev_state *cxlds)
> @@ -733,7 +739,6 @@ static void header_log_copy(void __iomem *ras_base, u32 *log)
> static bool __cxl_handle_ras(struct device *dev,
> void __iomem *ras_base)
> {
> - struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
> u32 hl[CXL_HEADERLOG_SIZE_U32];
> void __iomem *addr;
> u32 status;
> @@ -759,7 +764,13 @@ static bool __cxl_handle_ras(struct device *dev,
> }
>
> header_log_copy(ras_base, hl);
> - trace_cxl_aer_uncorrectable_error(cxlmd, status, fe, hl);
> + if (is_cxl_memdev(dev)) {
> + struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
> +
> + trace_cxl_aer_uncorrectable_error(cxlmd, status, fe, hl);
> + } else if (dev_is_pci(dev))
> + trace_cxl_port_aer_uncorrectable_error(dev, status);
> +
> writel(status & CXL_RAS_UNCORRECTABLE_STATUS_MASK, addr);
>
> return true;
> @@ -882,6 +893,80 @@ static bool cxl_handle_rdport_ras(struct cxl_dev_state *cxlds,
> return __cxl_handle_ras(&cxlds->cxlmd->dev, dport->regs.ras);
> }
>
> +static int match_uport(struct device *dev, void *data)
> +{
> + struct device *uport_dev = (struct device *)data;
> + struct cxl_port *port;
> +
> + if (!is_cxl_port(dev))
> + return 0;
> +
> + port = to_cxl_port(dev);
> +
> + return (port->uport_dev == uport_dev);
> +}
> +
> +static struct cxl_port *pci_to_cxl_uport(struct pci_dev *pdev)
> +{
> + struct cxl_dport *dport;
> + struct device *port_dev;
> + struct cxl_port *port;
> +
> + port = find_cxl_port(pdev->dev.parent, &dport);
> + if (!port)
> + return NULL;
> + put_device(&port->dev);
> +
> + port_dev = device_find_child(&port->dev, &pdev->dev, match_uport);
> + if (!port_dev)
> + return NULL;

seems like just a bus_find_device(&cxl_bus_type, NULL, &pdev->dev, match_uport) can replace these find_cxl_port() and device_find_child().

> + put_device(port_dev);
> +
> + port = to_cxl_port(port_dev);
> +
> + return port;
> +}
> +
> +static void __iomem *cxl_pci_port_ras(struct pci_dev *pdev)
> +{
> + void __iomem *ras_base = NULL;
> +
> + if ((pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT) ||
> + (pci_pcie_type(pdev) == PCI_EXP_TYPE_DOWNSTREAM)) {
> + struct cxl_dport *dport;
> +
> + find_cxl_port(&pdev->dev, &dport);
> + ras_base = dport ? dport->regs.ras : NULL;

Need put_device(&port->dev) after find_cxl_port(), use scope-based resource management __free() here should be better.

> + } else if (pci_pcie_type(pdev) == PCI_EXP_TYPE_UPSTREAM) {
> + struct cxl_port *port = pci_to_cxl_uport(pdev);
> +
> + ras_base = port ? port->regs.ras : NULL;
> + }
> +
> + return ras_base;
> +}
> +
> +int port_internal_err_cb(struct notifier_block *unused,
> + unsigned long event, void *ptr)
> +{
> + struct pci_dev *pdev = (struct pci_dev *)ptr;
> + void __iomem *ras_base;
> +
> + if (!pdev)
> + return 0;
> +
> + if (event == AER_CORRECTABLE) {
> + ras_base = cxl_pci_port_ras(pdev);
> + __cxl_handle_cor_ras(&pdev->dev, ras_base);
> + } else if ((event == AER_FATAL) || (event == AER_NONFATAL)) {
> + ras_base = cxl_pci_port_ras(pdev);
> + __cxl_handle_ras(&pdev->dev, ras_base);
> + }
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_NS_GPL(port_internal_err_cb, CXL);
> +
> /*
> * Copy the AER capability registers using 32 bit read accesses.
> * This is necessary because RCRB AER capability is MMIO mapped. Clear the
> diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> index 887ed6e358fb..d0f95c965ab4 100644
> --- a/drivers/cxl/core/port.c
> +++ b/drivers/cxl/core/port.c
> @@ -1027,7 +1027,7 @@ void put_cxl_root(struct cxl_root *cxl_root)
> }
> EXPORT_SYMBOL_NS_GPL(put_cxl_root, CXL);
>
> -static struct cxl_dport *find_dport(struct cxl_port *port, int id)
> +struct cxl_dport *find_dport(struct cxl_port *port, int id)
> {
> struct cxl_dport *dport;
> unsigned long index;
> @@ -1336,8 +1336,8 @@ static struct cxl_port *__find_cxl_port(struct cxl_find_port_ctx *ctx)
> return NULL;
> }
>
> -static struct cxl_port *find_cxl_port(struct device *dport_dev,
> - struct cxl_dport **dport)
> +struct cxl_port *find_cxl_port(struct device *dport_dev,
> + struct cxl_dport **dport)
> {
> struct cxl_find_port_ctx ctx = {
> .dport_dev = dport_dev,
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index 7cee678fdb75..04725344393b 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -11,6 +11,7 @@
> #include <linux/log2.h>
> #include <linux/node.h>
> #include <linux/io.h>
> +#include "../pci/pcie/portdrv.h"
>
> /**
> * DOC: cxl objects
> @@ -760,11 +761,15 @@ struct cxl_dport *devm_cxl_add_rch_dport(struct cxl_port *port,
> #ifdef CONFIG_PCIEAER_CXL
> void cxl_setup_parent_dport(struct device *host, struct cxl_dport *dport);
> void cxl_setup_parent_uport(struct device *host, struct cxl_port *port);
> +int port_internal_err_cb(struct notifier_block *unused,
> + unsigned long event, void *ptr);
> #else
> static inline void cxl_setup_parent_dport(struct device *host,
> struct cxl_dport *dport) { }
> static inline void cxl_setup_parent_uport(struct device *host,
> struct cxl_port *port) { }
> +static inline int port_internal_err_cb(struct notifier_block *unused,
> + unsigned long event, void *ptr) { return 0; }
> #endif
>
> struct cxl_decoder *to_cxl_decoder(struct device *dev);
> diff --git a/drivers/cxl/cxlpci.h b/drivers/cxl/cxlpci.h
> index 93992a1c8eec..6044955e1c48 100644
> --- a/drivers/cxl/cxlpci.h
> +++ b/drivers/cxl/cxlpci.h
> @@ -130,4 +130,6 @@ void read_cdat_data(struct cxl_port *port);
> void cxl_cor_error_detected(struct pci_dev *pdev);
> pci_ers_result_t cxl_error_detected(struct pci_dev *pdev,
> pci_channel_state_t state);
> +int port_err_nb_cb(struct notifier_block *unused,
> + unsigned long event, void *ptr);
> #endif /* __CXL_PCI_H__ */
> diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
> index 2ff361e756d6..f4183c5aea38 100644
> --- a/drivers/cxl/pci.c
> +++ b/drivers/cxl/pci.c
> @@ -926,6 +926,10 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> return rc;
> }
>
> +struct notifier_block port_internal_err_nb = {
> + .notifier_call = port_internal_err_cb,
> +};
> +
> static const struct pci_device_id cxl_mem_pci_tbl[] = {
> /* PCI class code for CXL.mem Type-3 Devices */
> { PCI_DEVICE_CLASS((PCI_CLASS_MEMORY_CXL << 8 | CXL_MEMORY_PROGIF), ~0)},
> @@ -974,6 +978,19 @@ static struct pci_driver cxl_pci_driver = {
> },
> };
>
> -module_pci_driver(cxl_pci_driver);
> +static int __init cxl_pci_init(void)
> +{
> + atomic_notifier_chain_register(&portdrv_aer_internal_err_chain, &port_internal_err_nb);
> + return pci_register_driver(&cxl_pci_driver);
> +}
> +module_init(cxl_pci_init);
> +
> +static void __exit cxl_pci_exit(void)
> +{
> + atomic_notifier_chain_unregister(&portdrv_aer_internal_err_chain, &port_internal_err_nb);
> + pci_unregister_driver(&cxl_pci_driver);
> +}
> +module_exit(cxl_pci_exit);
> +
> MODULE_LICENSE("GPL v2");
> MODULE_IMPORT_NS(CXL);