summaryrefslogtreecommitdiff
path: root/net/ethtool/rss.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ethtool/rss.c')
-rw-r--r--net/ethtool/rss.c72
1 files changed, 37 insertions, 35 deletions
diff --git a/net/ethtool/rss.c b/net/ethtool/rss.c
index 353110b862ab..d4a1a4724b67 100644
--- a/net/ethtool/rss.c
+++ b/net/ethtool/rss.c
@@ -2,6 +2,7 @@
#include <net/netdev_lock.h>
+#include "../core/dev.h"
#include "common.h"
#include "netlink.h"
@@ -134,8 +135,7 @@ rss_get_data_alloc(struct net_device *dev, struct rss_reply_data *data)
if (!rss_config)
return -ENOMEM;
- if (data->indir_size)
- data->indir_table = (u32 *)rss_config;
+ data->indir_table = (u32 *)rss_config;
if (data->hkey_size)
data->hkey = rss_config + indir_bytes;
@@ -170,8 +170,10 @@ rss_prepare_get(const struct rss_req_info *request, struct net_device *dev,
rxfh.key = data->hkey;
ret = ops->get_rxfh(dev, &rxfh);
- if (ret)
+ if (ret) {
+ rss_get_data_free(data);
goto out_unlock;
+ }
data->hfunc = rxfh.hfunc;
data->input_xfrm = rxfh.input_xfrm;
@@ -467,21 +469,16 @@ int ethnl_rss_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
{
struct rss_nl_dump_ctx *ctx = rss_dump_ctx(cb);
struct net *net = sock_net(skb->sk);
- struct net_device *dev;
int ret = 0;
- rtnl_lock();
- for_each_netdev_dump(net, dev, ctx->ifindex) {
+ for_each_netdev_lock_ops_compat_scoped(net, dev, ctx->ifindex) {
if (ctx->match_ifindex && ctx->match_ifindex != ctx->ifindex)
break;
- netdev_lock_ops(dev);
ret = rss_dump_one_dev(skb, cb, dev);
- netdev_unlock_ops(dev);
if (ret)
break;
}
- rtnl_unlock();
return ret;
}
@@ -573,7 +570,7 @@ static const struct nla_policy ethnl_rss_flows_policy[] = {
const struct nla_policy ethnl_rss_set_policy[ETHTOOL_A_RSS_FLOW_HASH + 1] = {
[ETHTOOL_A_RSS_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy),
[ETHTOOL_A_RSS_CONTEXT] = { .type = NLA_U32, },
- [ETHTOOL_A_RSS_HFUNC] = NLA_POLICY_MIN(NLA_U32, 1),
+ [ETHTOOL_A_RSS_HFUNC] = NLA_POLICY_RANGE(NLA_U32, 1, U8_MAX),
[ETHTOOL_A_RSS_INDIR] = { .type = NLA_BINARY, },
[ETHTOOL_A_RSS_HKEY] = NLA_POLICY_MIN(NLA_BINARY, 1),
[ETHTOOL_A_RSS_INPUT_XFRM] =
@@ -686,7 +683,7 @@ rss_set_prep_indir(struct net_device *dev, struct genl_info *info,
ethtool_rxfh_indir_default(i, num_rx_rings);
}
- *mod |= memcmp(rxfh->indir, data->indir_table, data->indir_size);
+ *mod |= memcmp(rxfh->indir, data->indir_table, alloc_size);
return user_size;
@@ -854,7 +851,7 @@ ethnl_rss_set(struct ethnl_req_info *req_info, struct genl_info *info)
indir_mod = !!tb[ETHTOOL_A_RSS_INDIR];
rxfh.hfunc = data.hfunc;
- ethnl_update_u8(&rxfh.hfunc, tb[ETHTOOL_A_RSS_HFUNC], &mod);
+ ethnl_update_u8_u32(&rxfh.hfunc, tb[ETHTOOL_A_RSS_HFUNC], &mod);
if (rxfh.hfunc == data.hfunc)
rxfh.hfunc = ETH_RSS_HASH_NO_CHANGE;
@@ -863,7 +860,8 @@ ethnl_rss_set(struct ethnl_req_info *req_info, struct genl_info *info)
goto exit_free_indir;
rxfh.input_xfrm = data.input_xfrm;
- ethnl_update_u8(&rxfh.input_xfrm, tb[ETHTOOL_A_RSS_INPUT_XFRM], &mod);
+ ethnl_update_u8_u32(&rxfh.input_xfrm, tb[ETHTOOL_A_RSS_INPUT_XFRM],
+ &mod);
xfrm_sym = rxfh.input_xfrm || data.input_xfrm;
if (rxfh.input_xfrm == data.input_xfrm)
rxfh.input_xfrm = RXH_XFRM_NO_CHANGE;
@@ -937,7 +935,7 @@ const struct ethnl_request_ops ethnl_rss_request_ops = {
const struct nla_policy ethnl_rss_create_policy[ETHTOOL_A_RSS_INPUT_XFRM + 1] = {
[ETHTOOL_A_RSS_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy),
[ETHTOOL_A_RSS_CONTEXT] = NLA_POLICY_MIN(NLA_U32, 1),
- [ETHTOOL_A_RSS_HFUNC] = NLA_POLICY_MIN(NLA_U32, 1),
+ [ETHTOOL_A_RSS_HFUNC] = NLA_POLICY_RANGE(NLA_U32, 1, U8_MAX),
[ETHTOOL_A_RSS_INDIR] = NLA_POLICY_MIN(NLA_BINARY, 1),
[ETHTOOL_A_RSS_HKEY] = NLA_POLICY_MIN(NLA_BINARY, 1),
[ETHTOOL_A_RSS_INPUT_XFRM] =
@@ -981,19 +979,24 @@ ethnl_rss_create_validate(struct net_device *dev, struct genl_info *info)
}
static void
-ethnl_rss_create_send_ntf(struct sk_buff *rsp, struct net_device *dev)
+ethnl_rss_create_send_ntf(const struct sk_buff *rsp, struct net_device *dev)
{
- struct nlmsghdr *nlh = (void *)rsp->data;
struct genlmsghdr *genl_hdr;
+ struct nlmsghdr *nlh;
+ struct sk_buff *ntf;
+
+ ntf = skb_copy_expand(rsp, 0, 0, GFP_KERNEL);
+ if (!ntf)
+ return;
+ nlh = nlmsg_hdr(ntf);
/* Convert the reply into a notification */
nlh->nlmsg_pid = 0;
- nlh->nlmsg_seq = ethnl_bcast_seq_next();
genl_hdr = nlmsg_data(nlh);
genl_hdr->cmd = ETHTOOL_MSG_RSS_CREATE_NTF;
- ethnl_multicast(rsp, dev);
+ ethnl_multicast(ntf, dev);
}
int ethnl_rss_create_doit(struct sk_buff *skb, struct genl_info *info)
@@ -1031,8 +1034,7 @@ int ethnl_rss_create_doit(struct sk_buff *skb, struct genl_info *info)
if (ret)
goto exit_free_dev;
- rtnl_lock();
- netdev_lock_ops(dev);
+ netdev_lock_ops_compat(dev);
ret = ethnl_ops_begin(dev);
if (ret < 0)
@@ -1047,14 +1049,15 @@ int ethnl_rss_create_doit(struct sk_buff *skb, struct genl_info *info)
goto exit_clean_data;
indir_user_size = ret;
- ethnl_update_u8(&rxfh.hfunc, tb[ETHTOOL_A_RSS_HFUNC], &mod);
+ ethnl_update_u8_u32(&rxfh.hfunc, tb[ETHTOOL_A_RSS_HFUNC], &mod);
ret = rss_set_prep_hkey(dev, info, &data, &rxfh, &mod);
if (ret)
goto exit_free_indir;
rxfh.input_xfrm = RXH_XFRM_NO_CHANGE;
- ethnl_update_u8(&rxfh.input_xfrm, tb[ETHTOOL_A_RSS_INPUT_XFRM], &mod);
+ ethnl_update_u8_u32(&rxfh.input_xfrm, tb[ETHTOOL_A_RSS_INPUT_XFRM],
+ &mod);
ctx = ethtool_rxfh_ctx_alloc(ops, data.indir_size, data.hkey_size);
if (!ctx) {
@@ -1099,17 +1102,13 @@ int ethnl_rss_create_doit(struct sk_buff *skb, struct genl_info *info)
ntf_fail |= rss_fill_reply(rsp, &req.base, &data.base);
if (WARN_ON(!hdr || ntf_fail)) {
ret = -EMSGSIZE;
- goto exit_unlock;
+ goto err_remove_ctx;
}
genlmsg_end(rsp, hdr);
- /* Use the same skb for the response and the notification,
- * genlmsg_reply() will copy the skb if it has elevated user count.
- */
- skb_get(rsp);
- ret = genlmsg_reply(rsp, info);
ethnl_rss_create_send_ntf(rsp, dev);
+ ret = genlmsg_reply(rsp, info);
rsp = NULL;
exit_unlock:
@@ -1123,14 +1122,17 @@ exit_clean_data:
exit_ops:
ethnl_ops_complete(dev);
exit_dev_unlock:
- netdev_unlock_ops(dev);
- rtnl_unlock();
+ netdev_unlock_ops_compat(dev);
exit_free_dev:
ethnl_parse_header_dev_put(&req.base);
exit_free_rsp:
nlmsg_free(rsp);
return ret;
+err_remove_ctx:
+ if (ops->remove_rxfh_context(dev, ctx, req.rss_context, NULL))
+ /* leave the context on failure, like ethnl_rss_delete_doit() */
+ goto exit_unlock;
err_ctx_id_free:
xa_erase(&dev->ethtool->rss_ctx, req.rss_context);
err_unlock_free_ctx:
@@ -1168,11 +1170,12 @@ int ethnl_rss_delete_doit(struct sk_buff *skb, struct genl_info *info)
dev = req.dev;
ops = dev->ethtool_ops;
- if (!ops->create_rxfh_context)
+ if (!ops->create_rxfh_context) {
+ ret = -EOPNOTSUPP;
goto exit_free_dev;
+ }
- rtnl_lock();
- netdev_lock_ops(dev);
+ netdev_lock_ops_compat(dev);
ret = ethnl_ops_begin(dev);
if (ret < 0)
@@ -1202,8 +1205,7 @@ exit_unlock:
mutex_unlock(&dev->ethtool->rss_lock);
ethnl_ops_complete(dev);
exit_dev_unlock:
- netdev_unlock_ops(dev);
- rtnl_unlock();
+ netdev_unlock_ops_compat(dev);
exit_free_dev:
ethnl_parse_header_dev_put(&req);
return ret;