diff options
author | Lothar Waßmann <LW@KARO-electronics.de> | 2011-12-07 16:59:31 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-12-08 19:51:20 -0500 |
commit | e163cc97f9ac169f00e86df57bee365e82e9c365 (patch) | |
tree | 9c0071c6fc4e8add7055829fe7c9e1519deb40f7 /drivers/net/ethernet/freescale | |
parent | 42431dc24de343d62bb8fb885586de7f408919c8 (diff) |
net/fec: fix the .remove code
The .remove code is broken in several ways.
- mdiobus_unregister() is called twice for the same object in case of dual FEC
- phy_disconnect() is being called when the PHY is already disconnected
- the requested IRQ(s) are not freed
- fec_stop() is being called with the inteface already stopped
All of those lead to kernel crashes if the remove function is actually used.
Signed-off-by: Lothar Waßmann <LW@KARO-electronics.de>
Tested-by: Shawn Guo <shawn.guo@linaro.org>
Acked-by: Shawn Guo <shawn.guo@linaro.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet/freescale')
-rw-r--r-- | drivers/net/ethernet/freescale/fec.c | 31 |
1 files changed, 22 insertions, 9 deletions
diff --git a/drivers/net/ethernet/freescale/fec.c b/drivers/net/ethernet/freescale/fec.c index ab0afb5961f6..01ee9cc417b2 100644 --- a/drivers/net/ethernet/freescale/fec.c +++ b/drivers/net/ethernet/freescale/fec.c | |||
@@ -259,6 +259,8 @@ struct fec_enet_private { | |||
259 | /* Transmitter timeout */ | 259 | /* Transmitter timeout */ |
260 | #define TX_TIMEOUT (2 * HZ) | 260 | #define TX_TIMEOUT (2 * HZ) |
261 | 261 | ||
262 | static int mii_cnt; | ||
263 | |||
262 | static void *swap_buffer(void *bufaddr, int len) | 264 | static void *swap_buffer(void *bufaddr, int len) |
263 | { | 265 | { |
264 | int i; | 266 | int i; |
@@ -1040,8 +1042,12 @@ static int fec_enet_mii_init(struct platform_device *pdev) | |||
1040 | */ | 1042 | */ |
1041 | if ((id_entry->driver_data & FEC_QUIRK_ENET_MAC) && pdev->id > 0) { | 1043 | if ((id_entry->driver_data & FEC_QUIRK_ENET_MAC) && pdev->id > 0) { |
1042 | /* fec1 uses fec0 mii_bus */ | 1044 | /* fec1 uses fec0 mii_bus */ |
1043 | fep->mii_bus = fec0_mii_bus; | 1045 | if (mii_cnt && fec0_mii_bus) { |
1044 | return 0; | 1046 | fep->mii_bus = fec0_mii_bus; |
1047 | mii_cnt++; | ||
1048 | return 0; | ||
1049 | } | ||
1050 | return -ENOENT; | ||
1045 | } | 1051 | } |
1046 | 1052 | ||
1047 | fep->mii_timeout = 0; | 1053 | fep->mii_timeout = 0; |
@@ -1086,6 +1092,8 @@ static int fec_enet_mii_init(struct platform_device *pdev) | |||
1086 | if (mdiobus_register(fep->mii_bus)) | 1092 | if (mdiobus_register(fep->mii_bus)) |
1087 | goto err_out_free_mdio_irq; | 1093 | goto err_out_free_mdio_irq; |
1088 | 1094 | ||
1095 | mii_cnt++; | ||
1096 | |||
1089 | /* save fec0 mii_bus */ | 1097 | /* save fec0 mii_bus */ |
1090 | if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) | 1098 | if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) |
1091 | fec0_mii_bus = fep->mii_bus; | 1099 | fec0_mii_bus = fep->mii_bus; |
@@ -1102,11 +1110,11 @@ err_out: | |||
1102 | 1110 | ||
1103 | static void fec_enet_mii_remove(struct fec_enet_private *fep) | 1111 | static void fec_enet_mii_remove(struct fec_enet_private *fep) |
1104 | { | 1112 | { |
1105 | if (fep->phy_dev) | 1113 | if (--mii_cnt == 0) { |
1106 | phy_disconnect(fep->phy_dev); | 1114 | mdiobus_unregister(fep->mii_bus); |
1107 | mdiobus_unregister(fep->mii_bus); | 1115 | kfree(fep->mii_bus->irq); |
1108 | kfree(fep->mii_bus->irq); | 1116 | mdiobus_free(fep->mii_bus); |
1109 | mdiobus_free(fep->mii_bus); | 1117 | } |
1110 | } | 1118 | } |
1111 | 1119 | ||
1112 | static int fec_enet_get_settings(struct net_device *ndev, | 1120 | static int fec_enet_get_settings(struct net_device *ndev, |
@@ -1646,13 +1654,18 @@ fec_drv_remove(struct platform_device *pdev) | |||
1646 | struct net_device *ndev = platform_get_drvdata(pdev); | 1654 | struct net_device *ndev = platform_get_drvdata(pdev); |
1647 | struct fec_enet_private *fep = netdev_priv(ndev); | 1655 | struct fec_enet_private *fep = netdev_priv(ndev); |
1648 | struct resource *r; | 1656 | struct resource *r; |
1657 | int i; | ||
1649 | 1658 | ||
1650 | fec_stop(ndev); | 1659 | unregister_netdev(ndev); |
1651 | fec_enet_mii_remove(fep); | 1660 | fec_enet_mii_remove(fep); |
1661 | for (i = 0; i < FEC_IRQ_NUM; i++) { | ||
1662 | int irq = platform_get_irq(pdev, i); | ||
1663 | if (irq > 0) | ||
1664 | free_irq(irq, ndev); | ||
1665 | } | ||
1652 | clk_disable(fep->clk); | 1666 | clk_disable(fep->clk); |
1653 | clk_put(fep->clk); | 1667 | clk_put(fep->clk); |
1654 | iounmap(fep->hwp); | 1668 | iounmap(fep->hwp); |
1655 | unregister_netdev(ndev); | ||
1656 | free_netdev(ndev); | 1669 | free_netdev(ndev); |
1657 | 1670 | ||
1658 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 1671 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |