treewide: remove redundant IS_ERR() before error code check
[linux/fpc-iii.git] / tools / testing / selftests / bpf / test_offload.py
blob8294ae3ffb3cbe4197a9c2b90bc531a41b903fe6
1 #!/usr/bin/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 for m in base_maps:
188 if m in maps:
189 maps.remove(m)
190 if expected is not None:
191 if len(maps) != expected:
192 fail(True, "%d BPF maps loaded, expected %d" %
193 (len(maps), expected))
194 return maps
196 def bpftool_prog_list_wait(expected=0, n_retry=20):
197 for i in range(n_retry):
198 nprogs = len(bpftool_prog_list())
199 if nprogs == expected:
200 return
201 time.sleep(0.05)
202 raise Exception("Time out waiting for program counts to stabilize want %d, have %d" % (expected, nprogs))
204 def bpftool_map_list_wait(expected=0, n_retry=20):
205 for i in range(n_retry):
206 nmaps = len(bpftool_map_list())
207 if nmaps == expected:
208 return
209 time.sleep(0.05)
210 raise Exception("Time out waiting for map counts to stabilize want %d, have %d" % (expected, nmaps))
212 def bpftool_prog_load(sample, file_name, maps=[], prog_type="xdp", dev=None,
213 fail=True, include_stderr=False):
214 args = "prog load %s %s" % (os.path.join(bpf_test_dir, sample), file_name)
215 if prog_type is not None:
216 args += " type " + prog_type
217 if dev is not None:
218 args += " dev " + dev
219 if len(maps):
220 args += " map " + " map ".join(maps)
222 res = bpftool(args, fail=fail, include_stderr=include_stderr)
223 if res[0] == 0:
224 files.append(file_name)
225 return res
227 def ip(args, force=False, JSON=True, ns="", fail=True, include_stderr=False):
228 if force:
229 args = "-force " + args
230 return tool("ip", args, {"json":"-j"}, JSON=JSON, ns=ns,
231 fail=fail, include_stderr=include_stderr)
233 def tc(args, JSON=True, ns="", fail=True, include_stderr=False):
234 return tool("tc", args, {"json":"-p"}, JSON=JSON, ns=ns,
235 fail=fail, include_stderr=include_stderr)
237 def ethtool(dev, opt, args, fail=True):
238 return cmd("ethtool %s %s %s" % (opt, dev["ifname"], args), fail=fail)
240 def bpf_obj(name, sec=".text", path=bpf_test_dir,):
241 return "obj %s sec %s" % (os.path.join(path, name), sec)
243 def bpf_pinned(name):
244 return "pinned %s" % (name)
246 def bpf_bytecode(bytecode):
247 return "bytecode \"%s\"" % (bytecode)
249 def mknetns(n_retry=10):
250 for i in range(n_retry):
251 name = ''.join([random.choice(string.ascii_letters) for i in range(8)])
252 ret, _ = ip("netns add %s" % (name), fail=False)
253 if ret == 0:
254 netns.append(name)
255 return name
256 return None
258 def int2str(fmt, val):
259 ret = []
260 for b in struct.pack(fmt, val):
261 ret.append(int(b))
262 return " ".join(map(lambda x: str(x), ret))
264 def str2int(strtab):
265 inttab = []
266 for i in strtab:
267 inttab.append(int(i, 16))
268 ba = bytearray(inttab)
269 if len(strtab) == 4:
270 fmt = "I"
271 elif len(strtab) == 8:
272 fmt = "Q"
273 else:
274 raise Exception("String array of len %d can't be unpacked to an int" %
275 (len(strtab)))
276 return struct.unpack(fmt, ba)[0]
278 class DebugfsDir:
280 Class for accessing DebugFS directories as a dictionary.
283 def __init__(self, path):
284 self.path = path
285 self._dict = self._debugfs_dir_read(path)
287 def __len__(self):
288 return len(self._dict.keys())
290 def __getitem__(self, key):
291 if type(key) is int:
292 key = list(self._dict.keys())[key]
293 return self._dict[key]
295 def __setitem__(self, key, value):
296 log("DebugFS set %s = %s" % (key, value), "")
297 log_level_inc()
299 cmd("echo '%s' > %s/%s" % (value, self.path, key))
300 log_level_dec()
302 _, out = cmd('cat %s/%s' % (self.path, key))
303 self._dict[key] = out.strip()
305 def _debugfs_dir_read(self, path):
306 dfs = {}
308 log("DebugFS state for %s" % (path), "")
309 log_level_inc(add=2)
311 _, out = cmd('ls ' + path)
312 for f in out.split():
313 if f == "ports":
314 continue
316 p = os.path.join(path, f)
317 if not os.stat(p).st_mode & stat.S_IRUSR:
318 continue
320 if os.path.isfile(p):
321 _, out = cmd('cat %s/%s' % (path, f))
322 dfs[f] = out.strip()
323 elif os.path.isdir(p):
324 dfs[f] = DebugfsDir(p)
325 else:
326 raise Exception("%s is neither file nor directory" % (p))
328 log_level_dec()
329 log("DebugFS state", dfs)
330 log_level_dec()
332 return dfs
334 class NetdevSimDev:
336 Class for netdevsim bus device and its attributes.
338 @staticmethod
339 def ctrl_write(path, val):
340 fullpath = os.path.join("/sys/bus/netdevsim/", path)
341 try:
342 with open(fullpath, "w") as f:
343 f.write(val)
344 except OSError as e:
345 log("WRITE %s: %r" % (fullpath, val), -e.errno)
346 raise e
347 log("WRITE %s: %r" % (fullpath, val), 0)
349 def __init__(self, port_count=1):
350 addr = 0
351 while True:
352 try:
353 self.ctrl_write("new_device", "%u %u" % (addr, port_count))
354 except OSError as e:
355 if e.errno == errno.ENOSPC:
356 addr += 1
357 continue
358 raise e
359 break
360 self.addr = addr
362 # As probe of netdevsim device might happen from a workqueue,
363 # so wait here until all netdevs appear.
364 self.wait_for_netdevs(port_count)
366 ret, out = cmd("udevadm settle", fail=False)
367 if ret:
368 raise Exception("udevadm settle failed")
369 ifnames = self.get_ifnames()
371 devs.append(self)
372 self.dfs_dir = "/sys/kernel/debug/netdevsim/netdevsim%u/" % addr
374 self.nsims = []
375 for port_index in range(port_count):
376 self.nsims.append(NetdevSim(self, port_index, ifnames[port_index]))
378 def get_ifnames(self):
379 ifnames = []
380 listdir = os.listdir("/sys/bus/netdevsim/devices/netdevsim%u/net/" % self.addr)
381 for ifname in listdir:
382 ifnames.append(ifname)
383 ifnames.sort()
384 return ifnames
386 def wait_for_netdevs(self, port_count):
387 timeout = 5
388 timeout_start = time.time()
390 while True:
391 try:
392 ifnames = self.get_ifnames()
393 except FileNotFoundError as e:
394 ifnames = []
395 if len(ifnames) == port_count:
396 break
397 if time.time() < timeout_start + timeout:
398 continue
399 raise Exception("netdevices did not appear within timeout")
401 def dfs_num_bound_progs(self):
402 path = os.path.join(self.dfs_dir, "bpf_bound_progs")
403 _, progs = cmd('ls %s' % (path))
404 return len(progs.split())
406 def dfs_get_bound_progs(self, expected):
407 progs = DebugfsDir(os.path.join(self.dfs_dir, "bpf_bound_progs"))
408 if expected is not None:
409 if len(progs) != expected:
410 fail(True, "%d BPF programs bound, expected %d" %
411 (len(progs), expected))
412 return progs
414 def remove(self):
415 self.ctrl_write("del_device", "%u" % (self.addr, ))
416 devs.remove(self)
418 def remove_nsim(self, nsim):
419 self.nsims.remove(nsim)
420 self.ctrl_write("devices/netdevsim%u/del_port" % (self.addr, ),
421 "%u" % (nsim.port_index, ))
423 class NetdevSim:
425 Class for netdevsim netdevice and its attributes.
428 def __init__(self, nsimdev, port_index, ifname):
429 # In case udev renamed the netdev to according to new schema,
430 # check if the name matches the port_index.
431 nsimnamere = re.compile("eni\d+np(\d+)")
432 match = nsimnamere.match(ifname)
433 if match and int(match.groups()[0]) != port_index + 1:
434 raise Exception("netdevice name mismatches the expected one")
436 self.nsimdev = nsimdev
437 self.port_index = port_index
438 self.ns = ""
439 self.dfs_dir = "%s/ports/%u/" % (nsimdev.dfs_dir, port_index)
440 self.dfs_refresh()
441 _, [self.dev] = ip("link show dev %s" % ifname)
443 def __getitem__(self, key):
444 return self.dev[key]
446 def remove(self):
447 self.nsimdev.remove_nsim(self)
449 def dfs_refresh(self):
450 self.dfs = DebugfsDir(self.dfs_dir)
451 return self.dfs
453 def dfs_read(self, f):
454 path = os.path.join(self.dfs_dir, f)
455 _, data = cmd('cat %s' % (path))
456 return data.strip()
458 def wait_for_flush(self, bound=0, total=0, n_retry=20):
459 for i in range(n_retry):
460 nbound = self.nsimdev.dfs_num_bound_progs()
461 nprogs = len(bpftool_prog_list())
462 if nbound == bound and nprogs == total:
463 return
464 time.sleep(0.05)
465 raise Exception("Time out waiting for program counts to stabilize want %d/%d, have %d bound, %d loaded" % (bound, total, nbound, nprogs))
467 def set_ns(self, ns):
468 name = "1" if ns == "" else ns
469 ip("link set dev %s netns %s" % (self.dev["ifname"], name), ns=self.ns)
470 self.ns = ns
472 def set_mtu(self, mtu, fail=True):
473 return ip("link set dev %s mtu %d" % (self.dev["ifname"], mtu),
474 fail=fail)
476 def set_xdp(self, bpf, mode, force=False, JSON=True, verbose=False,
477 fail=True, include_stderr=False):
478 if verbose:
479 bpf += " verbose"
480 return ip("link set dev %s xdp%s %s" % (self.dev["ifname"], mode, bpf),
481 force=force, JSON=JSON,
482 fail=fail, include_stderr=include_stderr)
484 def unset_xdp(self, mode, force=False, JSON=True,
485 fail=True, include_stderr=False):
486 return ip("link set dev %s xdp%s off" % (self.dev["ifname"], mode),
487 force=force, JSON=JSON,
488 fail=fail, include_stderr=include_stderr)
490 def ip_link_show(self, xdp):
491 _, link = ip("link show dev %s" % (self['ifname']))
492 if len(link) > 1:
493 raise Exception("Multiple objects on ip link show")
494 if len(link) < 1:
495 return {}
496 fail(xdp != "xdp" in link,
497 "XDP program not reporting in iplink (reported %s, expected %s)" %
498 ("xdp" in link, xdp))
499 return link[0]
501 def tc_add_ingress(self):
502 tc("qdisc add dev %s ingress" % (self['ifname']))
504 def tc_del_ingress(self):
505 tc("qdisc del dev %s ingress" % (self['ifname']))
507 def tc_flush_filters(self, bound=0, total=0):
508 self.tc_del_ingress()
509 self.tc_add_ingress()
510 self.wait_for_flush(bound=bound, total=total)
512 def tc_show_ingress(self, expected=None):
513 # No JSON support, oh well...
514 flags = ["skip_sw", "skip_hw", "in_hw"]
515 named = ["protocol", "pref", "chain", "handle", "id", "tag"]
517 args = "-s filter show dev %s ingress" % (self['ifname'])
518 _, out = tc(args, JSON=False)
520 filters = []
521 lines = out.split('\n')
522 for line in lines:
523 words = line.split()
524 if "handle" not in words:
525 continue
526 fltr = {}
527 for flag in flags:
528 fltr[flag] = flag in words
529 for name in named:
530 try:
531 idx = words.index(name)
532 fltr[name] = words[idx + 1]
533 except ValueError:
534 pass
535 filters.append(fltr)
537 if expected is not None:
538 fail(len(filters) != expected,
539 "%d ingress filters loaded, expected %d" %
540 (len(filters), expected))
541 return filters
543 def cls_filter_op(self, op, qdisc="ingress", prio=None, handle=None,
544 chain=None, cls="", params="",
545 fail=True, include_stderr=False):
546 spec = ""
547 if prio is not None:
548 spec += " prio %d" % (prio)
549 if handle:
550 spec += " handle %s" % (handle)
551 if chain is not None:
552 spec += " chain %d" % (chain)
554 return tc("filter {op} dev {dev} {qdisc} {spec} {cls} {params}"\
555 .format(op=op, dev=self['ifname'], qdisc=qdisc, spec=spec,
556 cls=cls, params=params),
557 fail=fail, include_stderr=include_stderr)
559 def cls_bpf_add_filter(self, bpf, op="add", prio=None, handle=None,
560 chain=None, da=False, verbose=False,
561 skip_sw=False, skip_hw=False,
562 fail=True, include_stderr=False):
563 cls = "bpf " + bpf
565 params = ""
566 if da:
567 params += " da"
568 if verbose:
569 params += " verbose"
570 if skip_sw:
571 params += " skip_sw"
572 if skip_hw:
573 params += " skip_hw"
575 return self.cls_filter_op(op=op, prio=prio, handle=handle, cls=cls,
576 chain=chain, params=params,
577 fail=fail, include_stderr=include_stderr)
579 def set_ethtool_tc_offloads(self, enable, fail=True):
580 args = "hw-tc-offload %s" % ("on" if enable else "off")
581 return ethtool(self, "-K", args, fail=fail)
583 ################################################################################
584 def clean_up():
585 global files, netns, devs
587 for dev in devs:
588 dev.remove()
589 for f in files:
590 cmd("rm -f %s" % (f))
591 for ns in netns:
592 cmd("ip netns delete %s" % (ns))
593 files = []
594 netns = []
596 def pin_prog(file_name, idx=0):
597 progs = bpftool_prog_list(expected=(idx + 1))
598 prog = progs[idx]
599 bpftool("prog pin id %d %s" % (prog["id"], file_name))
600 files.append(file_name)
602 return file_name, bpf_pinned(file_name)
604 def pin_map(file_name, idx=0, expected=1):
605 maps = bpftool_map_list(expected=expected)
606 m = maps[idx]
607 bpftool("map pin id %d %s" % (m["id"], file_name))
608 files.append(file_name)
610 return file_name, bpf_pinned(file_name)
612 def check_dev_info_removed(prog_file=None, map_file=None):
613 bpftool_prog_list(expected=0)
614 ret, err = bpftool("prog show pin %s" % (prog_file), fail=False)
615 fail(ret == 0, "Showing prog with removed device did not fail")
616 fail(err["error"].find("No such device") == -1,
617 "Showing prog with removed device expected ENODEV, error is %s" %
618 (err["error"]))
620 bpftool_map_list(expected=0)
621 ret, err = bpftool("map show pin %s" % (map_file), fail=False)
622 fail(ret == 0, "Showing map with removed device did not fail")
623 fail(err["error"].find("No such device") == -1,
624 "Showing map with removed device expected ENODEV, error is %s" %
625 (err["error"]))
627 def check_dev_info(other_ns, ns, prog_file=None, map_file=None, removed=False):
628 progs = bpftool_prog_list(expected=1, ns=ns)
629 prog = progs[0]
631 fail("dev" not in prog.keys(), "Device parameters not reported")
632 dev = prog["dev"]
633 fail("ifindex" not in dev.keys(), "Device parameters not reported")
634 fail("ns_dev" not in dev.keys(), "Device parameters not reported")
635 fail("ns_inode" not in dev.keys(), "Device parameters not reported")
637 if not other_ns:
638 fail("ifname" not in dev.keys(), "Ifname not reported")
639 fail(dev["ifname"] != sim["ifname"],
640 "Ifname incorrect %s vs %s" % (dev["ifname"], sim["ifname"]))
641 else:
642 fail("ifname" in dev.keys(), "Ifname is reported for other ns")
644 maps = bpftool_map_list(expected=2, ns=ns)
645 for m in maps:
646 fail("dev" not in m.keys(), "Device parameters not reported")
647 fail(dev != m["dev"], "Map's device different than program's")
649 def check_extack(output, reference, args):
650 if skip_extack:
651 return
652 lines = output.split("\n")
653 comp = len(lines) >= 2 and lines[1] == 'Error: ' + reference
654 fail(not comp, "Missing or incorrect netlink extack message")
656 def check_extack_nsim(output, reference, args):
657 check_extack(output, "netdevsim: " + reference, args)
659 def check_no_extack(res, needle):
660 fail((res[1] + res[2]).count(needle) or (res[1] + res[2]).count("Warning:"),
661 "Found '%s' in command output, leaky extack?" % (needle))
663 def check_verifier_log(output, reference):
664 lines = output.split("\n")
665 for l in reversed(lines):
666 if l == reference:
667 return
668 fail(True, "Missing or incorrect message from netdevsim in verifier log")
670 def check_multi_basic(two_xdps):
671 fail(two_xdps["mode"] != 4, "Bad mode reported with multiple programs")
672 fail("prog" in two_xdps, "Base program reported in multi program mode")
673 fail(len(two_xdps["attached"]) != 2,
674 "Wrong attached program count with two programs")
675 fail(two_xdps["attached"][0]["prog"]["id"] ==
676 two_xdps["attached"][1]["prog"]["id"],
677 "Offloaded and other programs have the same id")
679 def test_spurios_extack(sim, obj, skip_hw, needle):
680 res = sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=skip_hw,
681 include_stderr=True)
682 check_no_extack(res, needle)
683 res = sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1,
684 skip_hw=skip_hw, include_stderr=True)
685 check_no_extack(res, needle)
686 res = sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf",
687 include_stderr=True)
688 check_no_extack(res, needle)
690 def test_multi_prog(simdev, sim, obj, modename, modeid):
691 start_test("Test multi-attachment XDP - %s + offload..." %
692 (modename or "default", ))
693 sim.set_xdp(obj, "offload")
694 xdp = sim.ip_link_show(xdp=True)["xdp"]
695 offloaded = sim.dfs_read("bpf_offloaded_id")
696 fail("prog" not in xdp, "Base program not reported in single program mode")
697 fail(len(xdp["attached"]) != 1,
698 "Wrong attached program count with one program")
700 sim.set_xdp(obj, modename)
701 two_xdps = sim.ip_link_show(xdp=True)["xdp"]
703 fail(xdp["attached"][0] not in two_xdps["attached"],
704 "Offload program not reported after other activated")
705 check_multi_basic(two_xdps)
707 offloaded2 = sim.dfs_read("bpf_offloaded_id")
708 fail(offloaded != offloaded2,
709 "Offload ID changed after loading other program")
711 start_test("Test multi-attachment XDP - replace...")
712 ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True)
713 fail(ret == 0, "Replaced one of programs without -force")
714 check_extack(err, "XDP program already attached.", args)
716 if modename == "" or modename == "drv":
717 othermode = "" if modename == "drv" else "drv"
718 start_test("Test multi-attachment XDP - detach...")
719 ret, _, err = sim.unset_xdp(othermode, force=True,
720 fail=False, include_stderr=True)
721 fail(ret == 0, "Removed program with a bad mode")
722 check_extack(err, "program loaded with different flags.", args)
724 sim.unset_xdp("offload")
725 xdp = sim.ip_link_show(xdp=True)["xdp"]
726 offloaded = sim.dfs_read("bpf_offloaded_id")
728 fail(xdp["mode"] != modeid, "Bad mode reported after multiple programs")
729 fail("prog" not in xdp,
730 "Base program not reported after multi program mode")
731 fail(xdp["attached"][0] not in two_xdps["attached"],
732 "Offload program not reported after other activated")
733 fail(len(xdp["attached"]) != 1,
734 "Wrong attached program count with remaining programs")
735 fail(offloaded != "0", "Offload ID reported with only other program left")
737 start_test("Test multi-attachment XDP - reattach...")
738 sim.set_xdp(obj, "offload")
739 two_xdps = sim.ip_link_show(xdp=True)["xdp"]
741 fail(xdp["attached"][0] not in two_xdps["attached"],
742 "Other program not reported after offload activated")
743 check_multi_basic(two_xdps)
745 start_test("Test multi-attachment XDP - device remove...")
746 simdev.remove()
748 simdev = NetdevSimDev()
749 sim, = simdev.nsims
750 sim.set_ethtool_tc_offloads(True)
751 return [simdev, sim]
753 # Parse command line
754 parser = argparse.ArgumentParser()
755 parser.add_argument("--log", help="output verbose log to given file")
756 args = parser.parse_args()
757 if args.log:
758 logfile = open(args.log, 'w+')
759 logfile.write("# -*-Org-*-")
761 log("Prepare...", "", level=1)
762 log_level_inc()
764 # Check permissions
765 skip(os.getuid() != 0, "test must be run as root")
767 # Check tools
768 ret, progs = bpftool("prog", fail=False)
769 skip(ret != 0, "bpftool not installed")
770 base_progs = progs
771 _, base_maps = bpftool("map")
773 # Check netdevsim
774 ret, out = cmd("modprobe netdevsim", fail=False)
775 skip(ret != 0, "netdevsim module could not be loaded")
777 # Check debugfs
778 _, out = cmd("mount")
779 if out.find("/sys/kernel/debug type debugfs") == -1:
780 cmd("mount -t debugfs none /sys/kernel/debug")
782 # Check samples are compiled
783 samples = ["sample_ret0.o", "sample_map_ret0.o"]
784 for s in samples:
785 ret, out = cmd("ls %s/%s" % (bpf_test_dir, s), fail=False)
786 skip(ret != 0, "sample %s/%s not found, please compile it" %
787 (bpf_test_dir, s))
789 # Check if iproute2 is built with libmnl (needed by extack support)
790 _, _, err = cmd("tc qdisc delete dev lo handle 0",
791 fail=False, include_stderr=True)
792 if err.find("Error: Failed to find qdisc with specified handle.") == -1:
793 print("Warning: no extack message in iproute2 output, libmnl missing?")
794 log("Warning: no extack message in iproute2 output, libmnl missing?", "")
795 skip_extack = True
797 # Check if net namespaces seem to work
798 ns = mknetns()
799 skip(ns is None, "Could not create a net namespace")
800 cmd("ip netns delete %s" % (ns))
801 netns = []
803 try:
804 obj = bpf_obj("sample_ret0.o")
805 bytecode = bpf_bytecode("1,6 0 0 4294967295,")
807 start_test("Test destruction of generic XDP...")
808 simdev = NetdevSimDev()
809 sim, = simdev.nsims
810 sim.set_xdp(obj, "generic")
811 simdev.remove()
812 bpftool_prog_list_wait(expected=0)
814 simdev = NetdevSimDev()
815 sim, = simdev.nsims
816 sim.tc_add_ingress()
818 start_test("Test TC non-offloaded...")
819 ret, _ = sim.cls_bpf_add_filter(obj, skip_hw=True, fail=False)
820 fail(ret != 0, "Software TC filter did not load")
822 start_test("Test TC non-offloaded isn't getting bound...")
823 ret, _ = sim.cls_bpf_add_filter(obj, fail=False)
824 fail(ret != 0, "Software TC filter did not load")
825 simdev.dfs_get_bound_progs(expected=0)
827 sim.tc_flush_filters()
829 start_test("Test TC offloads are off by default...")
830 ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True,
831 fail=False, include_stderr=True)
832 fail(ret == 0, "TC filter loaded without enabling TC offloads")
833 check_extack(err, "TC offload is disabled on net device.", args)
834 sim.wait_for_flush()
836 sim.set_ethtool_tc_offloads(True)
837 sim.dfs["bpf_tc_non_bound_accept"] = "Y"
839 start_test("Test TC offload by default...")
840 ret, _ = sim.cls_bpf_add_filter(obj, fail=False)
841 fail(ret != 0, "Software TC filter did not load")
842 simdev.dfs_get_bound_progs(expected=0)
843 ingress = sim.tc_show_ingress(expected=1)
844 fltr = ingress[0]
845 fail(not fltr["in_hw"], "Filter not offloaded by default")
847 sim.tc_flush_filters()
849 start_test("Test TC cBPF bytcode tries offload by default...")
850 ret, _ = sim.cls_bpf_add_filter(bytecode, fail=False)
851 fail(ret != 0, "Software TC filter did not load")
852 simdev.dfs_get_bound_progs(expected=0)
853 ingress = sim.tc_show_ingress(expected=1)
854 fltr = ingress[0]
855 fail(not fltr["in_hw"], "Bytecode not offloaded by default")
857 sim.tc_flush_filters()
858 sim.dfs["bpf_tc_non_bound_accept"] = "N"
860 start_test("Test TC cBPF unbound bytecode doesn't offload...")
861 ret, _, err = sim.cls_bpf_add_filter(bytecode, skip_sw=True,
862 fail=False, include_stderr=True)
863 fail(ret == 0, "TC bytecode loaded for offload")
864 check_extack_nsim(err, "netdevsim configured to reject unbound programs.",
865 args)
866 sim.wait_for_flush()
868 start_test("Test non-0 chain offload...")
869 ret, _, err = sim.cls_bpf_add_filter(obj, chain=1, prio=1, handle=1,
870 skip_sw=True,
871 fail=False, include_stderr=True)
872 fail(ret == 0, "Offloaded a filter to chain other than 0")
873 check_extack(err, "Driver supports only offload of chain 0.", args)
874 sim.tc_flush_filters()
876 start_test("Test TC replace...")
877 sim.cls_bpf_add_filter(obj, prio=1, handle=1)
878 sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1)
879 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
881 sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_sw=True)
882 sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, skip_sw=True)
883 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
885 sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=True)
886 sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, skip_hw=True)
887 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
889 start_test("Test TC replace bad flags...")
890 for i in range(3):
891 for j in range(3):
892 ret, _ = sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1,
893 skip_sw=(j == 1), skip_hw=(j == 2),
894 fail=False)
895 fail(bool(ret) != bool(j),
896 "Software TC incorrect load in replace test, iteration %d" %
897 (j))
898 sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
900 start_test("Test spurious extack from the driver...")
901 test_spurios_extack(sim, obj, False, "netdevsim")
902 test_spurios_extack(sim, obj, True, "netdevsim")
904 sim.set_ethtool_tc_offloads(False)
906 test_spurios_extack(sim, obj, False, "TC offload is disabled")
907 test_spurios_extack(sim, obj, True, "TC offload is disabled")
909 sim.set_ethtool_tc_offloads(True)
911 sim.tc_flush_filters()
913 start_test("Test TC offloads work...")
914 ret, _, err = sim.cls_bpf_add_filter(obj, verbose=True, skip_sw=True,
915 fail=False, include_stderr=True)
916 fail(ret != 0, "TC filter did not load with TC offloads enabled")
917 check_verifier_log(err, "[netdevsim] Hello from netdevsim!")
919 start_test("Test TC offload basics...")
920 dfs = simdev.dfs_get_bound_progs(expected=1)
921 progs = bpftool_prog_list(expected=1)
922 ingress = sim.tc_show_ingress(expected=1)
924 dprog = dfs[0]
925 prog = progs[0]
926 fltr = ingress[0]
927 fail(fltr["skip_hw"], "TC does reports 'skip_hw' on offloaded filter")
928 fail(not fltr["in_hw"], "TC does not report 'in_hw' for offloaded filter")
929 fail(not fltr["skip_sw"], "TC does not report 'skip_sw' back")
931 start_test("Test TC offload is device-bound...")
932 fail(str(prog["id"]) != fltr["id"], "Program IDs don't match")
933 fail(prog["tag"] != fltr["tag"], "Program tags don't match")
934 fail(fltr["id"] != dprog["id"], "Program IDs don't match")
935 fail(dprog["state"] != "xlated", "Offloaded program state not translated")
936 fail(dprog["loaded"] != "Y", "Offloaded program is not loaded")
938 start_test("Test disabling TC offloads is rejected while filters installed...")
939 ret, _ = sim.set_ethtool_tc_offloads(False, fail=False)
940 fail(ret == 0, "Driver should refuse to disable TC offloads with filters installed...")
942 start_test("Test qdisc removal frees things...")
943 sim.tc_flush_filters()
944 sim.tc_show_ingress(expected=0)
946 start_test("Test disabling TC offloads is OK without filters...")
947 ret, _ = sim.set_ethtool_tc_offloads(False, fail=False)
948 fail(ret != 0,
949 "Driver refused to disable TC offloads without filters installed...")
951 sim.set_ethtool_tc_offloads(True)
953 start_test("Test destroying device gets rid of TC filters...")
954 sim.cls_bpf_add_filter(obj, skip_sw=True)
955 simdev.remove()
956 bpftool_prog_list_wait(expected=0)
958 simdev = NetdevSimDev()
959 sim, = simdev.nsims
960 sim.set_ethtool_tc_offloads(True)
962 start_test("Test destroying device gets rid of XDP...")
963 sim.set_xdp(obj, "offload")
964 simdev.remove()
965 bpftool_prog_list_wait(expected=0)
967 simdev = NetdevSimDev()
968 sim, = simdev.nsims
969 sim.set_ethtool_tc_offloads(True)
971 start_test("Test XDP prog reporting...")
972 sim.set_xdp(obj, "drv")
973 ipl = sim.ip_link_show(xdp=True)
974 progs = bpftool_prog_list(expected=1)
975 fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"],
976 "Loaded program has wrong ID")
978 start_test("Test XDP prog replace without force...")
979 ret, _ = sim.set_xdp(obj, "drv", fail=False)
980 fail(ret == 0, "Replaced XDP program without -force")
981 sim.wait_for_flush(total=1)
983 start_test("Test XDP prog replace with force...")
984 ret, _ = sim.set_xdp(obj, "drv", force=True, fail=False)
985 fail(ret != 0, "Could not replace XDP program with -force")
986 bpftool_prog_list_wait(expected=1)
987 ipl = sim.ip_link_show(xdp=True)
988 progs = bpftool_prog_list(expected=1)
989 fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"],
990 "Loaded program has wrong ID")
991 fail("dev" in progs[0].keys(),
992 "Device parameters reported for non-offloaded program")
994 start_test("Test XDP prog replace with bad flags...")
995 ret, _, err = sim.set_xdp(obj, "generic", force=True,
996 fail=False, include_stderr=True)
997 fail(ret == 0, "Replaced XDP program with a program in different mode")
998 check_extack(err,
999 "native and generic XDP can't be active at the same time.",
1000 args)
1001 ret, _, err = sim.set_xdp(obj, "", force=True,
1002 fail=False, include_stderr=True)
1003 fail(ret == 0, "Replaced XDP program with a program in different mode")
1004 check_extack(err, "program loaded with different flags.", args)
1006 start_test("Test XDP prog remove with bad flags...")
1007 ret, _, err = sim.unset_xdp("", force=True,
1008 fail=False, include_stderr=True)
1009 fail(ret == 0, "Removed program with a bad mode")
1010 check_extack(err, "program loaded with different flags.", 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 offload...")
1045 _, _, err = sim.set_xdp(obj, "offload", verbose=True, include_stderr=True)
1046 ipl = sim.ip_link_show(xdp=True)
1047 link_xdp = ipl["xdp"]["prog"]
1048 progs = bpftool_prog_list(expected=1)
1049 prog = progs[0]
1050 fail(link_xdp["id"] != prog["id"], "Loaded program has wrong ID")
1051 check_verifier_log(err, "[netdevsim] Hello from netdevsim!")
1053 start_test("Test XDP offload is device bound...")
1054 dfs = simdev.dfs_get_bound_progs(expected=1)
1055 dprog = dfs[0]
1057 fail(prog["id"] != link_xdp["id"], "Program IDs don't match")
1058 fail(prog["tag"] != link_xdp["tag"], "Program tags don't match")
1059 fail(str(link_xdp["id"]) != dprog["id"], "Program IDs don't match")
1060 fail(dprog["state"] != "xlated", "Offloaded program state not translated")
1061 fail(dprog["loaded"] != "Y", "Offloaded program is not loaded")
1063 start_test("Test removing XDP program many times...")
1064 sim.unset_xdp("offload")
1065 sim.unset_xdp("offload")
1066 sim.unset_xdp("drv")
1067 sim.unset_xdp("drv")
1068 sim.unset_xdp("")
1069 sim.unset_xdp("")
1070 bpftool_prog_list_wait(expected=0)
1072 start_test("Test attempt to use a program for a wrong device...")
1073 simdev2 = NetdevSimDev()
1074 sim2, = simdev2.nsims
1075 sim2.set_xdp(obj, "offload")
1076 pin_file, pinned = pin_prog("/sys/fs/bpf/tmp")
1078 ret, _, err = sim.set_xdp(pinned, "offload",
1079 fail=False, include_stderr=True)
1080 fail(ret == 0, "Pinned program loaded for a different device accepted")
1081 check_extack_nsim(err, "program bound to different dev.", args)
1082 simdev2.remove()
1083 ret, _, err = sim.set_xdp(pinned, "offload",
1084 fail=False, include_stderr=True)
1085 fail(ret == 0, "Pinned program loaded for a removed device accepted")
1086 check_extack_nsim(err, "xdpoffload of non-bound program.", args)
1087 rm(pin_file)
1088 bpftool_prog_list_wait(expected=0)
1090 simdev, sim = test_multi_prog(simdev, sim, obj, "", 1)
1091 simdev, sim = test_multi_prog(simdev, sim, obj, "drv", 1)
1092 simdev, sim = test_multi_prog(simdev, sim, obj, "generic", 2)
1094 start_test("Test mixing of TC and XDP...")
1095 sim.tc_add_ingress()
1096 sim.set_xdp(obj, "offload")
1097 ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True,
1098 fail=False, include_stderr=True)
1099 fail(ret == 0, "Loading TC when XDP active should fail")
1100 check_extack_nsim(err, "driver and netdev offload states mismatch.", args)
1101 sim.unset_xdp("offload")
1102 sim.wait_for_flush()
1104 sim.cls_bpf_add_filter(obj, skip_sw=True)
1105 ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True)
1106 fail(ret == 0, "Loading XDP when TC active should fail")
1107 check_extack_nsim(err, "TC program is already loaded.", args)
1109 start_test("Test binding TC from pinned...")
1110 pin_file, pinned = pin_prog("/sys/fs/bpf/tmp")
1111 sim.tc_flush_filters(bound=1, total=1)
1112 sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True)
1113 sim.tc_flush_filters(bound=1, total=1)
1115 start_test("Test binding XDP from pinned...")
1116 sim.set_xdp(obj, "offload")
1117 pin_file, pinned = pin_prog("/sys/fs/bpf/tmp2", idx=1)
1119 sim.set_xdp(pinned, "offload", force=True)
1120 sim.unset_xdp("offload")
1121 sim.set_xdp(pinned, "offload", force=True)
1122 sim.unset_xdp("offload")
1124 start_test("Test offload of wrong type fails...")
1125 ret, _ = sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True, fail=False)
1126 fail(ret == 0, "Managed to attach XDP program to TC")
1128 start_test("Test asking for TC offload of two filters...")
1129 sim.cls_bpf_add_filter(obj, da=True, skip_sw=True)
1130 ret, _, err = sim.cls_bpf_add_filter(obj, da=True, skip_sw=True,
1131 fail=False, include_stderr=True)
1132 fail(ret == 0, "Managed to offload two TC filters at the same time")
1133 check_extack_nsim(err, "driver and netdev offload states mismatch.", args)
1135 sim.tc_flush_filters(bound=2, total=2)
1137 start_test("Test if netdev removal waits for translation...")
1138 delay_msec = 500
1139 sim.dfs["dev/bpf_bind_verifier_delay"] = delay_msec
1140 start = time.time()
1141 cmd_line = "tc filter add dev %s ingress bpf %s da skip_sw" % \
1142 (sim['ifname'], obj)
1143 tc_proc = cmd(cmd_line, background=True, fail=False)
1144 # Wait for the verifier to start
1145 while simdev.dfs_num_bound_progs() <= 2:
1146 pass
1147 simdev.remove()
1148 end = time.time()
1149 ret, _ = cmd_result(tc_proc, fail=False)
1150 time_diff = end - start
1151 log("Time", "start:\t%s\nend:\t%s\ndiff:\t%s" % (start, end, time_diff))
1153 fail(ret == 0, "Managed to load TC filter on a unregistering device")
1154 delay_sec = delay_msec * 0.001
1155 fail(time_diff < delay_sec, "Removal process took %s, expected %s" %
1156 (time_diff, delay_sec))
1158 # Remove all pinned files and reinstantiate the netdev
1159 clean_up()
1160 bpftool_prog_list_wait(expected=0)
1162 simdev = NetdevSimDev()
1163 sim, = simdev.nsims
1164 map_obj = bpf_obj("sample_map_ret0.o")
1165 start_test("Test loading program with maps...")
1166 sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
1168 start_test("Test bpftool bound info reporting (own ns)...")
1169 check_dev_info(False, "")
1171 start_test("Test bpftool bound info reporting (other ns)...")
1172 ns = mknetns()
1173 sim.set_ns(ns)
1174 check_dev_info(True, "")
1176 start_test("Test bpftool bound info reporting (remote ns)...")
1177 check_dev_info(False, ns)
1179 start_test("Test bpftool bound info reporting (back to own ns)...")
1180 sim.set_ns("")
1181 check_dev_info(False, "")
1183 prog_file, _ = pin_prog("/sys/fs/bpf/tmp_prog")
1184 map_file, _ = pin_map("/sys/fs/bpf/tmp_map", idx=1, expected=2)
1185 simdev.remove()
1187 start_test("Test bpftool bound info reporting (removed dev)...")
1188 check_dev_info_removed(prog_file=prog_file, map_file=map_file)
1190 # Remove all pinned files and reinstantiate the netdev
1191 clean_up()
1192 bpftool_prog_list_wait(expected=0)
1194 simdev = NetdevSimDev()
1195 sim, = simdev.nsims
1197 start_test("Test map update (no flags)...")
1198 sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
1199 maps = bpftool_map_list(expected=2)
1200 array = maps[0] if maps[0]["type"] == "array" else maps[1]
1201 htab = maps[0] if maps[0]["type"] == "hash" else maps[1]
1202 for m in maps:
1203 for i in range(2):
1204 bpftool("map update id %d key %s value %s" %
1205 (m["id"], int2str("I", i), int2str("Q", i * 3)))
1207 for m in maps:
1208 ret, _ = bpftool("map update id %d key %s value %s" %
1209 (m["id"], int2str("I", 3), int2str("Q", 3 * 3)),
1210 fail=False)
1211 fail(ret == 0, "added too many entries")
1213 start_test("Test map update (exists)...")
1214 for m in maps:
1215 for i in range(2):
1216 bpftool("map update id %d key %s value %s exist" %
1217 (m["id"], int2str("I", i), int2str("Q", i * 3)))
1219 for m in maps:
1220 ret, err = bpftool("map update id %d key %s value %s exist" %
1221 (m["id"], int2str("I", 3), int2str("Q", 3 * 3)),
1222 fail=False)
1223 fail(ret == 0, "updated non-existing key")
1224 fail(err["error"].find("No such file or directory") == -1,
1225 "expected ENOENT, error is '%s'" % (err["error"]))
1227 start_test("Test map update (noexist)...")
1228 for m in maps:
1229 for i in range(2):
1230 ret, err = bpftool("map update id %d key %s value %s noexist" %
1231 (m["id"], int2str("I", i), int2str("Q", i * 3)),
1232 fail=False)
1233 fail(ret == 0, "updated existing key")
1234 fail(err["error"].find("File exists") == -1,
1235 "expected EEXIST, error is '%s'" % (err["error"]))
1237 start_test("Test map dump...")
1238 for m in maps:
1239 _, entries = bpftool("map dump id %d" % (m["id"]))
1240 for i in range(2):
1241 key = str2int(entries[i]["key"])
1242 fail(key != i, "expected key %d, got %d" % (key, i))
1243 val = str2int(entries[i]["value"])
1244 fail(val != i * 3, "expected value %d, got %d" % (val, i * 3))
1246 start_test("Test map getnext...")
1247 for m in maps:
1248 _, entry = bpftool("map getnext id %d" % (m["id"]))
1249 key = str2int(entry["next_key"])
1250 fail(key != 0, "next key %d, expected %d" % (key, 0))
1251 _, entry = bpftool("map getnext id %d key %s" %
1252 (m["id"], int2str("I", 0)))
1253 key = str2int(entry["next_key"])
1254 fail(key != 1, "next key %d, expected %d" % (key, 1))
1255 ret, err = bpftool("map getnext id %d key %s" %
1256 (m["id"], int2str("I", 1)), fail=False)
1257 fail(ret == 0, "got next key past the end of map")
1258 fail(err["error"].find("No such file or directory") == -1,
1259 "expected ENOENT, error is '%s'" % (err["error"]))
1261 start_test("Test map delete (htab)...")
1262 for i in range(2):
1263 bpftool("map delete id %d key %s" % (htab["id"], int2str("I", i)))
1265 start_test("Test map delete (array)...")
1266 for i in range(2):
1267 ret, err = bpftool("map delete id %d key %s" %
1268 (htab["id"], int2str("I", i)), fail=False)
1269 fail(ret == 0, "removed entry from an array")
1270 fail(err["error"].find("No such file or directory") == -1,
1271 "expected ENOENT, error is '%s'" % (err["error"]))
1273 start_test("Test map remove...")
1274 sim.unset_xdp("offload")
1275 bpftool_map_list_wait(expected=0)
1276 simdev.remove()
1278 simdev = NetdevSimDev()
1279 sim, = simdev.nsims
1280 sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
1281 simdev.remove()
1282 bpftool_map_list_wait(expected=0)
1284 start_test("Test map creation fail path...")
1285 simdev = NetdevSimDev()
1286 sim, = simdev.nsims
1287 sim.dfs["bpf_map_accept"] = "N"
1288 ret, _ = sim.set_xdp(map_obj, "offload", JSON=False, fail=False)
1289 fail(ret == 0,
1290 "netdevsim didn't refuse to create a map with offload disabled")
1292 simdev.remove()
1294 start_test("Test multi-dev ASIC program reuse...")
1295 simdevA = NetdevSimDev()
1296 simA, = simdevA.nsims
1297 simdevB = NetdevSimDev(3)
1298 simB1, simB2, simB3 = simdevB.nsims
1299 sims = (simA, simB1, simB2, simB3)
1300 simB = (simB1, simB2, simB3)
1302 bpftool_prog_load("sample_map_ret0.o", "/sys/fs/bpf/nsimA",
1303 dev=simA['ifname'])
1304 progA = bpf_pinned("/sys/fs/bpf/nsimA")
1305 bpftool_prog_load("sample_map_ret0.o", "/sys/fs/bpf/nsimB",
1306 dev=simB1['ifname'])
1307 progB = bpf_pinned("/sys/fs/bpf/nsimB")
1309 simA.set_xdp(progA, "offload", JSON=False)
1310 for d in simdevB.nsims:
1311 d.set_xdp(progB, "offload", JSON=False)
1313 start_test("Test multi-dev ASIC cross-dev replace...")
1314 ret, _ = simA.set_xdp(progB, "offload", force=True, JSON=False, fail=False)
1315 fail(ret == 0, "cross-ASIC program allowed")
1316 for d in simdevB.nsims:
1317 ret, _ = d.set_xdp(progA, "offload", force=True, JSON=False, fail=False)
1318 fail(ret == 0, "cross-ASIC program allowed")
1320 start_test("Test multi-dev ASIC cross-dev install...")
1321 for d in sims:
1322 d.unset_xdp("offload")
1324 ret, _, err = simA.set_xdp(progB, "offload", force=True, JSON=False,
1325 fail=False, include_stderr=True)
1326 fail(ret == 0, "cross-ASIC program allowed")
1327 check_extack_nsim(err, "program bound to different dev.", args)
1328 for d in simdevB.nsims:
1329 ret, _, err = d.set_xdp(progA, "offload", force=True, JSON=False,
1330 fail=False, include_stderr=True)
1331 fail(ret == 0, "cross-ASIC program allowed")
1332 check_extack_nsim(err, "program bound to different dev.", args)
1334 start_test("Test multi-dev ASIC cross-dev map reuse...")
1336 mapA = bpftool("prog show %s" % (progA))[1]["map_ids"][0]
1337 mapB = bpftool("prog show %s" % (progB))[1]["map_ids"][0]
1339 ret, _ = bpftool_prog_load("sample_map_ret0.o", "/sys/fs/bpf/nsimB_",
1340 dev=simB3['ifname'],
1341 maps=["idx 0 id %d" % (mapB)],
1342 fail=False)
1343 fail(ret != 0, "couldn't reuse a map on the same ASIC")
1344 rm("/sys/fs/bpf/nsimB_")
1346 ret, _, err = bpftool_prog_load("sample_map_ret0.o", "/sys/fs/bpf/nsimA_",
1347 dev=simA['ifname'],
1348 maps=["idx 0 id %d" % (mapB)],
1349 fail=False, include_stderr=True)
1350 fail(ret == 0, "could reuse a map on a different ASIC")
1351 fail(err.count("offload device mismatch between prog and map") == 0,
1352 "error message missing for cross-ASIC map")
1354 ret, _, err = bpftool_prog_load("sample_map_ret0.o", "/sys/fs/bpf/nsimB_",
1355 dev=simB1['ifname'],
1356 maps=["idx 0 id %d" % (mapA)],
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 start_test("Test multi-dev ASIC cross-dev destruction...")
1363 bpftool_prog_list_wait(expected=2)
1365 simdevA.remove()
1366 bpftool_prog_list_wait(expected=1)
1368 ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"]
1369 fail(ifnameB != simB1['ifname'], "program not bound to original device")
1370 simB1.remove()
1371 bpftool_prog_list_wait(expected=1)
1373 start_test("Test multi-dev ASIC cross-dev destruction - move...")
1374 ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"]
1375 fail(ifnameB not in (simB2['ifname'], simB3['ifname']),
1376 "program not bound to remaining devices")
1378 simB2.remove()
1379 ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"]
1380 fail(ifnameB != simB3['ifname'], "program not bound to remaining device")
1382 simB3.remove()
1383 simdevB.remove()
1384 bpftool_prog_list_wait(expected=0)
1386 start_test("Test multi-dev ASIC cross-dev destruction - orphaned...")
1387 ret, out = bpftool("prog show %s" % (progB), fail=False)
1388 fail(ret == 0, "got information about orphaned program")
1389 fail("error" not in out, "no error reported for get info on orphaned")
1390 fail(out["error"] != "can't get prog info: No such device",
1391 "wrong error for get info on orphaned")
1393 print("%s: OK" % (os.path.basename(__file__)))
1395 finally:
1396 log("Clean up...", "", level=1)
1397 log_level_inc()
1398 clean_up()