1417ec264SJakub Kicinski#!/usr/bin/python3
2417ec264SJakub Kicinski
3417ec264SJakub Kicinski# Copyright (C) 2017 Netronome Systems, Inc.
4e05b2d14SJiri Pirko# Copyright (c) 2019 Mellanox Technologies. All rights reserved
5417ec264SJakub Kicinski#
6417ec264SJakub Kicinski# This software is licensed under the GNU General License Version 2,
7417ec264SJakub Kicinski# June 1991 as shown in the file COPYING in the top-level directory of this
8417ec264SJakub Kicinski# source tree.
9417ec264SJakub Kicinski#
10417ec264SJakub Kicinski# THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
11417ec264SJakub Kicinski# WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
12417ec264SJakub Kicinski# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
13417ec264SJakub Kicinski# FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
14417ec264SJakub Kicinski# OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
15417ec264SJakub Kicinski# THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16417ec264SJakub Kicinski
17417ec264SJakub Kicinskifrom datetime import datetime
18417ec264SJakub Kicinskiimport argparse
19e05b2d14SJiri Pirkoimport errno
20417ec264SJakub Kicinskiimport json
21417ec264SJakub Kicinskiimport os
22417ec264SJakub Kicinskiimport pprint
23752d7b45SJakub Kicinskiimport random
24e05b2d14SJiri Pirkoimport re
258101e069SJakub Kicinskiimport stat
26752d7b45SJakub Kicinskiimport string
277fedbb7cSJakub Kicinskiimport struct
28417ec264SJakub Kicinskiimport subprocess
29417ec264SJakub Kicinskiimport time
30d7f35638SJakub Kicinskiimport traceback
31417ec264SJakub Kicinski
32417ec264SJakub Kicinskilogfile = None
33417ec264SJakub Kicinskilog_level = 1
34caf95228SQuentin Monnetskip_extack = False
35417ec264SJakub Kicinskibpf_test_dir = os.path.dirname(os.path.realpath(__file__))
36417ec264SJakub Kicinskipp = pprint.PrettyPrinter()
37417ec264SJakub Kicinskidevs = [] # devices we created for clean up
38417ec264SJakub Kicinskifiles = [] # files to be removed
39752d7b45SJakub Kicinskinetns = [] # net namespaces to be removed
40417ec264SJakub Kicinski
41417ec264SJakub Kicinskidef log_get_sec(level=0):
42417ec264SJakub Kicinski    return "*" * (log_level + level)
43417ec264SJakub Kicinski
44417ec264SJakub Kicinskidef log_level_inc(add=1):
45417ec264SJakub Kicinski    global log_level
46417ec264SJakub Kicinski    log_level += add
47417ec264SJakub Kicinski
48417ec264SJakub Kicinskidef log_level_dec(sub=1):
49417ec264SJakub Kicinski    global log_level
50417ec264SJakub Kicinski    log_level -= sub
51417ec264SJakub Kicinski
52417ec264SJakub Kicinskidef log_level_set(level):
53417ec264SJakub Kicinski    global log_level
54417ec264SJakub Kicinski    log_level = level
55417ec264SJakub Kicinski
56417ec264SJakub Kicinskidef log(header, data, level=None):
57417ec264SJakub Kicinski    """
58417ec264SJakub Kicinski    Output to an optional log.
59417ec264SJakub Kicinski    """
60417ec264SJakub Kicinski    if logfile is None:
61417ec264SJakub Kicinski        return
62417ec264SJakub Kicinski    if level is not None:
63417ec264SJakub Kicinski        log_level_set(level)
64417ec264SJakub Kicinski
65417ec264SJakub Kicinski    if not isinstance(data, str):
66417ec264SJakub Kicinski        data = pp.pformat(data)
67417ec264SJakub Kicinski
68417ec264SJakub Kicinski    if len(header):
69417ec264SJakub Kicinski        logfile.write("\n" + log_get_sec() + " ")
70417ec264SJakub Kicinski        logfile.write(header)
71417ec264SJakub Kicinski    if len(header) and len(data.strip()):
72417ec264SJakub Kicinski        logfile.write("\n")
73417ec264SJakub Kicinski    logfile.write(data)
74417ec264SJakub Kicinski
75417ec264SJakub Kicinskidef skip(cond, msg):
76417ec264SJakub Kicinski    if not cond:
77417ec264SJakub Kicinski        return
78417ec264SJakub Kicinski    print("SKIP: " + msg)
79417ec264SJakub Kicinski    log("SKIP: " + msg, "", level=1)
80417ec264SJakub Kicinski    os.sys.exit(0)
81417ec264SJakub Kicinski
82417ec264SJakub Kicinskidef fail(cond, msg):
83417ec264SJakub Kicinski    if not cond:
84417ec264SJakub Kicinski        return
85417ec264SJakub Kicinski    print("FAIL: " + msg)
86d7f35638SJakub Kicinski    tb = "".join(traceback.extract_stack().format())
87d7f35638SJakub Kicinski    print(tb)
88d7f35638SJakub Kicinski    log("FAIL: " + msg, tb, level=1)
89417ec264SJakub Kicinski    os.sys.exit(1)
90417ec264SJakub Kicinski
91417ec264SJakub Kicinskidef start_test(msg):
92417ec264SJakub Kicinski    log(msg, "", level=1)
93417ec264SJakub Kicinski    log_level_inc()
94417ec264SJakub Kicinski    print(msg)
95417ec264SJakub Kicinski
96417ec264SJakub Kicinskidef cmd(cmd, shell=True, include_stderr=False, background=False, fail=True):
97417ec264SJakub Kicinski    """
98417ec264SJakub Kicinski    Run a command in subprocess and return tuple of (retval, stdout);
99417ec264SJakub Kicinski    optionally return stderr as well as third value.
100417ec264SJakub Kicinski    """
101417ec264SJakub Kicinski    proc = subprocess.Popen(cmd, shell=shell, stdout=subprocess.PIPE,
102417ec264SJakub Kicinski                            stderr=subprocess.PIPE)
103417ec264SJakub Kicinski    if background:
104417ec264SJakub Kicinski        msg = "%s START: %s" % (log_get_sec(1),
105417ec264SJakub Kicinski                                datetime.now().strftime("%H:%M:%S.%f"))
106417ec264SJakub Kicinski        log("BKG " + proc.args, msg)
107417ec264SJakub Kicinski        return proc
108417ec264SJakub Kicinski
109417ec264SJakub Kicinski    return cmd_result(proc, include_stderr=include_stderr, fail=fail)
110417ec264SJakub Kicinski
111417ec264SJakub Kicinskidef cmd_result(proc, include_stderr=False, fail=False):
112417ec264SJakub Kicinski    stdout, stderr = proc.communicate()
113417ec264SJakub Kicinski    stdout = stdout.decode("utf-8")
114417ec264SJakub Kicinski    stderr = stderr.decode("utf-8")
115417ec264SJakub Kicinski    proc.stdout.close()
116417ec264SJakub Kicinski    proc.stderr.close()
117417ec264SJakub Kicinski
118417ec264SJakub Kicinski    stderr = "\n" + stderr
119417ec264SJakub Kicinski    if stderr[-1] == "\n":
120417ec264SJakub Kicinski        stderr = stderr[:-1]
121417ec264SJakub Kicinski
122417ec264SJakub Kicinski    sec = log_get_sec(1)
123417ec264SJakub Kicinski    log("CMD " + proc.args,
124417ec264SJakub Kicinski        "RETCODE: %d\n%s STDOUT:\n%s%s STDERR:%s\n%s END: %s" %
125417ec264SJakub Kicinski        (proc.returncode, sec, stdout, sec, stderr,
126417ec264SJakub Kicinski         sec, datetime.now().strftime("%H:%M:%S.%f")))
127417ec264SJakub Kicinski
128417ec264SJakub Kicinski    if proc.returncode != 0 and fail:
129417ec264SJakub Kicinski        if len(stderr) > 0 and stderr[-1] == "\n":
130417ec264SJakub Kicinski            stderr = stderr[:-1]
131417ec264SJakub Kicinski        raise Exception("Command failed: %s\n%s" % (proc.args, stderr))
132417ec264SJakub Kicinski
133417ec264SJakub Kicinski    if include_stderr:
134417ec264SJakub Kicinski        return proc.returncode, stdout, stderr
135417ec264SJakub Kicinski    else:
136417ec264SJakub Kicinski        return proc.returncode, stdout
137417ec264SJakub Kicinski
138417ec264SJakub Kicinskidef rm(f):
139417ec264SJakub Kicinski    cmd("rm -f %s" % (f))
140417ec264SJakub Kicinski    if f in files:
141417ec264SJakub Kicinski        files.remove(f)
142417ec264SJakub Kicinski
143caf95228SQuentin Monnetdef tool(name, args, flags, JSON=True, ns="", fail=True, include_stderr=False):
144417ec264SJakub Kicinski    params = ""
145417ec264SJakub Kicinski    if JSON:
146417ec264SJakub Kicinski        params += "%s " % (flags["json"])
147417ec264SJakub Kicinski
148752d7b45SJakub Kicinski    if ns != "":
149752d7b45SJakub Kicinski        ns = "ip netns exec %s " % (ns)
150752d7b45SJakub Kicinski
151caf95228SQuentin Monnet    if include_stderr:
152caf95228SQuentin Monnet        ret, stdout, stderr = cmd(ns + name + " " + params + args,
153caf95228SQuentin Monnet                                  fail=fail, include_stderr=True)
154caf95228SQuentin Monnet    else:
155caf95228SQuentin Monnet        ret, stdout = cmd(ns + name + " " + params + args,
156caf95228SQuentin Monnet                          fail=fail, include_stderr=False)
157caf95228SQuentin Monnet
158caf95228SQuentin Monnet    if JSON and len(stdout.strip()) != 0:
159caf95228SQuentin Monnet        out = json.loads(stdout)
160caf95228SQuentin Monnet    else:
161caf95228SQuentin Monnet        out = stdout
162caf95228SQuentin Monnet
163caf95228SQuentin Monnet    if include_stderr:
164caf95228SQuentin Monnet        return ret, out, stderr
165417ec264SJakub Kicinski    else:
166417ec264SJakub Kicinski        return ret, out
167417ec264SJakub Kicinski
1687736b6edSJakub Kicinskidef bpftool(args, JSON=True, ns="", fail=True, include_stderr=False):
1697736b6edSJakub Kicinski    return tool("bpftool", args, {"json":"-p"}, JSON=JSON, ns=ns,
1707736b6edSJakub Kicinski                fail=fail, include_stderr=include_stderr)
171417ec264SJakub Kicinski
172752d7b45SJakub Kicinskidef bpftool_prog_list(expected=None, ns=""):
173752d7b45SJakub Kicinski    _, progs = bpftool("prog show", JSON=True, ns=ns, fail=True)
17447cf52a2SJakub Kicinski    # Remove the base progs
17547cf52a2SJakub Kicinski    for p in base_progs:
17647cf52a2SJakub Kicinski        if p in progs:
17747cf52a2SJakub Kicinski            progs.remove(p)
178417ec264SJakub Kicinski    if expected is not None:
179417ec264SJakub Kicinski        if len(progs) != expected:
180417ec264SJakub Kicinski            fail(True, "%d BPF programs loaded, expected %d" %
181417ec264SJakub Kicinski                 (len(progs), expected))
182417ec264SJakub Kicinski    return progs
183417ec264SJakub Kicinski
1847fedbb7cSJakub Kicinskidef bpftool_map_list(expected=None, ns=""):
1857fedbb7cSJakub Kicinski    _, maps = bpftool("map show", JSON=True, ns=ns, fail=True)
18647cf52a2SJakub Kicinski    # Remove the base maps
18747cf52a2SJakub Kicinski    for m in base_maps:
18847cf52a2SJakub Kicinski        if m in maps:
18947cf52a2SJakub Kicinski            maps.remove(m)
1907fedbb7cSJakub Kicinski    if expected is not None:
1917fedbb7cSJakub Kicinski        if len(maps) != expected:
1927fedbb7cSJakub Kicinski            fail(True, "%d BPF maps loaded, expected %d" %
1937fedbb7cSJakub Kicinski                 (len(maps), expected))
1947fedbb7cSJakub Kicinski    return maps
1957fedbb7cSJakub Kicinski
196417ec264SJakub Kicinskidef bpftool_prog_list_wait(expected=0, n_retry=20):
197417ec264SJakub Kicinski    for i in range(n_retry):
198417ec264SJakub Kicinski        nprogs = len(bpftool_prog_list())
199417ec264SJakub Kicinski        if nprogs == expected:
200417ec264SJakub Kicinski            return
201417ec264SJakub Kicinski        time.sleep(0.05)
202417ec264SJakub Kicinski    raise Exception("Time out waiting for program counts to stabilize want %d, have %d" % (expected, nprogs))
203417ec264SJakub Kicinski
2047fedbb7cSJakub Kicinskidef bpftool_map_list_wait(expected=0, n_retry=20):
2057fedbb7cSJakub Kicinski    for i in range(n_retry):
2067fedbb7cSJakub Kicinski        nmaps = len(bpftool_map_list())
2077fedbb7cSJakub Kicinski        if nmaps == expected:
2087fedbb7cSJakub Kicinski            return
2097fedbb7cSJakub Kicinski        time.sleep(0.05)
2107fedbb7cSJakub Kicinski    raise Exception("Time out waiting for map counts to stabilize want %d, have %d" % (expected, nmaps))
2117fedbb7cSJakub Kicinski
2127736b6edSJakub Kicinskidef bpftool_prog_load(sample, file_name, maps=[], prog_type="xdp", dev=None,
2137736b6edSJakub Kicinski                      fail=True, include_stderr=False):
2147736b6edSJakub Kicinski    args = "prog load %s %s" % (os.path.join(bpf_test_dir, sample), file_name)
2157736b6edSJakub Kicinski    if prog_type is not None:
2167736b6edSJakub Kicinski        args += " type " + prog_type
2177736b6edSJakub Kicinski    if dev is not None:
2187736b6edSJakub Kicinski        args += " dev " + dev
2197736b6edSJakub Kicinski    if len(maps):
2207736b6edSJakub Kicinski        args += " map " + " map ".join(maps)
2217736b6edSJakub Kicinski
2227736b6edSJakub Kicinski    res = bpftool(args, fail=fail, include_stderr=include_stderr)
2237736b6edSJakub Kicinski    if res[0] == 0:
2247736b6edSJakub Kicinski        files.append(file_name)
2257736b6edSJakub Kicinski    return res
2267736b6edSJakub Kicinski
227caf95228SQuentin Monnetdef ip(args, force=False, JSON=True, ns="", fail=True, include_stderr=False):
228417ec264SJakub Kicinski    if force:
229417ec264SJakub Kicinski        args = "-force " + args
230caf95228SQuentin Monnet    return tool("ip", args, {"json":"-j"}, JSON=JSON, ns=ns,
231caf95228SQuentin Monnet                fail=fail, include_stderr=include_stderr)
232417ec264SJakub Kicinski
233caf95228SQuentin Monnetdef tc(args, JSON=True, ns="", fail=True, include_stderr=False):
234caf95228SQuentin Monnet    return tool("tc", args, {"json":"-p"}, JSON=JSON, ns=ns,
235caf95228SQuentin Monnet                fail=fail, include_stderr=include_stderr)
236417ec264SJakub Kicinski
237417ec264SJakub Kicinskidef ethtool(dev, opt, args, fail=True):
238417ec264SJakub Kicinski    return cmd("ethtool %s %s %s" % (opt, dev["ifname"], args), fail=fail)
239417ec264SJakub Kicinski
240417ec264SJakub Kicinskidef bpf_obj(name, sec=".text", path=bpf_test_dir,):
241417ec264SJakub Kicinski    return "obj %s sec %s" % (os.path.join(path, name), sec)
242417ec264SJakub Kicinski
243417ec264SJakub Kicinskidef bpf_pinned(name):
244417ec264SJakub Kicinski    return "pinned %s" % (name)
245417ec264SJakub Kicinski
246417ec264SJakub Kicinskidef bpf_bytecode(bytecode):
247417ec264SJakub Kicinski    return "bytecode \"%s\"" % (bytecode)
248417ec264SJakub Kicinski
249752d7b45SJakub Kicinskidef mknetns(n_retry=10):
250752d7b45SJakub Kicinski    for i in range(n_retry):
251752d7b45SJakub Kicinski        name = ''.join([random.choice(string.ascii_letters) for i in range(8)])
252752d7b45SJakub Kicinski        ret, _ = ip("netns add %s" % (name), fail=False)
253752d7b45SJakub Kicinski        if ret == 0:
254752d7b45SJakub Kicinski            netns.append(name)
255752d7b45SJakub Kicinski            return name
256752d7b45SJakub Kicinski    return None
257752d7b45SJakub Kicinski
2587fedbb7cSJakub Kicinskidef int2str(fmt, val):
2597fedbb7cSJakub Kicinski    ret = []
2607fedbb7cSJakub Kicinski    for b in struct.pack(fmt, val):
2617fedbb7cSJakub Kicinski        ret.append(int(b))
2627fedbb7cSJakub Kicinski    return " ".join(map(lambda x: str(x), ret))
2637fedbb7cSJakub Kicinski
2647fedbb7cSJakub Kicinskidef str2int(strtab):
2657fedbb7cSJakub Kicinski    inttab = []
2667fedbb7cSJakub Kicinski    for i in strtab:
2677fedbb7cSJakub Kicinski        inttab.append(int(i, 16))
2687fedbb7cSJakub Kicinski    ba = bytearray(inttab)
2697fedbb7cSJakub Kicinski    if len(strtab) == 4:
2707fedbb7cSJakub Kicinski        fmt = "I"
2717fedbb7cSJakub Kicinski    elif len(strtab) == 8:
2727fedbb7cSJakub Kicinski        fmt = "Q"
2737fedbb7cSJakub Kicinski    else:
2747fedbb7cSJakub Kicinski        raise Exception("String array of len %d can't be unpacked to an int" %
2757fedbb7cSJakub Kicinski                        (len(strtab)))
2767fedbb7cSJakub Kicinski    return struct.unpack(fmt, ba)[0]
2777fedbb7cSJakub Kicinski
278417ec264SJakub Kicinskiclass DebugfsDir:
279417ec264SJakub Kicinski    """
280417ec264SJakub Kicinski    Class for accessing DebugFS directories as a dictionary.
281417ec264SJakub Kicinski    """
282417ec264SJakub Kicinski
283417ec264SJakub Kicinski    def __init__(self, path):
284417ec264SJakub Kicinski        self.path = path
285417ec264SJakub Kicinski        self._dict = self._debugfs_dir_read(path)
286417ec264SJakub Kicinski
287417ec264SJakub Kicinski    def __len__(self):
288417ec264SJakub Kicinski        return len(self._dict.keys())
289417ec264SJakub Kicinski
290417ec264SJakub Kicinski    def __getitem__(self, key):
291417ec264SJakub Kicinski        if type(key) is int:
292417ec264SJakub Kicinski            key = list(self._dict.keys())[key]
293417ec264SJakub Kicinski        return self._dict[key]
294417ec264SJakub Kicinski
295417ec264SJakub Kicinski    def __setitem__(self, key, value):
296417ec264SJakub Kicinski        log("DebugFS set %s = %s" % (key, value), "")
297417ec264SJakub Kicinski        log_level_inc()
298417ec264SJakub Kicinski
299417ec264SJakub Kicinski        cmd("echo '%s' > %s/%s" % (value, self.path, key))
300417ec264SJakub Kicinski        log_level_dec()
301417ec264SJakub Kicinski
302417ec264SJakub Kicinski        _, out = cmd('cat %s/%s' % (self.path, key))
303417ec264SJakub Kicinski        self._dict[key] = out.strip()
304417ec264SJakub Kicinski
305417ec264SJakub Kicinski    def _debugfs_dir_read(self, path):
306417ec264SJakub Kicinski        dfs = {}
307417ec264SJakub Kicinski
308417ec264SJakub Kicinski        log("DebugFS state for %s" % (path), "")
309417ec264SJakub Kicinski        log_level_inc(add=2)
310417ec264SJakub Kicinski
311417ec264SJakub Kicinski        _, out = cmd('ls ' + path)
312417ec264SJakub Kicinski        for f in out.split():
313ab1d0cc0SJiri Pirko            if f == "ports":
314ab1d0cc0SJiri Pirko                continue
3158101e069SJakub Kicinski
316417ec264SJakub Kicinski            p = os.path.join(path, f)
31756c1291eSDaniel Borkmann            if not os.stat(p).st_mode & stat.S_IRUSR:
31856c1291eSDaniel Borkmann                continue
31956c1291eSDaniel Borkmann
32056c1291eSDaniel Borkmann            if os.path.isfile(p):
3214bbca662SHangbin Liu                # We need to init trap_flow_action_cookie before read it
3224bbca662SHangbin Liu                if f == "trap_flow_action_cookie":
3234bbca662SHangbin Liu                    cmd('echo deadbeef > %s/%s' % (path, f))
324417ec264SJakub Kicinski                _, out = cmd('cat %s/%s' % (path, f))
325417ec264SJakub Kicinski                dfs[f] = out.strip()
326417ec264SJakub Kicinski            elif os.path.isdir(p):
327417ec264SJakub Kicinski                dfs[f] = DebugfsDir(p)
328417ec264SJakub Kicinski            else:
329417ec264SJakub Kicinski                raise Exception("%s is neither file nor directory" % (p))
330417ec264SJakub Kicinski
331417ec264SJakub Kicinski        log_level_dec()
332417ec264SJakub Kicinski        log("DebugFS state", dfs)
333417ec264SJakub Kicinski        log_level_dec()
334417ec264SJakub Kicinski
335417ec264SJakub Kicinski        return dfs
336417ec264SJakub Kicinski
337e05b2d14SJiri Pirkoclass NetdevSimDev:
338e05b2d14SJiri Pirko    """
339e05b2d14SJiri Pirko    Class for netdevsim bus device and its attributes.
340e05b2d14SJiri Pirko    """
341acceca8dSJakub Kicinski    @staticmethod
342acceca8dSJakub Kicinski    def ctrl_write(path, val):
343acceca8dSJakub Kicinski        fullpath = os.path.join("/sys/bus/netdevsim/", path)
344acceca8dSJakub Kicinski        try:
345acceca8dSJakub Kicinski            with open(fullpath, "w") as f:
346acceca8dSJakub Kicinski                f.write(val)
347acceca8dSJakub Kicinski        except OSError as e:
348acceca8dSJakub Kicinski            log("WRITE %s: %r" % (fullpath, val), -e.errno)
349acceca8dSJakub Kicinski            raise e
350acceca8dSJakub Kicinski        log("WRITE %s: %r" % (fullpath, val), 0)
351e05b2d14SJiri Pirko
352e05b2d14SJiri Pirko    def __init__(self, port_count=1):
353e05b2d14SJiri Pirko        addr = 0
354e05b2d14SJiri Pirko        while True:
355e05b2d14SJiri Pirko            try:
356acceca8dSJakub Kicinski                self.ctrl_write("new_device", "%u %u" % (addr, port_count))
357e05b2d14SJiri Pirko            except OSError as e:
358e05b2d14SJiri Pirko                if e.errno == errno.ENOSPC:
359e05b2d14SJiri Pirko                    addr += 1
360e05b2d14SJiri Pirko                    continue
361e05b2d14SJiri Pirko                raise e
362e05b2d14SJiri Pirko            break
363e05b2d14SJiri Pirko        self.addr = addr
364e05b2d14SJiri Pirko
365e05b2d14SJiri Pirko        # As probe of netdevsim device might happen from a workqueue,
366e05b2d14SJiri Pirko        # so wait here until all netdevs appear.
367e05b2d14SJiri Pirko        self.wait_for_netdevs(port_count)
368e05b2d14SJiri Pirko
369e05b2d14SJiri Pirko        ret, out = cmd("udevadm settle", fail=False)
370e05b2d14SJiri Pirko        if ret:
371e05b2d14SJiri Pirko            raise Exception("udevadm settle failed")
372e05b2d14SJiri Pirko        ifnames = self.get_ifnames()
373e05b2d14SJiri Pirko
374e05b2d14SJiri Pirko        devs.append(self)
375e05b2d14SJiri Pirko        self.dfs_dir = "/sys/kernel/debug/netdevsim/netdevsim%u/" % addr
376e05b2d14SJiri Pirko
377e05b2d14SJiri Pirko        self.nsims = []
378e05b2d14SJiri Pirko        for port_index in range(port_count):
379e05b2d14SJiri Pirko            self.nsims.append(NetdevSim(self, port_index, ifnames[port_index]))
380e05b2d14SJiri Pirko
381e05b2d14SJiri Pirko    def get_ifnames(self):
382e05b2d14SJiri Pirko        ifnames = []
383e05b2d14SJiri Pirko        listdir = os.listdir("/sys/bus/netdevsim/devices/netdevsim%u/net/" % self.addr)
384e05b2d14SJiri Pirko        for ifname in listdir:
385e05b2d14SJiri Pirko            ifnames.append(ifname)
386e05b2d14SJiri Pirko        ifnames.sort()
387e05b2d14SJiri Pirko        return ifnames
388e05b2d14SJiri Pirko
389e05b2d14SJiri Pirko    def wait_for_netdevs(self, port_count):
390e05b2d14SJiri Pirko        timeout = 5
391e05b2d14SJiri Pirko        timeout_start = time.time()
392e05b2d14SJiri Pirko
393e05b2d14SJiri Pirko        while True:
394e05b2d14SJiri Pirko            try:
395e05b2d14SJiri Pirko                ifnames = self.get_ifnames()
396e05b2d14SJiri Pirko            except FileNotFoundError as e:
397e05b2d14SJiri Pirko                ifnames = []
398e05b2d14SJiri Pirko            if len(ifnames) == port_count:
399e05b2d14SJiri Pirko                break
400e05b2d14SJiri Pirko            if time.time() < timeout_start + timeout:
401e05b2d14SJiri Pirko                continue
402e05b2d14SJiri Pirko            raise Exception("netdevices did not appear within timeout")
403e05b2d14SJiri Pirko
404e05b2d14SJiri Pirko    def dfs_num_bound_progs(self):
405e05b2d14SJiri Pirko        path = os.path.join(self.dfs_dir, "bpf_bound_progs")
406e05b2d14SJiri Pirko        _, progs = cmd('ls %s' % (path))
407e05b2d14SJiri Pirko        return len(progs.split())
408e05b2d14SJiri Pirko
409e05b2d14SJiri Pirko    def dfs_get_bound_progs(self, expected):
410e05b2d14SJiri Pirko        progs = DebugfsDir(os.path.join(self.dfs_dir, "bpf_bound_progs"))
411e05b2d14SJiri Pirko        if expected is not None:
412e05b2d14SJiri Pirko            if len(progs) != expected:
413e05b2d14SJiri Pirko                fail(True, "%d BPF programs bound, expected %d" %
414e05b2d14SJiri Pirko                     (len(progs), expected))
415e05b2d14SJiri Pirko        return progs
416e05b2d14SJiri Pirko
417e05b2d14SJiri Pirko    def remove(self):
418acceca8dSJakub Kicinski        self.ctrl_write("del_device", "%u" % (self.addr, ))
419e05b2d14SJiri Pirko        devs.remove(self)
420e05b2d14SJiri Pirko
421e05b2d14SJiri Pirko    def remove_nsim(self, nsim):
422e05b2d14SJiri Pirko        self.nsims.remove(nsim)
423acceca8dSJakub Kicinski        self.ctrl_write("devices/netdevsim%u/del_port" % (self.addr, ),
424acceca8dSJakub Kicinski                        "%u" % (nsim.port_index, ))
425e05b2d14SJiri Pirko
426417ec264SJakub Kicinskiclass NetdevSim:
427417ec264SJakub Kicinski    """
428417ec264SJakub Kicinski    Class for netdevsim netdevice and its attributes.
429417ec264SJakub Kicinski    """
430417ec264SJakub Kicinski
431e05b2d14SJiri Pirko    def __init__(self, nsimdev, port_index, ifname):
432e05b2d14SJiri Pirko        # In case udev renamed the netdev to according to new schema,
433e05b2d14SJiri Pirko        # check if the name matches the port_index.
434e05b2d14SJiri Pirko        nsimnamere = re.compile("eni\d+np(\d+)")
435e05b2d14SJiri Pirko        match = nsimnamere.match(ifname)
436e05b2d14SJiri Pirko        if match and int(match.groups()[0]) != port_index + 1:
437e05b2d14SJiri Pirko            raise Exception("netdevice name mismatches the expected one")
4387736b6edSJakub Kicinski
439e05b2d14SJiri Pirko        self.nsimdev = nsimdev
440e05b2d14SJiri Pirko        self.port_index = port_index
441752d7b45SJakub Kicinski        self.ns = ""
442e05b2d14SJiri Pirko        self.dfs_dir = "%s/ports/%u/" % (nsimdev.dfs_dir, port_index)
443417ec264SJakub Kicinski        self.dfs_refresh()
444e05b2d14SJiri Pirko        _, [self.dev] = ip("link show dev %s" % ifname)
445417ec264SJakub Kicinski
446417ec264SJakub Kicinski    def __getitem__(self, key):
447417ec264SJakub Kicinski        return self.dev[key]
448417ec264SJakub Kicinski
449417ec264SJakub Kicinski    def remove(self):
450e05b2d14SJiri Pirko        self.nsimdev.remove_nsim(self)
451417ec264SJakub Kicinski
452417ec264SJakub Kicinski    def dfs_refresh(self):
453417ec264SJakub Kicinski        self.dfs = DebugfsDir(self.dfs_dir)
454417ec264SJakub Kicinski        return self.dfs
455417ec264SJakub Kicinski
45699dadb6eSJakub Kicinski    def dfs_read(self, f):
45799dadb6eSJakub Kicinski        path = os.path.join(self.dfs_dir, f)
45899dadb6eSJakub Kicinski        _, data = cmd('cat %s' % (path))
45999dadb6eSJakub Kicinski        return data.strip()
46099dadb6eSJakub Kicinski
461417ec264SJakub Kicinski    def wait_for_flush(self, bound=0, total=0, n_retry=20):
462417ec264SJakub Kicinski        for i in range(n_retry):
463e05b2d14SJiri Pirko            nbound = self.nsimdev.dfs_num_bound_progs()
464417ec264SJakub Kicinski            nprogs = len(bpftool_prog_list())
465417ec264SJakub Kicinski            if nbound == bound and nprogs == total:
466417ec264SJakub Kicinski                return
467417ec264SJakub Kicinski            time.sleep(0.05)
468417ec264SJakub Kicinski        raise Exception("Time out waiting for program counts to stabilize want %d/%d, have %d bound, %d loaded" % (bound, total, nbound, nprogs))
469417ec264SJakub Kicinski
470752d7b45SJakub Kicinski    def set_ns(self, ns):
471752d7b45SJakub Kicinski        name = "1" if ns == "" else ns
472752d7b45SJakub Kicinski        ip("link set dev %s netns %s" % (self.dev["ifname"], name), ns=self.ns)
473752d7b45SJakub Kicinski        self.ns = ns
474752d7b45SJakub Kicinski
475417ec264SJakub Kicinski    def set_mtu(self, mtu, fail=True):
476417ec264SJakub Kicinski        return ip("link set dev %s mtu %d" % (self.dev["ifname"], mtu),
477417ec264SJakub Kicinski                  fail=fail)
478417ec264SJakub Kicinski
4799045bdc8SQuentin Monnet    def set_xdp(self, bpf, mode, force=False, JSON=True, verbose=False,
480caf95228SQuentin Monnet                fail=True, include_stderr=False):
4819045bdc8SQuentin Monnet        if verbose:
4829045bdc8SQuentin Monnet            bpf += " verbose"
483417ec264SJakub Kicinski        return ip("link set dev %s xdp%s %s" % (self.dev["ifname"], mode, bpf),
484caf95228SQuentin Monnet                  force=force, JSON=JSON,
485caf95228SQuentin Monnet                  fail=fail, include_stderr=include_stderr)
486417ec264SJakub Kicinski
487caf95228SQuentin Monnet    def unset_xdp(self, mode, force=False, JSON=True,
488caf95228SQuentin Monnet                  fail=True, include_stderr=False):
489417ec264SJakub Kicinski        return ip("link set dev %s xdp%s off" % (self.dev["ifname"], mode),
490caf95228SQuentin Monnet                  force=force, JSON=JSON,
491caf95228SQuentin Monnet                  fail=fail, include_stderr=include_stderr)
492417ec264SJakub Kicinski
493417ec264SJakub Kicinski    def ip_link_show(self, xdp):
494417ec264SJakub Kicinski        _, link = ip("link show dev %s" % (self['ifname']))
495417ec264SJakub Kicinski        if len(link) > 1:
496417ec264SJakub Kicinski            raise Exception("Multiple objects on ip link show")
497417ec264SJakub Kicinski        if len(link) < 1:
498417ec264SJakub Kicinski            return {}
499417ec264SJakub Kicinski        fail(xdp != "xdp" in link,
500417ec264SJakub Kicinski             "XDP program not reporting in iplink (reported %s, expected %s)" %
501417ec264SJakub Kicinski             ("xdp" in link, xdp))
502417ec264SJakub Kicinski        return link[0]
503417ec264SJakub Kicinski
504417ec264SJakub Kicinski    def tc_add_ingress(self):
505417ec264SJakub Kicinski        tc("qdisc add dev %s ingress" % (self['ifname']))
506417ec264SJakub Kicinski
507417ec264SJakub Kicinski    def tc_del_ingress(self):
508417ec264SJakub Kicinski        tc("qdisc del dev %s ingress" % (self['ifname']))
509417ec264SJakub Kicinski
510417ec264SJakub Kicinski    def tc_flush_filters(self, bound=0, total=0):
511417ec264SJakub Kicinski        self.tc_del_ingress()
512417ec264SJakub Kicinski        self.tc_add_ingress()
513417ec264SJakub Kicinski        self.wait_for_flush(bound=bound, total=total)
514417ec264SJakub Kicinski
515417ec264SJakub Kicinski    def tc_show_ingress(self, expected=None):
516417ec264SJakub Kicinski        # No JSON support, oh well...
517417ec264SJakub Kicinski        flags = ["skip_sw", "skip_hw", "in_hw"]
518417ec264SJakub Kicinski        named = ["protocol", "pref", "chain", "handle", "id", "tag"]
519417ec264SJakub Kicinski
520417ec264SJakub Kicinski        args = "-s filter show dev %s ingress" % (self['ifname'])
521417ec264SJakub Kicinski        _, out = tc(args, JSON=False)
522417ec264SJakub Kicinski
523417ec264SJakub Kicinski        filters = []
524417ec264SJakub Kicinski        lines = out.split('\n')
525417ec264SJakub Kicinski        for line in lines:
526417ec264SJakub Kicinski            words = line.split()
527417ec264SJakub Kicinski            if "handle" not in words:
528417ec264SJakub Kicinski                continue
529417ec264SJakub Kicinski            fltr = {}
530417ec264SJakub Kicinski            for flag in flags:
531417ec264SJakub Kicinski                fltr[flag] = flag in words
532417ec264SJakub Kicinski            for name in named:
533417ec264SJakub Kicinski                try:
534417ec264SJakub Kicinski                    idx = words.index(name)
535417ec264SJakub Kicinski                    fltr[name] = words[idx + 1]
536417ec264SJakub Kicinski                except ValueError:
537417ec264SJakub Kicinski                    pass
538417ec264SJakub Kicinski            filters.append(fltr)
539417ec264SJakub Kicinski
540417ec264SJakub Kicinski        if expected is not None:
541417ec264SJakub Kicinski            fail(len(filters) != expected,
542417ec264SJakub Kicinski                 "%d ingress filters loaded, expected %d" %
543417ec264SJakub Kicinski                 (len(filters), expected))
544417ec264SJakub Kicinski        return filters
545417ec264SJakub Kicinski
5466d2d58f1SJakub Kicinski    def cls_filter_op(self, op, qdisc="ingress", prio=None, handle=None,
547baf6a07eSJakub Kicinski                      chain=None, cls="", params="",
5486d2d58f1SJakub Kicinski                      fail=True, include_stderr=False):
5496d2d58f1SJakub Kicinski        spec = ""
5506d2d58f1SJakub Kicinski        if prio is not None:
5516d2d58f1SJakub Kicinski            spec += " prio %d" % (prio)
5526d2d58f1SJakub Kicinski        if handle:
5536d2d58f1SJakub Kicinski            spec += " handle %s" % (handle)
554baf6a07eSJakub Kicinski        if chain is not None:
555baf6a07eSJakub Kicinski            spec += " chain %d" % (chain)
5566d2d58f1SJakub Kicinski
5576d2d58f1SJakub Kicinski        return tc("filter {op} dev {dev} {qdisc} {spec} {cls} {params}"\
5586d2d58f1SJakub Kicinski                  .format(op=op, dev=self['ifname'], qdisc=qdisc, spec=spec,
5596d2d58f1SJakub Kicinski                          cls=cls, params=params),
5606d2d58f1SJakub Kicinski                  fail=fail, include_stderr=include_stderr)
5616d2d58f1SJakub Kicinski
5626d2d58f1SJakub Kicinski    def cls_bpf_add_filter(self, bpf, op="add", prio=None, handle=None,
563baf6a07eSJakub Kicinski                           chain=None, da=False, verbose=False,
5646d2d58f1SJakub Kicinski                           skip_sw=False, skip_hw=False,
5656d2d58f1SJakub Kicinski                           fail=True, include_stderr=False):
5666d2d58f1SJakub Kicinski        cls = "bpf " + bpf
5676d2d58f1SJakub Kicinski
568417ec264SJakub Kicinski        params = ""
569417ec264SJakub Kicinski        if da:
570417ec264SJakub Kicinski            params += " da"
5719045bdc8SQuentin Monnet        if verbose:
5729045bdc8SQuentin Monnet            params += " verbose"
573417ec264SJakub Kicinski        if skip_sw:
574417ec264SJakub Kicinski            params += " skip_sw"
575417ec264SJakub Kicinski        if skip_hw:
576417ec264SJakub Kicinski            params += " skip_hw"
5776d2d58f1SJakub Kicinski
5786d2d58f1SJakub Kicinski        return self.cls_filter_op(op=op, prio=prio, handle=handle, cls=cls,
579baf6a07eSJakub Kicinski                                  chain=chain, params=params,
580caf95228SQuentin Monnet                                  fail=fail, include_stderr=include_stderr)
581417ec264SJakub Kicinski
582417ec264SJakub Kicinski    def set_ethtool_tc_offloads(self, enable, fail=True):
583417ec264SJakub Kicinski        args = "hw-tc-offload %s" % ("on" if enable else "off")
584417ec264SJakub Kicinski        return ethtool(self, "-K", args, fail=fail)
585417ec264SJakub Kicinski
586417ec264SJakub Kicinski################################################################################
587417ec264SJakub Kicinskidef clean_up():
5887fedbb7cSJakub Kicinski    global files, netns, devs
5897fedbb7cSJakub Kicinski
590417ec264SJakub Kicinski    for dev in devs:
591417ec264SJakub Kicinski        dev.remove()
592417ec264SJakub Kicinski    for f in files:
593417ec264SJakub Kicinski        cmd("rm -f %s" % (f))
594752d7b45SJakub Kicinski    for ns in netns:
595752d7b45SJakub Kicinski        cmd("ip netns delete %s" % (ns))
5967fedbb7cSJakub Kicinski    files = []
5977fedbb7cSJakub Kicinski    netns = []
598417ec264SJakub Kicinski
599417ec264SJakub Kicinskidef pin_prog(file_name, idx=0):
600417ec264SJakub Kicinski    progs = bpftool_prog_list(expected=(idx + 1))
601417ec264SJakub Kicinski    prog = progs[idx]
602417ec264SJakub Kicinski    bpftool("prog pin id %d %s" % (prog["id"], file_name))
603417ec264SJakub Kicinski    files.append(file_name)
604417ec264SJakub Kicinski
605417ec264SJakub Kicinski    return file_name, bpf_pinned(file_name)
606417ec264SJakub Kicinski
6077fedbb7cSJakub Kicinskidef pin_map(file_name, idx=0, expected=1):
6087fedbb7cSJakub Kicinski    maps = bpftool_map_list(expected=expected)
6097fedbb7cSJakub Kicinski    m = maps[idx]
6107fedbb7cSJakub Kicinski    bpftool("map pin id %d %s" % (m["id"], file_name))
6117fedbb7cSJakub Kicinski    files.append(file_name)
6127fedbb7cSJakub Kicinski
6137fedbb7cSJakub Kicinski    return file_name, bpf_pinned(file_name)
6147fedbb7cSJakub Kicinski
6157fedbb7cSJakub Kicinskidef check_dev_info_removed(prog_file=None, map_file=None):
616752d7b45SJakub Kicinski    bpftool_prog_list(expected=0)
6177fedbb7cSJakub Kicinski    ret, err = bpftool("prog show pin %s" % (prog_file), fail=False)
618752d7b45SJakub Kicinski    fail(ret == 0, "Showing prog with removed device did not fail")
619752d7b45SJakub Kicinski    fail(err["error"].find("No such device") == -1,
620752d7b45SJakub Kicinski         "Showing prog with removed device expected ENODEV, error is %s" %
621752d7b45SJakub Kicinski         (err["error"]))
6227fedbb7cSJakub Kicinski
6237fedbb7cSJakub Kicinski    bpftool_map_list(expected=0)
6247fedbb7cSJakub Kicinski    ret, err = bpftool("map show pin %s" % (map_file), fail=False)
6257fedbb7cSJakub Kicinski    fail(ret == 0, "Showing map with removed device did not fail")
6267fedbb7cSJakub Kicinski    fail(err["error"].find("No such device") == -1,
6277fedbb7cSJakub Kicinski         "Showing map with removed device expected ENODEV, error is %s" %
6287fedbb7cSJakub Kicinski         (err["error"]))
6297fedbb7cSJakub Kicinski
6307fedbb7cSJakub Kicinskidef check_dev_info(other_ns, ns, prog_file=None, map_file=None, removed=False):
6317fedbb7cSJakub Kicinski    progs = bpftool_prog_list(expected=1, ns=ns)
632752d7b45SJakub Kicinski    prog = progs[0]
633752d7b45SJakub Kicinski
634752d7b45SJakub Kicinski    fail("dev" not in prog.keys(), "Device parameters not reported")
635752d7b45SJakub Kicinski    dev = prog["dev"]
636752d7b45SJakub Kicinski    fail("ifindex" not in dev.keys(), "Device parameters not reported")
637752d7b45SJakub Kicinski    fail("ns_dev" not in dev.keys(), "Device parameters not reported")
638752d7b45SJakub Kicinski    fail("ns_inode" not in dev.keys(), "Device parameters not reported")
639752d7b45SJakub Kicinski
6407fedbb7cSJakub Kicinski    if not other_ns:
641752d7b45SJakub Kicinski        fail("ifname" not in dev.keys(), "Ifname not reported")
642752d7b45SJakub Kicinski        fail(dev["ifname"] != sim["ifname"],
643752d7b45SJakub Kicinski             "Ifname incorrect %s vs %s" % (dev["ifname"], sim["ifname"]))
644752d7b45SJakub Kicinski    else:
645752d7b45SJakub Kicinski        fail("ifname" in dev.keys(), "Ifname is reported for other ns")
6467fedbb7cSJakub Kicinski
6477fedbb7cSJakub Kicinski    maps = bpftool_map_list(expected=2, ns=ns)
6487fedbb7cSJakub Kicinski    for m in maps:
6497fedbb7cSJakub Kicinski        fail("dev" not in m.keys(), "Device parameters not reported")
6507fedbb7cSJakub Kicinski        fail(dev != m["dev"], "Map's device different than program's")
651752d7b45SJakub Kicinski
652caf95228SQuentin Monnetdef check_extack(output, reference, args):
653caf95228SQuentin Monnet    if skip_extack:
654caf95228SQuentin Monnet        return
655caf95228SQuentin Monnet    lines = output.split("\n")
656219f860dSJakub Kicinski    comp = len(lines) >= 2 and lines[1] == 'Error: ' + reference
657caf95228SQuentin Monnet    fail(not comp, "Missing or incorrect netlink extack message")
658caf95228SQuentin Monnet
659caf95228SQuentin Monnetdef check_extack_nsim(output, reference, args):
660219f860dSJakub Kicinski    check_extack(output, "netdevsim: " + reference, args)
661caf95228SQuentin Monnet
6622fb89a38SJakub Kicinskidef check_no_extack(res, needle):
6632fb89a38SJakub Kicinski    fail((res[1] + res[2]).count(needle) or (res[1] + res[2]).count("Warning:"),
6642fb89a38SJakub Kicinski         "Found '%s' in command output, leaky extack?" % (needle))
6652fb89a38SJakub Kicinski
6669045bdc8SQuentin Monnetdef check_verifier_log(output, reference):
6679045bdc8SQuentin Monnet    lines = output.split("\n")
6689045bdc8SQuentin Monnet    for l in reversed(lines):
6699045bdc8SQuentin Monnet        if l == reference:
6709045bdc8SQuentin Monnet            return
6719045bdc8SQuentin Monnet    fail(True, "Missing or incorrect message from netdevsim in verifier log")
6729045bdc8SQuentin Monnet
67342a40e84SJakub Kicinskidef check_multi_basic(two_xdps):
67442a40e84SJakub Kicinski    fail(two_xdps["mode"] != 4, "Bad mode reported with multiple programs")
67542a40e84SJakub Kicinski    fail("prog" in two_xdps, "Base program reported in multi program mode")
67642a40e84SJakub Kicinski    fail(len(two_xdps["attached"]) != 2,
67742a40e84SJakub Kicinski         "Wrong attached program count with two programs")
67842a40e84SJakub Kicinski    fail(two_xdps["attached"][0]["prog"]["id"] ==
67942a40e84SJakub Kicinski         two_xdps["attached"][1]["prog"]["id"],
68042a40e84SJakub Kicinski         "Offloaded and other programs have the same id")
68142a40e84SJakub Kicinski
6822fb89a38SJakub Kicinskidef test_spurios_extack(sim, obj, skip_hw, needle):
6832fb89a38SJakub Kicinski    res = sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=skip_hw,
6842fb89a38SJakub Kicinski                                 include_stderr=True)
6852fb89a38SJakub Kicinski    check_no_extack(res, needle)
6862fb89a38SJakub Kicinski    res = sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1,
6872fb89a38SJakub Kicinski                                 skip_hw=skip_hw, include_stderr=True)
6882fb89a38SJakub Kicinski    check_no_extack(res, needle)
6892fb89a38SJakub Kicinski    res = sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf",
6902fb89a38SJakub Kicinski                            include_stderr=True)
6912fb89a38SJakub Kicinski    check_no_extack(res, needle)
6922fb89a38SJakub Kicinski
693e05b2d14SJiri Pirkodef test_multi_prog(simdev, sim, obj, modename, modeid):
69406ea9e63SJakub Kicinski    start_test("Test multi-attachment XDP - %s + offload..." %
69506ea9e63SJakub Kicinski               (modename or "default", ))
69606ea9e63SJakub Kicinski    sim.set_xdp(obj, "offload")
69706ea9e63SJakub Kicinski    xdp = sim.ip_link_show(xdp=True)["xdp"]
69806ea9e63SJakub Kicinski    offloaded = sim.dfs_read("bpf_offloaded_id")
69906ea9e63SJakub Kicinski    fail("prog" not in xdp, "Base program not reported in single program mode")
70006ea9e63SJakub Kicinski    fail(len(xdp["attached"]) != 1,
70106ea9e63SJakub Kicinski         "Wrong attached program count with one program")
70206ea9e63SJakub Kicinski
70306ea9e63SJakub Kicinski    sim.set_xdp(obj, modename)
70406ea9e63SJakub Kicinski    two_xdps = sim.ip_link_show(xdp=True)["xdp"]
70506ea9e63SJakub Kicinski
70606ea9e63SJakub Kicinski    fail(xdp["attached"][0] not in two_xdps["attached"],
70706ea9e63SJakub Kicinski         "Offload program not reported after other activated")
70842a40e84SJakub Kicinski    check_multi_basic(two_xdps)
70942a40e84SJakub Kicinski
71042a40e84SJakub Kicinski    offloaded2 = sim.dfs_read("bpf_offloaded_id")
71106ea9e63SJakub Kicinski    fail(offloaded != offloaded2,
71206ea9e63SJakub Kicinski         "Offload ID changed after loading other program")
71306ea9e63SJakub Kicinski
71406ea9e63SJakub Kicinski    start_test("Test multi-attachment XDP - replace...")
71506ea9e63SJakub Kicinski    ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True)
71606ea9e63SJakub Kicinski    fail(ret == 0, "Replaced one of programs without -force")
71706ea9e63SJakub Kicinski    check_extack(err, "XDP program already attached.", args)
71806ea9e63SJakub Kicinski
71906ea9e63SJakub Kicinski    if modename == "" or modename == "drv":
72006ea9e63SJakub Kicinski        othermode = "" if modename == "drv" else "drv"
72106ea9e63SJakub Kicinski        start_test("Test multi-attachment XDP - detach...")
72206ea9e63SJakub Kicinski        ret, _, err = sim.unset_xdp(othermode, force=True,
72306ea9e63SJakub Kicinski                                    fail=False, include_stderr=True)
72406ea9e63SJakub Kicinski        fail(ret == 0, "Removed program with a bad mode")
72506ea9e63SJakub Kicinski        check_extack(err, "program loaded with different flags.", args)
72606ea9e63SJakub Kicinski
72706ea9e63SJakub Kicinski    sim.unset_xdp("offload")
72806ea9e63SJakub Kicinski    xdp = sim.ip_link_show(xdp=True)["xdp"]
72906ea9e63SJakub Kicinski    offloaded = sim.dfs_read("bpf_offloaded_id")
73006ea9e63SJakub Kicinski
73106ea9e63SJakub Kicinski    fail(xdp["mode"] != modeid, "Bad mode reported after multiple programs")
73206ea9e63SJakub Kicinski    fail("prog" not in xdp,
73306ea9e63SJakub Kicinski         "Base program not reported after multi program mode")
73406ea9e63SJakub Kicinski    fail(xdp["attached"][0] not in two_xdps["attached"],
73506ea9e63SJakub Kicinski         "Offload program not reported after other activated")
73606ea9e63SJakub Kicinski    fail(len(xdp["attached"]) != 1,
73706ea9e63SJakub Kicinski         "Wrong attached program count with remaining programs")
73806ea9e63SJakub Kicinski    fail(offloaded != "0", "Offload ID reported with only other program left")
73906ea9e63SJakub Kicinski
74042a40e84SJakub Kicinski    start_test("Test multi-attachment XDP - reattach...")
74106ea9e63SJakub Kicinski    sim.set_xdp(obj, "offload")
74242a40e84SJakub Kicinski    two_xdps = sim.ip_link_show(xdp=True)["xdp"]
74342a40e84SJakub Kicinski
74442a40e84SJakub Kicinski    fail(xdp["attached"][0] not in two_xdps["attached"],
74542a40e84SJakub Kicinski         "Other program not reported after offload activated")
74642a40e84SJakub Kicinski    check_multi_basic(two_xdps)
74742a40e84SJakub Kicinski
74842a40e84SJakub Kicinski    start_test("Test multi-attachment XDP - device remove...")
749e05b2d14SJiri Pirko    simdev.remove()
75006ea9e63SJakub Kicinski
751e05b2d14SJiri Pirko    simdev = NetdevSimDev()
752e05b2d14SJiri Pirko    sim, = simdev.nsims
75306ea9e63SJakub Kicinski    sim.set_ethtool_tc_offloads(True)
754e05b2d14SJiri Pirko    return [simdev, sim]
7552fb89a38SJakub Kicinski
756417ec264SJakub Kicinski# Parse command line
757417ec264SJakub Kicinskiparser = argparse.ArgumentParser()
758417ec264SJakub Kicinskiparser.add_argument("--log", help="output verbose log to given file")
759417ec264SJakub Kicinskiargs = parser.parse_args()
760417ec264SJakub Kicinskiif args.log:
761417ec264SJakub Kicinski    logfile = open(args.log, 'w+')
762417ec264SJakub Kicinski    logfile.write("# -*-Org-*-")
763417ec264SJakub Kicinski
764417ec264SJakub Kicinskilog("Prepare...", "", level=1)
765417ec264SJakub Kicinskilog_level_inc()
766417ec264SJakub Kicinski
767417ec264SJakub Kicinski# Check permissions
768417ec264SJakub Kicinskiskip(os.getuid() != 0, "test must be run as root")
769417ec264SJakub Kicinski
770417ec264SJakub Kicinski# Check tools
771417ec264SJakub Kicinskiret, progs = bpftool("prog", fail=False)
772417ec264SJakub Kicinskiskip(ret != 0, "bpftool not installed")
77347cf52a2SJakub Kicinskibase_progs = progs
77447cf52a2SJakub Kicinski_, base_maps = bpftool("map")
775417ec264SJakub Kicinski
776417ec264SJakub Kicinski# Check netdevsim
777417ec264SJakub Kicinskiret, out = cmd("modprobe netdevsim", fail=False)
778417ec264SJakub Kicinskiskip(ret != 0, "netdevsim module could not be loaded")
779417ec264SJakub Kicinski
780417ec264SJakub Kicinski# Check debugfs
781417ec264SJakub Kicinski_, out = cmd("mount")
782417ec264SJakub Kicinskiif out.find("/sys/kernel/debug type debugfs") == -1:
783417ec264SJakub Kicinski    cmd("mount -t debugfs none /sys/kernel/debug")
784417ec264SJakub Kicinski
785417ec264SJakub Kicinski# Check samples are compiled
7867fedbb7cSJakub Kicinskisamples = ["sample_ret0.o", "sample_map_ret0.o"]
787417ec264SJakub Kicinskifor s in samples:
788417ec264SJakub Kicinski    ret, out = cmd("ls %s/%s" % (bpf_test_dir, s), fail=False)
789417ec264SJakub Kicinski    skip(ret != 0, "sample %s/%s not found, please compile it" %
790417ec264SJakub Kicinski         (bpf_test_dir, s))
791417ec264SJakub Kicinski
792caf95228SQuentin Monnet# Check if iproute2 is built with libmnl (needed by extack support)
793caf95228SQuentin Monnet_, _, err = cmd("tc qdisc delete dev lo handle 0",
794caf95228SQuentin Monnet                fail=False, include_stderr=True)
795caf95228SQuentin Monnetif err.find("Error: Failed to find qdisc with specified handle.") == -1:
796caf95228SQuentin Monnet    print("Warning: no extack message in iproute2 output, libmnl missing?")
797caf95228SQuentin Monnet    log("Warning: no extack message in iproute2 output, libmnl missing?", "")
798caf95228SQuentin Monnet    skip_extack = True
799caf95228SQuentin Monnet
800752d7b45SJakub Kicinski# Check if net namespaces seem to work
801752d7b45SJakub Kicinskins = mknetns()
802752d7b45SJakub Kicinskiskip(ns is None, "Could not create a net namespace")
803752d7b45SJakub Kicinskicmd("ip netns delete %s" % (ns))
804752d7b45SJakub Kicinskinetns = []
805752d7b45SJakub Kicinski
806417ec264SJakub Kicinskitry:
807417ec264SJakub Kicinski    obj = bpf_obj("sample_ret0.o")
808417ec264SJakub Kicinski    bytecode = bpf_bytecode("1,6 0 0 4294967295,")
809417ec264SJakub Kicinski
810417ec264SJakub Kicinski    start_test("Test destruction of generic XDP...")
811e05b2d14SJiri Pirko    simdev = NetdevSimDev()
812e05b2d14SJiri Pirko    sim, = simdev.nsims
813417ec264SJakub Kicinski    sim.set_xdp(obj, "generic")
814e05b2d14SJiri Pirko    simdev.remove()
815417ec264SJakub Kicinski    bpftool_prog_list_wait(expected=0)
816417ec264SJakub Kicinski
817e05b2d14SJiri Pirko    simdev = NetdevSimDev()
818e05b2d14SJiri Pirko    sim, = simdev.nsims
819417ec264SJakub Kicinski    sim.tc_add_ingress()
820417ec264SJakub Kicinski
821417ec264SJakub Kicinski    start_test("Test TC non-offloaded...")
822417ec264SJakub Kicinski    ret, _ = sim.cls_bpf_add_filter(obj, skip_hw=True, fail=False)
823417ec264SJakub Kicinski    fail(ret != 0, "Software TC filter did not load")
824417ec264SJakub Kicinski
825417ec264SJakub Kicinski    start_test("Test TC non-offloaded isn't getting bound...")
826417ec264SJakub Kicinski    ret, _ = sim.cls_bpf_add_filter(obj, fail=False)
827417ec264SJakub Kicinski    fail(ret != 0, "Software TC filter did not load")
828e05b2d14SJiri Pirko    simdev.dfs_get_bound_progs(expected=0)
829417ec264SJakub Kicinski
830417ec264SJakub Kicinski    sim.tc_flush_filters()
831417ec264SJakub Kicinski
832417ec264SJakub Kicinski    start_test("Test TC offloads are off by default...")
833caf95228SQuentin Monnet    ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True,
834caf95228SQuentin Monnet                                         fail=False, include_stderr=True)
835417ec264SJakub Kicinski    fail(ret == 0, "TC filter loaded without enabling TC offloads")
836219f860dSJakub Kicinski    check_extack(err, "TC offload is disabled on net device.", args)
837417ec264SJakub Kicinski    sim.wait_for_flush()
838417ec264SJakub Kicinski
839417ec264SJakub Kicinski    sim.set_ethtool_tc_offloads(True)
840417ec264SJakub Kicinski    sim.dfs["bpf_tc_non_bound_accept"] = "Y"
841417ec264SJakub Kicinski
842417ec264SJakub Kicinski    start_test("Test TC offload by default...")
843417ec264SJakub Kicinski    ret, _ = sim.cls_bpf_add_filter(obj, fail=False)
844417ec264SJakub Kicinski    fail(ret != 0, "Software TC filter did not load")
845e05b2d14SJiri Pirko    simdev.dfs_get_bound_progs(expected=0)
846417ec264SJakub Kicinski    ingress = sim.tc_show_ingress(expected=1)
847417ec264SJakub Kicinski    fltr = ingress[0]
848417ec264SJakub Kicinski    fail(not fltr["in_hw"], "Filter not offloaded by default")
849417ec264SJakub Kicinski
850417ec264SJakub Kicinski    sim.tc_flush_filters()
851417ec264SJakub Kicinski
852417ec264SJakub Kicinski    start_test("Test TC cBPF bytcode tries offload by default...")
853417ec264SJakub Kicinski    ret, _ = sim.cls_bpf_add_filter(bytecode, fail=False)
854417ec264SJakub Kicinski    fail(ret != 0, "Software TC filter did not load")
855e05b2d14SJiri Pirko    simdev.dfs_get_bound_progs(expected=0)
856417ec264SJakub Kicinski    ingress = sim.tc_show_ingress(expected=1)
857417ec264SJakub Kicinski    fltr = ingress[0]
858417ec264SJakub Kicinski    fail(not fltr["in_hw"], "Bytecode not offloaded by default")
859417ec264SJakub Kicinski
860417ec264SJakub Kicinski    sim.tc_flush_filters()
861417ec264SJakub Kicinski    sim.dfs["bpf_tc_non_bound_accept"] = "N"
862417ec264SJakub Kicinski
863417ec264SJakub Kicinski    start_test("Test TC cBPF unbound bytecode doesn't offload...")
864caf95228SQuentin Monnet    ret, _, err = sim.cls_bpf_add_filter(bytecode, skip_sw=True,
865caf95228SQuentin Monnet                                         fail=False, include_stderr=True)
866417ec264SJakub Kicinski    fail(ret == 0, "TC bytecode loaded for offload")
867caf95228SQuentin Monnet    check_extack_nsim(err, "netdevsim configured to reject unbound programs.",
868caf95228SQuentin Monnet                      args)
869417ec264SJakub Kicinski    sim.wait_for_flush()
870417ec264SJakub Kicinski
871baf6a07eSJakub Kicinski    start_test("Test non-0 chain offload...")
872baf6a07eSJakub Kicinski    ret, _, err = sim.cls_bpf_add_filter(obj, chain=1, prio=1, handle=1,
873baf6a07eSJakub Kicinski                                         skip_sw=True,
874baf6a07eSJakub Kicinski                                         fail=False, include_stderr=True)
875baf6a07eSJakub Kicinski    fail(ret == 0, "Offloaded a filter to chain other than 0")
876219f860dSJakub Kicinski    check_extack(err, "Driver supports only offload of chain 0.", args)
877baf6a07eSJakub Kicinski    sim.tc_flush_filters()
878baf6a07eSJakub Kicinski
8796d2d58f1SJakub Kicinski    start_test("Test TC replace...")
8806d2d58f1SJakub Kicinski    sim.cls_bpf_add_filter(obj, prio=1, handle=1)
8816d2d58f1SJakub Kicinski    sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1)
8826d2d58f1SJakub Kicinski    sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
8836d2d58f1SJakub Kicinski
8846d2d58f1SJakub Kicinski    sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_sw=True)
8856d2d58f1SJakub Kicinski    sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, skip_sw=True)
8866d2d58f1SJakub Kicinski    sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
8876d2d58f1SJakub Kicinski
8886d2d58f1SJakub Kicinski    sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=True)
8896d2d58f1SJakub Kicinski    sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1, skip_hw=True)
8906d2d58f1SJakub Kicinski    sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
8916d2d58f1SJakub Kicinski
8926d2d58f1SJakub Kicinski    start_test("Test TC replace bad flags...")
8936d2d58f1SJakub Kicinski    for i in range(3):
8946d2d58f1SJakub Kicinski        for j in range(3):
8956d2d58f1SJakub Kicinski            ret, _ = sim.cls_bpf_add_filter(obj, op="replace", prio=1, handle=1,
8966d2d58f1SJakub Kicinski                                            skip_sw=(j == 1), skip_hw=(j == 2),
8976d2d58f1SJakub Kicinski                                            fail=False)
8986d2d58f1SJakub Kicinski            fail(bool(ret) != bool(j),
8996d2d58f1SJakub Kicinski                 "Software TC incorrect load in replace test, iteration %d" %
9006d2d58f1SJakub Kicinski                 (j))
9016d2d58f1SJakub Kicinski        sim.cls_filter_op(op="delete", prio=1, handle=1, cls="bpf")
9026d2d58f1SJakub Kicinski
9032fb89a38SJakub Kicinski    start_test("Test spurious extack from the driver...")
9042fb89a38SJakub Kicinski    test_spurios_extack(sim, obj, False, "netdevsim")
9052fb89a38SJakub Kicinski    test_spurios_extack(sim, obj, True, "netdevsim")
9062fb89a38SJakub Kicinski
9072fb89a38SJakub Kicinski    sim.set_ethtool_tc_offloads(False)
9082fb89a38SJakub Kicinski
9092fb89a38SJakub Kicinski    test_spurios_extack(sim, obj, False, "TC offload is disabled")
9102fb89a38SJakub Kicinski    test_spurios_extack(sim, obj, True, "TC offload is disabled")
9112fb89a38SJakub Kicinski
9122fb89a38SJakub Kicinski    sim.set_ethtool_tc_offloads(True)
9132fb89a38SJakub Kicinski
9146d2d58f1SJakub Kicinski    sim.tc_flush_filters()
9156d2d58f1SJakub Kicinski
916417ec264SJakub Kicinski    start_test("Test TC offloads work...")
9179045bdc8SQuentin Monnet    ret, _, err = sim.cls_bpf_add_filter(obj, verbose=True, skip_sw=True,
9189045bdc8SQuentin Monnet                                         fail=False, include_stderr=True)
919417ec264SJakub Kicinski    fail(ret != 0, "TC filter did not load with TC offloads enabled")
9209045bdc8SQuentin Monnet    check_verifier_log(err, "[netdevsim] Hello from netdevsim!")
921417ec264SJakub Kicinski
922417ec264SJakub Kicinski    start_test("Test TC offload basics...")
923e05b2d14SJiri Pirko    dfs = simdev.dfs_get_bound_progs(expected=1)
924417ec264SJakub Kicinski    progs = bpftool_prog_list(expected=1)
925417ec264SJakub Kicinski    ingress = sim.tc_show_ingress(expected=1)
926417ec264SJakub Kicinski
927417ec264SJakub Kicinski    dprog = dfs[0]
928417ec264SJakub Kicinski    prog = progs[0]
929417ec264SJakub Kicinski    fltr = ingress[0]
930417ec264SJakub Kicinski    fail(fltr["skip_hw"], "TC does reports 'skip_hw' on offloaded filter")
931417ec264SJakub Kicinski    fail(not fltr["in_hw"], "TC does not report 'in_hw' for offloaded filter")
932417ec264SJakub Kicinski    fail(not fltr["skip_sw"], "TC does not report 'skip_sw' back")
933417ec264SJakub Kicinski
934417ec264SJakub Kicinski    start_test("Test TC offload is device-bound...")
935417ec264SJakub Kicinski    fail(str(prog["id"]) != fltr["id"], "Program IDs don't match")
936417ec264SJakub Kicinski    fail(prog["tag"] != fltr["tag"], "Program tags don't match")
937417ec264SJakub Kicinski    fail(fltr["id"] != dprog["id"], "Program IDs don't match")
938417ec264SJakub Kicinski    fail(dprog["state"] != "xlated", "Offloaded program state not translated")
939417ec264SJakub Kicinski    fail(dprog["loaded"] != "Y", "Offloaded program is not loaded")
940417ec264SJakub Kicinski
941417ec264SJakub Kicinski    start_test("Test disabling TC offloads is rejected while filters installed...")
942417ec264SJakub Kicinski    ret, _ = sim.set_ethtool_tc_offloads(False, fail=False)
943417ec264SJakub Kicinski    fail(ret == 0, "Driver should refuse to disable TC offloads with filters installed...")
944417ec264SJakub Kicinski
945417ec264SJakub Kicinski    start_test("Test qdisc removal frees things...")
946417ec264SJakub Kicinski    sim.tc_flush_filters()
947417ec264SJakub Kicinski    sim.tc_show_ingress(expected=0)
948417ec264SJakub Kicinski
949417ec264SJakub Kicinski    start_test("Test disabling TC offloads is OK without filters...")
950417ec264SJakub Kicinski    ret, _ = sim.set_ethtool_tc_offloads(False, fail=False)
951417ec264SJakub Kicinski    fail(ret != 0,
952417ec264SJakub Kicinski         "Driver refused to disable TC offloads without filters installed...")
953417ec264SJakub Kicinski
954417ec264SJakub Kicinski    sim.set_ethtool_tc_offloads(True)
955417ec264SJakub Kicinski
956417ec264SJakub Kicinski    start_test("Test destroying device gets rid of TC filters...")
957417ec264SJakub Kicinski    sim.cls_bpf_add_filter(obj, skip_sw=True)
958e05b2d14SJiri Pirko    simdev.remove()
959417ec264SJakub Kicinski    bpftool_prog_list_wait(expected=0)
960417ec264SJakub Kicinski
961e05b2d14SJiri Pirko    simdev = NetdevSimDev()
962e05b2d14SJiri Pirko    sim, = simdev.nsims
963417ec264SJakub Kicinski    sim.set_ethtool_tc_offloads(True)
964417ec264SJakub Kicinski
965417ec264SJakub Kicinski    start_test("Test destroying device gets rid of XDP...")
966417ec264SJakub Kicinski    sim.set_xdp(obj, "offload")
967e05b2d14SJiri Pirko    simdev.remove()
968417ec264SJakub Kicinski    bpftool_prog_list_wait(expected=0)
969417ec264SJakub Kicinski
970e05b2d14SJiri Pirko    simdev = NetdevSimDev()
971e05b2d14SJiri Pirko    sim, = simdev.nsims
972417ec264SJakub Kicinski    sim.set_ethtool_tc_offloads(True)
973417ec264SJakub Kicinski
974417ec264SJakub Kicinski    start_test("Test XDP prog reporting...")
975417ec264SJakub Kicinski    sim.set_xdp(obj, "drv")
976417ec264SJakub Kicinski    ipl = sim.ip_link_show(xdp=True)
977417ec264SJakub Kicinski    progs = bpftool_prog_list(expected=1)
978417ec264SJakub Kicinski    fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"],
979417ec264SJakub Kicinski         "Loaded program has wrong ID")
980417ec264SJakub Kicinski
981417ec264SJakub Kicinski    start_test("Test XDP prog replace without force...")
982417ec264SJakub Kicinski    ret, _ = sim.set_xdp(obj, "drv", fail=False)
983417ec264SJakub Kicinski    fail(ret == 0, "Replaced XDP program without -force")
984417ec264SJakub Kicinski    sim.wait_for_flush(total=1)
985417ec264SJakub Kicinski
986417ec264SJakub Kicinski    start_test("Test XDP prog replace with force...")
987417ec264SJakub Kicinski    ret, _ = sim.set_xdp(obj, "drv", force=True, fail=False)
988417ec264SJakub Kicinski    fail(ret != 0, "Could not replace XDP program with -force")
989417ec264SJakub Kicinski    bpftool_prog_list_wait(expected=1)
990417ec264SJakub Kicinski    ipl = sim.ip_link_show(xdp=True)
991417ec264SJakub Kicinski    progs = bpftool_prog_list(expected=1)
992417ec264SJakub Kicinski    fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"],
993417ec264SJakub Kicinski         "Loaded program has wrong ID")
994752d7b45SJakub Kicinski    fail("dev" in progs[0].keys(),
995752d7b45SJakub Kicinski         "Device parameters reported for non-offloaded program")
996417ec264SJakub Kicinski
997417ec264SJakub Kicinski    start_test("Test XDP prog replace with bad flags...")
99899dadb6eSJakub Kicinski    ret, _, err = sim.set_xdp(obj, "generic", force=True,
99999dadb6eSJakub Kicinski                              fail=False, include_stderr=True)
100099dadb6eSJakub Kicinski    fail(ret == 0, "Replaced XDP program with a program in different mode")
100152158f00SJakub Kicinski    check_extack(err,
100252158f00SJakub Kicinski                 "native and generic XDP can't be active at the same time.",
100352158f00SJakub Kicinski                 args)
1004caf95228SQuentin Monnet    ret, _, err = sim.set_xdp(obj, "", force=True,
1005caf95228SQuentin Monnet                              fail=False, include_stderr=True)
1006417ec264SJakub Kicinski    fail(ret == 0, "Replaced XDP program with a program in different mode")
100705296620SJakub Kicinski    check_extack(err, "program loaded with different flags.", args)
1008417ec264SJakub Kicinski
1009417ec264SJakub Kicinski    start_test("Test XDP prog remove with bad flags...")
1010caf95228SQuentin Monnet    ret, _, err = sim.unset_xdp("", force=True,
1011caf95228SQuentin Monnet                                fail=False, include_stderr=True)
10127479efc7SJakub Kicinski    fail(ret == 0, "Removed program with a bad mode")
101305296620SJakub Kicinski    check_extack(err, "program loaded with different flags.", args)
1014417ec264SJakub Kicinski
1015417ec264SJakub Kicinski    start_test("Test MTU restrictions...")
1016417ec264SJakub Kicinski    ret, _ = sim.set_mtu(9000, fail=False)
1017417ec264SJakub Kicinski    fail(ret == 0,
1018417ec264SJakub Kicinski         "Driver should refuse to increase MTU to 9000 with XDP loaded...")
1019417ec264SJakub Kicinski    sim.unset_xdp("drv")
1020417ec264SJakub Kicinski    bpftool_prog_list_wait(expected=0)
1021417ec264SJakub Kicinski    sim.set_mtu(9000)
1022caf95228SQuentin Monnet    ret, _, err = sim.set_xdp(obj, "drv", fail=False, include_stderr=True)
1023417ec264SJakub Kicinski    fail(ret == 0, "Driver should refuse to load program with MTU of 9000...")
1024caf95228SQuentin Monnet    check_extack_nsim(err, "MTU too large w/ XDP enabled.", args)
1025417ec264SJakub Kicinski    sim.set_mtu(1500)
1026417ec264SJakub Kicinski
1027417ec264SJakub Kicinski    sim.wait_for_flush()
10287736b6edSJakub Kicinski    start_test("Test non-offload XDP attaching to HW...")
10297736b6edSJakub Kicinski    bpftool_prog_load("sample_ret0.o", "/sys/fs/bpf/nooffload")
10307736b6edSJakub Kicinski    nooffload = bpf_pinned("/sys/fs/bpf/nooffload")
10317736b6edSJakub Kicinski    ret, _, err = sim.set_xdp(nooffload, "offload",
10327736b6edSJakub Kicinski                              fail=False, include_stderr=True)
10337736b6edSJakub Kicinski    fail(ret == 0, "attached non-offloaded XDP program to HW")
10347736b6edSJakub Kicinski    check_extack_nsim(err, "xdpoffload of non-bound program.", args)
10357736b6edSJakub Kicinski    rm("/sys/fs/bpf/nooffload")
10367736b6edSJakub Kicinski
10377736b6edSJakub Kicinski    start_test("Test offload XDP attaching to drv...")
10387736b6edSJakub Kicinski    bpftool_prog_load("sample_ret0.o", "/sys/fs/bpf/offload",
10397736b6edSJakub Kicinski                      dev=sim['ifname'])
10407736b6edSJakub Kicinski    offload = bpf_pinned("/sys/fs/bpf/offload")
10417736b6edSJakub Kicinski    ret, _, err = sim.set_xdp(offload, "drv", fail=False, include_stderr=True)
10427736b6edSJakub Kicinski    fail(ret == 0, "attached offloaded XDP program to drv")
10437736b6edSJakub Kicinski    check_extack(err, "using device-bound program without HW_MODE flag is not supported.", args)
10447736b6edSJakub Kicinski    rm("/sys/fs/bpf/offload")
10457736b6edSJakub Kicinski    sim.wait_for_flush()
10467736b6edSJakub Kicinski
1047417ec264SJakub Kicinski    start_test("Test XDP offload...")
10489045bdc8SQuentin Monnet    _, _, err = sim.set_xdp(obj, "offload", verbose=True, include_stderr=True)
1049417ec264SJakub Kicinski    ipl = sim.ip_link_show(xdp=True)
1050417ec264SJakub Kicinski    link_xdp = ipl["xdp"]["prog"]
1051417ec264SJakub Kicinski    progs = bpftool_prog_list(expected=1)
1052417ec264SJakub Kicinski    prog = progs[0]
1053417ec264SJakub Kicinski    fail(link_xdp["id"] != prog["id"], "Loaded program has wrong ID")
10549045bdc8SQuentin Monnet    check_verifier_log(err, "[netdevsim] Hello from netdevsim!")
1055417ec264SJakub Kicinski
1056417ec264SJakub Kicinski    start_test("Test XDP offload is device bound...")
1057e05b2d14SJiri Pirko    dfs = simdev.dfs_get_bound_progs(expected=1)
1058417ec264SJakub Kicinski    dprog = dfs[0]
1059417ec264SJakub Kicinski
1060417ec264SJakub Kicinski    fail(prog["id"] != link_xdp["id"], "Program IDs don't match")
1061417ec264SJakub Kicinski    fail(prog["tag"] != link_xdp["tag"], "Program tags don't match")
1062417ec264SJakub Kicinski    fail(str(link_xdp["id"]) != dprog["id"], "Program IDs don't match")
1063417ec264SJakub Kicinski    fail(dprog["state"] != "xlated", "Offloaded program state not translated")
1064417ec264SJakub Kicinski    fail(dprog["loaded"] != "Y", "Offloaded program is not loaded")
1065417ec264SJakub Kicinski
1066417ec264SJakub Kicinski    start_test("Test removing XDP program many times...")
1067417ec264SJakub Kicinski    sim.unset_xdp("offload")
1068417ec264SJakub Kicinski    sim.unset_xdp("offload")
1069417ec264SJakub Kicinski    sim.unset_xdp("drv")
1070417ec264SJakub Kicinski    sim.unset_xdp("drv")
1071417ec264SJakub Kicinski    sim.unset_xdp("")
1072417ec264SJakub Kicinski    sim.unset_xdp("")
1073417ec264SJakub Kicinski    bpftool_prog_list_wait(expected=0)
1074417ec264SJakub Kicinski
1075417ec264SJakub Kicinski    start_test("Test attempt to use a program for a wrong device...")
1076e05b2d14SJiri Pirko    simdev2 = NetdevSimDev()
1077e05b2d14SJiri Pirko    sim2, = simdev2.nsims
1078417ec264SJakub Kicinski    sim2.set_xdp(obj, "offload")
1079417ec264SJakub Kicinski    pin_file, pinned = pin_prog("/sys/fs/bpf/tmp")
1080417ec264SJakub Kicinski
1081caf95228SQuentin Monnet    ret, _, err = sim.set_xdp(pinned, "offload",
1082caf95228SQuentin Monnet                              fail=False, include_stderr=True)
1083417ec264SJakub Kicinski    fail(ret == 0, "Pinned program loaded for a different device accepted")
1084caf95228SQuentin Monnet    check_extack_nsim(err, "program bound to different dev.", args)
1085e05b2d14SJiri Pirko    simdev2.remove()
1086caf95228SQuentin Monnet    ret, _, err = sim.set_xdp(pinned, "offload",
1087caf95228SQuentin Monnet                              fail=False, include_stderr=True)
1088417ec264SJakub Kicinski    fail(ret == 0, "Pinned program loaded for a removed device accepted")
1089caf95228SQuentin Monnet    check_extack_nsim(err, "xdpoffload of non-bound program.", args)
1090417ec264SJakub Kicinski    rm(pin_file)
1091417ec264SJakub Kicinski    bpftool_prog_list_wait(expected=0)
1092417ec264SJakub Kicinski
1093e05b2d14SJiri Pirko    simdev, sim = test_multi_prog(simdev, sim, obj, "", 1)
1094e05b2d14SJiri Pirko    simdev, sim = test_multi_prog(simdev, sim, obj, "drv", 1)
1095e05b2d14SJiri Pirko    simdev, sim = test_multi_prog(simdev, sim, obj, "generic", 2)
109699dadb6eSJakub Kicinski
1097417ec264SJakub Kicinski    start_test("Test mixing of TC and XDP...")
1098417ec264SJakub Kicinski    sim.tc_add_ingress()
1099417ec264SJakub Kicinski    sim.set_xdp(obj, "offload")
1100caf95228SQuentin Monnet    ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True,
1101caf95228SQuentin Monnet                                         fail=False, include_stderr=True)
1102417ec264SJakub Kicinski    fail(ret == 0, "Loading TC when XDP active should fail")
1103caf95228SQuentin Monnet    check_extack_nsim(err, "driver and netdev offload states mismatch.", args)
1104417ec264SJakub Kicinski    sim.unset_xdp("offload")
1105417ec264SJakub Kicinski    sim.wait_for_flush()
1106417ec264SJakub Kicinski
1107417ec264SJakub Kicinski    sim.cls_bpf_add_filter(obj, skip_sw=True)
1108caf95228SQuentin Monnet    ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True)
1109417ec264SJakub Kicinski    fail(ret == 0, "Loading XDP when TC active should fail")
1110caf95228SQuentin Monnet    check_extack_nsim(err, "TC program is already loaded.", args)
1111417ec264SJakub Kicinski
1112417ec264SJakub Kicinski    start_test("Test binding TC from pinned...")
1113417ec264SJakub Kicinski    pin_file, pinned = pin_prog("/sys/fs/bpf/tmp")
1114417ec264SJakub Kicinski    sim.tc_flush_filters(bound=1, total=1)
1115417ec264SJakub Kicinski    sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True)
1116417ec264SJakub Kicinski    sim.tc_flush_filters(bound=1, total=1)
1117417ec264SJakub Kicinski
1118417ec264SJakub Kicinski    start_test("Test binding XDP from pinned...")
1119417ec264SJakub Kicinski    sim.set_xdp(obj, "offload")
1120417ec264SJakub Kicinski    pin_file, pinned = pin_prog("/sys/fs/bpf/tmp2", idx=1)
1121417ec264SJakub Kicinski
1122417ec264SJakub Kicinski    sim.set_xdp(pinned, "offload", force=True)
1123417ec264SJakub Kicinski    sim.unset_xdp("offload")
1124417ec264SJakub Kicinski    sim.set_xdp(pinned, "offload", force=True)
1125417ec264SJakub Kicinski    sim.unset_xdp("offload")
1126417ec264SJakub Kicinski
1127417ec264SJakub Kicinski    start_test("Test offload of wrong type fails...")
1128417ec264SJakub Kicinski    ret, _ = sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True, fail=False)
1129417ec264SJakub Kicinski    fail(ret == 0, "Managed to attach XDP program to TC")
1130417ec264SJakub Kicinski
1131417ec264SJakub Kicinski    start_test("Test asking for TC offload of two filters...")
1132417ec264SJakub Kicinski    sim.cls_bpf_add_filter(obj, da=True, skip_sw=True)
1133caf95228SQuentin Monnet    ret, _, err = sim.cls_bpf_add_filter(obj, da=True, skip_sw=True,
1134caf95228SQuentin Monnet                                         fail=False, include_stderr=True)
1135fba961abSDavid S. Miller    fail(ret == 0, "Managed to offload two TC filters at the same time")
1136caf95228SQuentin Monnet    check_extack_nsim(err, "driver and netdev offload states mismatch.", args)
1137417ec264SJakub Kicinski
1138417ec264SJakub Kicinski    sim.tc_flush_filters(bound=2, total=2)
1139417ec264SJakub Kicinski
1140417ec264SJakub Kicinski    start_test("Test if netdev removal waits for translation...")
1141417ec264SJakub Kicinski    delay_msec = 500
1142d514f41eSJiri Pirko    sim.dfs["dev/bpf_bind_verifier_delay"] = delay_msec
1143417ec264SJakub Kicinski    start = time.time()
1144417ec264SJakub Kicinski    cmd_line = "tc filter add dev %s ingress bpf %s da skip_sw" % \
1145417ec264SJakub Kicinski               (sim['ifname'], obj)
1146417ec264SJakub Kicinski    tc_proc = cmd(cmd_line, background=True, fail=False)
1147417ec264SJakub Kicinski    # Wait for the verifier to start
1148e05b2d14SJiri Pirko    while simdev.dfs_num_bound_progs() <= 2:
1149417ec264SJakub Kicinski        pass
1150e05b2d14SJiri Pirko    simdev.remove()
1151417ec264SJakub Kicinski    end = time.time()
1152417ec264SJakub Kicinski    ret, _ = cmd_result(tc_proc, fail=False)
1153417ec264SJakub Kicinski    time_diff = end - start
1154417ec264SJakub Kicinski    log("Time", "start:\t%s\nend:\t%s\ndiff:\t%s" % (start, end, time_diff))
1155417ec264SJakub Kicinski
1156417ec264SJakub Kicinski    fail(ret == 0, "Managed to load TC filter on a unregistering device")
1157417ec264SJakub Kicinski    delay_sec = delay_msec * 0.001
1158417ec264SJakub Kicinski    fail(time_diff < delay_sec, "Removal process took %s, expected %s" %
1159417ec264SJakub Kicinski         (time_diff, delay_sec))
1160417ec264SJakub Kicinski
1161752d7b45SJakub Kicinski    # Remove all pinned files and reinstantiate the netdev
1162752d7b45SJakub Kicinski    clean_up()
1163752d7b45SJakub Kicinski    bpftool_prog_list_wait(expected=0)
1164752d7b45SJakub Kicinski
1165e05b2d14SJiri Pirko    simdev = NetdevSimDev()
1166e05b2d14SJiri Pirko    sim, = simdev.nsims
11677fedbb7cSJakub Kicinski    map_obj = bpf_obj("sample_map_ret0.o")
11687fedbb7cSJakub Kicinski    start_test("Test loading program with maps...")
11697fedbb7cSJakub Kicinski    sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
1170752d7b45SJakub Kicinski
1171752d7b45SJakub Kicinski    start_test("Test bpftool bound info reporting (own ns)...")
1172752d7b45SJakub Kicinski    check_dev_info(False, "")
1173752d7b45SJakub Kicinski
1174752d7b45SJakub Kicinski    start_test("Test bpftool bound info reporting (other ns)...")
1175752d7b45SJakub Kicinski    ns = mknetns()
1176752d7b45SJakub Kicinski    sim.set_ns(ns)
1177752d7b45SJakub Kicinski    check_dev_info(True, "")
1178752d7b45SJakub Kicinski
1179752d7b45SJakub Kicinski    start_test("Test bpftool bound info reporting (remote ns)...")
1180752d7b45SJakub Kicinski    check_dev_info(False, ns)
1181752d7b45SJakub Kicinski
1182752d7b45SJakub Kicinski    start_test("Test bpftool bound info reporting (back to own ns)...")
1183752d7b45SJakub Kicinski    sim.set_ns("")
1184752d7b45SJakub Kicinski    check_dev_info(False, "")
1185752d7b45SJakub Kicinski
11867fedbb7cSJakub Kicinski    prog_file, _ = pin_prog("/sys/fs/bpf/tmp_prog")
11877fedbb7cSJakub Kicinski    map_file, _ = pin_map("/sys/fs/bpf/tmp_map", idx=1, expected=2)
1188e05b2d14SJiri Pirko    simdev.remove()
1189752d7b45SJakub Kicinski
1190752d7b45SJakub Kicinski    start_test("Test bpftool bound info reporting (removed dev)...")
11917fedbb7cSJakub Kicinski    check_dev_info_removed(prog_file=prog_file, map_file=map_file)
11927fedbb7cSJakub Kicinski
11937fedbb7cSJakub Kicinski    # Remove all pinned files and reinstantiate the netdev
11947fedbb7cSJakub Kicinski    clean_up()
11957fedbb7cSJakub Kicinski    bpftool_prog_list_wait(expected=0)
11967fedbb7cSJakub Kicinski
1197e05b2d14SJiri Pirko    simdev = NetdevSimDev()
1198e05b2d14SJiri Pirko    sim, = simdev.nsims
11997fedbb7cSJakub Kicinski
12007fedbb7cSJakub Kicinski    start_test("Test map update (no flags)...")
12017fedbb7cSJakub Kicinski    sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
12027fedbb7cSJakub Kicinski    maps = bpftool_map_list(expected=2)
12037fedbb7cSJakub Kicinski    array = maps[0] if maps[0]["type"] == "array" else maps[1]
12047fedbb7cSJakub Kicinski    htab = maps[0] if maps[0]["type"] == "hash" else maps[1]
12057fedbb7cSJakub Kicinski    for m in maps:
12067fedbb7cSJakub Kicinski        for i in range(2):
12077fedbb7cSJakub Kicinski            bpftool("map update id %d key %s value %s" %
12087fedbb7cSJakub Kicinski                    (m["id"], int2str("I", i), int2str("Q", i * 3)))
12097fedbb7cSJakub Kicinski
12107fedbb7cSJakub Kicinski    for m in maps:
12117fedbb7cSJakub Kicinski        ret, _ = bpftool("map update id %d key %s value %s" %
12127fedbb7cSJakub Kicinski                         (m["id"], int2str("I", 3), int2str("Q", 3 * 3)),
12137fedbb7cSJakub Kicinski                         fail=False)
12147fedbb7cSJakub Kicinski        fail(ret == 0, "added too many entries")
12157fedbb7cSJakub Kicinski
12167fedbb7cSJakub Kicinski    start_test("Test map update (exists)...")
12177fedbb7cSJakub Kicinski    for m in maps:
12187fedbb7cSJakub Kicinski        for i in range(2):
12197fedbb7cSJakub Kicinski            bpftool("map update id %d key %s value %s exist" %
12207fedbb7cSJakub Kicinski                    (m["id"], int2str("I", i), int2str("Q", i * 3)))
12217fedbb7cSJakub Kicinski
12227fedbb7cSJakub Kicinski    for m in maps:
12237fedbb7cSJakub Kicinski        ret, err = bpftool("map update id %d key %s value %s exist" %
12247fedbb7cSJakub Kicinski                           (m["id"], int2str("I", 3), int2str("Q", 3 * 3)),
12257fedbb7cSJakub Kicinski                           fail=False)
12267fedbb7cSJakub Kicinski        fail(ret == 0, "updated non-existing key")
12277fedbb7cSJakub Kicinski        fail(err["error"].find("No such file or directory") == -1,
12287fedbb7cSJakub Kicinski             "expected ENOENT, error is '%s'" % (err["error"]))
12297fedbb7cSJakub Kicinski
12307fedbb7cSJakub Kicinski    start_test("Test map update (noexist)...")
12317fedbb7cSJakub Kicinski    for m in maps:
12327fedbb7cSJakub Kicinski        for i in range(2):
12337fedbb7cSJakub Kicinski            ret, err = bpftool("map update id %d key %s value %s noexist" %
12347fedbb7cSJakub Kicinski                               (m["id"], int2str("I", i), int2str("Q", i * 3)),
12357fedbb7cSJakub Kicinski                               fail=False)
12367fedbb7cSJakub Kicinski        fail(ret == 0, "updated existing key")
12377fedbb7cSJakub Kicinski        fail(err["error"].find("File exists") == -1,
12387fedbb7cSJakub Kicinski             "expected EEXIST, error is '%s'" % (err["error"]))
12397fedbb7cSJakub Kicinski
12407fedbb7cSJakub Kicinski    start_test("Test map dump...")
12417fedbb7cSJakub Kicinski    for m in maps:
12427fedbb7cSJakub Kicinski        _, entries = bpftool("map dump id %d" % (m["id"]))
12437fedbb7cSJakub Kicinski        for i in range(2):
12447fedbb7cSJakub Kicinski            key = str2int(entries[i]["key"])
12457fedbb7cSJakub Kicinski            fail(key != i, "expected key %d, got %d" % (key, i))
12467fedbb7cSJakub Kicinski            val = str2int(entries[i]["value"])
12477fedbb7cSJakub Kicinski            fail(val != i * 3, "expected value %d, got %d" % (val, i * 3))
12487fedbb7cSJakub Kicinski
12497fedbb7cSJakub Kicinski    start_test("Test map getnext...")
12507fedbb7cSJakub Kicinski    for m in maps:
12517fedbb7cSJakub Kicinski        _, entry = bpftool("map getnext id %d" % (m["id"]))
12527fedbb7cSJakub Kicinski        key = str2int(entry["next_key"])
12537fedbb7cSJakub Kicinski        fail(key != 0, "next key %d, expected %d" % (key, 0))
12547fedbb7cSJakub Kicinski        _, entry = bpftool("map getnext id %d key %s" %
12557fedbb7cSJakub Kicinski                           (m["id"], int2str("I", 0)))
12567fedbb7cSJakub Kicinski        key = str2int(entry["next_key"])
12577fedbb7cSJakub Kicinski        fail(key != 1, "next key %d, expected %d" % (key, 1))
12587fedbb7cSJakub Kicinski        ret, err = bpftool("map getnext id %d key %s" %
12597fedbb7cSJakub Kicinski                           (m["id"], int2str("I", 1)), fail=False)
12607fedbb7cSJakub Kicinski        fail(ret == 0, "got next key past the end of map")
12617fedbb7cSJakub Kicinski        fail(err["error"].find("No such file or directory") == -1,
12627fedbb7cSJakub Kicinski             "expected ENOENT, error is '%s'" % (err["error"]))
12637fedbb7cSJakub Kicinski
12647fedbb7cSJakub Kicinski    start_test("Test map delete (htab)...")
12657fedbb7cSJakub Kicinski    for i in range(2):
12667fedbb7cSJakub Kicinski        bpftool("map delete id %d key %s" % (htab["id"], int2str("I", i)))
12677fedbb7cSJakub Kicinski
12687fedbb7cSJakub Kicinski    start_test("Test map delete (array)...")
12697fedbb7cSJakub Kicinski    for i in range(2):
12707fedbb7cSJakub Kicinski        ret, err = bpftool("map delete id %d key %s" %
12717fedbb7cSJakub Kicinski                           (htab["id"], int2str("I", i)), fail=False)
12727fedbb7cSJakub Kicinski        fail(ret == 0, "removed entry from an array")
12737fedbb7cSJakub Kicinski        fail(err["error"].find("No such file or directory") == -1,
12747fedbb7cSJakub Kicinski             "expected ENOENT, error is '%s'" % (err["error"]))
12757fedbb7cSJakub Kicinski
12767fedbb7cSJakub Kicinski    start_test("Test map remove...")
12777fedbb7cSJakub Kicinski    sim.unset_xdp("offload")
12787fedbb7cSJakub Kicinski    bpftool_map_list_wait(expected=0)
1279e05b2d14SJiri Pirko    simdev.remove()
12807fedbb7cSJakub Kicinski
1281e05b2d14SJiri Pirko    simdev = NetdevSimDev()
1282e05b2d14SJiri Pirko    sim, = simdev.nsims
12837fedbb7cSJakub Kicinski    sim.set_xdp(map_obj, "offload", JSON=False) # map fixup msg breaks JSON
1284e05b2d14SJiri Pirko    simdev.remove()
12857fedbb7cSJakub Kicinski    bpftool_map_list_wait(expected=0)
12867fedbb7cSJakub Kicinski
12877fedbb7cSJakub Kicinski    start_test("Test map creation fail path...")
1288e05b2d14SJiri Pirko    simdev = NetdevSimDev()
1289e05b2d14SJiri Pirko    sim, = simdev.nsims
12907fedbb7cSJakub Kicinski    sim.dfs["bpf_map_accept"] = "N"
12917fedbb7cSJakub Kicinski    ret, _ = sim.set_xdp(map_obj, "offload", JSON=False, fail=False)
12927fedbb7cSJakub Kicinski    fail(ret == 0,
12937fedbb7cSJakub Kicinski         "netdevsim didn't refuse to create a map with offload disabled")
1294752d7b45SJakub Kicinski
1295e05b2d14SJiri Pirko    simdev.remove()
12967736b6edSJakub Kicinski
12977736b6edSJakub Kicinski    start_test("Test multi-dev ASIC program reuse...")
1298e05b2d14SJiri Pirko    simdevA = NetdevSimDev()
1299e05b2d14SJiri Pirko    simA, = simdevA.nsims
1300e05b2d14SJiri Pirko    simdevB = NetdevSimDev(3)
1301e05b2d14SJiri Pirko    simB1, simB2, simB3 = simdevB.nsims
13027736b6edSJakub Kicinski    sims = (simA, simB1, simB2, simB3)
13037736b6edSJakub Kicinski    simB = (simB1, simB2, simB3)
13047736b6edSJakub Kicinski
13057736b6edSJakub Kicinski    bpftool_prog_load("sample_map_ret0.o", "/sys/fs/bpf/nsimA",
13067736b6edSJakub Kicinski                      dev=simA['ifname'])
13077736b6edSJakub Kicinski    progA = bpf_pinned("/sys/fs/bpf/nsimA")
13087736b6edSJakub Kicinski    bpftool_prog_load("sample_map_ret0.o", "/sys/fs/bpf/nsimB",
13097736b6edSJakub Kicinski                      dev=simB1['ifname'])
13107736b6edSJakub Kicinski    progB = bpf_pinned("/sys/fs/bpf/nsimB")
13117736b6edSJakub Kicinski
13127736b6edSJakub Kicinski    simA.set_xdp(progA, "offload", JSON=False)
1313e05b2d14SJiri Pirko    for d in simdevB.nsims:
13147736b6edSJakub Kicinski        d.set_xdp(progB, "offload", JSON=False)
13157736b6edSJakub Kicinski
13167736b6edSJakub Kicinski    start_test("Test multi-dev ASIC cross-dev replace...")
13177736b6edSJakub Kicinski    ret, _ = simA.set_xdp(progB, "offload", force=True, JSON=False, fail=False)
13187736b6edSJakub Kicinski    fail(ret == 0, "cross-ASIC program allowed")
1319e05b2d14SJiri Pirko    for d in simdevB.nsims:
13207736b6edSJakub Kicinski        ret, _ = d.set_xdp(progA, "offload", force=True, JSON=False, fail=False)
13217736b6edSJakub Kicinski        fail(ret == 0, "cross-ASIC program allowed")
13227736b6edSJakub Kicinski
13237736b6edSJakub Kicinski    start_test("Test multi-dev ASIC cross-dev install...")
13247736b6edSJakub Kicinski    for d in sims:
13257736b6edSJakub Kicinski        d.unset_xdp("offload")
13267736b6edSJakub Kicinski
13277736b6edSJakub Kicinski    ret, _, err = simA.set_xdp(progB, "offload", force=True, JSON=False,
13287736b6edSJakub Kicinski                               fail=False, include_stderr=True)
13297736b6edSJakub Kicinski    fail(ret == 0, "cross-ASIC program allowed")
13307736b6edSJakub Kicinski    check_extack_nsim(err, "program bound to different dev.", args)
1331e05b2d14SJiri Pirko    for d in simdevB.nsims:
13327736b6edSJakub Kicinski        ret, _, err = d.set_xdp(progA, "offload", force=True, JSON=False,
13337736b6edSJakub Kicinski                                fail=False, include_stderr=True)
13347736b6edSJakub Kicinski        fail(ret == 0, "cross-ASIC program allowed")
13357736b6edSJakub Kicinski        check_extack_nsim(err, "program bound to different dev.", args)
13367736b6edSJakub Kicinski
13377736b6edSJakub Kicinski    start_test("Test multi-dev ASIC cross-dev map reuse...")
13387736b6edSJakub Kicinski
13397736b6edSJakub Kicinski    mapA = bpftool("prog show %s" % (progA))[1]["map_ids"][0]
13407736b6edSJakub Kicinski    mapB = bpftool("prog show %s" % (progB))[1]["map_ids"][0]
13417736b6edSJakub Kicinski
13427736b6edSJakub Kicinski    ret, _ = bpftool_prog_load("sample_map_ret0.o", "/sys/fs/bpf/nsimB_",
13437736b6edSJakub Kicinski                               dev=simB3['ifname'],
13447736b6edSJakub Kicinski                               maps=["idx 0 id %d" % (mapB)],
13457736b6edSJakub Kicinski                               fail=False)
13467736b6edSJakub Kicinski    fail(ret != 0, "couldn't reuse a map on the same ASIC")
13477736b6edSJakub Kicinski    rm("/sys/fs/bpf/nsimB_")
13487736b6edSJakub Kicinski
13497736b6edSJakub Kicinski    ret, _, err = bpftool_prog_load("sample_map_ret0.o", "/sys/fs/bpf/nsimA_",
13507736b6edSJakub Kicinski                                    dev=simA['ifname'],
13517736b6edSJakub Kicinski                                    maps=["idx 0 id %d" % (mapB)],
13527736b6edSJakub Kicinski                                    fail=False, include_stderr=True)
13537736b6edSJakub Kicinski    fail(ret == 0, "could reuse a map on a different ASIC")
13547736b6edSJakub Kicinski    fail(err.count("offload device mismatch between prog and map") == 0,
13557736b6edSJakub Kicinski         "error message missing for cross-ASIC map")
13567736b6edSJakub Kicinski
13577736b6edSJakub Kicinski    ret, _, err = bpftool_prog_load("sample_map_ret0.o", "/sys/fs/bpf/nsimB_",
13587736b6edSJakub Kicinski                                    dev=simB1['ifname'],
13597736b6edSJakub Kicinski                                    maps=["idx 0 id %d" % (mapA)],
13607736b6edSJakub Kicinski                                    fail=False, include_stderr=True)
13617736b6edSJakub Kicinski    fail(ret == 0, "could reuse a map on a different ASIC")
13627736b6edSJakub Kicinski    fail(err.count("offload device mismatch between prog and map") == 0,
13637736b6edSJakub Kicinski         "error message missing for cross-ASIC map")
13647736b6edSJakub Kicinski
13657736b6edSJakub Kicinski    start_test("Test multi-dev ASIC cross-dev destruction...")
13667736b6edSJakub Kicinski    bpftool_prog_list_wait(expected=2)
13677736b6edSJakub Kicinski
1368e05b2d14SJiri Pirko    simdevA.remove()
13697736b6edSJakub Kicinski    bpftool_prog_list_wait(expected=1)
13707736b6edSJakub Kicinski
13717736b6edSJakub Kicinski    ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"]
13721c6d6e02SMasanari Iida    fail(ifnameB != simB1['ifname'], "program not bound to original device")
13737736b6edSJakub Kicinski    simB1.remove()
13747736b6edSJakub Kicinski    bpftool_prog_list_wait(expected=1)
13757736b6edSJakub Kicinski
13767736b6edSJakub Kicinski    start_test("Test multi-dev ASIC cross-dev destruction - move...")
13777736b6edSJakub Kicinski    ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"]
13787736b6edSJakub Kicinski    fail(ifnameB not in (simB2['ifname'], simB3['ifname']),
13797736b6edSJakub Kicinski         "program not bound to remaining devices")
13807736b6edSJakub Kicinski
13817736b6edSJakub Kicinski    simB2.remove()
13827736b6edSJakub Kicinski    ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"]
13837736b6edSJakub Kicinski    fail(ifnameB != simB3['ifname'], "program not bound to remaining device")
13847736b6edSJakub Kicinski
13857736b6edSJakub Kicinski    simB3.remove()
1386e05b2d14SJiri Pirko    simdevB.remove()
13877736b6edSJakub Kicinski    bpftool_prog_list_wait(expected=0)
13887736b6edSJakub Kicinski
13897736b6edSJakub Kicinski    start_test("Test multi-dev ASIC cross-dev destruction - orphaned...")
13907736b6edSJakub Kicinski    ret, out = bpftool("prog show %s" % (progB), fail=False)
13917736b6edSJakub Kicinski    fail(ret == 0, "got information about orphaned program")
13927736b6edSJakub Kicinski    fail("error" not in out, "no error reported for get info on orphaned")
13937736b6edSJakub Kicinski    fail(out["error"] != "can't get prog info: No such device",
13947736b6edSJakub Kicinski         "wrong error for get info on orphaned")
13957736b6edSJakub Kicinski
1396417ec264SJakub Kicinski    print("%s: OK" % (os.path.basename(__file__)))
1397417ec264SJakub Kicinski
1398417ec264SJakub Kicinskifinally:
1399417ec264SJakub Kicinski    log("Clean up...", "", level=1)
1400417ec264SJakub Kicinski    log_level_inc()
1401417ec264SJakub Kicinski    clean_up()
1402