ethtool: Support for configurable RSS hash function
This patch extends the set/get_rxfh ethtool-options for getting or setting the RSS hash function. It modifies drivers implementation of set/get_rxfh accordingly. This change also delegates the responsibility of checking whether a modification to a certain RX flow hash parameter is supported to the driver implementation of set_rxfh. User-kernel API is done through the new hfunc bitmask field in the ethtool_rxfh struct. A bit set in the hfunc field is corresponding to an index in the new string-set ETH_SS_RSS_HASH_FUNCS. Got approval from most of the relevant driver maintainers that their driver is using Toeplitz, and for the few that didn't answered, also assumed it is Toeplitz. Cc: Tom Lendacky <thomas.lendacky@amd.com> Cc: Ariel Elior <ariel.elior@qlogic.com> Cc: Prashant Sreedharan <prashant@broadcom.com> Cc: Michael Chan <mchan@broadcom.com> Cc: Hariprasad S <hariprasad@chelsio.com> Cc: Sathya Perla <sathya.perla@emulex.com> Cc: Subbu Seetharaman <subbu.seetharaman@emulex.com> Cc: Ajit Khaparde <ajit.khaparde@emulex.com> Cc: Jeff Kirsher <jeffrey.t.kirsher@intel.com> Cc: Jesse Brandeburg <jesse.brandeburg@intel.com> Cc: Bruce Allan <bruce.w.allan@intel.com> Cc: Carolyn Wyborny <carolyn.wyborny@intel.com> Cc: Don Skidmore <donald.c.skidmore@intel.com> Cc: Greg Rose <gregory.v.rose@intel.com> Cc: Matthew Vick <matthew.vick@intel.com> Cc: John Ronciak <john.ronciak@intel.com> Cc: Mitch Williams <mitch.a.williams@intel.com> Cc: Amir Vadai <amirv@mellanox.com> Cc: Solarflare linux maintainers <linux-net-drivers@solarflare.com> Cc: Shradha Shah <sshah@solarflare.com> Cc: Shreyas Bhatewara <sbhatewara@vmware.com> Cc: "VMware, Inc." <pv-drivers@vmware.com> Cc: Ben Hutchings <ben@decadent.org.uk> Signed-off-by: Eyal Perry <eyalpe@mellanox.com> Signed-off-by: Amir Vadai <amirv@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:

committed by
David S. Miller

orang tua
18b5427ae1
melakukan
892311f66f
@@ -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);
|
||||
|
Reference in New Issue
Block a user