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