Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ethtool: rss: support creating contexts via Netlink

Support creating contexts via Netlink. Setting flow hashing
fields on the new context is not supported at this stage,
it can be added later.

An empty indirection table is not supported. This is a carry
over from the IOCTL interface where empty indirection table
meant delete. We can repurpose empty indirection table in
Netlink but for now to avoid confusion reject it using the
policy.

Support letting user choose the ID for the new context. This was
not possible in IOCTL since the context ID field for the create
action had to be set to the ETH_RXFH_CONTEXT_ALLOC magic value.

Link: https://patch.msgid.link/20250717234343.2328602-7-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+273 -2
+21 -2
Documentation/netlink/specs/ethtool.yaml
··· 2684 2684 name: rss-ntf 2685 2685 doc: | 2686 2686 Notification for change in RSS configuration. 2687 - For additional contexts only modifications are modified, not creation 2688 - or removal of the contexts. 2687 + For additional contexts only modifications use this notification, 2688 + creation and deletion have dedicated messages. 2689 2689 notify: rss-get 2690 + - 2691 + name: rss-create-act 2692 + doc: Create an RSS context. 2693 + attribute-set: rss 2694 + do: 2695 + request: &rss-create-attrs 2696 + attributes: 2697 + - header 2698 + - context 2699 + - hfunc 2700 + - indir 2701 + - hkey 2702 + - input-xfrm 2703 + reply: *rss-create-attrs 2704 + - 2705 + name: rss-create-ntf 2706 + doc: | 2707 + Notification for creation of an additional RSS context. 2708 + notify: rss-create-act 2690 2709 2691 2710 mcast-groups: 2692 2711 list:
+27
Documentation/networking/ethtool-netlink.rst
··· 240 240 ``ETHTOOL_MSG_TSCONFIG_GET`` get hw timestamping configuration 241 241 ``ETHTOOL_MSG_TSCONFIG_SET`` set hw timestamping configuration 242 242 ``ETHTOOL_MSG_RSS_SET`` set RSS settings 243 + ``ETHTOOL_MSG_RSS_CREATE_ACT`` create an additional RSS context 243 244 ===================================== ================================= 244 245 245 246 Kernel to userspace: ··· 295 294 ``ETHTOOL_MSG_TSCONFIG_SET_REPLY`` new hw timestamping configuration 296 295 ``ETHTOOL_MSG_PSE_NTF`` PSE events notification 297 296 ``ETHTOOL_MSG_RSS_NTF`` RSS settings notification 297 + ``ETHTOOL_MSG_RSS_CREATE_ACT_REPLY`` create an additional RSS context 298 + ``ETHTOOL_MSG_RSS_CREATE_NTF`` additional RSS context created 298 299 ======================================== ================================= 299 300 300 301 ``GET`` requests are sent by userspace applications to retrieve device ··· 2016 2013 ``[0, 1, 0, 1, 0, 1, 0, 1]``. Most devices require the table size to be power 2017 2014 of 2, so tables which size is not a power of 2 will likely be rejected. 2018 2015 Using table of size 0 will reset the indirection table to the default. 2016 + 2017 + RSS_CREATE_ACT 2018 + ============== 2019 + 2020 + Request contents: 2021 + 2022 + ===================================== ====== ============================== 2023 + ``ETHTOOL_A_RSS_HEADER`` nested request header 2024 + ``ETHTOOL_A_RSS_CONTEXT`` u32 context number 2025 + ``ETHTOOL_A_RSS_HFUNC`` u32 RSS hash func 2026 + ``ETHTOOL_A_RSS_INDIR`` binary Indir table bytes 2027 + ``ETHTOOL_A_RSS_HKEY`` binary Hash key bytes 2028 + ``ETHTOOL_A_RSS_INPUT_XFRM`` u32 RSS input data transformation 2029 + ===================================== ====== ============================== 2030 + 2031 + Kernel response contents: 2032 + 2033 + ===================================== ====== ============================== 2034 + ``ETHTOOL_A_RSS_HEADER`` nested request header 2035 + ``ETHTOOL_A_RSS_CONTEXT`` u32 context number 2036 + ===================================== ====== ============================== 2037 + 2038 + Create an additional RSS context, if ``ETHTOOL_A_RSS_CONTEXT`` is not 2039 + specified kernel will allocate one automatically. 2019 2040 2020 2041 PLCA_GET_CFG 2021 2042 ============
+1
net/ethtool/ioctl.c
··· 1640 1640 ntf = ETHTOOL_MSG_RSS_NTF; 1641 1641 ret = ops->set_rxfh(dev, &rxfh_dev, extack); 1642 1642 } else if (create) { 1643 + ntf = ETHTOOL_MSG_RSS_CREATE_NTF; 1643 1644 ret = ops->create_rxfh_context(dev, ctx, &rxfh_dev, extack); 1644 1645 /* Make sure driver populates defaults */ 1645 1646 WARN_ON_ONCE(!ret && !rxfh_dev.key && ops->rxfh_per_ctx_key &&
+15
net/ethtool/netlink.c
··· 81 81 } 82 82 } 83 83 84 + u32 ethnl_bcast_seq_next(void) 85 + { 86 + ASSERT_RTNL(); 87 + return ++ethnl_bcast_seq; 88 + } 89 + 84 90 int ethnl_ops_begin(struct net_device *dev) 85 91 { 86 92 int ret; ··· 960 954 [ETHTOOL_MSG_PLCA_NTF] = &ethnl_plca_cfg_request_ops, 961 955 [ETHTOOL_MSG_MM_NTF] = &ethnl_mm_request_ops, 962 956 [ETHTOOL_MSG_RSS_NTF] = &ethnl_rss_request_ops, 957 + [ETHTOOL_MSG_RSS_CREATE_NTF] = &ethnl_rss_request_ops, 963 958 }; 964 959 965 960 /* default notification handler */ ··· 1068 1061 [ETHTOOL_MSG_PLCA_NTF] = ethnl_default_notify, 1069 1062 [ETHTOOL_MSG_MM_NTF] = ethnl_default_notify, 1070 1063 [ETHTOOL_MSG_RSS_NTF] = ethnl_default_notify, 1064 + [ETHTOOL_MSG_RSS_CREATE_NTF] = ethnl_default_notify, 1071 1065 }; 1072 1066 1073 1067 void ethnl_notify(struct net_device *dev, unsigned int cmd, ··· 1519 1511 .doit = ethnl_default_set_doit, 1520 1512 .policy = ethnl_rss_set_policy, 1521 1513 .maxattr = ARRAY_SIZE(ethnl_rss_set_policy) - 1, 1514 + }, 1515 + { 1516 + .cmd = ETHTOOL_MSG_RSS_CREATE_ACT, 1517 + .flags = GENL_UNS_ADMIN_PERM, 1518 + .doit = ethnl_rss_create_doit, 1519 + .policy = ethnl_rss_create_policy, 1520 + .maxattr = ARRAY_SIZE(ethnl_rss_create_policy) - 1, 1522 1521 }, 1523 1522 }; 1524 1523
+3
net/ethtool/netlink.h
··· 10 10 11 11 struct ethnl_req_info; 12 12 13 + u32 ethnl_bcast_seq_next(void); 13 14 int ethnl_parse_header_dev_get(struct ethnl_req_info *req_info, 14 15 const struct nlattr *nest, struct net *net, 15 16 struct netlink_ext_ack *extack, ··· 486 485 extern const struct nla_policy ethnl_pse_set_policy[ETHTOOL_A_PSE_MAX + 1]; 487 486 extern const struct nla_policy ethnl_rss_get_policy[ETHTOOL_A_RSS_START_CONTEXT + 1]; 488 487 extern const struct nla_policy ethnl_rss_set_policy[ETHTOOL_A_RSS_FLOW_HASH + 1]; 488 + extern const struct nla_policy ethnl_rss_create_policy[ETHTOOL_A_RSS_INPUT_XFRM + 1]; 489 489 extern const struct nla_policy ethnl_plca_get_cfg_policy[ETHTOOL_A_PLCA_HEADER + 1]; 490 490 extern const struct nla_policy ethnl_plca_set_cfg_policy[ETHTOOL_A_PLCA_MAX + 1]; 491 491 extern const struct nla_policy ethnl_plca_get_status_policy[ETHTOOL_A_PLCA_HEADER + 1]; ··· 509 507 int ethnl_tsinfo_start(struct netlink_callback *cb); 510 508 int ethnl_tsinfo_dumpit(struct sk_buff *skb, struct netlink_callback *cb); 511 509 int ethnl_tsinfo_done(struct netlink_callback *cb); 510 + int ethnl_rss_create_doit(struct sk_buff *skb, struct genl_info *info); 512 511 513 512 extern const char stats_std_names[__ETHTOOL_STATS_CNT][ETH_GSTRING_LEN]; 514 513 extern const char stats_eth_phy_names[__ETHTOOL_A_STATS_ETH_PHY_CNT][ETH_GSTRING_LEN];
+203
net/ethtool/rss.c
··· 893 893 .set = ethnl_rss_set, 894 894 .set_ntf_cmd = ETHTOOL_MSG_RSS_NTF, 895 895 }; 896 + 897 + /* RSS_CREATE */ 898 + 899 + const struct nla_policy ethnl_rss_create_policy[ETHTOOL_A_RSS_INPUT_XFRM + 1] = { 900 + [ETHTOOL_A_RSS_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy), 901 + [ETHTOOL_A_RSS_CONTEXT] = NLA_POLICY_MIN(NLA_U32, 1), 902 + [ETHTOOL_A_RSS_HFUNC] = NLA_POLICY_MIN(NLA_U32, 1), 903 + [ETHTOOL_A_RSS_INDIR] = NLA_POLICY_MIN(NLA_BINARY, 1), 904 + [ETHTOOL_A_RSS_HKEY] = NLA_POLICY_MIN(NLA_BINARY, 1), 905 + [ETHTOOL_A_RSS_INPUT_XFRM] = 906 + NLA_POLICY_MAX(NLA_U32, RXH_XFRM_SYM_OR_XOR), 907 + }; 908 + 909 + static int 910 + ethnl_rss_create_validate(struct net_device *dev, struct genl_info *info) 911 + { 912 + const struct ethtool_ops *ops = dev->ethtool_ops; 913 + struct nlattr **tb = info->attrs; 914 + struct nlattr *bad_attr = NULL; 915 + u32 rss_context, input_xfrm; 916 + 917 + if (!ops->create_rxfh_context) 918 + return -EOPNOTSUPP; 919 + 920 + rss_context = nla_get_u32_default(tb[ETHTOOL_A_RSS_CONTEXT], 0); 921 + if (ops->rxfh_max_num_contexts && 922 + ops->rxfh_max_num_contexts <= rss_context) { 923 + NL_SET_BAD_ATTR(info->extack, tb[ETHTOOL_A_RSS_CONTEXT]); 924 + return -ERANGE; 925 + } 926 + 927 + if (!ops->rxfh_per_ctx_key) { 928 + bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_HFUNC]; 929 + bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_HKEY]; 930 + bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_INPUT_XFRM]; 931 + } 932 + 933 + input_xfrm = nla_get_u32_default(tb[ETHTOOL_A_RSS_INPUT_XFRM], 0); 934 + if (input_xfrm & ~ops->supported_input_xfrm) 935 + bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_INPUT_XFRM]; 936 + 937 + if (bad_attr) { 938 + NL_SET_BAD_ATTR(info->extack, bad_attr); 939 + return -EOPNOTSUPP; 940 + } 941 + 942 + return 0; 943 + } 944 + 945 + static void 946 + ethnl_rss_create_send_ntf(struct sk_buff *rsp, struct net_device *dev) 947 + { 948 + struct nlmsghdr *nlh = (void *)rsp->data; 949 + struct genlmsghdr *genl_hdr; 950 + 951 + /* Convert the reply into a notification */ 952 + nlh->nlmsg_pid = 0; 953 + nlh->nlmsg_seq = ethnl_bcast_seq_next(); 954 + 955 + genl_hdr = nlmsg_data(nlh); 956 + genl_hdr->cmd = ETHTOOL_MSG_RSS_CREATE_NTF; 957 + 958 + ethnl_multicast(rsp, dev); 959 + } 960 + 961 + int ethnl_rss_create_doit(struct sk_buff *skb, struct genl_info *info) 962 + { 963 + bool indir_dflt = false, mod = false, ntf_fail = false; 964 + struct ethtool_rxfh_param rxfh = {}; 965 + struct ethtool_rxfh_context *ctx; 966 + struct nlattr **tb = info->attrs; 967 + struct rss_reply_data data = {}; 968 + const struct ethtool_ops *ops; 969 + struct rss_req_info req = {}; 970 + struct net_device *dev; 971 + struct sk_buff *rsp; 972 + void *hdr; 973 + u32 limit; 974 + int ret; 975 + 976 + rsp = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); 977 + if (!rsp) 978 + return -ENOMEM; 979 + 980 + ret = ethnl_parse_header_dev_get(&req.base, tb[ETHTOOL_A_RSS_HEADER], 981 + genl_info_net(info), info->extack, 982 + true); 983 + if (ret < 0) 984 + goto exit_free_rsp; 985 + 986 + dev = req.base.dev; 987 + ops = dev->ethtool_ops; 988 + 989 + req.rss_context = nla_get_u32_default(tb[ETHTOOL_A_RSS_CONTEXT], 0); 990 + 991 + ret = ethnl_rss_create_validate(dev, info); 992 + if (ret) 993 + goto exit_free_dev; 994 + 995 + rtnl_lock(); 996 + netdev_lock_ops(dev); 997 + 998 + ret = ethnl_ops_begin(dev); 999 + if (ret < 0) 1000 + goto exit_dev_unlock; 1001 + 1002 + ret = rss_get_data_alloc(dev, &data); 1003 + if (ret) 1004 + goto exit_ops; 1005 + 1006 + ret = rss_set_prep_indir(dev, info, &data, &rxfh, &indir_dflt, &mod); 1007 + if (ret) 1008 + goto exit_clean_data; 1009 + 1010 + ethnl_update_u8(&rxfh.hfunc, tb[ETHTOOL_A_RSS_HFUNC], &mod); 1011 + 1012 + ret = rss_set_prep_hkey(dev, info, &data, &rxfh, &mod); 1013 + if (ret) 1014 + goto exit_free_indir; 1015 + 1016 + rxfh.input_xfrm = RXH_XFRM_NO_CHANGE; 1017 + ethnl_update_u8(&rxfh.input_xfrm, tb[ETHTOOL_A_RSS_INPUT_XFRM], &mod); 1018 + 1019 + ctx = ethtool_rxfh_ctx_alloc(ops, data.indir_size, data.hkey_size); 1020 + if (!ctx) { 1021 + ret = -ENOMEM; 1022 + goto exit_free_hkey; 1023 + } 1024 + 1025 + mutex_lock(&dev->ethtool->rss_lock); 1026 + if (!req.rss_context) { 1027 + limit = ops->rxfh_max_num_contexts ?: U32_MAX; 1028 + ret = xa_alloc(&dev->ethtool->rss_ctx, &req.rss_context, ctx, 1029 + XA_LIMIT(1, limit - 1), GFP_KERNEL_ACCOUNT); 1030 + } else { 1031 + ret = xa_insert(&dev->ethtool->rss_ctx, 1032 + req.rss_context, ctx, GFP_KERNEL_ACCOUNT); 1033 + } 1034 + if (ret < 0) { 1035 + NL_SET_ERR_MSG_ATTR(info->extack, tb[ETHTOOL_A_RSS_CONTEXT], 1036 + "error allocating context ID"); 1037 + goto err_unlock_free_ctx; 1038 + } 1039 + rxfh.rss_context = req.rss_context; 1040 + 1041 + ret = ops->create_rxfh_context(dev, ctx, &rxfh, info->extack); 1042 + if (ret) 1043 + goto err_ctx_id_free; 1044 + 1045 + /* Make sure driver populates defaults */ 1046 + WARN_ON_ONCE(!rxfh.key && ops->rxfh_per_ctx_key && 1047 + !memchr_inv(ethtool_rxfh_context_key(ctx), 0, 1048 + ctx->key_size)); 1049 + 1050 + /* Store the config from rxfh to Xarray.. */ 1051 + rss_set_ctx_update(ctx, tb, &data, &rxfh); 1052 + /* .. copy from Xarray to data. */ 1053 + __rss_prepare_ctx(dev, &data, ctx); 1054 + 1055 + hdr = ethnl_unicast_put(rsp, info->snd_portid, info->snd_seq, 1056 + ETHTOOL_MSG_RSS_CREATE_ACT_REPLY); 1057 + ntf_fail = ethnl_fill_reply_header(rsp, dev, ETHTOOL_A_RSS_HEADER); 1058 + ntf_fail |= rss_fill_reply(rsp, &req.base, &data.base); 1059 + if (WARN_ON(!hdr || ntf_fail)) { 1060 + ret = -EMSGSIZE; 1061 + goto exit_unlock; 1062 + } 1063 + 1064 + genlmsg_end(rsp, hdr); 1065 + 1066 + /* Use the same skb for the response and the notification, 1067 + * genlmsg_reply() will copy the skb if it has elevated user count. 1068 + */ 1069 + skb_get(rsp); 1070 + ret = genlmsg_reply(rsp, info); 1071 + ethnl_rss_create_send_ntf(rsp, dev); 1072 + rsp = NULL; 1073 + 1074 + exit_unlock: 1075 + mutex_unlock(&dev->ethtool->rss_lock); 1076 + exit_free_hkey: 1077 + kfree(rxfh.key); 1078 + exit_free_indir: 1079 + kfree(rxfh.indir); 1080 + exit_clean_data: 1081 + rss_get_data_free(&data); 1082 + exit_ops: 1083 + ethnl_ops_complete(dev); 1084 + exit_dev_unlock: 1085 + netdev_unlock_ops(dev); 1086 + rtnl_unlock(); 1087 + exit_free_dev: 1088 + ethnl_parse_header_dev_put(&req.base); 1089 + exit_free_rsp: 1090 + nlmsg_free(rsp); 1091 + return ret; 1092 + 1093 + err_ctx_id_free: 1094 + xa_erase(&dev->ethtool->rss_ctx, req.rss_context); 1095 + err_unlock_free_ctx: 1096 + kfree(ctx); 1097 + goto exit_unlock; 1098 + }