You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

285 lines
6.2 KiB
C

/*
* Atheros AR71xx built-in ethernet mac driver
*
* Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
* Based on Atheros' AG7100 driver
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#include "ag71xx.h"
static void ag71xx_phy_link_adjust(struct net_device *dev)
{
struct ag71xx *ag = netdev_priv(dev);
struct phy_device *phydev = ag->phy_dev;
unsigned long flags;
int status_change = 0;
spin_lock_irqsave(&ag->lock, flags);
if (phydev->link) {
if (ag->duplex != phydev->duplex
|| ag->speed != phydev->speed) {
status_change = 1;
}
}
if (phydev->link != ag->link)
status_change = 1;
ag->link = phydev->link;
ag->duplex = phydev->duplex;
ag->speed = phydev->speed;
if (status_change)
ag71xx_link_adjust(ag);
spin_unlock_irqrestore(&ag->lock, flags);
}
void ag71xx_phy_start(struct ag71xx *ag)
{
struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
if (ag->phy_dev) {
phy_start(ag->phy_dev);
ar71xx: ag71xx: don't start/stop built-in switch w/o mii_bus_dev Trying to do that causes a NULL pointer dereference: CPU 0 Unable to handle kernel paging request at virtual address 00000000, epc == 801d170c, ra == 801d18d4 Oops[#1]: Cpu 0 $ 0 : 00000000 00000000 00000000 00000001 $ 4 : 00000000 802e6390 ffff8f45 00000001 $ 8 : 804b5360 ffffff80 802f93bc 00000000 $12 : 802f93e8 00000000 00000000 00000000 $16 : 8381aba0 8381aba0 00000000 00000000 $20 : 8295eff0 00000032 80000000 004101b8 $24 : 00000000 777265b0 $28 : 82936000 82937cf0 00420000 801d18d4 Hi : 00000000 Lo : 00000001 epc : 801d170c ar7240sw_reset+0x1c/0x19c Tainted: G O ra : 801d18d4 ag71xx_ar7240_start+0x28/0xc0 Status: 1100fc03 KERNEL EXL IE Cause : 00800008 BadVA : 00000000 PrId : 00019750 (MIPS 74Kc) Modules linked in: ath79_wdt ohci_hcd ledtrig_usbdev ledtrig_netdev nf_nat_irc nf_nat_ftp nf_conntrack_irc nf_conntrack_ftp ipt_MASQUERADE iptable_nat nf_nat pppoe xt_conntrack xt_CT xt_NOTRACK iptable_raw xt_state nf_conntrack_ipv4 nf_defrag_ipv4 nf_conntrack ehci_hcd pppox ipt_REJECT xt_TCPMSS xt_LOG xt_comment xt_multiport xt_mac xt_limit iptable_mangle iptable_filter ip_tables xt_tcpudp x_tab les ppp_async ppp_generic slhc ath9k(O) ath9k_common(O) ath9k_hw(O) ath(O) mac80211(O) usbcore usb_common nls_base crc_ccitt cfg80211(O) c ompat(O) arc4 aes_generic crypto_blkcipher cryptomgr aead crypto_hash crypto_algapi ledtrig_timer ledtrig_default_on leds_gpio gpio_button _hotplug(O) Process netifd (pid: 677, threadinfo=82936000, task=82cbe140, tls=77803750) Stack : ffffffff 80087900 00000001 82937d74 8381aba0 8381aba0 00000000 00000000 8295eff0 801d18d4 8295eff0 801fa4a4 00420000 80092158 00000002 8381aba0 8381a800 00000000 00000080 801cf900 8027e720 00000000 00000000 8009223c 8381a800 8381a82c 8027e720 00000000 00000000 7f9d14f0 00420000 801ec08c 8381a800 801ebfb0 33000000 82937e30 00000001 8381a800 00001003 801ec348 ... Call Trace: [<801d170c>] ar7240sw_reset+0x1c/0x19c [<801d18d4>] ag71xx_ar7240_start+0x28/0xc0 [<801cf900>] ag71xx_open+0x1d0/0x258 [<801ec08c>] __dev_open+0xcc/0x130 [<801ec348>] __dev_change_flags+0xc0/0x160 [<801ec490>] dev_change_flags+0x20/0x6c [<801ec5c8>] dev_ifsioc+0xec/0x348 [<801ecdb8>] dev_ioctl+0x594/0x67c [<800ec00c>] do_vfs_ioctl+0x598/0x5ec [<800ec0b0>] sys_ioctl+0x50/0x90 [<8006a3c4>] stack_done+0x20/0x40 Code: afbf0024 afb40020 afb10014 <8c910000> 00809021 24100104 24130704 8e440000 02002821 Signed-off-by: Gabor Juhos <juhosg@openwrt.org> SVN-Revision: 34848
12 years ago
} else if (pdata->mii_bus_dev && pdata->switch_data) {
ag71xx_ar7240_start(ag);
} else {
ag->link = 1;
ag71xx_link_adjust(ag);
}
}
void ag71xx_phy_stop(struct ag71xx *ag)
{
struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
unsigned long flags;
if (ag->phy_dev)
phy_stop(ag->phy_dev);
ar71xx: ag71xx: don't start/stop built-in switch w/o mii_bus_dev Trying to do that causes a NULL pointer dereference: CPU 0 Unable to handle kernel paging request at virtual address 00000000, epc == 801d170c, ra == 801d18d4 Oops[#1]: Cpu 0 $ 0 : 00000000 00000000 00000000 00000001 $ 4 : 00000000 802e6390 ffff8f45 00000001 $ 8 : 804b5360 ffffff80 802f93bc 00000000 $12 : 802f93e8 00000000 00000000 00000000 $16 : 8381aba0 8381aba0 00000000 00000000 $20 : 8295eff0 00000032 80000000 004101b8 $24 : 00000000 777265b0 $28 : 82936000 82937cf0 00420000 801d18d4 Hi : 00000000 Lo : 00000001 epc : 801d170c ar7240sw_reset+0x1c/0x19c Tainted: G O ra : 801d18d4 ag71xx_ar7240_start+0x28/0xc0 Status: 1100fc03 KERNEL EXL IE Cause : 00800008 BadVA : 00000000 PrId : 00019750 (MIPS 74Kc) Modules linked in: ath79_wdt ohci_hcd ledtrig_usbdev ledtrig_netdev nf_nat_irc nf_nat_ftp nf_conntrack_irc nf_conntrack_ftp ipt_MASQUERADE iptable_nat nf_nat pppoe xt_conntrack xt_CT xt_NOTRACK iptable_raw xt_state nf_conntrack_ipv4 nf_defrag_ipv4 nf_conntrack ehci_hcd pppox ipt_REJECT xt_TCPMSS xt_LOG xt_comment xt_multiport xt_mac xt_limit iptable_mangle iptable_filter ip_tables xt_tcpudp x_tab les ppp_async ppp_generic slhc ath9k(O) ath9k_common(O) ath9k_hw(O) ath(O) mac80211(O) usbcore usb_common nls_base crc_ccitt cfg80211(O) c ompat(O) arc4 aes_generic crypto_blkcipher cryptomgr aead crypto_hash crypto_algapi ledtrig_timer ledtrig_default_on leds_gpio gpio_button _hotplug(O) Process netifd (pid: 677, threadinfo=82936000, task=82cbe140, tls=77803750) Stack : ffffffff 80087900 00000001 82937d74 8381aba0 8381aba0 00000000 00000000 8295eff0 801d18d4 8295eff0 801fa4a4 00420000 80092158 00000002 8381aba0 8381a800 00000000 00000080 801cf900 8027e720 00000000 00000000 8009223c 8381a800 8381a82c 8027e720 00000000 00000000 7f9d14f0 00420000 801ec08c 8381a800 801ebfb0 33000000 82937e30 00000001 8381a800 00001003 801ec348 ... Call Trace: [<801d170c>] ar7240sw_reset+0x1c/0x19c [<801d18d4>] ag71xx_ar7240_start+0x28/0xc0 [<801cf900>] ag71xx_open+0x1d0/0x258 [<801ec08c>] __dev_open+0xcc/0x130 [<801ec348>] __dev_change_flags+0xc0/0x160 [<801ec490>] dev_change_flags+0x20/0x6c [<801ec5c8>] dev_ifsioc+0xec/0x348 [<801ecdb8>] dev_ioctl+0x594/0x67c [<800ec00c>] do_vfs_ioctl+0x598/0x5ec [<800ec0b0>] sys_ioctl+0x50/0x90 [<8006a3c4>] stack_done+0x20/0x40 Code: afbf0024 afb40020 afb10014 <8c910000> 00809021 24100104 24130704 8e440000 02002821 Signed-off-by: Gabor Juhos <juhosg@openwrt.org> SVN-Revision: 34848
12 years ago
else if (pdata->mii_bus_dev && pdata->switch_data)
ag71xx_ar7240_stop(ag);
spin_lock_irqsave(&ag->lock, flags);
if (ag->link) {
ag->link = 0;
ag71xx_link_adjust(ag);
}
spin_unlock_irqrestore(&ag->lock, flags);
}
static int ag71xx_phy_connect_fixed(struct ag71xx *ag)
{
struct platform_device *pdev = ag->pdev;
struct device *dev = NULL;
struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
int ret = 0;
if (!pdev)
return -ENODEV;
dev = &pdev->dev;
if (!dev)
return -ENODEV;
if (!ag->phy_dev) {
pr_err("Missing PHY for %s", dev_name(dev));
return -ENODEV;
}
/* use fixed settings */
switch (pdata->speed) {
case SPEED_10:
case SPEED_100:
case SPEED_1000:
break;
default:
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
11 years ago
dev_err(dev, "invalid speed specified\n");
ret = -EINVAL;
break;
}
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
11 years ago
dev_dbg(dev, "using fixed link parameters\n");
ag->duplex = pdata->duplex;
ag->speed = pdata->speed;
if (!ret) {
dev_info(dev, "connected to fixed PHY at %s [uid=%08x, driver=%s]\n",
phydev_name(ag->phy_dev),
ag->phy_dev->phy_id, ag->phy_dev->drv->name);
} else {
pr_err("Failed to connect to fixed PHY\n");
}
return ret;
}
static int ag71xx_phy_connect_multi(struct ag71xx *ag)
{
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
11 years ago
struct device *dev = &ag->pdev->dev;
struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
struct phy_device *phydev = NULL;
int phy_addr;
int ret = 0;
for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
if (!(pdata->phy_mask & (1 << phy_addr)))
continue;
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,5,0)
if (ag->mii_bus->phy_map[phy_addr] == NULL)
continue;
DBG("%s: PHY found at %s, uid=%08x\n",
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
11 years ago
dev_name(dev),
dev_name(&ag->mii_bus->phy_map[phy_addr]->dev),
&ag->mii_bus->phy_map[phy_addr]->phy_id),
&ag->mii_bus->phy_map[phy_addr]->phy_id : 0);
if (phydev == NULL)
phydev = ag->mii_bus->phy_map[phy_addr];
#else
if (ag->mii_bus->mdio_map[phy_addr] == NULL)
continue;
DBG("%s: PHY found at %s, uid=%08x\n",
dev_name(dev),
dev_name(&ag->mii_bus->mdio_map[phy_addr]->dev),
mdiobus_get_phy(ag->mii_bus, phy_addr) ?
mdiobus_get_phy(ag->mii_bus, phy_addr)->phy_id : 0);
if (phydev == NULL)
phydev = mdiobus_get_phy(ag->mii_bus, phy_addr);
#endif
}
if (!phydev) {
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
11 years ago
dev_err(dev, "no PHY found with phy_mask=%08x\n",
pdata->phy_mask);
return -ENODEV;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,5,0)
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
11 years ago
ag->phy_dev = phy_connect(ag->dev, dev_name(&phydev->dev),
#else
ag->phy_dev = phy_connect(ag->dev, phydev_name(phydev),
#endif
&ag71xx_phy_link_adjust,
pdata->phy_if_mode);
if (IS_ERR(ag->phy_dev)) {
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
11 years ago
dev_err(dev, "could not connect to PHY at %s\n",
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,5,0)
dev_name(&phydev->dev));
#else
phydev_name(phydev));
#endif
return PTR_ERR(ag->phy_dev);
}
/* mask with MAC supported features */
if (pdata->has_gbit)
phydev->supported &= PHY_GBIT_FEATURES;
else
phydev->supported &= PHY_BASIC_FEATURES;
phydev->advertising = phydev->supported;
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
11 years ago
dev_info(dev, "connected to PHY at %s [uid=%08x, driver=%s]\n",
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,5,0)
dev_name(&phydev->dev),
#else
phydev_name(phydev),
#endif
phydev->phy_id, phydev->drv->name);
ag->link = 0;
ag->speed = 0;
ag->duplex = -1;
return ret;
}
static int dev_is_class(struct device *dev, void *class)
{
if (dev->class != NULL && !strcmp(dev->class->name, class))
return 1;
return 0;
}
static struct device *dev_find_class(struct device *parent, char *class)
{
if (dev_is_class(parent, class)) {
get_device(parent);
return parent;
}
return device_find_child(parent, class, dev_is_class);
}
static struct mii_bus *dev_to_mii_bus(struct device *dev)
{
struct device *d;
d = dev_find_class(dev, "mdio_bus");
if (d != NULL) {
struct mii_bus *bus;
bus = to_mii_bus(d);
put_device(d);
return bus;
}
return NULL;
}
int ag71xx_phy_connect(struct ag71xx *ag)
{
struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
if (pdata->mii_bus_dev == NULL ||
pdata->mii_bus_dev->bus == NULL )
return ag71xx_phy_connect_fixed(ag);
ag->mii_bus = dev_to_mii_bus(pdata->mii_bus_dev);
if (ag->mii_bus == NULL) {
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
11 years ago
dev_err(&ag->pdev->dev, "unable to find MII bus on device '%s'\n",
dev_name(pdata->mii_bus_dev));
return -ENODEV;
}
/* Reset the mdio bus explicitly */
if (ag->mii_bus->reset) {
mutex_lock(&ag->mii_bus->mdio_lock);
ag->mii_bus->reset(ag->mii_bus);
mutex_unlock(&ag->mii_bus->mdio_lock);
}
if (pdata->switch_data)
return ag71xx_ar7240_init(ag);
if (pdata->phy_mask)
return ag71xx_phy_connect_multi(ag);
return ag71xx_phy_connect_fixed(ag);
}
void ag71xx_phy_disconnect(struct ag71xx *ag)
{
struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
if (pdata->switch_data)
ag71xx_ar7240_cleanup(ag);
else if (ag->phy_dev)
phy_disconnect(ag->phy_dev);
}