Linux Audio

Check our new training course

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