WIP FPC-III support
[linux/fpc-iii.git] / tools / testing / selftests / bpf / test_offload.py
blobedaffd43da835ecfe6c10297be29079f7528fcb8
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 logfile = None
33 log_level = 1
34 skip_extack = False
35 bpf_test_dir = os.path.dirname(os.path.realpath(__file__))
36 pp = pprint.PrettyPrinter()
37 devs = [] # devices we created for clean up
38 files = [] # files to be removed
39 netns = [] # net namespaces to be removed
41 def log_get_sec(level=0):
42 return "*" * (log_level + level)
44 def log_level_inc(add=1):
45 global log_level
46 log_level += add
48 def log_level_dec(sub=1):
49 global log_level
50 log_level -= sub
52 def log_level_set(level):
53 global log_level
54 log_level = level
56 def log(header, data, level=None):
57 """
58 Output to an optional log.
59 """
60 if logfile is None:
61 return
62 if level is not None:
63 log_level_set(level)
65 if not isinstance(data, str):
66 data = pp.pformat(data)
68 if len(header):
69 logfile.write("\n" + log_get_sec() + " ")
70 logfile.write(header)
71 if len(header) and len(data.strip()):
72 logfile.write("\n")
73 logfile.write(data)
75 def skip(cond, msg):
76 if not cond:
77 return
78 print("SKIP: " + msg)
79 log("SKIP: " + msg, "", level=1)
80 os.sys.exit(0)
82 def fail(cond, msg):
83 if not cond:
84 return
85 print("FAIL: " + msg)
86 tb = "".join(traceback.extract_stack().format())
87 print(tb)
88 log("FAIL: " + msg, tb, level=1)
89 os.sys.exit(1)
91 def start_test(msg):
92 log(msg, "", level=1)
93 log_level_inc()
94 print(msg)
96 def cmd(cmd, shell=True, include_stderr=False, background=False, fail=True):
97 """
98 Run a command in subprocess and return tuple of (retval, stdout);
99 optionally return stderr as well as third value.
101 proc = subprocess.Popen(cmd, shell=shell, stdout=subprocess.PIPE,
102 stderr=subprocess.PIPE)
103 if background:
104 msg = "%s START: %s" % (log_get_sec(1),
105 datetime.now().strftime("%H:%M:%S.%f"))
106 log("BKG " + proc.args, msg)
107 return proc
109 return cmd_result(proc, include_stderr=include_stderr, fail=fail)
111 def cmd_result(proc, include_stderr=False, fail=False):
112 stdout, stderr = proc.communicate()
113 stdout = stdout.decode("utf-8")
114 stderr = stderr.decode("utf-8")
115 proc.stdout.close()
116 proc.stderr.close()
118 stderr = "\n" + stderr
119 if stderr[-1] == "\n":
120 stderr = stderr[:-1]
122 sec = log_get_sec(1)
123 log("CMD " + proc.args,
124 "RETCODE: %d\n%s STDOUT:\n%s%s STDERR:%s\n%s END: %s" %
125 (proc.returncode, sec, stdout, sec, stderr,
126 sec, datetime.now().strftime("%H:%M:%S.%f")))
128 if proc.returncode != 0 and fail:
129 if len(stderr) > 0 and stderr[-1] == "\n":
130 stderr = stderr[:-1]
131 raise Exception("Command failed: %s\n%s" % (proc.args, stderr))
133 if include_stderr:
134 return proc.returncode, stdout, stderr
135 else:
136 return proc.returncode, stdout
138 def rm(f):
139 cmd("rm -f %s" % (f))
140 if f in files:
141 files.remove(f)
143 def tool(name, args, flags, JSON=True, ns="", fail=True, include_stderr=False):
144 params = ""
145 if JSON:
146 params += "%s " % (flags["json"])
148 if ns != "":
149 ns = "ip netns exec %s " % (ns)
151 if include_stderr:
152 ret, stdout, stderr = cmd(ns + name + " " + params + args,
153 fail=fail, include_stderr=True)
154 else:
155 ret, stdout = cmd(ns + name + " " + params + args,
156 fail=fail, include_stderr=False)
158 if JSON and len(stdout.strip()) != 0:
159 out = json.loads(stdout)
160 else:
161 out = stdout
163 if include_stderr:
164 return ret, out, stderr
165 else:
166 return ret, out
168 def bpftool(args, JSON=True, ns="", fail=True, include_stderr=False):
169 return tool("bpftool", args, {"json":"-p"}, JSON=JSON, ns=ns,
170 fail=fail, include_stderr=include_stderr)
172 def bpftool_prog_list(expected=None, ns=""):
173 _, progs = bpftool("prog show", JSON=True, ns=ns, fail=True)
174 # Remove the base progs
175 for p in base_progs:
176 if p in progs:
177 progs.remove(p)
178 if expected is not None:
179 if len(progs) != expected:
180 fail(True, "%d BPF programs loaded, expected %d" %
181 (len(progs), expected))
182 return progs
184 def bpftool_map_list(expected=None, ns=""):
185 _, maps = bpftool("map show", JSON=True, ns=ns, fail=True)
186 # Remove the base maps
187 maps = [m for m in maps if m not in base_maps and m.get('name') not in base_map_names]
188 if expected is not None:
189 if len(maps) != expected:
190 fail(True, "%d BPF maps loaded, expected %d" %
191 (len(maps), expected))
192 return maps
194 def bpftool_prog_list_wait(expected=0, n_retry=20):
195 for i in range(n_retry):
196 nprogs = len(bpftool_prog_list())
197 if nprogs == expected:
198 return
199 time.sleep(0.05)
200 raise Exception("Time out waiting for program counts to stabilize want %d, have %d" % (expected, nprogs))
202 def bpftool_map_list_wait(expected=0, n_retry=20):
203 for i in range(n_retry):
204 nmaps = len(bpftool_map_list())
205 if nmaps == expected:
206 return
207 time.sleep(0.05)
208 raise Exception("Time out waiting for map counts to stabilize want %d, have %d" % (expected, nmaps))
210 def bpftool_prog_load(sample, file_name, maps=[], prog_type="xdp", dev=None,
211 fail=True, include_stderr=False):
212 args = "prog load %s %s" % (os.path.join(bpf_test_dir, sample), file_name)
213 if prog_type is not None:
214 args += " type " + prog_type
215 if dev is not None:
216 args += " dev " + dev
217 if len(maps):
218 args += " map " + " map ".join(maps)
220 res = bpftool(args, fail=fail, include_stderr=include_stderr)
221 if res[0] == 0:
222 files.append(file_name)
223 return res
225 def ip(args, force=False, JSON=True, ns="", fail=True, include_stderr=False):
226 if force:
227 args = "-force " + args
228 return tool("ip", args, {"json":"-j"}, JSON=JSON, ns=ns,
229 fail=fail, include_stderr=include_stderr)
231 def tc(args, JSON=True, ns="", fail=True, include_stderr=False):
232 return tool("tc", args, {"json":"-p"}, JSON=JSON, ns=ns,
233 fail=fail, include_stderr=include_stderr)
235 def ethtool(dev, opt, args, fail=True):
236 return cmd("ethtool %s %s %s" % (opt, dev["ifname"], args), fail=fail)
238 def bpf_obj(name, sec=".text", path=bpf_test_dir,):
239 return "obj %s sec %s" % (os.path.join(path, name), sec)
241 def bpf_pinned(name):
242 return "pinned %s" % (name)
244 def bpf_bytecode(bytecode):
245 return "bytecode \"%s\"" % (bytecode)
247 def mknetns(n_retry=10):
248 for i in range(n_retry):
249 name = ''.join([random.choice(string.ascii_letters) for i in range(8)])
250 ret, _ = ip("netns add %s" % (name), fail=False)
251 if ret == 0:
252 netns.append(name)
253 return name
254 return None
256 def int2str(fmt, val):
257 ret = []
258 for b in struct.pack(fmt, val):
259 ret.append(int(b))
260 return " ".join(map(lambda x: str(x), ret))
262 def str2int(strtab):
263 inttab = []
264 for i in strtab:
265 inttab.append(int(i, 16))
266 ba = bytearray(inttab)
267 if len(strtab) == 4:
268 fmt = "I"
269 elif len(strtab) == 8:
270 fmt = "Q"
271 else:
272 raise Exception("String array of len %d can't be unpacked to an int" %
273 (len(strtab)))
274 return struct.unpack(fmt, ba)[0]
276 class DebugfsDir:
278 Class for accessing DebugFS directories as a dictionary.
281 def __init__(self, path):
282 self.path = path
283 self._dict = self._debugfs_dir_read(path)
285 def __len__(self):
286 return len(self._dict.keys())
288 def __getitem__(self, key):
289 if type(key) is int:
290 key = list(self._dict.keys())[key]
291 return self._dict[key]
293 def __setitem__(self, key, value):
294 log("DebugFS set %s = %s" % (key, value), "")
295 log_level_inc()
297 cmd("echo '%s' > %s/%s" % (value, self.path, key))
298 log_level_dec()
300 _, out = cmd('cat %s/%s' % (self.path, key))
301 self._dict[key] = out.strip()
303 def _debugfs_dir_read(self, path):
304 dfs = {}
306 log("DebugFS state for %s" % (path), "")
307 log_level_inc(add=2)
309 _, out = cmd('ls ' + path)
310 for f in out.split():
311 if f == "ports":
312 continue
314 p = os.path.join(path, f)
315 if not os.stat(p).st_mode & stat.S_IRUSR:
316 continue
318 if os.path.isfile(p):
319 # We need to init trap_flow_action_cookie before read it
320 if f == "trap_flow_action_cookie":
321 cmd('echo deadbeef > %s/%s' % (path, f))
322 _, out = cmd('cat %s/%s' % (path, f))
323 dfs[f] = out.strip()
324 elif os.path.isdir(p):
325 dfs[f] = DebugfsDir(p)
326 else:
327 raise Exception("%s is neither file nor directory" % (p))
329 log_level_dec()
330 log("DebugFS state", dfs)
331 log_level_dec()
333 return dfs
335 class NetdevSimDev:
337 Class for netdevsim bus device and its attributes.
339 @staticmethod
340 def ctrl_write(path, val):
341 fullpath = os.path.join("/sys/bus/netdevsim/", path)
342 try:
343 with open(fullpath, "w") as f:
344 f.write(val)
345 except OSError as e:
346 log("WRITE %s: %r" % (fullpath, val), -e.errno)
347 raise e
348 log("WRITE %s: %r" % (fullpath, val), 0)
350 def __init__(self, port_count=1):
351 addr = 0
352 while True:
353 try:
354 self.ctrl_write("new_device", "%u %u" % (addr, port_count))
355 except OSError as e:
356 if e.errno == errno.ENOSPC:
357 addr += 1
358 continue
359 raise e
360 break
361 self.addr = addr
363 # As probe of netdevsim device might happen from a workqueue,
364 # so wait here until all netdevs appear.
365 self.wait_for_netdevs(port_count)
367 ret, out = cmd("udevadm settle", fail=False)
368 if ret:
369 raise Exception("udevadm settle failed")
370 ifnames = self.get_ifnames()
372 devs.append(self)
373 self.dfs_dir = "/sys/kernel/debug/netdevsim/netdevsim%u/" % addr
375 self.nsims = []
376 for port_index in range(port_count):
377 self.nsims.append(NetdevSim(self, port_index, ifnames[port_index]))
379 def get_ifnames(self):
380 ifnames = []
381 listdir = os.listdir("/sys/bus/netdevsim/devices/netdevsim%u/net/" % self.addr)
382 for ifname in listdir:
383 ifnames.append(ifname)
384 ifnames.sort()
385 return ifnames
387 def wait_for_netdevs(self, port_count):
388 timeout = 5
389 timeout_start = time.time()
391 while True:
392 try:
393 ifnames = self.get_ifnames()
394 except FileNotFoundError as e:
395 ifnames = []
396 if len(ifnames) == port_count:
397 break
398 if time.time() < timeout_start + timeout:
399 continue
400 raise Exception("netdevices did not appear within timeout")
402 def dfs_num_bound_progs(self):
403 path = os.path.join(self.dfs_dir, "bpf_bound_progs")
404 _, progs = cmd('ls %s' % (path))
405 return len(progs.split())
407 def dfs_get_bound_progs(self, expected):
408 progs = DebugfsDir(os.path.join(self.dfs_dir, "bpf_bound_progs"))
409 if expected is not None:
410 if len(progs) != expected:
411 fail(True, "%d BPF programs bound, expected %d" %
412 (len(progs), expected))
413 return progs
415 def remove(self):
416 self.ctrl_write("del_device", "%u" % (self.addr, ))
417 devs.remove(self)
419 def remove_nsim(self, nsim):
420 self.nsims.remove(nsim)
421 self.ctrl_write("devices/netdevsim%u/del_port" % (self.addr, ),
422 "%u" % (nsim.port_index, ))
424 class NetdevSim:
426 Class for netdevsim netdevice and its attributes.
429 def __init__(self, nsimdev, port_index, ifname):
430 # In case udev renamed the netdev to according to new schema,
431 # check if the name matches the port_index.
432 nsimnamere = re.compile("eni\d+np(\d+)")
433 match = nsimnamere.match(ifname)
434 if match and int(match.groups()[0]) != port_index + 1:
435 raise Exception("netdevice name mismatches the expected one")
437 self.nsimdev = nsimdev
438 self.port_index = port_index
439 self.ns = ""
440 self.dfs_dir = "%s/ports/%u/" % (nsimdev.dfs_dir, port_index)
441 self.dfs_refresh()
442 _, [self.dev] = ip("link show dev %s" % ifname)
444 def __getitem__(self, key):
445 return self.dev[key]
447 def remove(self):
448 self.nsimdev.remove_nsim(self)
450 def dfs_refresh(self):
451 self.dfs = DebugfsDir(self.dfs_dir)
452 return self.dfs
454 def dfs_read(self, f):
455 path = os.path.join(self.dfs_dir, f)
456 _, data = cmd('cat %s' % (path))
457 return data.strip()
459 def wait_for_flush(self, bound=0, total=0, n_retry=20):
460 for i in range(n_retry):
461 nbound = self.nsimdev.dfs_num_bound_progs()
462 nprogs = len(bpftool_prog_list())
463 if nbound == bound and nprogs == total:
464 return
465 time.sleep(0.05)
466 raise Exception("Time out waiting for program counts to stabilize want %d/%d, have %d bound, %d loaded" % (bound, total, nbound, nprogs))
468 def set_ns(self, ns):
469 name = "1" if ns == "" else ns
470 ip("link set dev %s netns %s" % (self.dev["ifname"], name), ns=self.ns)
471 self.ns = ns
473 def set_mtu(self, mtu, fail=True):
474 return ip("link set dev %s mtu %d" % (self.dev["ifname"], mtu),
475 fail=fail)
477 def set_xdp(self, bpf, mode, force=False, JSON=True, verbose=False,
478 fail=True, include_stderr=False):
479 if verbose:
480 bpf += " verbose"
481 return ip("link set dev %s xdp%s %s" % (self.dev["ifname"], mode, bpf),
482 force=force, JSON=JSON,
483 fail=fail, include_stderr=include_stderr)
485 def unset_xdp(self, mode, force=False, JSON=True,
486 fail=True, include_stderr=False):
487 return ip("link set dev %s xdp%s off" % (self.dev["ifname"], mode),
488 force=force, JSON=JSON,
489 fail=fail, include_stderr=include_stderr)
491 def ip_link_show(self, xdp):
492 _, link = ip("link show dev %s" % (self['ifname']))
493 if len(link) > 1:
494 raise Exception("Multiple objects on ip link show")
495 if len(link) < 1:
496 return {}
497 fail(xdp != "xdp" in link,
498 "XDP program not reporting in iplink (reported %s, expected %s)" %
499 ("xdp" in link, xdp))
500 return link[0]
502 def tc_add_ingress(self):
503 tc("qdisc add dev %s ingress" % (self['ifname']))
505 def tc_del_ingress(self):
506 tc("qdisc del dev %s ingress" % (self['ifname']))
508 def tc_flush_filters(self, bound=0, total=0):
509 self.tc_del_ingress()
510 self.tc_add_ingress()
511 self.wait_for_flush(bound=bound, total=total)
513 def tc_show_ingress(self, expected=None):
514 # No JSON support, oh well...
515 flags = ["skip_sw", "skip_hw", "in_hw"]
516 named = ["protocol", "pref", "chain", "handle", "id", "tag"]
518 args = "-s filter show dev %s ingress" % (self['ifname'])
519 _, out = tc(args, JSON=False)
521 filters = []
522 lines = out.split('\n')
523 for line in lines:
524 words = line.split()
525 if "handle" not in words:
526 continue
527 fltr = {}
528 for flag in flags:
529 fltr[flag] = flag in words
530 for name in named:
531 try:
532 idx = words.index(name)
533 fltr[name] = words[idx + 1]
534 except ValueError:
535 pass
536 filters.append(fltr)
538 if expected is not None:
539 fail(len(filters) != expected,
540 "%d ingress filters loaded, expected %d" %
541 (len(filters), expected))
542 return filters
544 def cls_filter_op(self, op, qdisc="ingress", prio=None, handle=None,
545 chain=None, cls="", params="",
546 fail=True, include_stderr=False):
547 spec = ""
548 if prio is not None:
549 spec += " prio %d" % (prio)
550 if handle:
551 spec += " handle %s" % (handle)
552 if chain is not None:
553 spec += " chain %d" % (chain)
555 return tc("filter {op} dev {dev} {qdisc} {spec} {cls} {params}"\
556 .format(op=op, dev=self['ifname'], qdisc=qdisc, spec=spec,
557 cls=cls, params=params),
558 fail=fail, include_stderr=include_stderr)
560 def cls_bpf_add_filter(self, bpf, op="add", prio=None, handle=None,
561 chain=None, da=False, verbose=False,
562 skip_sw=False, skip_hw=False,
563 fail=True, include_stderr=False):
564 cls = "bpf " + bpf
566 params = ""
567 if da:
568 params += " da"
569 if verbose:
570 params += " verbose"
571 if skip_sw:
572 params += " skip_sw"
573 if skip_hw:
574 params += " skip_hw"
576 return self.cls_filter_op(op=op, prio=prio, handle=handle, cls=cls,
577 chain=chain, params=params,
578 fail=fail, include_stderr=include_stderr)
580 def set_ethtool_tc_offloads(self, enable, fail=True):
581 args = "hw-tc-offload %s" % ("on" if enable else "off")
582 return ethtool(self, "-K", args, fail=fail)
584 ################################################################################
585 def clean_up():
586 global files, netns, devs
588 for dev in devs:
589 dev.remove()
590 for f in files:
591 cmd("rm -f %s" % (f))
592 for ns in netns:
593 cmd("ip netns delete %s" % (ns))
594 files = []
595 netns = []
597 def pin_prog(file_name, idx=0):
598 progs = bpftool_prog_list(expected=(idx + 1))
599 prog = progs[idx]
600 bpftool("prog pin id %d %s" % (prog["id"], file_name))
601 files.append(file_name)
603 return file_name, bpf_pinned(file_name)
605 def pin_map(file_name, idx=0, expected=1):
606 maps = bpftool_map_list(expected=expected)
607 m = maps[idx]
608 bpftool("map pin id %d %s" % (m["id"], file_name))
609 files.append(file_name)
611 return file_name, bpf_pinned(file_name)
613 def check_dev_info_removed(prog_file=None, map_file=None):
614 bpftool_prog_list(expected=0)
615 ret, err = bpftool("prog show pin %s" % (prog_file), fail=False)
616 fail(ret == 0, "Showing prog with removed device did not fail")
617 fail(err["error"].find("No such device") == -1,
618 "Showing prog with removed device expected ENODEV, error is %s" %
619 (err["error"]))
621 bpftool_map_list(expected=0)
622 ret, err = bpftool("map show pin %s" % (map_file), fail=False)
623 fail(ret == 0, "Showing map with removed device did not fail")
624 fail(err["error"].find("No such device") == -1,
625 "Showing map with removed device expected ENODEV, error is %s" %
626 (err["error"]))
628 def check_dev_info(other_ns, ns, prog_file=None, map_file=None, removed=False):
629 progs = bpftool_prog_list(expected=1, ns=ns)
630 prog = progs[0]
632 fail("dev" not in prog.keys(), "Device parameters not reported")
633 dev = prog["dev"]
634 fail("ifindex" not in dev.keys(), "Device parameters not reported")
635 fail("ns_dev" not in dev.keys(), "Device parameters not reported")
636 fail("ns_inode" not in dev.keys(), "Device parameters not reported")
638 if not other_ns:
639 fail("ifname" not in dev.keys(), "Ifname not reported")
640 fail(dev["ifname"] != sim["ifname"],
641 "Ifname incorrect %s vs %s" % (dev["ifname"], sim["ifname"]))
642 else:
643 fail("ifname" in dev.keys(), "Ifname is reported for other ns")
645 maps = bpftool_map_list(expected=2, ns=ns)
646 for m in maps:
647 fail("dev" not in m.keys(), "Device parameters not reported")
648 fail(dev != m["dev"], "Map's device different than program's")
650 def check_extack(output, reference, args):
651 if skip_extack:
652 return
653 lines = output.split("\n")
654 comp = len(lines) >= 2 and lines[1] == 'Error: ' + reference
655 fail(not comp, "Missing or incorrect netlink extack message")
657 def check_extack_nsim(output, reference, args):
658 check_extack(output, "netdevsim: " + reference, args)
660 def check_no_extack(res, needle):
661 fail((res[1] + res[2]).count(needle) or (res[1] + res[2]).count("Warning:"),
662 "Found '%s' in command output, leaky extack?" % (needle))
664 def check_verifier_log(output, reference):
665 lines = output.split("\n")
666 for l in reversed(lines):
667 if l == reference:
668 return
669 fail(True, "Missing or incorrect message from netdevsim in verifier log")
671 def check_multi_basic(two_xdps):
672 fail(two_xdps["mode"] != 4, "Bad mode reported with multiple programs")
673 fail("prog" in two_xdps, "Base program reported in multi program mode")
674 fail(len(two_xdps["attached"]) != 2,
675 "Wrong attached program count with two programs")
676 fail(two_xdps["attached"][0]["prog"]["id"] ==
677 two_xdps["attached"][1]["prog"]["id"],
678 "Offloaded and other programs have the same id")
680 def test_spurios_extack(sim, obj, skip_hw, needle):
681 res = sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=skip_hw,
682 include_stderr=True)
683 check_no_extack(res, needle)
684 res = sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1,
685 skip_hw=skip_hw, include_stderr=True)
686 check_no_extack(res, needle)
687 res = sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf",
688 include_stderr=True)
689 check_no_extack(res, needle)
691 def test_multi_prog(simdev, sim, obj, modename, modeid):
692 start_test("Test multi-attachment XDP - %s + offload..." %
693 (modename or "default", ))
694 sim.set_xdp(obj, "offload")
695 xdp = sim.ip_link_show(xdp=True)["xdp"]
696 offloaded = sim.dfs_read("bpf_offloaded_id")
697 fail("prog" not in xdp, "Base program not reported in single program mode")
698 fail(len(xdp["attached"]) != 1,
699 "Wrong attached program count with one program")
701 sim.set_xdp(obj, modename)
702 two_xdps = sim.ip_link_show(xdp=True)["xdp"]
704 fail(xdp["attached"][0] not in two_xdps["attached"],
705 "Offload program not reported after other activated")
706 check_multi_basic(two_xdps)
708 offloaded2 = sim.dfs_read("bpf_offloaded_id")
709 fail(offloaded != offloaded2,
710 "Offload ID changed after loading other program")
712 start_test("Test multi-attachment XDP - replace...")
713 ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True)
714 fail(ret == 0, "Replaced one of programs without -force")
715 check_extack(err, "XDP program already attached.", args)
717 start_test("Test multi-attachment XDP - remove without mode...")
718 ret, _, err = sim.unset_xdp("", force=True,
719 fail=False, include_stderr=True)
720 fail(ret == 0, "Removed program without a mode flag")
721 check_extack(err, "More than one program loaded, unset mode is ambiguous.", args)
723 sim.unset_xdp("offload")
724 xdp = sim.ip_link_show(xdp=True)["xdp"]
725 offloaded = sim.dfs_read("bpf_offloaded_id")
727 fail(xdp["mode"] != modeid, "Bad mode reported after multiple programs")
728 fail("prog" not in xdp,
729 "Base program not reported after multi program mode")
730 fail(xdp["attached"][0] not in two_xdps["attached"],
731 "Offload program not reported after other activated")
732 fail(len(xdp["attached"]) != 1,
733 "Wrong attached program count with remaining programs")
734 fail(offloaded != "0", "Offload ID reported with only other program left")
736 start_test("Test multi-attachment XDP - reattach...")
737 sim.set_xdp(obj, "offload")
738 two_xdps = sim.ip_link_show(xdp=True)["xdp"]
740 fail(xdp["attached"][0] not in two_xdps["attached"],
741 "Other program not reported after offload activated")
742 check_multi_basic(two_xdps)
744 start_test("Test multi-attachment XDP - device remove...")
745 simdev.remove()
747 simdev = NetdevSimDev()
748 sim, = simdev.nsims
749 sim.set_ethtool_tc_offloads(True)
750 return [simdev, sim]
752 # Parse command line
753 parser = argparse.ArgumentParser()
754 parser.add_argument("--log", help="output verbose log to given file")
755 args = parser.parse_args()
756 if args.log:
757 logfile = open(args.log, 'w+')
758 logfile.write("# -*-Org-*-")
760 log("Prepare...", "", level=1)
761 log_level_inc()
763 # Check permissions
764 skip(os.getuid() != 0, "test must be run as root")
766 # Check tools
767 ret, progs = bpftool("prog", fail=False)
768 skip(ret != 0, "bpftool not installed")
769 base_progs = progs
770 _, base_maps = bpftool("map")
771 base_map_names = [
772 'pid_iter.rodata' # created on each bpftool invocation
775 # Check netdevsim
776 ret, out = cmd("modprobe netdevsim", fail=False)
777 skip(ret != 0, "netdevsim module could not be loaded")
779 # Check debugfs
780 _, out = cmd("mount")
781 if out.find("/sys/kernel/debug type debugfs") == -1:
782 cmd("mount -t debugfs none /sys/kernel/debug")
784 # Check samples are compiled
785 samples = ["sample_ret0.o", "sample_map_ret0.o"]
786 for s in samples:
787 ret, out = cmd("ls %s/%s" % (bpf_test_dir, s), fail=False)
788 skip(ret != 0, "sample %s/%s not found, please compile it" %
789 (bpf_test_dir, s))
791 # Check if iproute2 is built with libmnl (needed by extack support)
792 _, _, err = cmd("tc qdisc delete dev lo handle 0",
793 fail=False, include_stderr=True)
794 if err.find("Error: Failed to find qdisc with specified handle.") == -1:
795 print("Warning: no extack message in iproute2 output, libmnl missing?")
796 log("Warning: no extack message in iproute2 output, libmnl missing?", "")
797 skip_extack = True
799 # Check if net namespaces seem to work
800 ns = mknetns()
801 skip(ns is None, "Could not create a net namespace")
802 cmd("ip netns delete %s" % (ns))
803 netns = []
805 try:
806 obj = bpf_obj("sample_ret0.o")
807 bytecode = bpf_bytecode("1,6 0 0 4294967295,")
809 start_test("Test destruction of generic XDP...")
810 simdev = NetdevSimDev()
811 sim, = simdev.nsims
812 sim.set_xdp(obj, "generic")
813 simdev.remove()
814 bpftool_prog_list_wait(expected=0)
816 simdev = NetdevSimDev()
817 sim, = simdev.nsims
818 sim.tc_add_ingress()
820 start_test("Test TC non-offloaded...")
821 ret, _ = sim.cls_bpf_add_filter(obj, skip_hw=True, fail=False)
822 fail(ret != 0, "Software TC filter did not load")
824 start_test("Test TC non-offloaded isn't getting bound...")
825 ret, _ = sim.cls_bpf_add_filter(obj, fail=False)
826 fail(ret != 0, "Software TC filter did not load")
827 simdev.dfs_get_bound_progs(expected=0)
829 sim.tc_flush_filters()
831 start_test("Test TC offloads are off by default...")
832 ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True,
833 fail=False, include_stderr=True)
834 fail(ret == 0, "TC filter loaded without enabling TC offloads")
835 check_extack(err, "TC offload is disabled on net device.", args)
836 sim.wait_for_flush()
838 sim.set_ethtool_tc_offloads(True)
839 sim.dfs["bpf_tc_non_bound_accept"] = "Y"
841 start_test("Test TC offload by default...")
842 ret, _ = sim.cls_bpf_add_filter(obj, fail=False)
843 fail(ret != 0, "Software TC filter did not load")
844 simdev.dfs_get_bound_progs(expected=0)
845 ingress = sim.tc_show_ingress(expected=1)
846 fltr = ingress[0]
847 fail(not fltr["in_hw"], "Filter not offloaded by default")
849 sim.tc_flush_filters()
851 start_test("Test TC cBPF bytcode tries offload by default...")
852 ret, _ = sim.cls_bpf_add_filter(bytecode, fail=False)
853 fail(ret != 0, "Software TC filter did not load")
854 simdev.dfs_get_bound_progs(expected=0)
855 ingress = sim.tc_show_ingress(expected=1)
856 fltr = ingress[0]
857 fail(not fltr["in_hw"], "Bytecode not offloaded by default")
859 sim.tc_flush_filters()
860 sim.dfs["bpf_tc_non_bound_accept"] = "N"
862 start_test("Test TC cBPF unbound bytecode doesn't offload...")
863 ret, _, err = sim.cls_bpf_add_filter(bytecode, skip_sw=True,
864 fail=False, include_stderr=True)
865 fail(ret == 0, "TC bytecode loaded for offload")
866 check_extack_nsim(err, "netdevsim configured to reject unbound programs.",
867 args)
868 sim.wait_for_flush()
870 start_test("Test non-0 chain offload...")
871 ret, _, err = sim.cls_bpf_add_filter(obj, chain=1, prio=1, handle=1,
872 skip_sw=True,
873 fail=False, include_stderr=True)
874 fail(ret == 0, "Offloaded a filter to chain other than 0")
875 check_extack(err, "Driver supports only offload of chain 0.", args)
876 sim.tc_flush_filters()
878 start_test("Test TC replace...")
879 sim.cls_bpf_add_filter(obj, prio=1, handle=1)
880 sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1)
881 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
883 sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_sw=True)
884 sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, skip_sw=True)
885 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
887 sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=True)
888 sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, skip_hw=True)
889 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
891 start_test("Test TC replace bad flags...")
892 for i in range(3):
893 for j in range(3):
894 ret, _ = sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1,
895 skip_sw=(j == 1), skip_hw=(j == 2),
896 fail=False)
897 fail(bool(ret) != bool(j),
898 "Software TC incorrect load in replace test, iteration %d" %
899 (j))
900 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
902 start_test("Test spurious extack from the driver...")
903 test_spurios_extack(sim, obj, False, "netdevsim")
904 test_spurios_extack(sim, obj, True, "netdevsim")
906 sim.set_ethtool_tc_offloads(False)
908 test_spurios_extack(sim, obj, False, "TC offload is disabled")
909 test_spurios_extack(sim, obj, True, "TC offload is disabled")
911 sim.set_ethtool_tc_offloads(True)
913 sim.tc_flush_filters()
915 start_test("Test TC offloads failure...")
916 sim.dfs["dev/bpf_bind_verifier_accept"] = 0
917 ret, _, err = sim.cls_bpf_add_filter(obj, verbose=True, skip_sw=True,
918 fail=False, include_stderr=True)
919 fail(ret == 0, "TC filter did not reject with TC offloads enabled")
920 check_verifier_log(err, "[netdevsim] Hello from netdevsim!")
921 sim.dfs["dev/bpf_bind_verifier_accept"] = 1
923 start_test("Test TC offloads work...")
924 ret, _, err = sim.cls_bpf_add_filter(obj, verbose=True, skip_sw=True,
925 fail=False, include_stderr=True)
926 fail(ret != 0, "TC filter did not load with TC offloads enabled")
928 start_test("Test TC offload basics...")
929 dfs = simdev.dfs_get_bound_progs(expected=1)
930 progs = bpftool_prog_list(expected=1)
931 ingress = sim.tc_show_ingress(expected=1)
933 dprog = dfs[0]
934 prog = progs[0]
935 fltr = ingress[0]
936 fail(fltr["skip_hw"], "TC does reports 'skip_hw' on offloaded filter")
937 fail(not fltr["in_hw"], "TC does not report 'in_hw' for offloaded filter")
938 fail(not fltr["skip_sw"], "TC does not report 'skip_sw' back")
940 start_test("Test TC offload is device-bound...")
941 fail(str(prog["id"]) != fltr["id"], "Program IDs don't match")
942 fail(prog["tag"] != fltr["tag"], "Program tags don't match")
943 fail(fltr["id"] != dprog["id"], "Program IDs don't match")
944 fail(dprog["state"] != "xlated", "Offloaded program state not translated")
945 fail(dprog["loaded"] != "Y", "Offloaded program is not loaded")
947 start_test("Test disabling TC offloads is rejected while filters installed...")
948 ret, _ = sim.set_ethtool_tc_offloads(False, fail=False)
949 fail(ret == 0, "Driver should refuse to disable TC offloads with filters installed...")
950 sim.set_ethtool_tc_offloads(True)
952 start_test("Test qdisc removal frees things...")
953 sim.tc_flush_filters()
954 sim.tc_show_ingress(expected=0)
956 start_test("Test disabling TC offloads is OK without filters...")
957 ret, _ = sim.set_ethtool_tc_offloads(False, fail=False)
958 fail(ret != 0,
959 "Driver refused to disable TC offloads without filters installed...")
961 sim.set_ethtool_tc_offloads(True)
963 start_test("Test destroying device gets rid of TC filters...")
964 sim.cls_bpf_add_filter(obj, skip_sw=True)
965 simdev.remove()
966 bpftool_prog_list_wait(expected=0)
968 simdev = NetdevSimDev()
969 sim, = simdev.nsims
970 sim.set_ethtool_tc_offloads(True)
972 start_test("Test destroying device gets rid of XDP...")
973 sim.set_xdp(obj, "offload")
974 simdev.remove()
975 bpftool_prog_list_wait(expected=0)
977 simdev = NetdevSimDev()
978 sim, = simdev.nsims
979 sim.set_ethtool_tc_offloads(True)
981 start_test("Test XDP prog reporting...")
982 sim.set_xdp(obj, "drv")
983 ipl = sim.ip_link_show(xdp=True)
984 progs = bpftool_prog_list(expected=1)
985 fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"],
986 "Loaded program has wrong ID")
988 start_test("Test XDP prog replace without force...")
989 ret, _ = sim.set_xdp(obj, "drv", fail=False)
990 fail(ret == 0, "Replaced XDP program without -force")
991 sim.wait_for_flush(total=1)
993 start_test("Test XDP prog replace with force...")
994 ret, _ = sim.set_xdp(obj, "drv", force=True, fail=False)
995 fail(ret != 0, "Could not replace XDP program with -force")
996 bpftool_prog_list_wait(expected=1)
997 ipl = sim.ip_link_show(xdp=True)
998 progs = bpftool_prog_list(expected=1)
999 fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"],
1000 "Loaded program has wrong ID")
1001 fail("dev" in progs[0].keys(),
1002 "Device parameters reported for non-offloaded program")
1004 start_test("Test XDP prog replace with bad flags...")
1005 ret, _, err = sim.set_xdp(obj, "generic", force=True,
1006 fail=False, include_stderr=True)
1007 fail(ret == 0, "Replaced XDP program with a program in different mode")
1008 check_extack(err,
1009 "Native and generic XDP can't be active at the same time.",
1010 args)
1012 start_test("Test MTU restrictions...")
1013 ret, _ = sim.set_mtu(9000, fail=False)
1014 fail(ret == 0,
1015 "Driver should refuse to increase MTU to 9000 with XDP loaded...")
1016 sim.unset_xdp("drv")
1017 bpftool_prog_list_wait(expected=0)
1018 sim.set_mtu(9000)
1019 ret, _, err = sim.set_xdp(obj, "drv", fail=False, include_stderr=True)
1020 fail(ret == 0, "Driver should refuse to load program with MTU of 9000...")
1021 check_extack_nsim(err, "MTU too large w/ XDP enabled.", args)
1022 sim.set_mtu(1500)
1024 sim.wait_for_flush()
1025 start_test("Test non-offload XDP attaching to HW...")
1026 bpftool_prog_load("sample_ret0.o", "/sys/fs/bpf/nooffload")
1027 nooffload = bpf_pinned("/sys/fs/bpf/nooffload")
1028 ret, _, err = sim.set_xdp(nooffload, "offload",
1029 fail=False, include_stderr=True)
1030 fail(ret == 0, "attached non-offloaded XDP program to HW")
1031 check_extack_nsim(err, "xdpoffload of non-bound program.", args)
1032 rm("/sys/fs/bpf/nooffload")
1034 start_test("Test offload XDP attaching to drv...")
1035 bpftool_prog_load("sample_ret0.o", "/sys/fs/bpf/offload",
1036 dev=sim['ifname'])
1037 offload = bpf_pinned("/sys/fs/bpf/offload")
1038 ret, _, err = sim.set_xdp(offload, "drv", fail=False, include_stderr=True)
1039 fail(ret == 0, "attached offloaded XDP program to drv")
1040 check_extack(err, "Using device-bound program without HW_MODE flag is not supported.", args)
1041 rm("/sys/fs/bpf/offload")
1042 sim.wait_for_flush()
1044 start_test("Test XDP load failure...")
1045 sim.dfs["dev/bpf_bind_verifier_accept"] = 0
1046 ret, _, err = bpftool_prog_load("sample_ret0.o", "/sys/fs/bpf/offload",
1047 dev=sim['ifname'], fail=False, include_stderr=True)
1048 fail(ret == 0, "verifier should fail on load")
1049 check_verifier_log(err, "[netdevsim] Hello from netdevsim!")
1050 sim.dfs["dev/bpf_bind_verifier_accept"] = 1
1051 sim.wait_for_flush()
1053 start_test("Test XDP offload...")
1054 _, _, err = sim.set_xdp(obj, "offload", verbose=True, include_stderr=True)
1055 ipl = sim.ip_link_show(xdp=True)
1056 link_xdp = ipl["xdp"]["prog"]
1057 progs = bpftool_prog_list(expected=1)
1058 prog = progs[0]
1059 fail(link_xdp["id"] != prog["id"], "Loaded program has wrong ID")
1061 start_test("Test XDP offload is device bound...")
1062 dfs = simdev.dfs_get_bound_progs(expected=1)
1063 dprog = dfs[0]
1065 fail(prog["id"] != link_xdp["id"], "Program IDs don't match")
1066 fail(prog["tag"] != link_xdp["tag"], "Program tags don't match")
1067 fail(str(link_xdp["id"]) != dprog["id"], "Program IDs don't match")
1068 fail(dprog["state"] != "xlated", "Offloaded program state not translated")
1069 fail(dprog["loaded"] != "Y", "Offloaded program is not loaded")
1071 start_test("Test removing XDP program many times...")
1072 sim.unset_xdp("offload")
1073 sim.unset_xdp("offload")
1074 sim.unset_xdp("drv")
1075 sim.unset_xdp("drv")
1076 sim.unset_xdp("")
1077 sim.unset_xdp("")
1078 bpftool_prog_list_wait(expected=0)
1080 start_test("Test attempt to use a program for a wrong device...")
1081 simdev2 = NetdevSimDev()
1082 sim2, = simdev2.nsims
1083 sim2.set_xdp(obj, "offload")
1084 pin_file, pinned = pin_prog("/sys/fs/bpf/tmp")
1086 ret, _, err = sim.set_xdp(pinned, "offload",
1087 fail=False, include_stderr=True)
1088 fail(ret == 0, "Pinned program loaded for a different device accepted")
1089 check_extack_nsim(err, "program bound to different dev.", args)
1090 simdev2.remove()
1091 ret, _, err = sim.set_xdp(pinned, "offload",
1092 fail=False, include_stderr=True)
1093 fail(ret == 0, "Pinned program loaded for a removed device accepted")
1094 check_extack_nsim(err, "xdpoffload of non-bound program.", args)
1095 rm(pin_file)
1096 bpftool_prog_list_wait(expected=0)
1098 simdev, sim = test_multi_prog(simdev, sim, obj, "", 1)
1099 simdev, sim = test_multi_prog(simdev, sim, obj, "drv", 1)
1100 simdev, sim = test_multi_prog(simdev, sim, obj, "generic", 2)
1102 start_test("Test mixing of TC and XDP...")
1103 sim.tc_add_ingress()
1104 sim.set_xdp(obj, "offload")
1105 ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True,
1106 fail=False, include_stderr=True)
1107 fail(ret == 0, "Loading TC when XDP active should fail")
1108 check_extack_nsim(err, "driver and netdev offload states mismatch.", args)
1109 sim.unset_xdp("offload")
1110 sim.wait_for_flush()
1112 sim.cls_bpf_add_filter(obj, skip_sw=True)
1113 ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True)
1114 fail(ret == 0, "Loading XDP when TC active should fail")
1115 check_extack_nsim(err, "TC program is already loaded.", args)
1117 start_test("Test binding TC from pinned...")
1118 pin_file, pinned = pin_prog("/sys/fs/bpf/tmp")
1119 sim.tc_flush_filters(bound=1, total=1)
1120 sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True)
1121 sim.tc_flush_filters(bound=1, total=1)
1123 start_test("Test binding XDP from pinned...")
1124 sim.set_xdp(obj, "offload")
1125 pin_file, pinned = pin_prog("/sys/fs/bpf/tmp2", idx=1)
1127 sim.set_xdp(pinned, "offload", force=True)
1128 sim.unset_xdp("offload")
1129 sim.set_xdp(pinned, "offload", force=True)
1130 sim.unset_xdp("offload")
1132 start_test("Test offload of wrong type fails...")
1133 ret, _ = sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True, fail=False)
1134 fail(ret == 0, "Managed to attach XDP program to TC")
1136 start_test("Test asking for TC offload of two filters...")
1137 sim.cls_bpf_add_filter(obj, da=True, skip_sw=True)
1138 ret, _, err = sim.cls_bpf_add_filter(obj, da=True, skip_sw=True,
1139 fail=False, include_stderr=True)
1140 fail(ret == 0, "Managed to offload two TC filters at the same time")
1141 check_extack_nsim(err, "driver and netdev offload states mismatch.", args)
1143 sim.tc_flush_filters(bound=2, total=2)
1145 start_test("Test if netdev removal waits for translation...")
1146 delay_msec = 500
1147 sim.dfs["dev/bpf_bind_verifier_delay"] = delay_msec
1148 start = time.time()
1149 cmd_line = "tc filter add dev %s ingress bpf %s da skip_sw" % \
1150 (sim['ifname'], obj)
1151 tc_proc = cmd(cmd_line, background=True, fail=False)
1152 # Wait for the verifier to start
1153 while simdev.dfs_num_bound_progs() <= 2:
1154 pass
1155 simdev.remove()
1156 end = time.time()
1157 ret, _ = cmd_result(tc_proc, fail=False)
1158 time_diff = end - start
1159 log("Time", "start:\t%s\nend:\t%s\ndiff:\t%s" % (start, end, time_diff))
1161 fail(ret == 0, "Managed to load TC filter on a unregistering device")
1162 delay_sec = delay_msec * 0.001
1163 fail(time_diff < delay_sec, "Removal process took %s, expected %s" %
1164 (time_diff, delay_sec))
1166 # Remove all pinned files and reinstantiate the netdev
1167 clean_up()
1168 bpftool_prog_list_wait(expected=0)
1170 simdev = NetdevSimDev()
1171 sim, = simdev.nsims
1172 map_obj = bpf_obj("sample_map_ret0.o")
1173 start_test("Test loading program with maps...")
1174 sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
1176 start_test("Test bpftool bound info reporting (own ns)...")
1177 check_dev_info(False, "")
1179 start_test("Test bpftool bound info reporting (other ns)...")
1180 ns = mknetns()
1181 sim.set_ns(ns)
1182 check_dev_info(True, "")
1184 start_test("Test bpftool bound info reporting (remote ns)...")
1185 check_dev_info(False, ns)
1187 start_test("Test bpftool bound info reporting (back to own ns)...")
1188 sim.set_ns("")
1189 check_dev_info(False, "")
1191 prog_file, _ = pin_prog("/sys/fs/bpf/tmp_prog")
1192 map_file, _ = pin_map("/sys/fs/bpf/tmp_map", idx=1, expected=2)
1193 simdev.remove()
1195 start_test("Test bpftool bound info reporting (removed dev)...")
1196 check_dev_info_removed(prog_file=prog_file, map_file=map_file)
1198 # Remove all pinned files and reinstantiate the netdev
1199 clean_up()
1200 bpftool_prog_list_wait(expected=0)
1202 simdev = NetdevSimDev()
1203 sim, = simdev.nsims
1205 start_test("Test map update (no flags)...")
1206 sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
1207 maps = bpftool_map_list(expected=2)
1208 array = maps[0] if maps[0]["type"] == "array" else maps[1]
1209 htab = maps[0] if maps[0]["type"] == "hash" else maps[1]
1210 for m in maps:
1211 for i in range(2):
1212 bpftool("map update id %d key %s value %s" %
1213 (m["id"], int2str("I", i), int2str("Q", i * 3)))
1215 for m in maps:
1216 ret, _ = bpftool("map update id %d key %s value %s" %
1217 (m["id"], int2str("I", 3), int2str("Q", 3 * 3)),
1218 fail=False)
1219 fail(ret == 0, "added too many entries")
1221 start_test("Test map update (exists)...")
1222 for m in maps:
1223 for i in range(2):
1224 bpftool("map update id %d key %s value %s exist" %
1225 (m["id"], int2str("I", i), int2str("Q", i * 3)))
1227 for m in maps:
1228 ret, err = bpftool("map update id %d key %s value %s exist" %
1229 (m["id"], int2str("I", 3), int2str("Q", 3 * 3)),
1230 fail=False)
1231 fail(ret == 0, "updated non-existing key")
1232 fail(err["error"].find("No such file or directory") == -1,
1233 "expected ENOENT, error is '%s'" % (err["error"]))
1235 start_test("Test map update (noexist)...")
1236 for m in maps:
1237 for i in range(2):
1238 ret, err = bpftool("map update id %d key %s value %s noexist" %
1239 (m["id"], int2str("I", i), int2str("Q", i * 3)),
1240 fail=False)
1241 fail(ret == 0, "updated existing key")
1242 fail(err["error"].find("File exists") == -1,
1243 "expected EEXIST, error is '%s'" % (err["error"]))
1245 start_test("Test map dump...")
1246 for m in maps:
1247 _, entries = bpftool("map dump id %d" % (m["id"]))
1248 for i in range(2):
1249 key = str2int(entries[i]["key"])
1250 fail(key != i, "expected key %d, got %d" % (key, i))
1251 val = str2int(entries[i]["value"])
1252 fail(val != i * 3, "expected value %d, got %d" % (val, i * 3))
1254 start_test("Test map getnext...")
1255 for m in maps:
1256 _, entry = bpftool("map getnext id %d" % (m["id"]))
1257 key = str2int(entry["next_key"])
1258 fail(key != 0, "next key %d, expected %d" % (key, 0))
1259 _, entry = bpftool("map getnext id %d key %s" %
1260 (m["id"], int2str("I", 0)))
1261 key = str2int(entry["next_key"])
1262 fail(key != 1, "next key %d, expected %d" % (key, 1))
1263 ret, err = bpftool("map getnext id %d key %s" %
1264 (m["id"], int2str("I", 1)), fail=False)
1265 fail(ret == 0, "got next key past the end of map")
1266 fail(err["error"].find("No such file or directory") == -1,
1267 "expected ENOENT, error is '%s'" % (err["error"]))
1269 start_test("Test map delete (htab)...")
1270 for i in range(2):
1271 bpftool("map delete id %d key %s" % (htab["id"], int2str("I", i)))
1273 start_test("Test map delete (array)...")
1274 for i in range(2):
1275 ret, err = bpftool("map delete id %d key %s" %
1276 (htab["id"], int2str("I", i)), fail=False)
1277 fail(ret == 0, "removed entry from an array")
1278 fail(err["error"].find("No such file or directory") == -1,
1279 "expected ENOENT, error is '%s'" % (err["error"]))
1281 start_test("Test map remove...")
1282 sim.unset_xdp("offload")
1283 bpftool_map_list_wait(expected=0)
1284 simdev.remove()
1286 simdev = NetdevSimDev()
1287 sim, = simdev.nsims
1288 sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
1289 simdev.remove()
1290 bpftool_map_list_wait(expected=0)
1292 start_test("Test map creation fail path...")
1293 simdev = NetdevSimDev()
1294 sim, = simdev.nsims
1295 sim.dfs["bpf_map_accept"] = "N"
1296 ret, _ = sim.set_xdp(map_obj, "offload", JSON=False, fail=False)
1297 fail(ret == 0,
1298 "netdevsim didn't refuse to create a map with offload disabled")
1300 simdev.remove()
1302 start_test("Test multi-dev ASIC program reuse...")
1303 simdevA = NetdevSimDev()
1304 simA, = simdevA.nsims
1305 simdevB = NetdevSimDev(3)
1306 simB1, simB2, simB3 = simdevB.nsims
1307 sims = (simA, simB1, simB2, simB3)
1308 simB = (simB1, simB2, simB3)
1310 bpftool_prog_load("sample_map_ret0.o", "/sys/fs/bpf/nsimA",
1311 dev=simA['ifname'])
1312 progA = bpf_pinned("/sys/fs/bpf/nsimA")
1313 bpftool_prog_load("sample_map_ret0.o", "/sys/fs/bpf/nsimB",
1314 dev=simB1['ifname'])
1315 progB = bpf_pinned("/sys/fs/bpf/nsimB")
1317 simA.set_xdp(progA, "offload", JSON=False)
1318 for d in simdevB.nsims:
1319 d.set_xdp(progB, "offload", JSON=False)
1321 start_test("Test multi-dev ASIC cross-dev replace...")
1322 ret, _ = simA.set_xdp(progB, "offload", force=True, JSON=False, fail=False)
1323 fail(ret == 0, "cross-ASIC program allowed")
1324 for d in simdevB.nsims:
1325 ret, _ = d.set_xdp(progA, "offload", force=True, JSON=False, fail=False)
1326 fail(ret == 0, "cross-ASIC program allowed")
1328 start_test("Test multi-dev ASIC cross-dev install...")
1329 for d in sims:
1330 d.unset_xdp("offload")
1332 ret, _, err = simA.set_xdp(progB, "offload", force=True, JSON=False,
1333 fail=False, include_stderr=True)
1334 fail(ret == 0, "cross-ASIC program allowed")
1335 check_extack_nsim(err, "program bound to different dev.", args)
1336 for d in simdevB.nsims:
1337 ret, _, err = d.set_xdp(progA, "offload", force=True, JSON=False,
1338 fail=False, include_stderr=True)
1339 fail(ret == 0, "cross-ASIC program allowed")
1340 check_extack_nsim(err, "program bound to different dev.", args)
1342 start_test("Test multi-dev ASIC cross-dev map reuse...")
1344 mapA = bpftool("prog show %s" % (progA))[1]["map_ids"][0]
1345 mapB = bpftool("prog show %s" % (progB))[1]["map_ids"][0]
1347 ret, _ = bpftool_prog_load("sample_map_ret0.o", "/sys/fs/bpf/nsimB_",
1348 dev=simB3['ifname'],
1349 maps=["idx 0 id %d" % (mapB)],
1350 fail=False)
1351 fail(ret != 0, "couldn't reuse a map on the same ASIC")
1352 rm("/sys/fs/bpf/nsimB_")
1354 ret, _, err = bpftool_prog_load("sample_map_ret0.o", "/sys/fs/bpf/nsimA_",
1355 dev=simA['ifname'],
1356 maps=["idx 0 id %d" % (mapB)],
1357 fail=False, include_stderr=True)
1358 fail(ret == 0, "could reuse a map on a different ASIC")
1359 fail(err.count("offload device mismatch between prog and map") == 0,
1360 "error message missing for cross-ASIC map")
1362 ret, _, err = bpftool_prog_load("sample_map_ret0.o", "/sys/fs/bpf/nsimB_",
1363 dev=simB1['ifname'],
1364 maps=["idx 0 id %d" % (mapA)],
1365 fail=False, include_stderr=True)
1366 fail(ret == 0, "could reuse a map on a different ASIC")
1367 fail(err.count("offload device mismatch between prog and map") == 0,
1368 "error message missing for cross-ASIC map")
1370 start_test("Test multi-dev ASIC cross-dev destruction...")
1371 bpftool_prog_list_wait(expected=2)
1373 simdevA.remove()
1374 bpftool_prog_list_wait(expected=1)
1376 ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"]
1377 fail(ifnameB != simB1['ifname'], "program not bound to original device")
1378 simB1.remove()
1379 bpftool_prog_list_wait(expected=1)
1381 start_test("Test multi-dev ASIC cross-dev destruction - move...")
1382 ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"]
1383 fail(ifnameB not in (simB2['ifname'], simB3['ifname']),
1384 "program not bound to remaining devices")
1386 simB2.remove()
1387 ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"]
1388 fail(ifnameB != simB3['ifname'], "program not bound to remaining device")
1390 simB3.remove()
1391 simdevB.remove()
1392 bpftool_prog_list_wait(expected=0)
1394 start_test("Test multi-dev ASIC cross-dev destruction - orphaned...")
1395 ret, out = bpftool("prog show %s" % (progB), fail=False)
1396 fail(ret == 0, "got information about orphaned program")
1397 fail("error" not in out, "no error reported for get info on orphaned")
1398 fail(out["error"] != "can't get prog info: No such device",
1399 "wrong error for get info on orphaned")
1401 print("%s: OK" % (os.path.basename(__file__)))
1403 finally:
1404 log("Clean up...", "", level=1)
1405 log_level_inc()
1406 clean_up()