📄 ethtool.c
字号:
/* * net/core/ethtool.c - Ethtool ioctl handler * Copyright (c) 2003 Matthew Wilcox <matthew@wil.cx> * * This file is where we call all the ethtool_ops commands to get * the information ethtool needs. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */#include <linux/module.h>#include <linux/types.h>#include <linux/capability.h>#include <linux/errno.h>#include <linux/ethtool.h>#include <linux/netdevice.h>#include <asm/uaccess.h>/* * Some useful ethtool_ops methods that're device independent. * If we find that all drivers want to do the same thing here, * we can turn these into dev_() function calls. */u32 ethtool_op_get_link(struct net_device *dev){ return netif_carrier_ok(dev) ? 1 : 0;}u32 ethtool_op_get_tx_csum(struct net_device *dev){ return (dev->features & NETIF_F_ALL_CSUM) != 0;}int ethtool_op_set_tx_csum(struct net_device *dev, u32 data){ if (data) dev->features |= NETIF_F_IP_CSUM; else dev->features &= ~NETIF_F_IP_CSUM; return 0;}int ethtool_op_set_tx_hw_csum(struct net_device *dev, u32 data){ if (data) dev->features |= NETIF_F_HW_CSUM; else dev->features &= ~NETIF_F_HW_CSUM; return 0;}int ethtool_op_set_tx_ipv6_csum(struct net_device *dev, u32 data){ if (data) dev->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; else dev->features &= ~(NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM); return 0;}u32 ethtool_op_get_sg(struct net_device *dev){ return (dev->features & NETIF_F_SG) != 0;}int ethtool_op_set_sg(struct net_device *dev, u32 data){ if (data) dev->features |= NETIF_F_SG; else dev->features &= ~NETIF_F_SG; return 0;}u32 ethtool_op_get_tso(struct net_device *dev){ return (dev->features & NETIF_F_TSO) != 0;}int ethtool_op_set_tso(struct net_device *dev, u32 data){ if (data) dev->features |= NETIF_F_TSO; else dev->features &= ~NETIF_F_TSO; return 0;}u32 ethtool_op_get_ufo(struct net_device *dev){ return (dev->features & NETIF_F_UFO) != 0;}int ethtool_op_set_ufo(struct net_device *dev, u32 data){ if (data) dev->features |= NETIF_F_UFO; else dev->features &= ~NETIF_F_UFO; return 0;}/* the following list of flags are the same as their associated * NETIF_F_xxx values in include/linux/netdevice.h */static const u32 flags_dup_features = ETH_FLAG_LRO;u32 ethtool_op_get_flags(struct net_device *dev){ /* in the future, this function will probably contain additional * handling for flags which are not so easily handled * by a simple masking operation */ return dev->features & flags_dup_features;}int ethtool_op_set_flags(struct net_device *dev, u32 data){ if (data & ETH_FLAG_LRO) dev->features |= NETIF_F_LRO; else dev->features &= ~NETIF_F_LRO; return 0;}/* Handlers for each ethtool command */static int ethtool_get_settings(struct net_device *dev, void __user *useraddr){ struct ethtool_cmd cmd = { ETHTOOL_GSET }; int err; if (!dev->ethtool_ops->get_settings) return -EOPNOTSUPP; err = dev->ethtool_ops->get_settings(dev, &cmd); if (err < 0) return err; if (copy_to_user(useraddr, &cmd, sizeof(cmd))) return -EFAULT; return 0;}static int ethtool_set_settings(struct net_device *dev, void __user *useraddr){ struct ethtool_cmd cmd; if (!dev->ethtool_ops->set_settings) return -EOPNOTSUPP; if (copy_from_user(&cmd, useraddr, sizeof(cmd))) return -EFAULT; return dev->ethtool_ops->set_settings(dev, &cmd);}static int ethtool_get_drvinfo(struct net_device *dev, void __user *useraddr){ struct ethtool_drvinfo info; const struct ethtool_ops *ops = dev->ethtool_ops; if (!ops->get_drvinfo) return -EOPNOTSUPP; memset(&info, 0, sizeof(info)); info.cmd = ETHTOOL_GDRVINFO; ops->get_drvinfo(dev, &info); 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; } else { /* code path for obsolete hooks */ if (ops->self_test_count) info.testinfo_len = ops->self_test_count(dev); if (ops->get_stats_count) info.n_stats = ops->get_stats_count(dev); } 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;}static int ethtool_get_regs(struct net_device *dev, char __user *useraddr){ struct ethtool_regs regs; const struct ethtool_ops *ops = dev->ethtool_ops; void *regbuf; int reglen, ret; if (!ops->get_regs || !ops->get_regs_len) return -EOPNOTSUPP; if (copy_from_user(®s, useraddr, sizeof(regs))) return -EFAULT; reglen = ops->get_regs_len(dev); if (regs.len > reglen) regs.len = reglen; regbuf = kmalloc(reglen, GFP_USER); if (!regbuf) return -ENOMEM; ops->get_regs(dev, ®s, regbuf); ret = -EFAULT; if (copy_to_user(useraddr, ®s, sizeof(regs))) goto out; useraddr += offsetof(struct ethtool_regs, data); if (copy_to_user(useraddr, regbuf, regs.len)) goto out; ret = 0; out: kfree(regbuf); return ret;}static int ethtool_get_wol(struct net_device *dev, char __user *useraddr){ struct ethtool_wolinfo wol = { ETHTOOL_GWOL }; if (!dev->ethtool_ops->get_wol) return -EOPNOTSUPP; dev->ethtool_ops->get_wol(dev, &wol); if (copy_to_user(useraddr, &wol, sizeof(wol))) return -EFAULT; return 0;}static int ethtool_set_wol(struct net_device *dev, char __user *useraddr){ struct ethtool_wolinfo wol; if (!dev->ethtool_ops->set_wol) return -EOPNOTSUPP; if (copy_from_user(&wol, useraddr, sizeof(wol))) return -EFAULT; return dev->ethtool_ops->set_wol(dev, &wol);}static int ethtool_nway_reset(struct net_device *dev){ if (!dev->ethtool_ops->nway_reset) return -EOPNOTSUPP; return dev->ethtool_ops->nway_reset(dev);}static int ethtool_get_eeprom(struct net_device *dev, void __user *useraddr){ struct ethtool_eeprom eeprom; const struct ethtool_ops *ops = dev->ethtool_ops; u8 *data; int ret; if (!ops->get_eeprom || !ops->get_eeprom_len) return -EOPNOTSUPP; if (copy_from_user(&eeprom, useraddr, sizeof(eeprom))) return -EFAULT; /* Check for wrap and zero */ if (eeprom.offset + eeprom.len <= eeprom.offset) return -EINVAL; /* Check for exceeding total eeprom len */ if (eeprom.offset + eeprom.len > ops->get_eeprom_len(dev)) return -EINVAL; data = kmalloc(eeprom.len, GFP_USER); if (!data) return -ENOMEM; ret = -EFAULT; if (copy_from_user(data, useraddr + sizeof(eeprom), eeprom.len)) goto out; ret = ops->get_eeprom(dev, &eeprom, data); if (ret) goto out; ret = -EFAULT; if (copy_to_user(useraddr, &eeprom, sizeof(eeprom))) goto out; if (copy_to_user(useraddr + sizeof(eeprom), data, eeprom.len)) goto out; ret = 0; out: kfree(data); return ret;}static int ethtool_set_eeprom(struct net_device *dev, void __user *useraddr){ struct ethtool_eeprom eeprom; const struct ethtool_ops *ops = dev->ethtool_ops; u8 *data; int ret; if (!ops->set_eeprom || !ops->get_eeprom_len) return -EOPNOTSUPP; if (copy_from_user(&eeprom, useraddr, sizeof(eeprom))) return -EFAULT; /* Check for wrap and zero */ if (eeprom.offset + eeprom.len <= eeprom.offset) return -EINVAL; /* Check for exceeding total eeprom len */ if (eeprom.offset + eeprom.len > ops->get_eeprom_len(dev)) return -EINVAL; data = kmalloc(eeprom.len, GFP_USER); if (!data) return -ENOMEM; ret = -EFAULT; if (copy_from_user(data, useraddr + sizeof(eeprom), eeprom.len)) goto out; ret = ops->set_eeprom(dev, &eeprom, data); if (ret) goto out; if (copy_to_user(useraddr + sizeof(eeprom), data, eeprom.len)) ret = -EFAULT; out: kfree(data); return ret;}static int ethtool_get_coalesce(struct net_device *dev, void __user *useraddr){ struct ethtool_coalesce coalesce = { ETHTOOL_GCOALESCE }; if (!dev->ethtool_ops->get_coalesce) return -EOPNOTSUPP; dev->ethtool_ops->get_coalesce(dev, &coalesce); if (copy_to_user(useraddr, &coalesce, sizeof(coalesce))) return -EFAULT; return 0;}static int ethtool_set_coalesce(struct net_device *dev, void __user *useraddr){ struct ethtool_coalesce coalesce; if (!dev->ethtool_ops->set_coalesce) return -EOPNOTSUPP; if (copy_from_user(&coalesce, useraddr, sizeof(coalesce))) return -EFAULT; return dev->ethtool_ops->set_coalesce(dev, &coalesce);}static int ethtool_get_ringparam(struct net_device *dev, void __user *useraddr){ struct ethtool_ringparam ringparam = { ETHTOOL_GRINGPARAM }; if (!dev->ethtool_ops->get_ringparam) return -EOPNOTSUPP; dev->ethtool_ops->get_ringparam(dev, &ringparam); if (copy_to_user(useraddr, &ringparam, sizeof(ringparam))) return -EFAULT; return 0;}static int ethtool_set_ringparam(struct net_device *dev, void __user *useraddr){ struct ethtool_ringparam ringparam; if (!dev->ethtool_ops->set_ringparam) return -EOPNOTSUPP; if (copy_from_user(&ringparam, useraddr, sizeof(ringparam))) return -EFAULT; return dev->ethtool_ops->set_ringparam(dev, &ringparam);}static int ethtool_get_pauseparam(struct net_device *dev, void __user *useraddr){ struct ethtool_pauseparam pauseparam = { ETHTOOL_GPAUSEPARAM }; if (!dev->ethtool_ops->get_pauseparam) return -EOPNOTSUPP; dev->ethtool_ops->get_pauseparam(dev, &pauseparam); if (copy_to_user(useraddr, &pauseparam, sizeof(pauseparam))) return -EFAULT; return 0;}static int ethtool_set_pauseparam(struct net_device *dev, void __user *useraddr){ struct ethtool_pauseparam pauseparam; if (!dev->ethtool_ops->set_pauseparam) return -EOPNOTSUPP; if (copy_from_user(&pauseparam, useraddr, sizeof(pauseparam))) return -EFAULT; return dev->ethtool_ops->set_pauseparam(dev, &pauseparam);}static int __ethtool_set_sg(struct net_device *dev, u32 data){ int err; if (!data && dev->ethtool_ops->set_tso) { err = dev->ethtool_ops->set_tso(dev, 0); if (err) return err; } if (!data && dev->ethtool_ops->set_ufo) { err = dev->ethtool_ops->set_ufo(dev, 0); if (err) return err; } return dev->ethtool_ops->set_sg(dev, data);}static int ethtool_set_tx_csum(struct net_device *dev, char __user *useraddr){ struct ethtool_value edata; int err; if (!dev->ethtool_ops->set_tx_csum) return -EOPNOTSUPP; if (copy_from_user(&edata, useraddr, sizeof(edata))) return -EFAULT; if (!edata.data && dev->ethtool_ops->set_sg) { err = __ethtool_set_sg(dev, 0); if (err) return err; } return dev->ethtool_ops->set_tx_csum(dev, edata.data);}static int ethtool_set_sg(struct net_device *dev, char __user *useraddr){ struct ethtool_value edata; if (!dev->ethtool_ops->set_sg) return -EOPNOTSUPP; if (copy_from_user(&edata, useraddr, sizeof(edata))) return -EFAULT;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -