ethtool: factorize get/set_one_feature
This allows to enable GRO even if RX csum is disabled. GRO will not be used for packets without hardware checksum anyway. Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Родитель
340ae1654c
Коммит
0a41770477
|
@ -976,6 +976,12 @@ struct net_device {
|
||||||
#define NETIF_F_V6_CSUM (NETIF_F_GEN_CSUM | NETIF_F_IPV6_CSUM)
|
#define NETIF_F_V6_CSUM (NETIF_F_GEN_CSUM | NETIF_F_IPV6_CSUM)
|
||||||
#define NETIF_F_ALL_CSUM (NETIF_F_V4_CSUM | NETIF_F_V6_CSUM)
|
#define NETIF_F_ALL_CSUM (NETIF_F_V4_CSUM | NETIF_F_V6_CSUM)
|
||||||
|
|
||||||
|
#define NETIF_F_ALL_TSO (NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_TSO_ECN)
|
||||||
|
|
||||||
|
#define NETIF_F_ALL_TX_OFFLOADS (NETIF_F_ALL_CSUM | NETIF_F_SG | \
|
||||||
|
NETIF_F_FRAGLIST | NETIF_F_ALL_TSO | \
|
||||||
|
NETIF_F_SCTP_CSUM | NETIF_F_FCOE_CRC)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If one device supports one of these features, then enable them
|
* If one device supports one of these features, then enable them
|
||||||
* for all in netdev_increment_features.
|
* for all in netdev_increment_features.
|
||||||
|
|
|
@ -191,6 +191,109 @@ static void __ethtool_get_strings(struct net_device *dev,
|
||||||
ops->get_strings(dev, stringset, data);
|
ops->get_strings(dev, stringset, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static u32 ethtool_get_feature_mask(u32 eth_cmd)
|
||||||
|
{
|
||||||
|
/* feature masks of legacy discrete ethtool ops */
|
||||||
|
|
||||||
|
switch (eth_cmd) {
|
||||||
|
case ETHTOOL_GTXCSUM:
|
||||||
|
case ETHTOOL_STXCSUM:
|
||||||
|
return NETIF_F_ALL_CSUM | NETIF_F_SCTP_CSUM;
|
||||||
|
case ETHTOOL_GSG:
|
||||||
|
case ETHTOOL_SSG:
|
||||||
|
return NETIF_F_SG;
|
||||||
|
case ETHTOOL_GTSO:
|
||||||
|
case ETHTOOL_STSO:
|
||||||
|
return NETIF_F_ALL_TSO;
|
||||||
|
case ETHTOOL_GUFO:
|
||||||
|
case ETHTOOL_SUFO:
|
||||||
|
return NETIF_F_UFO;
|
||||||
|
case ETHTOOL_GGSO:
|
||||||
|
case ETHTOOL_SGSO:
|
||||||
|
return NETIF_F_GSO;
|
||||||
|
case ETHTOOL_GGRO:
|
||||||
|
case ETHTOOL_SGRO:
|
||||||
|
return NETIF_F_GRO;
|
||||||
|
default:
|
||||||
|
BUG();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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_SSG:
|
||||||
|
return ops->get_sg;
|
||||||
|
case ETHTOOL_STSO:
|
||||||
|
return ops->get_tso;
|
||||||
|
case ETHTOOL_SUFO:
|
||||||
|
return ops->get_ufo;
|
||||||
|
default:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ethtool_get_one_feature(struct net_device *dev,
|
||||||
|
char __user *useraddr, u32 ethcmd)
|
||||||
|
{
|
||||||
|
struct ethtool_value edata = {
|
||||||
|
.cmd = ethcmd,
|
||||||
|
.data = !!(dev->features & ethtool_get_feature_mask(ethcmd)),
|
||||||
|
};
|
||||||
|
u32 (*actor)(struct net_device *);
|
||||||
|
|
||||||
|
actor = __ethtool_get_one_feature_actor(dev, ethcmd);
|
||||||
|
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_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)
|
||||||
|
{
|
||||||
|
struct ethtool_value edata;
|
||||||
|
u32 mask;
|
||||||
|
|
||||||
|
if (copy_from_user(&edata, useraddr, sizeof(edata)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
switch (ethcmd) {
|
||||||
|
case ETHTOOL_STXCSUM:
|
||||||
|
return __ethtool_set_tx_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);
|
||||||
|
case ETHTOOL_SGSO:
|
||||||
|
case ETHTOOL_SGRO:
|
||||||
|
mask = ethtool_get_feature_mask(ethcmd);
|
||||||
|
if (edata.data)
|
||||||
|
dev->features |= mask;
|
||||||
|
else
|
||||||
|
dev->features &= ~mask;
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int ethtool_get_settings(struct net_device *dev, void __user *useraddr)
|
static int ethtool_get_settings(struct net_device *dev, void __user *useraddr)
|
||||||
{
|
{
|
||||||
struct ethtool_cmd cmd = { .cmd = ETHTOOL_GSET };
|
struct ethtool_cmd cmd = { .cmd = ETHTOOL_GSET };
|
||||||
|
@ -1107,6 +1210,9 @@ static int __ethtool_set_sg(struct net_device *dev, u32 data)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
if (data && !(dev->features & NETIF_F_ALL_CSUM))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
if (!data && dev->ethtool_ops->set_tso) {
|
if (!data && dev->ethtool_ops->set_tso) {
|
||||||
err = dev->ethtool_ops->set_tso(dev, 0);
|
err = dev->ethtool_ops->set_tso(dev, 0);
|
||||||
if (err)
|
if (err)
|
||||||
|
@ -1121,24 +1227,20 @@ static int __ethtool_set_sg(struct net_device *dev, u32 data)
|
||||||
return dev->ethtool_ops->set_sg(dev, data);
|
return dev->ethtool_ops->set_sg(dev, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ethtool_set_tx_csum(struct net_device *dev, char __user *useraddr)
|
static int __ethtool_set_tx_csum(struct net_device *dev, u32 data)
|
||||||
{
|
{
|
||||||
struct ethtool_value edata;
|
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (!dev->ethtool_ops->set_tx_csum)
|
if (!dev->ethtool_ops->set_tx_csum)
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
if (copy_from_user(&edata, useraddr, sizeof(edata)))
|
if (!data && dev->ethtool_ops->set_sg) {
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
if (!edata.data && dev->ethtool_ops->set_sg) {
|
|
||||||
err = __ethtool_set_sg(dev, 0);
|
err = __ethtool_set_sg(dev, 0);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
return dev->ethtool_ops->set_tx_csum(dev, edata.data);
|
return dev->ethtool_ops->set_tx_csum(dev, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ethtool_set_rx_csum(struct net_device *dev, char __user *useraddr)
|
static int ethtool_set_rx_csum(struct net_device *dev, char __user *useraddr)
|
||||||
|
@ -1157,108 +1259,28 @@ static int ethtool_set_rx_csum(struct net_device *dev, char __user *useraddr)
|
||||||
return dev->ethtool_ops->set_rx_csum(dev, edata.data);
|
return dev->ethtool_ops->set_rx_csum(dev, edata.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ethtool_set_sg(struct net_device *dev, char __user *useraddr)
|
static int __ethtool_set_tso(struct net_device *dev, u32 data)
|
||||||
{
|
{
|
||||||
struct ethtool_value edata;
|
|
||||||
|
|
||||||
if (!dev->ethtool_ops->set_sg)
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
|
|
||||||
if (copy_from_user(&edata, useraddr, sizeof(edata)))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
if (edata.data &&
|
|
||||||
!(dev->features & NETIF_F_ALL_CSUM))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
return __ethtool_set_sg(dev, edata.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ethtool_set_tso(struct net_device *dev, char __user *useraddr)
|
|
||||||
{
|
|
||||||
struct ethtool_value edata;
|
|
||||||
|
|
||||||
if (!dev->ethtool_ops->set_tso)
|
if (!dev->ethtool_ops->set_tso)
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
if (copy_from_user(&edata, useraddr, sizeof(edata)))
|
if (data && !(dev->features & NETIF_F_SG))
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
if (edata.data && !(dev->features & NETIF_F_SG))
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
return dev->ethtool_ops->set_tso(dev, edata.data);
|
return dev->ethtool_ops->set_tso(dev, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ethtool_set_ufo(struct net_device *dev, char __user *useraddr)
|
static int __ethtool_set_ufo(struct net_device *dev, u32 data)
|
||||||
{
|
{
|
||||||
struct ethtool_value edata;
|
|
||||||
|
|
||||||
if (!dev->ethtool_ops->set_ufo)
|
if (!dev->ethtool_ops->set_ufo)
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
if (copy_from_user(&edata, useraddr, sizeof(edata)))
|
if (data && !(dev->features & NETIF_F_SG))
|
||||||
return -EFAULT;
|
|
||||||
if (edata.data && !(dev->features & NETIF_F_SG))
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (edata.data && !((dev->features & NETIF_F_GEN_CSUM) ||
|
if (data && !((dev->features & NETIF_F_GEN_CSUM) ||
|
||||||
(dev->features & (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))
|
(dev->features & (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))
|
||||||
== (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM)))
|
== (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM)))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
return dev->ethtool_ops->set_ufo(dev, edata.data);
|
return dev->ethtool_ops->set_ufo(dev, data);
|
||||||
}
|
|
||||||
|
|
||||||
static int ethtool_get_gso(struct net_device *dev, char __user *useraddr)
|
|
||||||
{
|
|
||||||
struct ethtool_value edata = { ETHTOOL_GGSO };
|
|
||||||
|
|
||||||
edata.data = dev->features & NETIF_F_GSO;
|
|
||||||
if (copy_to_user(useraddr, &edata, sizeof(edata)))
|
|
||||||
return -EFAULT;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ethtool_set_gso(struct net_device *dev, char __user *useraddr)
|
|
||||||
{
|
|
||||||
struct ethtool_value edata;
|
|
||||||
|
|
||||||
if (copy_from_user(&edata, useraddr, sizeof(edata)))
|
|
||||||
return -EFAULT;
|
|
||||||
if (edata.data)
|
|
||||||
dev->features |= NETIF_F_GSO;
|
|
||||||
else
|
|
||||||
dev->features &= ~NETIF_F_GSO;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ethtool_get_gro(struct net_device *dev, char __user *useraddr)
|
|
||||||
{
|
|
||||||
struct ethtool_value edata = { ETHTOOL_GGRO };
|
|
||||||
|
|
||||||
edata.data = dev->features & NETIF_F_GRO;
|
|
||||||
if (copy_to_user(useraddr, &edata, sizeof(edata)))
|
|
||||||
return -EFAULT;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ethtool_set_gro(struct net_device *dev, char __user *useraddr)
|
|
||||||
{
|
|
||||||
struct ethtool_value edata;
|
|
||||||
|
|
||||||
if (copy_from_user(&edata, useraddr, sizeof(edata)))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
if (edata.data) {
|
|
||||||
u32 rxcsum = dev->ethtool_ops->get_rx_csum ?
|
|
||||||
dev->ethtool_ops->get_rx_csum(dev) :
|
|
||||||
ethtool_op_get_rx_csum(dev);
|
|
||||||
|
|
||||||
if (!rxcsum)
|
|
||||||
return -EINVAL;
|
|
||||||
dev->features |= NETIF_F_GRO;
|
|
||||||
} else
|
|
||||||
dev->features &= ~NETIF_F_GRO;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ethtool_self_test(struct net_device *dev, char __user *useraddr)
|
static int ethtool_self_test(struct net_device *dev, char __user *useraddr)
|
||||||
|
@ -1590,33 +1612,6 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
|
||||||
case ETHTOOL_SRXCSUM:
|
case ETHTOOL_SRXCSUM:
|
||||||
rc = ethtool_set_rx_csum(dev, useraddr);
|
rc = ethtool_set_rx_csum(dev, useraddr);
|
||||||
break;
|
break;
|
||||||
case ETHTOOL_GTXCSUM:
|
|
||||||
rc = ethtool_get_value(dev, useraddr, ethcmd,
|
|
||||||
(dev->ethtool_ops->get_tx_csum ?
|
|
||||||
dev->ethtool_ops->get_tx_csum :
|
|
||||||
ethtool_op_get_tx_csum));
|
|
||||||
break;
|
|
||||||
case ETHTOOL_STXCSUM:
|
|
||||||
rc = ethtool_set_tx_csum(dev, useraddr);
|
|
||||||
break;
|
|
||||||
case ETHTOOL_GSG:
|
|
||||||
rc = ethtool_get_value(dev, useraddr, ethcmd,
|
|
||||||
(dev->ethtool_ops->get_sg ?
|
|
||||||
dev->ethtool_ops->get_sg :
|
|
||||||
ethtool_op_get_sg));
|
|
||||||
break;
|
|
||||||
case ETHTOOL_SSG:
|
|
||||||
rc = ethtool_set_sg(dev, useraddr);
|
|
||||||
break;
|
|
||||||
case ETHTOOL_GTSO:
|
|
||||||
rc = ethtool_get_value(dev, useraddr, ethcmd,
|
|
||||||
(dev->ethtool_ops->get_tso ?
|
|
||||||
dev->ethtool_ops->get_tso :
|
|
||||||
ethtool_op_get_tso));
|
|
||||||
break;
|
|
||||||
case ETHTOOL_STSO:
|
|
||||||
rc = ethtool_set_tso(dev, useraddr);
|
|
||||||
break;
|
|
||||||
case ETHTOOL_TEST:
|
case ETHTOOL_TEST:
|
||||||
rc = ethtool_self_test(dev, useraddr);
|
rc = ethtool_self_test(dev, useraddr);
|
||||||
break;
|
break;
|
||||||
|
@ -1632,21 +1627,6 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
|
||||||
case ETHTOOL_GPERMADDR:
|
case ETHTOOL_GPERMADDR:
|
||||||
rc = ethtool_get_perm_addr(dev, useraddr);
|
rc = ethtool_get_perm_addr(dev, useraddr);
|
||||||
break;
|
break;
|
||||||
case ETHTOOL_GUFO:
|
|
||||||
rc = ethtool_get_value(dev, useraddr, ethcmd,
|
|
||||||
(dev->ethtool_ops->get_ufo ?
|
|
||||||
dev->ethtool_ops->get_ufo :
|
|
||||||
ethtool_op_get_ufo));
|
|
||||||
break;
|
|
||||||
case ETHTOOL_SUFO:
|
|
||||||
rc = ethtool_set_ufo(dev, useraddr);
|
|
||||||
break;
|
|
||||||
case ETHTOOL_GGSO:
|
|
||||||
rc = ethtool_get_gso(dev, useraddr);
|
|
||||||
break;
|
|
||||||
case ETHTOOL_SGSO:
|
|
||||||
rc = ethtool_set_gso(dev, useraddr);
|
|
||||||
break;
|
|
||||||
case ETHTOOL_GFLAGS:
|
case ETHTOOL_GFLAGS:
|
||||||
rc = ethtool_get_value(dev, useraddr, ethcmd,
|
rc = ethtool_get_value(dev, useraddr, ethcmd,
|
||||||
(dev->ethtool_ops->get_flags ?
|
(dev->ethtool_ops->get_flags ?
|
||||||
|
@ -1677,12 +1657,6 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
|
||||||
case ETHTOOL_SRXCLSRLINS:
|
case ETHTOOL_SRXCLSRLINS:
|
||||||
rc = ethtool_set_rxnfc(dev, ethcmd, useraddr);
|
rc = ethtool_set_rxnfc(dev, ethcmd, useraddr);
|
||||||
break;
|
break;
|
||||||
case ETHTOOL_GGRO:
|
|
||||||
rc = ethtool_get_gro(dev, useraddr);
|
|
||||||
break;
|
|
||||||
case ETHTOOL_SGRO:
|
|
||||||
rc = ethtool_set_gro(dev, useraddr);
|
|
||||||
break;
|
|
||||||
case ETHTOOL_FLASHDEV:
|
case ETHTOOL_FLASHDEV:
|
||||||
rc = ethtool_flash_device(dev, useraddr);
|
rc = ethtool_flash_device(dev, useraddr);
|
||||||
break;
|
break;
|
||||||
|
@ -1704,6 +1678,22 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
|
||||||
case ETHTOOL_SRXFHINDIR:
|
case ETHTOOL_SRXFHINDIR:
|
||||||
rc = ethtool_set_rxfh_indir(dev, useraddr);
|
rc = ethtool_set_rxfh_indir(dev, useraddr);
|
||||||
break;
|
break;
|
||||||
|
case ETHTOOL_GTXCSUM:
|
||||||
|
case ETHTOOL_GSG:
|
||||||
|
case ETHTOOL_GTSO:
|
||||||
|
case ETHTOOL_GUFO:
|
||||||
|
case ETHTOOL_GGSO:
|
||||||
|
case ETHTOOL_GGRO:
|
||||||
|
rc = ethtool_get_one_feature(dev, useraddr, ethcmd);
|
||||||
|
break;
|
||||||
|
case ETHTOOL_STXCSUM:
|
||||||
|
case ETHTOOL_SSG:
|
||||||
|
case ETHTOOL_STSO:
|
||||||
|
case ETHTOOL_SUFO:
|
||||||
|
case ETHTOOL_SGSO:
|
||||||
|
case ETHTOOL_SGRO:
|
||||||
|
rc = ethtool_set_one_feature(dev, useraddr, ethcmd);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
rc = -EOPNOTSUPP;
|
rc = -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче