修复mt7621 设置ethtool rx-checksum无效的bug

    公司低端产品使用openwrt定制的mips架构设备,在项目中出现了网卡大量丢包的问题。使用ethtool -S eth0 查看详细统计,发现 rx_checksum_errors 大量上涨。
    由于用户业务比较特殊,使用了私有协议。报文二层ethernet的type是0x0800,表示3层是ip协议,但是三层报文不是ip报文,而是用户的私有协议。

20201219143336501.png

    初步推断是网卡将私有协议当作ip协议进行校验,校验值错误从而将报文丢弃。为了验证问题,使用 ethtool -K eth0 rx-checksum off 将网卡的收报校验关闭,发现问题依然存在。
    难道是推断错误?不太可能,明明统计的就是 rx_checksum_errors。突然想到,是不是 网卡没有支持rx-checksum开关,导致 ethtool -K eth0 rx-checksum off 命令没有生效?
    ethtool工具在内核中是通过 dev_ioctl–>dev_ethtool–>通过ethcmd 来进行对应处理。为了快速确认 ethtool -K eth0 rx-checksum off 具体使用了哪个ethcmd,我使用funchook 模块,在dev_ethtool 函数前后增加了debug信息,编译并加载hook ko后,在测试centos机器上执行 ethtool -K eth0 rx-checksum off,
通过log发现,该命令调用了多个 ethcmd,但实际使用的是 ETHTOOL_SFEATURES 来设置开关。

ethtool_set_features–>__netdev_update_features–>
    if (dev->netdev_ops->ndo_set_features)
        err = dev->netdev_ops->ndo_set_features(dev, features);
    else
        err = 0;

     查到这里发现,如果网卡设备没有定义 ndo_set_features 函数的话,则直接返回0。从结果上看好像成功了,但是网卡什么操作没有做。
查看网卡驱动代码,找到 fe_netdev_ops 定义,发现确实没有定义 ndo_set_features 函数。这是ethtool 关闭rx-checksum无效的根本原因。

static const struct net_device_ops fe_netdev_ops = {
    .ndo_init        = fe_init,
    .ndo_uninit        = fe_uninit,
    .ndo_open        = fe_open,
    .ndo_stop        = fe_stop,
    .ndo_start_xmit        = fe_start_xmit,
    .ndo_set_mac_address    = fe_set_mac_address,
    .ndo_validate_addr    = eth_validate_addr,
    .ndo_do_ioctl        = fe_do_ioctl,
    .ndo_change_mtu        = fe_change_mtu,
    .ndo_tx_timeout        = fe_tx_timeout,
    .ndo_get_stats64        = fe_get_stats64,
    .ndo_vlan_rx_add_vid    = fe_vlan_rx_add_vid,
    .ndo_vlan_rx_kill_vid    = fe_vlan_rx_kill_vid,
#ifdef CONFIG_NET_POLL_CONTROLLER
    .ndo_poll_controller    = fe_poll_controller,
#endif
};

查阅了驱动的相关代码,写了个 ndo_set_features 的实现。

static int fe_set_features(struct net_device *dev,
                            netdev_features_t features)
{
    struct fe_priv *priv = netdev_priv(dev);
    const struct of_device_id *match = of_match_device(of_fe_match, priv->device);
    struct fe_soc_data *soc = (struct fe_soc_data *)match->data;
    netdev_features_t changed = features ^ dev->features;
    if (!(changed & NETIF_F_RXCSUM))
        return 0;
    dev->features = features;    //更新features
    soc->fwd_config(priv);        //使用fwd_config回调函数来更新设置
    return 0;
}

更新后的驱动,可以通过 ethtool -K eth0 rx-checksum on/off 来开启/关闭网卡的rx-checksum功能。
如果遇到类似问题,想打patch的话,可以使用以下patch文件。

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
old mode 100644
new mode 100755
index 8c71a92..770cd6b
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -1392,6 +1392,26 @@ static int fe_change_mtu(struct net_device *dev, int new_mtu)
        return fe_open(dev);
 }
 
+static int fe_set_features(struct net_device *dev,
+                             netdev_features_t features)
+{
+       struct fe_priv *priv = netdev_priv(dev);
+       const struct of_device_id *match = of_match_device(of_fe_match, priv->device);
+       struct fe_soc_data *soc = (struct fe_soc_data *)match->data;
+       netdev_features_t changed = features ^ dev->features;
+
+       if (!(changed & NETIF_F_RXCSUM))
+               return 0;
+
+       dev->features = features;
+       soc->fwd_config(priv);
+
+       return 0;
+}
+
+
 static const struct net_device_ops fe_netdev_ops = {
        .ndo_init              = fe_init,
        .ndo_uninit             = fe_uninit,
@@ -1409,6 +1429,7 @@ static const struct net_device_ops fe_netdev_ops = {
 #ifdef CONFIG_NET_POLL_CONTROLLER
        .ndo_poll_controller    = fe_poll_controller,
 #endif
+       .ndo_set_features = fe_set_features,
 };
 
 static void fe_reset_pending(struct fe_priv *priv)

原文地址:https://blog.csdn.net/xqjcool/article/details/111404500

本文章由作者:佐须之男 整理编辑,原文地址: 修复mt7621 设置ethtool rx-checksum无效的bug
本站的文章和资源来自互联网或者站长的原创,按照 CC BY -NC -SA 3.0 CN协议发布和共享,转载或引用本站文章应遵循相同协议。如果有侵犯版权的资 源请尽快联系站长,我们会在24h内删除有争议的资源。欢迎大家多多交流,期待共同学习进步。

相关推荐