aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorReece R. Pollack2015-02-16 13:35:36 -0600
committerReece R. Pollack2015-02-16 16:30:35 -0600
commit1cd9632166b2863dfe5bebe8f19ce085cf9fb754 (patch)
tree5e6478c40765c124b9bd3f73d702d7ae3a8e2867
parent760a81522f9b37745a9fde6afd21a727c8f3cbbd (diff)
downloadlinux-1cd9632166b2863dfe5bebe8f19ce085cf9fb754.tar.gz
linux-1cd9632166b2863dfe5bebe8f19ce085cf9fb754.tar.xz
linux-1cd9632166b2863dfe5bebe8f19ce085cf9fb754.zip
net: keystone: Fix memory leak in CPSW ALE configuration
Prior to this patch, the CPSW ALE context was created when the first interface on a device was opened, but it was not destroyed when the last interface was closed (it was stopped instead). This resulted in a memory leak. This patch corrects this. Also, the ALE Slaves were being shut down after the ALE context was being destroyed. This could result in a NULL pointer reference. This patch fixes this too. Finally, the ALE reference count in keystone_xgess.c was a non-atomic integer, while the other modules used an atomic. This patch fixes this and includes some minor restructuring so the code looks more like the other two modules. Signed-off-by: Reece R. Pollack <x0183204@ti.com>
-rw-r--r--drivers/net/ethernet/ti/keystone_ethss.c10
-rw-r--r--drivers/net/ethernet/ti/keystone_ethss2.c10
-rw-r--r--drivers/net/ethernet/ti/keystone_xgess.c103
3 files changed, 64 insertions, 59 deletions
diff --git a/drivers/net/ethernet/ti/keystone_ethss.c b/drivers/net/ethernet/ti/keystone_ethss.c
index a6c55d22e2e..dd3dc51ac8f 100644
--- a/drivers/net/ethernet/ti/keystone_ethss.c
+++ b/drivers/net/ethernet/ti/keystone_ethss.c
@@ -3146,11 +3146,13 @@ static int cpsw_close(void *intf_priv, struct net_device *ndev)
3146 3146
3147 del_timer_sync(&cpsw_intf->timer); 3147 del_timer_sync(&cpsw_intf->timer);
3148 3148
3149 if (atomic_dec_return(&cpsw_dev->ale_refcnt) == 0)
3150 cpsw_ale_stop(cpsw_dev->ale);
3151
3152 for_each_slave(cpsw_intf, cpsw_slave_stop, cpsw_intf); 3149 for_each_slave(cpsw_intf, cpsw_slave_stop, cpsw_intf);
3153 3150
3151 if (atomic_dec_return(&cpsw_dev->ale_refcnt) == 0) {
3152 cpsw_ale_destroy(cpsw_dev->ale);
3153 cpsw_dev->ale = NULL;
3154 }
3155
3154 if(!cpsw_dev->force_no_hwtstamp) 3156 if(!cpsw_dev->force_no_hwtstamp)
3155 netcp_unregister_rxhook(netcp, CPSW_RXHOOK_ORDER, 3157 netcp_unregister_rxhook(netcp, CPSW_RXHOOK_ORDER,
3156 cpsw_rx_hook, cpsw_intf); 3158 cpsw_rx_hook, cpsw_intf);
@@ -3174,8 +3176,6 @@ static int cpsw_remove(struct netcp_device *netcp_device, void *inst_priv)
3174 3176
3175 of_node_put(cpsw_dev->interfaces); 3177 of_node_put(cpsw_dev->interfaces);
3176 3178
3177 cpsw_ale_destroy(cpsw_dev->ale);
3178
3179 list_for_each_entry_safe(cpsw_intf, tmp, &cpsw_dev->cpsw_intf_head, 3179 list_for_each_entry_safe(cpsw_intf, tmp, &cpsw_dev->cpsw_intf_head,
3180 cpsw_intf_list) { 3180 cpsw_intf_list) {
3181 netcp_delete_interface(netcp_device, cpsw_intf->ndev); 3181 netcp_delete_interface(netcp_device, cpsw_intf->ndev);
diff --git a/drivers/net/ethernet/ti/keystone_ethss2.c b/drivers/net/ethernet/ti/keystone_ethss2.c
index f334bdb3ab5..5560d6e2c00 100644
--- a/drivers/net/ethernet/ti/keystone_ethss2.c
+++ b/drivers/net/ethernet/ti/keystone_ethss2.c
@@ -3510,11 +3510,13 @@ static int cpsw2_close(void *intf_priv, struct net_device *ndev)
3510 3510
3511 del_timer_sync(&cpsw_intf->timer); 3511 del_timer_sync(&cpsw_intf->timer);
3512 3512
3513 if (atomic_dec_return(&cpsw_dev->ale_refcnt) == 0)
3514 cpsw_ale_stop(cpsw_dev->ale);
3515
3516 for_each_slave(cpsw_intf, cpsw2_slave_stop, cpsw_intf); 3513 for_each_slave(cpsw_intf, cpsw2_slave_stop, cpsw_intf);
3517 3514
3515 if (atomic_dec_return(&cpsw_dev->ale_refcnt) == 0) {
3516 cpsw_ale_destroy(cpsw_dev->ale);
3517 cpsw_dev->ale = NULL;
3518 }
3519
3518 if (!cpsw_dev->force_no_hwtstamp) 3520 if (!cpsw_dev->force_no_hwtstamp)
3519 netcp_unregister_rxhook(netcp, CPSW2_RXHOOK_ORDER, 3521 netcp_unregister_rxhook(netcp, CPSW2_RXHOOK_ORDER,
3520 cpsw2_rx_hook, cpsw_intf); 3522 cpsw2_rx_hook, cpsw_intf);
@@ -3538,8 +3540,6 @@ static int cpsw2_remove(struct netcp_device *netcp_device, void *inst_priv)
3538 3540
3539 of_node_put(cpsw_dev->interfaces); 3541 of_node_put(cpsw_dev->interfaces);
3540 3542
3541 cpsw_ale_destroy(cpsw_dev->ale);
3542
3543 list_for_each_entry_safe(cpsw_intf, tmp, &cpsw_dev->cpsw_intf_head, 3543 list_for_each_entry_safe(cpsw_intf, tmp, &cpsw_dev->cpsw_intf_head,
3544 cpsw_intf_list) { 3544 cpsw_intf_list) {
3545 netcp_delete_interface(netcp_device, cpsw_intf->ndev); 3545 netcp_delete_interface(netcp_device, cpsw_intf->ndev);
diff --git a/drivers/net/ethernet/ti/keystone_xgess.c b/drivers/net/ethernet/ti/keystone_xgess.c
index 7c7c231b261..e683d591016 100644
--- a/drivers/net/ethernet/ti/keystone_xgess.c
+++ b/drivers/net/ethernet/ti/keystone_xgess.c
@@ -276,11 +276,11 @@ struct cpswx_priv {
276 struct cpswx_host_regs __iomem *host_port_regs; 276 struct cpswx_host_regs __iomem *host_port_regs;
277 struct cpswx_ale_regs __iomem *ale_reg; 277 struct cpswx_ale_regs __iomem *ale_reg;
278 278
279 void __iomem *sgmii_port_regs; 279 void __iomem *sgmii_port_regs;
280 void __iomem *pcsr_port_regs; 280 void __iomem *pcsr_port_regs;
281 281
282 struct cpsw_ale *ale; 282 struct cpsw_ale *ale;
283 u32 ale_refcnt; 283 atomic_t ale_refcnt;
284 284
285 u32 link[5]; 285 u32 link[5];
286 struct device_node *phy_node[4]; 286 struct device_node *phy_node[4];
@@ -1710,47 +1710,66 @@ static void cpsw_slave_open(struct cpswx_slave *slave,
1710 } 1710 }
1711} 1711}
1712 1712
1713static void cpsw_init_host_port(struct cpswx_priv *priv, 1713static int cpsw_init_ale(struct cpswx_priv *cpsw_dev,
1714 struct cpswx_intf *cpsw_intf) 1714 struct cpswx_intf *cpsw_intf)
1715{ 1715{
1716 int bypass_en = 1; 1716 struct cpsw_ale_params ale_params;
1717 1717
1718 /* Host Tx Pri */ 1718 memset(&ale_params, 0, sizeof(ale_params));
1719 __raw_writel(HOST_TX_PRI_MAP_DEFAULT,
1720 &priv->host_port_regs->tx_pri_map);
1721 1719
1722 /* Max length register */ 1720 ale_params.dev = cpsw_dev->dev;
1723 __raw_writel(MAX_SIZE_STREAM_BUFFER, 1721 ale_params.ale_regs = (void *)((u32)cpsw_dev->ale_reg);
1724 &priv->host_port_regs->rx_maxlen); 1722 ale_params.ale_ageout = cpsw_dev->ale_ageout;
1723 ale_params.ale_entries = cpsw_dev->ale_entries;
1724 ale_params.ale_ports = cpsw_dev->ale_ports;
1725 1725
1726 if (priv->ale_refcnt == 1) 1726 cpsw_dev->ale = cpsw_ale_create(&ale_params);
1727 cpsw_ale_start(priv->ale); 1727 if (!cpsw_dev->ale) {
1728 dev_err(cpsw_dev->dev, "error initializing ale engine\n");
1729 return -ENODEV;
1730 }
1728 1731
1729 if (!priv->multi_if) 1732 dev_info(cpsw_dev->dev, "Created a cpsw ale engine\n");
1730 bypass_en = 0; 1733
1734 cpsw_ale_start(cpsw_dev->ale);
1731 1735
1732 cpsw_ale_control_set(priv->ale, 0, ALE_BYPASS, bypass_en); 1736 cpsw_ale_control_set(cpsw_dev->ale, 0, ALE_BYPASS,
1737 cpsw_dev->multi_if ? 1 : 0);
1733 1738
1734 cpsw_ale_control_set(priv->ale, 0, ALE_NO_PORT_VLAN, 1); 1739 cpsw_ale_control_set(cpsw_dev->ale, 0, ALE_NO_PORT_VLAN, 1);
1735 1740
1736 cpsw_ale_control_set(priv->ale, priv->host_port, 1741 cpsw_ale_control_set(cpsw_dev->ale, cpsw_dev->host_port,
1737 ALE_PORT_STATE, ALE_PORT_STATE_FORWARD); 1742 ALE_PORT_STATE, ALE_PORT_STATE_FORWARD);
1738 1743
1739 cpsw_ale_control_set(priv->ale, 0, 1744 cpsw_ale_control_set(cpsw_dev->ale, 0,
1740 ALE_PORT_UNKNOWN_VLAN_MEMBER, 1745 ALE_PORT_UNKNOWN_VLAN_MEMBER,
1741 CPSW_MASK_ALL_PORTS); 1746 CPSW_MASK_ALL_PORTS);
1742 1747
1743 cpsw_ale_control_set(priv->ale, 0, 1748 cpsw_ale_control_set(cpsw_dev->ale, 0,
1744 ALE_PORT_UNKNOWN_MCAST_FLOOD, 1749 ALE_PORT_UNKNOWN_MCAST_FLOOD,
1745 CPSW_MASK_PHYS_PORTS); 1750 CPSW_MASK_PHYS_PORTS);
1746 1751
1747 cpsw_ale_control_set(priv->ale, 0, 1752 cpsw_ale_control_set(cpsw_dev->ale, 0,
1748 ALE_PORT_UNKNOWN_REG_MCAST_FLOOD, 1753 ALE_PORT_UNKNOWN_REG_MCAST_FLOOD,
1749 CPSW_MASK_ALL_PORTS); 1754 CPSW_MASK_ALL_PORTS);
1750 1755
1751 cpsw_ale_control_set(priv->ale, 0, 1756 cpsw_ale_control_set(cpsw_dev->ale, 0,
1752 ALE_PORT_UNTAGGED_EGRESS, 1757 ALE_PORT_UNTAGGED_EGRESS,
1753 CPSW_MASK_ALL_PORTS); 1758 CPSW_MASK_ALL_PORTS);
1759
1760 return 0;
1761}
1762
1763static void cpsw_init_host_port(struct cpswx_priv *priv,
1764 struct cpswx_intf *cpsw_intf)
1765{
1766 /* Host Tx Pri */
1767 __raw_writel(HOST_TX_PRI_MAP_DEFAULT,
1768 &priv->host_port_regs->tx_pri_map);
1769
1770 /* Max length register */
1771 __raw_writel(MAX_SIZE_STREAM_BUFFER,
1772 &priv->host_port_regs->rx_maxlen);
1754} 1773}
1755 1774
1756static void cpsw_slave_init(struct cpswx_slave *slave, struct cpswx_priv *priv) 1775static void cpsw_slave_init(struct cpswx_slave *slave, struct cpswx_priv *priv)
@@ -1963,7 +1982,6 @@ static int cpswx_open(void *intf_mod_priv, struct net_device *ndev)
1963 struct cpswx_intf *cpsw_intf = intf_mod_priv; 1982 struct cpswx_intf *cpsw_intf = intf_mod_priv;
1964 struct cpswx_priv *cpsw_dev = cpsw_intf->cpsw_priv; 1983 struct cpswx_priv *cpsw_dev = cpsw_intf->cpsw_priv;
1965 struct netcp_priv *netcp = netdev_priv(ndev); 1984 struct netcp_priv *netcp = netdev_priv(ndev);
1966 struct cpsw_ale_params ale_params;
1967 u32 xgmii_mode = 0; 1985 u32 xgmii_mode = 0;
1968 int ret = 0; 1986 int ret = 0;
1969 u32 reg, i; 1987 u32 reg, i;
@@ -1997,23 +2015,14 @@ static int cpswx_open(void *intf_mod_priv, struct net_device *ndev)
1997 cpsw_intf->tx_pipe.dma_channel, 2015 cpsw_intf->tx_pipe.dma_channel,
1998 cpsw_intf->tx_pipe.dma_psflags); 2016 cpsw_intf->tx_pipe.dma_psflags);
1999 2017
2000 cpsw_dev->ale_refcnt++; 2018 /* initialize host and slave ports */
2001 if (cpsw_dev->ale_refcnt == 1) { 2019 if (atomic_inc_return(&cpsw_dev->ale_refcnt) == 1) {
2002 memset(&ale_params, 0, sizeof(ale_params)); 2020 ret = cpsw_init_ale(cpsw_dev, cpsw_intf);
2003 2021 if (ret < 0) {
2004 ale_params.dev = cpsw_dev->dev; 2022 atomic_dec(&cpsw_dev->ale_refcnt);
2005 ale_params.ale_regs = (void *)((u32)cpsw_dev->ale_reg);
2006 ale_params.ale_ageout = cpsw_dev->ale_ageout;
2007 ale_params.ale_entries = cpsw_dev->ale_entries;
2008 ale_params.ale_ports = cpsw_dev->ale_ports;
2009
2010 cpsw_dev->ale = cpsw_ale_create(&ale_params);
2011 if (!cpsw_dev->ale) {
2012 dev_err(cpsw_dev->dev, "error initializing ale engine\n");
2013 ret = -ENODEV;
2014 goto ale_fail; 2023 goto ale_fail;
2015 } else 2024 }
2016 dev_info(cpsw_dev->dev, "Created a cpsw ale engine\n"); 2025 cpsw_init_host_port(cpsw_dev, cpsw_intf);
2017 } 2026 }
2018 2027
2019 for_each_slave(cpsw_intf, cpsw_slave_init, cpsw_dev); 2028 for_each_slave(cpsw_intf, cpsw_slave_init, cpsw_dev);
@@ -2026,9 +2035,6 @@ static int cpswx_open(void *intf_mod_priv, struct net_device *ndev)
2026 xgmii_mode |= (1 << i); 2035 xgmii_mode |= (1 << i);
2027 __raw_writel(xgmii_mode, &cpsw_dev->ss_regs->control); 2036 __raw_writel(xgmii_mode, &cpsw_dev->ss_regs->control);
2028 2037
2029 /* initialize host and slave ports */
2030 cpsw_init_host_port(cpsw_dev, cpsw_intf);
2031
2032 /* disable priority elevation and enable statistics on all ports */ 2038 /* disable priority elevation and enable statistics on all ports */
2033 __raw_writel(0, &cpsw_dev->regs->ptype); 2039 __raw_writel(0, &cpsw_dev->regs->ptype);
2034 2040
@@ -2079,12 +2085,13 @@ static int cpswx_close(void *intf_modpriv, struct net_device *ndev)
2079 2085
2080 del_timer_sync(&cpsw_intf->timer); 2086 del_timer_sync(&cpsw_intf->timer);
2081 2087
2082 cpsw_dev->ale_refcnt--;
2083 if (!cpsw_dev->ale_refcnt)
2084 cpsw_ale_stop(cpsw_dev->ale);
2085
2086 for_each_slave(cpsw_intf, cpsw_slave_stop, cpsw_dev); 2088 for_each_slave(cpsw_intf, cpsw_slave_stop, cpsw_dev);
2087 2089
2090 if (atomic_dec_return(&cpsw_dev->ale_refcnt) == 0) {
2091 cpsw_ale_destroy(cpsw_dev->ale);
2092 cpsw_dev->ale = NULL;
2093 }
2094
2088 netcp_unregister_txhook(netcp, CPSW_TXHOOK_ORDER, cpsw_tx_hook, 2095 netcp_unregister_txhook(netcp, CPSW_TXHOOK_ORDER, cpsw_tx_hook,
2089 cpsw_intf); 2096 cpsw_intf);
2090 netcp_txpipe_close(&cpsw_intf->tx_pipe); 2097 netcp_txpipe_close(&cpsw_intf->tx_pipe);
@@ -2102,8 +2109,6 @@ static int cpswx_remove(struct netcp_device *netcp_device, void *inst_priv)
2102 2109
2103 of_node_put(cpsw_dev->interfaces); 2110 of_node_put(cpsw_dev->interfaces);
2104 2111
2105 cpsw_ale_destroy(cpsw_dev->ale);
2106
2107 list_for_each_entry_safe(cpsw_intf, tmp, &cpsw_dev->cpsw_intf_head, 2112 list_for_each_entry_safe(cpsw_intf, tmp, &cpsw_dev->cpsw_intf_head,
2108 cpsw_intf_list) { 2113 cpsw_intf_list) {
2109 netcp_delete_interface(netcp_device, cpsw_intf->ndev); 2114 netcp_delete_interface(netcp_device, cpsw_intf->ndev);