网卡在 down 掉后 ethtool 获取不到网卡信息的问题

问题描述

最近有同事在搞 stmmac 网卡时发现这个网卡在接口 down 掉后执行 ethtool 就获取不到任何信息了,这个问题有点奇怪。

根据我的经验,ethtool_ops 是在驱动 probe 的时候注册的,注册之后一直可以调用,除非它没有实现相应的接口。但是在这里明显不是没有实现的问题,毕竟接口在 up 的情况下 ethtool 能够正常获取信息。难不成这个驱动在 ndo_stop 函数中会重置 ethtool_ops,然后在 ndo_open 中重新注册吗?

问题的根本原因在哪里?

有了上面的猜想,我阅读了下接口 up、down 调用的驱动底层函数,没有发现与 ethtool_ops 相关的设定,搜索代码也没有搜出来,只搜出来在 probe 函数中设定 ethtool_ops 的代码。

这让我感到非常的不解,一段时间后我重新阅读 stmmac 驱动的代码,终于发现了问题的根本原因。

ethtool_ops 中的 begin 函数

我之前有阅读过 ethtool 执行过程中的代码,对于 ethtool 调用 ioctl 操作网卡的过程还算熟悉,但是我没有意识到 ethtool_ops 中 begin 函数的作用。

内核在 dev_ethtool 函数中分发 ethtool 的不同子命令,在分发子命令之前会调用 begin 函数(如果存在的话),如果 begin 函数返回值小于 0 ,dev_ethtool 函数将直接返回,子命令不会得到执行。

相关代码如下:

/* The main entry point in this file.  Called from net/core/dev_ioctl.c */
int dev_ethtool(struct net *net, struct ifreq *ifr)
············
 if (dev->ethtool_ops->begin) {
                rc = dev->ethtool_ops->begin(dev);
                if (rc  < 0)
                        return rc;
        }
        old_features = dev->features;

        switch (ethcmd) {

上述代码中 switch 语句用于分发不同的 ethtool 子命令到不同的函数中。有了这个基础,我们再来看看 stmmac 驱动代码中 ethtoo_ops 中 begin 函数的实现代码,至少我们确定在接口 down 的情况下该 begin 函数一定返回了一个小于 0 的值。

static const struct ethtool_ops stmmac_ethtool_ops = { .begin = stmmac_check_if_running,
..............

static int stmmac_check_if_running(struct net_device *dev) { if (!netif_running(dev)) return -EBUSY; return 0; }

可以看到当接口状态不为 RUNNING 的情况下,stmmac_check_if_running 函数会返回 -EBUSY,这就导致当接口在 down 的情况下 ethtool 获取不到任何信息。

complete 方法

类似的还有 complete 方法,它在 ethtool 子命令执行完成后调用,相关代码如下:

   if (dev->ethtool_ops->complete)
                dev->ethtool_ops->complete(dev);

这个接口在 ethtool 子命令执行完成后被调用,它是子命令的统一出口,可以被设计为执行一些额外的处理。

一点点联想

我之前编写 gui 程序的时候也遇到过类似的情况。当时的业务需求是,在一个页面中有一个电源选项,当用户点击了这个电源选项,关闭电源后,页面上的其它控件将不会再响应点击事件,除非电源被开启。

由于每个控件有自己单独的方法,要实现这种功能,可以在每个控件中都检查电源状态,只有当电源开启的时候才向下执行,可是这样的实现不太优雅。

最终我选择将这些控件的事件回调都注册为一个相同的入口不同控件的回调函数以参数的形式传递到这个统一入口中,入口函数中首先判断电源状态,当电源状态开启时就调用不同控件的回调函数来处理,反之则直接返回。

按照这个想法,上述 stmmac 驱动中实现的需求就是当接口 down 的情况下,不允许 ethtool 获取信息,可是这个与常见的网卡驱动行为不同,算是一个异类行为吧。

reset 方法

阅读代码的过程中,发现 ethtool_ops 中已经支持 reset 方法了。这个 reset 方法能够在网卡出现异常的时候 reset 网卡来恢复。

适配层的调用代码如下:

static int ethtool_reset(struct net_device *dev, char __user *useraddr)
{
        struct ethtool_value reset;
        int ret;

        if (!dev->ethtool_ops->reset)
                return -EOPNOTSUPP;

        if (copy_from_user(&reset, useraddr, sizeof(reset)))
                return -EFAULT;

        ret = dev->ethtool_ops->reset(dev, &reset.data);
        if (ret)
                return ret;

        if (copy_to_user(useraddr, &reset, sizeof(reset)))
                return -EFAULT;
        return 0;
}

与之类似的是 nway_reset 方法,在 linux-4.4.19 中 igb、 e1000、 ixgbe、 i40e、 均支持 nway_reset 功能来 reset 网卡。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值