Add a new rss_multiqueue Python test variant that exercises multi-queue zero-copy receive on a single listening socket, where the server dispatches accepted connections to worker threads by SO_INCOMING_NAPI_ID. The setup creates an RSS context spanning N receive queues and a single flow rule that uses that context, then queries the NAPI ID for each queue at runtime via netlink queue_get(). The NAPI IDs are passed to the C binary via a new -n option so it can map each accepted connection to the worker handling that NAPI's queue. The client spawns more connections than worker threads to exercise multiple connections per worker. Signed-off-by: Juanlu Herrero --- .../selftests/drivers/net/hw/iou-zcrx.py | 59 ++++++++++++++++++- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/drivers/net/hw/iou-zcrx.py b/tools/testing/selftests/drivers/net/hw/iou-zcrx.py index e81724cb5542a..896376b26e01a 100755 --- a/tools/testing/selftests/drivers/net/hw/iou-zcrx.py +++ b/tools/testing/selftests/drivers/net/hw/iou-zcrx.py @@ -29,6 +29,12 @@ def create_rss_ctx(cfg): return int(values) +def create_rss_ctx_multi(cfg, start, count): + output = ethtool(f"-X {cfg.ifname} context new start {start} equal {count}").stdout + values = re.search(r'New RSS context is (\d+)', output).group(1) + return int(values) + + def set_flow_rule(cfg): output = ethtool(f"-N {cfg.ifname} flow-type tcp6 dst-port {cfg.port} action {cfg.target}").stdout values = re.search(r'ID (\d+)', output).group(1) @@ -100,16 +106,65 @@ def rss(cfg): defer(ethtool, f"-N {cfg.ifname} delete {flow_rule_id}") +def rss_multiqueue(cfg): + channels = cfg.ethnl.channels_get({'header': {'dev-index': cfg.ifindex}}) + channels = channels['combined-count'] + if channels < 3: + raise KsftSkipEx('Test requires NETIF with at least 3 combined channels') + + rings = cfg.ethnl.rings_get({'header': {'dev-index': cfg.ifindex}}) + rx_rings = rings['rx'] + hds_thresh = rings.get('hds-thresh', 0) + + cfg.ethnl.rings_set({'header': {'dev-index': cfg.ifindex}, + 'tcp-data-split': 'enabled', + 'hds-thresh': 0, + 'rx': 64}) + defer(cfg.ethnl.rings_set, {'header': {'dev-index': cfg.ifindex}, + 'tcp-data-split': 'unknown', + 'hds-thresh': hds_thresh, + 'rx': rx_rings}) + defer(mp_clear_wait, cfg) + + cfg.num_threads = 2 + cfg.target = channels - cfg.num_threads + ethtool(f"-X {cfg.ifname} equal {cfg.target}") + defer(ethtool, f"-X {cfg.ifname} default") + + rss_ctx_id = create_rss_ctx_multi(cfg, cfg.target, cfg.num_threads) + defer(ethtool, f"-X {cfg.ifname} delete context {rss_ctx_id}") + + flow_rule_id = set_flow_rule_rss(cfg, rss_ctx_id) + defer(ethtool, f"-N {cfg.ifname} delete {flow_rule_id}") + + napi_ids = [] + for i in range(cfg.num_threads): + queue = cfg.netnl.queue_get({'ifindex': cfg.ifindex, + 'id': cfg.target + i, + 'type': 'rx'}) + napi_ids.append(str(queue['napi-id'])) + cfg.napi_ids = ','.join(napi_ids) + + @ksft_variants([ KsftNamedVariant("single", single), KsftNamedVariant("rss", rss), + KsftNamedVariant("rss_multiqueue", rss_multiqueue), ]) def test_zcrx(cfg, setup) -> None: cfg.require_ipver('6') + cfg.num_threads = 1 + cfg.napi_ids = None + setup(cfg) - rx_cmd = f"{cfg.bin_local} -s -p {cfg.port} -i {cfg.ifname} -q {cfg.target}" - tx_cmd = f"{cfg.bin_remote} -c -h {cfg.addr_v['6']} -p {cfg.port} -l 12840" + + rx_cmd = (f"{cfg.bin_local} -s -p {cfg.port} -i {cfg.ifname} " + f"-q {cfg.target} -t {cfg.num_threads}") + if cfg.napi_ids: + rx_cmd += f" -n {cfg.napi_ids}" + tx_cmd = (f"{cfg.bin_remote} -c -h {cfg.addr_v['6']} -p {cfg.port} " + f"-l 12840 -t {cfg.num_threads}") with bkg(rx_cmd, exit_wait=True): wait_port_listen(cfg.port, proto="tcp") cmd(tx_cmd, host=cfg.remote) -- 2.52.0