前言
环境介绍:
1.编译环境
Ubuntu 18.04.5 LTS
2.SDK
T113-i_v1.0
3.单板
迅龙TLT113-EVM-A1.1-000 + 自制底板
# 一、现象
插上网线启动,内核打印信息正常
不插网线启动,内核存在CPU崩溃打印[ cut here ]
二、问题根因
根据错误提示找到
/home/zfeng/T113- v1.8/kernel/linux-5.4/drivers/net/phy/phy.c:839 phy stop
phy_stop用于中止soc的gmac与phy的链接。
/**
* phy_stop - Bring down the PHY link, and stop checking the status
* @phydev: target phy_device struct
*/
void phy_stop(struct phy_device *phydev)
{
if (!phy_is_started(phydev)) {
WARN(1, "called from state %s\n",
phy_state_to_str(phydev->state));
return;
}
mutex_lock(&phydev->lock);
phydev->state = PHY_HALTED;
mutex_unlock(&phydev->lock);
phy_state_machine(&phydev->state_queue.work);
phy_stop_machine(phydev);
/* Cannot call flush_scheduled_work() here as desired because
* of rtnl_lock(), but PHY_HALTED shall guarantee irq handler
* will not reenable interrupts.
*/
}
EXPORT_SYMBOL(phy_stop);
phy_stop该函数在
/home/zfeng/T113-i_v1.0/kernel/linux-5.4/drivers/net/ethernet/allwinner/sunxi-gmac.c→geth_phy_release引用
static int geth_phy_release(struct net_device *ndev)
{
... ...
/* Stop and disconnect the PHY */
if (phydev)
phy_stop(phydev);
... ...
}
而函数geth_phy_release 在static int geth_stop(struct net_device *ndev)、static int geth_open(struct net_device *ndev)都有被引用。
static int geth_stop(struct net_device *ndev)
{
... ...
/* Release PHY resources */
geth_phy_release(ndev);
... ...
}
static int geth_open(struct net_device *ndev)
{
... ...
if (!priv->is_suspend) {
ret = geth_dma_desc_init(ndev);
if (ret) {
ret = -EINVAL;
goto desc_err;//当没插入网线,会执行desc_err 中断网络配置
}
}
... ...
//该内容被跳过了
if (ndev->phydev)
phy_start(ndev->phydev);
... ...
desc_err:
geth_phy_release(ndev);
... ...
}
从geth_open函数可以看到,当没插入网线,会执行desc_err 中断网络配置,跳过 phy_start(ndev->phydev),直接执行geth_phy_release的phy_stop(phydev),导致cpu执行了空指针或是空函数出错。
如果从启动正常把网线插入,geth_open将正常执行完,phy_start(ndev->phydev)也会正常执行,这样当将网络down,geth_stop可以正常执行phy_stop不会报错。
三、问题解决
根据上述,只要在geth_phy_release将phy_start(ndev->phydev)有没有正常执行区分开来,就可以轻松把问题解决,增加phy_start_flag标志位。
修改如下:
static int geth_open(struct net_device *ndev)
{
... ...
int phy_start_flag = 0;
... ...
if (ndev->phydev)
{
phy_start(ndev->phydev);
phy_start_flag = 1;
}
... ...
desc_err:
geth_phy_release(ndev, phy_start_flag);
... ...
}
static int geth_phy_release(struct net_device *ndev, int phy_start_flag)
{
... ...
// /* Stop and disconnect the PHY */
if (phydev && phy_start_flag)
phy_stop(phydev);
... ...
}