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:
Родитель
0a41770477
Коммит
5455c6998d
|
@ -251,6 +251,7 @@ enum ethtool_stringset {
|
|||
ETH_SS_STATS,
|
||||
ETH_SS_PRIV_FLAGS,
|
||||
ETH_SS_NTUPLE_FILTERS,
|
||||
ETH_SS_FEATURES,
|
||||
};
|
||||
|
||||
/* for passing string sets for data tagging */
|
||||
|
@ -523,6 +524,87 @@ struct ethtool_flash {
|
|||
char data[ETHTOOL_FLASH_MAX_FILENAME];
|
||||
};
|
||||
|
||||
/* for returning and changing feature sets */
|
||||
|
||||
/**
|
||||
* struct ethtool_get_features_block - block with state of 32 features
|
||||
* @available: mask of changeable features
|
||||
* @requested: mask of features requested to be enabled if possible
|
||||
* @active: mask of currently enabled features
|
||||
* @never_changed: mask of features not changeable for any device
|
||||
*/
|
||||
struct ethtool_get_features_block {
|
||||
__u32 available;
|
||||
__u32 requested;
|
||||
__u32 active;
|
||||
__u32 never_changed;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ethtool_gfeatures - command to get state of device's features
|
||||
* @cmd: command number = %ETHTOOL_GFEATURES
|
||||
* @size: in: number of elements in the features[] array;
|
||||
* out: number of elements in features[] needed to hold all features
|
||||
* @features: state of features
|
||||
*/
|
||||
struct ethtool_gfeatures {
|
||||
__u32 cmd;
|
||||
__u32 size;
|
||||
struct ethtool_get_features_block features[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ethtool_set_features_block - block with request for 32 features
|
||||
* @valid: mask of features to be changed
|
||||
* @requested: values of features to be changed
|
||||
*/
|
||||
struct ethtool_set_features_block {
|
||||
__u32 valid;
|
||||
__u32 requested;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ethtool_sfeatures - command to request change in device's features
|
||||
* @cmd: command number = %ETHTOOL_SFEATURES
|
||||
* @size: array size of the features[] array
|
||||
* @features: feature change masks
|
||||
*/
|
||||
struct ethtool_sfeatures {
|
||||
__u32 cmd;
|
||||
__u32 size;
|
||||
struct ethtool_set_features_block features[0];
|
||||
};
|
||||
|
||||
/*
|
||||
* %ETHTOOL_SFEATURES changes features present in features[].valid to the
|
||||
* values of corresponding bits in features[].requested. Bits in .requested
|
||||
* not set in .valid or not changeable are ignored.
|
||||
*
|
||||
* Returns %EINVAL when .valid contains undefined or never-changable bits
|
||||
* or size is not equal to required number of features words (32-bit blocks).
|
||||
* Returns >= 0 if request was completed; bits set in the value mean:
|
||||
* %ETHTOOL_F_UNSUPPORTED - there were bits set in .valid that are not
|
||||
* changeable (not present in %ETHTOOL_GFEATURES' features[].available)
|
||||
* those bits were ignored.
|
||||
* %ETHTOOL_F_WISH - some or all changes requested were recorded but the
|
||||
* resulting state of bits masked by .valid is not equal to .requested.
|
||||
* Probably there are other device-specific constraints on some features
|
||||
* in the set. When %ETHTOOL_F_UNSUPPORTED is set, .valid is considered
|
||||
* here as though ignored bits were cleared.
|
||||
*
|
||||
* Meaning of bits in the masks are obtained by %ETHTOOL_GSSET_INFO (number of
|
||||
* bits in the arrays - always multiple of 32) and %ETHTOOL_GSTRINGS commands
|
||||
* for ETH_SS_FEATURES string set. First entry in the table corresponds to least
|
||||
* significant bit in features[0] fields. Empty strings mark undefined features.
|
||||
*/
|
||||
enum ethtool_sfeatures_retval_bits {
|
||||
ETHTOOL_F_UNSUPPORTED__BIT,
|
||||
ETHTOOL_F_WISH__BIT,
|
||||
};
|
||||
|
||||
#define ETHTOOL_F_UNSUPPORTED (1 << ETHTOOL_F_UNSUPPORTED__BIT)
|
||||
#define ETHTOOL_F_WISH (1 << ETHTOOL_F_WISH__BIT)
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
#include <linux/rculist.h>
|
||||
|
@ -744,6 +826,9 @@ struct ethtool_ops {
|
|||
#define ETHTOOL_GRXFHINDIR 0x00000038 /* Get RX flow hash indir'n table */
|
||||
#define ETHTOOL_SRXFHINDIR 0x00000039 /* Set RX flow hash indir'n table */
|
||||
|
||||
#define ETHTOOL_GFEATURES 0x0000003a /* Get device offload settings */
|
||||
#define ETHTOOL_SFEATURES 0x0000003b /* Change device offload settings */
|
||||
|
||||
/* compatibility with older code */
|
||||
#define SPARC_ETH_GSET ETHTOOL_GSET
|
||||
#define SPARC_ETH_SSET ETHTOOL_SSET
|
||||
|
|
|
@ -791,6 +791,18 @@ struct netdev_tc_txq {
|
|||
*
|
||||
* int (*ndo_del_slave)(struct net_device *dev, struct net_device *slave_dev);
|
||||
* Called to release previously enslaved netdev.
|
||||
*
|
||||
* Feature/offload setting functions.
|
||||
* u32 (*ndo_fix_features)(struct net_device *dev, u32 features);
|
||||
* Adjusts the requested feature flags according to device-specific
|
||||
* constraints, and returns the resulting flags. Must not modify
|
||||
* the device state.
|
||||
*
|
||||
* int (*ndo_set_features)(struct net_device *dev, u32 features);
|
||||
* Called to update device configuration to new features. Passed
|
||||
* feature set might be less than what was returned by ndo_fix_features()).
|
||||
* Must return >0 or -errno if it changed dev->features itself.
|
||||
*
|
||||
*/
|
||||
#define HAVE_NET_DEVICE_OPS
|
||||
struct net_device_ops {
|
||||
|
@ -874,6 +886,10 @@ struct net_device_ops {
|
|||
struct net_device *slave_dev);
|
||||
int (*ndo_del_slave)(struct net_device *dev,
|
||||
struct net_device *slave_dev);
|
||||
u32 (*ndo_fix_features)(struct net_device *dev,
|
||||
u32 features);
|
||||
int (*ndo_set_features)(struct net_device *dev,
|
||||
u32 features);
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -925,12 +941,18 @@ struct net_device {
|
|||
struct list_head napi_list;
|
||||
struct list_head unreg_list;
|
||||
|
||||
/* Net device features */
|
||||
/* currently active device features */
|
||||
u32 features;
|
||||
|
||||
/* user-changeable features */
|
||||
u32 hw_features;
|
||||
/* user-requested features */
|
||||
u32 wanted_features;
|
||||
/* VLAN feature mask */
|
||||
u32 vlan_features;
|
||||
|
||||
/* Net device feature bits; if you change something,
|
||||
* also update netdev_features_strings[] in ethtool.c */
|
||||
|
||||
#define NETIF_F_SG 1 /* Scatter/gather IO. */
|
||||
#define NETIF_F_IP_CSUM 2 /* Can checksum TCP/UDP over IPv4. */
|
||||
#define NETIF_F_NO_CSUM 4 /* Does not require checksum. F.e. loopack. */
|
||||
|
@ -966,6 +988,12 @@ struct net_device {
|
|||
#define NETIF_F_TSO6 (SKB_GSO_TCPV6 << NETIF_F_GSO_SHIFT)
|
||||
#define NETIF_F_FSO (SKB_GSO_FCOE << NETIF_F_GSO_SHIFT)
|
||||
|
||||
/* Features valid for ethtool to change */
|
||||
/* = all defined minus driver/device-class-related */
|
||||
#define NETIF_F_NEVER_CHANGE (NETIF_F_HIGHDMA | NETIF_F_VLAN_CHALLENGED | \
|
||||
NETIF_F_LLTX | NETIF_F_NETNS_LOCAL)
|
||||
#define NETIF_F_ETHTOOL_BITS (0x1f3fffff & ~NETIF_F_NEVER_CHANGE)
|
||||
|
||||
/* List of features with software fallbacks. */
|
||||
#define NETIF_F_GSO_SOFTWARE (NETIF_F_TSO | NETIF_F_TSO_ECN | \
|
||||
NETIF_F_TSO6 | NETIF_F_UFO)
|
||||
|
@ -2428,8 +2456,13 @@ extern char *netdev_drivername(const struct net_device *dev, char *buffer, int l
|
|||
|
||||
extern void linkwatch_run_queue(void);
|
||||
|
||||
static inline u32 netdev_get_wanted_features(struct net_device *dev)
|
||||
{
|
||||
return (dev->features & ~dev->hw_features) | dev->wanted_features;
|
||||
}
|
||||
u32 netdev_increment_features(u32 all, u32 one, u32 mask);
|
||||
u32 netdev_fix_features(struct net_device *dev, u32 features);
|
||||
void netdev_update_features(struct net_device *dev);
|
||||
|
||||
void netif_stacked_transfer_operstate(const struct net_device *rootdev,
|
||||
struct net_device *dev);
|
||||
|
|
|
@ -5302,6 +5302,37 @@ u32 netdev_fix_features(struct net_device *dev, u32 features)
|
|||
}
|
||||
EXPORT_SYMBOL(netdev_fix_features);
|
||||
|
||||
void netdev_update_features(struct net_device *dev)
|
||||
{
|
||||
u32 features;
|
||||
int err = 0;
|
||||
|
||||
features = netdev_get_wanted_features(dev);
|
||||
|
||||
if (dev->netdev_ops->ndo_fix_features)
|
||||
features = dev->netdev_ops->ndo_fix_features(dev, features);
|
||||
|
||||
/* driver might be less strict about feature dependencies */
|
||||
features = netdev_fix_features(dev, features);
|
||||
|
||||
if (dev->features == features)
|
||||
return;
|
||||
|
||||
netdev_info(dev, "Features changed: 0x%08x -> 0x%08x\n",
|
||||
dev->features, features);
|
||||
|
||||
if (dev->netdev_ops->ndo_set_features)
|
||||
err = dev->netdev_ops->ndo_set_features(dev, features);
|
||||
|
||||
if (!err)
|
||||
dev->features = features;
|
||||
else if (err < 0)
|
||||
netdev_err(dev,
|
||||
"set_features() failed (%d); wanted 0x%08x, left 0x%08x\n",
|
||||
err, features, dev->features);
|
||||
}
|
||||
EXPORT_SYMBOL(netdev_update_features);
|
||||
|
||||
/**
|
||||
* netif_stacked_transfer_operstate - transfer operstate
|
||||
* @rootdev: the root or lower level device to transfer state from
|
||||
|
@ -5436,15 +5467,18 @@ int register_netdevice(struct net_device *dev)
|
|||
if (dev->iflink == -1)
|
||||
dev->iflink = dev->ifindex;
|
||||
|
||||
/* Enable software offloads by default - will be stripped in
|
||||
* netdev_fix_features() if not supported. */
|
||||
dev->features |= NETIF_F_SOFT_FEATURES;
|
||||
/* Transfer changeable features to wanted_features and enable
|
||||
* software offloads (GSO and GRO).
|
||||
*/
|
||||
dev->hw_features |= NETIF_F_SOFT_FEATURES;
|
||||
dev->wanted_features = (dev->features & dev->hw_features)
|
||||
| NETIF_F_SOFT_FEATURES;
|
||||
|
||||
/* Avoid warning from netdev_fix_features() for GSO without SG */
|
||||
if (!(dev->features & NETIF_F_SG))
|
||||
dev->features &= ~NETIF_F_GSO;
|
||||
if (!(dev->wanted_features & NETIF_F_SG))
|
||||
dev->wanted_features &= ~NETIF_F_GSO;
|
||||
|
||||
dev->features = netdev_fix_features(dev, dev->features);
|
||||
netdev_update_features(dev);
|
||||
|
||||
/* Enable GRO and NETIF_F_HIGHDMA for vlans by default,
|
||||
* vlan_dev_init() will do the dev->features check, so these features
|
||||
|
|
|
@ -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:
|
||||
|
|
Загрузка…
Ссылка в новой задаче