Commit e163cc97f9ac169f00e86df57bee365e82e9c365
Committed by
David S. Miller
1 parent
42431dc24d
Exists in
master
and in
6 other branches
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>
Showing 1 changed file with 22 additions and 9 deletions Side-by-side Diff
drivers/net/ethernet/freescale/fec.c
... | ... | @@ -259,6 +259,8 @@ |
259 | 259 | /* Transmitter timeout */ |
260 | 260 | #define TX_TIMEOUT (2 * HZ) |
261 | 261 | |
262 | +static int mii_cnt; | |
263 | + | |
262 | 264 | static void *swap_buffer(void *bufaddr, int len) |
263 | 265 | { |
264 | 266 | int i; |
... | ... | @@ -1040,8 +1042,12 @@ |
1040 | 1042 | */ |
1041 | 1043 | if ((id_entry->driver_data & FEC_QUIRK_ENET_MAC) && pdev->id > 0) { |
1042 | 1044 | /* fec1 uses fec0 mii_bus */ |
1043 | - fep->mii_bus = fec0_mii_bus; | |
1044 | - return 0; | |
1045 | + if (mii_cnt && fec0_mii_bus) { | |
1046 | + fep->mii_bus = fec0_mii_bus; | |
1047 | + mii_cnt++; | |
1048 | + return 0; | |
1049 | + } | |
1050 | + return -ENOENT; | |
1045 | 1051 | } |
1046 | 1052 | |
1047 | 1053 | fep->mii_timeout = 0; |
... | ... | @@ -1086,6 +1092,8 @@ |
1086 | 1092 | if (mdiobus_register(fep->mii_bus)) |
1087 | 1093 | goto err_out_free_mdio_irq; |
1088 | 1094 | |
1095 | + mii_cnt++; | |
1096 | + | |
1089 | 1097 | /* save fec0 mii_bus */ |
1090 | 1098 | if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) |
1091 | 1099 | fec0_mii_bus = fep->mii_bus; |
... | ... | @@ -1102,11 +1110,11 @@ |
1102 | 1110 | |
1103 | 1111 | static void fec_enet_mii_remove(struct fec_enet_private *fep) |
1104 | 1112 | { |
1105 | - if (fep->phy_dev) | |
1106 | - phy_disconnect(fep->phy_dev); | |
1107 | - mdiobus_unregister(fep->mii_bus); | |
1108 | - kfree(fep->mii_bus->irq); | |
1109 | - mdiobus_free(fep->mii_bus); | |
1113 | + if (--mii_cnt == 0) { | |
1114 | + mdiobus_unregister(fep->mii_bus); | |
1115 | + kfree(fep->mii_bus->irq); | |
1116 | + mdiobus_free(fep->mii_bus); | |
1117 | + } | |
1110 | 1118 | } |
1111 | 1119 | |
1112 | 1120 | static int fec_enet_get_settings(struct net_device *ndev, |
1113 | 1121 | |
1114 | 1122 | |
1115 | 1123 | |
... | ... | @@ -1646,13 +1654,18 @@ |
1646 | 1654 | struct net_device *ndev = platform_get_drvdata(pdev); |
1647 | 1655 | struct fec_enet_private *fep = netdev_priv(ndev); |
1648 | 1656 | struct resource *r; |
1657 | + int i; | |
1649 | 1658 | |
1650 | - fec_stop(ndev); | |
1659 | + unregister_netdev(ndev); | |
1651 | 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 | 1666 | clk_disable(fep->clk); |
1653 | 1667 | clk_put(fep->clk); |
1654 | 1668 | iounmap(fep->hwp); |
1655 | - unregister_netdev(ndev); | |
1656 | 1669 | free_netdev(ndev); |
1657 | 1670 | |
1658 | 1671 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |