From f81171fecd0c33f442fe3aa94cff650d0069152a Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 20 Nov 2025 20:02:55 -0800 Subject: selftests: hw-net: auto-disable building the iouring C code Looks like the liburing is not updated by distros very aggressively. Presumably because a lot of packages depend on it. I just updated to Fedora 43 and it's still on liburing 2.9. The test is 9mo old, at this stage I think this warrants handling the build failure more gracefully. Detect if iouring is recent enough and if not print a warning and exclude the C prog from build. The Python test will just fail since the binary won't exist. But it removes the major annoyance of having to update liburing from sources when developing other tests. Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/20251121040259.3647749-2-kuba@kernel.org Signed-off-by: Jakub Kicinski --- tools/testing/selftests/drivers/net/hw/Makefile | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/drivers/net/hw/Makefile b/tools/testing/selftests/drivers/net/hw/Makefile index 1760238e9d4f..949aeeeb357d 100644 --- a/tools/testing/selftests/drivers/net/hw/Makefile +++ b/tools/testing/selftests/drivers/net/hw/Makefile @@ -1,7 +1,20 @@ # SPDX-License-Identifier: GPL-2.0+ OR MIT +# Check if io_uring supports zero-copy receive +HAS_IOURING_ZCRX := $(shell \ + echo -e '#include \n' \ + 'void *func = (void *)io_uring_register_ifq;\n' \ + 'int main() {return 0;}' | \ + $(CC) -luring -x c - -o /dev/null 2>&1 && echo y) + +ifeq ($(HAS_IOURING_ZCRX),y) +COND_GEN_FILES += iou-zcrx +else +$(warning excluding iouring tests, liburing not installed or too old) +endif + TEST_GEN_FILES := \ - iou-zcrx \ + $(COND_GEN_FILES) \ toeplitz \ # end of TEST_GEN_FILES @@ -58,4 +71,6 @@ include ../../../net/ynl.mk include ../../../net/bpf.mk +ifeq ($(HAS_IOURING_ZCRX),y) $(OUTPUT)/iou-zcrx: LDLIBS += -luring +endif -- cgit v1.2.3 From 27c512af190e037a6f330e9461fc4741fb77da45 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 20 Nov 2025 20:02:56 -0800 Subject: selftests: hw-net: toeplitz: make sure NICs have pure Toeplitz configured Make sure that the NIC under test is configured for pure Toeplitz hashing, and no input key transform (no symmetric hashing). Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/20251121040259.3647749-3-kuba@kernel.org Signed-off-by: Jakub Kicinski --- tools/testing/selftests/drivers/net/hw/toeplitz.py | 29 ++++++++++++++-------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/tools/testing/selftests/drivers/net/hw/toeplitz.py b/tools/testing/selftests/drivers/net/hw/toeplitz.py index 9019a8c1ff62..642a5cc385b6 100755 --- a/tools/testing/selftests/drivers/net/hw/toeplitz.py +++ b/tools/testing/selftests/drivers/net/hw/toeplitz.py @@ -17,6 +17,9 @@ from lib.py import cmd, bkg, rand_port, defer from lib.py import ksft_in from lib.py import ksft_variants, KsftNamedVariant, KsftSkipEx, KsftFailEx +# "define" for the ID of the Toeplitz hash function +ETH_RSS_HASH_TOP = 1 + def _check_rps_and_rfs_not_configured(cfg): """Verify that RPS is not already configured.""" @@ -34,16 +37,6 @@ def _check_rps_and_rfs_not_configured(cfg): raise KsftSkipEx(f"RFS already configured {rfs_file}: {val}") -def _get_rss_key(cfg): - """ - Read the RSS key from the device. - Return a string in the traditional %02x:%02x:%02x:.. format. - """ - - rss = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) - return ':'.join(f'{b:02x}' for b in rss["hkey"]) - - def _get_cpu_for_irq(irq): with open(f"/proc/irq/{irq}/smp_affinity_list", "r", encoding="utf-8") as fp: @@ -153,8 +146,22 @@ def test(cfg, proto_flag, ipver, grp): # Check that rxhash is enabled ksft_in("receive-hashing: on", cmd(f"ethtool -k {cfg.ifname}").stdout) + rss = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) + # Make sure NIC is configured to use Toeplitz hash, and no key xfrm. + if rss.get('hfunc') != ETH_RSS_HASH_TOP or rss.get('input-xfrm'): + cfg.ethnl.rss_set({"header": {"dev-index": cfg.ifindex}, + "hfunc": ETH_RSS_HASH_TOP, + "input-xfrm": {}}) + defer(cfg.ethnl.rss_set, {"header": {"dev-index": cfg.ifindex}, + "hfunc": rss.get('hfunc'), + "input-xfrm": rss.get('input-xfrm', {}) + }) + # Refresh in case changing hfunc changes things. + rss = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) + + key = ':'.join(f'{b:02x}' for b in rss["hkey"]) + port = rand_port(socket.SOCK_DGRAM) - key = _get_rss_key(cfg) toeplitz_path = cfg.test_dir / "toeplitz" rx_cmd = [ -- cgit v1.2.3 From aa91dbf3eda2977e3046d4838eadf7af4dbd47ec Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 20 Nov 2025 20:02:57 -0800 Subject: selftests: hw-net: toeplitz: read the RSS key directly from C Now that we have YNL support for RSS accessing the RSS info from C is very easy. Instead of passing the RSS key from Python do it directly in the C code. Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/20251121040259.3647749-4-kuba@kernel.org Signed-off-by: Jakub Kicinski --- tools/testing/selftests/drivers/net/hw/Makefile | 6 ++-- tools/testing/selftests/drivers/net/hw/toeplitz.c | 41 +++++++++++++++++++++- tools/testing/selftests/drivers/net/hw/toeplitz.py | 5 --- 3 files changed, 44 insertions(+), 8 deletions(-) diff --git a/tools/testing/selftests/drivers/net/hw/Makefile b/tools/testing/selftests/drivers/net/hw/Makefile index 949aeeeb357d..7c819fdfa107 100644 --- a/tools/testing/selftests/drivers/net/hw/Makefile +++ b/tools/testing/selftests/drivers/net/hw/Makefile @@ -15,7 +15,6 @@ endif TEST_GEN_FILES := \ $(COND_GEN_FILES) \ - toeplitz \ # end of TEST_GEN_FILES TEST_PROGS = \ @@ -55,7 +54,10 @@ TEST_INCLUDES := \ # # YNL files, must be before "include ..lib.mk" -YNL_GEN_FILES := ncdevmem +YNL_GEN_FILES := \ + ncdevmem \ + toeplitz \ +# end of YNL_GEN_FILES TEST_GEN_FILES += $(YNL_GEN_FILES) TEST_GEN_FILES += $(patsubst %.c,%.o,$(wildcard *.bpf.c)) diff --git a/tools/testing/selftests/drivers/net/hw/toeplitz.c b/tools/testing/selftests/drivers/net/hw/toeplitz.c index afc5f910b006..7420a4e201cc 100644 --- a/tools/testing/selftests/drivers/net/hw/toeplitz.c +++ b/tools/testing/selftests/drivers/net/hw/toeplitz.c @@ -52,6 +52,9 @@ #include #include +#include +#include "ethtool-user.h" + #include "../../../kselftest.h" #include "../../../net/lib/ksft.h" @@ -483,6 +486,42 @@ static void parse_rps_bitmap(const char *arg) rps_silo_to_cpu[cfg_num_rps_cpus++] = i; } +static void read_rss_dev_info_ynl(void) +{ + struct ethtool_rss_get_req *req; + struct ethtool_rss_get_rsp *rsp; + struct ynl_sock *ys; + + ys = ynl_sock_create(&ynl_ethtool_family, NULL); + if (!ys) + error(1, errno, "ynl_sock_create failed"); + + req = ethtool_rss_get_req_alloc(); + if (!req) + error(1, errno, "ethtool_rss_get_req_alloc failed"); + + ethtool_rss_get_req_set_header_dev_name(req, cfg_ifname); + + rsp = ethtool_rss_get(ys, req); + if (!rsp) + error(1, ys->err.code, "YNL: %s", ys->err.msg); + + if (!rsp->_len.hkey) + error(1, 0, "RSS key not available for %s", cfg_ifname); + + if (rsp->_len.hkey < TOEPLITZ_KEY_MIN_LEN || + rsp->_len.hkey > TOEPLITZ_KEY_MAX_LEN) + error(1, 0, "RSS key length %u out of bounds [%u, %u]", + rsp->_len.hkey, TOEPLITZ_KEY_MIN_LEN, + TOEPLITZ_KEY_MAX_LEN); + + memcpy(toeplitz_key, rsp->hkey, rsp->_len.hkey); + + ethtool_rss_get_rsp_free(rsp); + ethtool_rss_get_req_free(req); + ynl_sock_destroy(ys); +} + static void parse_opts(int argc, char **argv) { static struct option long_options[] = { @@ -551,7 +590,7 @@ static void parse_opts(int argc, char **argv) } if (!have_toeplitz) - error(1, 0, "Must supply rss key ('-k')"); + read_rss_dev_info_ynl(); num_cpus = get_nprocs(); if (num_cpus > RSS_MAX_CPUS) diff --git a/tools/testing/selftests/drivers/net/hw/toeplitz.py b/tools/testing/selftests/drivers/net/hw/toeplitz.py index 642a5cc385b6..945c58d23310 100755 --- a/tools/testing/selftests/drivers/net/hw/toeplitz.py +++ b/tools/testing/selftests/drivers/net/hw/toeplitz.py @@ -156,10 +156,6 @@ def test(cfg, proto_flag, ipver, grp): "hfunc": rss.get('hfunc'), "input-xfrm": rss.get('input-xfrm', {}) }) - # Refresh in case changing hfunc changes things. - rss = cfg.ethnl.rss_get({"header": {"dev-index": cfg.ifindex}}) - - key = ':'.join(f'{b:02x}' for b in rss["hkey"]) port = rand_port(socket.SOCK_DGRAM) @@ -170,7 +166,6 @@ def test(cfg, proto_flag, ipver, grp): proto_flag, "-d", str(port), "-i", cfg.ifname, - "-k", key, "-T", "1000", "-s", "-v" -- cgit v1.2.3 From c0105ffc5056852b16824bea9e8e0cd9a598d715 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 20 Nov 2025 20:02:58 -0800 Subject: selftests: hw-net: toeplitz: read indirection table from the device Replace the simple modulo math with the real indirection table read from the device. This makes the tests pass for mlx5 and bnxt NICs. Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/20251121040259.3647749-5-kuba@kernel.org Signed-off-by: Jakub Kicinski --- tools/testing/selftests/drivers/net/hw/toeplitz.c | 24 ++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/drivers/net/hw/toeplitz.c b/tools/testing/selftests/drivers/net/hw/toeplitz.c index 7420a4e201cc..a4d04438c313 100644 --- a/tools/testing/selftests/drivers/net/hw/toeplitz.c +++ b/tools/testing/selftests/drivers/net/hw/toeplitz.c @@ -68,6 +68,7 @@ #define FOUR_TUPLE_MAX_LEN ((sizeof(struct in6_addr) * 2) + (sizeof(uint16_t) * 2)) #define RSS_MAX_CPUS (1 << 16) /* real constraint is PACKET_FANOUT_MAX */ +#define RSS_MAX_INDIR (1 << 16) #define RPS_MAX_CPUS 16UL /* must be a power of 2 */ @@ -105,6 +106,8 @@ struct ring_state { static unsigned int rx_irq_cpus[RSS_MAX_CPUS]; /* map from rxq to cpu */ static int rps_silo_to_cpu[RPS_MAX_CPUS]; static unsigned char toeplitz_key[TOEPLITZ_KEY_MAX_LEN]; +static unsigned int rss_indir_tbl[RSS_MAX_INDIR]; +static unsigned int rss_indir_tbl_size; static struct ring_state rings[RSS_MAX_CPUS]; static inline uint32_t toeplitz(const unsigned char *four_tuple, @@ -133,7 +136,12 @@ static inline uint32_t toeplitz(const unsigned char *four_tuple, /* Compare computed cpu with arrival cpu from packet_fanout_cpu */ static void verify_rss(uint32_t rx_hash, int cpu) { - int queue = rx_hash % cfg_num_queues; + int queue; + + if (rss_indir_tbl_size) + queue = rss_indir_tbl[rx_hash % rss_indir_tbl_size]; + else + queue = rx_hash % cfg_num_queues; log_verbose(" rxq %d (cpu %d)", queue, rx_irq_cpus[queue]); if (rx_irq_cpus[queue] != cpu) { @@ -517,6 +525,20 @@ static void read_rss_dev_info_ynl(void) memcpy(toeplitz_key, rsp->hkey, rsp->_len.hkey); + if (rsp->_count.indir > RSS_MAX_INDIR) + error(1, 0, "RSS indirection table too large (%u > %u)", + rsp->_count.indir, RSS_MAX_INDIR); + + /* If indir table not available we'll fallback to simple modulo math */ + if (rsp->_count.indir) { + memcpy(rss_indir_tbl, rsp->indir, + rsp->_count.indir * sizeof(rss_indir_tbl[0])); + rss_indir_tbl_size = rsp->_count.indir; + + log_verbose("RSS indirection table size: %u\n", + rss_indir_tbl_size); + } + ethtool_rss_get_rsp_free(rsp); ethtool_rss_get_req_free(req); ynl_sock_destroy(ys); -- cgit v1.2.3 From 5aadc155849eb85d799604939fd9e9024b7392a3 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 20 Nov 2025 20:02:59 -0800 Subject: selftests: hw-net: toeplitz: give the test up to 4 seconds Increase the receiver timeout. When running between machines in different geographic regions the test needs more than a second to SSH across and send the frames. The bkg() command that runs the receiver defaults to 5 sec timeout, so using 4 sec sounds like a reasonable value for the receiver itself. Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/20251121040259.3647749-6-kuba@kernel.org Signed-off-by: Jakub Kicinski --- tools/testing/selftests/drivers/net/hw/toeplitz.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/drivers/net/hw/toeplitz.py b/tools/testing/selftests/drivers/net/hw/toeplitz.py index 945c58d23310..d2db5ee9e358 100755 --- a/tools/testing/selftests/drivers/net/hw/toeplitz.py +++ b/tools/testing/selftests/drivers/net/hw/toeplitz.py @@ -166,7 +166,7 @@ def test(cfg, proto_flag, ipver, grp): proto_flag, "-d", str(port), "-i", cfg.ifname, - "-T", "1000", + "-T", "4000", "-s", "-v" ] -- cgit v1.2.3