From: Greg Kroah-Hartman <gre...@linuxfoundation.org>

From: Vladimir Oltean <vladimir.olt...@nxp.com>

[ Upstream commit 3222b5b613db558e9a494bbf53f3c984d90f71ea ]

Michael reports that since linux-next-20210211, the AER messages for ECC
errors have started reappearing, and this time they can be reliably
reproduced with the first ping on one of his LS1028A boards.

$ ping 1[   33.258069] pcieport 0000:00:1f.0: AER: Multiple Corrected error 
received: 0000:00:00.0
72.16.0.1
PING [   33.267050] pcieport 0000:00:1f.0: AER: can't find device of ID0000
172.16.0.1 (172.16.0.1): 56 data bytes
64 bytes from 172.16.0.1: seq=0 ttl=64 time=17.124 ms
64 bytes from 172.16.0.1: seq=1 ttl=64 time=0.273 ms

$ devmem 0x1f8010e10 32
0xC0000006

It isn't clear why this is necessary, but it seems that for the errors
to go away, we must clear the entire RFS and RSS memory, not just for
the ports in use.

Sadly the code is structured in such a way that we can't have unified
logic for the used and unused ports. For the minimal initialization of
an unused port, we need just to enable and ioremap the PF memory space,
and a control buffer descriptor ring. Unused ports must then free the
CBDR because the driver will exit, but used ports can not pick up from
where that code path left, since the CBDR API does not reinitialize a
ring when setting it up, so its producer and consumer indices are out of
sync between the software and hardware state. So a separate
enetc_init_unused_port function was created, and it gets called right
after the PF memory space is enabled.

Fixes: 07bf34a50e32 ("net: enetc: initialize the RFS and RSS memories")
Reported-by: Michael Walle <mich...@walle.cc>
Cc: Jesse Brandeburg <jesse.brandeb...@intel.com>
Signed-off-by: Vladimir Oltean <vladimir.olt...@nxp.com>
Tested-by: Michael Walle <mich...@walle.cc>
Signed-off-by: David S. Miller <da...@davemloft.net>
Signed-off-by: Sasha Levin <sas...@kernel.org>
---
 drivers/net/ethernet/freescale/enetc/enetc.c  |  8 ++---
 drivers/net/ethernet/freescale/enetc/enetc.h  |  4 +++
 .../net/ethernet/freescale/enetc/enetc_pf.c   | 33 ++++++++++++++++---
 3 files changed, 36 insertions(+), 9 deletions(-)

diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c 
b/drivers/net/ethernet/freescale/enetc/enetc.c
index 3e24e78c0dcd..68e9825b86b9 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc.c
@@ -942,7 +942,7 @@ static void enetc_free_rxtx_rings(struct enetc_ndev_priv 
*priv)
                enetc_free_tx_ring(priv->tx_ring[i]);
 }
 
-static int enetc_alloc_cbdr(struct device *dev, struct enetc_cbdr *cbdr)
+int enetc_alloc_cbdr(struct device *dev, struct enetc_cbdr *cbdr)
 {
        int size = cbdr->bd_count * sizeof(struct enetc_cbd);
 
@@ -963,7 +963,7 @@ static int enetc_alloc_cbdr(struct device *dev, struct 
enetc_cbdr *cbdr)
        return 0;
 }
 
-static void enetc_free_cbdr(struct device *dev, struct enetc_cbdr *cbdr)
+void enetc_free_cbdr(struct device *dev, struct enetc_cbdr *cbdr)
 {
        int size = cbdr->bd_count * sizeof(struct enetc_cbd);
 
@@ -971,7 +971,7 @@ static void enetc_free_cbdr(struct device *dev, struct 
enetc_cbdr *cbdr)
        cbdr->bd_base = NULL;
 }
 
-static void enetc_setup_cbdr(struct enetc_hw *hw, struct enetc_cbdr *cbdr)
+void enetc_setup_cbdr(struct enetc_hw *hw, struct enetc_cbdr *cbdr)
 {
        /* set CBDR cache attributes */
        enetc_wr(hw, ENETC_SICAR2,
@@ -991,7 +991,7 @@ static void enetc_setup_cbdr(struct enetc_hw *hw, struct 
enetc_cbdr *cbdr)
        cbdr->cir = hw->reg + ENETC_SICBDRCIR;
 }
 
-static void enetc_clear_cbdr(struct enetc_hw *hw)
+void enetc_clear_cbdr(struct enetc_hw *hw)
 {
        enetc_wr(hw, ENETC_SICBDRMR, 0);
 }
diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h 
b/drivers/net/ethernet/freescale/enetc/enetc.h
index ef0a67bc32ce..b8801a2b6a02 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc.h
@@ -237,6 +237,10 @@ int enetc_setup_tc(struct net_device *ndev, enum 
tc_setup_type type,
 void enetc_set_ethtool_ops(struct net_device *ndev);
 
 /* control buffer descriptor ring (CBDR) */
+int enetc_alloc_cbdr(struct device *dev, struct enetc_cbdr *cbdr);
+void enetc_free_cbdr(struct device *dev, struct enetc_cbdr *cbdr);
+void enetc_setup_cbdr(struct enetc_hw *hw, struct enetc_cbdr *cbdr);
+void enetc_clear_cbdr(struct enetc_hw *hw);
 int enetc_set_mac_flt_entry(struct enetc_si *si, int index,
                            char *mac_addr, int si_map);
 int enetc_clear_mac_flt_entry(struct enetc_si *si, int index);
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c 
b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
index 955383307b93..ac62464e0416 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
@@ -854,6 +854,26 @@ static int enetc_init_port_rss_memory(struct enetc_si *si)
        return err;
 }
 
+static void enetc_init_unused_port(struct enetc_si *si)
+{
+       struct device *dev = &si->pdev->dev;
+       struct enetc_hw *hw = &si->hw;
+       int err;
+
+       si->cbd_ring.bd_count = ENETC_CBDR_DEFAULT_SIZE;
+       err = enetc_alloc_cbdr(dev, &si->cbd_ring);
+       if (err)
+               return;
+
+       enetc_setup_cbdr(hw, &si->cbd_ring);
+
+       enetc_init_port_rfs_memory(si);
+       enetc_init_port_rss_memory(si);
+
+       enetc_clear_cbdr(hw);
+       enetc_free_cbdr(dev, &si->cbd_ring);
+}
+
 static int enetc_pf_probe(struct pci_dev *pdev,
                          const struct pci_device_id *ent)
 {
@@ -863,11 +883,6 @@ static int enetc_pf_probe(struct pci_dev *pdev,
        struct enetc_pf *pf;
        int err;
 
-       if (pdev->dev.of_node && !of_device_is_available(pdev->dev.of_node)) {
-               dev_info(&pdev->dev, "device is disabled, skipping\n");
-               return -ENODEV;
-       }
-
        err = enetc_pci_probe(pdev, KBUILD_MODNAME, sizeof(*pf));
        if (err) {
                dev_err(&pdev->dev, "PCI probing failed\n");
@@ -881,6 +896,13 @@ static int enetc_pf_probe(struct pci_dev *pdev,
                goto err_map_pf_space;
        }
 
+       if (pdev->dev.of_node && !of_device_is_available(pdev->dev.of_node)) {
+               enetc_init_unused_port(si);
+               dev_info(&pdev->dev, "device is disabled, skipping\n");
+               err = -ENODEV;
+               goto err_device_disabled;
+       }
+
        pf = enetc_si_priv(si);
        pf->si = si;
        pf->total_vfs = pci_sriov_get_totalvfs(pdev);
@@ -960,6 +982,7 @@ err_alloc_si_res:
        si->ndev = NULL;
        free_netdev(ndev);
 err_alloc_netdev:
+err_device_disabled:
 err_map_pf_space:
        enetc_pci_remove(pdev);
 
-- 
2.30.1



Reply via email to