cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

test_offload.py (51878B)


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