diff options
author | Gal Pressman <gal@nvidia.com> | 2022-11-06 14:31:27 +0200 |
---|---|---|
committer | Paolo Abeni <pabeni@redhat.com> | 2022-11-08 12:08:44 +0100 |
commit | 47f3ecf4763d3fea37d3453c9ee1f9f2169d71b3 (patch) | |
tree | 90403e03187c2518d8543ab37fe045d74109aee4 /net/ethtool/common.c | |
parent | 9a0f830f80265bd1ef816e1541ac24bee80e9a3c (diff) | |
download | linux-stable-47f3ecf4763d3fea37d3453c9ee1f9f2169d71b3.tar.gz linux-stable-47f3ecf4763d3fea37d3453c9ee1f9f2169d71b3.tar.bz2 linux-stable-47f3ecf4763d3fea37d3453c9ee1f9f2169d71b3.zip |
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 d4ab4286276f ("ethtool: correctly ensure {GS}CHANNELS doesn't conflict with GS{RXFH}")
Reviewed-by: Tariq Toukan <tariqt@nvidia.com>
Signed-off-by: Gal Pressman <gal@nvidia.com>
Link: https://lore.kernel.org/r/20221106123127.522985-1-gal@nvidia.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Diffstat (limited to 'net/ethtool/common.c')
-rw-r--r-- | net/ethtool/common.c | 66 |
1 files changed, 66 insertions, 0 deletions
diff --git a/net/ethtool/common.c b/net/ethtool/common.c index ee3e02da0013..21cfe8557205 100644 --- a/net/ethtool/common.c +++ b/net/ethtool/common.c @@ -512,6 +512,72 @@ int __ethtool_get_link(struct net_device *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) { u32 dev_size, current_max = 0; |