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

Merge branch 'net-ethtool-rss-add-notifications'

Jakub Kicinski says:

====================
net: ethtool: rss: add notifications

Next step on the path to moving RSS config to Netlink. With the
refactoring of the driver-facing API for ETHTOOL_GRXFH/ETHTOOL_SRXFH
out of the way we can move on to more interesting work.

Add Netlink notifications for changes in RSS configuration.

As a reminder (part) of rss-get was introduced in previous releases
when input-xfrm (symmetric hashing) was added. rss-set isn't
implemented, yet, but we can implement rss-ntf and hook it into
the changes done via the IOCTL path (same as other ethtool-nl
notifications do).

Most of the series is concerned with passing arguments to notifications.
So far none of the notifications needed to be parametrized, but RSS can
have multiple contexts per device, and since GET operates on a single
context at a time, the notification needs to also be scoped to a context.
Patches 2-5 add support for passing arguments to notifications thru
ethtool-nl generic infra.

The notification handling itself is pretty trivial, it's mostly
hooking in the right entries into the ethool-nl op tables.

v1: https://lore.kernel.org/20250621171944.2619249-1-kuba@kernel.org
====================

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

+180 -33
+13
Documentation/netlink/specs/ethtool.yaml
··· 2492 2492 attributes: 2493 2493 - header 2494 2494 - events 2495 + - 2496 + name: rss-ntf 2497 + doc: | 2498 + Notification for change in RSS configuration. 2499 + For additional contexts only modifications are modified, not creation 2500 + or removal of the contexts. 2501 + notify: rss-get 2502 + 2503 + mcast-groups: 2504 + list: 2505 + - 2506 + name: monitor 2507 + c-define-name: ethtool-mcgrp-monitor-name
+2 -1
Documentation/networking/ethtool-netlink.rst
··· 281 281 ``ETHTOOL_MSG_MODULE_GET_REPLY`` transceiver module parameters 282 282 ``ETHTOOL_MSG_PSE_GET_REPLY`` PSE parameters 283 283 ``ETHTOOL_MSG_RSS_GET_REPLY`` RSS settings 284 + ``ETHTOOL_MSG_RSS_NTF`` RSS settings 284 285 ``ETHTOOL_MSG_PLCA_GET_CFG_REPLY`` PLCA RS parameters 285 286 ``ETHTOOL_MSG_PLCA_GET_STATUS_REPLY`` PLCA RS status 286 287 ``ETHTOOL_MSG_PLCA_NTF`` PLCA RS parameters ··· 2451 2450 ``ETHTOOL_SRXNTUPLE`` n/a 2452 2451 ``ETHTOOL_GRXNTUPLE`` n/a 2453 2452 ``ETHTOOL_GSSET_INFO`` ``ETHTOOL_MSG_STRSET_GET`` 2454 - ``ETHTOOL_GRXFHINDIR`` n/a 2453 + ``ETHTOOL_GRXFHINDIR`` ``ETHTOOL_MSG_RSS_GET`` 2455 2454 ``ETHTOOL_SRXFHINDIR`` n/a 2456 2455 ``ETHTOOL_GFEATURES`` ``ETHTOOL_MSG_FEATURES_GET`` 2457 2456 ``ETHTOOL_SFEATURES`` ``ETHTOOL_MSG_FEATURES_SET``
+2 -3
include/linux/netdevice.h
··· 5138 5138 struct netdev_bonding_info *bonding_info); 5139 5139 5140 5140 #if IS_ENABLED(CONFIG_ETHTOOL_NETLINK) 5141 - void ethtool_notify(struct net_device *dev, unsigned int cmd, const void *data); 5141 + void ethtool_notify(struct net_device *dev, unsigned int cmd); 5142 5142 #else 5143 - static inline void ethtool_notify(struct net_device *dev, unsigned int cmd, 5144 - const void *data) 5143 + static inline void ethtool_notify(struct net_device *dev, unsigned int cmd) 5145 5144 { 5146 5145 } 5147 5146 #endif
-2
include/uapi/linux/ethtool_netlink.h
··· 208 208 ETHTOOL_A_STATS_PHY_MAX = (__ETHTOOL_A_STATS_PHY_CNT - 1) 209 209 }; 210 210 211 - #define ETHTOOL_MCGRP_MONITOR_NAME "monitor" 212 - 213 211 #endif /* _UAPI_LINUX_ETHTOOL_NETLINK_H_ */
+8
net/ethtool/common.h
··· 74 74 75 75 bool __ethtool_dev_mm_supported(struct net_device *dev); 76 76 77 + #if IS_ENABLED(CONFIG_ETHTOOL_NETLINK) 78 + void ethtool_rss_notify(struct net_device *dev, u32 rss_context); 79 + #else 80 + static inline void ethtool_rss_notify(struct net_device *dev, u32 rss_context) 81 + { 82 + } 83 + #endif 84 + 77 85 #endif /* _ETHTOOL_COMMON_H */
+16 -12
net/ethtool/ioctl.c
··· 617 617 618 618 err = dev->ethtool_ops->set_link_ksettings(dev, &link_ksettings); 619 619 if (err >= 0) { 620 - ethtool_notify(dev, ETHTOOL_MSG_LINKINFO_NTF, NULL); 621 - ethtool_notify(dev, ETHTOOL_MSG_LINKMODES_NTF, NULL); 620 + ethtool_notify(dev, ETHTOOL_MSG_LINKINFO_NTF); 621 + ethtool_notify(dev, ETHTOOL_MSG_LINKMODES_NTF); 622 622 } 623 623 return err; 624 624 } ··· 708 708 __ETHTOOL_LINK_MODE_MASK_NU32; 709 709 ret = dev->ethtool_ops->set_link_ksettings(dev, &link_ksettings); 710 710 if (ret >= 0) { 711 - ethtool_notify(dev, ETHTOOL_MSG_LINKINFO_NTF, NULL); 712 - ethtool_notify(dev, ETHTOOL_MSG_LINKMODES_NTF, NULL); 711 + ethtool_notify(dev, ETHTOOL_MSG_LINKINFO_NTF); 712 + ethtool_notify(dev, ETHTOOL_MSG_LINKMODES_NTF); 713 713 } 714 714 return ret; 715 715 } ··· 1502 1502 struct ethtool_rxfh rxfh; 1503 1503 bool locked = false; /* dev->ethtool->rss_lock taken */ 1504 1504 bool create = false; 1505 + bool mod = false; 1505 1506 u8 *rss_config; 1506 1507 int ret; 1507 1508 ··· 1689 1688 } 1690 1689 goto out; 1691 1690 } 1691 + mod = !create && !rxfh_dev.rss_delete; 1692 1692 1693 1693 if (copy_to_user(useraddr + offsetof(struct ethtool_rxfh, rss_context), 1694 1694 &rxfh_dev.rss_context, sizeof(rxfh_dev.rss_context))) ··· 1759 1757 if (locked) 1760 1758 mutex_unlock(&dev->ethtool->rss_lock); 1761 1759 kfree(rss_config); 1760 + if (mod) 1761 + ethtool_rss_notify(dev, rxfh.rss_context); 1762 1762 return ret; 1763 1763 } 1764 1764 ··· 1872 1868 return ret; 1873 1869 1874 1870 dev->ethtool->wol_enabled = !!wol.wolopts; 1875 - ethtool_notify(dev, ETHTOOL_MSG_WOL_NTF, NULL); 1871 + ethtool_notify(dev, ETHTOOL_MSG_WOL_NTF); 1876 1872 1877 1873 return 0; 1878 1874 } ··· 1948 1944 eee_to_keee(&keee, &eee); 1949 1945 ret = dev->ethtool_ops->set_eee(dev, &keee); 1950 1946 if (!ret) 1951 - ethtool_notify(dev, ETHTOOL_MSG_EEE_NTF, NULL); 1947 + ethtool_notify(dev, ETHTOOL_MSG_EEE_NTF); 1952 1948 return ret; 1953 1949 } 1954 1950 ··· 2188 2184 ret = dev->ethtool_ops->set_coalesce(dev, &coalesce, &kernel_coalesce, 2189 2185 NULL); 2190 2186 if (!ret) 2191 - ethtool_notify(dev, ETHTOOL_MSG_COALESCE_NTF, NULL); 2187 + ethtool_notify(dev, ETHTOOL_MSG_COALESCE_NTF); 2192 2188 return ret; 2193 2189 } 2194 2190 ··· 2232 2228 ret = dev->ethtool_ops->set_ringparam(dev, &ringparam, 2233 2229 &kernel_ringparam, NULL); 2234 2230 if (!ret) 2235 - ethtool_notify(dev, ETHTOOL_MSG_RINGS_NTF, NULL); 2231 + ethtool_notify(dev, ETHTOOL_MSG_RINGS_NTF); 2236 2232 return ret; 2237 2233 } 2238 2234 ··· 2299 2295 2300 2296 ret = dev->ethtool_ops->set_channels(dev, &channels); 2301 2297 if (!ret) 2302 - ethtool_notify(dev, ETHTOOL_MSG_CHANNELS_NTF, NULL); 2298 + ethtool_notify(dev, ETHTOOL_MSG_CHANNELS_NTF); 2303 2299 return ret; 2304 2300 } 2305 2301 ··· 2330 2326 2331 2327 ret = dev->ethtool_ops->set_pauseparam(dev, &pauseparam); 2332 2328 if (!ret) 2333 - ethtool_notify(dev, ETHTOOL_MSG_PAUSE_NTF, NULL); 2329 + ethtool_notify(dev, ETHTOOL_MSG_PAUSE_NTF); 2334 2330 return ret; 2335 2331 } 2336 2332 ··· 3332 3328 rc = ethtool_set_value_void(dev, useraddr, 3333 3329 dev->ethtool_ops->set_msglevel); 3334 3330 if (!rc) 3335 - ethtool_notify(dev, ETHTOOL_MSG_DEBUG_NTF, NULL); 3331 + ethtool_notify(dev, ETHTOOL_MSG_DEBUG_NTF); 3336 3332 break; 3337 3333 case ETHTOOL_GEEE: 3338 3334 rc = ethtool_get_eee(dev, useraddr); ··· 3396 3392 rc = ethtool_get_value(dev, useraddr, ethcmd, 3397 3393 dev->ethtool_ops->get_priv_flags); 3398 3394 if (!rc) 3399 - ethtool_notify(dev, ETHTOOL_MSG_PRIVFLAGS_NTF, NULL); 3395 + ethtool_notify(dev, ETHTOOL_MSG_PRIVFLAGS_NTF); 3400 3396 break; 3401 3397 case ETHTOOL_SPFLAGS: 3402 3398 rc = ethtool_set_value(dev, useraddr,
+32 -15
net/ethtool/netlink.c
··· 863 863 static int ethnl_default_set_doit(struct sk_buff *skb, struct genl_info *info) 864 864 { 865 865 const struct ethnl_request_ops *ops; 866 - struct ethnl_req_info req_info = {}; 867 866 const u8 cmd = info->genlhdr->cmd; 867 + struct ethnl_req_info *req_info; 868 868 struct net_device *dev; 869 869 int ret; 870 870 ··· 874 874 if (GENL_REQ_ATTR_CHECK(info, ops->hdr_attr)) 875 875 return -EINVAL; 876 876 877 - ret = ethnl_parse_header_dev_get(&req_info, info->attrs[ops->hdr_attr], 878 - genl_info_net(info), info->extack, 879 - true); 877 + req_info = kzalloc(ops->req_info_size, GFP_KERNEL); 878 + if (!req_info) 879 + return -ENOMEM; 880 + 881 + ret = ethnl_default_parse(req_info, info, ops, true); 880 882 if (ret < 0) 881 - return ret; 883 + goto out_free_req; 882 884 883 885 if (ops->set_validate) { 884 - ret = ops->set_validate(&req_info, info); 886 + ret = ops->set_validate(req_info, info); 885 887 /* 0 means nothing to do */ 886 888 if (ret <= 0) 887 889 goto out_dev; 888 890 } 889 891 890 - dev = req_info.dev; 892 + dev = req_info->dev; 891 893 892 894 rtnl_lock(); 893 895 netdev_lock_ops(dev); ··· 904 902 if (ret < 0) 905 903 goto out_free_cfg; 906 904 907 - ret = ops->set(&req_info, info); 905 + ret = ops->set(req_info, info); 908 906 if (ret < 0) 909 907 goto out_ops; 910 908 911 909 swap(dev->cfg, dev->cfg_pending); 912 910 if (!ret) 913 911 goto out_ops; 914 - ethtool_notify(dev, ops->set_ntf_cmd, NULL); 912 + ethnl_notify(dev, ops->set_ntf_cmd, req_info); 915 913 916 914 ret = 0; 917 915 out_ops: ··· 923 921 netdev_unlock_ops(dev); 924 922 rtnl_unlock(); 925 923 out_dev: 926 - ethnl_parse_header_dev_put(&req_info); 924 + ethnl_parse_header_dev_put(req_info); 925 + out_free_req: 926 + kfree(req_info); 927 927 return ret; 928 928 } 929 929 ··· 946 942 [ETHTOOL_MSG_MODULE_NTF] = &ethnl_module_request_ops, 947 943 [ETHTOOL_MSG_PLCA_NTF] = &ethnl_plca_cfg_request_ops, 948 944 [ETHTOOL_MSG_MM_NTF] = &ethnl_mm_request_ops, 945 + [ETHTOOL_MSG_RSS_NTF] = &ethnl_rss_request_ops, 949 946 }; 950 947 951 948 /* default notification handler */ 952 949 static void ethnl_default_notify(struct net_device *dev, unsigned int cmd, 953 - const void *data) 950 + const struct ethnl_req_info *orig_req_info) 954 951 { 955 952 struct ethnl_reply_data *reply_data; 956 953 const struct ethnl_request_ops *ops; ··· 980 975 981 976 req_info->dev = dev; 982 977 req_info->flags |= ETHTOOL_FLAG_COMPACT_BITSETS; 978 + if (orig_req_info) { 979 + req_info->phy_index = orig_req_info->phy_index; 980 + memcpy(&req_info[1], &orig_req_info[1], 981 + ops->req_info_size - sizeof(*req_info)); 982 + } 983 983 984 984 netdev_ops_assert_locked(dev); 985 985 ··· 1035 1025 /* notifications */ 1036 1026 1037 1027 typedef void (*ethnl_notify_handler_t)(struct net_device *dev, unsigned int cmd, 1038 - const void *data); 1028 + const struct ethnl_req_info *req_info); 1039 1029 1040 1030 static const ethnl_notify_handler_t ethnl_notify_handlers[] = { 1041 1031 [ETHTOOL_MSG_LINKINFO_NTF] = ethnl_default_notify, ··· 1053 1043 [ETHTOOL_MSG_MODULE_NTF] = ethnl_default_notify, 1054 1044 [ETHTOOL_MSG_PLCA_NTF] = ethnl_default_notify, 1055 1045 [ETHTOOL_MSG_MM_NTF] = ethnl_default_notify, 1046 + [ETHTOOL_MSG_RSS_NTF] = ethnl_default_notify, 1056 1047 }; 1057 1048 1058 - void ethtool_notify(struct net_device *dev, unsigned int cmd, const void *data) 1049 + void ethnl_notify(struct net_device *dev, unsigned int cmd, 1050 + const struct ethnl_req_info *req_info) 1059 1051 { 1060 1052 if (unlikely(!ethnl_ok)) 1061 1053 return; ··· 1065 1053 1066 1054 if (likely(cmd < ARRAY_SIZE(ethnl_notify_handlers) && 1067 1055 ethnl_notify_handlers[cmd])) 1068 - ethnl_notify_handlers[cmd](dev, cmd, data); 1056 + ethnl_notify_handlers[cmd](dev, cmd, req_info); 1069 1057 else 1070 1058 WARN_ONCE(1, "notification %u not implemented (dev=%s)\n", 1071 1059 cmd, netdev_name(dev)); 1060 + } 1061 + 1062 + void ethtool_notify(struct net_device *dev, unsigned int cmd) 1063 + { 1064 + ethnl_notify(dev, cmd, NULL); 1072 1065 } 1073 1066 EXPORT_SYMBOL(ethtool_notify); 1074 1067 ··· 1081 1064 { 1082 1065 struct net_device *dev = netdev_notifier_info_to_dev(info); 1083 1066 1084 - ethtool_notify(dev, ETHTOOL_MSG_FEATURES_NTF, NULL); 1067 + ethtool_notify(dev, ETHTOOL_MSG_FEATURES_NTF); 1085 1068 } 1086 1069 1087 1070 static int ethnl_netdev_event(struct notifier_block *this, unsigned long event,
+4
net/ethtool/netlink.h
··· 23 23 void *ethnl_bcastmsg_put(struct sk_buff *skb, u8 cmd); 24 24 void *ethnl_unicast_put(struct sk_buff *skb, u32 portid, u32 seq, u8 cmd); 25 25 int ethnl_multicast(struct sk_buff *skb, struct net_device *dev); 26 + void ethnl_notify(struct net_device *dev, unsigned int cmd, 27 + const struct ethnl_req_info *req_info); 26 28 27 29 /** 28 30 * ethnl_strz_size() - calculate attribute length for fixed size string ··· 339 337 * header is already filled on entry, the rest up to @repdata_offset 340 338 * is zero initialized. This callback should only modify type specific 341 339 * request info by parsed attributes from request message. 340 + * Called for both GET and SET. Information parsed for SET will 341 + * be conveyed to the req_info used during NTF generation. 342 342 * @prepare_data: 343 343 * Retrieve and prepare data needed to compose a reply message. Calls to 344 344 * ethtool_ops handlers are limited to this callback. Common reply data
+11
net/ethtool/rss.c
··· 358 358 return ret; 359 359 } 360 360 361 + /* RSS_NTF */ 362 + 363 + void ethtool_rss_notify(struct net_device *dev, u32 rss_context) 364 + { 365 + struct rss_req_info req_info = { 366 + .rss_context = rss_context, 367 + }; 368 + 369 + ethnl_notify(dev, ETHTOOL_MSG_RSS_NTF, &req_info.base); 370 + } 371 + 361 372 const struct ethnl_request_ops ethnl_rss_request_ops = { 362 373 .request_cmd = ETHTOOL_MSG_RSS_GET, 363 374 .reply_cmd = ETHTOOL_MSG_RSS_GET_REPLY,
+89
tools/testing/selftests/drivers/net/hw/rss_api.py
··· 1 + #!/usr/bin/env python3 2 + # SPDX-License-Identifier: GPL-2.0 3 + 4 + """ 5 + API level tests for RSS (mostly Netlink vs IOCTL). 6 + """ 7 + 8 + import glob 9 + from lib.py import ksft_run, ksft_exit, ksft_eq, ksft_is, ksft_ne 10 + from lib.py import KsftSkipEx, KsftFailEx 11 + from lib.py import defer, ethtool 12 + from lib.py import EthtoolFamily 13 + from lib.py import NetDrvEnv 14 + 15 + 16 + def _ethtool_create(cfg, act, opts): 17 + output = ethtool(f"{act} {cfg.ifname} {opts}").stdout 18 + # Output will be something like: "New RSS context is 1" or 19 + # "Added rule with ID 7", we want the integer from the end 20 + return int(output.split()[-1]) 21 + 22 + 23 + def test_rxfh_indir_ntf(cfg): 24 + """ 25 + Check that Netlink notifications are generated when RSS indirection 26 + table was modified. 27 + """ 28 + 29 + qcnt = len(glob.glob(f"/sys/class/net/{cfg.ifname}/queues/rx-*")) 30 + if qcnt < 2: 31 + raise KsftSkipEx(f"Local has only {qcnt} queues") 32 + 33 + ethnl = EthtoolFamily() 34 + ethnl.ntf_subscribe("monitor") 35 + 36 + ethtool(f"--disable-netlink -X {cfg.ifname} weight 0 1") 37 + reset = defer(ethtool, f"-X {cfg.ifname} default") 38 + 39 + ntf = next(ethnl.poll_ntf(duration=0.2), None) 40 + if ntf is None: 41 + raise KsftFailEx("No notification received") 42 + ksft_eq(ntf["name"], "rss-ntf") 43 + ksft_eq(set(ntf["msg"]["indir"]), {1}) 44 + 45 + reset.exec() 46 + ntf = next(ethnl.poll_ntf(duration=0.2), None) 47 + if ntf is None: 48 + raise KsftFailEx("No notification received after reset") 49 + ksft_eq(ntf["name"], "rss-ntf") 50 + ksft_is(ntf["msg"].get("context"), None) 51 + ksft_ne(set(ntf["msg"]["indir"]), {1}) 52 + 53 + 54 + def test_rxfh_indir_ctx_ntf(cfg): 55 + """ 56 + Check that Netlink notifications are generated when RSS indirection 57 + table was modified on an additional RSS context. 58 + """ 59 + 60 + qcnt = len(glob.glob(f"/sys/class/net/{cfg.ifname}/queues/rx-*")) 61 + if qcnt < 2: 62 + raise KsftSkipEx(f"Local has only {qcnt} queues") 63 + 64 + ctx_id = _ethtool_create(cfg, "-X", "context new") 65 + defer(ethtool, f"-X {cfg.ifname} context {ctx_id} delete") 66 + 67 + ethnl = EthtoolFamily() 68 + ethnl.ntf_subscribe("monitor") 69 + 70 + ethtool(f"--disable-netlink -X {cfg.ifname} context {ctx_id} weight 0 1") 71 + 72 + ntf = next(ethnl.poll_ntf(duration=0.2), None) 73 + if ntf is None: 74 + raise KsftFailEx("No notification received") 75 + ksft_eq(ntf["name"], "rss-ntf") 76 + ksft_eq(ntf["msg"].get("context"), ctx_id) 77 + ksft_eq(set(ntf["msg"]["indir"]), {1}) 78 + 79 + 80 + def main() -> None: 81 + """ Ksft boiler plate main """ 82 + 83 + with NetDrvEnv(__file__, nsim_test=False) as cfg: 84 + ksft_run(globs=globals(), case_pfx={"test_"}, args=(cfg, )) 85 + ksft_exit() 86 + 87 + 88 + if __name__ == "__main__": 89 + main()