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