net: Introduce new feature setting ops
This introduces a new framework to handle device features setting. It consists of: - new fields in struct net_device: + hw_features - features that hw/driver supports toggling + wanted_features - features that user wants enabled, when possible - new netdev_ops: + feat = ndo_fix_features(dev, feat) - API checking constraints for enabling features or their combinations + ndo_set_features(dev) - API updating hardware state to match changed dev->features - new ethtool commands: + ETHTOOL_GFEATURES/ETHTOOL_SFEATURES: get/set dev->wanted_features and trigger device reconfiguration if resulting dev->features changed + ETHTOOL_GSTRINGS(ETH_SS_FEATURES): get feature bits names (meaning) 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:
committed by
David S. Miller
parent
0a41770477
commit
5455c6998d
@@ -172,10 +172,120 @@ EXPORT_SYMBOL(ethtool_ntuple_flush);
|
||||
|
||||
/* Handlers for each ethtool command */
|
||||
|
||||
#define ETHTOOL_DEV_FEATURE_WORDS 1
|
||||
|
||||
static int ethtool_get_features(struct net_device *dev, void __user *useraddr)
|
||||
{
|
||||
struct ethtool_gfeatures cmd = {
|
||||
.cmd = ETHTOOL_GFEATURES,
|
||||
.size = ETHTOOL_DEV_FEATURE_WORDS,
|
||||
};
|
||||
struct ethtool_get_features_block features[ETHTOOL_DEV_FEATURE_WORDS] = {
|
||||
{
|
||||
.available = dev->hw_features,
|
||||
.requested = dev->wanted_features,
|
||||
.active = dev->features,
|
||||
.never_changed = NETIF_F_NEVER_CHANGE,
|
||||
},
|
||||
};
|
||||
u32 __user *sizeaddr;
|
||||
u32 copy_size;
|
||||
|
||||
sizeaddr = useraddr + offsetof(struct ethtool_gfeatures, size);
|
||||
if (get_user(copy_size, sizeaddr))
|
||||
return -EFAULT;
|
||||
|
||||
if (copy_size > ETHTOOL_DEV_FEATURE_WORDS)
|
||||
copy_size = ETHTOOL_DEV_FEATURE_WORDS;
|
||||
|
||||
if (copy_to_user(useraddr, &cmd, sizeof(cmd)))
|
||||
return -EFAULT;
|
||||
useraddr += sizeof(cmd);
|
||||
if (copy_to_user(useraddr, features, copy_size * sizeof(*features)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ethtool_set_features(struct net_device *dev, void __user *useraddr)
|
||||
{
|
||||
struct ethtool_sfeatures cmd;
|
||||
struct ethtool_set_features_block features[ETHTOOL_DEV_FEATURE_WORDS];
|
||||
int ret = 0;
|
||||
|
||||
if (copy_from_user(&cmd, useraddr, sizeof(cmd)))
|
||||
return -EFAULT;
|
||||
useraddr += sizeof(cmd);
|
||||
|
||||
if (cmd.size != ETHTOOL_DEV_FEATURE_WORDS)
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(features, useraddr, sizeof(features)))
|
||||
return -EFAULT;
|
||||
|
||||
if (features[0].valid & ~NETIF_F_ETHTOOL_BITS)
|
||||
return -EINVAL;
|
||||
|
||||
if (features[0].valid & ~dev->hw_features) {
|
||||
features[0].valid &= dev->hw_features;
|
||||
ret |= ETHTOOL_F_UNSUPPORTED;
|
||||
}
|
||||
|
||||
dev->wanted_features &= ~features[0].valid;
|
||||
dev->wanted_features |= features[0].valid & features[0].requested;
|
||||
netdev_update_features(dev);
|
||||
|
||||
if ((dev->wanted_features ^ dev->features) & features[0].valid)
|
||||
ret |= ETHTOOL_F_WISH;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char netdev_features_strings[ETHTOOL_DEV_FEATURE_WORDS * 32][ETH_GSTRING_LEN] = {
|
||||
/* NETIF_F_SG */ "tx-scatter-gather",
|
||||
/* NETIF_F_IP_CSUM */ "tx-checksum-ipv4",
|
||||
/* NETIF_F_NO_CSUM */ "tx-checksum-unneeded",
|
||||
/* NETIF_F_HW_CSUM */ "tx-checksum-ip-generic",
|
||||
/* NETIF_F_IPV6_CSUM */ "tx_checksum-ipv6",
|
||||
/* NETIF_F_HIGHDMA */ "highdma",
|
||||
/* NETIF_F_FRAGLIST */ "tx-scatter-gather-fraglist",
|
||||
/* NETIF_F_HW_VLAN_TX */ "tx-vlan-hw-insert",
|
||||
|
||||
/* NETIF_F_HW_VLAN_RX */ "rx-vlan-hw-parse",
|
||||
/* NETIF_F_HW_VLAN_FILTER */ "rx-vlan-filter",
|
||||
/* NETIF_F_VLAN_CHALLENGED */ "vlan-challenged",
|
||||
/* NETIF_F_GSO */ "tx-generic-segmentation",
|
||||
/* NETIF_F_LLTX */ "tx-lockless",
|
||||
/* NETIF_F_NETNS_LOCAL */ "netns-local",
|
||||
/* NETIF_F_GRO */ "rx-gro",
|
||||
/* NETIF_F_LRO */ "rx-lro",
|
||||
|
||||
/* NETIF_F_TSO */ "tx-tcp-segmentation",
|
||||
/* NETIF_F_UFO */ "tx-udp-fragmentation",
|
||||
/* NETIF_F_GSO_ROBUST */ "tx-gso-robust",
|
||||
/* NETIF_F_TSO_ECN */ "tx-tcp-ecn-segmentation",
|
||||
/* NETIF_F_TSO6 */ "tx-tcp6-segmentation",
|
||||
/* NETIF_F_FSO */ "tx-fcoe-segmentation",
|
||||
"",
|
||||
"",
|
||||
|
||||
/* NETIF_F_FCOE_CRC */ "tx-checksum-fcoe-crc",
|
||||
/* NETIF_F_SCTP_CSUM */ "tx-checksum-sctp",
|
||||
/* NETIF_F_FCOE_MTU */ "fcoe-mtu",
|
||||
/* NETIF_F_NTUPLE */ "rx-ntuple-filter",
|
||||
/* NETIF_F_RXHASH */ "rx-hashing",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
};
|
||||
|
||||
static int __ethtool_get_sset_count(struct net_device *dev, int sset)
|
||||
{
|
||||
const struct ethtool_ops *ops = dev->ethtool_ops;
|
||||
|
||||
if (sset == ETH_SS_FEATURES)
|
||||
return ARRAY_SIZE(netdev_features_strings);
|
||||
|
||||
if (ops && ops->get_sset_count && ops->get_strings)
|
||||
return ops->get_sset_count(dev, sset);
|
||||
else
|
||||
@@ -187,8 +297,12 @@ static void __ethtool_get_strings(struct net_device *dev,
|
||||
{
|
||||
const struct ethtool_ops *ops = dev->ethtool_ops;
|
||||
|
||||
/* ops->get_strings is valid because checked earlier */
|
||||
ops->get_strings(dev, stringset, data);
|
||||
if (stringset == ETH_SS_FEATURES)
|
||||
memcpy(data, netdev_features_strings,
|
||||
sizeof(netdev_features_strings));
|
||||
else
|
||||
/* ops->get_strings is valid because checked earlier */
|
||||
ops->get_strings(dev, stringset, data);
|
||||
}
|
||||
|
||||
static u32 ethtool_get_feature_mask(u32 eth_cmd)
|
||||
@@ -1533,6 +1647,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
|
||||
case ETHTOOL_GRXCLSRLCNT:
|
||||
case ETHTOOL_GRXCLSRULE:
|
||||
case ETHTOOL_GRXCLSRLALL:
|
||||
case ETHTOOL_GFEATURES:
|
||||
break;
|
||||
default:
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
@@ -1678,6 +1793,12 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
|
||||
case ETHTOOL_SRXFHINDIR:
|
||||
rc = ethtool_set_rxfh_indir(dev, useraddr);
|
||||
break;
|
||||
case ETHTOOL_GFEATURES:
|
||||
rc = ethtool_get_features(dev, useraddr);
|
||||
break;
|
||||
case ETHTOOL_SFEATURES:
|
||||
rc = ethtool_set_features(dev, useraddr);
|
||||
break;
|
||||
case ETHTOOL_GTXCSUM:
|
||||
case ETHTOOL_GSG:
|
||||
case ETHTOOL_GTSO:
|
||||
|
Reference in New Issue
Block a user