From: Qiuxu Zhuo <[email protected]>

When attempting error recovery for an RCiEP associated with an RCEC device,
there needs to be a way to update the Root Error Status, the Uncorrectable
Error Status and the Uncorrectable Error Severity of the parent RCEC.
So add the 'rcec' field to the pci_dev structure and provide a hook for the
Root Port Driver to associate RCiEPs with their respective parent RCEC.

Co-developed-by: Sean V Kelley <[email protected]>
Signed-off-by: Qiuxu Zhuo <[email protected]>
Signed-off-by: Sean V Kelley <[email protected]>
---
 drivers/pci/pcie/aer.c         |  9 +++++----
 drivers/pci/pcie/err.c         |  9 +++++++++
 drivers/pci/pcie/portdrv_pci.c | 15 +++++++++++++++
 include/linux/pci.h            |  3 +++
 4 files changed, 32 insertions(+), 4 deletions(-)

diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c
index 3acf56683915..f1bf06be449e 100644
--- a/drivers/pci/pcie/aer.c
+++ b/drivers/pci/pcie/aer.c
@@ -1335,17 +1335,18 @@ static int aer_probe(struct pcie_device *dev)
 static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
 {
        int aer = dev->aer_cap;
+       int rc = 0;
        u32 reg32;
-       int rc;
-
 
        /* Disable Root's interrupt in response to error messages */
        pci_read_config_dword(dev, aer + PCI_ERR_ROOT_COMMAND, &reg32);
        reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK;
        pci_write_config_dword(dev, aer + PCI_ERR_ROOT_COMMAND, reg32);
 
-       rc = pci_bus_error_reset(dev);
-       pci_info(dev, "Root Port link has been reset\n");
+       if (pci_pcie_type(dev) != PCI_EXP_TYPE_RC_EC) {
+               rc = pci_bus_error_reset(dev);
+               pci_info(dev, "Root Port link has been reset\n");
+       }
 
        /* Clear Root Error Status */
        pci_read_config_dword(dev, aer + PCI_ERR_ROOT_STATUS, &reg32);
diff --git a/drivers/pci/pcie/err.c b/drivers/pci/pcie/err.c
index 9b3ec94bdf1d..0aae7643132e 100644
--- a/drivers/pci/pcie/err.c
+++ b/drivers/pci/pcie/err.c
@@ -203,6 +203,11 @@ pci_ers_result_t pcie_do_recovery(struct pci_dev *dev,
                pci_walk_dev_affected(dev, report_frozen_detected, &status);
                if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END) {
                        status = flr_on_rciep(dev);
+                       /*
+                        * The callback only clears the Root Error Status
+                        * of the RCEC (see aer.c).
+                        */
+                       reset_link(dev->rcec);
                        if (status != PCI_ERS_RESULT_RECOVERED) {
                                pci_warn(dev, "function level reset failed\n");
                                goto failed;
@@ -246,7 +251,11 @@ pci_ers_result_t pcie_do_recovery(struct pci_dev *dev,
             pci_pcie_type(dev) == PCI_EXP_TYPE_RC_EC)) {
                pci_aer_clear_device_status(dev);
                pci_aer_clear_nonfatal_status(dev);
+       } else if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END) {
+               pci_aer_clear_device_status(dev->rcec);
+               pci_aer_clear_nonfatal_status(dev->rcec);
        }
+
        pci_info(dev, "device recovery successful\n");
        return status;
 
diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c
index d5b109499b10..f9409a0110c2 100644
--- a/drivers/pci/pcie/portdrv_pci.c
+++ b/drivers/pci/pcie/portdrv_pci.c
@@ -90,6 +90,18 @@ static const struct dev_pm_ops pcie_portdrv_pm_ops = {
 #define PCIE_PORTDRV_PM_OPS    NULL
 #endif /* !PM */
 
+static int pcie_hook_rcec(struct pci_dev *pdev, void *data)
+{
+       struct pci_dev *rcec = (struct pci_dev *)data;
+
+       pdev->rcec = rcec;
+       pci_info(rcec, "RCiEP(under an RCEC) %04x:%02x:%02x.%d\n",
+                pci_domain_nr(pdev->bus), pdev->bus->number,
+                PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
+
+       return 0;
+}
+
 /*
  * pcie_portdrv_probe - Probe PCI-Express port devices
  * @dev: PCI-Express port device being probed
@@ -110,6 +122,9 @@ static int pcie_portdrv_probe(struct pci_dev *dev,
             (pci_pcie_type(dev) != PCI_EXP_TYPE_RC_EC)))
                return -ENODEV;
 
+       if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_EC)
+               pcie_walk_rcec(dev, pcie_hook_rcec, dev);
+
        status = pcie_port_device_register(dev);
        if (status)
                return status;
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 34c1c4f45288..e920f29df40b 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -326,6 +326,9 @@ struct pci_dev {
 #ifdef CONFIG_PCIEAER
        u16             aer_cap;        /* AER capability offset */
        struct aer_stats *aer_stats;    /* AER stats for this device */
+#endif
+#ifdef CONFIG_PCIEPORTBUS
+       struct pci_dev  *rcec;          /* Associated RCEC device */
 #endif
        u8              pcie_cap;       /* PCIe capability offset */
        u8              msi_cap;        /* MSI capability offset */
-- 
2.27.0

Reply via email to