|
@@ -642,6 +642,37 @@ void netdev_rss_key_fill(void *buffer, size_t len)
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(netdev_rss_key_fill);
|
|
EXPORT_SYMBOL(netdev_rss_key_fill);
|
|
|
|
|
|
|
|
+static int ethtool_get_max_rxfh_channel(struct net_device *dev, u32 *max)
|
|
|
|
+{
|
|
|
|
+ u32 dev_size, current_max = 0;
|
|
|
|
+ u32 *indir;
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ if (!dev->ethtool_ops->get_rxfh_indir_size ||
|
|
|
|
+ !dev->ethtool_ops->get_rxfh)
|
|
|
|
+ return -EOPNOTSUPP;
|
|
|
|
+ dev_size = dev->ethtool_ops->get_rxfh_indir_size(dev);
|
|
|
|
+ if (dev_size == 0)
|
|
|
|
+ return -EOPNOTSUPP;
|
|
|
|
+
|
|
|
|
+ indir = kcalloc(dev_size, sizeof(indir[0]), GFP_USER);
|
|
|
|
+ if (!indir)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
|
|
+ ret = dev->ethtool_ops->get_rxfh(dev, indir, NULL, NULL);
|
|
|
|
+ if (ret)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ while (dev_size--)
|
|
|
|
+ current_max = max(current_max, indir[dev_size]);
|
|
|
|
+
|
|
|
|
+ *max = current_max;
|
|
|
|
+
|
|
|
|
+out:
|
|
|
|
+ kfree(indir);
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev,
|
|
static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev,
|
|
void __user *useraddr)
|
|
void __user *useraddr)
|
|
{
|
|
{
|
|
@@ -738,6 +769,14 @@ static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev,
|
|
}
|
|
}
|
|
|
|
|
|
ret = ops->set_rxfh(dev, indir, NULL, ETH_RSS_HASH_NO_CHANGE);
|
|
ret = ops->set_rxfh(dev, indir, NULL, ETH_RSS_HASH_NO_CHANGE);
|
|
|
|
+ if (ret)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ /* indicate whether rxfh was set to default */
|
|
|
|
+ if (user_size == 0)
|
|
|
|
+ dev->priv_flags &= ~IFF_RXFH_CONFIGURED;
|
|
|
|
+ else
|
|
|
|
+ dev->priv_flags |= IFF_RXFH_CONFIGURED;
|
|
|
|
|
|
out:
|
|
out:
|
|
kfree(indir);
|
|
kfree(indir);
|
|
@@ -897,6 +936,14 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
|
|
}
|
|
}
|
|
|
|
|
|
ret = ops->set_rxfh(dev, indir, hkey, rxfh.hfunc);
|
|
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;
|
|
|
|
|
|
out:
|
|
out:
|
|
kfree(rss_config);
|
|
kfree(rss_config);
|
|
@@ -1227,14 +1274,31 @@ static noinline_for_stack int ethtool_get_channels(struct net_device *dev,
|
|
static noinline_for_stack int ethtool_set_channels(struct net_device *dev,
|
|
static noinline_for_stack int ethtool_set_channels(struct net_device *dev,
|
|
void __user *useraddr)
|
|
void __user *useraddr)
|
|
{
|
|
{
|
|
- struct ethtool_channels channels;
|
|
|
|
|
|
+ struct ethtool_channels channels, max;
|
|
|
|
+ u32 max_rx_in_use = 0;
|
|
|
|
|
|
- if (!dev->ethtool_ops->set_channels)
|
|
|
|
|
|
+ if (!dev->ethtool_ops->set_channels || !dev->ethtool_ops->get_channels)
|
|
return -EOPNOTSUPP;
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
if (copy_from_user(&channels, useraddr, sizeof(channels)))
|
|
if (copy_from_user(&channels, useraddr, sizeof(channels)))
|
|
return -EFAULT;
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
+ dev->ethtool_ops->get_channels(dev, &max);
|
|
|
|
+
|
|
|
|
+ /* ensure new counts are within the maximums */
|
|
|
|
+ if ((channels.rx_count > max.max_rx) ||
|
|
|
|
+ (channels.tx_count > max.max_tx) ||
|
|
|
|
+ (channels.combined_count > max.max_combined) ||
|
|
|
|
+ (channels.other_count > max.max_other))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ /* ensure the new Rx count fits within the configured Rx flow
|
|
|
|
+ * indirection table settings */
|
|
|
|
+ if (netif_is_rxfh_configured(dev) &&
|
|
|
|
+ !ethtool_get_max_rxfh_channel(dev, &max_rx_in_use) &&
|
|
|
|
+ (channels.combined_count + channels.rx_count) <= max_rx_in_use)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
return dev->ethtool_ops->set_channels(dev, &channels);
|
|
return dev->ethtool_ops->set_channels(dev, &channels);
|
|
}
|
|
}
|
|
|
|
|