diff options
author | Gabor Juhos <juhosg@openwrt.org> | 2013-11-08 08:17:54 +0000 |
---|---|---|
committer | Gabor Juhos <juhosg@openwrt.org> | 2013-11-08 08:17:54 +0000 |
commit | 7bd3a7bc7b21a98a6ba3198a3ecb94a6a16716f6 (patch) | |
tree | 6d71ae3959319d29533d9602b4f1061babbef2a3 /target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c | |
parent | f02c8802e363317211ce72b62c29a89911019769 (diff) | |
download | upstream-7bd3a7bc7b21a98a6ba3198a3ecb94a6a16716f6.tar.gz upstream-7bd3a7bc7b21a98a6ba3198a3ecb94a6a16716f6.tar.bz2 upstream-7bd3a7bc7b21a98a6ba3198a3ecb94a6a16716f6.zip |
ar71xx: ag71xx: fix a race involving netdev registration
In particular, phy_connect before register_netdev. This is because
register_netdev runs the netdev notifiers, which can race with the rest of
the initialization in ag71xx_probe. In my case this manifested in two ways:
1) If ag71xx is compiled as a module and inserted after netifd has started,
netifd is notified by register_netdev before the call to
ag71xx_phy_connect. netifd tries to bring the interface up, which calls
ag71xx_open, which in turn enters ag71xx_phy_start. This keys off
ag->phy_dev (which is still NULL) and thinks this is a fixed-link board,
and enters ag71xx_link_adjust. This looks at ag->speed which is not yet
initialized and hits the BUG() in the switch (ag->speed) in
ag71xx_link_adjust.
This is the wrong code path for ag71xx_phy_start - my board has PHYs that
need to be brought up with phy_start. Doing ag71xx_phy_connect before
register_netdev ensures that ag->phy_dev is non-NULL before
ag71xx_phy_start is ever called.
2) When ag71xx is built into the kernel, and netconsole is enabled, there
is a gap in the initial burst of replayed printks right after the netdev
comes up. My assumption is that netconsole is also triggered by a netdev
notifier, and part of this printk burst happens before the call into
ag71xx_phy_connect, so part of the burst is lost while the PHY comes up.
This patch fixes the gap - all the printks before eth0 comes up are bursted
in full when netconsole initializes.
ag71xx_phy_connect_xxx no longer runs with a registered netdev, so the
logging has been adjusted accordingly to avoid "unregistered net_device" or
"eth%d" messages in dmesg.
Signed-off-by: Catalin Patulea <cat@vv.carleton.ca>
Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
SVN-Revision: 38689
Diffstat (limited to 'target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c')
-rw-r--r-- | target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c | 24 |
1 files changed, 11 insertions, 13 deletions
diff --git a/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c index fc6be0e001..f4d6735349 100644 --- a/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c +++ b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c @@ -1178,16 +1178,6 @@ static int ag71xx_probe(struct platform_device *pdev) netif_napi_add(dev, &ag->napi, ag71xx_poll, AG71XX_NAPI_WEIGHT); - err = register_netdev(dev); - if (err) { - dev_err(&pdev->dev, "unable to register net device\n"); - goto err_free_desc; - } - - pr_info("%s: Atheros AG71xx at 0x%08lx, irq %d, mode:%s\n", - dev->name, dev->base_addr, dev->irq, - ag71xx_get_phy_if_mode_name(pdata->phy_if_mode)); - ag71xx_dump_regs(ag); ag71xx_hw_init(ag); @@ -1196,7 +1186,7 @@ static int ag71xx_probe(struct platform_device *pdev) err = ag71xx_phy_connect(ag); if (err) - goto err_unregister_netdev; + goto err_free_desc; err = ag71xx_debugfs_init(ag); if (err) @@ -1204,12 +1194,20 @@ static int ag71xx_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dev); + err = register_netdev(dev); + if (err) { + dev_err(&pdev->dev, "unable to register net device\n"); + goto err_phy_disconnect; + } + + pr_info("%s: Atheros AG71xx at 0x%08lx, irq %d, mode:%s\n", + dev->name, dev->base_addr, dev->irq, + ag71xx_get_phy_if_mode_name(pdata->phy_if_mode)); + return 0; err_phy_disconnect: ag71xx_phy_disconnect(ag); -err_unregister_netdev: - unregister_netdev(dev); err_free_desc: dma_free_coherent(NULL, sizeof(struct ag71xx_desc), ag->stop_desc, ag->stop_desc_dma); |