diff options
Diffstat (limited to 'tools/testing/selftests/bpf/test_offload.py')
-rwxr-xr-x | tools/testing/selftests/bpf/test_offload.py | 1405 |
1 files changed, 0 insertions, 1405 deletions
diff --git a/tools/testing/selftests/bpf/test_offload.py b/tools/testing/selftests/bpf/test_offload.py deleted file mode 100755 index 6157f884d0..0000000000 --- a/tools/testing/selftests/bpf/test_offload.py +++ /dev/null @@ -1,1405 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright (C) 2017 Netronome Systems, Inc. -# Copyright (c) 2019 Mellanox Technologies. All rights reserved -# -# This software is licensed under the GNU General License Version 2, -# June 1991 as shown in the file COPYING in the top-level directory of this -# source tree. -# -# THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" -# WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, -# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -# FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE -# OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME -# THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - -from datetime import datetime -import argparse -import errno -import json -import os -import pprint -import random -import re -import stat -import string -import struct -import subprocess -import time -import traceback - -logfile = None -log_level = 1 -skip_extack = False -bpf_test_dir = os.path.dirname(os.path.realpath(__file__)) -pp = pprint.PrettyPrinter() -devs = [] # devices we created for clean up -files = [] # files to be removed -netns = [] # net namespaces to be removed - -def log_get_sec(level=0): - return "*" * (log_level + level) - -def log_level_inc(add=1): - global log_level - log_level += add - -def log_level_dec(sub=1): - global log_level - log_level -= sub - -def log_level_set(level): - global log_level - log_level = level - -def log(header, data, level=None): - """ - Output to an optional log. - """ - if logfile is None: - return - if level is not None: - log_level_set(level) - - if not isinstance(data, str): - data = pp.pformat(data) - - if len(header): - logfile.write("\n" + log_get_sec() + " ") - logfile.write(header) - if len(header) and len(data.strip()): - logfile.write("\n") - logfile.write(data) - -def skip(cond, msg): - if not cond: - return - print("SKIP: " + msg) - log("SKIP: " + msg, "", level=1) - os.sys.exit(0) - -def fail(cond, msg): - if not cond: - return - print("FAIL: " + msg) - tb = "".join(traceback.extract_stack().format()) - print(tb) - log("FAIL: " + msg, tb, level=1) - os.sys.exit(1) - -def start_test(msg): - log(msg, "", level=1) - log_level_inc() - print(msg) - -def cmd(cmd, shell=True, include_stderr=False, background=False, fail=True): - """ - Run a command in subprocess and return tuple of (retval, stdout); - optionally return stderr as well as third value. - """ - proc = subprocess.Popen(cmd, shell=shell, stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - if background: - msg = "%s START: %s" % (log_get_sec(1), - datetime.now().strftime("%H:%M:%S.%f")) - log("BKG " + proc.args, msg) - return proc - - return cmd_result(proc, include_stderr=include_stderr, fail=fail) - -def cmd_result(proc, include_stderr=False, fail=False): - stdout, stderr = proc.communicate() - stdout = stdout.decode("utf-8") - stderr = stderr.decode("utf-8") - proc.stdout.close() - proc.stderr.close() - - stderr = "\n" + stderr - if stderr[-1] == "\n": - stderr = stderr[:-1] - - sec = log_get_sec(1) - log("CMD " + proc.args, - "RETCODE: %d\n%s STDOUT:\n%s%s STDERR:%s\n%s END: %s" % - (proc.returncode, sec, stdout, sec, stderr, - sec, datetime.now().strftime("%H:%M:%S.%f"))) - - if proc.returncode != 0 and fail: - if len(stderr) > 0 and stderr[-1] == "\n": - stderr = stderr[:-1] - raise Exception("Command failed: %s\n%s" % (proc.args, stderr)) - - if include_stderr: - return proc.returncode, stdout, stderr - else: - return proc.returncode, stdout - -def rm(f): - cmd("rm -f %s" % (f)) - if f in files: - files.remove(f) - -def tool(name, args, flags, JSON=True, ns="", fail=True, include_stderr=False): - params = "" - if JSON: - params += "%s " % (flags["json"]) - - if ns != "": - ns = "ip netns exec %s " % (ns) - - if include_stderr: - ret, stdout, stderr = cmd(ns + name + " " + params + args, - fail=fail, include_stderr=True) - else: - ret, stdout = cmd(ns + name + " " + params + args, - fail=fail, include_stderr=False) - - if JSON and len(stdout.strip()) != 0: - out = json.loads(stdout) - else: - out = stdout - - if include_stderr: - return ret, out, stderr - else: - return ret, out - -def bpftool(args, JSON=True, ns="", fail=True, include_stderr=False): - return tool("bpftool", args, {"json":"-p"}, JSON=JSON, ns=ns, - fail=fail, include_stderr=include_stderr) - -def bpftool_prog_list(expected=None, ns="", exclude_orphaned=True): - _, progs = bpftool("prog show", JSON=True, ns=ns, fail=True) - # Remove the base progs - for p in base_progs: - if p in progs: - progs.remove(p) - if exclude_orphaned: - progs = [ p for p in progs if not p['orphaned'] ] - if expected is not None: - if len(progs) != expected: - fail(True, "%d BPF programs loaded, expected %d" % - (len(progs), expected)) - return progs - -def bpftool_map_list(expected=None, ns=""): - _, maps = bpftool("map show", JSON=True, ns=ns, fail=True) - # Remove the base maps - maps = [m for m in maps if m not in base_maps and m.get('name') and m.get('name') not in base_map_names] - if expected is not None: - if len(maps) != expected: - fail(True, "%d BPF maps loaded, expected %d" % - (len(maps), expected)) - return maps - -def bpftool_prog_list_wait(expected=0, n_retry=20): - for i in range(n_retry): - nprogs = len(bpftool_prog_list()) - if nprogs == expected: - return - time.sleep(0.05) - raise Exception("Time out waiting for program counts to stabilize want %d, have %d" % (expected, nprogs)) - -def bpftool_map_list_wait(expected=0, n_retry=20): - for i in range(n_retry): - nmaps = len(bpftool_map_list()) - if nmaps == expected: - return - time.sleep(0.05) - raise Exception("Time out waiting for map counts to stabilize want %d, have %d" % (expected, nmaps)) - -def bpftool_prog_load(sample, file_name, maps=[], prog_type="xdp", dev=None, - fail=True, include_stderr=False): - args = "prog load %s %s" % (os.path.join(bpf_test_dir, sample), file_name) - if prog_type is not None: - args += " type " + prog_type - if dev is not None: - args += " dev " + dev - if len(maps): - args += " map " + " map ".join(maps) - - res = bpftool(args, fail=fail, include_stderr=include_stderr) - if res[0] == 0: - files.append(file_name) - return res - -def ip(args, force=False, JSON=True, ns="", fail=True, include_stderr=False): - if force: - args = "-force " + args - return tool("ip", args, {"json":"-j"}, JSON=JSON, ns=ns, - fail=fail, include_stderr=include_stderr) - -def tc(args, JSON=True, ns="", fail=True, include_stderr=False): - return tool("tc", args, {"json":"-p"}, JSON=JSON, ns=ns, - fail=fail, include_stderr=include_stderr) - -def ethtool(dev, opt, args, fail=True): - return cmd("ethtool %s %s %s" % (opt, dev["ifname"], args), fail=fail) - -def bpf_obj(name, sec=".text", path=bpf_test_dir,): - return "obj %s sec %s" % (os.path.join(path, name), sec) - -def bpf_pinned(name): - return "pinned %s" % (name) - -def bpf_bytecode(bytecode): - return "bytecode \"%s\"" % (bytecode) - -def mknetns(n_retry=10): - for i in range(n_retry): - name = ''.join([random.choice(string.ascii_letters) for i in range(8)]) - ret, _ = ip("netns add %s" % (name), fail=False) - if ret == 0: - netns.append(name) - return name - return None - -def int2str(fmt, val): - ret = [] - for b in struct.pack(fmt, val): - ret.append(int(b)) - return " ".join(map(lambda x: str(x), ret)) - -def str2int(strtab): - inttab = [] - for i in strtab: - inttab.append(int(i, 16)) - ba = bytearray(inttab) - if len(strtab) == 4: - fmt = "I" - elif len(strtab) == 8: - fmt = "Q" - else: - raise Exception("String array of len %d can't be unpacked to an int" % - (len(strtab))) - return struct.unpack(fmt, ba)[0] - -class DebugfsDir: - """ - Class for accessing DebugFS directories as a dictionary. - """ - - def __init__(self, path): - self.path = path - self._dict = self._debugfs_dir_read(path) - - def __len__(self): - return len(self._dict.keys()) - - def __getitem__(self, key): - if type(key) is int: - key = list(self._dict.keys())[key] - return self._dict[key] - - def __setitem__(self, key, value): - log("DebugFS set %s = %s" % (key, value), "") - log_level_inc() - - cmd("echo '%s' > %s/%s" % (value, self.path, key)) - log_level_dec() - - _, out = cmd('cat %s/%s' % (self.path, key)) - self._dict[key] = out.strip() - - def _debugfs_dir_read(self, path): - dfs = {} - - log("DebugFS state for %s" % (path), "") - log_level_inc(add=2) - - _, out = cmd('ls ' + path) - for f in out.split(): - if f == "ports": - continue - - p = os.path.join(path, f) - if not os.stat(p).st_mode & stat.S_IRUSR: - continue - - if os.path.isfile(p): - # We need to init trap_flow_action_cookie before read it - if f == "trap_flow_action_cookie": - cmd('echo deadbeef > %s/%s' % (path, f)) - _, out = cmd('cat %s/%s' % (path, f)) - dfs[f] = out.strip() - elif os.path.isdir(p): - dfs[f] = DebugfsDir(p) - else: - raise Exception("%s is neither file nor directory" % (p)) - - log_level_dec() - log("DebugFS state", dfs) - log_level_dec() - - return dfs - -class NetdevSimDev: - """ - Class for netdevsim bus device and its attributes. - """ - @staticmethod - def ctrl_write(path, val): - fullpath = os.path.join("/sys/bus/netdevsim/", path) - try: - with open(fullpath, "w") as f: - f.write(val) - except OSError as e: - log("WRITE %s: %r" % (fullpath, val), -e.errno) - raise e - log("WRITE %s: %r" % (fullpath, val), 0) - - def __init__(self, port_count=1): - addr = 0 - while True: - try: - self.ctrl_write("new_device", "%u %u" % (addr, port_count)) - except OSError as e: - if e.errno == errno.ENOSPC: - addr += 1 - continue - raise e - break - self.addr = addr - - # As probe of netdevsim device might happen from a workqueue, - # so wait here until all netdevs appear. - self.wait_for_netdevs(port_count) - - ret, out = cmd("udevadm settle", fail=False) - if ret: - raise Exception("udevadm settle failed") - ifnames = self.get_ifnames() - - devs.append(self) - self.dfs_dir = "/sys/kernel/debug/netdevsim/netdevsim%u/" % addr - - self.nsims = [] - for port_index in range(port_count): - self.nsims.append(NetdevSim(self, port_index, ifnames[port_index])) - - def get_ifnames(self): - ifnames = [] - listdir = os.listdir("/sys/bus/netdevsim/devices/netdevsim%u/net/" % self.addr) - for ifname in listdir: - ifnames.append(ifname) - ifnames.sort() - return ifnames - - def wait_for_netdevs(self, port_count): - timeout = 5 - timeout_start = time.time() - - while True: - try: - ifnames = self.get_ifnames() - except FileNotFoundError as e: - ifnames = [] - if len(ifnames) == port_count: - break - if time.time() < timeout_start + timeout: - continue - raise Exception("netdevices did not appear within timeout") - - def dfs_num_bound_progs(self): - path = os.path.join(self.dfs_dir, "bpf_bound_progs") - _, progs = cmd('ls %s' % (path)) - return len(progs.split()) - - def dfs_get_bound_progs(self, expected): - progs = DebugfsDir(os.path.join(self.dfs_dir, "bpf_bound_progs")) - if expected is not None: - if len(progs) != expected: - fail(True, "%d BPF programs bound, expected %d" % - (len(progs), expected)) - return progs - - def remove(self): - self.ctrl_write("del_device", "%u" % (self.addr, )) - devs.remove(self) - - def remove_nsim(self, nsim): - self.nsims.remove(nsim) - self.ctrl_write("devices/netdevsim%u/del_port" % (self.addr, ), - "%u" % (nsim.port_index, )) - -class NetdevSim: - """ - Class for netdevsim netdevice and its attributes. - """ - - def __init__(self, nsimdev, port_index, ifname): - # In case udev renamed the netdev to according to new schema, - # check if the name matches the port_index. - nsimnamere = re.compile("eni\d+np(\d+)") - match = nsimnamere.match(ifname) - if match and int(match.groups()[0]) != port_index + 1: - raise Exception("netdevice name mismatches the expected one") - - self.nsimdev = nsimdev - self.port_index = port_index - self.ns = "" - self.dfs_dir = "%s/ports/%u/" % (nsimdev.dfs_dir, port_index) - self.dfs_refresh() - _, [self.dev] = ip("link show dev %s" % ifname) - - def __getitem__(self, key): - return self.dev[key] - - def remove(self): - self.nsimdev.remove_nsim(self) - - def dfs_refresh(self): - self.dfs = DebugfsDir(self.dfs_dir) - return self.dfs - - def dfs_read(self, f): - path = os.path.join(self.dfs_dir, f) - _, data = cmd('cat %s' % (path)) - return data.strip() - - def wait_for_flush(self, bound=0, total=0, n_retry=20): - for i in range(n_retry): - nbound = self.nsimdev.dfs_num_bound_progs() - nprogs = len(bpftool_prog_list()) - if nbound == bound and nprogs == total: - return - time.sleep(0.05) - raise Exception("Time out waiting for program counts to stabilize want %d/%d, have %d bound, %d loaded" % (bound, total, nbound, nprogs)) - - def set_ns(self, ns): - name = "1" if ns == "" else ns - ip("link set dev %s netns %s" % (self.dev["ifname"], name), ns=self.ns) - self.ns = ns - - def set_mtu(self, mtu, fail=True): - return ip("link set dev %s mtu %d" % (self.dev["ifname"], mtu), - fail=fail) - - def set_xdp(self, bpf, mode, force=False, JSON=True, verbose=False, - fail=True, include_stderr=False): - if verbose: - bpf += " verbose" - return ip("link set dev %s xdp%s %s" % (self.dev["ifname"], mode, bpf), - force=force, JSON=JSON, - fail=fail, include_stderr=include_stderr) - - def unset_xdp(self, mode, force=False, JSON=True, - fail=True, include_stderr=False): - return ip("link set dev %s xdp%s off" % (self.dev["ifname"], mode), - force=force, JSON=JSON, - fail=fail, include_stderr=include_stderr) - - def ip_link_show(self, xdp): - _, link = ip("link show dev %s" % (self['ifname'])) - if len(link) > 1: - raise Exception("Multiple objects on ip link show") - if len(link) < 1: - return {} - fail(xdp != "xdp" in link, - "XDP program not reporting in iplink (reported %s, expected %s)" % - ("xdp" in link, xdp)) - return link[0] - - def tc_add_ingress(self): - tc("qdisc add dev %s ingress" % (self['ifname'])) - - def tc_del_ingress(self): - tc("qdisc del dev %s ingress" % (self['ifname'])) - - def tc_flush_filters(self, bound=0, total=0): - self.tc_del_ingress() - self.tc_add_ingress() - self.wait_for_flush(bound=bound, total=total) - - def tc_show_ingress(self, expected=None): - # No JSON support, oh well... - flags = ["skip_sw", "skip_hw", "in_hw"] - named = ["protocol", "pref", "chain", "handle", "id", "tag"] - - args = "-s filter show dev %s ingress" % (self['ifname']) - _, out = tc(args, JSON=False) - - filters = [] - lines = out.split('\n') - for line in lines: - words = line.split() - if "handle" not in words: - continue - fltr = {} - for flag in flags: - fltr[flag] = flag in words - for name in named: - try: - idx = words.index(name) - fltr[name] = words[idx + 1] - except ValueError: - pass - filters.append(fltr) - - if expected is not None: - fail(len(filters) != expected, - "%d ingress filters loaded, expected %d" % - (len(filters), expected)) - return filters - - def cls_filter_op(self, op, qdisc="ingress", prio=None, handle=None, - chain=None, cls="", params="", - fail=True, include_stderr=False): - spec = "" - if prio is not None: - spec += " prio %d" % (prio) - if handle: - spec += " handle %s" % (handle) - if chain is not None: - spec += " chain %d" % (chain) - - return tc("filter {op} dev {dev} {qdisc} {spec} {cls} {params}"\ - .format(op=op, dev=self['ifname'], qdisc=qdisc, spec=spec, - cls=cls, params=params), - fail=fail, include_stderr=include_stderr) - - def cls_bpf_add_filter(self, bpf, op="add", prio=None, handle=None, - chain=None, da=False, verbose=False, - skip_sw=False, skip_hw=False, - fail=True, include_stderr=False): - cls = "bpf " + bpf - - params = "" - if da: - params += " da" - if verbose: - params += " verbose" - if skip_sw: - params += " skip_sw" - if skip_hw: - params += " skip_hw" - - return self.cls_filter_op(op=op, prio=prio, handle=handle, cls=cls, - chain=chain, params=params, - fail=fail, include_stderr=include_stderr) - - def set_ethtool_tc_offloads(self, enable, fail=True): - args = "hw-tc-offload %s" % ("on" if enable else "off") - return ethtool(self, "-K", args, fail=fail) - -################################################################################ -def clean_up(): - global files, netns, devs - - for dev in devs: - dev.remove() - for f in files: - cmd("rm -f %s" % (f)) - for ns in netns: - cmd("ip netns delete %s" % (ns)) - files = [] - netns = [] - -def pin_prog(file_name, idx=0): - progs = bpftool_prog_list(expected=(idx + 1)) - prog = progs[idx] - bpftool("prog pin id %d %s" % (prog["id"], file_name)) - files.append(file_name) - - return file_name, bpf_pinned(file_name) - -def pin_map(file_name, idx=0, expected=1): - maps = bpftool_map_list(expected=expected) - m = maps[idx] - bpftool("map pin id %d %s" % (m["id"], file_name)) - files.append(file_name) - - return file_name, bpf_pinned(file_name) - -def check_dev_info_removed(prog_file=None, map_file=None): - bpftool_prog_list(expected=0) - bpftool_prog_list(expected=1, exclude_orphaned=False) - ret, err = bpftool("prog show pin %s" % (prog_file), fail=False) - fail(ret != 0, "failed to show prog with removed device") - - bpftool_map_list(expected=0) - ret, err = bpftool("map show pin %s" % (map_file), fail=False) - fail(ret == 0, "Showing map with removed device did not fail") - fail(err["error"].find("No such device") == -1, - "Showing map with removed device expected ENODEV, error is %s" % - (err["error"])) - -def check_dev_info(other_ns, ns, prog_file=None, map_file=None, removed=False): - progs = bpftool_prog_list(expected=1, ns=ns) - prog = progs[0] - - fail("dev" not in prog.keys(), "Device parameters not reported") - dev = prog["dev"] - fail("ifindex" not in dev.keys(), "Device parameters not reported") - fail("ns_dev" not in dev.keys(), "Device parameters not reported") - fail("ns_inode" not in dev.keys(), "Device parameters not reported") - - if not other_ns: - fail("ifname" not in dev.keys(), "Ifname not reported") - fail(dev["ifname"] != sim["ifname"], - "Ifname incorrect %s vs %s" % (dev["ifname"], sim["ifname"])) - else: - fail("ifname" in dev.keys(), "Ifname is reported for other ns") - - maps = bpftool_map_list(expected=2, ns=ns) - for m in maps: - fail("dev" not in m.keys(), "Device parameters not reported") - fail(dev != m["dev"], "Map's device different than program's") - -def check_extack(output, reference, args): - if skip_extack: - return - lines = output.split("\n") - comp = len(lines) >= 2 and lines[1] == 'Error: ' + reference - fail(not comp, "Missing or incorrect netlink extack message") - -def check_extack_nsim(output, reference, args): - check_extack(output, "netdevsim: " + reference, args) - -def check_no_extack(res, needle): - fail((res[1] + res[2]).count(needle) or (res[1] + res[2]).count("Warning:"), - "Found '%s' in command output, leaky extack?" % (needle)) - -def check_verifier_log(output, reference): - lines = output.split("\n") - for l in reversed(lines): - if l == reference: - return - fail(True, "Missing or incorrect message from netdevsim in verifier log") - -def check_multi_basic(two_xdps): - fail(two_xdps["mode"] != 4, "Bad mode reported with multiple programs") - fail("prog" in two_xdps, "Base program reported in multi program mode") - fail(len(two_xdps["attached"]) != 2, - "Wrong attached program count with two programs") - fail(two_xdps["attached"][0]["prog"]["id"] == - two_xdps["attached"][1]["prog"]["id"], - "Offloaded and other programs have the same id") - -def test_spurios_extack(sim, obj, skip_hw, needle): - res = sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=skip_hw, - include_stderr=True) - check_no_extack(res, needle) - res = sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, - skip_hw=skip_hw, include_stderr=True) - check_no_extack(res, needle) - res = sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf", - include_stderr=True) - check_no_extack(res, needle) - -def test_multi_prog(simdev, sim, obj, modename, modeid): - start_test("Test multi-attachment XDP - %s + offload..." % - (modename or "default", )) - sim.set_xdp(obj, "offload") - xdp = sim.ip_link_show(xdp=True)["xdp"] - offloaded = sim.dfs_read("bpf_offloaded_id") - fail("prog" not in xdp, "Base program not reported in single program mode") - fail(len(xdp["attached"]) != 1, - "Wrong attached program count with one program") - - sim.set_xdp(obj, modename) - two_xdps = sim.ip_link_show(xdp=True)["xdp"] - - fail(xdp["attached"][0] not in two_xdps["attached"], - "Offload program not reported after other activated") - check_multi_basic(two_xdps) - - offloaded2 = sim.dfs_read("bpf_offloaded_id") - fail(offloaded != offloaded2, - "Offload ID changed after loading other program") - - start_test("Test multi-attachment XDP - replace...") - ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True) - fail(ret == 0, "Replaced one of programs without -force") - check_extack(err, "XDP program already attached.", args) - - start_test("Test multi-attachment XDP - remove without mode...") - ret, _, err = sim.unset_xdp("", force=True, - fail=False, include_stderr=True) - fail(ret == 0, "Removed program without a mode flag") - check_extack(err, "More than one program loaded, unset mode is ambiguous.", args) - - sim.unset_xdp("offload") - xdp = sim.ip_link_show(xdp=True)["xdp"] - offloaded = sim.dfs_read("bpf_offloaded_id") - - fail(xdp["mode"] != modeid, "Bad mode reported after multiple programs") - fail("prog" not in xdp, - "Base program not reported after multi program mode") - fail(xdp["attached"][0] not in two_xdps["attached"], - "Offload program not reported after other activated") - fail(len(xdp["attached"]) != 1, - "Wrong attached program count with remaining programs") - fail(offloaded != "0", "Offload ID reported with only other program left") - - start_test("Test multi-attachment XDP - reattach...") - sim.set_xdp(obj, "offload") - two_xdps = sim.ip_link_show(xdp=True)["xdp"] - - fail(xdp["attached"][0] not in two_xdps["attached"], - "Other program not reported after offload activated") - check_multi_basic(two_xdps) - - start_test("Test multi-attachment XDP - device remove...") - simdev.remove() - - simdev = NetdevSimDev() - sim, = simdev.nsims - sim.set_ethtool_tc_offloads(True) - return [simdev, sim] - -# Parse command line -parser = argparse.ArgumentParser() -parser.add_argument("--log", help="output verbose log to given file") -args = parser.parse_args() -if args.log: - logfile = open(args.log, 'w+') - logfile.write("# -*-Org-*-") - -log("Prepare...", "", level=1) -log_level_inc() - -# Check permissions -skip(os.getuid() != 0, "test must be run as root") - -# Check tools -ret, progs = bpftool("prog", fail=False) -skip(ret != 0, "bpftool not installed") -base_progs = progs -_, base_maps = bpftool("map") -base_map_names = [ - 'pid_iter.rodata', # created on each bpftool invocation - 'libbpf_det_bind', # created on each bpftool invocation -] - -# Check netdevsim -if not os.path.isdir("/sys/bus/netdevsim/"): - ret, out = cmd("modprobe netdevsim", fail=False) - skip(ret != 0, "netdevsim module could not be loaded") - -# Check debugfs -_, out = cmd("mount") -if out.find("/sys/kernel/debug type debugfs") == -1: - cmd("mount -t debugfs none /sys/kernel/debug") - -# Check samples are compiled -samples = ["sample_ret0.bpf.o", "sample_map_ret0.bpf.o"] -for s in samples: - ret, out = cmd("ls %s/%s" % (bpf_test_dir, s), fail=False) - skip(ret != 0, "sample %s/%s not found, please compile it" % - (bpf_test_dir, s)) - -# Check if iproute2 is built with libmnl (needed by extack support) -_, _, err = cmd("tc qdisc delete dev lo handle 0", - fail=False, include_stderr=True) -if err.find("Error: Failed to find qdisc with specified handle.") == -1: - print("Warning: no extack message in iproute2 output, libmnl missing?") - log("Warning: no extack message in iproute2 output, libmnl missing?", "") - skip_extack = True - -# Check if net namespaces seem to work -ns = mknetns() -skip(ns is None, "Could not create a net namespace") -cmd("ip netns delete %s" % (ns)) -netns = [] - -try: - obj = bpf_obj("sample_ret0.bpf.o") - bytecode = bpf_bytecode("1,6 0 0 4294967295,") - - start_test("Test destruction of generic XDP...") - simdev = NetdevSimDev() - sim, = simdev.nsims - sim.set_xdp(obj, "generic") - simdev.remove() - bpftool_prog_list_wait(expected=0) - - simdev = NetdevSimDev() - sim, = simdev.nsims - sim.tc_add_ingress() - - start_test("Test TC non-offloaded...") - ret, _ = sim.cls_bpf_add_filter(obj, skip_hw=True, fail=False) - fail(ret != 0, "Software TC filter did not load") - - start_test("Test TC non-offloaded isn't getting bound...") - ret, _ = sim.cls_bpf_add_filter(obj, fail=False) - fail(ret != 0, "Software TC filter did not load") - simdev.dfs_get_bound_progs(expected=0) - - sim.tc_flush_filters() - - start_test("Test TC offloads are off by default...") - ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True, - fail=False, include_stderr=True) - fail(ret == 0, "TC filter loaded without enabling TC offloads") - check_extack(err, "TC offload is disabled on net device.", args) - sim.wait_for_flush() - - sim.set_ethtool_tc_offloads(True) - sim.dfs["bpf_tc_non_bound_accept"] = "Y" - - start_test("Test TC offload by default...") - ret, _ = sim.cls_bpf_add_filter(obj, fail=False) - fail(ret != 0, "Software TC filter did not load") - simdev.dfs_get_bound_progs(expected=0) - ingress = sim.tc_show_ingress(expected=1) - fltr = ingress[0] - fail(not fltr["in_hw"], "Filter not offloaded by default") - - sim.tc_flush_filters() - - start_test("Test TC cBPF bytcode tries offload by default...") - ret, _ = sim.cls_bpf_add_filter(bytecode, fail=False) - fail(ret != 0, "Software TC filter did not load") - simdev.dfs_get_bound_progs(expected=0) - ingress = sim.tc_show_ingress(expected=1) - fltr = ingress[0] - fail(not fltr["in_hw"], "Bytecode not offloaded by default") - - sim.tc_flush_filters() - sim.dfs["bpf_tc_non_bound_accept"] = "N" - - start_test("Test TC cBPF unbound bytecode doesn't offload...") - ret, _, err = sim.cls_bpf_add_filter(bytecode, skip_sw=True, - fail=False, include_stderr=True) - fail(ret == 0, "TC bytecode loaded for offload") - check_extack_nsim(err, "netdevsim configured to reject unbound programs.", - args) - sim.wait_for_flush() - - start_test("Test non-0 chain offload...") - ret, _, err = sim.cls_bpf_add_filter(obj, chain=1, prio=1, handle=1, - skip_sw=True, - fail=False, include_stderr=True) - fail(ret == 0, "Offloaded a filter to chain other than 0") - check_extack(err, "Driver supports only offload of chain 0.", args) - sim.tc_flush_filters() - - start_test("Test TC replace...") - sim.cls_bpf_add_filter(obj, prio=1, handle=1) - sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1) - sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf") - - sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_sw=True) - sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, skip_sw=True) - sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf") - - sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=True) - sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, skip_hw=True) - sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf") - - start_test("Test TC replace bad flags...") - for i in range(3): - for j in range(3): - ret, _ = sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, - skip_sw=(j == 1), skip_hw=(j == 2), - fail=False) - fail(bool(ret) != bool(j), - "Software TC incorrect load in replace test, iteration %d" % - (j)) - sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf") - - start_test("Test spurious extack from the driver...") - test_spurios_extack(sim, obj, False, "netdevsim") - test_spurios_extack(sim, obj, True, "netdevsim") - - sim.set_ethtool_tc_offloads(False) - - test_spurios_extack(sim, obj, False, "TC offload is disabled") - test_spurios_extack(sim, obj, True, "TC offload is disabled") - - sim.set_ethtool_tc_offloads(True) - - sim.tc_flush_filters() - - start_test("Test TC offloads failure...") - sim.dfs["dev/bpf_bind_verifier_accept"] = 0 - ret, _, err = sim.cls_bpf_add_filter(obj, verbose=True, skip_sw=True, - fail=False, include_stderr=True) - fail(ret == 0, "TC filter did not reject with TC offloads enabled") - check_verifier_log(err, "[netdevsim] Hello from netdevsim!") - sim.dfs["dev/bpf_bind_verifier_accept"] = 1 - - start_test("Test TC offloads work...") - ret, _, err = sim.cls_bpf_add_filter(obj, verbose=True, skip_sw=True, - fail=False, include_stderr=True) - fail(ret != 0, "TC filter did not load with TC offloads enabled") - - start_test("Test TC offload basics...") - dfs = simdev.dfs_get_bound_progs(expected=1) - progs = bpftool_prog_list(expected=1) - ingress = sim.tc_show_ingress(expected=1) - - dprog = dfs[0] - prog = progs[0] - fltr = ingress[0] - fail(fltr["skip_hw"], "TC does reports 'skip_hw' on offloaded filter") - fail(not fltr["in_hw"], "TC does not report 'in_hw' for offloaded filter") - fail(not fltr["skip_sw"], "TC does not report 'skip_sw' back") - - start_test("Test TC offload is device-bound...") - fail(str(prog["id"]) != fltr["id"], "Program IDs don't match") - fail(prog["tag"] != fltr["tag"], "Program tags don't match") - fail(fltr["id"] != dprog["id"], "Program IDs don't match") - fail(dprog["state"] != "xlated", "Offloaded program state not translated") - fail(dprog["loaded"] != "Y", "Offloaded program is not loaded") - - start_test("Test disabling TC offloads is rejected while filters installed...") - ret, _ = sim.set_ethtool_tc_offloads(False, fail=False) - fail(ret == 0, "Driver should refuse to disable TC offloads with filters installed...") - sim.set_ethtool_tc_offloads(True) - - start_test("Test qdisc removal frees things...") - sim.tc_flush_filters() - sim.tc_show_ingress(expected=0) - - start_test("Test disabling TC offloads is OK without filters...") - ret, _ = sim.set_ethtool_tc_offloads(False, fail=False) - fail(ret != 0, - "Driver refused to disable TC offloads without filters installed...") - - sim.set_ethtool_tc_offloads(True) - - start_test("Test destroying device gets rid of TC filters...") - sim.cls_bpf_add_filter(obj, skip_sw=True) - simdev.remove() - bpftool_prog_list_wait(expected=0) - - simdev = NetdevSimDev() - sim, = simdev.nsims - sim.set_ethtool_tc_offloads(True) - - start_test("Test destroying device gets rid of XDP...") - sim.set_xdp(obj, "offload") - simdev.remove() - bpftool_prog_list_wait(expected=0) - - simdev = NetdevSimDev() - sim, = simdev.nsims - sim.set_ethtool_tc_offloads(True) - - start_test("Test XDP prog reporting...") - sim.set_xdp(obj, "drv") - ipl = sim.ip_link_show(xdp=True) - progs = bpftool_prog_list(expected=1) - fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"], - "Loaded program has wrong ID") - - start_test("Test XDP prog replace without force...") - ret, _ = sim.set_xdp(obj, "drv", fail=False) - fail(ret == 0, "Replaced XDP program without -force") - sim.wait_for_flush(total=1) - - start_test("Test XDP prog replace with force...") - ret, _ = sim.set_xdp(obj, "drv", force=True, fail=False) - fail(ret != 0, "Could not replace XDP program with -force") - bpftool_prog_list_wait(expected=1) - ipl = sim.ip_link_show(xdp=True) - progs = bpftool_prog_list(expected=1) - fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"], - "Loaded program has wrong ID") - fail("dev" in progs[0].keys(), - "Device parameters reported for non-offloaded program") - - start_test("Test XDP prog replace with bad flags...") - ret, _, err = sim.set_xdp(obj, "generic", force=True, - fail=False, include_stderr=True) - fail(ret == 0, "Replaced XDP program with a program in different mode") - check_extack(err, - "Native and generic XDP can't be active at the same time.", - args) - - start_test("Test MTU restrictions...") - ret, _ = sim.set_mtu(9000, fail=False) - fail(ret == 0, - "Driver should refuse to increase MTU to 9000 with XDP loaded...") - sim.unset_xdp("drv") - bpftool_prog_list_wait(expected=0) - sim.set_mtu(9000) - ret, _, err = sim.set_xdp(obj, "drv", fail=False, include_stderr=True) - fail(ret == 0, "Driver should refuse to load program with MTU of 9000...") - check_extack_nsim(err, "MTU too large w/ XDP enabled.", args) - sim.set_mtu(1500) - - sim.wait_for_flush() - start_test("Test non-offload XDP attaching to HW...") - bpftool_prog_load("sample_ret0.bpf.o", "/sys/fs/bpf/nooffload") - nooffload = bpf_pinned("/sys/fs/bpf/nooffload") - ret, _, err = sim.set_xdp(nooffload, "offload", - fail=False, include_stderr=True) - fail(ret == 0, "attached non-offloaded XDP program to HW") - check_extack_nsim(err, "xdpoffload of non-bound program.", args) - rm("/sys/fs/bpf/nooffload") - - start_test("Test offload XDP attaching to drv...") - bpftool_prog_load("sample_ret0.bpf.o", "/sys/fs/bpf/offload", - dev=sim['ifname']) - offload = bpf_pinned("/sys/fs/bpf/offload") - ret, _, err = sim.set_xdp(offload, "drv", fail=False, include_stderr=True) - fail(ret == 0, "attached offloaded XDP program to drv") - check_extack(err, "Using offloaded program without HW_MODE flag is not supported.", args) - rm("/sys/fs/bpf/offload") - sim.wait_for_flush() - - start_test("Test XDP load failure...") - sim.dfs["dev/bpf_bind_verifier_accept"] = 0 - ret, _, err = bpftool_prog_load("sample_ret0.bpf.o", "/sys/fs/bpf/offload", - dev=sim['ifname'], fail=False, include_stderr=True) - fail(ret == 0, "verifier should fail on load") - check_verifier_log(err, "[netdevsim] Hello from netdevsim!") - sim.dfs["dev/bpf_bind_verifier_accept"] = 1 - sim.wait_for_flush() - - start_test("Test XDP offload...") - _, _, err = sim.set_xdp(obj, "offload", verbose=True, include_stderr=True) - ipl = sim.ip_link_show(xdp=True) - link_xdp = ipl["xdp"]["prog"] - progs = bpftool_prog_list(expected=1) - prog = progs[0] - fail(link_xdp["id"] != prog["id"], "Loaded program has wrong ID") - - start_test("Test XDP offload is device bound...") - dfs = simdev.dfs_get_bound_progs(expected=1) - dprog = dfs[0] - - fail(prog["id"] != link_xdp["id"], "Program IDs don't match") - fail(prog["tag"] != link_xdp["tag"], "Program tags don't match") - fail(str(link_xdp["id"]) != dprog["id"], "Program IDs don't match") - fail(dprog["state"] != "xlated", "Offloaded program state not translated") - fail(dprog["loaded"] != "Y", "Offloaded program is not loaded") - - start_test("Test removing XDP program many times...") - sim.unset_xdp("offload") - sim.unset_xdp("offload") - sim.unset_xdp("drv") - sim.unset_xdp("drv") - sim.unset_xdp("") - sim.unset_xdp("") - bpftool_prog_list_wait(expected=0) - - start_test("Test attempt to use a program for a wrong device...") - simdev2 = NetdevSimDev() - sim2, = simdev2.nsims - sim2.set_xdp(obj, "offload") - pin_file, pinned = pin_prog("/sys/fs/bpf/tmp") - - ret, _, err = sim.set_xdp(pinned, "offload", - fail=False, include_stderr=True) - fail(ret == 0, "Pinned program loaded for a different device accepted") - check_extack(err, "Program bound to different device.", args) - simdev2.remove() - ret, _, err = sim.set_xdp(pinned, "offload", - fail=False, include_stderr=True) - fail(ret == 0, "Pinned program loaded for a removed device accepted") - check_extack(err, "Program bound to different device.", args) - rm(pin_file) - bpftool_prog_list_wait(expected=0) - - simdev, sim = test_multi_prog(simdev, sim, obj, "", 1) - simdev, sim = test_multi_prog(simdev, sim, obj, "drv", 1) - simdev, sim = test_multi_prog(simdev, sim, obj, "generic", 2) - - start_test("Test mixing of TC and XDP...") - sim.tc_add_ingress() - sim.set_xdp(obj, "offload") - ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True, - fail=False, include_stderr=True) - fail(ret == 0, "Loading TC when XDP active should fail") - check_extack_nsim(err, "driver and netdev offload states mismatch.", args) - sim.unset_xdp("offload") - sim.wait_for_flush() - - sim.cls_bpf_add_filter(obj, skip_sw=True) - ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True) - fail(ret == 0, "Loading XDP when TC active should fail") - check_extack_nsim(err, "TC program is already loaded.", args) - - start_test("Test binding TC from pinned...") - pin_file, pinned = pin_prog("/sys/fs/bpf/tmp") - sim.tc_flush_filters(bound=1, total=1) - sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True) - sim.tc_flush_filters(bound=1, total=1) - - start_test("Test binding XDP from pinned...") - sim.set_xdp(obj, "offload") - pin_file, pinned = pin_prog("/sys/fs/bpf/tmp2", idx=1) - - sim.set_xdp(pinned, "offload", force=True) - sim.unset_xdp("offload") - sim.set_xdp(pinned, "offload", force=True) - sim.unset_xdp("offload") - - start_test("Test offload of wrong type fails...") - ret, _ = sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True, fail=False) - fail(ret == 0, "Managed to attach XDP program to TC") - - start_test("Test asking for TC offload of two filters...") - sim.cls_bpf_add_filter(obj, da=True, skip_sw=True) - ret, _, err = sim.cls_bpf_add_filter(obj, da=True, skip_sw=True, - fail=False, include_stderr=True) - fail(ret == 0, "Managed to offload two TC filters at the same time") - check_extack_nsim(err, "driver and netdev offload states mismatch.", args) - - sim.tc_flush_filters(bound=2, total=2) - - start_test("Test if netdev removal waits for translation...") - delay_msec = 500 - sim.dfs["dev/bpf_bind_verifier_delay"] = delay_msec - start = time.time() - cmd_line = "tc filter add dev %s ingress bpf %s da skip_sw" % \ - (sim['ifname'], obj) - tc_proc = cmd(cmd_line, background=True, fail=False) - # Wait for the verifier to start - while simdev.dfs_num_bound_progs() <= 2: - pass - simdev.remove() - end = time.time() - ret, _ = cmd_result(tc_proc, fail=False) - time_diff = end - start - log("Time", "start:\t%s\nend:\t%s\ndiff:\t%s" % (start, end, time_diff)) - - fail(ret == 0, "Managed to load TC filter on a unregistering device") - delay_sec = delay_msec * 0.001 - fail(time_diff < delay_sec, "Removal process took %s, expected %s" % - (time_diff, delay_sec)) - - # Remove all pinned files and reinstantiate the netdev - clean_up() - bpftool_prog_list_wait(expected=0) - - simdev = NetdevSimDev() - sim, = simdev.nsims - map_obj = bpf_obj("sample_map_ret0.bpf.o") - start_test("Test loading program with maps...") - sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON - - start_test("Test bpftool bound info reporting (own ns)...") - check_dev_info(False, "") - - start_test("Test bpftool bound info reporting (other ns)...") - ns = mknetns() - sim.set_ns(ns) - check_dev_info(True, "") - - start_test("Test bpftool bound info reporting (remote ns)...") - check_dev_info(False, ns) - - start_test("Test bpftool bound info reporting (back to own ns)...") - sim.set_ns("") - check_dev_info(False, "") - - prog_file, _ = pin_prog("/sys/fs/bpf/tmp_prog") - map_file, _ = pin_map("/sys/fs/bpf/tmp_map", idx=1, expected=2) - simdev.remove() - - start_test("Test bpftool bound info reporting (removed dev)...") - check_dev_info_removed(prog_file=prog_file, map_file=map_file) - - # Remove all pinned files and reinstantiate the netdev - clean_up() - bpftool_prog_list_wait(expected=0) - - simdev = NetdevSimDev() - sim, = simdev.nsims - - start_test("Test map update (no flags)...") - sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON - maps = bpftool_map_list(expected=2) - array = maps[0] if maps[0]["type"] == "array" else maps[1] - htab = maps[0] if maps[0]["type"] == "hash" else maps[1] - for m in maps: - for i in range(2): - bpftool("map update id %d key %s value %s" % - (m["id"], int2str("I", i), int2str("Q", i * 3))) - - for m in maps: - ret, _ = bpftool("map update id %d key %s value %s" % - (m["id"], int2str("I", 3), int2str("Q", 3 * 3)), - fail=False) - fail(ret == 0, "added too many entries") - - start_test("Test map update (exists)...") - for m in maps: - for i in range(2): - bpftool("map update id %d key %s value %s exist" % - (m["id"], int2str("I", i), int2str("Q", i * 3))) - - for m in maps: - ret, err = bpftool("map update id %d key %s value %s exist" % - (m["id"], int2str("I", 3), int2str("Q", 3 * 3)), - fail=False) - fail(ret == 0, "updated non-existing key") - fail(err["error"].find("No such file or directory") == -1, - "expected ENOENT, error is '%s'" % (err["error"])) - - start_test("Test map update (noexist)...") - for m in maps: - for i in range(2): - ret, err = bpftool("map update id %d key %s value %s noexist" % - (m["id"], int2str("I", i), int2str("Q", i * 3)), - fail=False) - fail(ret == 0, "updated existing key") - fail(err["error"].find("File exists") == -1, - "expected EEXIST, error is '%s'" % (err["error"])) - - start_test("Test map dump...") - for m in maps: - _, entries = bpftool("map dump id %d" % (m["id"])) - for i in range(2): - key = str2int(entries[i]["key"]) - fail(key != i, "expected key %d, got %d" % (key, i)) - val = str2int(entries[i]["value"]) - fail(val != i * 3, "expected value %d, got %d" % (val, i * 3)) - - start_test("Test map getnext...") - for m in maps: - _, entry = bpftool("map getnext id %d" % (m["id"])) - key = str2int(entry["next_key"]) - fail(key != 0, "next key %d, expected %d" % (key, 0)) - _, entry = bpftool("map getnext id %d key %s" % - (m["id"], int2str("I", 0))) - key = str2int(entry["next_key"]) - fail(key != 1, "next key %d, expected %d" % (key, 1)) - ret, err = bpftool("map getnext id %d key %s" % - (m["id"], int2str("I", 1)), fail=False) - fail(ret == 0, "got next key past the end of map") - fail(err["error"].find("No such file or directory") == -1, - "expected ENOENT, error is '%s'" % (err["error"])) - - start_test("Test map delete (htab)...") - for i in range(2): - bpftool("map delete id %d key %s" % (htab["id"], int2str("I", i))) - - start_test("Test map delete (array)...") - for i in range(2): - ret, err = bpftool("map delete id %d key %s" % - (htab["id"], int2str("I", i)), fail=False) - fail(ret == 0, "removed entry from an array") - fail(err["error"].find("No such file or directory") == -1, - "expected ENOENT, error is '%s'" % (err["error"])) - - start_test("Test map remove...") - sim.unset_xdp("offload") - bpftool_map_list_wait(expected=0) - simdev.remove() - - simdev = NetdevSimDev() - sim, = simdev.nsims - sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON - simdev.remove() - bpftool_map_list_wait(expected=0) - - start_test("Test map creation fail path...") - simdev = NetdevSimDev() - sim, = simdev.nsims - sim.dfs["bpf_map_accept"] = "N" - ret, _ = sim.set_xdp(map_obj, "offload", JSON=False, fail=False) - fail(ret == 0, - "netdevsim didn't refuse to create a map with offload disabled") - - simdev.remove() - - start_test("Test multi-dev ASIC program reuse...") - simdevA = NetdevSimDev() - simA, = simdevA.nsims - simdevB = NetdevSimDev(3) - simB1, simB2, simB3 = simdevB.nsims - sims = (simA, simB1, simB2, simB3) - simB = (simB1, simB2, simB3) - - bpftool_prog_load("sample_map_ret0.bpf.o", "/sys/fs/bpf/nsimA", - dev=simA['ifname']) - progA = bpf_pinned("/sys/fs/bpf/nsimA") - bpftool_prog_load("sample_map_ret0.bpf.o", "/sys/fs/bpf/nsimB", - dev=simB1['ifname']) - progB = bpf_pinned("/sys/fs/bpf/nsimB") - - simA.set_xdp(progA, "offload", JSON=False) - for d in simdevB.nsims: - d.set_xdp(progB, "offload", JSON=False) - - start_test("Test multi-dev ASIC cross-dev replace...") - ret, _ = simA.set_xdp(progB, "offload", force=True, JSON=False, fail=False) - fail(ret == 0, "cross-ASIC program allowed") - for d in simdevB.nsims: - ret, _ = d.set_xdp(progA, "offload", force=True, JSON=False, fail=False) - fail(ret == 0, "cross-ASIC program allowed") - - start_test("Test multi-dev ASIC cross-dev install...") - for d in sims: - d.unset_xdp("offload") - - ret, _, err = simA.set_xdp(progB, "offload", force=True, JSON=False, - fail=False, include_stderr=True) - fail(ret == 0, "cross-ASIC program allowed") - check_extack(err, "Program bound to different device.", args) - for d in simdevB.nsims: - ret, _, err = d.set_xdp(progA, "offload", force=True, JSON=False, - fail=False, include_stderr=True) - fail(ret == 0, "cross-ASIC program allowed") - check_extack(err, "Program bound to different device.", args) - - start_test("Test multi-dev ASIC cross-dev map reuse...") - - mapA = bpftool("prog show %s" % (progA))[1]["map_ids"][0] - mapB = bpftool("prog show %s" % (progB))[1]["map_ids"][0] - - ret, _ = bpftool_prog_load("sample_map_ret0.bpf.o", "/sys/fs/bpf/nsimB_", - dev=simB3['ifname'], - maps=["idx 0 id %d" % (mapB)], - fail=False) - fail(ret != 0, "couldn't reuse a map on the same ASIC") - rm("/sys/fs/bpf/nsimB_") - - ret, _, err = bpftool_prog_load("sample_map_ret0.bpf.o", "/sys/fs/bpf/nsimA_", - dev=simA['ifname'], - maps=["idx 0 id %d" % (mapB)], - fail=False, include_stderr=True) - fail(ret == 0, "could reuse a map on a different ASIC") - fail(err.count("offload device mismatch between prog and map") == 0, - "error message missing for cross-ASIC map") - - ret, _, err = bpftool_prog_load("sample_map_ret0.bpf.o", "/sys/fs/bpf/nsimB_", - dev=simB1['ifname'], - maps=["idx 0 id %d" % (mapA)], - fail=False, include_stderr=True) - fail(ret == 0, "could reuse a map on a different ASIC") - fail(err.count("offload device mismatch between prog and map") == 0, - "error message missing for cross-ASIC map") - - start_test("Test multi-dev ASIC cross-dev destruction...") - bpftool_prog_list_wait(expected=2) - - simdevA.remove() - bpftool_prog_list_wait(expected=1) - - ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"] - fail(ifnameB != simB1['ifname'], "program not bound to original device") - simB1.remove() - bpftool_prog_list_wait(expected=1) - - start_test("Test multi-dev ASIC cross-dev destruction - move...") - ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"] - fail(ifnameB not in (simB2['ifname'], simB3['ifname']), - "program not bound to remaining devices") - - simB2.remove() - ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"] - fail(ifnameB != simB3['ifname'], "program not bound to remaining device") - - simB3.remove() - simdevB.remove() - bpftool_prog_list_wait(expected=0) - - start_test("Test multi-dev ASIC cross-dev destruction - orphaned...") - ret, out = bpftool("prog show %s" % (progB), fail=False) - fail(ret != 0, "couldn't get information about orphaned program") - - print("%s: OK" % (os.path.basename(__file__))) - -finally: - log("Clean up...", "", level=1) - log_level_inc() - clean_up() |