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