Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e163cc97 authored by Lothar Waßmann's avatar Lothar Waßmann Committed by David S. Miller
Browse files

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: default avatarLothar Waßmann <LW@KARO-electronics.de>
Tested-by: default avatarShawn Guo <shawn.guo@linaro.org>
Acked-by: default avatarShawn Guo <shawn.guo@linaro.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 42431dc2
Loading
Loading
Loading
Loading
+22 −9
Original line number Diff line number Diff line
@@ -259,6 +259,8 @@ struct fec_enet_private {
/* Transmitter timeout */
#define TX_TIMEOUT (2 * HZ)

static int mii_cnt;

static void *swap_buffer(void *bufaddr, int len)
{
	int i;
@@ -1040,9 +1042,13 @@ static int fec_enet_mii_init(struct platform_device *pdev)
	 */
	if ((id_entry->driver_data & FEC_QUIRK_ENET_MAC) && pdev->id > 0) {
		/* fec1 uses fec0 mii_bus */
		if (mii_cnt && fec0_mii_bus) {
			fep->mii_bus = fec0_mii_bus;
			mii_cnt++;
			return 0;
		}
		return -ENOENT;
	}

	fep->mii_timeout = 0;

@@ -1086,6 +1092,8 @@ static int fec_enet_mii_init(struct platform_device *pdev)
	if (mdiobus_register(fep->mii_bus))
		goto err_out_free_mdio_irq;

	mii_cnt++;

	/* save fec0 mii_bus */
	if (id_entry->driver_data & FEC_QUIRK_ENET_MAC)
		fec0_mii_bus = fep->mii_bus;
@@ -1102,12 +1110,12 @@ err_out:

static void fec_enet_mii_remove(struct fec_enet_private *fep)
{
	if (fep->phy_dev)
		phy_disconnect(fep->phy_dev);
	if (--mii_cnt == 0) {
		mdiobus_unregister(fep->mii_bus);
		kfree(fep->mii_bus->irq);
		mdiobus_free(fep->mii_bus);
	}
}

static int fec_enet_get_settings(struct net_device *ndev,
				  struct ethtool_cmd *cmd)
@@ -1646,13 +1654,18 @@ fec_drv_remove(struct platform_device *pdev)
	struct net_device *ndev = platform_get_drvdata(pdev);
	struct fec_enet_private *fep = netdev_priv(ndev);
	struct resource *r;
	int i;

	fec_stop(ndev);
	unregister_netdev(ndev);
	fec_enet_mii_remove(fep);
	for (i = 0; i < FEC_IRQ_NUM; i++) {
		int irq = platform_get_irq(pdev, i);
		if (irq > 0)
			free_irq(irq, ndev);
	}
	clk_disable(fep->clk);
	clk_put(fep->clk);
	iounmap(fep->hwp);
	unregister_netdev(ndev);
	free_netdev(ndev);

	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);