|
@@ -36,236 +36,10 @@ u32 ethtool_op_get_link(struct net_device *dev)
|
|
|
}
|
|
|
EXPORT_SYMBOL(ethtool_op_get_link);
|
|
|
|
|
|
-u32 ethtool_op_get_tx_csum(struct net_device *dev)
|
|
|
-{
|
|
|
- return (dev->features & NETIF_F_ALL_CSUM) != 0;
|
|
|
-}
|
|
|
-EXPORT_SYMBOL(ethtool_op_get_tx_csum);
|
|
|
-
|
|
|
-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;
|
|
|
-}
|
|
|
-EXPORT_SYMBOL(ethtool_op_set_tx_csum);
|
|
|
-
|
|
|
-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;
|
|
|
-}
|
|
|
-EXPORT_SYMBOL(ethtool_op_set_tx_hw_csum);
|
|
|
-
|
|
|
-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;
|
|
|
-}
|
|
|
-EXPORT_SYMBOL(ethtool_op_set_tx_ipv6_csum);
|
|
|
-
|
|
|
-u32 ethtool_op_get_sg(struct net_device *dev)
|
|
|
-{
|
|
|
- return (dev->features & NETIF_F_SG) != 0;
|
|
|
-}
|
|
|
-EXPORT_SYMBOL(ethtool_op_get_sg);
|
|
|
-
|
|
|
-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;
|
|
|
-}
|
|
|
-EXPORT_SYMBOL(ethtool_op_set_sg);
|
|
|
-
|
|
|
-u32 ethtool_op_get_tso(struct net_device *dev)
|
|
|
-{
|
|
|
- return (dev->features & NETIF_F_TSO) != 0;
|
|
|
-}
|
|
|
-EXPORT_SYMBOL(ethtool_op_get_tso);
|
|
|
-
|
|
|
-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;
|
|
|
-}
|
|
|
-EXPORT_SYMBOL(ethtool_op_set_tso);
|
|
|
-
|
|
|
-u32 ethtool_op_get_ufo(struct net_device *dev)
|
|
|
-{
|
|
|
- return (dev->features & NETIF_F_UFO) != 0;
|
|
|
-}
|
|
|
-EXPORT_SYMBOL(ethtool_op_get_ufo);
|
|
|
-
|
|
|
-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;
|
|
|
-}
|
|
|
-EXPORT_SYMBOL(ethtool_op_set_ufo);
|
|
|
-
|
|
|
-/* 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 | ETH_FLAG_RXVLAN | ETH_FLAG_TXVLAN | ETH_FLAG_NTUPLE |
|
|
|
- ETH_FLAG_RXHASH);
|
|
|
-
|
|
|
-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;
|
|
|
-}
|
|
|
-EXPORT_SYMBOL(ethtool_op_get_flags);
|
|
|
-
|
|
|
-/* Check if device can enable (or disable) particular feature coded in "data"
|
|
|
- * argument. Flags "supported" describe features that can be toggled by device.
|
|
|
- * If feature can not be toggled, it state (enabled or disabled) must match
|
|
|
- * hardcoded device features state, otherwise flags are marked as invalid.
|
|
|
- */
|
|
|
-bool ethtool_invalid_flags(struct net_device *dev, u32 data, u32 supported)
|
|
|
-{
|
|
|
- u32 features = dev->features & flags_dup_features;
|
|
|
- /* "data" can contain only flags_dup_features bits,
|
|
|
- * see __ethtool_set_flags */
|
|
|
-
|
|
|
- return (features & ~supported) != (data & ~supported);
|
|
|
-}
|
|
|
-EXPORT_SYMBOL(ethtool_invalid_flags);
|
|
|
-
|
|
|
-int ethtool_op_set_flags(struct net_device *dev, u32 data, u32 supported)
|
|
|
-{
|
|
|
- if (ethtool_invalid_flags(dev, data, supported))
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- dev->features = ((dev->features & ~flags_dup_features) |
|
|
|
- (data & flags_dup_features));
|
|
|
- return 0;
|
|
|
-}
|
|
|
-EXPORT_SYMBOL(ethtool_op_set_flags);
|
|
|
-
|
|
|
/* Handlers for each ethtool command */
|
|
|
|
|
|
#define ETHTOOL_DEV_FEATURE_WORDS 1
|
|
|
|
|
|
-static void ethtool_get_features_compat(struct net_device *dev,
|
|
|
- struct ethtool_get_features_block *features)
|
|
|
-{
|
|
|
- if (!dev->ethtool_ops)
|
|
|
- return;
|
|
|
-
|
|
|
- /* getting RX checksum */
|
|
|
- if (dev->ethtool_ops->get_rx_csum)
|
|
|
- if (dev->ethtool_ops->get_rx_csum(dev))
|
|
|
- features[0].active |= NETIF_F_RXCSUM;
|
|
|
-
|
|
|
- /* mark legacy-changeable features */
|
|
|
- if (dev->ethtool_ops->set_sg)
|
|
|
- features[0].available |= NETIF_F_SG;
|
|
|
- if (dev->ethtool_ops->set_tx_csum)
|
|
|
- features[0].available |= NETIF_F_ALL_CSUM;
|
|
|
- if (dev->ethtool_ops->set_tso)
|
|
|
- features[0].available |= NETIF_F_ALL_TSO;
|
|
|
- if (dev->ethtool_ops->set_rx_csum)
|
|
|
- features[0].available |= NETIF_F_RXCSUM;
|
|
|
- if (dev->ethtool_ops->set_flags)
|
|
|
- features[0].available |= flags_dup_features;
|
|
|
-}
|
|
|
-
|
|
|
-static int ethtool_set_feature_compat(struct net_device *dev,
|
|
|
- int (*legacy_set)(struct net_device *, u32),
|
|
|
- struct ethtool_set_features_block *features, u32 mask)
|
|
|
-{
|
|
|
- u32 do_set;
|
|
|
-
|
|
|
- if (!legacy_set)
|
|
|
- return 0;
|
|
|
-
|
|
|
- if (!(features[0].valid & mask))
|
|
|
- return 0;
|
|
|
-
|
|
|
- features[0].valid &= ~mask;
|
|
|
-
|
|
|
- do_set = !!(features[0].requested & mask);
|
|
|
-
|
|
|
- if (legacy_set(dev, do_set) < 0)
|
|
|
- netdev_info(dev,
|
|
|
- "Legacy feature change (%s) failed for 0x%08x\n",
|
|
|
- do_set ? "set" : "clear", mask);
|
|
|
-
|
|
|
- return 1;
|
|
|
-}
|
|
|
-
|
|
|
-static int ethtool_set_flags_compat(struct net_device *dev,
|
|
|
- int (*legacy_set)(struct net_device *, u32),
|
|
|
- struct ethtool_set_features_block *features, u32 mask)
|
|
|
-{
|
|
|
- u32 value;
|
|
|
-
|
|
|
- if (!legacy_set)
|
|
|
- return 0;
|
|
|
-
|
|
|
- if (!(features[0].valid & mask))
|
|
|
- return 0;
|
|
|
-
|
|
|
- value = dev->features & ~features[0].valid;
|
|
|
- value |= features[0].requested;
|
|
|
-
|
|
|
- features[0].valid &= ~mask;
|
|
|
-
|
|
|
- if (legacy_set(dev, value & mask) < 0)
|
|
|
- netdev_info(dev, "Legacy flags change failed\n");
|
|
|
-
|
|
|
- return 1;
|
|
|
-}
|
|
|
-
|
|
|
-static int ethtool_set_features_compat(struct net_device *dev,
|
|
|
- struct ethtool_set_features_block *features)
|
|
|
-{
|
|
|
- int compat;
|
|
|
-
|
|
|
- if (!dev->ethtool_ops)
|
|
|
- return 0;
|
|
|
-
|
|
|
- compat = ethtool_set_feature_compat(dev, dev->ethtool_ops->set_sg,
|
|
|
- features, NETIF_F_SG);
|
|
|
- compat |= ethtool_set_feature_compat(dev, dev->ethtool_ops->set_tx_csum,
|
|
|
- features, NETIF_F_ALL_CSUM);
|
|
|
- compat |= ethtool_set_feature_compat(dev, dev->ethtool_ops->set_tso,
|
|
|
- features, NETIF_F_ALL_TSO);
|
|
|
- compat |= ethtool_set_feature_compat(dev, dev->ethtool_ops->set_rx_csum,
|
|
|
- features, NETIF_F_RXCSUM);
|
|
|
- compat |= ethtool_set_flags_compat(dev, dev->ethtool_ops->set_flags,
|
|
|
- features, flags_dup_features);
|
|
|
-
|
|
|
- return compat;
|
|
|
-}
|
|
|
-
|
|
|
static int ethtool_get_features(struct net_device *dev, void __user *useraddr)
|
|
|
{
|
|
|
struct ethtool_gfeatures cmd = {
|
|
@@ -283,8 +57,6 @@ static int ethtool_get_features(struct net_device *dev, void __user *useraddr)
|
|
|
u32 __user *sizeaddr;
|
|
|
u32 copy_size;
|
|
|
|
|
|
- ethtool_get_features_compat(dev, features);
|
|
|
-
|
|
|
sizeaddr = useraddr + offsetof(struct ethtool_gfeatures, size);
|
|
|
if (get_user(copy_size, sizeaddr))
|
|
|
return -EFAULT;
|
|
@@ -320,9 +92,6 @@ static int ethtool_set_features(struct net_device *dev, void __user *useraddr)
|
|
|
if (features[0].valid & ~NETIF_F_ETHTOOL_BITS)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- if (ethtool_set_features_compat(dev, features))
|
|
|
- ret |= ETHTOOL_F_COMPAT;
|
|
|
-
|
|
|
if (features[0].valid & ~dev->hw_features) {
|
|
|
features[0].valid &= dev->hw_features;
|
|
|
ret |= ETHTOOL_F_UNSUPPORTED;
|
|
@@ -433,34 +202,6 @@ static u32 ethtool_get_feature_mask(u32 eth_cmd)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static void *__ethtool_get_one_feature_actor(struct net_device *dev, u32 ethcmd)
|
|
|
-{
|
|
|
- const struct ethtool_ops *ops = dev->ethtool_ops;
|
|
|
-
|
|
|
- if (!ops)
|
|
|
- return NULL;
|
|
|
-
|
|
|
- switch (ethcmd) {
|
|
|
- case ETHTOOL_GTXCSUM:
|
|
|
- return ops->get_tx_csum;
|
|
|
- case ETHTOOL_GRXCSUM:
|
|
|
- return ops->get_rx_csum;
|
|
|
- case ETHTOOL_SSG:
|
|
|
- return ops->get_sg;
|
|
|
- case ETHTOOL_STSO:
|
|
|
- return ops->get_tso;
|
|
|
- case ETHTOOL_SUFO:
|
|
|
- return ops->get_ufo;
|
|
|
- default:
|
|
|
- return NULL;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-static u32 __ethtool_get_rx_csum_oldbug(struct net_device *dev)
|
|
|
-{
|
|
|
- return !!(dev->features & NETIF_F_ALL_CSUM);
|
|
|
-}
|
|
|
-
|
|
|
static int ethtool_get_one_feature(struct net_device *dev,
|
|
|
char __user *useraddr, u32 ethcmd)
|
|
|
{
|
|
@@ -470,31 +211,11 @@ static int ethtool_get_one_feature(struct net_device *dev,
|
|
|
.data = !!(dev->features & mask),
|
|
|
};
|
|
|
|
|
|
- /* compatibility with discrete get_ ops */
|
|
|
- if (!(dev->hw_features & mask)) {
|
|
|
- u32 (*actor)(struct net_device *);
|
|
|
-
|
|
|
- actor = __ethtool_get_one_feature_actor(dev, ethcmd);
|
|
|
-
|
|
|
- /* bug compatibility with old get_rx_csum */
|
|
|
- if (ethcmd == ETHTOOL_GRXCSUM && !actor)
|
|
|
- actor = __ethtool_get_rx_csum_oldbug;
|
|
|
-
|
|
|
- if (actor)
|
|
|
- edata.data = actor(dev);
|
|
|
- }
|
|
|
-
|
|
|
if (copy_to_user(useraddr, &edata, sizeof(edata)))
|
|
|
return -EFAULT;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int __ethtool_set_tx_csum(struct net_device *dev, u32 data);
|
|
|
-static int __ethtool_set_rx_csum(struct net_device *dev, u32 data);
|
|
|
-static int __ethtool_set_sg(struct net_device *dev, u32 data);
|
|
|
-static int __ethtool_set_tso(struct net_device *dev, u32 data);
|
|
|
-static int __ethtool_set_ufo(struct net_device *dev, u32 data);
|
|
|
-
|
|
|
static int ethtool_set_one_feature(struct net_device *dev,
|
|
|
void __user *useraddr, u32 ethcmd)
|
|
|
{
|
|
@@ -506,56 +227,38 @@ static int ethtool_set_one_feature(struct net_device *dev,
|
|
|
|
|
|
mask = ethtool_get_feature_mask(ethcmd);
|
|
|
mask &= dev->hw_features;
|
|
|
- if (mask) {
|
|
|
- if (edata.data)
|
|
|
- dev->wanted_features |= mask;
|
|
|
- else
|
|
|
- dev->wanted_features &= ~mask;
|
|
|
+ if (!mask)
|
|
|
+ return -EOPNOTSUPP;
|
|
|
|
|
|
- __netdev_update_features(dev);
|
|
|
- return 0;
|
|
|
- }
|
|
|
+ if (edata.data)
|
|
|
+ dev->wanted_features |= mask;
|
|
|
+ else
|
|
|
+ dev->wanted_features &= ~mask;
|
|
|
|
|
|
- /* Driver is not converted to ndo_fix_features or does not
|
|
|
- * support changing this offload. In the latter case it won't
|
|
|
- * have corresponding ethtool_ops field set.
|
|
|
- *
|
|
|
- * Following part is to be removed after all drivers advertise
|
|
|
- * their changeable features in netdev->hw_features and stop
|
|
|
- * using discrete offload setting ops.
|
|
|
- */
|
|
|
+ __netdev_update_features(dev);
|
|
|
|
|
|
- switch (ethcmd) {
|
|
|
- case ETHTOOL_STXCSUM:
|
|
|
- return __ethtool_set_tx_csum(dev, edata.data);
|
|
|
- case ETHTOOL_SRXCSUM:
|
|
|
- return __ethtool_set_rx_csum(dev, edata.data);
|
|
|
- case ETHTOOL_SSG:
|
|
|
- return __ethtool_set_sg(dev, edata.data);
|
|
|
- case ETHTOOL_STSO:
|
|
|
- return __ethtool_set_tso(dev, edata.data);
|
|
|
- case ETHTOOL_SUFO:
|
|
|
- return __ethtool_set_ufo(dev, edata.data);
|
|
|
- default:
|
|
|
- return -EOPNOTSUPP;
|
|
|
- }
|
|
|
+ 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 | ETH_FLAG_RXVLAN | ETH_FLAG_TXVLAN | ETH_FLAG_NTUPLE |
|
|
|
+ ETH_FLAG_RXHASH);
|
|
|
+
|
|
|
+static u32 __ethtool_get_flags(struct net_device *dev)
|
|
|
+{
|
|
|
+ return dev->features & flags_dup_features;
|
|
|
}
|
|
|
|
|
|
-int __ethtool_set_flags(struct net_device *dev, u32 data)
|
|
|
+static int __ethtool_set_flags(struct net_device *dev, u32 data)
|
|
|
{
|
|
|
u32 changed;
|
|
|
|
|
|
if (data & ~flags_dup_features)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- /* legacy set_flags() op */
|
|
|
- if (dev->ethtool_ops->set_flags) {
|
|
|
- if (unlikely(dev->hw_features & flags_dup_features))
|
|
|
- netdev_warn(dev,
|
|
|
- "driver BUG: mixed hw_features and set_flags()\n");
|
|
|
- return dev->ethtool_ops->set_flags(dev, data);
|
|
|
- }
|
|
|
-
|
|
|
/* allow changing only bits set in hw_features */
|
|
|
changed = (data ^ dev->features) & flags_dup_features;
|
|
|
if (changed & ~dev->hw_features)
|
|
@@ -1231,81 +934,6 @@ static int ethtool_set_pauseparam(struct net_device *dev, void __user *useraddr)
|
|
|
return dev->ethtool_ops->set_pauseparam(dev, &pauseparam);
|
|
|
}
|
|
|
|
|
|
-static int __ethtool_set_sg(struct net_device *dev, u32 data)
|
|
|
-{
|
|
|
- int err;
|
|
|
-
|
|
|
- if (!dev->ethtool_ops->set_sg)
|
|
|
- return -EOPNOTSUPP;
|
|
|
-
|
|
|
- if (data && !(dev->features & NETIF_F_ALL_CSUM))
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- 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, u32 data)
|
|
|
-{
|
|
|
- int err;
|
|
|
-
|
|
|
- if (!dev->ethtool_ops->set_tx_csum)
|
|
|
- return -EOPNOTSUPP;
|
|
|
-
|
|
|
- if (!data && dev->ethtool_ops->set_sg) {
|
|
|
- err = __ethtool_set_sg(dev, 0);
|
|
|
- if (err)
|
|
|
- return err;
|
|
|
- }
|
|
|
-
|
|
|
- return dev->ethtool_ops->set_tx_csum(dev, data);
|
|
|
-}
|
|
|
-
|
|
|
-static int __ethtool_set_rx_csum(struct net_device *dev, u32 data)
|
|
|
-{
|
|
|
- if (!dev->ethtool_ops->set_rx_csum)
|
|
|
- return -EOPNOTSUPP;
|
|
|
-
|
|
|
- if (!data)
|
|
|
- dev->features &= ~NETIF_F_GRO;
|
|
|
-
|
|
|
- return dev->ethtool_ops->set_rx_csum(dev, data);
|
|
|
-}
|
|
|
-
|
|
|
-static int __ethtool_set_tso(struct net_device *dev, u32 data)
|
|
|
-{
|
|
|
- if (!dev->ethtool_ops->set_tso)
|
|
|
- return -EOPNOTSUPP;
|
|
|
-
|
|
|
- if (data && !(dev->features & NETIF_F_SG))
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- return dev->ethtool_ops->set_tso(dev, data);
|
|
|
-}
|
|
|
-
|
|
|
-static int __ethtool_set_ufo(struct net_device *dev, u32 data)
|
|
|
-{
|
|
|
- if (!dev->ethtool_ops->set_ufo)
|
|
|
- return -EOPNOTSUPP;
|
|
|
- if (data && !(dev->features & NETIF_F_SG))
|
|
|
- return -EINVAL;
|
|
|
- if (data && !((dev->features & NETIF_F_GEN_CSUM) ||
|
|
|
- (dev->features & (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))
|
|
|
- == (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM)))
|
|
|
- return -EINVAL;
|
|
|
- return dev->ethtool_ops->set_ufo(dev, data);
|
|
|
-}
|
|
|
-
|
|
|
static int ethtool_self_test(struct net_device *dev, char __user *useraddr)
|
|
|
{
|
|
|
struct ethtool_test test;
|
|
@@ -1771,9 +1399,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
|
|
|
break;
|
|
|
case ETHTOOL_GFLAGS:
|
|
|
rc = ethtool_get_value(dev, useraddr, ethcmd,
|
|
|
- (dev->ethtool_ops->get_flags ?
|
|
|
- dev->ethtool_ops->get_flags :
|
|
|
- ethtool_op_get_flags));
|
|
|
+ __ethtool_get_flags);
|
|
|
break;
|
|
|
case ETHTOOL_SFLAGS:
|
|
|
rc = ethtool_set_value(dev, useraddr, __ethtool_set_flags);
|