Commit 47f3ecf4 authored by Gal Pressman's avatar Gal Pressman Committed by Paolo Abeni

ethtool: Fail number of channels change when it conflicts with rxnfc

Similar to what we do with the hash indirection table [1], when network
flow classification rules are forwarding traffic to channels greater
than the requested number of channels, fail the operation.
Without this, traffic could be directed to channels which no longer
exist (dropped) after changing number of channels.

[1] commit d4ab4286 ("ethtool: correctly ensure {GS}CHANNELS doesn't conflict with GS{RXFH}")
Reviewed-by: default avatarTariq Toukan <tariqt@nvidia.com>
Signed-off-by: default avatarGal Pressman <gal@nvidia.com>
Link: https://lore.kernel.org/r/20221106123127.522985-1-gal@nvidia.comSigned-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parent 9a0f830f
...@@ -116,9 +116,10 @@ int ethnl_set_channels(struct sk_buff *skb, struct genl_info *info) ...@@ -116,9 +116,10 @@ int ethnl_set_channels(struct sk_buff *skb, struct genl_info *info)
struct ethtool_channels channels = {}; struct ethtool_channels channels = {};
struct ethnl_req_info req_info = {}; struct ethnl_req_info req_info = {};
struct nlattr **tb = info->attrs; struct nlattr **tb = info->attrs;
u32 err_attr, max_rx_in_use = 0; u32 err_attr, max_rxfh_in_use;
const struct ethtool_ops *ops; const struct ethtool_ops *ops;
struct net_device *dev; struct net_device *dev;
u64 max_rxnfc_in_use;
int ret; int ret;
ret = ethnl_parse_header_dev_get(&req_info, ret = ethnl_parse_header_dev_get(&req_info,
...@@ -189,15 +190,23 @@ int ethnl_set_channels(struct sk_buff *skb, struct genl_info *info) ...@@ -189,15 +190,23 @@ int ethnl_set_channels(struct sk_buff *skb, struct genl_info *info)
} }
/* ensure the new Rx count fits within the configured Rx flow /* ensure the new Rx count fits within the configured Rx flow
* indirection table settings * indirection table/rxnfc settings
*/ */
if (netif_is_rxfh_configured(dev) && if (ethtool_get_max_rxnfc_channel(dev, &max_rxnfc_in_use))
!ethtool_get_max_rxfh_channel(dev, &max_rx_in_use) && max_rxnfc_in_use = 0;
(channels.combined_count + channels.rx_count) <= max_rx_in_use) { if (!netif_is_rxfh_configured(dev) ||
ethtool_get_max_rxfh_channel(dev, &max_rxfh_in_use))
max_rxfh_in_use = 0;
if (channels.combined_count + channels.rx_count <= max_rxfh_in_use) {
ret = -EINVAL; ret = -EINVAL;
GENL_SET_ERR_MSG(info, "requested channel counts are too low for existing indirection table settings"); GENL_SET_ERR_MSG(info, "requested channel counts are too low for existing indirection table settings");
goto out_ops; goto out_ops;
} }
if (channels.combined_count + channels.rx_count <= max_rxnfc_in_use) {
ret = -EINVAL;
GENL_SET_ERR_MSG(info, "requested channel counts are too low for existing ntuple filter settings");
goto out_ops;
}
/* Disabling channels, query zero-copy AF_XDP sockets */ /* Disabling channels, query zero-copy AF_XDP sockets */
from_channel = channels.combined_count + from_channel = channels.combined_count +
......
...@@ -512,6 +512,72 @@ int __ethtool_get_link(struct net_device *dev) ...@@ -512,6 +512,72 @@ int __ethtool_get_link(struct net_device *dev)
return netif_running(dev) && dev->ethtool_ops->get_link(dev); return netif_running(dev) && dev->ethtool_ops->get_link(dev);
} }
static int ethtool_get_rxnfc_rule_count(struct net_device *dev)
{
const struct ethtool_ops *ops = dev->ethtool_ops;
struct ethtool_rxnfc info = {
.cmd = ETHTOOL_GRXCLSRLCNT,
};
int err;
err = ops->get_rxnfc(dev, &info, NULL);
if (err)
return err;
return info.rule_cnt;
}
int ethtool_get_max_rxnfc_channel(struct net_device *dev, u64 *max)
{
const struct ethtool_ops *ops = dev->ethtool_ops;
struct ethtool_rxnfc *info;
int err, i, rule_cnt;
u64 max_ring = 0;
if (!ops->get_rxnfc)
return -EOPNOTSUPP;
rule_cnt = ethtool_get_rxnfc_rule_count(dev);
if (rule_cnt <= 0)
return -EINVAL;
info = kvzalloc(struct_size(info, rule_locs, rule_cnt), GFP_KERNEL);
if (!info)
return -ENOMEM;
info->cmd = ETHTOOL_GRXCLSRLALL;
info->rule_cnt = rule_cnt;
err = ops->get_rxnfc(dev, info, info->rule_locs);
if (err)
goto err_free_info;
for (i = 0; i < rule_cnt; i++) {
struct ethtool_rxnfc rule_info = {
.cmd = ETHTOOL_GRXCLSRULE,
.fs.location = info->rule_locs[i],
};
err = ops->get_rxnfc(dev, &rule_info, NULL);
if (err)
goto err_free_info;
if (rule_info.fs.ring_cookie != RX_CLS_FLOW_DISC &&
rule_info.fs.ring_cookie != RX_CLS_FLOW_WAKE &&
!(rule_info.flow_type & FLOW_RSS) &&
!ethtool_get_flow_spec_ring_vf(rule_info.fs.ring_cookie))
max_ring =
max_t(u64, max_ring, rule_info.fs.ring_cookie);
}
kvfree(info);
*max = max_ring;
return 0;
err_free_info:
kvfree(info);
return err;
}
int ethtool_get_max_rxfh_channel(struct net_device *dev, u32 *max) int ethtool_get_max_rxfh_channel(struct net_device *dev, u32 *max)
{ {
u32 dev_size, current_max = 0; u32 dev_size, current_max = 0;
......
...@@ -43,6 +43,7 @@ bool convert_legacy_settings_to_link_ksettings( ...@@ -43,6 +43,7 @@ bool convert_legacy_settings_to_link_ksettings(
struct ethtool_link_ksettings *link_ksettings, struct ethtool_link_ksettings *link_ksettings,
const struct ethtool_cmd *legacy_settings); const struct ethtool_cmd *legacy_settings);
int ethtool_get_max_rxfh_channel(struct net_device *dev, u32 *max); int ethtool_get_max_rxfh_channel(struct net_device *dev, u32 *max);
int ethtool_get_max_rxnfc_channel(struct net_device *dev, u64 *max);
int __ethtool_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info); int __ethtool_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info);
extern const struct ethtool_phy_ops *ethtool_phy_ops; extern const struct ethtool_phy_ops *ethtool_phy_ops;
......
...@@ -1789,7 +1789,8 @@ static noinline_for_stack int ethtool_set_channels(struct net_device *dev, ...@@ -1789,7 +1789,8 @@ static noinline_for_stack int ethtool_set_channels(struct net_device *dev,
{ {
struct ethtool_channels channels, curr = { .cmd = ETHTOOL_GCHANNELS }; struct ethtool_channels channels, curr = { .cmd = ETHTOOL_GCHANNELS };
u16 from_channel, to_channel; u16 from_channel, to_channel;
u32 max_rx_in_use = 0; u64 max_rxnfc_in_use;
u32 max_rxfh_in_use;
unsigned int i; unsigned int i;
int ret; int ret;
...@@ -1820,10 +1821,14 @@ static noinline_for_stack int ethtool_set_channels(struct net_device *dev, ...@@ -1820,10 +1821,14 @@ static noinline_for_stack int ethtool_set_channels(struct net_device *dev,
return -EINVAL; return -EINVAL;
/* ensure the new Rx count fits within the configured Rx flow /* ensure the new Rx count fits within the configured Rx flow
* indirection table settings */ * indirection table/rxnfc settings */
if (netif_is_rxfh_configured(dev) && if (ethtool_get_max_rxnfc_channel(dev, &max_rxnfc_in_use))
!ethtool_get_max_rxfh_channel(dev, &max_rx_in_use) && max_rxnfc_in_use = 0;
(channels.combined_count + channels.rx_count) <= max_rx_in_use) if (!netif_is_rxfh_configured(dev) ||
ethtool_get_max_rxfh_channel(dev, &max_rxfh_in_use))
max_rxfh_in_use = 0;
if (channels.combined_count + channels.rx_count <=
max_t(u64, max_rxnfc_in_use, max_rxfh_in_use))
return -EINVAL; return -EINVAL;
/* Disabling channels, query zero-copy AF_XDP sockets */ /* Disabling channels, query zero-copy AF_XDP sockets */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment