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>
This commit is contained in:
		
					parent
					
						
							
								42431dc24d
							
						
					
				
			
			
				commit
				
					
						e163cc97f9
					
				
			
		
					 1 changed files with 22 additions and 9 deletions
				
			
		|  | @ -259,6 +259,8 @@ struct fec_enet_private { | ||||||
| /* Transmitter timeout */ | /* Transmitter timeout */ | ||||||
| #define TX_TIMEOUT (2 * HZ) | #define TX_TIMEOUT (2 * HZ) | ||||||
| 
 | 
 | ||||||
|  | static int mii_cnt; | ||||||
|  | 
 | ||||||
| static void *swap_buffer(void *bufaddr, int len) | static void *swap_buffer(void *bufaddr, int len) | ||||||
| { | { | ||||||
| 	int i; | 	int i; | ||||||
|  | @ -1040,8 +1042,12 @@ static int fec_enet_mii_init(struct platform_device *pdev) | ||||||
| 	 */ | 	 */ | ||||||
| 	if ((id_entry->driver_data & FEC_QUIRK_ENET_MAC) && pdev->id > 0) { | 	if ((id_entry->driver_data & FEC_QUIRK_ENET_MAC) && pdev->id > 0) { | ||||||
| 		/* fec1 uses fec0 mii_bus */ | 		/* fec1 uses fec0 mii_bus */ | ||||||
| 		fep->mii_bus = fec0_mii_bus; | 		if (mii_cnt && fec0_mii_bus) { | ||||||
| 		return 0; | 			fep->mii_bus = fec0_mii_bus; | ||||||
|  | 			mii_cnt++; | ||||||
|  | 			return 0; | ||||||
|  | 		} | ||||||
|  | 		return -ENOENT; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	fep->mii_timeout = 0; | 	fep->mii_timeout = 0; | ||||||
|  | @ -1086,6 +1092,8 @@ static int fec_enet_mii_init(struct platform_device *pdev) | ||||||
| 	if (mdiobus_register(fep->mii_bus)) | 	if (mdiobus_register(fep->mii_bus)) | ||||||
| 		goto err_out_free_mdio_irq; | 		goto err_out_free_mdio_irq; | ||||||
| 
 | 
 | ||||||
|  | 	mii_cnt++; | ||||||
|  | 
 | ||||||
| 	/* save fec0 mii_bus */ | 	/* save fec0 mii_bus */ | ||||||
| 	if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) | 	if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) | ||||||
| 		fec0_mii_bus = fep->mii_bus; | 		fec0_mii_bus = fep->mii_bus; | ||||||
|  | @ -1102,11 +1110,11 @@ err_out: | ||||||
| 
 | 
 | ||||||
| static void fec_enet_mii_remove(struct fec_enet_private *fep) | static void fec_enet_mii_remove(struct fec_enet_private *fep) | ||||||
| { | { | ||||||
| 	if (fep->phy_dev) | 	if (--mii_cnt == 0) { | ||||||
| 		phy_disconnect(fep->phy_dev); | 		mdiobus_unregister(fep->mii_bus); | ||||||
| 	mdiobus_unregister(fep->mii_bus); | 		kfree(fep->mii_bus->irq); | ||||||
| 	kfree(fep->mii_bus->irq); | 		mdiobus_free(fep->mii_bus); | ||||||
| 	mdiobus_free(fep->mii_bus); | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int fec_enet_get_settings(struct net_device *ndev, | static int fec_enet_get_settings(struct net_device *ndev, | ||||||
|  | @ -1646,13 +1654,18 @@ fec_drv_remove(struct platform_device *pdev) | ||||||
| 	struct net_device *ndev = platform_get_drvdata(pdev); | 	struct net_device *ndev = platform_get_drvdata(pdev); | ||||||
| 	struct fec_enet_private *fep = netdev_priv(ndev); | 	struct fec_enet_private *fep = netdev_priv(ndev); | ||||||
| 	struct resource *r; | 	struct resource *r; | ||||||
|  | 	int i; | ||||||
| 
 | 
 | ||||||
| 	fec_stop(ndev); | 	unregister_netdev(ndev); | ||||||
| 	fec_enet_mii_remove(fep); | 	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_disable(fep->clk); | ||||||
| 	clk_put(fep->clk); | 	clk_put(fep->clk); | ||||||
| 	iounmap(fep->hwp); | 	iounmap(fep->hwp); | ||||||
| 	unregister_netdev(ndev); |  | ||||||
| 	free_netdev(ndev); | 	free_netdev(ndev); | ||||||
| 
 | 
 | ||||||
| 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Lothar Waßmann
				Lothar Waßmann