在net/core/dev_ethtool 中的dev_ethtool定义了ethtool 这个工具向下调用的接口
int dev_ethtool(struct net *net, struct ifreq *ifr)
{
struct net_device *dev = __dev_get_by_name(net, ifr->ifr_name);
void __user *useraddr = ifr->ifr_data;
u32 ethcmd, sub_cmd;
int rc;
netdev_features_t old_features;
if (!dev || !netif_device_present(dev))
return -ENODEV;
if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd)))
return -EFAULT;
if (ethcmd == ETHTOOL_PERQUEUE) {
if (copy_from_user(&sub_cmd, useraddr + sizeof(ethcmd), sizeof(sub_cmd)))
return -EFAULT;
} else {
sub_cmd = ethcmd;
}
/* Allow some commands to be done by anyone */
switch (ethcmd) {
case ETHTOOL_GSET:
rc = ethtool_get_settings(dev, useraddr);
break;
case ETHTOOL_SSET:
rc = ethtool_set_settings(dev, useraddr);
break;
case ETHTOOL_GDRVINFO:
rc = ethtool_get_drvinfo(dev, useraddr);
break;
return rc;
}
这里先通过netif_device_present 来判断要通过ioctl查询的net device是否存在
static inline bool netif_device_present(struct net_device *dev)
{
return test_bit(__LINK_STATE_PRESENT, &dev->state);
}
其实也就是判断dev->state 是否包含__LINK_STATE_PRESENT 这个flag.
然后通过copy_from_user将user space的值copy到kernel space,然后根据ethcmd来掉对应的函数,这里以ETHTOOL_GDRVINFO 为例
static noinline_for_stack int ethtool_get_drvinfo(struct net_device *dev,
void __user *useraddr)
{
struct ethtool_drvinfo info;
const struct ethtool_ops *ops = dev->ethtool_ops;
memset(&info, 0, sizeof(info));
info.cmd = ETHTOOL_GDRVINFO;
if (ops->get_drvinfo) {
ops->get_drvinfo(dev, &info);
} else if (dev->dev.parent && dev->dev.parent->driver) {
strlcpy(info.bus_info, dev_name(dev->dev.parent),
sizeof(info.bus_info));
strlcpy(info.driver, dev->dev.parent->driver->name,
sizeof(info.driver));
} else {
return -EOPNOTSUPP;
}
/*
* this method of obtaining string set info is deprecated;
* Use ETHTOOL_GSSET_INFO instead.
*/
if (ops->get_sset_count) {
int rc;
rc = ops->get_sset_count(dev, ETH_SS_TEST);
if (rc >= 0)
info.testinfo_len = rc;
rc = ops->get_sset_count(dev, ETH_SS_STATS);
if (rc >= 0)
info.n_stats = rc;
rc = ops->get_sset_count(dev, ETH_SS_PRIV_FLAGS);
if (rc >= 0)
info.n_priv_flags = rc;
}
if (ops->get_regs_len)
info.regdump_len = ops->get_regs_len(dev);
if (ops->get_eeprom_len)
info.eedump_len = ops->get_eeprom_len(dev);
if (copy_to_user(useraddr, &info, sizeof(info)))
return -EFAULT;
return 0;
}
注意这里的const struct ethtool_ops *ops = dev->ethtool_ops;的赋值,也就是说driver到底支持多少ethtool的命令是有driver自己实现的。
这里通过ops->get_drvinfo(dev, &info);将的到的至存在info中,如果driver本事没有实现这个get_drvinfo 这个函数,则将其parent的businfo 和driver info复制到info中.
最后通过copy_to_user将得到的info copy到user space。
那这里的dev->ethtool_ops 是在哪里赋值的呢?
一般是在net driver 底层实现总赋值的,举例如下:
static const struct ethtool_ops hns_ethtool_ops = {
.get_drvinfo = hns_nic_get_drvinfo,
.get_link = hns_nic_get_link,
.get_ringparam = hns_get_ringparam,
};
void hns_ethtool_set_ops(struct net_device *ndev)
{
ndev->ethtool_ops = &hns_ethtool_ops;
}
通过调用hns_ethtool_set_ops 就会给ndev->ethtool_ops 赋值,这样在ethtool_get_drvinfo 中就可以通过const struct ethtool_ops *ops = dev->ethtool_ops; 来得到这个ops。
我们还是以get_drvinfo 的实现函数hns_nic_get_drvinfo为例继续看
static void hns_nic_get_drvinfo(struct net_device *net_dev,
struct ethtool_drvinfo *drvinfo)
{
struct hns_nic_priv *priv = netdev_priv(net_dev);
strncpy(drvinfo->version, HNAE_DRIVER_VERSION,
sizeof(drvinfo->version));
drvinfo->version[sizeof(drvinfo->version) - 1] = '\0';
strncpy(drvinfo->driver, HNAE_DRIVER_NAME, sizeof(drvinfo->driver));
drvinfo->driver[sizeof(drvinfo->driver) - 1] = '\0';
strncpy(drvinfo->bus_info, priv->dev->bus->name,
sizeof(drvinfo->bus_info));
drvinfo->bus_info[ETHTOOL_BUSINFO_LEN - 1] = '\0';
strncpy(drvinfo->fw_version, "N/A", ETHTOOL_FWVERS_LEN);
drvinfo->eedump_len = 0;
}
原来所谓的driverinfo就是HNAE_DRIVER_VERSION/HNAE_DRIVER_NAME/priv->dev->bus->name/"N/A" 这四个字符串啊