net: ethtool: extend RXNFC API to support RSS spreading of filter matches
We use a two-step process to configure a filter with RSS spreading. First, the RSS context is allocated and configured using ETHTOOL_SRSSH; this returns an identifier (rss_context) which can then be passed to subsequent invocations of ETHTOOL_SRXCLSRLINS to specify that the offset from the RSS indirection table lookup should be added to the queue number (ring_cookie) when delivering the packet. Drivers for devices which can only use the indirection table entry directly (not add it to a base queue number) should reject rule insertions combining RSS with a nonzero ring_cookie. Signed-off-by: Edward Cree <ecree@solarflare.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
571e6776ad
commit
84a1d9c482
|
@ -371,6 +371,11 @@ struct ethtool_ops {
|
|||
u8 *hfunc);
|
||||
int (*set_rxfh)(struct net_device *, const u32 *indir,
|
||||
const u8 *key, const u8 hfunc);
|
||||
int (*get_rxfh_context)(struct net_device *, u32 *indir, u8 *key,
|
||||
u8 *hfunc, u32 rss_context);
|
||||
int (*set_rxfh_context)(struct net_device *, const u32 *indir,
|
||||
const u8 *key, const u8 hfunc,
|
||||
u32 *rss_context, bool delete);
|
||||
void (*get_channels)(struct net_device *, struct ethtool_channels *);
|
||||
int (*set_channels)(struct net_device *, struct ethtool_channels *);
|
||||
int (*get_dump_flag)(struct net_device *, struct ethtool_dump *);
|
||||
|
|
|
@ -914,12 +914,15 @@ static inline __u64 ethtool_get_flow_spec_ring_vf(__u64 ring_cookie)
|
|||
* @flow_type: Type of flow to be affected, e.g. %TCP_V4_FLOW
|
||||
* @data: Command-dependent value
|
||||
* @fs: Flow classification rule
|
||||
* @rss_context: RSS context to be affected
|
||||
* @rule_cnt: Number of rules to be affected
|
||||
* @rule_locs: Array of used rule locations
|
||||
*
|
||||
* For %ETHTOOL_GRXFH and %ETHTOOL_SRXFH, @data is a bitmask indicating
|
||||
* the fields included in the flow hash, e.g. %RXH_IP_SRC. The following
|
||||
* structure fields must not be used.
|
||||
* structure fields must not be used, except that if @flow_type includes
|
||||
* the %FLOW_RSS flag, then @rss_context determines which RSS context to
|
||||
* act on.
|
||||
*
|
||||
* For %ETHTOOL_GRXRINGS, @data is set to the number of RX rings/queues
|
||||
* on return.
|
||||
|
@ -931,7 +934,9 @@ static inline __u64 ethtool_get_flow_spec_ring_vf(__u64 ring_cookie)
|
|||
* set in @data then special location values should not be used.
|
||||
*
|
||||
* For %ETHTOOL_GRXCLSRULE, @fs.@location specifies the location of an
|
||||
* existing rule on entry and @fs contains the rule on return.
|
||||
* existing rule on entry and @fs contains the rule on return; if
|
||||
* @fs.@flow_type includes the %FLOW_RSS flag, then @rss_context is
|
||||
* filled with the RSS context ID associated with the rule.
|
||||
*
|
||||
* For %ETHTOOL_GRXCLSRLALL, @rule_cnt specifies the array size of the
|
||||
* user buffer for @rule_locs on entry. On return, @data is the size
|
||||
|
@ -942,7 +947,11 @@ static inline __u64 ethtool_get_flow_spec_ring_vf(__u64 ring_cookie)
|
|||
* For %ETHTOOL_SRXCLSRLINS, @fs specifies the rule to add or update.
|
||||
* @fs.@location either specifies the location to use or is a special
|
||||
* location value with %RX_CLS_LOC_SPECIAL flag set. On return,
|
||||
* @fs.@location is the actual rule location.
|
||||
* @fs.@location is the actual rule location. If @fs.@flow_type
|
||||
* includes the %FLOW_RSS flag, @rss_context is the RSS context ID to
|
||||
* use for flow spreading traffic which matches this rule. The value
|
||||
* from the rxfh indirection table will be added to @fs.@ring_cookie
|
||||
* to choose which ring to deliver to.
|
||||
*
|
||||
* For %ETHTOOL_SRXCLSRLDEL, @fs.@location specifies the location of an
|
||||
* existing rule on entry.
|
||||
|
@ -963,7 +972,10 @@ struct ethtool_rxnfc {
|
|||
__u32 flow_type;
|
||||
__u64 data;
|
||||
struct ethtool_rx_flow_spec fs;
|
||||
__u32 rule_cnt;
|
||||
union {
|
||||
__u32 rule_cnt;
|
||||
__u32 rss_context;
|
||||
};
|
||||
__u32 rule_locs[0];
|
||||
};
|
||||
|
||||
|
@ -990,7 +1002,11 @@ struct ethtool_rxfh_indir {
|
|||
/**
|
||||
* struct ethtool_rxfh - command to get/set RX flow hash indir or/and hash key.
|
||||
* @cmd: Specific command number - %ETHTOOL_GRSSH or %ETHTOOL_SRSSH
|
||||
* @rss_context: RSS context identifier.
|
||||
* @rss_context: RSS context identifier. Context 0 is the default for normal
|
||||
* traffic; other contexts can be referenced as the destination for RX flow
|
||||
* classification rules. %ETH_RXFH_CONTEXT_ALLOC is used with command
|
||||
* %ETHTOOL_SRSSH to allocate a new RSS context; on return this field will
|
||||
* contain the ID of the newly allocated context.
|
||||
* @indir_size: On entry, the array size of the user buffer for the
|
||||
* indirection table, which may be zero, or (for %ETHTOOL_SRSSH),
|
||||
* %ETH_RXFH_INDIR_NO_CHANGE. On return from %ETHTOOL_GRSSH,
|
||||
|
@ -1009,7 +1025,8 @@ struct ethtool_rxfh_indir {
|
|||
* size should be returned. For %ETHTOOL_SRSSH, an @indir_size of
|
||||
* %ETH_RXFH_INDIR_NO_CHANGE means that indir table setting is not requested
|
||||
* and a @indir_size of zero means the indir table should be reset to default
|
||||
* values. An hfunc of zero means that hash function setting is not requested.
|
||||
* values (if @rss_context == 0) or that the RSS context should be deleted.
|
||||
* An hfunc of zero means that hash function setting is not requested.
|
||||
*/
|
||||
struct ethtool_rxfh {
|
||||
__u32 cmd;
|
||||
|
@ -1021,6 +1038,7 @@ struct ethtool_rxfh {
|
|||
__u32 rsvd32;
|
||||
__u32 rss_config[0];
|
||||
};
|
||||
#define ETH_RXFH_CONTEXT_ALLOC 0xffffffff
|
||||
#define ETH_RXFH_INDIR_NO_CHANGE 0xffffffff
|
||||
|
||||
/**
|
||||
|
@ -1635,6 +1653,8 @@ static inline int ethtool_validate_duplex(__u8 duplex)
|
|||
/* Flag to enable additional fields in struct ethtool_rx_flow_spec */
|
||||
#define FLOW_EXT 0x80000000
|
||||
#define FLOW_MAC_EXT 0x40000000
|
||||
/* Flag to enable RSS spreading of traffic matching rule (nfc only) */
|
||||
#define FLOW_RSS 0x20000000
|
||||
|
||||
/* L3-L4 network traffic flow hash options */
|
||||
#define RXH_L2DA (1 << 1)
|
||||
|
|
|
@ -1022,6 +1022,15 @@ static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev,
|
|||
if (copy_from_user(&info, useraddr, info_size))
|
||||
return -EFAULT;
|
||||
|
||||
/* If FLOW_RSS was requested then user-space must be using the
|
||||
* new definition, as FLOW_RSS is newer.
|
||||
*/
|
||||
if (cmd == ETHTOOL_GRXFH && info.flow_type & FLOW_RSS) {
|
||||
info_size = sizeof(info);
|
||||
if (copy_from_user(&info, useraddr, info_size))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (info.cmd == ETHTOOL_GRXCLSRLALL) {
|
||||
if (info.rule_cnt > 0) {
|
||||
if (info.rule_cnt <= KMALLOC_MAX_SIZE / sizeof(u32))
|
||||
|
@ -1251,9 +1260,11 @@ static noinline_for_stack int ethtool_get_rxfh(struct net_device *dev,
|
|||
user_key_size = rxfh.key_size;
|
||||
|
||||
/* Check that reserved fields are 0 for now */
|
||||
if (rxfh.rss_context || rxfh.rsvd8[0] || rxfh.rsvd8[1] ||
|
||||
rxfh.rsvd8[2] || rxfh.rsvd32)
|
||||
if (rxfh.rsvd8[0] || rxfh.rsvd8[1] || rxfh.rsvd8[2] || rxfh.rsvd32)
|
||||
return -EINVAL;
|
||||
/* Most drivers don't handle rss_context, check it's 0 as well */
|
||||
if (rxfh.rss_context && !ops->get_rxfh_context)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
rxfh.indir_size = dev_indir_size;
|
||||
rxfh.key_size = dev_key_size;
|
||||
|
@ -1276,7 +1287,12 @@ static noinline_for_stack int ethtool_get_rxfh(struct net_device *dev,
|
|||
if (user_key_size)
|
||||
hkey = rss_config + indir_bytes;
|
||||
|
||||
ret = dev->ethtool_ops->get_rxfh(dev, indir, hkey, &dev_hfunc);
|
||||
if (rxfh.rss_context)
|
||||
ret = dev->ethtool_ops->get_rxfh_context(dev, indir, hkey,
|
||||
&dev_hfunc,
|
||||
rxfh.rss_context);
|
||||
else
|
||||
ret = dev->ethtool_ops->get_rxfh(dev, indir, hkey, &dev_hfunc);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
|
@ -1306,6 +1322,7 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
|
|||
u8 *hkey = NULL;
|
||||
u8 *rss_config;
|
||||
u32 rss_cfg_offset = offsetof(struct ethtool_rxfh, rss_config[0]);
|
||||
bool delete = false;
|
||||
|
||||
if (!ops->get_rxnfc || !ops->set_rxfh)
|
||||
return -EOPNOTSUPP;
|
||||
|
@ -1319,9 +1336,11 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
|
|||
return -EFAULT;
|
||||
|
||||
/* Check that reserved fields are 0 for now */
|
||||
if (rxfh.rss_context || rxfh.rsvd8[0] || rxfh.rsvd8[1] ||
|
||||
rxfh.rsvd8[2] || rxfh.rsvd32)
|
||||
if (rxfh.rsvd8[0] || rxfh.rsvd8[1] || rxfh.rsvd8[2] || rxfh.rsvd32)
|
||||
return -EINVAL;
|
||||
/* Most drivers don't handle rss_context, check it's 0 as well */
|
||||
if (rxfh.rss_context && !ops->set_rxfh_context)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* If either indir, hash key or function is valid, proceed further.
|
||||
* Must request at least one change: indir size, hash key or function.
|
||||
|
@ -1346,7 +1365,8 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
|
|||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* rxfh.indir_size == 0 means reset the indir table to default.
|
||||
/* rxfh.indir_size == 0 means reset the indir table to default (master
|
||||
* context) or delete the context (other RSS contexts).
|
||||
* rxfh.indir_size == ETH_RXFH_INDIR_NO_CHANGE means leave it unchanged.
|
||||
*/
|
||||
if (rxfh.indir_size &&
|
||||
|
@ -1359,9 +1379,13 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
|
|||
if (ret)
|
||||
goto out;
|
||||
} else if (rxfh.indir_size == 0) {
|
||||
indir = (u32 *)rss_config;
|
||||
for (i = 0; i < dev_indir_size; i++)
|
||||
indir[i] = ethtool_rxfh_indir_default(i, rx_rings.data);
|
||||
if (rxfh.rss_context == 0) {
|
||||
indir = (u32 *)rss_config;
|
||||
for (i = 0; i < dev_indir_size; i++)
|
||||
indir[i] = ethtool_rxfh_indir_default(i, rx_rings.data);
|
||||
} else {
|
||||
delete = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (rxfh.key_size) {
|
||||
|
@ -1374,15 +1398,25 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
|
|||
}
|
||||
}
|
||||
|
||||
ret = ops->set_rxfh(dev, indir, hkey, rxfh.hfunc);
|
||||
if (rxfh.rss_context)
|
||||
ret = ops->set_rxfh_context(dev, indir, hkey, rxfh.hfunc,
|
||||
&rxfh.rss_context, delete);
|
||||
else
|
||||
ret = ops->set_rxfh(dev, indir, hkey, rxfh.hfunc);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* indicate whether rxfh was set to default */
|
||||
if (rxfh.indir_size == 0)
|
||||
dev->priv_flags &= ~IFF_RXFH_CONFIGURED;
|
||||
else if (rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE)
|
||||
dev->priv_flags |= IFF_RXFH_CONFIGURED;
|
||||
if (copy_to_user(useraddr + offsetof(struct ethtool_rxfh, rss_context),
|
||||
&rxfh.rss_context, sizeof(rxfh.rss_context)))
|
||||
ret = -EFAULT;
|
||||
|
||||
if (!rxfh.rss_context) {
|
||||
/* indicate whether rxfh was set to default */
|
||||
if (rxfh.indir_size == 0)
|
||||
dev->priv_flags &= ~IFF_RXFH_CONFIGURED;
|
||||
else if (rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE)
|
||||
dev->priv_flags |= IFF_RXFH_CONFIGURED;
|
||||
}
|
||||
|
||||
out:
|
||||
kfree(rss_config);
|
||||
|
|
Loading…
Reference in a new issue