Linux Audio

Check our new training course

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