]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/commitdiff
drivers: net: xgene: Fix module unload crash - hw resource cleanup
authorIyappan Subramanian <isubramanian@apm.com>
Tue, 26 Jul 2016 00:12:37 +0000 (17:12 -0700)
committerLuis Henriques <luis.henriques@canonical.com>
Tue, 8 Nov 2016 16:44:09 +0000 (16:44 +0000)
BugLink: https://launchpad.net/bugs/1632739
When the driver is configured as kernel module and when it gets
unloaded and reloaded, kernel crash was observed.  This patch
address the hardware resource cleanups by doing the following,

- Added mac_ops->clear() to do prefetch buffer clean up
- Fixed delete freepool buffers logic
- Reordered mac_enable and mac_disable
- Added Tx completion ring free
- Moved down delete_desc_rings after ring cleanup

Signed-off-by: Iyappan Subramanian <isubramanian@apm.com>
Tested-by: Fushen Chen <fchen@apm.com>
Tested-by: Toan Le <toanle@apm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
(cherry picked from commit cb11c062f9052c6bde6a5fa18cab1f41d81131b3 yakkety)
Signed-off-by: Craig Magina <craig.magina@canonical.com>
Acked-by: Tim Gardner <tim.gardner@canonical.com>
Acked-by: Seth Forshee <seth.forshee@canonical.com>
Signed-off-by: Seth Forshee <seth.forshee@canonical.com>
drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
drivers/net/ethernet/apm/xgene/xgene_enet_hw.h
drivers/net/ethernet/apm/xgene/xgene_enet_main.c
drivers/net/ethernet/apm/xgene/xgene_enet_main.h
drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c
drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c

index 5758d5c0cd9f72847c2664da1e64489b282399d6..c3fdcc5c7abbe61c4475062bf46b91f99407b9b3 100644 (file)
@@ -697,8 +697,48 @@ static int xgene_enet_reset(struct xgene_enet_pdata *pdata)
        return 0;
 }
 
+static void xgene_enet_clear(struct xgene_enet_pdata *pdata,
+                            struct xgene_enet_desc_ring *ring)
+{
+       u32 addr, val, data;
+
+       val = xgene_enet_ring_bufnum(ring->id);
+
+       if (xgene_enet_is_bufpool(ring->id)) {
+               addr = ENET_CFGSSQMIFPRESET_ADDR;
+               data = BIT(val - 0x20);
+       } else {
+               addr = ENET_CFGSSQMIWQRESET_ADDR;
+               data = BIT(val);
+       }
+
+       xgene_enet_wr_ring_if(pdata, addr, data);
+}
+
 static void xgene_gport_shutdown(struct xgene_enet_pdata *pdata)
 {
+       struct xgene_enet_desc_ring *ring;
+       u32 pb, val;
+       int i;
+
+       pb = 0;
+       for (i = 0; i < pdata->rxq_cnt; i++) {
+               ring = pdata->rx_ring[i]->buf_pool;
+
+               val = xgene_enet_ring_bufnum(ring->id);
+               pb |= BIT(val - 0x20);
+       }
+       xgene_enet_wr_ring_if(pdata, ENET_CFGSSQMIFPRESET_ADDR, pb);
+
+       pb = 0;
+       for (i = 0; i < pdata->txq_cnt; i++) {
+               ring = pdata->tx_ring[i];
+
+               val = xgene_enet_ring_bufnum(ring->id);
+               pb |= BIT(val);
+       }
+       xgene_enet_wr_ring_if(pdata, ENET_CFGSSQMIWQRESET_ADDR, pb);
+
        if (!IS_ERR(pdata->clk))
                clk_disable_unprepare(pdata->clk);
 }
@@ -901,6 +941,7 @@ struct xgene_mac_ops xgene_gmac_ops = {
 
 struct xgene_port_ops xgene_gport_ops = {
        .reset = xgene_enet_reset,
+       .clear = xgene_enet_clear,
        .cle_bypass = xgene_enet_cle_bypass,
        .shutdown = xgene_gport_shutdown,
 };
index 0892c1955a7bc79664f590250a22ccba840570f8..b44fcde69414a32a97a02e038a093d939baf0eb4 100644 (file)
@@ -167,6 +167,8 @@ enum xgene_enet_rm {
 #define TX_DV_GATE_EN0                 BIT(2)
 #define RX_DV_GATE_EN0                 BIT(1)
 #define RESUME_RX0                     BIT(0)
+#define ENET_CFGSSQMIFPRESET_ADDR              0x14
+#define ENET_CFGSSQMIWQRESET_ADDR              0x1c
 #define ENET_CFGSSQMIWQASSOC_ADDR              0xe0
 #define ENET_CFGSSQMIFPQASSOC_ADDR             0xdc
 #define ENET_CFGSSQMIQMLITEFPQASSOC_ADDR       0xf0
index a64a99334c706f998a340220535b31a684e9359a..61b813c7ac15e862d74a62be7d5f5e17f09a3360 100644 (file)
@@ -102,25 +102,13 @@ static u8 xgene_enet_hdr_len(const void *data)
 
 static void xgene_enet_delete_bufpool(struct xgene_enet_desc_ring *buf_pool)
 {
-       struct xgene_enet_pdata *pdata = netdev_priv(buf_pool->ndev);
-       struct xgene_enet_raw_desc16 *raw_desc;
-       u32 slots = buf_pool->slots - 1;
-       u32 tail = buf_pool->tail;
-       u32 userinfo;
-       int i, len;
-
-       len = pdata->ring_ops->len(buf_pool);
-       for (i = 0; i < len; i++) {
-               tail = (tail - 1) & slots;
-               raw_desc = &buf_pool->raw_desc16[tail];
+       int i;
 
-               /* Hardware stores descriptor in little endian format */
-               userinfo = GET_VAL(USERINFO, le64_to_cpu(raw_desc->m0));
-               dev_kfree_skb_any(buf_pool->rx_skb[userinfo]);
+       /* Free up the buffers held by hardware */
+       for (i = 0; i < buf_pool->slots; i++) {
+               if (buf_pool->rx_skb[i])
+                       dev_kfree_skb_any(buf_pool->rx_skb[i]);
        }
-
-       pdata->ring_ops->wr_cmd(buf_pool, -len);
-       buf_pool->tail = tail;
 }
 
 static irqreturn_t xgene_enet_rx_irq(const int irq, void *data)
@@ -481,6 +469,7 @@ static int xgene_enet_rx_frame(struct xgene_enet_desc_ring *rx_ring,
                         XGENE_ENET_MAX_MTU, DMA_FROM_DEVICE);
        skb_index = GET_VAL(USERINFO, le64_to_cpu(raw_desc->m0));
        skb = buf_pool->rx_skb[skb_index];
+       buf_pool->rx_skb[skb_index] = NULL;
 
        /* checking for error */
        status = (GET_VAL(ELERR, le64_to_cpu(raw_desc->m0)) << LERR_LEN) ||
@@ -720,9 +709,6 @@ static int xgene_enet_open(struct net_device *ndev)
        if (ret)
                return ret;
 
-       mac_ops->tx_enable(pdata);
-       mac_ops->rx_enable(pdata);
-
        xgene_enet_napi_enable(pdata);
        ret = xgene_enet_register_irq(ndev);
        if (ret)
@@ -735,6 +721,8 @@ static int xgene_enet_open(struct net_device *ndev)
                netif_carrier_off(ndev);
        }
 
+       mac_ops->tx_enable(pdata);
+       mac_ops->rx_enable(pdata);
        netif_start_queue(ndev);
 
        return ret;
@@ -747,15 +735,14 @@ static int xgene_enet_close(struct net_device *ndev)
        int i;
 
        netif_stop_queue(ndev);
+       mac_ops->tx_disable(pdata);
+       mac_ops->rx_disable(pdata);
 
        if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII)
                phy_stop(pdata->phy_dev);
        else
                cancel_delayed_work_sync(&pdata->link_work);
 
-       mac_ops->tx_disable(pdata);
-       mac_ops->rx_disable(pdata);
-
        xgene_enet_free_irq(ndev);
        xgene_enet_napi_disable(pdata);
        for (i = 0; i < pdata->rxq_cnt; i++)
@@ -785,6 +772,9 @@ static void xgene_enet_delete_desc_rings(struct xgene_enet_pdata *pdata)
                ring = pdata->tx_ring[i];
                if (ring) {
                        xgene_enet_delete_ring(ring);
+                       pdata->port_ops->clear(pdata, ring);
+                       if (pdata->cq_cnt)
+                               xgene_enet_delete_ring(ring->cp_ring);
                        pdata->tx_ring[i] = NULL;
                }
        }
@@ -795,6 +785,7 @@ static void xgene_enet_delete_desc_rings(struct xgene_enet_pdata *pdata)
                        buf_pool = ring->buf_pool;
                        xgene_enet_delete_bufpool(buf_pool);
                        xgene_enet_delete_ring(buf_pool);
+                       pdata->port_ops->clear(pdata, buf_pool);
                        xgene_enet_delete_ring(ring);
                        pdata->rx_ring[i] = NULL;
                }
@@ -1682,8 +1673,8 @@ static int xgene_enet_remove(struct platform_device *pdev)
        if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII)
                xgene_enet_mdio_remove(pdata);
        unregister_netdev(ndev);
-       xgene_enet_delete_desc_rings(pdata);
        pdata->port_ops->shutdown(pdata);
+       xgene_enet_delete_desc_rings(pdata);
        free_netdev(ndev);
 
        return 0;
index 0a0fd228b7bb4549e8acbe291998e309b3dc2117..9943cc262f0daf22d4460b7350851ad743a7ac32 100644 (file)
@@ -148,6 +148,8 @@ struct xgene_mac_ops {
 
 struct xgene_port_ops {
        int (*reset)(struct xgene_enet_pdata *pdata);
+       void (*clear)(struct xgene_enet_pdata *pdata,
+                     struct xgene_enet_desc_ring *ring);
        void (*cle_bypass)(struct xgene_enet_pdata *pdata,
                           u32 dst_ring_num, u16 bufpool_id);
        void (*shutdown)(struct xgene_enet_pdata *pdata);
index a0d308beaba2af1bd7cdb785d3e62a69ee777745..5a13047d001795c37c25d0ff9ae13b9d18f039af 100644 (file)
@@ -137,9 +137,17 @@ static u32 xgene_enet_rd_mac(struct xgene_enet_pdata *p, u32 rd_addr)
 static int xgene_enet_ecc_init(struct xgene_enet_pdata *p)
 {
        struct net_device *ndev = p->ndev;
-       u32 data;
+       u32 data, shutdown;
        int i = 0;
 
+       shutdown = xgene_enet_rd_diag_csr(p, ENET_CFG_MEM_RAM_SHUTDOWN_ADDR);
+       data = xgene_enet_rd_diag_csr(p, ENET_BLOCK_MEM_RDY_ADDR);
+
+       if (!shutdown && data == ~0U) {
+               netdev_dbg(ndev, "+ ecc_init done, skipping\n");
+               return 0;
+       }
+
        xgene_enet_wr_diag_csr(p, ENET_CFG_MEM_RAM_SHUTDOWN_ADDR, 0);
        do {
                usleep_range(100, 110);
@@ -464,10 +472,47 @@ static void xgene_enet_cle_bypass(struct xgene_enet_pdata *p,
        xgene_enet_wr_csr(p, cle_bypass_reg1 + offset, data);
 }
 
+static void xgene_enet_clear(struct xgene_enet_pdata *pdata,
+                            struct xgene_enet_desc_ring *ring)
+{
+       u32 addr, val, data;
+
+       val = xgene_enet_ring_bufnum(ring->id);
+
+       if (xgene_enet_is_bufpool(ring->id)) {
+               addr = ENET_CFGSSQMIFPRESET_ADDR;
+               data = BIT(val - 0x20);
+       } else {
+               addr = ENET_CFGSSQMIWQRESET_ADDR;
+               data = BIT(val);
+       }
+
+       xgene_enet_wr_ring_if(pdata, addr, data);
+}
+
 static void xgene_enet_shutdown(struct xgene_enet_pdata *p)
 {
-       if (!IS_ERR(p->clk))
-               clk_disable_unprepare(p->clk);
+       struct xgene_enet_desc_ring *ring;
+       u32 pb, val;
+       int i;
+
+       pb = 0;
+       for (i = 0; i < p->rxq_cnt; i++) {
+               ring = p->rx_ring[i]->buf_pool;
+
+               val = xgene_enet_ring_bufnum(ring->id);
+               pb |= BIT(val - 0x20);
+       }
+       xgene_enet_wr_ring_if(p, ENET_CFGSSQMIFPRESET_ADDR, pb);
+
+       pb = 0;
+       for (i = 0; i < p->txq_cnt; i++) {
+               ring = p->tx_ring[i];
+
+               val = xgene_enet_ring_bufnum(ring->id);
+               pb |= BIT(val);
+       }
+       xgene_enet_wr_ring_if(p, ENET_CFGSSQMIWQRESET_ADDR, pb);
 }
 
 static void xgene_enet_link_state(struct work_struct *work)
@@ -515,6 +560,7 @@ struct xgene_mac_ops xgene_sgmac_ops = {
 
 struct xgene_port_ops xgene_sgport_ops = {
        .reset          = xgene_enet_reset,
+       .clear          = xgene_enet_clear,
        .cle_bypass     = xgene_enet_cle_bypass,
        .shutdown       = xgene_enet_shutdown
 };
index 7a28a48cb2c779816ed92b8c3616c4000129cbc2..9a5d56492054341548f12ce35cfe185ba484fd7f 100644 (file)
@@ -292,8 +292,45 @@ static void xgene_enet_xgcle_bypass(struct xgene_enet_pdata *pdata,
 
 static void xgene_enet_shutdown(struct xgene_enet_pdata *pdata)
 {
-       if (!IS_ERR(pdata->clk))
-               clk_disable_unprepare(pdata->clk);
+       struct xgene_enet_desc_ring *ring;
+       u32 pb, val;
+       int i;
+
+       pb = 0;
+       for (i = 0; i < pdata->rxq_cnt; i++) {
+               ring = pdata->rx_ring[i]->buf_pool;
+
+               val = xgene_enet_ring_bufnum(ring->id);
+               pb |= BIT(val - 0x20);
+       }
+       xgene_enet_wr_ring_if(pdata, ENET_CFGSSQMIFPRESET_ADDR, pb);
+
+       pb = 0;
+       for (i = 0; i < pdata->txq_cnt; i++) {
+               ring = pdata->tx_ring[i];
+
+               val = xgene_enet_ring_bufnum(ring->id);
+               pb |= BIT(val);
+       }
+       xgene_enet_wr_ring_if(pdata, ENET_CFGSSQMIWQRESET_ADDR, pb);
+}
+
+static void xgene_enet_clear(struct xgene_enet_pdata *pdata,
+                            struct xgene_enet_desc_ring *ring)
+{
+       u32 addr, val, data;
+
+       val = xgene_enet_ring_bufnum(ring->id);
+
+       if (xgene_enet_is_bufpool(ring->id)) {
+               addr = ENET_CFGSSQMIFPRESET_ADDR;
+               data = BIT(val - 0x20);
+       } else {
+               addr = ENET_CFGSSQMIWQRESET_ADDR;
+               data = BIT(val);
+       }
+
+       xgene_enet_wr_ring_if(pdata, addr, data);
 }
 
 static void xgene_enet_link_state(struct work_struct *work)
@@ -340,6 +377,7 @@ struct xgene_mac_ops xgene_xgmac_ops = {
 
 struct xgene_port_ops xgene_xgport_ops = {
        .reset = xgene_enet_reset,
+       .clear = xgene_enet_clear,
        .cle_bypass = xgene_enet_xgcle_bypass,
        .shutdown = xgene_enet_shutdown,
 };