Merge tag 'mvebu-dt-4.19-1' of git://git.infradead.org/linux-mvebu into next/dt
[linux-block.git] / tools / testing / selftests / bpf / test_offload.py
CommitLineData
417ec264
JK
1#!/usr/bin/python3
2
3# Copyright (C) 2017 Netronome Systems, Inc.
4#
5# This software is licensed under the GNU General License Version 2,
6# June 1991 as shown in the file COPYING in the top-level directory of this
7# source tree.
8#
9# THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
10# WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
11# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
12# FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
13# OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
14# THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
15
16from datetime import datetime
17import argparse
18import json
19import os
20import pprint
752d7b45
JK
21import random
22import string
7fedbb7c 23import struct
417ec264
JK
24import subprocess
25import time
26
27logfile = None
28log_level = 1
caf95228 29skip_extack = False
417ec264
JK
30bpf_test_dir = os.path.dirname(os.path.realpath(__file__))
31pp = pprint.PrettyPrinter()
32devs = [] # devices we created for clean up
33files = [] # files to be removed
752d7b45 34netns = [] # net namespaces to be removed
417ec264
JK
35
36def log_get_sec(level=0):
37 return "*" * (log_level + level)
38
39def log_level_inc(add=1):
40 global log_level
41 log_level += add
42
43def log_level_dec(sub=1):
44 global log_level
45 log_level -= sub
46
47def log_level_set(level):
48 global log_level
49 log_level = level
50
51def log(header, data, level=None):
52 """
53 Output to an optional log.
54 """
55 if logfile is None:
56 return
57 if level is not None:
58 log_level_set(level)
59
60 if not isinstance(data, str):
61 data = pp.pformat(data)
62
63 if len(header):
64 logfile.write("\n" + log_get_sec() + " ")
65 logfile.write(header)
66 if len(header) and len(data.strip()):
67 logfile.write("\n")
68 logfile.write(data)
69
70def skip(cond, msg):
71 if not cond:
72 return
73 print("SKIP: " + msg)
74 log("SKIP: " + msg, "", level=1)
75 os.sys.exit(0)
76
77def fail(cond, msg):
78 if not cond:
79 return
80 print("FAIL: " + msg)
81 log("FAIL: " + msg, "", level=1)
82 os.sys.exit(1)
83
84def start_test(msg):
85 log(msg, "", level=1)
86 log_level_inc()
87 print(msg)
88
89def cmd(cmd, shell=True, include_stderr=False, background=False, fail=True):
90 """
91 Run a command in subprocess and return tuple of (retval, stdout);
92 optionally return stderr as well as third value.
93 """
94 proc = subprocess.Popen(cmd, shell=shell, stdout=subprocess.PIPE,
95 stderr=subprocess.PIPE)
96 if background:
97 msg = "%s START: %s" % (log_get_sec(1),
98 datetime.now().strftime("%H:%M:%S.%f"))
99 log("BKG " + proc.args, msg)
100 return proc
101
102 return cmd_result(proc, include_stderr=include_stderr, fail=fail)
103
104def cmd_result(proc, include_stderr=False, fail=False):
105 stdout, stderr = proc.communicate()
106 stdout = stdout.decode("utf-8")
107 stderr = stderr.decode("utf-8")
108 proc.stdout.close()
109 proc.stderr.close()
110
111 stderr = "\n" + stderr
112 if stderr[-1] == "\n":
113 stderr = stderr[:-1]
114
115 sec = log_get_sec(1)
116 log("CMD " + proc.args,
117 "RETCODE: %d\n%s STDOUT:\n%s%s STDERR:%s\n%s END: %s" %
118 (proc.returncode, sec, stdout, sec, stderr,
119 sec, datetime.now().strftime("%H:%M:%S.%f")))
120
121 if proc.returncode != 0 and fail:
122 if len(stderr) > 0 and stderr[-1] == "\n":
123 stderr = stderr[:-1]
124 raise Exception("Command failed: %s\n%s" % (proc.args, stderr))
125
126 if include_stderr:
127 return proc.returncode, stdout, stderr
128 else:
129 return proc.returncode, stdout
130
131def rm(f):
132 cmd("rm -f %s" % (f))
133 if f in files:
134 files.remove(f)
135
caf95228 136def tool(name, args, flags, JSON=True, ns="", fail=True, include_stderr=False):
417ec264
JK
137 params = ""
138 if JSON:
139 params += "%s " % (flags["json"])
140
752d7b45
JK
141 if ns != "":
142 ns = "ip netns exec %s " % (ns)
143
caf95228
QM
144 if include_stderr:
145 ret, stdout, stderr = cmd(ns + name + " " + params + args,
146 fail=fail, include_stderr=True)
147 else:
148 ret, stdout = cmd(ns + name + " " + params + args,
149 fail=fail, include_stderr=False)
150
151 if JSON and len(stdout.strip()) != 0:
152 out = json.loads(stdout)
153 else:
154 out = stdout
155
156 if include_stderr:
157 return ret, out, stderr
417ec264
JK
158 else:
159 return ret, out
160
752d7b45
JK
161def bpftool(args, JSON=True, ns="", fail=True):
162 return tool("bpftool", args, {"json":"-p"}, JSON=JSON, ns=ns, fail=fail)
417ec264 163
752d7b45
JK
164def bpftool_prog_list(expected=None, ns=""):
165 _, progs = bpftool("prog show", JSON=True, ns=ns, fail=True)
47cf52a2
JK
166 # Remove the base progs
167 for p in base_progs:
168 if p in progs:
169 progs.remove(p)
417ec264
JK
170 if expected is not None:
171 if len(progs) != expected:
172 fail(True, "%d BPF programs loaded, expected %d" %
173 (len(progs), expected))
174 return progs
175
7fedbb7c
JK
176def bpftool_map_list(expected=None, ns=""):
177 _, maps = bpftool("map show", JSON=True, ns=ns, fail=True)
47cf52a2
JK
178 # Remove the base maps
179 for m in base_maps:
180 if m in maps:
181 maps.remove(m)
7fedbb7c
JK
182 if expected is not None:
183 if len(maps) != expected:
184 fail(True, "%d BPF maps loaded, expected %d" %
185 (len(maps), expected))
186 return maps
187
417ec264
JK
188def bpftool_prog_list_wait(expected=0, n_retry=20):
189 for i in range(n_retry):
190 nprogs = len(bpftool_prog_list())
191 if nprogs == expected:
192 return
193 time.sleep(0.05)
194 raise Exception("Time out waiting for program counts to stabilize want %d, have %d" % (expected, nprogs))
195
7fedbb7c
JK
196def bpftool_map_list_wait(expected=0, n_retry=20):
197 for i in range(n_retry):
198 nmaps = len(bpftool_map_list())
199 if nmaps == expected:
200 return
201 time.sleep(0.05)
202 raise Exception("Time out waiting for map counts to stabilize want %d, have %d" % (expected, nmaps))
203
caf95228 204def ip(args, force=False, JSON=True, ns="", fail=True, include_stderr=False):
417ec264
JK
205 if force:
206 args = "-force " + args
caf95228
QM
207 return tool("ip", args, {"json":"-j"}, JSON=JSON, ns=ns,
208 fail=fail, include_stderr=include_stderr)
417ec264 209
caf95228
QM
210def tc(args, JSON=True, ns="", fail=True, include_stderr=False):
211 return tool("tc", args, {"json":"-p"}, JSON=JSON, ns=ns,
212 fail=fail, include_stderr=include_stderr)
417ec264
JK
213
214def ethtool(dev, opt, args, fail=True):
215 return cmd("ethtool %s %s %s" % (opt, dev["ifname"], args), fail=fail)
216
217def bpf_obj(name, sec=".text", path=bpf_test_dir,):
218 return "obj %s sec %s" % (os.path.join(path, name), sec)
219
220def bpf_pinned(name):
221 return "pinned %s" % (name)
222
223def bpf_bytecode(bytecode):
224 return "bytecode \"%s\"" % (bytecode)
225
752d7b45
JK
226def mknetns(n_retry=10):
227 for i in range(n_retry):
228 name = ''.join([random.choice(string.ascii_letters) for i in range(8)])
229 ret, _ = ip("netns add %s" % (name), fail=False)
230 if ret == 0:
231 netns.append(name)
232 return name
233 return None
234
7fedbb7c
JK
235def int2str(fmt, val):
236 ret = []
237 for b in struct.pack(fmt, val):
238 ret.append(int(b))
239 return " ".join(map(lambda x: str(x), ret))
240
241def str2int(strtab):
242 inttab = []
243 for i in strtab:
244 inttab.append(int(i, 16))
245 ba = bytearray(inttab)
246 if len(strtab) == 4:
247 fmt = "I"
248 elif len(strtab) == 8:
249 fmt = "Q"
250 else:
251 raise Exception("String array of len %d can't be unpacked to an int" %
252 (len(strtab)))
253 return struct.unpack(fmt, ba)[0]
254
417ec264
JK
255class DebugfsDir:
256 """
257 Class for accessing DebugFS directories as a dictionary.
258 """
259
260 def __init__(self, path):
261 self.path = path
262 self._dict = self._debugfs_dir_read(path)
263
264 def __len__(self):
265 return len(self._dict.keys())
266
267 def __getitem__(self, key):
268 if type(key) is int:
269 key = list(self._dict.keys())[key]
270 return self._dict[key]
271
272 def __setitem__(self, key, value):
273 log("DebugFS set %s = %s" % (key, value), "")
274 log_level_inc()
275
276 cmd("echo '%s' > %s/%s" % (value, self.path, key))
277 log_level_dec()
278
279 _, out = cmd('cat %s/%s' % (self.path, key))
280 self._dict[key] = out.strip()
281
282 def _debugfs_dir_read(self, path):
283 dfs = {}
284
285 log("DebugFS state for %s" % (path), "")
286 log_level_inc(add=2)
287
288 _, out = cmd('ls ' + path)
289 for f in out.split():
290 p = os.path.join(path, f)
291 if os.path.isfile(p):
292 _, out = cmd('cat %s/%s' % (path, f))
293 dfs[f] = out.strip()
294 elif os.path.isdir(p):
295 dfs[f] = DebugfsDir(p)
296 else:
297 raise Exception("%s is neither file nor directory" % (p))
298
299 log_level_dec()
300 log("DebugFS state", dfs)
301 log_level_dec()
302
303 return dfs
304
305class NetdevSim:
306 """
307 Class for netdevsim netdevice and its attributes.
308 """
309
310 def __init__(self):
311 self.dev = self._netdevsim_create()
312 devs.append(self)
313
752d7b45
JK
314 self.ns = ""
315
417ec264
JK
316 self.dfs_dir = '/sys/kernel/debug/netdevsim/%s' % (self.dev['ifname'])
317 self.dfs_refresh()
318
319 def __getitem__(self, key):
320 return self.dev[key]
321
322 def _netdevsim_create(self):
323 _, old = ip("link show")
324 ip("link add sim%d type netdevsim")
325 _, new = ip("link show")
326
327 for dev in new:
328 f = filter(lambda x: x["ifname"] == dev["ifname"], old)
329 if len(list(f)) == 0:
330 return dev
331
332 raise Exception("failed to create netdevsim device")
333
334 def remove(self):
335 devs.remove(self)
752d7b45 336 ip("link del dev %s" % (self.dev["ifname"]), ns=self.ns)
417ec264
JK
337
338 def dfs_refresh(self):
339 self.dfs = DebugfsDir(self.dfs_dir)
340 return self.dfs
341
342 def dfs_num_bound_progs(self):
343 path = os.path.join(self.dfs_dir, "bpf_bound_progs")
344 _, progs = cmd('ls %s' % (path))
345 return len(progs.split())
346
347 def dfs_get_bound_progs(self, expected):
348 progs = DebugfsDir(os.path.join(self.dfs_dir, "bpf_bound_progs"))
349 if expected is not None:
350 if len(progs) != expected:
351 fail(True, "%d BPF programs bound, expected %d" %
352 (len(progs), expected))
353 return progs
354
355 def wait_for_flush(self, bound=0, total=0, n_retry=20):
356 for i in range(n_retry):
357 nbound = self.dfs_num_bound_progs()
358 nprogs = len(bpftool_prog_list())
359 if nbound == bound and nprogs == total:
360 return
361 time.sleep(0.05)
362 raise Exception("Time out waiting for program counts to stabilize want %d/%d, have %d bound, %d loaded" % (bound, total, nbound, nprogs))
363
752d7b45
JK
364 def set_ns(self, ns):
365 name = "1" if ns == "" else ns
366 ip("link set dev %s netns %s" % (self.dev["ifname"], name), ns=self.ns)
367 self.ns = ns
368
417ec264
JK
369 def set_mtu(self, mtu, fail=True):
370 return ip("link set dev %s mtu %d" % (self.dev["ifname"], mtu),
371 fail=fail)
372
9045bdc8 373 def set_xdp(self, bpf, mode, force=False, JSON=True, verbose=False,
caf95228 374 fail=True, include_stderr=False):
9045bdc8
QM
375 if verbose:
376 bpf += " verbose"
417ec264 377 return ip("link set dev %s xdp%s %s" % (self.dev["ifname"], mode, bpf),
caf95228
QM
378 force=force, JSON=JSON,
379 fail=fail, include_stderr=include_stderr)
417ec264 380
caf95228
QM
381 def unset_xdp(self, mode, force=False, JSON=True,
382 fail=True, include_stderr=False):
417ec264 383 return ip("link set dev %s xdp%s off" % (self.dev["ifname"], mode),
caf95228
QM
384 force=force, JSON=JSON,
385 fail=fail, include_stderr=include_stderr)
417ec264
JK
386
387 def ip_link_show(self, xdp):
388 _, link = ip("link show dev %s" % (self['ifname']))
389 if len(link) > 1:
390 raise Exception("Multiple objects on ip link show")
391 if len(link) < 1:
392 return {}
393 fail(xdp != "xdp" in link,
394 "XDP program not reporting in iplink (reported %s, expected %s)" %
395 ("xdp" in link, xdp))
396 return link[0]
397
398 def tc_add_ingress(self):
399 tc("qdisc add dev %s ingress" % (self['ifname']))
400
401 def tc_del_ingress(self):
402 tc("qdisc del dev %s ingress" % (self['ifname']))
403
404 def tc_flush_filters(self, bound=0, total=0):
405 self.tc_del_ingress()
406 self.tc_add_ingress()
407 self.wait_for_flush(bound=bound, total=total)
408
409 def tc_show_ingress(self, expected=None):
410 # No JSON support, oh well...
411 flags = ["skip_sw", "skip_hw", "in_hw"]
412 named = ["protocol", "pref", "chain", "handle", "id", "tag"]
413
414 args = "-s filter show dev %s ingress" % (self['ifname'])
415 _, out = tc(args, JSON=False)
416
417 filters = []
418 lines = out.split('\n')
419 for line in lines:
420 words = line.split()
421 if "handle" not in words:
422 continue
423 fltr = {}
424 for flag in flags:
425 fltr[flag] = flag in words
426 for name in named:
427 try:
428 idx = words.index(name)
429 fltr[name] = words[idx + 1]
430 except ValueError:
431 pass
432 filters.append(fltr)
433
434 if expected is not None:
435 fail(len(filters) != expected,
436 "%d ingress filters loaded, expected %d" %
437 (len(filters), expected))
438 return filters
439
6d2d58f1 440 def cls_filter_op(self, op, qdisc="ingress", prio=None, handle=None,
baf6a07e 441 chain=None, cls="", params="",
6d2d58f1
JK
442 fail=True, include_stderr=False):
443 spec = ""
444 if prio is not None:
445 spec += " prio %d" % (prio)
446 if handle:
447 spec += " handle %s" % (handle)
baf6a07e
JK
448 if chain is not None:
449 spec += " chain %d" % (chain)
6d2d58f1
JK
450
451 return tc("filter {op} dev {dev} {qdisc} {spec} {cls} {params}"\
452 .format(op=op, dev=self['ifname'], qdisc=qdisc, spec=spec,
453 cls=cls, params=params),
454 fail=fail, include_stderr=include_stderr)
455
456 def cls_bpf_add_filter(self, bpf, op="add", prio=None, handle=None,
baf6a07e 457 chain=None, da=False, verbose=False,
6d2d58f1
JK
458 skip_sw=False, skip_hw=False,
459 fail=True, include_stderr=False):
460 cls = "bpf " + bpf
461
417ec264
JK
462 params = ""
463 if da:
464 params += " da"
9045bdc8
QM
465 if verbose:
466 params += " verbose"
417ec264
JK
467 if skip_sw:
468 params += " skip_sw"
469 if skip_hw:
470 params += " skip_hw"
6d2d58f1
JK
471
472 return self.cls_filter_op(op=op, prio=prio, handle=handle, cls=cls,
baf6a07e 473 chain=chain, params=params,
6d2d58f1 474 fail=fail, include_stderr=include_stderr)
417ec264
JK
475
476 def set_ethtool_tc_offloads(self, enable, fail=True):
477 args = "hw-tc-offload %s" % ("on" if enable else "off")
478 return ethtool(self, "-K", args, fail=fail)
479
480################################################################################
481def clean_up():
7fedbb7c
JK
482 global files, netns, devs
483
417ec264
JK
484 for dev in devs:
485 dev.remove()
486 for f in files:
487 cmd("rm -f %s" % (f))
752d7b45
JK
488 for ns in netns:
489 cmd("ip netns delete %s" % (ns))
7fedbb7c
JK
490 files = []
491 netns = []
417ec264
JK
492
493def pin_prog(file_name, idx=0):
494 progs = bpftool_prog_list(expected=(idx + 1))
495 prog = progs[idx]
496 bpftool("prog pin id %d %s" % (prog["id"], file_name))
497 files.append(file_name)
498
499 return file_name, bpf_pinned(file_name)
500
7fedbb7c
JK
501def pin_map(file_name, idx=0, expected=1):
502 maps = bpftool_map_list(expected=expected)
503 m = maps[idx]
504 bpftool("map pin id %d %s" % (m["id"], file_name))
505 files.append(file_name)
506
507 return file_name, bpf_pinned(file_name)
508
509def check_dev_info_removed(prog_file=None, map_file=None):
510 bpftool_prog_list(expected=0)
511 ret, err = bpftool("prog show pin %s" % (prog_file), fail=False)
512 fail(ret == 0, "Showing prog with removed device did not fail")
513 fail(err["error"].find("No such device") == -1,
514 "Showing prog with removed device expected ENODEV, error is %s" %
515 (err["error"]))
516
517 bpftool_map_list(expected=0)
518 ret, err = bpftool("map show pin %s" % (map_file), fail=False)
519 fail(ret == 0, "Showing map with removed device did not fail")
520 fail(err["error"].find("No such device") == -1,
521 "Showing map with removed device expected ENODEV, error is %s" %
522 (err["error"]))
523
524def check_dev_info(other_ns, ns, prog_file=None, map_file=None, removed=False):
525 progs = bpftool_prog_list(expected=1, ns=ns)
752d7b45
JK
526 prog = progs[0]
527
528 fail("dev" not in prog.keys(), "Device parameters not reported")
529 dev = prog["dev"]
530 fail("ifindex" not in dev.keys(), "Device parameters not reported")
531 fail("ns_dev" not in dev.keys(), "Device parameters not reported")
532 fail("ns_inode" not in dev.keys(), "Device parameters not reported")
533
7fedbb7c 534 if not other_ns:
752d7b45
JK
535 fail("ifname" not in dev.keys(), "Ifname not reported")
536 fail(dev["ifname"] != sim["ifname"],
537 "Ifname incorrect %s vs %s" % (dev["ifname"], sim["ifname"]))
538 else:
539 fail("ifname" in dev.keys(), "Ifname is reported for other ns")
7fedbb7c
JK
540
541 maps = bpftool_map_list(expected=2, ns=ns)
542 for m in maps:
543 fail("dev" not in m.keys(), "Device parameters not reported")
544 fail(dev != m["dev"], "Map's device different than program's")
752d7b45 545
caf95228
QM
546def check_extack(output, reference, args):
547 if skip_extack:
548 return
549 lines = output.split("\n")
550 comp = len(lines) >= 2 and lines[1] == reference
551 fail(not comp, "Missing or incorrect netlink extack message")
552
553def check_extack_nsim(output, reference, args):
554 check_extack(output, "Error: netdevsim: " + reference, args)
555
2fb89a38
JK
556def check_no_extack(res, needle):
557 fail((res[1] + res[2]).count(needle) or (res[1] + res[2]).count("Warning:"),
558 "Found '%s' in command output, leaky extack?" % (needle))
559
9045bdc8
QM
560def check_verifier_log(output, reference):
561 lines = output.split("\n")
562 for l in reversed(lines):
563 if l == reference:
564 return
565 fail(True, "Missing or incorrect message from netdevsim in verifier log")
566
2fb89a38
JK
567def test_spurios_extack(sim, obj, skip_hw, needle):
568 res = sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=skip_hw,
569 include_stderr=True)
570 check_no_extack(res, needle)
571 res = sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1,
572 skip_hw=skip_hw, include_stderr=True)
573 check_no_extack(res, needle)
574 res = sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf",
575 include_stderr=True)
576 check_no_extack(res, needle)
577
578
417ec264
JK
579# Parse command line
580parser = argparse.ArgumentParser()
581parser.add_argument("--log", help="output verbose log to given file")
582args = parser.parse_args()
583if args.log:
584 logfile = open(args.log, 'w+')
585 logfile.write("# -*-Org-*-")
586
587log("Prepare...", "", level=1)
588log_level_inc()
589
590# Check permissions
591skip(os.getuid() != 0, "test must be run as root")
592
593# Check tools
594ret, progs = bpftool("prog", fail=False)
595skip(ret != 0, "bpftool not installed")
47cf52a2
JK
596base_progs = progs
597_, base_maps = bpftool("map")
417ec264
JK
598
599# Check netdevsim
600ret, out = cmd("modprobe netdevsim", fail=False)
601skip(ret != 0, "netdevsim module could not be loaded")
602
603# Check debugfs
604_, out = cmd("mount")
605if out.find("/sys/kernel/debug type debugfs") == -1:
606 cmd("mount -t debugfs none /sys/kernel/debug")
607
608# Check samples are compiled
7fedbb7c 609samples = ["sample_ret0.o", "sample_map_ret0.o"]
417ec264
JK
610for s in samples:
611 ret, out = cmd("ls %s/%s" % (bpf_test_dir, s), fail=False)
612 skip(ret != 0, "sample %s/%s not found, please compile it" %
613 (bpf_test_dir, s))
614
caf95228
QM
615# Check if iproute2 is built with libmnl (needed by extack support)
616_, _, err = cmd("tc qdisc delete dev lo handle 0",
617 fail=False, include_stderr=True)
618if err.find("Error: Failed to find qdisc with specified handle.") == -1:
619 print("Warning: no extack message in iproute2 output, libmnl missing?")
620 log("Warning: no extack message in iproute2 output, libmnl missing?", "")
621 skip_extack = True
622
752d7b45
JK
623# Check if net namespaces seem to work
624ns = mknetns()
625skip(ns is None, "Could not create a net namespace")
626cmd("ip netns delete %s" % (ns))
627netns = []
628
417ec264
JK
629try:
630 obj = bpf_obj("sample_ret0.o")
631 bytecode = bpf_bytecode("1,6 0 0 4294967295,")
632
633 start_test("Test destruction of generic XDP...")
634 sim = NetdevSim()
635 sim.set_xdp(obj, "generic")
636 sim.remove()
637 bpftool_prog_list_wait(expected=0)
638
639 sim = NetdevSim()
640 sim.tc_add_ingress()
641
642 start_test("Test TC non-offloaded...")
643 ret, _ = sim.cls_bpf_add_filter(obj, skip_hw=True, fail=False)
644 fail(ret != 0, "Software TC filter did not load")
645
646 start_test("Test TC non-offloaded isn't getting bound...")
647 ret, _ = sim.cls_bpf_add_filter(obj, fail=False)
648 fail(ret != 0, "Software TC filter did not load")
649 sim.dfs_get_bound_progs(expected=0)
650
651 sim.tc_flush_filters()
652
653 start_test("Test TC offloads are off by default...")
caf95228
QM
654 ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True,
655 fail=False, include_stderr=True)
417ec264 656 fail(ret == 0, "TC filter loaded without enabling TC offloads")
caf95228 657 check_extack(err, "Error: TC offload is disabled on net device.", args)
417ec264
JK
658 sim.wait_for_flush()
659
660 sim.set_ethtool_tc_offloads(True)
661 sim.dfs["bpf_tc_non_bound_accept"] = "Y"
662
663 start_test("Test TC offload by default...")
664 ret, _ = sim.cls_bpf_add_filter(obj, fail=False)
665 fail(ret != 0, "Software TC filter did not load")
666 sim.dfs_get_bound_progs(expected=0)
667 ingress = sim.tc_show_ingress(expected=1)
668 fltr = ingress[0]
669 fail(not fltr["in_hw"], "Filter not offloaded by default")
670
671 sim.tc_flush_filters()
672
673 start_test("Test TC cBPF bytcode tries offload by default...")
674 ret, _ = sim.cls_bpf_add_filter(bytecode, fail=False)
675 fail(ret != 0, "Software TC filter did not load")
676 sim.dfs_get_bound_progs(expected=0)
677 ingress = sim.tc_show_ingress(expected=1)
678 fltr = ingress[0]
679 fail(not fltr["in_hw"], "Bytecode not offloaded by default")
680
681 sim.tc_flush_filters()
682 sim.dfs["bpf_tc_non_bound_accept"] = "N"
683
684 start_test("Test TC cBPF unbound bytecode doesn't offload...")
caf95228
QM
685 ret, _, err = sim.cls_bpf_add_filter(bytecode, skip_sw=True,
686 fail=False, include_stderr=True)
417ec264 687 fail(ret == 0, "TC bytecode loaded for offload")
caf95228
QM
688 check_extack_nsim(err, "netdevsim configured to reject unbound programs.",
689 args)
417ec264
JK
690 sim.wait_for_flush()
691
baf6a07e
JK
692 start_test("Test non-0 chain offload...")
693 ret, _, err = sim.cls_bpf_add_filter(obj, chain=1, prio=1, handle=1,
694 skip_sw=True,
695 fail=False, include_stderr=True)
696 fail(ret == 0, "Offloaded a filter to chain other than 0")
697 check_extack(err, "Error: Driver supports only offload of chain 0.", args)
698 sim.tc_flush_filters()
699
6d2d58f1
JK
700 start_test("Test TC replace...")
701 sim.cls_bpf_add_filter(obj, prio=1, handle=1)
702 sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1)
703 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
704
705 sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_sw=True)
706 sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, skip_sw=True)
707 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
708
709 sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=True)
710 sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, skip_hw=True)
711 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
712
713 start_test("Test TC replace bad flags...")
714 for i in range(3):
715 for j in range(3):
716 ret, _ = sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1,
717 skip_sw=(j == 1), skip_hw=(j == 2),
718 fail=False)
719 fail(bool(ret) != bool(j),
720 "Software TC incorrect load in replace test, iteration %d" %
721 (j))
722 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
723
2fb89a38
JK
724 start_test("Test spurious extack from the driver...")
725 test_spurios_extack(sim, obj, False, "netdevsim")
726 test_spurios_extack(sim, obj, True, "netdevsim")
727
728 sim.set_ethtool_tc_offloads(False)
729
730 test_spurios_extack(sim, obj, False, "TC offload is disabled")
731 test_spurios_extack(sim, obj, True, "TC offload is disabled")
732
733 sim.set_ethtool_tc_offloads(True)
734
6d2d58f1
JK
735 sim.tc_flush_filters()
736
417ec264 737 start_test("Test TC offloads work...")
9045bdc8
QM
738 ret, _, err = sim.cls_bpf_add_filter(obj, verbose=True, skip_sw=True,
739 fail=False, include_stderr=True)
417ec264 740 fail(ret != 0, "TC filter did not load with TC offloads enabled")
9045bdc8 741 check_verifier_log(err, "[netdevsim] Hello from netdevsim!")
417ec264
JK
742
743 start_test("Test TC offload basics...")
744 dfs = sim.dfs_get_bound_progs(expected=1)
745 progs = bpftool_prog_list(expected=1)
746 ingress = sim.tc_show_ingress(expected=1)
747
748 dprog = dfs[0]
749 prog = progs[0]
750 fltr = ingress[0]
751 fail(fltr["skip_hw"], "TC does reports 'skip_hw' on offloaded filter")
752 fail(not fltr["in_hw"], "TC does not report 'in_hw' for offloaded filter")
753 fail(not fltr["skip_sw"], "TC does not report 'skip_sw' back")
754
755 start_test("Test TC offload is device-bound...")
756 fail(str(prog["id"]) != fltr["id"], "Program IDs don't match")
757 fail(prog["tag"] != fltr["tag"], "Program tags don't match")
758 fail(fltr["id"] != dprog["id"], "Program IDs don't match")
759 fail(dprog["state"] != "xlated", "Offloaded program state not translated")
760 fail(dprog["loaded"] != "Y", "Offloaded program is not loaded")
761
762 start_test("Test disabling TC offloads is rejected while filters installed...")
763 ret, _ = sim.set_ethtool_tc_offloads(False, fail=False)
764 fail(ret == 0, "Driver should refuse to disable TC offloads with filters installed...")
765
766 start_test("Test qdisc removal frees things...")
767 sim.tc_flush_filters()
768 sim.tc_show_ingress(expected=0)
769
770 start_test("Test disabling TC offloads is OK without filters...")
771 ret, _ = sim.set_ethtool_tc_offloads(False, fail=False)
772 fail(ret != 0,
773 "Driver refused to disable TC offloads without filters installed...")
774
775 sim.set_ethtool_tc_offloads(True)
776
777 start_test("Test destroying device gets rid of TC filters...")
778 sim.cls_bpf_add_filter(obj, skip_sw=True)
779 sim.remove()
780 bpftool_prog_list_wait(expected=0)
781
782 sim = NetdevSim()
783 sim.set_ethtool_tc_offloads(True)
784
785 start_test("Test destroying device gets rid of XDP...")
786 sim.set_xdp(obj, "offload")
787 sim.remove()
788 bpftool_prog_list_wait(expected=0)
789
790 sim = NetdevSim()
791 sim.set_ethtool_tc_offloads(True)
792
793 start_test("Test XDP prog reporting...")
794 sim.set_xdp(obj, "drv")
795 ipl = sim.ip_link_show(xdp=True)
796 progs = bpftool_prog_list(expected=1)
797 fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"],
798 "Loaded program has wrong ID")
799
800 start_test("Test XDP prog replace without force...")
801 ret, _ = sim.set_xdp(obj, "drv", fail=False)
802 fail(ret == 0, "Replaced XDP program without -force")
803 sim.wait_for_flush(total=1)
804
805 start_test("Test XDP prog replace with force...")
806 ret, _ = sim.set_xdp(obj, "drv", force=True, fail=False)
807 fail(ret != 0, "Could not replace XDP program with -force")
808 bpftool_prog_list_wait(expected=1)
809 ipl = sim.ip_link_show(xdp=True)
810 progs = bpftool_prog_list(expected=1)
811 fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"],
812 "Loaded program has wrong ID")
752d7b45
JK
813 fail("dev" in progs[0].keys(),
814 "Device parameters reported for non-offloaded program")
417ec264
JK
815
816 start_test("Test XDP prog replace with bad flags...")
caf95228
QM
817 ret, _, err = sim.set_xdp(obj, "offload", force=True,
818 fail=False, include_stderr=True)
417ec264 819 fail(ret == 0, "Replaced XDP program with a program in different mode")
caf95228
QM
820 check_extack_nsim(err, "program loaded with different flags.", args)
821 ret, _, err = sim.set_xdp(obj, "", force=True,
822 fail=False, include_stderr=True)
417ec264 823 fail(ret == 0, "Replaced XDP program with a program in different mode")
caf95228 824 check_extack_nsim(err, "program loaded with different flags.", args)
417ec264
JK
825
826 start_test("Test XDP prog remove with bad flags...")
caf95228
QM
827 ret, _, err = sim.unset_xdp("offload", force=True,
828 fail=False, include_stderr=True)
417ec264 829 fail(ret == 0, "Removed program with a bad mode mode")
caf95228
QM
830 check_extack_nsim(err, "program loaded with different flags.", args)
831 ret, _, err = sim.unset_xdp("", force=True,
832 fail=False, include_stderr=True)
417ec264 833 fail(ret == 0, "Removed program with a bad mode mode")
caf95228 834 check_extack_nsim(err, "program loaded with different flags.", args)
417ec264
JK
835
836 start_test("Test MTU restrictions...")
837 ret, _ = sim.set_mtu(9000, fail=False)
838 fail(ret == 0,
839 "Driver should refuse to increase MTU to 9000 with XDP loaded...")
840 sim.unset_xdp("drv")
841 bpftool_prog_list_wait(expected=0)
842 sim.set_mtu(9000)
caf95228 843 ret, _, err = sim.set_xdp(obj, "drv", fail=False, include_stderr=True)
417ec264 844 fail(ret == 0, "Driver should refuse to load program with MTU of 9000...")
caf95228 845 check_extack_nsim(err, "MTU too large w/ XDP enabled.", args)
417ec264
JK
846 sim.set_mtu(1500)
847
848 sim.wait_for_flush()
849 start_test("Test XDP offload...")
9045bdc8 850 _, _, err = sim.set_xdp(obj, "offload", verbose=True, include_stderr=True)
417ec264
JK
851 ipl = sim.ip_link_show(xdp=True)
852 link_xdp = ipl["xdp"]["prog"]
853 progs = bpftool_prog_list(expected=1)
854 prog = progs[0]
855 fail(link_xdp["id"] != prog["id"], "Loaded program has wrong ID")
9045bdc8 856 check_verifier_log(err, "[netdevsim] Hello from netdevsim!")
417ec264
JK
857
858 start_test("Test XDP offload is device bound...")
859 dfs = sim.dfs_get_bound_progs(expected=1)
860 dprog = dfs[0]
861
862 fail(prog["id"] != link_xdp["id"], "Program IDs don't match")
863 fail(prog["tag"] != link_xdp["tag"], "Program tags don't match")
864 fail(str(link_xdp["id"]) != dprog["id"], "Program IDs don't match")
865 fail(dprog["state"] != "xlated", "Offloaded program state not translated")
866 fail(dprog["loaded"] != "Y", "Offloaded program is not loaded")
867
868 start_test("Test removing XDP program many times...")
869 sim.unset_xdp("offload")
870 sim.unset_xdp("offload")
871 sim.unset_xdp("drv")
872 sim.unset_xdp("drv")
873 sim.unset_xdp("")
874 sim.unset_xdp("")
875 bpftool_prog_list_wait(expected=0)
876
877 start_test("Test attempt to use a program for a wrong device...")
878 sim2 = NetdevSim()
879 sim2.set_xdp(obj, "offload")
880 pin_file, pinned = pin_prog("/sys/fs/bpf/tmp")
881
caf95228
QM
882 ret, _, err = sim.set_xdp(pinned, "offload",
883 fail=False, include_stderr=True)
417ec264 884 fail(ret == 0, "Pinned program loaded for a different device accepted")
caf95228 885 check_extack_nsim(err, "program bound to different dev.", args)
417ec264 886 sim2.remove()
caf95228
QM
887 ret, _, err = sim.set_xdp(pinned, "offload",
888 fail=False, include_stderr=True)
417ec264 889 fail(ret == 0, "Pinned program loaded for a removed device accepted")
caf95228 890 check_extack_nsim(err, "xdpoffload of non-bound program.", args)
417ec264
JK
891 rm(pin_file)
892 bpftool_prog_list_wait(expected=0)
893
894 start_test("Test mixing of TC and XDP...")
895 sim.tc_add_ingress()
896 sim.set_xdp(obj, "offload")
caf95228
QM
897 ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True,
898 fail=False, include_stderr=True)
417ec264 899 fail(ret == 0, "Loading TC when XDP active should fail")
caf95228 900 check_extack_nsim(err, "driver and netdev offload states mismatch.", args)
417ec264
JK
901 sim.unset_xdp("offload")
902 sim.wait_for_flush()
903
904 sim.cls_bpf_add_filter(obj, skip_sw=True)
caf95228 905 ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True)
417ec264 906 fail(ret == 0, "Loading XDP when TC active should fail")
caf95228 907 check_extack_nsim(err, "TC program is already loaded.", args)
417ec264
JK
908
909 start_test("Test binding TC from pinned...")
910 pin_file, pinned = pin_prog("/sys/fs/bpf/tmp")
911 sim.tc_flush_filters(bound=1, total=1)
912 sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True)
913 sim.tc_flush_filters(bound=1, total=1)
914
915 start_test("Test binding XDP from pinned...")
916 sim.set_xdp(obj, "offload")
917 pin_file, pinned = pin_prog("/sys/fs/bpf/tmp2", idx=1)
918
919 sim.set_xdp(pinned, "offload", force=True)
920 sim.unset_xdp("offload")
921 sim.set_xdp(pinned, "offload", force=True)
922 sim.unset_xdp("offload")
923
924 start_test("Test offload of wrong type fails...")
925 ret, _ = sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True, fail=False)
926 fail(ret == 0, "Managed to attach XDP program to TC")
927
928 start_test("Test asking for TC offload of two filters...")
929 sim.cls_bpf_add_filter(obj, da=True, skip_sw=True)
caf95228
QM
930 ret, _, err = sim.cls_bpf_add_filter(obj, da=True, skip_sw=True,
931 fail=False, include_stderr=True)
fba961ab 932 fail(ret == 0, "Managed to offload two TC filters at the same time")
caf95228 933 check_extack_nsim(err, "driver and netdev offload states mismatch.", args)
417ec264
JK
934
935 sim.tc_flush_filters(bound=2, total=2)
936
937 start_test("Test if netdev removal waits for translation...")
938 delay_msec = 500
939 sim.dfs["bpf_bind_verifier_delay"] = delay_msec
940 start = time.time()
941 cmd_line = "tc filter add dev %s ingress bpf %s da skip_sw" % \
942 (sim['ifname'], obj)
943 tc_proc = cmd(cmd_line, background=True, fail=False)
944 # Wait for the verifier to start
945 while sim.dfs_num_bound_progs() <= 2:
946 pass
947 sim.remove()
948 end = time.time()
949 ret, _ = cmd_result(tc_proc, fail=False)
950 time_diff = end - start
951 log("Time", "start:\t%s\nend:\t%s\ndiff:\t%s" % (start, end, time_diff))
952
953 fail(ret == 0, "Managed to load TC filter on a unregistering device")
954 delay_sec = delay_msec * 0.001
955 fail(time_diff < delay_sec, "Removal process took %s, expected %s" %
956 (time_diff, delay_sec))
957
752d7b45
JK
958 # Remove all pinned files and reinstantiate the netdev
959 clean_up()
960 bpftool_prog_list_wait(expected=0)
961
962 sim = NetdevSim()
7fedbb7c
JK
963 map_obj = bpf_obj("sample_map_ret0.o")
964 start_test("Test loading program with maps...")
965 sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
752d7b45
JK
966
967 start_test("Test bpftool bound info reporting (own ns)...")
968 check_dev_info(False, "")
969
970 start_test("Test bpftool bound info reporting (other ns)...")
971 ns = mknetns()
972 sim.set_ns(ns)
973 check_dev_info(True, "")
974
975 start_test("Test bpftool bound info reporting (remote ns)...")
976 check_dev_info(False, ns)
977
978 start_test("Test bpftool bound info reporting (back to own ns)...")
979 sim.set_ns("")
980 check_dev_info(False, "")
981
7fedbb7c
JK
982 prog_file, _ = pin_prog("/sys/fs/bpf/tmp_prog")
983 map_file, _ = pin_map("/sys/fs/bpf/tmp_map", idx=1, expected=2)
752d7b45
JK
984 sim.remove()
985
986 start_test("Test bpftool bound info reporting (removed dev)...")
7fedbb7c
JK
987 check_dev_info_removed(prog_file=prog_file, map_file=map_file)
988
989 # Remove all pinned files and reinstantiate the netdev
990 clean_up()
991 bpftool_prog_list_wait(expected=0)
992
993 sim = NetdevSim()
994
995 start_test("Test map update (no flags)...")
996 sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
997 maps = bpftool_map_list(expected=2)
998 array = maps[0] if maps[0]["type"] == "array" else maps[1]
999 htab = maps[0] if maps[0]["type"] == "hash" else maps[1]
1000 for m in maps:
1001 for i in range(2):
1002 bpftool("map update id %d key %s value %s" %
1003 (m["id"], int2str("I", i), int2str("Q", i * 3)))
1004
1005 for m in maps:
1006 ret, _ = bpftool("map update id %d key %s value %s" %
1007 (m["id"], int2str("I", 3), int2str("Q", 3 * 3)),
1008 fail=False)
1009 fail(ret == 0, "added too many entries")
1010
1011 start_test("Test map update (exists)...")
1012 for m in maps:
1013 for i in range(2):
1014 bpftool("map update id %d key %s value %s exist" %
1015 (m["id"], int2str("I", i), int2str("Q", i * 3)))
1016
1017 for m in maps:
1018 ret, err = bpftool("map update id %d key %s value %s exist" %
1019 (m["id"], int2str("I", 3), int2str("Q", 3 * 3)),
1020 fail=False)
1021 fail(ret == 0, "updated non-existing key")
1022 fail(err["error"].find("No such file or directory") == -1,
1023 "expected ENOENT, error is '%s'" % (err["error"]))
1024
1025 start_test("Test map update (noexist)...")
1026 for m in maps:
1027 for i in range(2):
1028 ret, err = bpftool("map update id %d key %s value %s noexist" %
1029 (m["id"], int2str("I", i), int2str("Q", i * 3)),
1030 fail=False)
1031 fail(ret == 0, "updated existing key")
1032 fail(err["error"].find("File exists") == -1,
1033 "expected EEXIST, error is '%s'" % (err["error"]))
1034
1035 start_test("Test map dump...")
1036 for m in maps:
1037 _, entries = bpftool("map dump id %d" % (m["id"]))
1038 for i in range(2):
1039 key = str2int(entries[i]["key"])
1040 fail(key != i, "expected key %d, got %d" % (key, i))
1041 val = str2int(entries[i]["value"])
1042 fail(val != i * 3, "expected value %d, got %d" % (val, i * 3))
1043
1044 start_test("Test map getnext...")
1045 for m in maps:
1046 _, entry = bpftool("map getnext id %d" % (m["id"]))
1047 key = str2int(entry["next_key"])
1048 fail(key != 0, "next key %d, expected %d" % (key, 0))
1049 _, entry = bpftool("map getnext id %d key %s" %
1050 (m["id"], int2str("I", 0)))
1051 key = str2int(entry["next_key"])
1052 fail(key != 1, "next key %d, expected %d" % (key, 1))
1053 ret, err = bpftool("map getnext id %d key %s" %
1054 (m["id"], int2str("I", 1)), fail=False)
1055 fail(ret == 0, "got next key past the end of map")
1056 fail(err["error"].find("No such file or directory") == -1,
1057 "expected ENOENT, error is '%s'" % (err["error"]))
1058
1059 start_test("Test map delete (htab)...")
1060 for i in range(2):
1061 bpftool("map delete id %d key %s" % (htab["id"], int2str("I", i)))
1062
1063 start_test("Test map delete (array)...")
1064 for i in range(2):
1065 ret, err = bpftool("map delete id %d key %s" %
1066 (htab["id"], int2str("I", i)), fail=False)
1067 fail(ret == 0, "removed entry from an array")
1068 fail(err["error"].find("No such file or directory") == -1,
1069 "expected ENOENT, error is '%s'" % (err["error"]))
1070
1071 start_test("Test map remove...")
1072 sim.unset_xdp("offload")
1073 bpftool_map_list_wait(expected=0)
1074 sim.remove()
1075
1076 sim = NetdevSim()
1077 sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
1078 sim.remove()
1079 bpftool_map_list_wait(expected=0)
1080
1081 start_test("Test map creation fail path...")
1082 sim = NetdevSim()
1083 sim.dfs["bpf_map_accept"] = "N"
1084 ret, _ = sim.set_xdp(map_obj, "offload", JSON=False, fail=False)
1085 fail(ret == 0,
1086 "netdevsim didn't refuse to create a map with offload disabled")
752d7b45 1087
417ec264
JK
1088 print("%s: OK" % (os.path.basename(__file__)))
1089
1090finally:
1091 log("Clean up...", "", level=1)
1092 log_level_inc()
1093 clean_up()