diff options
author | Sebastian Siewior <bigeasy@linutronix.de> | 2013-04-24 08:48:23 (GMT) |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-04-25 08:12:29 (GMT) |
commit | d1bd9acfa3419dc9d5c32589b34a370ca6ae100e (patch) | |
tree | 71f1e076abd4d043f5eadb83b8c252fd5d6c02b5 | |
parent | 4bc21d4162366bb892dc1a4a92110c656e2622ca (diff) | |
download | linux-d1bd9acfa3419dc9d5c32589b34a370ca6ae100e.tar.xz |
net/cpsw: make sure modules remove does not leak any ressources
This driver does not clean up properly after leaving. Here is a list:
- Use unregister_netdev(). free_netdev() is good but not enough
- Use the above also on the other ndev in case of dual mac
- Free data.slave_data. The name of the strucre makes it look like
it is platform_data but it is not. It is just a trick!
- Free all irqs. Again: freeing one irq is good start, but freeing all
of them is better.
With this rmmod & modprobe of cpsw seems to work. The remaining issue
is:
|WARNING: at fs/sysfs/dir.c:536 sysfs_add_one+0x9c/0xd4()
|sysfs: cannot create duplicate filename '/devices/ocp.2/4a100000.ethernet/4a101000.mdio'
|WARNING: at lib/kobject.c:196 kobject_add_internal+0x1a4/0x1c8()
comming from of_platform_populate() and I am not sure that this belongs
here.
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Acked-by: Mugunthan V N <mugunthanvnm@ti.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/ethernet/ti/cpsw.c | 23 |
1 files changed, 16 insertions, 7 deletions
diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index d9f5e74..93a60e2 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -1632,7 +1632,7 @@ static int cpsw_probe_dual_emac(struct platform_device *pdev, static int cpsw_probe(struct platform_device *pdev) { - struct cpsw_platform_data *data = pdev->dev.platform_data; + struct cpsw_platform_data *data; struct net_device *ndev; struct cpsw_priv *priv; struct cpdma_params dma_params; @@ -1845,7 +1845,7 @@ static int cpsw_probe(struct platform_device *pdev) goto clean_ale_ret; } priv->irqs_table[k] = i; - priv->num_irqs = k; + priv->num_irqs = k + 1; } k++; } @@ -1883,7 +1883,8 @@ static int cpsw_probe(struct platform_device *pdev) return 0; clean_irq_ret: - free_irq(ndev->irq, priv); + for (i = 0; i < priv->num_irqs; i++) + free_irq(priv->irqs_table[i], priv); clean_ale_ret: cpsw_ale_destroy(priv->ale); clean_dma_ret: @@ -1906,7 +1907,8 @@ clean_slave_ret: pm_runtime_disable(&pdev->dev); kfree(priv->slaves); clean_ndev_ret: - free_netdev(ndev); + kfree(priv->data.slave_data); + free_netdev(priv->ndev); return ret; } @@ -1914,12 +1916,17 @@ static int cpsw_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); struct cpsw_priv *priv = netdev_priv(ndev); + int i; - pr_info("removing device"); platform_set_drvdata(pdev, NULL); + if (priv->data.dual_emac) + unregister_netdev(cpsw_get_slave_ndev(priv, 1)); + unregister_netdev(ndev); cpts_unregister(priv->cpts); - free_irq(ndev->irq, priv); + for (i = 0; i < priv->num_irqs; i++) + free_irq(priv->irqs_table[i], priv); + cpsw_ale_destroy(priv->ale); cpdma_chan_destroy(priv->txch); cpdma_chan_destroy(priv->rxch); @@ -1933,8 +1940,10 @@ static int cpsw_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); clk_put(priv->clk); kfree(priv->slaves); + kfree(priv->data.slave_data); + if (priv->data.dual_emac) + free_netdev(cpsw_get_slave_ndev(priv, 1)); free_netdev(ndev); - return 0; } |