##内核ethtool工具源码解析
###背景
用户态使用ethtool工具设置网口的模式(速率,双工,协商)时,发现设置的内容和读取的内容是不一
致的,以此问题为出发点,跟踪下内容ethtool的处理流程。
用户态设置网口模式的方法是 linux c通过socket打开网口,封装ethtool_cmd和ifreq结构体,然后ioctl逐步陷入内核中。
###用户态代码
struct ifreq ifr;
struct ethtool_cmd *ethtool = NULL;
strncpy(ifr.ifr_name, priv_data->eth_name, ethlen);
ifr.ifr_data = (void *)ethtool;
ethtool->cmd = ETHTOOL_SSET;
ethtool->speed = mode->speed;
ethtool->autoneg = mode->autoneg;
ethtool->duplex = mode->duplex;
ethtool->phy_address = phy_addr;
ethtool->advertising = MII_BCMR_ADVERTIS;
ret = ioctl(priv_data->mac_fd, SIOCETHTOOL, &ifr);
###内核解析流程
[ 133.060870] [<ffff00000866124c>] phy_ethtool_sset+0x24/0x130
[ 133.066528] [<ffff000008851348>] dpa_set_settings+0x64/0x90
[ 133.072099] [<ffff0000089e6064>] ethtool_set_settings+0x104/0x110
[ 133.078190] [<ffff00000887d65c>] dev_ethtool+0x17fc/0x1bb0
[ 133.083674] [<ffff00000889188c>] dev_ioctl+0x31c/0x608
[ 133.088809] [<ffff000008852fc0>] sock_do_ioctl+0x70/0x88
[ 133.094119] [<ffff0000088546f8>] sock_ioctl+0x260/0x360
[ 133.099342] [<ffff0000082624f8>] do_vfs_ioctl+0xb0/0x830
[ 133.104651] [<ffff000008262d0c>] SyS_ioctl+0x94/0xa8
[ 133.109612] [<ffff000008083b00>] el0_svc_naked+0x34/0x38
static int __cold dpa_set_settings(struct net_device *net_dev,
struct ethtool_cmd *et_cmd)
{
int _errno;
struct dpa_priv_s *priv;
priv = netdev_priv(net_dev);
if (priv->mac_dev == NULL) {
netdev_info(net_dev, "This is a MAC-less interface\n");
return -ENODEV;
}
if (unlikely(priv->mac_dev->phy_dev == NULL)) {
netdev_err(net_dev, "phy device not initialized\n");
return -ENODEV;
}
_errno = phy_ethtool_sset(priv->mac_dev->phy_dev, et_cmd);
if (unlikely(_errno < 0))
netdev_err(net_dev, "phy_ethtool_sset() = %d\n", _errno);
return _errno;
}
int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd)
{
dump_stack();
printk("error phy_ethtool_sset cmd speed high %d %d\n", cmd->speed_hi, cmd->speed);
u32 speed = ethtool_cmd_speed(cmd);
if (cmd->phy_address != phydev->mdio.addr)
return -EINVAL;
/* We make sure that we don't pass unsupported values in to the PHY */
cmd->advertising &= phydev->supported;
/* Verify the settings we care about. */
if (cmd->autoneg != AUTONEG_ENABLE && cmd->autoneg != AUTONEG_DISABLE)
return -EINVAL;
if (cmd->autoneg == AUTONEG_ENABLE && cmd->advertising == 0)
return -EINVAL;
printk("error phy_ethtool_sset 111\n");
if (cmd->autoneg == AUTONEG_DISABLE &&
((speed != SPEED_1000 &&
speed != SPEED_100 &&
speed != SPEED_10) ||
(cmd->duplex != DUPLEX_HALF &&
cmd->duplex != DUPLEX_FULL))) /*从这里可以看出,1) 如果自协商关闭,2) 速度也不是10 100 1000M或者双工模式不是全或者半的话,返回错误 */
return -EINVAL;
phydev->autoneg = cmd->autoneg;
phydev->speed = speed;
phydev->advertising = cmd->advertising;
if (AUTONEG_ENABLE == cmd->autoneg)
phydev->advertising |= ADVERTISED_Autoneg;
else
phydev->advertising &= ~ADVERTISED_Autoneg;
phydev->duplex = cmd->duplex;
phydev->mdix = cmd->eth_tp_mdix_ctrl;
printk("error phy_ethtool_sset 222\n");
/* Restart the PHY */
phy_start_aneg(phydev);
return 0;
}
static int phy_start_aneg_priv(struct phy_device *phydev, bool sync)
{
bool trigger = 0;
int err;
if (!phydev->drv)
return -EIO;
mutex_lock(&phydev->lock);
if (AUTONEG_DISABLE == phydev->autoneg)
phy_sanitize_settings(phydev);
/* Invalidate LP advertising flags */
phydev->lp_advertising = 0;
printk("error phy_start_aneg_priv config_aneg %pf\n", phydev->drv->config_aneg);
err = phydev->drv->config_aneg(phydev); /*这里是关键,这个地方是注册的网卡对应的配置驱动,由于看代码找这个函数的实现比较麻烦,索性之间用上面printk将这个函数指针的名字给打印出来了。比较简便快捷。*/
if (err < 0)
goto out_unlock;
if (phydev->state != PHY_HALTED) {
if (AUTONEG_ENABLE == phydev->autoneg) {
phydev->state = PHY_AN;
phydev->link_timeout = PHY_AN_TIMEOUT;
} else {
phydev->state = PHY_FORCING;
phydev->link_timeout = PHY_FORCE_TIMEOUT;
}
}
/* Re-schedule a PHY state machine to check PHY status because
* negotiation may already be done and aneg interrupt may not be
* generated.
*/
if (phy_interrupt_is_valid(phydev) && (phydev->state == PHY_AN)) {
err = phy_aneg_done(phydev);
if (err > 0) {
trigger = true;
err = 0;
}
}
out_unlock:
mutex_unlock(&phydev->lock);
if (trigger)
phy_trigger_machine(phydev, sync);
return err;
}
根据上面printk打印出来的函数名字发现,这个是一个ethtool工具对应的函数集合,这个集合中定义了
多种处理函数,分别对应于用户态的不同需求。如下所示:
const struct ethtool_ops dpa_ethtool_ops = {
.get_settings = dpa_get_settings,
.set_settings = dpa_set_settings,
.get_drvinfo = dpa_get_drvinfo,
.get_msglevel = dpa_get_msglevel,
.set_msglevel = dpa_set_msglevel,
.nway_reset = dpa_nway_reset,
.get_pauseparam = dpa_get_pauseparam,
.set_pauseparam = dpa_set_pauseparam,
.self_test = NULL, /* TODO invoke the cold-boot unit-test? */
.get_link = ethtool_op_get_link,
.get_eee = dpa_get_eee,
.set_eee = dpa_set_eee,
.get_sset_count = dpa_get_sset_count,
.get_ethtool_stats = dpa_get_ethtool_stats,
.get_strings = dpa_get_strings,
#ifdef CONFIG_PM
.get_wol = dpa_get_wol,
.set_wol = dpa_set_wol,
#endif
};
接着往下看,对于本例而言,走得是第二个函数 dpa_set_settings。
static int __cold dpa_set_settings(struct net_device *net_dev,
struct ethtool_cmd *et_cmd)
{
int _errno;
struct dpa_priv_s *priv;
priv = netdev_priv(net_dev);
if (priv->mac_dev == NULL) {
netdev_info(net_dev, "This is a MAC-less interface\n");
return -ENODEV;
}
if (unlikely(priv->mac_dev->phy_dev == NULL)) {
netdev_err(net_dev, "phy device not initialized\n");
return -ENODEV;
}
_errno = phy_ethtool_sset(priv->mac_dev->phy_dev, et_cmd);
if (unlikely(_errno < 0))
netdev_err(net_dev, "phy_ethtool_sset() = %d\n", _errno);
return _errno;
}
phy_ethtool_sset调用了函数phy_start_aneg_priv,其实现如下。 比较关键的一行是err = phydev->drv->config_aneg(phydev); 其实就是调用了函数genphy_config_aneg对phy进行设置。
static int phy_start_aneg_priv(struct phy_device *phydev, bool sync)
{
bool trigger = 0;
int err;
if (!phydev->drv)
return -EIO;
mutex_lock(&phydev->lock);
if (AUTONEG_DISABLE == phydev->autoneg)
phy_sanitize_settings(phydev);
/* Invalidate LP advertising flags */
phydev->lp_advertising = 0;
printk("error phy_start_aneg_priv config_aneg %pf\n", phydev->drv->config_aneg);
err = phydev->drv->config_aneg(phydev);
if (err < 0)
goto out_unlock;
if (phydev->state != PHY_HALTED) {
if (AUTONEG_ENABLE == phydev->autoneg) {
phydev->state = PHY_AN;
phydev->link_timeout = PHY_AN_TIMEOUT;
} else {
phydev->state = PHY_FORCING;
phydev->link_timeout = PHY_FORCE_TIMEOUT;
}
}
/* Re-schedule a PHY state machine to check PHY status because
* negotiation may already be done and aneg interrupt may not be
* generated.
*/
if (phy_interrupt_is_valid(phydev) && (phydev->state == PHY_AN)) {
err = phy_aneg_done(phydev);
if (err > 0) {
trigger = true;
err = 0;
}
}
out_unlock:
mutex_unlock(&phydev->lock);
if (trigger)
phy_trigger_machine(phydev, sync);
return err;
}
该函数比较关键,最后对网卡模式相关寄存器的设置从这里开始,也就是这里开始真正的操作硬件了。
如果设置的自协商模式,那么,网卡会重新开始自协商。如果不是,就开始按照用户的设置进行强制修改网卡寄存器BMCR。
int genphy_config_aneg(struct phy_device *phydev)
{
int err, changed;
dump_stack();
changed = genphy_config_eee_advert(phydev);
if (AUTONEG_ENABLE != phydev->autoneg)
return genphy_setup_forced(phydev); //如果非自协商模式,那么强制根据用户输入寄存器。
err = genphy_config_advert(phydev);
if (err < 0) /* error */
return err;
changed |= err;
if (changed == 0) {
/* Advertisement hasn't changed, but maybe aneg was never on to
* begin with? Or maybe phy was isolated?
*/
int ctl = phy_read(phydev, MII_BMCR);
if (ctl < 0)
return ctl;
if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE))
changed = 1; /* do restart aneg */
}
/* Only restart aneg if we are advertising something different
* than we were before.
*/
if (changed > 0)
return genphy_restart_aneg(phydev);
return 0;
}
/*强制设置网卡模式,里面主要调用了phy_read和phy_write,这两个函数都是通过mdio总线对phy进行读写。*/
int genphy_setup_forced(struct phy_device *phydev)
{
int ctl = phy_read(phydev, MII_BMCR);
ctl &= BMCR_LOOPBACK | BMCR_ISOLATE | BMCR_PDOWN;
phydev->pause = 0;
phydev->asym_pause = 0;
if (SPEED_1000 == phydev->speed)
ctl |= BMCR_SPEED1000;
else if (SPEED_100 == phydev->speed)
ctl |= BMCR_SPEED100;
if (DUPLEX_FULL == phydev->duplex)
ctl |= BMCR_FULLDPLX;
return phy_write(phydev, MII_BMCR, ctl);
}
上述函数中的phy_read和phy_write函数实现方式均是通过mdio总线的方式对phy的寄存器进行读写,这个是网卡的硬件结构决定的,mdio总线是mac对phy管理的通道。
通过打印,可以查看出phy_read内部调用的函数是:xgmac_mdio_read。具体不在往下研究了。
设置为自协商关闭时的回溯如下:
[ 247.036800] [<ffff00000808aae8>] dump_backtrace+0x0/0x270
[ 247.042196] [<ffff00000808af6c>] show_stack+0x24/0x30
[ 247.047245] [<ffff0000084a8fc8>] dump_stack+0xa8/0xd0
[ 247.052293] [<ffff000008663284>] genphy_setup_forced+0x44/0xa0
[ 247.058123] [<ffff000008663324>] genphy_config_aneg+0x44/0x1f8
[ 247.063954] [<ffff000008661034>] phy_start_aneg_priv.part.4+0x5c/0x218
[ 247.070480] [<ffff000008661350>] phy_ethtool_sset+0x120/0x130
[ 247.076224] [<ffff000008851368>] dpa_set_settings+0x64/0x90
[ 247.081794] [<ffff0000089e6078>] ethtool_set_settings+0xf8/0x104
[ 247.087798] [<ffff00000887d6a4>] dev_ethtool+0x1824/0x1bb0
[ 247.093281] [<ffff0000088918ac>] dev_ioctl+0x31c/0x608
[ 247.098416] [<ffff000008852fe0>] sock_do_ioctl+0x70/0x88
[ 247.103725] [<ffff000008854718>] sock_ioctl+0x260/0x360
[ 247.108948] [<ffff0000082624f8>] do_vfs_ioctl+0xb0/0x830
[ 247.114256] [<ffff000008262d0c>] SyS_ioctl+0x94/0xa8
[ 247.119218] [<ffff000008083b00>] el0_svc_naked+0x34/0x38
###关键的数据结构
ethtool工具相关功能内核的处理函数集合,用户态的ethtool设置或者获取网卡的相关信息,都是下面这个函数集合在处理的。本文来自于内核文件 dpa_ethtool.c。不同网卡对应的函数集合不一样。
const struct ethtool_ops dpa_ethtool_ops = {
.get_settings = dpa_get_settings,
.set_settings = dpa_set_settings,
.get_drvinfo = dpa_get_drvinfo,
.get_msglevel = dpa_get_msglevel,
.set_msglevel = dpa_set_msglevel,
.nway_reset = dpa_nway_reset,
.get_pauseparam = dpa_get_pauseparam,
.set_pauseparam = dpa_set_pauseparam,
.self_test = NULL, /* TODO invoke the cold-boot unit-test? */
.get_link = ethtool_op_get_link,
.get_eee = dpa_get_eee,
.set_eee = dpa_set_eee,
.get_sset_count = dpa_get_sset_count,
.get_ethtool_stats = dpa_get_ethtool_stats,
.get_strings = dpa_get_strings,
#ifdef CONFIG_PM
.get_wol = dpa_get_wol,
.set_wol = dpa_set_wol,
#endif
};
###问题
无论用户态网速设置为多少(自协商关闭,全双工开启),在内核中均会被改为10M 半双工,经过多次家打印,发现改的地方是如下的代码:
static void phy_sanitize_settings(struct phy_device *phydev)
{
u32 features = phydev->supported;
unsigned int idx;
printk("error feature %d\n", features);
print_setting();
/* Sanitize settings based on PHY capabilities */
if ((features & SUPPORTED_Autoneg) == 0)
phydev->autoneg = AUTONEG_DISABLE;
printk("error phy_sanitize_settings phydev speed %d dup %d\n", phydev->speed, phydev->duplex);
idx = phy_find_valid(phy_find_setting(phydev->speed, phydev->duplex),
features);
phydev->speed = settings[idx].speed;
phydev->duplex = settings[idx].duplex;
printk("error phy_sanitize_settings phydev speed %d dup %d\n", phydev->speed, phydev->duplex);
}
phy_find_setting会查找表settings,当发现速度和双工模式与表中匹配时,返回index。
static inline unsigned int phy_find_setting(int speed, int duplex)
{
unsigned int idx = 0;
while (idx < ARRAY_SIZE(settings) &&
(settings[idx].speed != speed || settings[idx].duplex != duplex)) /*speed 和 duplex全部相等时,才会退出*/
idx++;
printk("error phy_find_setting idx %d MAX_NUM_SETTINGS %d\n", idx, MAX_NUM_SETTINGS);
return idx < MAX_NUM_SETTINGS ? idx : MAX_NUM_SETTINGS - 1;
}
这个函数是可以正确查找到index的。 问题是下面这个函数的第二个入参,从idx开始往后遍历,settings[idx].setting 和入参features找不到一致的。
static inline unsigned int phy_find_valid(unsigned int idx, u32 features)
{
while (idx < MAX_NUM_SETTINGS && !(settings[idx].setting & features))
idx++;
printk("error phy_find_valid idx is %d\n", idx);
return idx < MAX_NUM_SETTINGS ? idx : MAX_NUM_SETTINGS - 1;
}
编写函数将setting表中的setsting全部打印出来,
[ 83.614561] error feature 25088 --- phy_find_valid 的第二个入参。
[ 83.617705] error setting idx 0 524288
[ 83.621544] error setting idx 1 262144
[ 83.625382] error setting idx 2 4096
[ 83.629048] error setting idx 3 32768
[ 83.632799] error setting idx 4 131072
[ 83.636638] error setting idx 5 32
[ 83.640129] error setting idx 6 16
[ 83.643620] error setting idx 7 8
[ 83.647020] error setting idx 8 4
[ 83.650424] error setting idx 9 2
[ 83.653827] error setting idx 10 1
###SFUL单板实验
[BSP]bspNet_EthModeGet "eth1",&aaa,&bbb,&ccc
CPU: 0 PID: 2430 Comm: appver Not tainted 4.9.115-rt93-EMBSYS-CGEL-6.1.R6 #2
Call Trace:
[c0000001df3ff6e0] [c000000000a37124] .dump_stack+0xac/0xec (unreliable)
[c0000001df3ff770] [c000000000627fc8] .phy_ethtool_gset+0x78/0xe0
[c0000001df3ff7e0] [c000000000a3c754] .dpa_get_settings+0x4c/0x98
[c0000001df3ff860] [c0000000008bdce0] .ethtool_get_settings+0x1a0/0x1e0
[c0000001df3ff970] [c0000000008c1884] .dev_ethtool+0x1294/0x2100
[c0000001df3ffa80] [c0000000008db9a0] .dev_ioctl+0x4c0/0x890
[c0000001df3ffb70] [c00000000088d740] .sock_do_ioctl+0x90/0xb0
[c0000001df3ffc00] [c00000000088e110] .sock_ioctl+0x2b0/0x3c0
[c0000001df3ffca0] [c0000000001f9bd4] .do_vfs_ioctl+0xc4/0x8a0
[c0000001df3ffd90] [c0000000001fa408] .SyS_ioctl+0x58/0xb0
[c0000001df3ffe30] [c000000000000698] system_call+0x38/0x108
0 , 0x0
[BSP]aaa
281474976710656000 , 0x3e8000000000000
[BSP]bbb
72057594037927936 , 0x100000000000000
[BSP]ccc
72057594037927936 , 0x100000000000000
[BSP]bsp_boot_sync,211: No need to sync boot.
[BSP]
[BSP]bspNet_EthModeSet "eth1",100,0,1
CPU: 0 PID: 2430 Comm: appver Not tainted 4.9.115-rt93-EMBSYS-CGEL-6.1.R6 #2
Call Trace:
[c0000001df3ff6e0] [c000000000a37124] .dump_stack+0xac/0xec (unreliable)
[c0000001df3ff770] [c000000000627fc8] .phy_ethtool_gset+0x78/0xe0
[c0000001df3ff7e0] [c000000000a3c754] .dpa_get_settings+0x4c/0x98
[c0000001df3ff860] [c0000000008bdce0] .ethtool_get_settings+0x1a0/0x1e0
[c0000001df3ff970] [c0000000008c1884] .dev_ethtool+0x1294/0x2100
[c0000001df3ffa80] [c0000000008db9a0] .dev_ioctl+0x4c0/0x890
[c0000001df3ffb70] [c00000000088d740] .sock_do_ioctl+0x90/0xb0
[c0000001df3ffc00] [c00000000088e110] .sock_ioctl+0x2b0/0x3c0
[c0000001df3ffca0] [c0000000001f9bd4] .do_vfs_ioctl+0xc4/0x8a0
[c0000001df3ffd90] [c0000000001fa408] .SyS_ioctl+0x58/0xb0
[c0000001df3ffe30] [c000000000000698] system_call+0x38/0x108
CPU: 0 PID: 2430 Comm: appver Not tainted 4.9.115-rt93-EMBSYS-CGEL-6.1.R6 #2
Call Trace:
[c0000001df3ff6d0] [c000000000a37124] .dump_stack+0xac/0xec (unreliable)
[c0000001df3ff760] [c000000000629404] .phy_ethtool_sset+0x24/0x180
[c0000001df3ff7f0] [c000000000a3c6c4] .dpa_set_settings+0x68/0xac
[c0000001df3ff870] [c0000000008bd888] .ethtool_set_settings+0x108/0x170
[c0000001df3ff970] [c0000000008c186c] .dev_ethtool+0x127c/0x2100
[c0000001df3ffa80] [c0000000008db9a0] .dev_ioctl+0x4c0/0x890
[c0000001df3ffb70] [c00000000088d740] .sock_do_ioctl+0x90/0xb0
[c0000001df3ffc00] [c00000000088e110] .sock_ioctl+0x2b0/0x3c0
[c0000001df3ffca0] [c0000000001f9bd4] .do_vfs_ioctl+0xc4/0x8a0
[c0000001df3ffd90] [c0000000001fa408] .SyS_ioctl+0x58/0xb0
[c0000001df3ffe30] [c000000000000698] system_call+0x38/0x108
error phy_ethtool_sset cmd speed high 0 100
error speed 100
error phy_ethtool_sset 111
error phy_ethtool_sset 222
error feature 25194
error setting idx 0 524288
error setting idx 1 262144
error setting idx 2 4096
error setting idx 3 32768
error setting idx 4 131072
error setting idx 5 32
error setting idx 6 16
error setting idx 7 8
error setting idx 8 4
error setting idx 9 2
error setting idx 10 1
error phy_sanitize_settings phydev speed 100 dup 1
error phy_find_setting idx 7 MAX_NUM_SETTINGS 11
error phy_find_valid idx is 7
error phy_sanitize_settings phydev speed 100 dup 1
error phy_start_aneg_priv config_aneg .genphy_config_aneg
CPU: 0 PID: 2430 Comm: appver Not tainted 4.9.115-rt93-EMBSYS-CGEL-6.1.R6 #2
Call Trace:
[c0000001df3ff590] [c000000000a37124] .dump_stack+0xac/0xec (unreliable)
[c0000001df3ff620] [c00000000062bbd4] .genphy_config_aneg+0x24/0x2f0
[c0000001df3ff6b0] [c000000000629060] .phy_start_aneg_priv.part.6+0x90/0x3e0
[c0000001df3ff760] [c000000000629548] .phy_ethtool_sset+0x168/0x180
[c0000001df3ff7f0] [c000000000a3c6c4] .dpa_set_settings+0x68/0xac
[c0000001df3ff870] [c0000000008bd888] .ethtool_set_settings+0x108/0x170
[c0000001df3ff970] [c0000000008c186c] .dev_ethtool+0x127c/0x2100
[c0000001df3ffa80] [c0000000008db9a0] .dev_ioctl+0x4c0/0x890
[c0000001df3ffb70] [c00000000088d740] .sock_do_ioctl+0x90/0xb0
[c0000001df3ffc00] [c00000000088e110] .sock_ioctl+0x2b0/0x3c0
[c0000001df3ffca0] [c0000000001f9bd4] .do_vfs_ioctl+0xc4/0x8a0
[c0000001df3ffd90] [c0000000001fa408] .SyS_ioctl+0x58/0xb0
[c0000001df3ffe30] [c000000000000698] system_call+0x38/0x108
bus mdio read is .xgmac_mdio_read
CPU: 0 PID: 2430 Comm: appver Not tainted 4.9.115-rt93-EMBSYS-CGEL-6.1.R6 #2
Call Trace:
[c0000001df3ff5a0] [c000000000a37124] .dump_stack+0xac/0xec (unreliable)
[c0000001df3ff630] [c00000000062baec] .genphy_setup_forced+0x4c/0x110
[c0000001df3ff6b0] [c000000000629060] .phy_start_aneg_priv.part.6+0x90/0x3e0
[c0000001df3ff760] [c000000000629548] .phy_ethtool_sset+0x168/0x180
[c0000001df3ff7f0] [c000000000a3c6c4] .dpa_set_settings+0x68/0xac
[c0000001df3ff870] [c0000000008bd888] .ethtool_set_settings+0x108/0x170
[c0000001df3ff970] [c0000000008c186c] .dev_ethtool+0x127c/0x2100
[c0000001df3ffa80] [c0000000008db9a0] .dev_ioctl+0x4c0/0x890
[c0000001df3ffb70] [c00000000088d740] .sock_do_ioctl+0x90/0xb0
[c0000001df3ffc00] [c00000000088e110] .sock_ioctl+0x2b0/0x3c0
[c0000001df3ffca0] [c0000000001f9bd4] .do_vfs_ioctl+0xc4/0x8a0
[c0000001df3ffd90] [c0000000001fa408] .SyS_ioctl+0x58/0xb0
[c0000001df3ffe30] [c000000000000698] system_call+0x38/0x108
error phy speed is 100 duplex 1
error MII_BMCR 0x2100
0 , 0x0
appver中的结构可以正常的get和set的。
这样看来,确实是SFUQ的板子有问题的,那么
定时器一致读phy的回溯。
[ 33.007200] CPU: 0 PID: 70 Comm: kworker/0:1 Tainted: G O 4.9.115-rt93-CGEL-V6.02.10.R2 #42
[ 33.016682] Hardware name: LS1046A RDB Board (DT)
[ 33.021383] Workqueue: events_power_efficient phy_state_machine
[ 33.027305] Call trace:
[ 33.029747] [<ffff00000808aae8>] dump_backtrace+0x0/0x270
[ 33.035143] [<ffff00000808af6c>] show_stack+0x24/0x30
[ 33.040192] [<ffff0000084aa008>] dump_stack+0xa8/0xd0
[ 33.045242] [<ffff000008666110>] mdiobus_read+0x38/0x80
[ 33.050464] [<ffff000008664a70>] genphy_read_status+0xb0/0x218
[ 33.056295] [<ffff000008663188>] phy_state_machine+0x4c0/0x5a8
[ 33.062126] [<ffff0000080ef338>] process_one_work+0x218/0x4e0
[ 33.067869] [<ffff0000080ef658>] worker_thread+0x58/0x4b8
[ 33.073266] [<ffff0000080f5d58>] kthread+0xd8/0xf0
[ 33.078053] [<ffff000008083aa0>] ret_from_fork+0x10/0x30
dts mac
ethernet@e0000 { /*这个就是mac*/
cell-index = <0x0>;
compatible = "fsl,fman-memac";
reg = <0xe0000 0x1000>;
fsl,fman-ports = <0x14 0x15>;
ptp-timer = <0x16>;
pcsphy-handle = <0x17>;
linux,phandle = <0x32>;
phandle = <0x32>;
};
eth2
ethernet@2 { /*eth3*/
compatible = "fsl,dpa-ethernet";
fsl,fman-mac = <0x34>;
dma-coherent;
};