Commit e163cc97f9ac169f00e86df57bee365e82e9c365

Authored by Lothar Waßmann
Committed by David S. Miller
1 parent 42431dc24d

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);