From patchwork Thu Dec 8 07:59:31 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Lothar_Wa=C3=9Fmann?= X-Patchwork-Id: 130119 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 49CE81007D1 for ; Thu, 8 Dec 2011 19:02:06 +1100 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751997Ab1LHIBr (ORCPT ); Thu, 8 Dec 2011 03:01:47 -0500 Received: from mail.karo-electronics.de ([81.173.242.67]:64402 "EHLO mail.karo-electronics.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751941Ab1LHH7h (ORCPT ); Thu, 8 Dec 2011 02:59:37 -0500 From: =?utf-8?q?Lothar=20Wa=C3=9Fmann?= To: netdev@vger.kernel.org Cc: David Miller , linux-kernel@vger.kernel.org, Shawn Guo , =?utf-8?q?Lothar=20Wa=C3=9Fmann?= Subject: [PATCH v3 7/8] net/fec: fix the .remove code Date: Thu, 8 Dec 2011 08:59:31 +0100 Message-Id: <04c11563fee748562d7b53d5e5b3c5cf37fef200.1323326019.git.LW@KARO-electronics.de> X-Mailer: git-send-email 1.5.6.5 In-Reply-To: References: <6c28f25c5c6d2f88d0985ce361c5e16f19db27d0.1323326019.git.LW@KARO-electronics.de> <0b92136d69e509a6ce49b526e1834f0ec90b04a4.1323326019.git.LW@KARO-electronics.de> <6d1e5cdcbfc48df864ec9e0939645c0e95389d2a.1323326019.git.LW@KARO-electronics.de> <55b78c1766da5b0d0d679f5eae3fb9fc74a6ceef.1323326019.git.LW@KARO-electronics.de> In-Reply-To: References: MIME-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org 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 Tested-by: Shawn Guo Acked-by: Shawn Guo --- drivers/net/ethernet/freescale/fec.c | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec.c b/drivers/net/ethernet/freescale/fec.c index ab0afb5..01ee9cc 100644 --- a/drivers/net/ethernet/freescale/fec.c +++ b/drivers/net/ethernet/freescale/fec.c @@ -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,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) { /* fec1 uses fec0 mii_bus */ - fep->mii_bus = fec0_mii_bus; - return 0; + 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,11 +1110,11 @@ err_out: static void fec_enet_mii_remove(struct fec_enet_private *fep) { - if (fep->phy_dev) - phy_disconnect(fep->phy_dev); - mdiobus_unregister(fep->mii_bus); - kfree(fep->mii_bus->irq); - mdiobus_free(fep->mii_bus); + 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, @@ -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);