accel/qaic: Add AIC200 support
[drm/drm-misc.git] / tools / testing / selftests / net / bpf_offload.py
blobd10f420e4ef65e5849d412804b90ff391cc9fc69
1 #!/usr/bin/env python3
3 # Copyright (C) 2017 Netronome Systems, Inc.
4 # Copyright (c) 2019 Mellanox Technologies. All rights reserved
6 # This software is licensed under the GNU General License Version 2,
7 # June 1991 as shown in the file COPYING in the top-level directory of this
8 # source tree.
10 # THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
11 # WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
12 # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
13 # FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
14 # OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
15 # THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
17 from datetime import datetime
18 import argparse
19 import errno
20 import json
21 import os
22 import pprint
23 import random
24 import re
25 import stat
26 import string
27 import struct
28 import subprocess
29 import time
30 import traceback
32 from lib.py import NetdevSim, NetdevSimDev
35 logfile = None
36 log_level = 1
37 skip_extack = False
38 bpf_test_dir = os.path.dirname(os.path.realpath(__file__))
39 pp = pprint.PrettyPrinter()
40 devs = [] # devices we created for clean up
41 files = [] # files to be removed
42 netns = [] # net namespaces to be removed
44 def log_get_sec(level=0):
45 return "*" * (log_level + level)
47 def log_level_inc(add=1):
48 global log_level
49 log_level += add
51 def log_level_dec(sub=1):
52 global log_level
53 log_level -= sub
55 def log_level_set(level):
56 global log_level
57 log_level = level
59 def log(header, data, level=None):
60 """
61 Output to an optional log.
62 """
63 if logfile is None:
64 return
65 if level is not None:
66 log_level_set(level)
68 if not isinstance(data, str):
69 data = pp.pformat(data)
71 if len(header):
72 logfile.write("\n" + log_get_sec() + " ")
73 logfile.write(header)
74 if len(header) and len(data.strip()):
75 logfile.write("\n")
76 logfile.write(data)
78 def skip(cond, msg):
79 if not cond:
80 return
81 print("SKIP: " + msg)
82 log("SKIP: " + msg, "", level=1)
83 os.sys.exit(0)
85 def fail(cond, msg):
86 if not cond:
87 return
88 print("FAIL: " + msg)
89 tb = "".join(traceback.extract_stack().format())
90 print(tb)
91 log("FAIL: " + msg, tb, level=1)
92 os.sys.exit(1)
94 def start_test(msg):
95 log(msg, "", level=1)
96 log_level_inc()
97 print(msg)
99 def cmd(cmd, shell=True, include_stderr=False, background=False, fail=True):
101 Run a command in subprocess and return tuple of (retval, stdout);
102 optionally return stderr as well as third value.
104 proc = subprocess.Popen(cmd, shell=shell, stdout=subprocess.PIPE,
105 stderr=subprocess.PIPE)
106 if background:
107 msg = "%s START: %s" % (log_get_sec(1),
108 datetime.now().strftime("%H:%M:%S.%f"))
109 log("BKG " + proc.args, msg)
110 return proc
112 return cmd_result(proc, include_stderr=include_stderr, fail=fail)
114 def cmd_result(proc, include_stderr=False, fail=False):
115 stdout, stderr = proc.communicate()
116 stdout = stdout.decode("utf-8")
117 stderr = stderr.decode("utf-8")
118 proc.stdout.close()
119 proc.stderr.close()
121 stderr = "\n" + stderr
122 if stderr[-1] == "\n":
123 stderr = stderr[:-1]
125 sec = log_get_sec(1)
126 log("CMD " + proc.args,
127 "RETCODE: %d\n%s STDOUT:\n%s%s STDERR:%s\n%s END: %s" %
128 (proc.returncode, sec, stdout, sec, stderr,
129 sec, datetime.now().strftime("%H:%M:%S.%f")))
131 if proc.returncode != 0 and fail:
132 if len(stderr) > 0 and stderr[-1] == "\n":
133 stderr = stderr[:-1]
134 raise Exception("Command failed: %s\n%s" % (proc.args, stderr))
136 if include_stderr:
137 return proc.returncode, stdout, stderr
138 else:
139 return proc.returncode, stdout
141 def rm(f):
142 cmd("rm -f %s" % (f))
143 if f in files:
144 files.remove(f)
146 def tool(name, args, flags, JSON=True, ns="", fail=True, include_stderr=False):
147 params = ""
148 if JSON:
149 params += "%s " % (flags["json"])
151 if ns:
152 ns = "ip netns exec %s " % (ns)
153 elif ns is None:
154 ns = ""
156 if include_stderr:
157 ret, stdout, stderr = cmd(ns + name + " " + params + args,
158 fail=fail, include_stderr=True)
159 else:
160 ret, stdout = cmd(ns + name + " " + params + args,
161 fail=fail, include_stderr=False)
163 if JSON and len(stdout.strip()) != 0:
164 out = json.loads(stdout)
165 else:
166 out = stdout
168 if include_stderr:
169 return ret, out, stderr
170 else:
171 return ret, out
173 def bpftool(args, JSON=True, ns="", fail=True, include_stderr=False):
174 return tool("bpftool", args, {"json":"-p"}, JSON=JSON, ns=ns,
175 fail=fail, include_stderr=include_stderr)
177 def bpftool_prog_list(expected=None, ns="", exclude_orphaned=True):
178 _, progs = bpftool("prog show", JSON=True, ns=ns, fail=True)
179 # Remove the base progs
180 for p in base_progs:
181 if p in progs:
182 progs.remove(p)
183 if exclude_orphaned:
184 progs = [ p for p in progs if not p['orphaned'] ]
185 if expected is not None:
186 if len(progs) != expected:
187 fail(True, "%d BPF programs loaded, expected %d" %
188 (len(progs), expected))
189 return progs
191 def bpftool_map_list(expected=None, ns=""):
192 _, maps = bpftool("map show", JSON=True, ns=ns, fail=True)
193 # Remove the base maps
194 maps = [m for m in maps if m not in base_maps and m.get('name') and m.get('name') not in base_map_names]
195 if expected is not None:
196 if len(maps) != expected:
197 fail(True, "%d BPF maps loaded, expected %d" %
198 (len(maps), expected))
199 return maps
201 def bpftool_prog_list_wait(expected=0, n_retry=20):
202 for i in range(n_retry):
203 nprogs = len(bpftool_prog_list())
204 if nprogs == expected:
205 return
206 time.sleep(0.05)
207 raise Exception("Time out waiting for program counts to stabilize want %d, have %d" % (expected, nprogs))
209 def bpftool_map_list_wait(expected=0, n_retry=20, ns=""):
210 for i in range(n_retry):
211 maps = bpftool_map_list(ns=ns)
212 if len(maps) == expected:
213 return maps
214 time.sleep(0.05)
215 raise Exception("Time out waiting for map counts to stabilize want %d, have %d" % (expected, nmaps))
217 def bpftool_prog_load(sample, file_name, maps=[], prog_type="xdp", dev=None,
218 fail=True, include_stderr=False):
219 args = "prog load %s %s" % (os.path.join(bpf_test_dir, sample), file_name)
220 if prog_type is not None:
221 args += " type " + prog_type
222 if dev is not None:
223 args += " dev " + dev
224 if len(maps):
225 args += " map " + " map ".join(maps)
227 res = bpftool(args, fail=fail, include_stderr=include_stderr)
228 if res[0] == 0:
229 files.append(file_name)
230 return res
232 def ip(args, force=False, JSON=True, ns="", fail=True, include_stderr=False):
233 if force:
234 args = "-force " + args
235 return tool("ip", args, {"json":"-j"}, JSON=JSON, ns=ns,
236 fail=fail, include_stderr=include_stderr)
238 def tc(args, JSON=True, ns="", fail=True, include_stderr=False):
239 return tool("tc", args, {"json":"-p"}, JSON=JSON, ns=ns,
240 fail=fail, include_stderr=include_stderr)
242 def ethtool(dev, opt, args, fail=True):
243 return cmd("ethtool %s %s %s" % (opt, dev["ifname"], args), fail=fail)
245 def bpf_obj(name, sec="xdp", path=bpf_test_dir,):
246 return "obj %s sec %s" % (os.path.join(path, name), sec)
248 def bpf_pinned(name):
249 return "pinned %s" % (name)
251 def bpf_bytecode(bytecode):
252 return "bytecode \"%s\"" % (bytecode)
254 def mknetns(n_retry=10):
255 for i in range(n_retry):
256 name = ''.join([random.choice(string.ascii_letters) for i in range(8)])
257 ret, _ = ip("netns add %s" % (name), fail=False)
258 if ret == 0:
259 netns.append(name)
260 return name
261 return None
263 def int2str(fmt, val):
264 ret = []
265 for b in struct.pack(fmt, val):
266 ret.append(int(b))
267 return " ".join(map(lambda x: str(x), ret))
269 def str2int(strtab):
270 inttab = []
271 for i in strtab:
272 inttab.append(int(i, 16))
273 ba = bytearray(inttab)
274 if len(strtab) == 4:
275 fmt = "I"
276 elif len(strtab) == 8:
277 fmt = "Q"
278 else:
279 raise Exception("String array of len %d can't be unpacked to an int" %
280 (len(strtab)))
281 return struct.unpack(fmt, ba)[0]
283 class DebugfsDir:
285 Class for accessing DebugFS directories as a dictionary.
288 def __init__(self, path):
289 self.path = path
290 self._dict = self._debugfs_dir_read(path)
292 def __len__(self):
293 return len(self._dict.keys())
295 def __getitem__(self, key):
296 if type(key) is int:
297 key = list(self._dict.keys())[key]
298 return self._dict[key]
300 def __setitem__(self, key, value):
301 log("DebugFS set %s = %s" % (key, value), "")
302 log_level_inc()
304 cmd("echo '%s' > %s/%s" % (value, self.path, key))
305 log_level_dec()
307 _, out = cmd('cat %s/%s' % (self.path, key))
308 self._dict[key] = out.strip()
310 def _debugfs_dir_read(self, path):
311 dfs = {}
313 log("DebugFS state for %s" % (path), "")
314 log_level_inc(add=2)
316 _, out = cmd('ls ' + path)
317 for f in out.split():
318 if f == "ports":
319 continue
321 p = os.path.join(path, f)
322 if not os.stat(p).st_mode & stat.S_IRUSR:
323 continue
325 if os.path.isfile(p):
326 # We need to init trap_flow_action_cookie before read it
327 if f == "trap_flow_action_cookie":
328 cmd('echo deadbeef > %s/%s' % (path, f))
329 _, out = cmd('cat %s/%s' % (path, f))
330 dfs[f] = out.strip()
331 elif os.path.isdir(p):
332 dfs[f] = DebugfsDir(p)
333 else:
334 raise Exception("%s is neither file nor directory" % (p))
336 log_level_dec()
337 log("DebugFS state", dfs)
338 log_level_dec()
340 return dfs
342 class BpfNetdevSimDev(NetdevSimDev):
344 Class for netdevsim bus device and its attributes.
346 def __init__(self, port_count=1, ns=None):
347 super().__init__(port_count, ns=ns)
348 devs.append(self)
350 def _make_port(self, port_index, ifname):
351 return BpfNetdevSim(self, port_index, ifname, self.ns)
353 def dfs_num_bound_progs(self):
354 path = os.path.join(self.dfs_dir, "bpf_bound_progs")
355 _, progs = cmd('ls %s' % (path))
356 return len(progs.split())
358 def dfs_get_bound_progs(self, expected):
359 progs = DebugfsDir(os.path.join(self.dfs_dir, "bpf_bound_progs"))
360 if expected is not None:
361 if len(progs) != expected:
362 fail(True, "%d BPF programs bound, expected %d" %
363 (len(progs), expected))
364 return progs
366 def remove(self):
367 super().remove()
368 devs.remove(self)
371 class BpfNetdevSim(NetdevSim):
373 Class for netdevsim netdevice and its attributes.
376 def __init__(self, nsimdev, port_index, ifname, ns=None):
377 super().__init__(nsimdev, port_index, ifname, ns=ns)
379 self.dfs_dir = "%s/ports/%u/" % (nsimdev.dfs_dir, port_index)
380 self.dfs_refresh()
382 def __getitem__(self, key):
383 return self.dev[key]
385 def remove(self):
386 self.nsimdev.remove_nsim(self)
388 def dfs_refresh(self):
389 self.dfs = DebugfsDir(self.dfs_dir)
390 return self.dfs
392 def dfs_read(self, f):
393 path = os.path.join(self.dfs_dir, f)
394 _, data = cmd('cat %s' % (path))
395 return data.strip()
397 def wait_for_flush(self, bound=0, total=0, n_retry=20):
398 for i in range(n_retry):
399 nbound = self.nsimdev.dfs_num_bound_progs()
400 nprogs = len(bpftool_prog_list())
401 if nbound == bound and nprogs == total:
402 return
403 time.sleep(0.05)
404 raise Exception("Time out waiting for program counts to stabilize want %d/%d, have %d bound, %d loaded" % (bound, total, nbound, nprogs))
406 def set_ns(self, ns):
407 name = ns if ns else "1"
408 ip("link set dev %s netns %s" % (self.dev["ifname"], name), ns=self.ns)
409 self.ns = ns
411 def set_mtu(self, mtu, fail=True):
412 return ip("link set dev %s mtu %d" % (self.dev["ifname"], mtu),
413 fail=fail)
415 def set_xdp(self, bpf, mode, force=False, JSON=True, verbose=False,
416 fail=True, include_stderr=False):
417 if verbose:
418 bpf += " verbose"
419 return ip("link set dev %s xdp%s %s" % (self.dev["ifname"], mode, bpf),
420 force=force, JSON=JSON,
421 fail=fail, include_stderr=include_stderr)
423 def unset_xdp(self, mode, force=False, JSON=True,
424 fail=True, include_stderr=False):
425 return ip("link set dev %s xdp%s off" % (self.dev["ifname"], mode),
426 force=force, JSON=JSON,
427 fail=fail, include_stderr=include_stderr)
429 def ip_link_show(self, xdp):
430 _, link = ip("link show dev %s" % (self['ifname']))
431 if len(link) > 1:
432 raise Exception("Multiple objects on ip link show")
433 if len(link) < 1:
434 return {}
435 fail(xdp != "xdp" in link,
436 "XDP program not reporting in iplink (reported %s, expected %s)" %
437 ("xdp" in link, xdp))
438 return link[0]
440 def tc_add_ingress(self):
441 tc("qdisc add dev %s ingress" % (self['ifname']))
443 def tc_del_ingress(self):
444 tc("qdisc del dev %s ingress" % (self['ifname']))
446 def tc_flush_filters(self, bound=0, total=0):
447 self.tc_del_ingress()
448 self.tc_add_ingress()
449 self.wait_for_flush(bound=bound, total=total)
451 def tc_show_ingress(self, expected=None):
452 # No JSON support, oh well...
453 flags = ["skip_sw", "skip_hw", "in_hw"]
454 named = ["protocol", "pref", "chain", "handle", "id", "tag"]
456 args = "-s filter show dev %s ingress" % (self['ifname'])
457 _, out = tc(args, JSON=False)
459 filters = []
460 lines = out.split('\n')
461 for line in lines:
462 words = line.split()
463 if "handle" not in words:
464 continue
465 fltr = {}
466 for flag in flags:
467 fltr[flag] = flag in words
468 for name in named:
469 try:
470 idx = words.index(name)
471 fltr[name] = words[idx + 1]
472 except ValueError:
473 pass
474 filters.append(fltr)
476 if expected is not None:
477 fail(len(filters) != expected,
478 "%d ingress filters loaded, expected %d" %
479 (len(filters), expected))
480 return filters
482 def cls_filter_op(self, op, qdisc="ingress", prio=None, handle=None,
483 chain=None, cls="", params="",
484 fail=True, include_stderr=False):
485 spec = ""
486 if prio is not None:
487 spec += " prio %d" % (prio)
488 if handle:
489 spec += " handle %s" % (handle)
490 if chain is not None:
491 spec += " chain %d" % (chain)
493 return tc("filter {op} dev {dev} {qdisc} {spec} {cls} {params}"\
494 .format(op=op, dev=self['ifname'], qdisc=qdisc, spec=spec,
495 cls=cls, params=params),
496 fail=fail, include_stderr=include_stderr)
498 def cls_bpf_add_filter(self, bpf, op="add", prio=None, handle=None,
499 chain=None, da=False, verbose=False,
500 skip_sw=False, skip_hw=False,
501 fail=True, include_stderr=False):
502 cls = "bpf " + bpf
504 params = ""
505 if da:
506 params += " da"
507 if verbose:
508 params += " verbose"
509 if skip_sw:
510 params += " skip_sw"
511 if skip_hw:
512 params += " skip_hw"
514 return self.cls_filter_op(op=op, prio=prio, handle=handle, cls=cls,
515 chain=chain, params=params,
516 fail=fail, include_stderr=include_stderr)
518 def set_ethtool_tc_offloads(self, enable, fail=True):
519 args = "hw-tc-offload %s" % ("on" if enable else "off")
520 return ethtool(self, "-K", args, fail=fail)
522 ################################################################################
523 def clean_up():
524 global files, netns, devs
526 for dev in devs:
527 dev.remove()
528 for f in files:
529 cmd("rm -f %s" % (f))
530 for ns in netns:
531 cmd("ip netns delete %s" % (ns))
532 files = []
533 netns = []
535 def pin_prog(file_name, idx=0):
536 progs = bpftool_prog_list(expected=(idx + 1))
537 prog = progs[idx]
538 bpftool("prog pin id %d %s" % (prog["id"], file_name))
539 files.append(file_name)
541 return file_name, bpf_pinned(file_name)
543 def pin_map(file_name, idx=0, expected=1):
544 maps = bpftool_map_list_wait(expected=expected)
545 m = maps[idx]
546 bpftool("map pin id %d %s" % (m["id"], file_name))
547 files.append(file_name)
549 return file_name, bpf_pinned(file_name)
551 def check_dev_info_removed(prog_file=None, map_file=None):
552 bpftool_prog_list(expected=0)
553 bpftool_prog_list(expected=1, exclude_orphaned=False)
554 ret, err = bpftool("prog show pin %s" % (prog_file), fail=False)
555 fail(ret != 0, "failed to show prog with removed device")
557 bpftool_map_list_wait(expected=0)
558 ret, err = bpftool("map show pin %s" % (map_file), fail=False)
559 fail(ret == 0, "Showing map with removed device did not fail")
560 fail(err["error"].find("No such device") == -1,
561 "Showing map with removed device expected ENODEV, error is %s" %
562 (err["error"]))
564 def check_dev_info(other_ns, ns, prog_file=None, map_file=None, removed=False):
565 progs = bpftool_prog_list(expected=1, ns=ns)
566 prog = progs[0]
568 fail("dev" not in prog.keys(), "Device parameters not reported")
569 dev = prog["dev"]
570 fail("ifindex" not in dev.keys(), "Device parameters not reported")
571 fail("ns_dev" not in dev.keys(), "Device parameters not reported")
572 fail("ns_inode" not in dev.keys(), "Device parameters not reported")
574 if not other_ns:
575 fail("ifname" not in dev.keys(), "Ifname not reported")
576 fail(dev["ifname"] != sim["ifname"],
577 "Ifname incorrect %s vs %s" % (dev["ifname"], sim["ifname"]))
578 else:
579 fail("ifname" in dev.keys(), "Ifname is reported for other ns")
581 maps = bpftool_map_list_wait(expected=2, ns=ns)
582 for m in maps:
583 fail("dev" not in m.keys(), "Device parameters not reported")
584 fail(dev != m["dev"], "Map's device different than program's")
586 def check_extack(output, reference, args):
587 if skip_extack:
588 return
589 lines = output.split("\n")
590 comp = len(lines) >= 2 and lines[1] == 'Error: ' + reference
591 fail(not comp, "Missing or incorrect netlink extack message")
593 def check_extack_nsim(output, reference, args):
594 check_extack(output, "netdevsim: " + reference, args)
596 def check_no_extack(res, needle):
597 haystack = (res[1] + res[2]).strip()
598 fail(haystack.count(needle) or haystack.count("Warning:"),
599 "Unexpected command output, leaky extack? ('%s', '%s')" % (needle, haystack))
601 def check_verifier_log(output, reference):
602 lines = output.split("\n")
603 for l in reversed(lines):
604 if l == reference:
605 return
606 fail(True, "Missing or incorrect message from netdevsim in verifier log")
608 def check_multi_basic(two_xdps):
609 fail(two_xdps["mode"] != 4, "Bad mode reported with multiple programs")
610 fail("prog" in two_xdps, "Base program reported in multi program mode")
611 fail(len(two_xdps["attached"]) != 2,
612 "Wrong attached program count with two programs")
613 fail(two_xdps["attached"][0]["prog"]["id"] ==
614 two_xdps["attached"][1]["prog"]["id"],
615 "Offloaded and other programs have the same id")
617 def test_spurios_extack(sim, obj, skip_hw, needle):
618 res = sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=skip_hw,
619 include_stderr=True)
620 check_no_extack(res, needle)
621 res = sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1,
622 skip_hw=skip_hw, include_stderr=True)
623 check_no_extack(res, needle)
624 res = sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf",
625 include_stderr=True)
626 check_no_extack(res, needle)
628 def test_multi_prog(simdev, sim, obj, modename, modeid):
629 start_test("Test multi-attachment XDP - %s + offload..." %
630 (modename or "default", ))
631 sim.set_xdp(obj, "offload")
632 xdp = sim.ip_link_show(xdp=True)["xdp"]
633 offloaded = sim.dfs_read("bpf_offloaded_id")
634 fail("prog" not in xdp, "Base program not reported in single program mode")
635 fail(len(xdp["attached"]) != 1,
636 "Wrong attached program count with one program")
638 sim.set_xdp(obj, modename)
639 two_xdps = sim.ip_link_show(xdp=True)["xdp"]
641 fail(xdp["attached"][0] not in two_xdps["attached"],
642 "Offload program not reported after other activated")
643 check_multi_basic(two_xdps)
645 offloaded2 = sim.dfs_read("bpf_offloaded_id")
646 fail(offloaded != offloaded2,
647 "Offload ID changed after loading other program")
649 start_test("Test multi-attachment XDP - replace...")
650 ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True)
651 fail(ret == 0, "Replaced one of programs without -force")
652 check_extack(err, "XDP program already attached.", args)
654 start_test("Test multi-attachment XDP - remove without mode...")
655 ret, _, err = sim.unset_xdp("", force=True,
656 fail=False, include_stderr=True)
657 fail(ret == 0, "Removed program without a mode flag")
658 check_extack(err, "More than one program loaded, unset mode is ambiguous.", args)
660 sim.unset_xdp("offload")
661 xdp = sim.ip_link_show(xdp=True)["xdp"]
662 offloaded = sim.dfs_read("bpf_offloaded_id")
664 fail(xdp["mode"] != modeid, "Bad mode reported after multiple programs")
665 fail("prog" not in xdp,
666 "Base program not reported after multi program mode")
667 fail(xdp["attached"][0] not in two_xdps["attached"],
668 "Offload program not reported after other activated")
669 fail(len(xdp["attached"]) != 1,
670 "Wrong attached program count with remaining programs")
671 fail(offloaded != "0", "Offload ID reported with only other program left")
673 start_test("Test multi-attachment XDP - reattach...")
674 sim.set_xdp(obj, "offload")
675 two_xdps = sim.ip_link_show(xdp=True)["xdp"]
677 fail(xdp["attached"][0] not in two_xdps["attached"],
678 "Other program not reported after offload activated")
679 check_multi_basic(two_xdps)
681 start_test("Test multi-attachment XDP - device remove...")
682 simdev.remove()
684 simdev = BpfNetdevSimDev()
685 sim, = simdev.nsims
686 sim.set_ethtool_tc_offloads(True)
687 return [simdev, sim]
689 # Parse command line
690 parser = argparse.ArgumentParser()
691 parser.add_argument("--log", help="output verbose log to given file")
692 args = parser.parse_args()
693 if args.log:
694 logfile = open(args.log, 'w+')
695 logfile.write("# -*-Org-*-")
697 log("Prepare...", "", level=1)
698 log_level_inc()
700 # Check permissions
701 skip(os.getuid() != 0, "test must be run as root")
703 # Check tools
704 ret, progs = bpftool("prog", fail=False)
705 skip(ret != 0, "bpftool not installed")
706 base_progs = progs
707 _, base_maps = bpftool("map")
708 base_map_names = [
709 'pid_iter.rodata', # created on each bpftool invocation
710 'libbpf_det_bind', # created on each bpftool invocation
713 # Check netdevsim
714 if not os.path.isdir("/sys/bus/netdevsim/"):
715 ret, out = cmd("modprobe netdevsim", fail=False)
716 skip(ret != 0, "netdevsim module could not be loaded")
718 # Check debugfs
719 _, out = cmd("mount")
720 if out.find("/sys/kernel/debug type debugfs") == -1:
721 cmd("mount -t debugfs none /sys/kernel/debug")
723 # Check samples are compiled
724 samples = ["sample_ret0.bpf.o", "sample_map_ret0.bpf.o"]
725 for s in samples:
726 ret, out = cmd("ls %s/%s" % (bpf_test_dir, s), fail=False)
727 skip(ret != 0, "sample %s/%s not found, please compile it" %
728 (bpf_test_dir, s))
730 # Check if iproute2 is built with libmnl (needed by extack support)
731 _, _, err = cmd("tc qdisc delete dev lo handle 0",
732 fail=False, include_stderr=True)
733 if err.find("Error: Failed to find qdisc with specified handle.") == -1:
734 print("Warning: no extack message in iproute2 output, libmnl missing?")
735 log("Warning: no extack message in iproute2 output, libmnl missing?", "")
736 skip_extack = True
738 # Check if net namespaces seem to work
739 ns = mknetns()
740 skip(ns is None, "Could not create a net namespace")
741 cmd("ip netns delete %s" % (ns))
742 netns = []
744 try:
745 obj = bpf_obj("sample_ret0.bpf.o")
746 bytecode = bpf_bytecode("1,6 0 0 4294967295,")
748 start_test("Test destruction of generic XDP...")
749 simdev = BpfNetdevSimDev()
750 sim, = simdev.nsims
751 sim.set_xdp(obj, "generic")
752 simdev.remove()
753 bpftool_prog_list_wait(expected=0)
755 simdev = BpfNetdevSimDev()
756 sim, = simdev.nsims
757 sim.tc_add_ingress()
759 start_test("Test TC non-offloaded...")
760 ret, _ = sim.cls_bpf_add_filter(obj, skip_hw=True, fail=False)
761 fail(ret != 0, "Software TC filter did not load")
763 start_test("Test TC non-offloaded isn't getting bound...")
764 ret, _ = sim.cls_bpf_add_filter(obj, fail=False)
765 fail(ret != 0, "Software TC filter did not load")
766 simdev.dfs_get_bound_progs(expected=0)
768 sim.tc_flush_filters()
770 start_test("Test TC offloads are off by default...")
771 ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True,
772 fail=False, include_stderr=True)
773 fail(ret == 0, "TC filter loaded without enabling TC offloads")
774 check_extack(err, "TC offload is disabled on net device.", args)
775 sim.wait_for_flush()
777 sim.set_ethtool_tc_offloads(True)
778 sim.dfs["bpf_tc_non_bound_accept"] = "Y"
780 start_test("Test TC offload by default...")
781 ret, _ = sim.cls_bpf_add_filter(obj, fail=False)
782 fail(ret != 0, "Software TC filter did not load")
783 simdev.dfs_get_bound_progs(expected=0)
784 ingress = sim.tc_show_ingress(expected=1)
785 fltr = ingress[0]
786 fail(not fltr["in_hw"], "Filter not offloaded by default")
788 sim.tc_flush_filters()
790 start_test("Test TC cBPF bytcode tries offload by default...")
791 ret, _ = sim.cls_bpf_add_filter(bytecode, fail=False)
792 fail(ret != 0, "Software TC filter did not load")
793 simdev.dfs_get_bound_progs(expected=0)
794 ingress = sim.tc_show_ingress(expected=1)
795 fltr = ingress[0]
796 fail(not fltr["in_hw"], "Bytecode not offloaded by default")
798 sim.tc_flush_filters()
799 sim.dfs["bpf_tc_non_bound_accept"] = "N"
801 start_test("Test TC cBPF unbound bytecode doesn't offload...")
802 ret, _, err = sim.cls_bpf_add_filter(bytecode, skip_sw=True,
803 fail=False, include_stderr=True)
804 fail(ret == 0, "TC bytecode loaded for offload")
805 check_extack_nsim(err, "netdevsim configured to reject unbound programs.",
806 args)
807 sim.wait_for_flush()
809 start_test("Test non-0 chain offload...")
810 ret, _, err = sim.cls_bpf_add_filter(obj, chain=1, prio=1, handle=1,
811 skip_sw=True,
812 fail=False, include_stderr=True)
813 fail(ret == 0, "Offloaded a filter to chain other than 0")
814 check_extack(err, "Driver supports only offload of chain 0.", args)
815 sim.tc_flush_filters()
817 start_test("Test TC replace...")
818 sim.cls_bpf_add_filter(obj, prio=1, handle=1)
819 sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1)
820 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
822 sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_sw=True)
823 sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, skip_sw=True)
824 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
826 sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=True)
827 sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, skip_hw=True)
828 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
830 start_test("Test TC replace bad flags...")
831 for i in range(3):
832 for j in range(3):
833 ret, _ = sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1,
834 skip_sw=(j == 1), skip_hw=(j == 2),
835 fail=False)
836 fail(bool(ret) != bool(j),
837 "Software TC incorrect load in replace test, iteration %d" %
838 (j))
839 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
841 start_test("Test spurious extack from the driver...")
842 test_spurios_extack(sim, obj, False, "netdevsim")
843 test_spurios_extack(sim, obj, True, "netdevsim")
845 sim.set_ethtool_tc_offloads(False)
847 test_spurios_extack(sim, obj, False, "TC offload is disabled")
848 test_spurios_extack(sim, obj, True, "TC offload is disabled")
850 sim.set_ethtool_tc_offloads(True)
852 sim.tc_flush_filters()
854 start_test("Test TC offloads failure...")
855 sim.dfs["dev/bpf_bind_verifier_accept"] = 0
856 ret, _, err = sim.cls_bpf_add_filter(obj, verbose=True, skip_sw=True,
857 fail=False, include_stderr=True)
858 fail(ret == 0, "TC filter did not reject with TC offloads enabled")
859 check_verifier_log(err, "[netdevsim] Hello from netdevsim!")
860 sim.dfs["dev/bpf_bind_verifier_accept"] = 1
862 start_test("Test TC offloads work...")
863 ret, _, err = sim.cls_bpf_add_filter(obj, verbose=True, skip_sw=True,
864 fail=False, include_stderr=True)
865 fail(ret != 0, "TC filter did not load with TC offloads enabled")
867 start_test("Test TC offload basics...")
868 dfs = simdev.dfs_get_bound_progs(expected=1)
869 progs = bpftool_prog_list(expected=1)
870 ingress = sim.tc_show_ingress(expected=1)
872 dprog = dfs[0]
873 prog = progs[0]
874 fltr = ingress[0]
875 fail(fltr["skip_hw"], "TC does reports 'skip_hw' on offloaded filter")
876 fail(not fltr["in_hw"], "TC does not report 'in_hw' for offloaded filter")
877 fail(not fltr["skip_sw"], "TC does not report 'skip_sw' back")
879 start_test("Test TC offload is device-bound...")
880 fail(str(prog["id"]) != fltr["id"], "Program IDs don't match")
881 fail(prog["tag"] != fltr["tag"], "Program tags don't match")
882 fail(fltr["id"] != dprog["id"], "Program IDs don't match")
883 fail(dprog["state"] != "xlated", "Offloaded program state not translated")
884 fail(dprog["loaded"] != "Y", "Offloaded program is not loaded")
886 start_test("Test disabling TC offloads is rejected while filters installed...")
887 ret, _ = sim.set_ethtool_tc_offloads(False, fail=False)
888 fail(ret == 0, "Driver should refuse to disable TC offloads with filters installed...")
889 sim.set_ethtool_tc_offloads(True)
891 start_test("Test qdisc removal frees things...")
892 sim.tc_flush_filters()
893 sim.tc_show_ingress(expected=0)
895 start_test("Test disabling TC offloads is OK without filters...")
896 ret, _ = sim.set_ethtool_tc_offloads(False, fail=False)
897 fail(ret != 0,
898 "Driver refused to disable TC offloads without filters installed...")
900 sim.set_ethtool_tc_offloads(True)
902 start_test("Test destroying device gets rid of TC filters...")
903 sim.cls_bpf_add_filter(obj, skip_sw=True)
904 simdev.remove()
905 bpftool_prog_list_wait(expected=0)
907 simdev = BpfNetdevSimDev()
908 sim, = simdev.nsims
909 sim.set_ethtool_tc_offloads(True)
911 start_test("Test destroying device gets rid of XDP...")
912 sim.set_xdp(obj, "offload")
913 simdev.remove()
914 bpftool_prog_list_wait(expected=0)
916 simdev = BpfNetdevSimDev()
917 sim, = simdev.nsims
918 sim.set_ethtool_tc_offloads(True)
920 start_test("Test XDP prog reporting...")
921 sim.set_xdp(obj, "drv")
922 ipl = sim.ip_link_show(xdp=True)
923 progs = bpftool_prog_list(expected=1)
924 fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"],
925 "Loaded program has wrong ID")
927 start_test("Test XDP prog replace without force...")
928 ret, _ = sim.set_xdp(obj, "drv", fail=False)
929 fail(ret == 0, "Replaced XDP program without -force")
930 sim.wait_for_flush(total=1)
932 start_test("Test XDP prog replace with force...")
933 ret, _ = sim.set_xdp(obj, "drv", force=True, fail=False)
934 fail(ret != 0, "Could not replace XDP program with -force")
935 bpftool_prog_list_wait(expected=1)
936 ipl = sim.ip_link_show(xdp=True)
937 progs = bpftool_prog_list(expected=1)
938 fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"],
939 "Loaded program has wrong ID")
940 fail("dev" in progs[0].keys(),
941 "Device parameters reported for non-offloaded program")
943 start_test("Test XDP prog replace with bad flags...")
944 ret, _, err = sim.set_xdp(obj, "generic", force=True,
945 fail=False, include_stderr=True)
946 fail(ret == 0, "Replaced XDP program with a program in different mode")
947 check_extack(err,
948 "Native and generic XDP can't be active at the same time.",
949 args)
951 start_test("Test MTU restrictions...")
952 ret, _ = sim.set_mtu(9000, fail=False)
953 fail(ret == 0,
954 "Driver should refuse to increase MTU to 9000 with XDP loaded...")
955 sim.unset_xdp("drv")
956 bpftool_prog_list_wait(expected=0)
957 sim.set_mtu(9000)
958 ret, _, err = sim.set_xdp(obj, "drv", fail=False, include_stderr=True)
959 fail(ret == 0, "Driver should refuse to load program with MTU of 9000...")
960 check_extack_nsim(err, "MTU too large w/ XDP enabled.", args)
961 sim.set_mtu(1500)
963 sim.wait_for_flush()
964 start_test("Test non-offload XDP attaching to HW...")
965 bpftool_prog_load("sample_ret0.bpf.o", "/sys/fs/bpf/nooffload")
966 nooffload = bpf_pinned("/sys/fs/bpf/nooffload")
967 ret, _, err = sim.set_xdp(nooffload, "offload",
968 fail=False, include_stderr=True)
969 fail(ret == 0, "attached non-offloaded XDP program to HW")
970 check_extack_nsim(err, "xdpoffload of non-bound program.", args)
971 rm("/sys/fs/bpf/nooffload")
973 start_test("Test offload XDP attaching to drv...")
974 bpftool_prog_load("sample_ret0.bpf.o", "/sys/fs/bpf/offload",
975 dev=sim['ifname'])
976 offload = bpf_pinned("/sys/fs/bpf/offload")
977 ret, _, err = sim.set_xdp(offload, "drv", fail=False, include_stderr=True)
978 fail(ret == 0, "attached offloaded XDP program to drv")
979 check_extack(err, "Using offloaded program without HW_MODE flag is not supported.", args)
980 rm("/sys/fs/bpf/offload")
981 sim.wait_for_flush()
983 start_test("Test XDP load failure...")
984 sim.dfs["dev/bpf_bind_verifier_accept"] = 0
985 ret, _, err = bpftool_prog_load("sample_ret0.bpf.o", "/sys/fs/bpf/offload",
986 dev=sim['ifname'], fail=False, include_stderr=True)
987 fail(ret == 0, "verifier should fail on load")
988 check_verifier_log(err, "[netdevsim] Hello from netdevsim!")
989 sim.dfs["dev/bpf_bind_verifier_accept"] = 1
990 sim.wait_for_flush()
992 start_test("Test XDP offload...")
993 _, _, err = sim.set_xdp(obj, "offload", verbose=True, include_stderr=True)
994 ipl = sim.ip_link_show(xdp=True)
995 link_xdp = ipl["xdp"]["prog"]
996 progs = bpftool_prog_list(expected=1)
997 prog = progs[0]
998 fail(link_xdp["id"] != prog["id"], "Loaded program has wrong ID")
1000 start_test("Test XDP offload is device bound...")
1001 dfs = simdev.dfs_get_bound_progs(expected=1)
1002 dprog = dfs[0]
1004 fail(prog["id"] != link_xdp["id"], "Program IDs don't match")
1005 fail(prog["tag"] != link_xdp["tag"], "Program tags don't match")
1006 fail(str(link_xdp["id"]) != dprog["id"], "Program IDs don't match")
1007 fail(dprog["state"] != "xlated", "Offloaded program state not translated")
1008 fail(dprog["loaded"] != "Y", "Offloaded program is not loaded")
1010 start_test("Test removing XDP program many times...")
1011 sim.unset_xdp("offload")
1012 sim.unset_xdp("offload")
1013 sim.unset_xdp("drv")
1014 sim.unset_xdp("drv")
1015 sim.unset_xdp("")
1016 sim.unset_xdp("")
1017 bpftool_prog_list_wait(expected=0)
1019 start_test("Test attempt to use a program for a wrong device...")
1020 simdev2 = BpfNetdevSimDev()
1021 sim2, = simdev2.nsims
1022 sim2.set_xdp(obj, "offload")
1023 pin_file, pinned = pin_prog("/sys/fs/bpf/tmp")
1025 ret, _, err = sim.set_xdp(pinned, "offload",
1026 fail=False, include_stderr=True)
1027 fail(ret == 0, "Pinned program loaded for a different device accepted")
1028 check_extack(err, "Program bound to different device.", args)
1029 simdev2.remove()
1030 ret, _, err = sim.set_xdp(pinned, "offload",
1031 fail=False, include_stderr=True)
1032 fail(ret == 0, "Pinned program loaded for a removed device accepted")
1033 check_extack(err, "Program bound to different device.", args)
1034 rm(pin_file)
1035 bpftool_prog_list_wait(expected=0)
1037 simdev, sim = test_multi_prog(simdev, sim, obj, "", 1)
1038 simdev, sim = test_multi_prog(simdev, sim, obj, "drv", 1)
1039 simdev, sim = test_multi_prog(simdev, sim, obj, "generic", 2)
1041 start_test("Test mixing of TC and XDP...")
1042 sim.tc_add_ingress()
1043 sim.set_xdp(obj, "offload")
1044 ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True,
1045 fail=False, include_stderr=True)
1046 fail(ret == 0, "Loading TC when XDP active should fail")
1047 check_extack_nsim(err, "driver and netdev offload states mismatch.", args)
1048 sim.unset_xdp("offload")
1049 sim.wait_for_flush()
1051 sim.cls_bpf_add_filter(obj, skip_sw=True)
1052 ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True)
1053 fail(ret == 0, "Loading XDP when TC active should fail")
1054 check_extack_nsim(err, "TC program is already loaded.", args)
1056 start_test("Test binding TC from pinned...")
1057 pin_file, pinned = pin_prog("/sys/fs/bpf/tmp")
1058 sim.tc_flush_filters(bound=1, total=1)
1059 sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True)
1060 sim.tc_flush_filters(bound=1, total=1)
1062 start_test("Test binding XDP from pinned...")
1063 sim.set_xdp(obj, "offload")
1064 pin_file, pinned = pin_prog("/sys/fs/bpf/tmp2", idx=1)
1066 sim.set_xdp(pinned, "offload", force=True)
1067 sim.unset_xdp("offload")
1068 sim.set_xdp(pinned, "offload", force=True)
1069 sim.unset_xdp("offload")
1071 start_test("Test offload of wrong type fails...")
1072 ret, _ = sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True, fail=False)
1073 fail(ret == 0, "Managed to attach XDP program to TC")
1075 start_test("Test asking for TC offload of two filters...")
1076 sim.cls_bpf_add_filter(obj, da=True, skip_sw=True)
1077 ret, _, err = sim.cls_bpf_add_filter(obj, da=True, skip_sw=True,
1078 fail=False, include_stderr=True)
1079 fail(ret == 0, "Managed to offload two TC filters at the same time")
1080 check_extack_nsim(err, "driver and netdev offload states mismatch.", args)
1082 sim.tc_flush_filters(bound=2, total=2)
1084 start_test("Test if netdev removal waits for translation...")
1085 delay_msec = 500
1086 sim.dfs["dev/bpf_bind_verifier_delay"] = delay_msec
1087 start = time.time()
1088 cmd_line = "tc filter add dev %s ingress bpf %s da skip_sw" % \
1089 (sim['ifname'], obj)
1090 tc_proc = cmd(cmd_line, background=True, fail=False)
1091 # Wait for the verifier to start
1092 while simdev.dfs_num_bound_progs() <= 2:
1093 pass
1094 simdev.remove()
1095 end = time.time()
1096 ret, _ = cmd_result(tc_proc, fail=False)
1097 time_diff = end - start
1098 log("Time", "start:\t%s\nend:\t%s\ndiff:\t%s" % (start, end, time_diff))
1100 fail(ret == 0, "Managed to load TC filter on a unregistering device")
1101 delay_sec = delay_msec * 0.001
1102 fail(time_diff < delay_sec, "Removal process took %s, expected %s" %
1103 (time_diff, delay_sec))
1105 # Remove all pinned files and reinstantiate the netdev
1106 clean_up()
1107 bpftool_prog_list_wait(expected=0)
1109 simdev = BpfNetdevSimDev()
1110 sim, = simdev.nsims
1111 map_obj = bpf_obj("sample_map_ret0.bpf.o")
1112 start_test("Test loading program with maps...")
1113 sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
1115 start_test("Test bpftool bound info reporting (own ns)...")
1116 check_dev_info(False, "")
1118 start_test("Test bpftool bound info reporting (other ns)...")
1119 ns = mknetns()
1120 sim.set_ns(ns)
1121 check_dev_info(True, "")
1123 start_test("Test bpftool bound info reporting (remote ns)...")
1124 check_dev_info(False, ns)
1126 start_test("Test bpftool bound info reporting (back to own ns)...")
1127 sim.set_ns("")
1128 check_dev_info(False, "")
1130 prog_file, _ = pin_prog("/sys/fs/bpf/tmp_prog")
1131 map_file, _ = pin_map("/sys/fs/bpf/tmp_map", idx=1, expected=2)
1132 simdev.remove()
1134 start_test("Test bpftool bound info reporting (removed dev)...")
1135 check_dev_info_removed(prog_file=prog_file, map_file=map_file)
1137 # Remove all pinned files and reinstantiate the netdev
1138 clean_up()
1139 bpftool_prog_list_wait(expected=0)
1141 simdev = BpfNetdevSimDev()
1142 sim, = simdev.nsims
1144 start_test("Test map update (no flags)...")
1145 sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
1146 maps = bpftool_map_list_wait(expected=2)
1147 array = maps[0] if maps[0]["type"] == "array" else maps[1]
1148 htab = maps[0] if maps[0]["type"] == "hash" else maps[1]
1149 for m in maps:
1150 for i in range(2):
1151 bpftool("map update id %d key %s value %s" %
1152 (m["id"], int2str("I", i), int2str("Q", i * 3)))
1154 for m in maps:
1155 ret, _ = bpftool("map update id %d key %s value %s" %
1156 (m["id"], int2str("I", 3), int2str("Q", 3 * 3)),
1157 fail=False)
1158 fail(ret == 0, "added too many entries")
1160 start_test("Test map update (exists)...")
1161 for m in maps:
1162 for i in range(2):
1163 bpftool("map update id %d key %s value %s exist" %
1164 (m["id"], int2str("I", i), int2str("Q", i * 3)))
1166 for m in maps:
1167 ret, err = bpftool("map update id %d key %s value %s exist" %
1168 (m["id"], int2str("I", 3), int2str("Q", 3 * 3)),
1169 fail=False)
1170 fail(ret == 0, "updated non-existing key")
1171 fail(err["error"].find("No such file or directory") == -1,
1172 "expected ENOENT, error is '%s'" % (err["error"]))
1174 start_test("Test map update (noexist)...")
1175 for m in maps:
1176 for i in range(2):
1177 ret, err = bpftool("map update id %d key %s value %s noexist" %
1178 (m["id"], int2str("I", i), int2str("Q", i * 3)),
1179 fail=False)
1180 fail(ret == 0, "updated existing key")
1181 fail(err["error"].find("File exists") == -1,
1182 "expected EEXIST, error is '%s'" % (err["error"]))
1184 start_test("Test map dump...")
1185 for m in maps:
1186 _, entries = bpftool("map dump id %d" % (m["id"]))
1187 for i in range(2):
1188 key = str2int(entries[i]["key"])
1189 fail(key != i, "expected key %d, got %d" % (key, i))
1190 val = str2int(entries[i]["value"])
1191 fail(val != i * 3, "expected value %d, got %d" % (val, i * 3))
1193 start_test("Test map getnext...")
1194 for m in maps:
1195 _, entry = bpftool("map getnext id %d" % (m["id"]))
1196 key = str2int(entry["next_key"])
1197 fail(key != 0, "next key %d, expected %d" % (key, 0))
1198 _, entry = bpftool("map getnext id %d key %s" %
1199 (m["id"], int2str("I", 0)))
1200 key = str2int(entry["next_key"])
1201 fail(key != 1, "next key %d, expected %d" % (key, 1))
1202 ret, err = bpftool("map getnext id %d key %s" %
1203 (m["id"], int2str("I", 1)), fail=False)
1204 fail(ret == 0, "got next key past the end of map")
1205 fail(err["error"].find("No such file or directory") == -1,
1206 "expected ENOENT, error is '%s'" % (err["error"]))
1208 start_test("Test map delete (htab)...")
1209 for i in range(2):
1210 bpftool("map delete id %d key %s" % (htab["id"], int2str("I", i)))
1212 start_test("Test map delete (array)...")
1213 for i in range(2):
1214 ret, err = bpftool("map delete id %d key %s" %
1215 (htab["id"], int2str("I", i)), fail=False)
1216 fail(ret == 0, "removed entry from an array")
1217 fail(err["error"].find("No such file or directory") == -1,
1218 "expected ENOENT, error is '%s'" % (err["error"]))
1220 start_test("Test map remove...")
1221 sim.unset_xdp("offload")
1222 bpftool_map_list_wait(expected=0)
1223 simdev.remove()
1225 simdev = BpfNetdevSimDev()
1226 sim, = simdev.nsims
1227 sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
1228 simdev.remove()
1229 bpftool_map_list_wait(expected=0)
1231 start_test("Test map creation fail path...")
1232 simdev = BpfNetdevSimDev()
1233 sim, = simdev.nsims
1234 sim.dfs["bpf_map_accept"] = "N"
1235 ret, _ = sim.set_xdp(map_obj, "offload", JSON=False, fail=False)
1236 fail(ret == 0,
1237 "netdevsim didn't refuse to create a map with offload disabled")
1239 simdev.remove()
1241 start_test("Test multi-dev ASIC program reuse...")
1242 simdevA = BpfNetdevSimDev()
1243 simA, = simdevA.nsims
1244 simdevB = BpfNetdevSimDev(3)
1245 simB1, simB2, simB3 = simdevB.nsims
1246 sims = (simA, simB1, simB2, simB3)
1247 simB = (simB1, simB2, simB3)
1249 bpftool_prog_load("sample_map_ret0.bpf.o", "/sys/fs/bpf/nsimA",
1250 dev=simA['ifname'])
1251 progA = bpf_pinned("/sys/fs/bpf/nsimA")
1252 bpftool_prog_load("sample_map_ret0.bpf.o", "/sys/fs/bpf/nsimB",
1253 dev=simB1['ifname'])
1254 progB = bpf_pinned("/sys/fs/bpf/nsimB")
1256 simA.set_xdp(progA, "offload", JSON=False)
1257 for d in simdevB.nsims:
1258 d.set_xdp(progB, "offload", JSON=False)
1260 start_test("Test multi-dev ASIC cross-dev replace...")
1261 ret, _ = simA.set_xdp(progB, "offload", force=True, JSON=False, fail=False)
1262 fail(ret == 0, "cross-ASIC program allowed")
1263 for d in simdevB.nsims:
1264 ret, _ = d.set_xdp(progA, "offload", force=True, JSON=False, fail=False)
1265 fail(ret == 0, "cross-ASIC program allowed")
1267 start_test("Test multi-dev ASIC cross-dev install...")
1268 for d in sims:
1269 d.unset_xdp("offload")
1271 ret, _, err = simA.set_xdp(progB, "offload", force=True, JSON=False,
1272 fail=False, include_stderr=True)
1273 fail(ret == 0, "cross-ASIC program allowed")
1274 check_extack(err, "Program bound to different device.", args)
1275 for d in simdevB.nsims:
1276 ret, _, err = d.set_xdp(progA, "offload", force=True, JSON=False,
1277 fail=False, include_stderr=True)
1278 fail(ret == 0, "cross-ASIC program allowed")
1279 check_extack(err, "Program bound to different device.", args)
1281 start_test("Test multi-dev ASIC cross-dev map reuse...")
1283 mapA = bpftool("prog show %s" % (progA))[1]["map_ids"][0]
1284 mapB = bpftool("prog show %s" % (progB))[1]["map_ids"][0]
1286 ret, _ = bpftool_prog_load("sample_map_ret0.bpf.o", "/sys/fs/bpf/nsimB_",
1287 dev=simB3['ifname'],
1288 maps=["idx 0 id %d" % (mapB)],
1289 fail=False)
1290 fail(ret != 0, "couldn't reuse a map on the same ASIC")
1291 rm("/sys/fs/bpf/nsimB_")
1293 ret, _, err = bpftool_prog_load("sample_map_ret0.bpf.o", "/sys/fs/bpf/nsimA_",
1294 dev=simA['ifname'],
1295 maps=["idx 0 id %d" % (mapB)],
1296 fail=False, include_stderr=True)
1297 fail(ret == 0, "could reuse a map on a different ASIC")
1298 fail(err.count("offload device mismatch between prog and map") == 0,
1299 "error message missing for cross-ASIC map")
1301 ret, _, err = bpftool_prog_load("sample_map_ret0.bpf.o", "/sys/fs/bpf/nsimB_",
1302 dev=simB1['ifname'],
1303 maps=["idx 0 id %d" % (mapA)],
1304 fail=False, include_stderr=True)
1305 fail(ret == 0, "could reuse a map on a different ASIC")
1306 fail(err.count("offload device mismatch between prog and map") == 0,
1307 "error message missing for cross-ASIC map")
1309 start_test("Test multi-dev ASIC cross-dev destruction...")
1310 bpftool_prog_list_wait(expected=2)
1312 simdevA.remove()
1313 bpftool_prog_list_wait(expected=1)
1315 ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"]
1316 fail(ifnameB != simB1['ifname'], "program not bound to original device")
1317 simB1.remove()
1318 bpftool_prog_list_wait(expected=1)
1320 start_test("Test multi-dev ASIC cross-dev destruction - move...")
1321 ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"]
1322 fail(ifnameB not in (simB2['ifname'], simB3['ifname']),
1323 "program not bound to remaining devices")
1325 simB2.remove()
1326 ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"]
1327 fail(ifnameB != simB3['ifname'], "program not bound to remaining device")
1329 simB3.remove()
1330 simdevB.remove()
1331 bpftool_prog_list_wait(expected=0)
1333 start_test("Test multi-dev ASIC cross-dev destruction - orphaned...")
1334 ret, out = bpftool("prog show %s" % (progB), fail=False)
1335 fail(ret != 0, "couldn't get information about orphaned program")
1337 print("%s: OK" % (os.path.basename(__file__)))
1339 finally:
1340 log("Clean up...", "", level=1)
1341 log_level_inc()
1342 clean_up()