Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1#!/usr/bin/env python3
2# SPDX-License-Identifier: GPL-2.0
3
4"""
5Tests for RSS hashing on IPv6 Flow Label.
6"""
7
8import glob
9import os
10import socket
11from lib.py import CmdExitFailure
12from lib.py import ksft_run, ksft_exit, ksft_eq, ksft_ge, ksft_in, \
13 ksft_not_in, ksft_raises, KsftSkipEx
14from lib.py import bkg, cmd, defer, fd_read_timeout, rand_port
15from lib.py import NetDrvEpEnv
16
17
18def _check_system(cfg):
19 if not hasattr(socket, "SO_INCOMING_CPU"):
20 raise KsftSkipEx("socket.SO_INCOMING_CPU was added in Python 3.11")
21
22 qcnt = len(glob.glob(f"/sys/class/net/{cfg.ifname}/queues/rx-*"))
23 if qcnt < 2:
24 raise KsftSkipEx(f"Local has only {qcnt} queues")
25
26 for f in [f"/sys/class/net/{cfg.ifname}/queues/rx-0/rps_flow_cnt",
27 f"/sys/class/net/{cfg.ifname}/queues/rx-0/rps_cpus"]:
28 try:
29 with open(f, 'r') as fp:
30 setting = fp.read().strip()
31 # CPU mask will be zeros and commas
32 if setting.replace("0", "").replace(",", ""):
33 raise KsftSkipEx(f"RPS/RFS is configured: {f}: {setting}")
34 except FileNotFoundError:
35 pass
36
37 # 1 is the default, if someone changed it we probably shouldn"t mess with it
38 af = cmd("cat /proc/sys/net/ipv6/auto_flowlabels", host=cfg.remote).stdout
39 if af.strip() != "1":
40 raise KsftSkipEx("Remote does not have auto_flowlabels enabled")
41
42
43def _ethtool_get_cfg(cfg, fl_type):
44 descr = cmd(f"ethtool -n {cfg.ifname} rx-flow-hash {fl_type}").stdout
45
46 converter = {
47 "IP SA": "s",
48 "IP DA": "d",
49 "L3 proto": "t",
50 "L4 bytes 0 & 1 [TCP/UDP src port]": "f",
51 "L4 bytes 2 & 3 [TCP/UDP dst port]": "n",
52 "IPv6 Flow Label": "l",
53 }
54
55 ret = ""
56 for line in descr.split("\n")[1:-2]:
57 # if this raises we probably need to add more keys to converter above
58 ret += converter[line]
59 return ret
60
61
62def _traffic(cfg, one_sock, one_cpu):
63 local_port = rand_port(socket.SOCK_DGRAM)
64 remote_port = rand_port(socket.SOCK_DGRAM)
65
66 sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
67 sock.bind(("", local_port))
68 sock.connect((cfg.remote_addr_v["6"], 0))
69 if one_sock:
70 send = f"exec 5<>/dev/udp/{cfg.addr_v['6']}/{local_port}; " \
71 "for i in `seq 20`; do echo a >&5; sleep 0.02; done; exec 5>&-"
72 else:
73 send = "for i in `seq 20`; do echo a | socat -t0.02 - UDP6:" \
74 f"[{cfg.addr_v['6']}]:{local_port},sourceport={remote_port}; done"
75
76 cpus = set()
77 with bkg(send, shell=True, host=cfg.remote, exit_wait=True):
78 for _ in range(20):
79 fd_read_timeout(sock.fileno(), 1)
80 cpu = sock.getsockopt(socket.SOL_SOCKET, socket.SO_INCOMING_CPU)
81 cpus.add(cpu)
82
83 if one_cpu:
84 ksft_eq(len(cpus), 1,
85 f"{one_sock=} - expected one CPU, got traffic on: {cpus=}")
86 else:
87 ksft_ge(len(cpus), 2,
88 f"{one_sock=} - expected many CPUs, got traffic on: {cpus=}")
89
90
91def test_rss_flow_label(cfg):
92 """
93 Test hashing on IPv6 flow label. Send traffic over a single socket
94 and over multiple sockets. Depend on the remote having auto-label
95 enabled so that it randomizes the label per socket.
96 """
97
98 cfg.require_ipver("6")
99 cfg.require_cmd("socat", remote=True)
100 _check_system(cfg)
101
102 # Enable flow label hashing for UDP6
103 initial = _ethtool_get_cfg(cfg, "udp6")
104 no_lbl = initial.replace("l", "")
105 if "l" not in initial:
106 try:
107 cmd(f"ethtool -N {cfg.ifname} rx-flow-hash udp6 l{no_lbl}")
108 except CmdExitFailure as exc:
109 raise KsftSkipEx("Device doesn't support Flow Label for UDP6") from exc
110
111 defer(cmd, f"ethtool -N {cfg.ifname} rx-flow-hash udp6 {initial}")
112
113 _traffic(cfg, one_sock=True, one_cpu=True)
114 _traffic(cfg, one_sock=False, one_cpu=False)
115
116 # Disable it, we should see no hashing (reset was already defer()ed)
117 cmd(f"ethtool -N {cfg.ifname} rx-flow-hash udp6 {no_lbl}")
118
119 _traffic(cfg, one_sock=False, one_cpu=True)
120
121
122def _check_v4_flow_types(cfg):
123 for fl_type in ["tcp4", "udp4", "ah4", "esp4", "sctp4"]:
124 try:
125 cur = cmd(f"ethtool -n {cfg.ifname} rx-flow-hash {fl_type}").stdout
126 ksft_not_in("Flow Label", cur,
127 comment=f"{fl_type=} has Flow Label:" + cur)
128 except CmdExitFailure:
129 # Probably does not support this flow type
130 pass
131
132
133def test_rss_flow_label_6only(cfg):
134 """
135 Test interactions with IPv4 flow types. It should not be possible to set
136 IPv6 Flow Label hashing for an IPv4 flow type. The Flow Label should also
137 not appear in the IPv4 "current config".
138 """
139
140 with ksft_raises(CmdExitFailure) as cm:
141 cmd(f"ethtool -N {cfg.ifname} rx-flow-hash tcp4 sdfnl")
142 ksft_in("Invalid argument", cm.exception.cmd.stderr)
143
144 _check_v4_flow_types(cfg)
145
146 # Try to enable Flow Labels and check again, in case it leaks thru
147 initial = _ethtool_get_cfg(cfg, "udp6")
148 no_lbl = initial.replace("l", "")
149 if "l" not in initial:
150 try:
151 cmd(f"ethtool -N {cfg.ifname} rx-flow-hash udp6 l{no_lbl}")
152 except CmdExitFailure as exc:
153 raise KsftSkipEx("Device doesn't support Flow Label for UDP6") from exc
154 else:
155 cmd(f"ethtool -N {cfg.ifname} rx-flow-hash udp6 {no_lbl}")
156 restore = defer(cmd, f"ethtool -N {cfg.ifname} rx-flow-hash udp6 {initial}")
157
158 _check_v4_flow_types(cfg)
159 restore.exec()
160 _check_v4_flow_types(cfg)
161
162
163def main() -> None:
164 with NetDrvEpEnv(__file__, nsim_test=False) as cfg:
165 ksft_run([test_rss_flow_label,
166 test_rss_flow_label_6only],
167 args=(cfg, ))
168 ksft_exit()
169
170
171if __name__ == "__main__":
172 main()