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