|
@@ -100,6 +100,12 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN]
|
|
|
[NETIF_F_BUSY_POLL_BIT] = "busy-poll",
|
|
|
};
|
|
|
|
|
|
+static const char
|
|
|
+rss_hash_func_strings[ETH_RSS_HASH_FUNCS_COUNT][ETH_GSTRING_LEN] = {
|
|
|
+ [ETH_RSS_HASH_TOP_BIT] = "toeplitz",
|
|
|
+ [ETH_RSS_HASH_XOR_BIT] = "xor",
|
|
|
+};
|
|
|
+
|
|
|
static int ethtool_get_features(struct net_device *dev, void __user *useraddr)
|
|
|
{
|
|
|
struct ethtool_gfeatures cmd = {
|
|
@@ -185,6 +191,9 @@ static int __ethtool_get_sset_count(struct net_device *dev, int sset)
|
|
|
if (sset == ETH_SS_FEATURES)
|
|
|
return ARRAY_SIZE(netdev_features_strings);
|
|
|
|
|
|
+ if (sset == ETH_SS_RSS_HASH_FUNCS)
|
|
|
+ return ARRAY_SIZE(rss_hash_func_strings);
|
|
|
+
|
|
|
if (ops->get_sset_count && ops->get_strings)
|
|
|
return ops->get_sset_count(dev, sset);
|
|
|
else
|
|
@@ -199,6 +208,9 @@ static void __ethtool_get_strings(struct net_device *dev,
|
|
|
if (stringset == ETH_SS_FEATURES)
|
|
|
memcpy(data, netdev_features_strings,
|
|
|
sizeof(netdev_features_strings));
|
|
|
+ else if (stringset == ETH_SS_RSS_HASH_FUNCS)
|
|
|
+ memcpy(data, rss_hash_func_strings,
|
|
|
+ sizeof(rss_hash_func_strings));
|
|
|
else
|
|
|
/* ops->get_strings is valid because checked earlier */
|
|
|
ops->get_strings(dev, stringset, data);
|
|
@@ -618,7 +630,7 @@ static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev,
|
|
|
if (!indir)
|
|
|
return -ENOMEM;
|
|
|
|
|
|
- ret = dev->ethtool_ops->get_rxfh(dev, indir, NULL);
|
|
|
+ ret = dev->ethtool_ops->get_rxfh(dev, indir, NULL, NULL);
|
|
|
if (ret)
|
|
|
goto out;
|
|
|
|
|
@@ -679,7 +691,7 @@ static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev,
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
|
- ret = ops->set_rxfh(dev, indir, NULL);
|
|
|
+ ret = ops->set_rxfh(dev, indir, NULL, ETH_RSS_HASH_NO_CHANGE);
|
|
|
|
|
|
out:
|
|
|
kfree(indir);
|
|
@@ -697,12 +709,11 @@ static noinline_for_stack int ethtool_get_rxfh(struct net_device *dev,
|
|
|
u32 total_size;
|
|
|
u32 indir_bytes;
|
|
|
u32 *indir = NULL;
|
|
|
+ u8 dev_hfunc = 0;
|
|
|
u8 *hkey = NULL;
|
|
|
u8 *rss_config;
|
|
|
|
|
|
- if (!(dev->ethtool_ops->get_rxfh_indir_size ||
|
|
|
- dev->ethtool_ops->get_rxfh_key_size) ||
|
|
|
- !dev->ethtool_ops->get_rxfh)
|
|
|
+ if (!ops->get_rxfh)
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
if (ops->get_rxfh_indir_size)
|
|
@@ -710,16 +721,14 @@ static noinline_for_stack int ethtool_get_rxfh(struct net_device *dev,
|
|
|
if (ops->get_rxfh_key_size)
|
|
|
dev_key_size = ops->get_rxfh_key_size(dev);
|
|
|
|
|
|
- if ((dev_key_size + dev_indir_size) == 0)
|
|
|
- return -EOPNOTSUPP;
|
|
|
-
|
|
|
if (copy_from_user(&rxfh, useraddr, sizeof(rxfh)))
|
|
|
return -EFAULT;
|
|
|
user_indir_size = rxfh.indir_size;
|
|
|
user_key_size = rxfh.key_size;
|
|
|
|
|
|
/* Check that reserved fields are 0 for now */
|
|
|
- if (rxfh.rss_context || rxfh.rsvd[0] || rxfh.rsvd[1])
|
|
|
+ if (rxfh.rss_context || rxfh.rsvd8[0] || rxfh.rsvd8[1] ||
|
|
|
+ rxfh.rsvd8[2] || rxfh.rsvd32)
|
|
|
return -EINVAL;
|
|
|
|
|
|
rxfh.indir_size = dev_indir_size;
|
|
@@ -727,13 +736,6 @@ static noinline_for_stack int ethtool_get_rxfh(struct net_device *dev,
|
|
|
if (copy_to_user(useraddr, &rxfh, sizeof(rxfh)))
|
|
|
return -EFAULT;
|
|
|
|
|
|
- /* If the user buffer size is 0, this is just a query for the
|
|
|
- * device table size and key size. Otherwise, if the User size is
|
|
|
- * not equal to device table size or key size it's an error.
|
|
|
- */
|
|
|
- if (!user_indir_size && !user_key_size)
|
|
|
- return 0;
|
|
|
-
|
|
|
if ((user_indir_size && (user_indir_size != dev_indir_size)) ||
|
|
|
(user_key_size && (user_key_size != dev_key_size)))
|
|
|
return -EINVAL;
|
|
@@ -750,14 +752,19 @@ 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);
|
|
|
- if (!ret) {
|
|
|
- if (copy_to_user(useraddr +
|
|
|
- offsetof(struct ethtool_rxfh, rss_config[0]),
|
|
|
- rss_config, total_size))
|
|
|
- ret = -EFAULT;
|
|
|
- }
|
|
|
+ ret = dev->ethtool_ops->get_rxfh(dev, indir, hkey, &dev_hfunc);
|
|
|
+ if (ret)
|
|
|
+ goto out;
|
|
|
|
|
|
+ if (copy_to_user(useraddr + offsetof(struct ethtool_rxfh, hfunc),
|
|
|
+ &dev_hfunc, sizeof(rxfh.hfunc))) {
|
|
|
+ ret = -EFAULT;
|
|
|
+ } else if (copy_to_user(useraddr +
|
|
|
+ offsetof(struct ethtool_rxfh, rss_config[0]),
|
|
|
+ rss_config, total_size)) {
|
|
|
+ ret = -EFAULT;
|
|
|
+ }
|
|
|
+out:
|
|
|
kfree(rss_config);
|
|
|
|
|
|
return ret;
|
|
@@ -776,33 +783,31 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
|
|
|
u8 *rss_config;
|
|
|
u32 rss_cfg_offset = offsetof(struct ethtool_rxfh, rss_config[0]);
|
|
|
|
|
|
- if (!(ops->get_rxfh_indir_size || ops->get_rxfh_key_size) ||
|
|
|
- !ops->get_rxnfc || !ops->set_rxfh)
|
|
|
+ if (!ops->get_rxnfc || !ops->set_rxfh)
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
if (ops->get_rxfh_indir_size)
|
|
|
dev_indir_size = ops->get_rxfh_indir_size(dev);
|
|
|
if (ops->get_rxfh_key_size)
|
|
|
dev_key_size = dev->ethtool_ops->get_rxfh_key_size(dev);
|
|
|
- if ((dev_key_size + dev_indir_size) == 0)
|
|
|
- return -EOPNOTSUPP;
|
|
|
|
|
|
if (copy_from_user(&rxfh, useraddr, sizeof(rxfh)))
|
|
|
return -EFAULT;
|
|
|
|
|
|
/* Check that reserved fields are 0 for now */
|
|
|
- if (rxfh.rss_context || rxfh.rsvd[0] || rxfh.rsvd[1])
|
|
|
+ if (rxfh.rss_context || rxfh.rsvd8[0] || rxfh.rsvd8[1] ||
|
|
|
+ rxfh.rsvd8[2] || rxfh.rsvd32)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- /* If either indir or hash key is valid, proceed further.
|
|
|
- * It is not valid to request that both be unchanged.
|
|
|
+ /* If either indir, hash key or function is valid, proceed further.
|
|
|
+ * Must request at least one change: indir size, hash key or function.
|
|
|
*/
|
|
|
if ((rxfh.indir_size &&
|
|
|
rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE &&
|
|
|
rxfh.indir_size != dev_indir_size) ||
|
|
|
(rxfh.key_size && (rxfh.key_size != dev_key_size)) ||
|
|
|
(rxfh.indir_size == ETH_RXFH_INDIR_NO_CHANGE &&
|
|
|
- rxfh.key_size == 0))
|
|
|
+ rxfh.key_size == 0 && rxfh.hfunc == ETH_RSS_HASH_NO_CHANGE))
|
|
|
return -EINVAL;
|
|
|
|
|
|
if (rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE)
|
|
@@ -845,7 +850,7 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- ret = ops->set_rxfh(dev, indir, hkey);
|
|
|
+ ret = ops->set_rxfh(dev, indir, hkey, rxfh.hfunc);
|
|
|
|
|
|
out:
|
|
|
kfree(rss_config);
|