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

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'ethtool-rss-support-creating-and-removing-contexts-via-netlink'

Jakub Kicinski says:

====================
ethtool: rss: support creating and removing contexts via Netlink

This series completes support of RSS configuration via Netlink.
All functionality supported by the IOCTL is now supported by
Netlink. Future series (time allowing) will add:
- hashing on the flow label, which started this whole thing;
- pinning the RSS context to a Netlink socket for auto-cleanup.

The first patch is a leftover held back from previous series
to avoid conflicting with Gal's fix.

Next 4 patches refactor existing code to make reusing it for
context creation possible. 2 patches after that add create
and delete commands. Last but not least the test is extended.
====================

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

+603 -72
+39 -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 2709 + - 2710 + name: rss-delete-act 2711 + doc: Delete an RSS context. 2712 + attribute-set: rss 2713 + do: 2714 + request: 2715 + attributes: 2716 + - header 2717 + - context 2718 + - 2719 + name: rss-delete-ntf 2720 + doc: | 2721 + Notification for deletion of an additional RSS context. 2722 + attribute-set: rss 2723 + event: 2724 + attributes: 2725 + - header 2726 + - context 2690 2727 2691 2728 mcast-groups: 2692 2729 list:
+41
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 244 + ``ETHTOOL_MSG_RSS_DELETE_ACT`` delete an additional RSS context 243 245 ===================================== ================================= 244 246 245 247 Kernel to userspace: ··· 296 294 ``ETHTOOL_MSG_TSCONFIG_SET_REPLY`` new hw timestamping configuration 297 295 ``ETHTOOL_MSG_PSE_NTF`` PSE events notification 298 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 299 + ``ETHTOOL_MSG_RSS_DELETE_NTF`` additional RSS context deleted 299 300 ======================================== ================================= 300 301 301 302 ``GET`` requests are sent by userspace applications to retrieve device ··· 2018 2013 ``[0, 1, 0, 1, 0, 1, 0, 1]``. Most devices require the table size to be power 2019 2014 of 2, so tables which size is not a power of 2 will likely be rejected. 2020 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. 2040 + 2041 + RSS_DELETE_ACT 2042 + ============== 2043 + 2044 + Request contents: 2045 + 2046 + ===================================== ====== ============================== 2047 + ``ETHTOOL_A_RSS_HEADER`` nested request header 2048 + ``ETHTOOL_A_RSS_CONTEXT`` u32 context number 2049 + ===================================== ====== ============================== 2050 + 2051 + Delete an additional RSS context. 2021 2052 2022 2053 PLCA_GET_CFG 2023 2054 ============
+39
net/ethtool/common.c
··· 806 806 return rc; 807 807 } 808 808 809 + struct ethtool_rxfh_context * 810 + ethtool_rxfh_ctx_alloc(const struct ethtool_ops *ops, 811 + u32 indir_size, u32 key_size) 812 + { 813 + size_t indir_bytes, flex_len, key_off, size; 814 + struct ethtool_rxfh_context *ctx; 815 + u32 priv_bytes, indir_max; 816 + u16 key_max; 817 + 818 + key_max = max(key_size, ops->rxfh_key_space); 819 + indir_max = max(indir_size, ops->rxfh_indir_space); 820 + 821 + priv_bytes = ALIGN(ops->rxfh_priv_size, sizeof(u32)); 822 + indir_bytes = array_size(indir_max, sizeof(u32)); 823 + 824 + key_off = size_add(priv_bytes, indir_bytes); 825 + flex_len = size_add(key_off, key_max); 826 + size = struct_size_t(struct ethtool_rxfh_context, data, flex_len); 827 + 828 + ctx = kzalloc(size, GFP_KERNEL_ACCOUNT); 829 + if (!ctx) 830 + return NULL; 831 + 832 + ctx->indir_size = indir_size; 833 + ctx->key_size = key_size; 834 + ctx->key_off = key_off; 835 + ctx->priv_size = ops->rxfh_priv_size; 836 + 837 + ctx->hfunc = ETH_RSS_HASH_NO_CHANGE; 838 + ctx->input_xfrm = RXH_XFRM_NO_CHANGE; 839 + 840 + return ctx; 841 + } 842 + 809 843 /* Check if fields configured for flow hash are symmetric - if src is included 810 844 * so is dst and vice versa. 811 845 */ ··· 863 829 return -EINVAL; 864 830 if (WARN_ON(ops->supported_input_xfrm && !ops->get_rxfh_fields)) 865 831 return -EINVAL; 832 + if (WARN_ON(ops->supported_input_xfrm && 833 + ops->rxfh_per_ctx_fields != ops->rxfh_per_ctx_key)) 834 + return -EINVAL; 835 + 866 836 /* NOTE: sufficiently insane drivers may swap ethtool_ops at runtime, 867 837 * the fact that ops are checked at registration time does not 868 838 * mean the ops attached to a netdev later on are sane. ··· 1136 1098 netdev_err(dev, "device error, RSS context %d lost\n", context_id); 1137 1099 ctx = xa_erase(&dev->ethtool->rss_ctx, context_id); 1138 1100 kfree(ctx); 1101 + ethtool_rss_notify(dev, ETHTOOL_MSG_RSS_DELETE_NTF, context_id); 1139 1102 } 1140 1103 EXPORT_SYMBOL(ethtool_rxfh_context_lost);
+6 -2
net/ethtool/common.h
··· 43 43 int ethtool_check_max_channel(struct net_device *dev, 44 44 struct ethtool_channels channels, 45 45 struct genl_info *info); 46 + struct ethtool_rxfh_context * 47 + ethtool_rxfh_ctx_alloc(const struct ethtool_ops *ops, 48 + u32 indir_size, u32 key_size); 46 49 int ethtool_check_rss_ctx_busy(struct net_device *dev, u32 rss_context); 47 50 int ethtool_rxfh_config_is_sym(u64 rxfh); 48 51 ··· 79 76 bool __ethtool_dev_mm_supported(struct net_device *dev); 80 77 81 78 #if IS_ENABLED(CONFIG_ETHTOOL_NETLINK) 82 - void ethtool_rss_notify(struct net_device *dev, u32 rss_context); 79 + void ethtool_rss_notify(struct net_device *dev, u32 type, u32 rss_context); 83 80 #else 84 - static inline void ethtool_rss_notify(struct net_device *dev, u32 rss_context) 81 + static inline void 82 + ethtool_rss_notify(struct net_device *dev, u32 type, u32 rss_context) 85 83 { 86 84 } 87 85 #endif
+9 -39
net/ethtool/ioctl.c
··· 1105 1105 if (rc) 1106 1106 return rc; 1107 1107 1108 - ethtool_rss_notify(dev, fields.rss_context); 1108 + ethtool_rss_notify(dev, ETHTOOL_MSG_RSS_NTF, fields.rss_context); 1109 1109 return 0; 1110 1110 } 1111 1111 ··· 1473 1473 return ret; 1474 1474 } 1475 1475 1476 - static struct ethtool_rxfh_context * 1477 - ethtool_rxfh_ctx_alloc(const struct ethtool_ops *ops, 1478 - u32 indir_size, u32 key_size) 1479 - { 1480 - size_t indir_bytes, flex_len, key_off, size; 1481 - struct ethtool_rxfh_context *ctx; 1482 - u32 priv_bytes, indir_max; 1483 - u16 key_max; 1484 - 1485 - key_max = max(key_size, ops->rxfh_key_space); 1486 - indir_max = max(indir_size, ops->rxfh_indir_space); 1487 - 1488 - priv_bytes = ALIGN(ops->rxfh_priv_size, sizeof(u32)); 1489 - indir_bytes = array_size(indir_max, sizeof(u32)); 1490 - 1491 - key_off = size_add(priv_bytes, indir_bytes); 1492 - flex_len = size_add(key_off, key_max); 1493 - size = struct_size_t(struct ethtool_rxfh_context, data, flex_len); 1494 - 1495 - ctx = kzalloc(size, GFP_KERNEL_ACCOUNT); 1496 - if (!ctx) 1497 - return NULL; 1498 - 1499 - ctx->indir_size = indir_size; 1500 - ctx->key_size = key_size; 1501 - ctx->key_off = key_off; 1502 - ctx->priv_size = ops->rxfh_priv_size; 1503 - 1504 - ctx->hfunc = ETH_RSS_HASH_NO_CHANGE; 1505 - ctx->input_xfrm = RXH_XFRM_NO_CHANGE; 1506 - 1507 - return ctx; 1508 - } 1509 - 1510 1476 static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev, 1511 1477 void __user *useraddr) 1512 1478 { ··· 1486 1520 struct ethtool_rxnfc rx_rings; 1487 1521 struct ethtool_rxfh rxfh; 1488 1522 bool create = false; 1489 - bool mod = false; 1490 1523 u8 *rss_config; 1524 + int ntf = 0; 1491 1525 int ret; 1492 1526 1493 1527 if (!ops->get_rxnfc || !ops->set_rxfh) ··· 1637 1671 rxfh_dev.input_xfrm = rxfh.input_xfrm; 1638 1672 1639 1673 if (!rxfh.rss_context) { 1674 + ntf = ETHTOOL_MSG_RSS_NTF; 1640 1675 ret = ops->set_rxfh(dev, &rxfh_dev, extack); 1641 1676 } else if (create) { 1677 + ntf = ETHTOOL_MSG_RSS_CREATE_NTF; 1642 1678 ret = ops->create_rxfh_context(dev, ctx, &rxfh_dev, extack); 1643 1679 /* Make sure driver populates defaults */ 1644 1680 WARN_ON_ONCE(!ret && !rxfh_dev.key && ops->rxfh_per_ctx_key && 1645 1681 !memchr_inv(ethtool_rxfh_context_key(ctx), 0, 1646 1682 ctx->key_size)); 1647 1683 } else if (rxfh_dev.rss_delete) { 1684 + ntf = ETHTOOL_MSG_RSS_DELETE_NTF; 1648 1685 ret = ops->remove_rxfh_context(dev, ctx, rxfh.rss_context, 1649 1686 extack); 1650 1687 } else { 1688 + ntf = ETHTOOL_MSG_RSS_NTF; 1651 1689 ret = ops->modify_rxfh_context(dev, ctx, &rxfh_dev, extack); 1652 1690 } 1653 1691 if (ret) { 1692 + ntf = 0; 1654 1693 if (create) { 1655 1694 /* failed to create, free our new tracking entry */ 1656 1695 xa_erase(&dev->ethtool->rss_ctx, rxfh.rss_context); ··· 1663 1692 } 1664 1693 goto out_unlock; 1665 1694 } 1666 - mod = !create && !rxfh_dev.rss_delete; 1667 1695 1668 1696 if (copy_to_user(useraddr + offsetof(struct ethtool_rxfh, rss_context), 1669 1697 &rxfh_dev.rss_context, sizeof(rxfh_dev.rss_context))) ··· 1702 1732 mutex_unlock(&dev->ethtool->rss_lock); 1703 1733 out_free: 1704 1734 kfree(rss_config); 1705 - if (mod) 1706 - ethtool_rss_notify(dev, rxfh.rss_context); 1735 + if (ntf) 1736 + ethtool_rss_notify(dev, ntf, rxfh.rss_context); 1707 1737 return ret; 1708 1738 } 1709 1739
+22
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, 1521 + }, 1522 + { 1523 + .cmd = ETHTOOL_MSG_RSS_DELETE_ACT, 1524 + .flags = GENL_UNS_ADMIN_PERM, 1525 + .doit = ethnl_rss_delete_doit, 1526 + .policy = ethnl_rss_delete_policy, 1527 + .maxattr = ARRAY_SIZE(ethnl_rss_delete_policy) - 1, 1522 1528 }, 1523 1529 }; 1524 1530
+5
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 + extern const struct nla_policy ethnl_rss_delete_policy[ETHTOOL_A_RSS_CONTEXT + 1]; 489 490 extern const struct nla_policy ethnl_plca_get_cfg_policy[ETHTOOL_A_PLCA_HEADER + 1]; 490 491 extern const struct nla_policy ethnl_plca_set_cfg_policy[ETHTOOL_A_PLCA_MAX + 1]; 491 492 extern const struct nla_policy ethnl_plca_get_status_policy[ETHTOOL_A_PLCA_HEADER + 1]; ··· 510 507 int ethnl_tsinfo_start(struct netlink_callback *cb); 511 508 int ethnl_tsinfo_dumpit(struct sk_buff *skb, struct netlink_callback *cb); 512 509 int ethnl_tsinfo_done(struct netlink_callback *cb); 510 + int ethnl_rss_create_doit(struct sk_buff *skb, struct genl_info *info); 511 + int ethnl_rss_delete_doit(struct sk_buff *skb, struct genl_info *info); 513 512 514 513 extern const char stats_std_names[__ETHTOOL_STATS_CNT][ETH_GSTRING_LEN]; 515 514 extern const char stats_eth_phy_names[__ETHTOOL_A_STATS_ETH_PHY_CNT][ETH_GSTRING_LEN];
+364 -29
net/ethtool/rss.c
··· 113 113 } 114 114 115 115 static int 116 - rss_prepare_get(const struct rss_req_info *request, struct net_device *dev, 117 - struct rss_reply_data *data, const struct genl_info *info) 116 + rss_get_data_alloc(struct net_device *dev, struct rss_reply_data *data) 118 117 { 119 - struct ethtool_rxfh_param rxfh = {}; 120 - const struct ethtool_ops *ops; 118 + const struct ethtool_ops *ops = dev->ethtool_ops; 121 119 u32 total_size, indir_bytes; 122 120 u8 *rss_config; 123 - int ret; 124 - 125 - ops = dev->ethtool_ops; 126 - 127 - ret = ethnl_ops_begin(dev); 128 - if (ret < 0) 129 - return ret; 130 - mutex_lock(&dev->ethtool->rss_lock); 131 121 132 122 data->indir_size = 0; 133 123 data->hkey_size = 0; ··· 129 139 indir_bytes = data->indir_size * sizeof(u32); 130 140 total_size = indir_bytes + data->hkey_size; 131 141 rss_config = kzalloc(total_size, GFP_KERNEL); 132 - if (!rss_config) { 133 - ret = -ENOMEM; 134 - goto out_unlock; 135 - } 142 + if (!rss_config) 143 + return -ENOMEM; 136 144 137 145 if (data->indir_size) 138 146 data->indir_table = (u32 *)rss_config; 139 147 if (data->hkey_size) 140 148 data->hkey = rss_config + indir_bytes; 149 + 150 + return 0; 151 + } 152 + 153 + static void rss_get_data_free(const struct rss_reply_data *data) 154 + { 155 + kfree(data->indir_table); 156 + } 157 + 158 + static int 159 + rss_prepare_get(const struct rss_req_info *request, struct net_device *dev, 160 + struct rss_reply_data *data, const struct genl_info *info) 161 + { 162 + const struct ethtool_ops *ops = dev->ethtool_ops; 163 + struct ethtool_rxfh_param rxfh = {}; 164 + int ret; 165 + 166 + ret = ethnl_ops_begin(dev); 167 + if (ret < 0) 168 + return ret; 169 + mutex_lock(&dev->ethtool->rss_lock); 170 + 171 + ret = rss_get_data_alloc(dev, data); 172 + if (ret) 173 + goto out_unlock; 141 174 142 175 rxfh.indir_size = data->indir_size; 143 176 rxfh.indir = data->indir_table; ··· 179 166 return ret; 180 167 } 181 168 169 + static void 170 + __rss_prepare_ctx(struct net_device *dev, struct rss_reply_data *data, 171 + struct ethtool_rxfh_context *ctx) 172 + { 173 + if (WARN_ON_ONCE(data->indir_size != ctx->indir_size || 174 + data->hkey_size != ctx->key_size)) 175 + return; 176 + 177 + data->no_key_fields = !dev->ethtool_ops->rxfh_per_ctx_key; 178 + 179 + data->hfunc = ctx->hfunc; 180 + data->input_xfrm = ctx->input_xfrm; 181 + memcpy(data->indir_table, ethtool_rxfh_context_indir(ctx), 182 + data->indir_size * sizeof(u32)); 183 + if (data->hkey_size) 184 + memcpy(data->hkey, ethtool_rxfh_context_key(ctx), 185 + data->hkey_size); 186 + } 187 + 182 188 static int 183 189 rss_prepare_ctx(const struct rss_req_info *request, struct net_device *dev, 184 190 struct rss_reply_data *data, const struct genl_info *info) ··· 206 174 u32 total_size, indir_bytes; 207 175 u8 *rss_config; 208 176 int ret; 209 - 210 - data->no_key_fields = !dev->ethtool_ops->rxfh_per_ctx_key; 211 177 212 178 mutex_lock(&dev->ethtool->rss_lock); 213 179 ctx = xa_load(&dev->ethtool->rss_ctx, request->rss_context); ··· 216 186 217 187 data->indir_size = ctx->indir_size; 218 188 data->hkey_size = ctx->key_size; 219 - data->hfunc = ctx->hfunc; 220 - data->input_xfrm = ctx->input_xfrm; 221 189 222 190 indir_bytes = data->indir_size * sizeof(u32); 223 191 total_size = indir_bytes + data->hkey_size; ··· 226 198 } 227 199 228 200 data->indir_table = (u32 *)rss_config; 229 - memcpy(data->indir_table, ethtool_rxfh_context_indir(ctx), indir_bytes); 230 - 231 - if (data->hkey_size) { 201 + if (data->hkey_size) 232 202 data->hkey = rss_config + indir_bytes; 233 - memcpy(data->hkey, ethtool_rxfh_context_key(ctx), 234 - data->hkey_size); 235 - } 203 + 204 + __rss_prepare_ctx(dev, data, ctx); 236 205 237 206 ret = 0; 238 207 out_unlock: ··· 343 318 { 344 319 const struct rss_reply_data *data = RSS_REPDATA(reply_base); 345 320 346 - kfree(data->indir_table); 321 + rss_get_data_free(data); 347 322 } 348 323 349 324 struct rss_nl_dump_ctx { ··· 486 461 487 462 /* RSS_NTF */ 488 463 489 - void ethtool_rss_notify(struct net_device *dev, u32 rss_context) 464 + static void ethnl_rss_delete_notify(struct net_device *dev, u32 rss_context) 465 + { 466 + struct sk_buff *ntf; 467 + size_t ntf_size; 468 + void *hdr; 469 + 470 + ntf_size = ethnl_reply_header_size() + 471 + nla_total_size(sizeof(u32)); /* _RSS_CONTEXT */ 472 + 473 + ntf = genlmsg_new(ntf_size, GFP_KERNEL); 474 + if (!ntf) 475 + goto out_warn; 476 + 477 + hdr = ethnl_bcastmsg_put(ntf, ETHTOOL_MSG_RSS_DELETE_NTF); 478 + if (!hdr) 479 + goto out_free_ntf; 480 + 481 + if (ethnl_fill_reply_header(ntf, dev, ETHTOOL_A_RSS_HEADER) || 482 + nla_put_u32(ntf, ETHTOOL_A_RSS_CONTEXT, rss_context)) 483 + goto out_free_ntf; 484 + 485 + genlmsg_end(ntf, hdr); 486 + if (ethnl_multicast(ntf, dev)) 487 + goto out_warn; 488 + 489 + return; 490 + 491 + out_free_ntf: 492 + nlmsg_free(ntf); 493 + out_warn: 494 + pr_warn_once("Failed to send a RSS delete notification"); 495 + } 496 + 497 + void ethtool_rss_notify(struct net_device *dev, u32 type, u32 rss_context) 490 498 { 491 499 struct rss_req_info req_info = { 492 500 .rss_context = rss_context, 493 501 }; 494 502 495 - ethnl_notify(dev, ETHTOOL_MSG_RSS_NTF, &req_info.base); 503 + if (type == ETHTOOL_MSG_RSS_DELETE_NTF) 504 + ethnl_rss_delete_notify(dev, rss_context); 505 + else 506 + ethnl_notify(dev, type, &req_info.base); 496 507 } 497 508 498 509 /* RSS_SET */ ··· 929 868 .set = ethnl_rss_set, 930 869 .set_ntf_cmd = ETHTOOL_MSG_RSS_NTF, 931 870 }; 871 + 872 + /* RSS_CREATE */ 873 + 874 + const struct nla_policy ethnl_rss_create_policy[ETHTOOL_A_RSS_INPUT_XFRM + 1] = { 875 + [ETHTOOL_A_RSS_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy), 876 + [ETHTOOL_A_RSS_CONTEXT] = NLA_POLICY_MIN(NLA_U32, 1), 877 + [ETHTOOL_A_RSS_HFUNC] = NLA_POLICY_MIN(NLA_U32, 1), 878 + [ETHTOOL_A_RSS_INDIR] = NLA_POLICY_MIN(NLA_BINARY, 1), 879 + [ETHTOOL_A_RSS_HKEY] = NLA_POLICY_MIN(NLA_BINARY, 1), 880 + [ETHTOOL_A_RSS_INPUT_XFRM] = 881 + NLA_POLICY_MAX(NLA_U32, RXH_XFRM_SYM_OR_XOR), 882 + }; 883 + 884 + static int 885 + ethnl_rss_create_validate(struct net_device *dev, struct genl_info *info) 886 + { 887 + const struct ethtool_ops *ops = dev->ethtool_ops; 888 + struct nlattr **tb = info->attrs; 889 + struct nlattr *bad_attr = NULL; 890 + u32 rss_context, input_xfrm; 891 + 892 + if (!ops->create_rxfh_context) 893 + return -EOPNOTSUPP; 894 + 895 + rss_context = nla_get_u32_default(tb[ETHTOOL_A_RSS_CONTEXT], 0); 896 + if (ops->rxfh_max_num_contexts && 897 + ops->rxfh_max_num_contexts <= rss_context) { 898 + NL_SET_BAD_ATTR(info->extack, tb[ETHTOOL_A_RSS_CONTEXT]); 899 + return -ERANGE; 900 + } 901 + 902 + if (!ops->rxfh_per_ctx_key) { 903 + bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_HFUNC]; 904 + bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_HKEY]; 905 + bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_INPUT_XFRM]; 906 + } 907 + 908 + input_xfrm = nla_get_u32_default(tb[ETHTOOL_A_RSS_INPUT_XFRM], 0); 909 + if (input_xfrm & ~ops->supported_input_xfrm) 910 + bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_INPUT_XFRM]; 911 + 912 + if (bad_attr) { 913 + NL_SET_BAD_ATTR(info->extack, bad_attr); 914 + return -EOPNOTSUPP; 915 + } 916 + 917 + return 0; 918 + } 919 + 920 + static void 921 + ethnl_rss_create_send_ntf(struct sk_buff *rsp, struct net_device *dev) 922 + { 923 + struct nlmsghdr *nlh = (void *)rsp->data; 924 + struct genlmsghdr *genl_hdr; 925 + 926 + /* Convert the reply into a notification */ 927 + nlh->nlmsg_pid = 0; 928 + nlh->nlmsg_seq = ethnl_bcast_seq_next(); 929 + 930 + genl_hdr = nlmsg_data(nlh); 931 + genl_hdr->cmd = ETHTOOL_MSG_RSS_CREATE_NTF; 932 + 933 + ethnl_multicast(rsp, dev); 934 + } 935 + 936 + int ethnl_rss_create_doit(struct sk_buff *skb, struct genl_info *info) 937 + { 938 + bool indir_dflt = false, mod = false, ntf_fail = false; 939 + struct ethtool_rxfh_param rxfh = {}; 940 + struct ethtool_rxfh_context *ctx; 941 + struct nlattr **tb = info->attrs; 942 + struct rss_reply_data data = {}; 943 + const struct ethtool_ops *ops; 944 + struct rss_req_info req = {}; 945 + struct net_device *dev; 946 + struct sk_buff *rsp; 947 + void *hdr; 948 + u32 limit; 949 + int ret; 950 + 951 + rsp = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); 952 + if (!rsp) 953 + return -ENOMEM; 954 + 955 + ret = ethnl_parse_header_dev_get(&req.base, tb[ETHTOOL_A_RSS_HEADER], 956 + genl_info_net(info), info->extack, 957 + true); 958 + if (ret < 0) 959 + goto exit_free_rsp; 960 + 961 + dev = req.base.dev; 962 + ops = dev->ethtool_ops; 963 + 964 + req.rss_context = nla_get_u32_default(tb[ETHTOOL_A_RSS_CONTEXT], 0); 965 + 966 + ret = ethnl_rss_create_validate(dev, info); 967 + if (ret) 968 + goto exit_free_dev; 969 + 970 + rtnl_lock(); 971 + netdev_lock_ops(dev); 972 + 973 + ret = ethnl_ops_begin(dev); 974 + if (ret < 0) 975 + goto exit_dev_unlock; 976 + 977 + ret = rss_get_data_alloc(dev, &data); 978 + if (ret) 979 + goto exit_ops; 980 + 981 + ret = rss_set_prep_indir(dev, info, &data, &rxfh, &indir_dflt, &mod); 982 + if (ret) 983 + goto exit_clean_data; 984 + 985 + ethnl_update_u8(&rxfh.hfunc, tb[ETHTOOL_A_RSS_HFUNC], &mod); 986 + 987 + ret = rss_set_prep_hkey(dev, info, &data, &rxfh, &mod); 988 + if (ret) 989 + goto exit_free_indir; 990 + 991 + rxfh.input_xfrm = RXH_XFRM_NO_CHANGE; 992 + ethnl_update_u8(&rxfh.input_xfrm, tb[ETHTOOL_A_RSS_INPUT_XFRM], &mod); 993 + 994 + ctx = ethtool_rxfh_ctx_alloc(ops, data.indir_size, data.hkey_size); 995 + if (!ctx) { 996 + ret = -ENOMEM; 997 + goto exit_free_hkey; 998 + } 999 + 1000 + mutex_lock(&dev->ethtool->rss_lock); 1001 + if (!req.rss_context) { 1002 + limit = ops->rxfh_max_num_contexts ?: U32_MAX; 1003 + ret = xa_alloc(&dev->ethtool->rss_ctx, &req.rss_context, ctx, 1004 + XA_LIMIT(1, limit - 1), GFP_KERNEL_ACCOUNT); 1005 + } else { 1006 + ret = xa_insert(&dev->ethtool->rss_ctx, 1007 + req.rss_context, ctx, GFP_KERNEL_ACCOUNT); 1008 + } 1009 + if (ret < 0) { 1010 + NL_SET_ERR_MSG_ATTR(info->extack, tb[ETHTOOL_A_RSS_CONTEXT], 1011 + "error allocating context ID"); 1012 + goto err_unlock_free_ctx; 1013 + } 1014 + rxfh.rss_context = req.rss_context; 1015 + 1016 + ret = ops->create_rxfh_context(dev, ctx, &rxfh, info->extack); 1017 + if (ret) 1018 + goto err_ctx_id_free; 1019 + 1020 + /* Make sure driver populates defaults */ 1021 + WARN_ON_ONCE(!rxfh.key && ops->rxfh_per_ctx_key && 1022 + !memchr_inv(ethtool_rxfh_context_key(ctx), 0, 1023 + ctx->key_size)); 1024 + 1025 + /* Store the config from rxfh to Xarray.. */ 1026 + rss_set_ctx_update(ctx, tb, &data, &rxfh); 1027 + /* .. copy from Xarray to data. */ 1028 + __rss_prepare_ctx(dev, &data, ctx); 1029 + 1030 + hdr = ethnl_unicast_put(rsp, info->snd_portid, info->snd_seq, 1031 + ETHTOOL_MSG_RSS_CREATE_ACT_REPLY); 1032 + ntf_fail = ethnl_fill_reply_header(rsp, dev, ETHTOOL_A_RSS_HEADER); 1033 + ntf_fail |= rss_fill_reply(rsp, &req.base, &data.base); 1034 + if (WARN_ON(!hdr || ntf_fail)) { 1035 + ret = -EMSGSIZE; 1036 + goto exit_unlock; 1037 + } 1038 + 1039 + genlmsg_end(rsp, hdr); 1040 + 1041 + /* Use the same skb for the response and the notification, 1042 + * genlmsg_reply() will copy the skb if it has elevated user count. 1043 + */ 1044 + skb_get(rsp); 1045 + ret = genlmsg_reply(rsp, info); 1046 + ethnl_rss_create_send_ntf(rsp, dev); 1047 + rsp = NULL; 1048 + 1049 + exit_unlock: 1050 + mutex_unlock(&dev->ethtool->rss_lock); 1051 + exit_free_hkey: 1052 + kfree(rxfh.key); 1053 + exit_free_indir: 1054 + kfree(rxfh.indir); 1055 + exit_clean_data: 1056 + rss_get_data_free(&data); 1057 + exit_ops: 1058 + ethnl_ops_complete(dev); 1059 + exit_dev_unlock: 1060 + netdev_unlock_ops(dev); 1061 + rtnl_unlock(); 1062 + exit_free_dev: 1063 + ethnl_parse_header_dev_put(&req.base); 1064 + exit_free_rsp: 1065 + nlmsg_free(rsp); 1066 + return ret; 1067 + 1068 + err_ctx_id_free: 1069 + xa_erase(&dev->ethtool->rss_ctx, req.rss_context); 1070 + err_unlock_free_ctx: 1071 + kfree(ctx); 1072 + goto exit_unlock; 1073 + } 1074 + 1075 + /* RSS_DELETE */ 1076 + 1077 + const struct nla_policy ethnl_rss_delete_policy[ETHTOOL_A_RSS_CONTEXT + 1] = { 1078 + [ETHTOOL_A_RSS_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy), 1079 + [ETHTOOL_A_RSS_CONTEXT] = NLA_POLICY_MIN(NLA_U32, 1), 1080 + }; 1081 + 1082 + int ethnl_rss_delete_doit(struct sk_buff *skb, struct genl_info *info) 1083 + { 1084 + struct ethtool_rxfh_context *ctx; 1085 + struct nlattr **tb = info->attrs; 1086 + struct ethnl_req_info req = {}; 1087 + const struct ethtool_ops *ops; 1088 + struct net_device *dev; 1089 + u32 rss_context; 1090 + int ret; 1091 + 1092 + if (GENL_REQ_ATTR_CHECK(info, ETHTOOL_A_RSS_CONTEXT)) 1093 + return -EINVAL; 1094 + rss_context = nla_get_u32(tb[ETHTOOL_A_RSS_CONTEXT]); 1095 + 1096 + ret = ethnl_parse_header_dev_get(&req, tb[ETHTOOL_A_RSS_HEADER], 1097 + genl_info_net(info), info->extack, 1098 + true); 1099 + if (ret < 0) 1100 + return ret; 1101 + 1102 + dev = req.dev; 1103 + ops = dev->ethtool_ops; 1104 + 1105 + if (!ops->create_rxfh_context) 1106 + goto exit_free_dev; 1107 + 1108 + rtnl_lock(); 1109 + netdev_lock_ops(dev); 1110 + 1111 + ret = ethnl_ops_begin(dev); 1112 + if (ret < 0) 1113 + goto exit_dev_unlock; 1114 + 1115 + mutex_lock(&dev->ethtool->rss_lock); 1116 + ret = ethtool_check_rss_ctx_busy(dev, rss_context); 1117 + if (ret) 1118 + goto exit_unlock; 1119 + 1120 + ctx = xa_load(&dev->ethtool->rss_ctx, rss_context); 1121 + if (!ctx) { 1122 + ret = -ENOENT; 1123 + goto exit_unlock; 1124 + } 1125 + 1126 + ret = ops->remove_rxfh_context(dev, ctx, rss_context, info->extack); 1127 + if (ret) 1128 + goto exit_unlock; 1129 + 1130 + WARN_ON(xa_erase(&dev->ethtool->rss_ctx, rss_context) != ctx); 1131 + kfree(ctx); 1132 + 1133 + ethnl_rss_delete_notify(dev, rss_context); 1134 + 1135 + exit_unlock: 1136 + mutex_unlock(&dev->ethtool->rss_lock); 1137 + ethnl_ops_complete(dev); 1138 + exit_dev_unlock: 1139 + netdev_unlock_ops(dev); 1140 + rtnl_unlock(); 1141 + exit_free_dev: 1142 + ethnl_parse_header_dev_put(&req); 1143 + return ret; 1144 + }
+73
tools/testing/selftests/drivers/net/hw/rss_api.py
··· 5 5 API level tests for RSS (mostly Netlink vs IOCTL). 6 6 """ 7 7 8 + import errno 8 9 import glob 9 10 import random 10 11 from lib.py import ksft_run, ksft_exit, ksft_eq, ksft_is, ksft_ne, ksft_raises ··· 389 388 ksft_eq(ntf["name"], "rss-ntf") 390 389 ksft_ne(ntf["msg"]["flow-hash"]["tcp4"], change) 391 390 ksft_eq(next(ethnl.poll_ntf(duration=0.01), None), None) 391 + 392 + 393 + def test_rss_ctx_add(cfg): 394 + """ Test creating an additional RSS context via Netlink """ 395 + 396 + _require_2qs(cfg) 397 + 398 + # Test basic creation 399 + ctx = cfg.ethnl.rss_create_act({"header": {"dev-index": cfg.ifindex}}) 400 + d = defer(ethtool, f"-X {cfg.ifname} context {ctx.get('context')} delete") 401 + ksft_ne(ctx.get("context", 0), 0) 402 + ksft_ne(set(ctx.get("indir", [0])), {0}, 403 + comment="Driver should init the indirection table") 404 + 405 + # Try requesting the ID we just got allocated 406 + with ksft_raises(NlError) as cm: 407 + ctx = cfg.ethnl.rss_create_act({ 408 + "header": {"dev-index": cfg.ifindex}, 409 + "context": ctx.get("context"), 410 + }) 411 + ethtool(f"-X {cfg.ifname} context {ctx.get('context')} delete") 412 + d.exec() 413 + ksft_eq(cm.exception.nl_msg.error, -errno.EBUSY) 414 + 415 + # Test creating with a specified RSS table, and context ID 416 + ctx_id = ctx.get("context") 417 + ctx = cfg.ethnl.rss_create_act({ 418 + "header": {"dev-index": cfg.ifindex}, 419 + "context": ctx_id, 420 + "indir": [1], 421 + }) 422 + ethtool(f"-X {cfg.ifname} context {ctx.get('context')} delete") 423 + ksft_eq(ctx.get("context"), ctx_id) 424 + ksft_eq(set(ctx.get("indir", [0])), {1}) 425 + 426 + 427 + def test_rss_ctx_ntf(cfg): 428 + """ Test notifications for creating additional RSS contexts """ 429 + 430 + ethnl = EthtoolFamily() 431 + ethnl.ntf_subscribe("monitor") 432 + 433 + # Create / delete via Netlink 434 + ctx = cfg.ethnl.rss_create_act({"header": {"dev-index": cfg.ifindex}}) 435 + cfg.ethnl.rss_delete_act({ 436 + "header": {"dev-index": cfg.ifindex}, 437 + "context": ctx["context"], 438 + }) 439 + 440 + ntf = next(ethnl.poll_ntf(duration=0.2), None) 441 + if ntf is None: 442 + raise KsftFailEx("[NL] No notification after context creation") 443 + ksft_eq(ntf["name"], "rss-create-ntf") 444 + ksft_eq(ctx, ntf["msg"]) 445 + 446 + ntf = next(ethnl.poll_ntf(duration=0.2), None) 447 + if ntf is None: 448 + raise KsftFailEx("[NL] No notification after context deletion") 449 + ksft_eq(ntf["name"], "rss-delete-ntf") 450 + 451 + # Create / deleve via IOCTL 452 + ctx_id = _ethtool_create(cfg, "--disable-netlink -X", "context new") 453 + ethtool(f"--disable-netlink -X {cfg.ifname} context {ctx_id} delete") 454 + ntf = next(ethnl.poll_ntf(duration=0.2), None) 455 + if ntf is None: 456 + raise KsftFailEx("[IOCTL] No notification after context creation") 457 + ksft_eq(ntf["name"], "rss-create-ntf") 458 + 459 + ntf = next(ethnl.poll_ntf(duration=0.2), None) 460 + if ntf is None: 461 + raise KsftFailEx("[IOCTL] No notification after context deletion") 462 + ksft_eq(ntf["name"], "rss-delete-ntf") 392 463 393 464 394 465 def main() -> None: