Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/testing/selftests/drivers/net/gro.py
122953 views
1
#!/usr/bin/env python3
2
# SPDX-License-Identifier: GPL-2.0
3
4
"""
5
GRO (Generic Receive Offload) conformance tests.
6
7
Validates that GRO coalescing works correctly by running the gro
8
binary in different configurations and checking for correct packet
9
coalescing behavior.
10
11
Test cases:
12
- data_same: Same size data packets coalesce
13
- data_lrg_sml: Large packet followed by smaller one coalesces
14
- data_sml_lrg: Small packet followed by larger one doesn't coalesce
15
- ack: Pure ACK packets do not coalesce
16
- flags_psh: Packets with PSH flag don't coalesce
17
- flags_syn: Packets with SYN flag don't coalesce
18
- flags_rst: Packets with RST flag don't coalesce
19
- flags_urg: Packets with URG flag don't coalesce
20
- flags_cwr: Packets with CWR flag don't coalesce
21
- tcp_csum: Packets with incorrect checksum don't coalesce
22
- tcp_seq: Packets with non-consecutive seqno don't coalesce
23
- tcp_ts: Packets with different timestamp options don't coalesce
24
- tcp_opt: Packets with different TCP options don't coalesce
25
- ip_ecn: Packets with different ECN don't coalesce
26
- ip_tos: Packets with different TOS don't coalesce
27
- ip_ttl: (IPv4) Packets with different TTL don't coalesce
28
- ip_opt: (IPv4) Packets with IP options don't coalesce
29
- ip_frag4: (IPv4) IPv4 fragments don't coalesce
30
- ip_id_df*: (IPv4) IP ID field coalescing tests
31
- ip_frag6: (IPv6) IPv6 fragments don't coalesce
32
- ip_v6ext_same: (IPv6) IPv6 ext header with same payload coalesces
33
- ip_v6ext_diff: (IPv6) IPv6 ext header with different payload doesn't coalesce
34
- large_max: Packets exceeding GRO_MAX_SIZE don't coalesce
35
- large_rem: Large packet remainder handling
36
"""
37
38
import os
39
from lib.py import ksft_run, ksft_exit, ksft_pr
40
from lib.py import NetDrvEpEnv, KsftXfailEx
41
from lib.py import bkg, cmd, defer, ethtool, ip
42
from lib.py import ksft_variants
43
44
45
def _resolve_dmac(cfg, ipver):
46
"""
47
Find the destination MAC address remote host should use to send packets
48
towards the local host. It may be a router / gateway address.
49
"""
50
51
attr = "dmac" + ipver
52
# Cache the response across test cases
53
if hasattr(cfg, attr):
54
return getattr(cfg, attr)
55
56
route = ip(f"-{ipver} route get {cfg.addr_v[ipver]}",
57
json=True, host=cfg.remote)[0]
58
gw = route.get("gateway")
59
# Local L2 segment, address directly
60
if not gw:
61
setattr(cfg, attr, cfg.dev['address'])
62
return getattr(cfg, attr)
63
64
# ping to make sure neighbor is resolved,
65
# bind to an interface, for v6 the GW is likely link local
66
cmd(f"ping -c1 -W0 -I{cfg.remote_ifname} {gw}", host=cfg.remote)
67
68
neigh = ip(f"neigh get {gw} dev {cfg.remote_ifname}",
69
json=True, host=cfg.remote)[0]
70
setattr(cfg, attr, neigh['lladdr'])
71
return getattr(cfg, attr)
72
73
74
def _write_defer_restore(cfg, path, val, defer_undo=False):
75
with open(path, "r", encoding="utf-8") as fp:
76
orig_val = fp.read().strip()
77
if str(val) == orig_val:
78
return
79
with open(path, "w", encoding="utf-8") as fp:
80
fp.write(val)
81
if defer_undo:
82
defer(_write_defer_restore, cfg, path, orig_val)
83
84
85
def _set_mtu_restore(dev, mtu, host):
86
if dev['mtu'] < mtu:
87
ip(f"link set dev {dev['ifname']} mtu {mtu}", host=host)
88
defer(ip, f"link set dev {dev['ifname']} mtu {dev['mtu']}", host=host)
89
90
91
def _set_ethtool_feat(dev, current, feats, host=None):
92
s2n = {True: "on", False: "off"}
93
94
new = ["-K", dev]
95
old = ["-K", dev]
96
no_change = True
97
for name, state in feats.items():
98
new += [name, s2n[state]]
99
old += [name, s2n[current[name]["active"]]]
100
101
if current[name]["active"] != state:
102
no_change = False
103
if current[name]["fixed"]:
104
raise KsftXfailEx(f"Device does not support {name}")
105
if no_change:
106
return
107
108
eth_cmd = ethtool(" ".join(new), host=host)
109
defer(ethtool, " ".join(old), host=host)
110
111
# If ethtool printed something kernel must have modified some features
112
if eth_cmd.stdout:
113
ksft_pr(eth_cmd)
114
115
116
def _setup(cfg, mode, test_name):
117
""" Setup hardware loopback mode for GRO testing. """
118
119
if not hasattr(cfg, "bin_remote"):
120
cfg.bin_local = cfg.test_dir / "gro"
121
cfg.bin_remote = cfg.remote.deploy(cfg.bin_local)
122
123
if not hasattr(cfg, "feat"):
124
cfg.feat = ethtool(f"-k {cfg.ifname}", json=True)[0]
125
cfg.remote_feat = ethtool(f"-k {cfg.remote_ifname}",
126
host=cfg.remote, json=True)[0]
127
128
# "large_*" tests need at least 4k MTU
129
if test_name.startswith("large_"):
130
_set_mtu_restore(cfg.dev, 4096, None)
131
_set_mtu_restore(cfg.remote_dev, 4096, cfg.remote)
132
133
if mode == "sw":
134
flush_path = f"/sys/class/net/{cfg.ifname}/gro_flush_timeout"
135
irq_path = f"/sys/class/net/{cfg.ifname}/napi_defer_hard_irqs"
136
137
_write_defer_restore(cfg, flush_path, "200000", defer_undo=True)
138
_write_defer_restore(cfg, irq_path, "10", defer_undo=True)
139
140
_set_ethtool_feat(cfg.ifname, cfg.feat,
141
{"generic-receive-offload": True,
142
"rx-gro-hw": False,
143
"large-receive-offload": False})
144
elif mode == "hw":
145
_set_ethtool_feat(cfg.ifname, cfg.feat,
146
{"generic-receive-offload": False,
147
"rx-gro-hw": True,
148
"large-receive-offload": False})
149
150
# Some NICs treat HW GRO as a GRO sub-feature so disabling GRO
151
# will also clear HW GRO. Use a hack of installing XDP generic
152
# to skip SW GRO, even when enabled.
153
feat = ethtool(f"-k {cfg.ifname}", json=True)[0]
154
if not feat["rx-gro-hw"]["active"]:
155
ksft_pr("Driver clears HW GRO and SW GRO is cleared, using generic XDP workaround")
156
prog = cfg.net_lib_dir / "xdp_dummy.bpf.o"
157
ip(f"link set dev {cfg.ifname} xdpgeneric obj {prog} sec xdp")
158
defer(ip, f"link set dev {cfg.ifname} xdpgeneric off")
159
160
# Attaching XDP may change features, fetch the latest state
161
feat = ethtool(f"-k {cfg.ifname}", json=True)[0]
162
163
_set_ethtool_feat(cfg.ifname, feat,
164
{"generic-receive-offload": True,
165
"rx-gro-hw": True,
166
"large-receive-offload": False})
167
elif mode == "lro":
168
# netdevsim advertises LRO for feature inheritance testing with
169
# bonding/team tests but it doesn't actually perform the offload
170
cfg.require_nsim(nsim_test=False)
171
172
_set_ethtool_feat(cfg.ifname, cfg.feat,
173
{"generic-receive-offload": False,
174
"rx-gro-hw": False,
175
"large-receive-offload": True})
176
177
try:
178
# Disable TSO for local tests
179
cfg.require_nsim() # will raise KsftXfailEx if not running on nsim
180
181
_set_ethtool_feat(cfg.remote_ifname, cfg.remote_feat,
182
{"tcp-segmentation-offload": False},
183
host=cfg.remote)
184
except KsftXfailEx:
185
pass
186
187
188
def _gro_variants():
189
"""Generator that yields all combinations of protocol and test types."""
190
191
# Tests that work for all protocols
192
common_tests = [
193
"data_same", "data_lrg_sml", "data_sml_lrg",
194
"ack",
195
"flags_psh", "flags_syn", "flags_rst", "flags_urg", "flags_cwr",
196
"tcp_csum", "tcp_seq", "tcp_ts", "tcp_opt",
197
"ip_ecn", "ip_tos",
198
"large_max", "large_rem",
199
]
200
201
# Tests specific to IPv4
202
ipv4_tests = [
203
"ip_ttl", "ip_opt", "ip_frag4",
204
"ip_id_df1_inc", "ip_id_df1_fixed",
205
"ip_id_df0_inc", "ip_id_df0_fixed",
206
"ip_id_df1_inc_fixed", "ip_id_df1_fixed_inc",
207
]
208
209
# Tests specific to IPv6
210
ipv6_tests = [
211
"ip_frag6", "ip_v6ext_same", "ip_v6ext_diff",
212
]
213
214
for mode in ["sw", "hw", "lro"]:
215
for protocol in ["ipv4", "ipv6", "ipip"]:
216
for test_name in common_tests:
217
yield mode, protocol, test_name
218
219
if protocol in ["ipv4", "ipip"]:
220
for test_name in ipv4_tests:
221
yield mode, protocol, test_name
222
elif protocol == "ipv6":
223
for test_name in ipv6_tests:
224
yield mode, protocol, test_name
225
226
227
@ksft_variants(_gro_variants())
228
def test(cfg, mode, protocol, test_name):
229
"""Run a single GRO test with retries."""
230
231
ipver = "6" if protocol[-1] == "6" else "4"
232
cfg.require_ipver(ipver)
233
234
_setup(cfg, mode, test_name)
235
236
base_cmd_args = [
237
f"--{protocol}",
238
f"--dmac {_resolve_dmac(cfg, ipver)}",
239
f"--smac {cfg.remote_dev['address']}",
240
f"--daddr {cfg.addr_v[ipver]}",
241
f"--saddr {cfg.remote_addr_v[ipver]}",
242
f"--test {test_name}",
243
"--verbose"
244
]
245
base_args = " ".join(base_cmd_args)
246
247
# Each test is run 6 times to deflake, because given the receive timing,
248
# not all packets that should coalesce will be considered in the same flow
249
# on every try.
250
max_retries = 6
251
for attempt in range(max_retries):
252
rx_cmd = f"{cfg.bin_local} {base_args} --rx --iface {cfg.ifname}"
253
tx_cmd = f"{cfg.bin_remote} {base_args} --iface {cfg.remote_ifname}"
254
255
fail_now = attempt >= max_retries - 1
256
257
with bkg(rx_cmd, ksft_ready=True, exit_wait=True,
258
fail=fail_now) as rx_proc:
259
cmd(tx_cmd, host=cfg.remote)
260
261
if rx_proc.ret == 0:
262
return
263
264
ksft_pr(rx_proc)
265
266
if test_name.startswith("large_") and os.environ.get("KSFT_MACHINE_SLOW"):
267
ksft_pr(f"Ignoring {protocol}/{test_name} failure due to slow environment")
268
return
269
270
ksft_pr(f"Attempt {attempt + 1}/{max_retries} failed, retrying...")
271
272
273
def main() -> None:
274
""" Ksft boiler plate main """
275
276
with NetDrvEpEnv(__file__) as cfg:
277
ksft_run(cases=[test], args=(cfg,))
278
ksft_exit()
279
280
281
if __name__ == "__main__":
282
main()
283
284