Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/bpf: add offload test based on netdevsim

Add a test of BPF offload control path interfaces based on
just-added netdevsim driver. Perform various checks of both
the stack and the expected driver behaviour.

Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Reviewed-by: Simon Horman <simon.horman@netronome.com>
Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>

authored by

Jakub Kicinski and committed by
Daniel Borkmann
417ec264 31d3ad83

+691 -2
+3 -2
tools/testing/selftests/bpf/Makefile
··· 17 17 18 18 TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test_obj_id.o \ 19 19 test_pkt_md_access.o test_xdp_redirect.o test_xdp_meta.o sockmap_parse_prog.o \ 20 - sockmap_verdict_prog.o dev_cgroup.o 20 + sockmap_verdict_prog.o dev_cgroup.o sample_ret0.o 21 21 22 - TEST_PROGS := test_kmod.sh test_xdp_redirect.sh test_xdp_meta.sh 22 + TEST_PROGS := test_kmod.sh test_xdp_redirect.sh test_xdp_meta.sh \ 23 + test_offload.py 23 24 24 25 include ../lib.mk 25 26
+7
tools/testing/selftests/bpf/sample_ret0.c
··· 1 + /* SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) */ 2 + 3 + /* Sample program which should always load for testing control paths. */ 4 + int func() 5 + { 6 + return 0; 7 + }
+681
tools/testing/selftests/bpf/test_offload.py
··· 1 + #!/usr/bin/python3 2 + 3 + # Copyright (C) 2017 Netronome Systems, Inc. 4 + # 5 + # This software is licensed under the GNU General License Version 2, 6 + # June 1991 as shown in the file COPYING in the top-level directory of this 7 + # source tree. 8 + # 9 + # THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" 10 + # WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, 11 + # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 12 + # FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE 13 + # OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME 14 + # THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 15 + 16 + from datetime import datetime 17 + import argparse 18 + import json 19 + import os 20 + import pprint 21 + import subprocess 22 + import time 23 + 24 + logfile = None 25 + log_level = 1 26 + bpf_test_dir = os.path.dirname(os.path.realpath(__file__)) 27 + pp = pprint.PrettyPrinter() 28 + devs = [] # devices we created for clean up 29 + files = [] # files to be removed 30 + 31 + def log_get_sec(level=0): 32 + return "*" * (log_level + level) 33 + 34 + def log_level_inc(add=1): 35 + global log_level 36 + log_level += add 37 + 38 + def log_level_dec(sub=1): 39 + global log_level 40 + log_level -= sub 41 + 42 + def log_level_set(level): 43 + global log_level 44 + log_level = level 45 + 46 + def log(header, data, level=None): 47 + """ 48 + Output to an optional log. 49 + """ 50 + if logfile is None: 51 + return 52 + if level is not None: 53 + log_level_set(level) 54 + 55 + if not isinstance(data, str): 56 + data = pp.pformat(data) 57 + 58 + if len(header): 59 + logfile.write("\n" + log_get_sec() + " ") 60 + logfile.write(header) 61 + if len(header) and len(data.strip()): 62 + logfile.write("\n") 63 + logfile.write(data) 64 + 65 + def skip(cond, msg): 66 + if not cond: 67 + return 68 + print("SKIP: " + msg) 69 + log("SKIP: " + msg, "", level=1) 70 + os.sys.exit(0) 71 + 72 + def fail(cond, msg): 73 + if not cond: 74 + return 75 + print("FAIL: " + msg) 76 + log("FAIL: " + msg, "", level=1) 77 + os.sys.exit(1) 78 + 79 + def start_test(msg): 80 + log(msg, "", level=1) 81 + log_level_inc() 82 + print(msg) 83 + 84 + def cmd(cmd, shell=True, include_stderr=False, background=False, fail=True): 85 + """ 86 + Run a command in subprocess and return tuple of (retval, stdout); 87 + optionally return stderr as well as third value. 88 + """ 89 + proc = subprocess.Popen(cmd, shell=shell, stdout=subprocess.PIPE, 90 + stderr=subprocess.PIPE) 91 + if background: 92 + msg = "%s START: %s" % (log_get_sec(1), 93 + datetime.now().strftime("%H:%M:%S.%f")) 94 + log("BKG " + proc.args, msg) 95 + return proc 96 + 97 + return cmd_result(proc, include_stderr=include_stderr, fail=fail) 98 + 99 + def cmd_result(proc, include_stderr=False, fail=False): 100 + stdout, stderr = proc.communicate() 101 + stdout = stdout.decode("utf-8") 102 + stderr = stderr.decode("utf-8") 103 + proc.stdout.close() 104 + proc.stderr.close() 105 + 106 + stderr = "\n" + stderr 107 + if stderr[-1] == "\n": 108 + stderr = stderr[:-1] 109 + 110 + sec = log_get_sec(1) 111 + log("CMD " + proc.args, 112 + "RETCODE: %d\n%s STDOUT:\n%s%s STDERR:%s\n%s END: %s" % 113 + (proc.returncode, sec, stdout, sec, stderr, 114 + sec, datetime.now().strftime("%H:%M:%S.%f"))) 115 + 116 + if proc.returncode != 0 and fail: 117 + if len(stderr) > 0 and stderr[-1] == "\n": 118 + stderr = stderr[:-1] 119 + raise Exception("Command failed: %s\n%s" % (proc.args, stderr)) 120 + 121 + if include_stderr: 122 + return proc.returncode, stdout, stderr 123 + else: 124 + return proc.returncode, stdout 125 + 126 + def rm(f): 127 + cmd("rm -f %s" % (f)) 128 + if f in files: 129 + files.remove(f) 130 + 131 + def tool(name, args, flags, JSON=True, fail=True): 132 + params = "" 133 + if JSON: 134 + params += "%s " % (flags["json"]) 135 + 136 + ret, out = cmd(name + " " + params + args, fail=fail) 137 + if JSON and len(out.strip()) != 0: 138 + return ret, json.loads(out) 139 + else: 140 + return ret, out 141 + 142 + def bpftool(args, JSON=True, fail=True): 143 + return tool("bpftool", args, {"json":"-p"}, JSON=JSON, fail=fail) 144 + 145 + def bpftool_prog_list(expected=None): 146 + _, progs = bpftool("prog show", JSON=True, fail=True) 147 + if expected is not None: 148 + if len(progs) != expected: 149 + fail(True, "%d BPF programs loaded, expected %d" % 150 + (len(progs), expected)) 151 + return progs 152 + 153 + def bpftool_prog_list_wait(expected=0, n_retry=20): 154 + for i in range(n_retry): 155 + nprogs = len(bpftool_prog_list()) 156 + if nprogs == expected: 157 + return 158 + time.sleep(0.05) 159 + raise Exception("Time out waiting for program counts to stabilize want %d, have %d" % (expected, nprogs)) 160 + 161 + def ip(args, force=False, JSON=True, fail=True): 162 + if force: 163 + args = "-force " + args 164 + return tool("ip", args, {"json":"-j"}, JSON=JSON, fail=fail) 165 + 166 + def tc(args, JSON=True, fail=True): 167 + return tool("tc", args, {"json":"-p"}, JSON=JSON, fail=fail) 168 + 169 + def ethtool(dev, opt, args, fail=True): 170 + return cmd("ethtool %s %s %s" % (opt, dev["ifname"], args), fail=fail) 171 + 172 + def bpf_obj(name, sec=".text", path=bpf_test_dir,): 173 + return "obj %s sec %s" % (os.path.join(path, name), sec) 174 + 175 + def bpf_pinned(name): 176 + return "pinned %s" % (name) 177 + 178 + def bpf_bytecode(bytecode): 179 + return "bytecode \"%s\"" % (bytecode) 180 + 181 + class DebugfsDir: 182 + """ 183 + Class for accessing DebugFS directories as a dictionary. 184 + """ 185 + 186 + def __init__(self, path): 187 + self.path = path 188 + self._dict = self._debugfs_dir_read(path) 189 + 190 + def __len__(self): 191 + return len(self._dict.keys()) 192 + 193 + def __getitem__(self, key): 194 + if type(key) is int: 195 + key = list(self._dict.keys())[key] 196 + return self._dict[key] 197 + 198 + def __setitem__(self, key, value): 199 + log("DebugFS set %s = %s" % (key, value), "") 200 + log_level_inc() 201 + 202 + cmd("echo '%s' > %s/%s" % (value, self.path, key)) 203 + log_level_dec() 204 + 205 + _, out = cmd('cat %s/%s' % (self.path, key)) 206 + self._dict[key] = out.strip() 207 + 208 + def _debugfs_dir_read(self, path): 209 + dfs = {} 210 + 211 + log("DebugFS state for %s" % (path), "") 212 + log_level_inc(add=2) 213 + 214 + _, out = cmd('ls ' + path) 215 + for f in out.split(): 216 + p = os.path.join(path, f) 217 + if os.path.isfile(p): 218 + _, out = cmd('cat %s/%s' % (path, f)) 219 + dfs[f] = out.strip() 220 + elif os.path.isdir(p): 221 + dfs[f] = DebugfsDir(p) 222 + else: 223 + raise Exception("%s is neither file nor directory" % (p)) 224 + 225 + log_level_dec() 226 + log("DebugFS state", dfs) 227 + log_level_dec() 228 + 229 + return dfs 230 + 231 + class NetdevSim: 232 + """ 233 + Class for netdevsim netdevice and its attributes. 234 + """ 235 + 236 + def __init__(self): 237 + self.dev = self._netdevsim_create() 238 + devs.append(self) 239 + 240 + self.dfs_dir = '/sys/kernel/debug/netdevsim/%s' % (self.dev['ifname']) 241 + self.dfs_refresh() 242 + 243 + def __getitem__(self, key): 244 + return self.dev[key] 245 + 246 + def _netdevsim_create(self): 247 + _, old = ip("link show") 248 + ip("link add sim%d type netdevsim") 249 + _, new = ip("link show") 250 + 251 + for dev in new: 252 + f = filter(lambda x: x["ifname"] == dev["ifname"], old) 253 + if len(list(f)) == 0: 254 + return dev 255 + 256 + raise Exception("failed to create netdevsim device") 257 + 258 + def remove(self): 259 + devs.remove(self) 260 + ip("link del dev %s" % (self.dev["ifname"])) 261 + 262 + def dfs_refresh(self): 263 + self.dfs = DebugfsDir(self.dfs_dir) 264 + return self.dfs 265 + 266 + def dfs_num_bound_progs(self): 267 + path = os.path.join(self.dfs_dir, "bpf_bound_progs") 268 + _, progs = cmd('ls %s' % (path)) 269 + return len(progs.split()) 270 + 271 + def dfs_get_bound_progs(self, expected): 272 + progs = DebugfsDir(os.path.join(self.dfs_dir, "bpf_bound_progs")) 273 + if expected is not None: 274 + if len(progs) != expected: 275 + fail(True, "%d BPF programs bound, expected %d" % 276 + (len(progs), expected)) 277 + return progs 278 + 279 + def wait_for_flush(self, bound=0, total=0, n_retry=20): 280 + for i in range(n_retry): 281 + nbound = self.dfs_num_bound_progs() 282 + nprogs = len(bpftool_prog_list()) 283 + if nbound == bound and nprogs == total: 284 + return 285 + time.sleep(0.05) 286 + raise Exception("Time out waiting for program counts to stabilize want %d/%d, have %d bound, %d loaded" % (bound, total, nbound, nprogs)) 287 + 288 + def set_mtu(self, mtu, fail=True): 289 + return ip("link set dev %s mtu %d" % (self.dev["ifname"], mtu), 290 + fail=fail) 291 + 292 + def set_xdp(self, bpf, mode, force=False, fail=True): 293 + return ip("link set dev %s xdp%s %s" % (self.dev["ifname"], mode, bpf), 294 + force=force, fail=fail) 295 + 296 + def unset_xdp(self, mode, force=False, fail=True): 297 + return ip("link set dev %s xdp%s off" % (self.dev["ifname"], mode), 298 + force=force, fail=fail) 299 + 300 + def ip_link_show(self, xdp): 301 + _, link = ip("link show dev %s" % (self['ifname'])) 302 + if len(link) > 1: 303 + raise Exception("Multiple objects on ip link show") 304 + if len(link) < 1: 305 + return {} 306 + fail(xdp != "xdp" in link, 307 + "XDP program not reporting in iplink (reported %s, expected %s)" % 308 + ("xdp" in link, xdp)) 309 + return link[0] 310 + 311 + def tc_add_ingress(self): 312 + tc("qdisc add dev %s ingress" % (self['ifname'])) 313 + 314 + def tc_del_ingress(self): 315 + tc("qdisc del dev %s ingress" % (self['ifname'])) 316 + 317 + def tc_flush_filters(self, bound=0, total=0): 318 + self.tc_del_ingress() 319 + self.tc_add_ingress() 320 + self.wait_for_flush(bound=bound, total=total) 321 + 322 + def tc_show_ingress(self, expected=None): 323 + # No JSON support, oh well... 324 + flags = ["skip_sw", "skip_hw", "in_hw"] 325 + named = ["protocol", "pref", "chain", "handle", "id", "tag"] 326 + 327 + args = "-s filter show dev %s ingress" % (self['ifname']) 328 + _, out = tc(args, JSON=False) 329 + 330 + filters = [] 331 + lines = out.split('\n') 332 + for line in lines: 333 + words = line.split() 334 + if "handle" not in words: 335 + continue 336 + fltr = {} 337 + for flag in flags: 338 + fltr[flag] = flag in words 339 + for name in named: 340 + try: 341 + idx = words.index(name) 342 + fltr[name] = words[idx + 1] 343 + except ValueError: 344 + pass 345 + filters.append(fltr) 346 + 347 + if expected is not None: 348 + fail(len(filters) != expected, 349 + "%d ingress filters loaded, expected %d" % 350 + (len(filters), expected)) 351 + return filters 352 + 353 + def cls_bpf_add_filter(self, bpf, da=False, skip_sw=False, skip_hw=False, 354 + fail=True): 355 + params = "" 356 + if da: 357 + params += " da" 358 + if skip_sw: 359 + params += " skip_sw" 360 + if skip_hw: 361 + params += " skip_hw" 362 + return tc("filter add dev %s ingress bpf %s %s" % 363 + (self['ifname'], bpf, params), fail=fail) 364 + 365 + def set_ethtool_tc_offloads(self, enable, fail=True): 366 + args = "hw-tc-offload %s" % ("on" if enable else "off") 367 + return ethtool(self, "-K", args, fail=fail) 368 + 369 + ################################################################################ 370 + def clean_up(): 371 + for dev in devs: 372 + dev.remove() 373 + for f in files: 374 + cmd("rm -f %s" % (f)) 375 + 376 + def pin_prog(file_name, idx=0): 377 + progs = bpftool_prog_list(expected=(idx + 1)) 378 + prog = progs[idx] 379 + bpftool("prog pin id %d %s" % (prog["id"], file_name)) 380 + files.append(file_name) 381 + 382 + return file_name, bpf_pinned(file_name) 383 + 384 + # Parse command line 385 + parser = argparse.ArgumentParser() 386 + parser.add_argument("--log", help="output verbose log to given file") 387 + args = parser.parse_args() 388 + if args.log: 389 + logfile = open(args.log, 'w+') 390 + logfile.write("# -*-Org-*-") 391 + 392 + log("Prepare...", "", level=1) 393 + log_level_inc() 394 + 395 + # Check permissions 396 + skip(os.getuid() != 0, "test must be run as root") 397 + 398 + # Check tools 399 + ret, progs = bpftool("prog", fail=False) 400 + skip(ret != 0, "bpftool not installed") 401 + # Check no BPF programs are loaded 402 + skip(len(progs) != 0, "BPF programs already loaded on the system") 403 + 404 + # Check netdevsim 405 + ret, out = cmd("modprobe netdevsim", fail=False) 406 + skip(ret != 0, "netdevsim module could not be loaded") 407 + 408 + # Check debugfs 409 + _, out = cmd("mount") 410 + if out.find("/sys/kernel/debug type debugfs") == -1: 411 + cmd("mount -t debugfs none /sys/kernel/debug") 412 + 413 + # Check samples are compiled 414 + samples = ["sample_ret0.o"] 415 + for s in samples: 416 + ret, out = cmd("ls %s/%s" % (bpf_test_dir, s), fail=False) 417 + skip(ret != 0, "sample %s/%s not found, please compile it" % 418 + (bpf_test_dir, s)) 419 + 420 + try: 421 + obj = bpf_obj("sample_ret0.o") 422 + bytecode = bpf_bytecode("1,6 0 0 4294967295,") 423 + 424 + start_test("Test destruction of generic XDP...") 425 + sim = NetdevSim() 426 + sim.set_xdp(obj, "generic") 427 + sim.remove() 428 + bpftool_prog_list_wait(expected=0) 429 + 430 + sim = NetdevSim() 431 + sim.tc_add_ingress() 432 + 433 + start_test("Test TC non-offloaded...") 434 + ret, _ = sim.cls_bpf_add_filter(obj, skip_hw=True, fail=False) 435 + fail(ret != 0, "Software TC filter did not load") 436 + 437 + start_test("Test TC non-offloaded isn't getting bound...") 438 + ret, _ = sim.cls_bpf_add_filter(obj, fail=False) 439 + fail(ret != 0, "Software TC filter did not load") 440 + sim.dfs_get_bound_progs(expected=0) 441 + 442 + sim.tc_flush_filters() 443 + 444 + start_test("Test TC offloads are off by default...") 445 + ret, _ = sim.cls_bpf_add_filter(obj, skip_sw=True, fail=False) 446 + fail(ret == 0, "TC filter loaded without enabling TC offloads") 447 + sim.wait_for_flush() 448 + 449 + sim.set_ethtool_tc_offloads(True) 450 + sim.dfs["bpf_tc_non_bound_accept"] = "Y" 451 + 452 + start_test("Test TC offload by default...") 453 + ret, _ = sim.cls_bpf_add_filter(obj, fail=False) 454 + fail(ret != 0, "Software TC filter did not load") 455 + sim.dfs_get_bound_progs(expected=0) 456 + ingress = sim.tc_show_ingress(expected=1) 457 + fltr = ingress[0] 458 + fail(not fltr["in_hw"], "Filter not offloaded by default") 459 + 460 + sim.tc_flush_filters() 461 + 462 + start_test("Test TC cBPF bytcode tries offload by default...") 463 + ret, _ = sim.cls_bpf_add_filter(bytecode, fail=False) 464 + fail(ret != 0, "Software TC filter did not load") 465 + sim.dfs_get_bound_progs(expected=0) 466 + ingress = sim.tc_show_ingress(expected=1) 467 + fltr = ingress[0] 468 + fail(not fltr["in_hw"], "Bytecode not offloaded by default") 469 + 470 + sim.tc_flush_filters() 471 + sim.dfs["bpf_tc_non_bound_accept"] = "N" 472 + 473 + start_test("Test TC cBPF unbound bytecode doesn't offload...") 474 + ret, _ = sim.cls_bpf_add_filter(bytecode, skip_sw=True, fail=False) 475 + fail(ret == 0, "TC bytecode loaded for offload") 476 + sim.wait_for_flush() 477 + 478 + start_test("Test TC offloads work...") 479 + ret, _ = sim.cls_bpf_add_filter(obj, skip_sw=True, fail=False) 480 + fail(ret != 0, "TC filter did not load with TC offloads enabled") 481 + 482 + start_test("Test TC offload basics...") 483 + dfs = sim.dfs_get_bound_progs(expected=1) 484 + progs = bpftool_prog_list(expected=1) 485 + ingress = sim.tc_show_ingress(expected=1) 486 + 487 + dprog = dfs[0] 488 + prog = progs[0] 489 + fltr = ingress[0] 490 + fail(fltr["skip_hw"], "TC does reports 'skip_hw' on offloaded filter") 491 + fail(not fltr["in_hw"], "TC does not report 'in_hw' for offloaded filter") 492 + fail(not fltr["skip_sw"], "TC does not report 'skip_sw' back") 493 + 494 + start_test("Test TC offload is device-bound...") 495 + fail(str(prog["id"]) != fltr["id"], "Program IDs don't match") 496 + fail(prog["tag"] != fltr["tag"], "Program tags don't match") 497 + fail(fltr["id"] != dprog["id"], "Program IDs don't match") 498 + fail(dprog["state"] != "xlated", "Offloaded program state not translated") 499 + fail(dprog["loaded"] != "Y", "Offloaded program is not loaded") 500 + 501 + start_test("Test disabling TC offloads is rejected while filters installed...") 502 + ret, _ = sim.set_ethtool_tc_offloads(False, fail=False) 503 + fail(ret == 0, "Driver should refuse to disable TC offloads with filters installed...") 504 + 505 + start_test("Test qdisc removal frees things...") 506 + sim.tc_flush_filters() 507 + sim.tc_show_ingress(expected=0) 508 + 509 + start_test("Test disabling TC offloads is OK without filters...") 510 + ret, _ = sim.set_ethtool_tc_offloads(False, fail=False) 511 + fail(ret != 0, 512 + "Driver refused to disable TC offloads without filters installed...") 513 + 514 + sim.set_ethtool_tc_offloads(True) 515 + 516 + start_test("Test destroying device gets rid of TC filters...") 517 + sim.cls_bpf_add_filter(obj, skip_sw=True) 518 + sim.remove() 519 + bpftool_prog_list_wait(expected=0) 520 + 521 + sim = NetdevSim() 522 + sim.set_ethtool_tc_offloads(True) 523 + 524 + start_test("Test destroying device gets rid of XDP...") 525 + sim.set_xdp(obj, "offload") 526 + sim.remove() 527 + bpftool_prog_list_wait(expected=0) 528 + 529 + sim = NetdevSim() 530 + sim.set_ethtool_tc_offloads(True) 531 + 532 + start_test("Test XDP prog reporting...") 533 + sim.set_xdp(obj, "drv") 534 + ipl = sim.ip_link_show(xdp=True) 535 + progs = bpftool_prog_list(expected=1) 536 + fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"], 537 + "Loaded program has wrong ID") 538 + 539 + start_test("Test XDP prog replace without force...") 540 + ret, _ = sim.set_xdp(obj, "drv", fail=False) 541 + fail(ret == 0, "Replaced XDP program without -force") 542 + sim.wait_for_flush(total=1) 543 + 544 + start_test("Test XDP prog replace with force...") 545 + ret, _ = sim.set_xdp(obj, "drv", force=True, fail=False) 546 + fail(ret != 0, "Could not replace XDP program with -force") 547 + bpftool_prog_list_wait(expected=1) 548 + ipl = sim.ip_link_show(xdp=True) 549 + progs = bpftool_prog_list(expected=1) 550 + fail(ipl["xdp"]["prog"]["id"] != progs[0]["id"], 551 + "Loaded program has wrong ID") 552 + 553 + start_test("Test XDP prog replace with bad flags...") 554 + ret, _ = sim.set_xdp(obj, "offload", force=True, fail=False) 555 + fail(ret == 0, "Replaced XDP program with a program in different mode") 556 + ret, _ = sim.set_xdp(obj, "", force=True, fail=False) 557 + fail(ret == 0, "Replaced XDP program with a program in different mode") 558 + 559 + start_test("Test XDP prog remove with bad flags...") 560 + ret, _ = sim.unset_xdp("offload", force=True, fail=False) 561 + fail(ret == 0, "Removed program with a bad mode mode") 562 + ret, _ = sim.unset_xdp("", force=True, fail=False) 563 + fail(ret == 0, "Removed program with a bad mode mode") 564 + 565 + start_test("Test MTU restrictions...") 566 + ret, _ = sim.set_mtu(9000, fail=False) 567 + fail(ret == 0, 568 + "Driver should refuse to increase MTU to 9000 with XDP loaded...") 569 + sim.unset_xdp("drv") 570 + bpftool_prog_list_wait(expected=0) 571 + sim.set_mtu(9000) 572 + ret, _ = sim.set_xdp(obj, "drv", fail=False) 573 + fail(ret == 0, "Driver should refuse to load program with MTU of 9000...") 574 + sim.set_mtu(1500) 575 + 576 + sim.wait_for_flush() 577 + start_test("Test XDP offload...") 578 + sim.set_xdp(obj, "offload") 579 + ipl = sim.ip_link_show(xdp=True) 580 + link_xdp = ipl["xdp"]["prog"] 581 + progs = bpftool_prog_list(expected=1) 582 + prog = progs[0] 583 + fail(link_xdp["id"] != prog["id"], "Loaded program has wrong ID") 584 + 585 + start_test("Test XDP offload is device bound...") 586 + dfs = sim.dfs_get_bound_progs(expected=1) 587 + dprog = dfs[0] 588 + 589 + fail(prog["id"] != link_xdp["id"], "Program IDs don't match") 590 + fail(prog["tag"] != link_xdp["tag"], "Program tags don't match") 591 + fail(str(link_xdp["id"]) != dprog["id"], "Program IDs don't match") 592 + fail(dprog["state"] != "xlated", "Offloaded program state not translated") 593 + fail(dprog["loaded"] != "Y", "Offloaded program is not loaded") 594 + 595 + start_test("Test removing XDP program many times...") 596 + sim.unset_xdp("offload") 597 + sim.unset_xdp("offload") 598 + sim.unset_xdp("drv") 599 + sim.unset_xdp("drv") 600 + sim.unset_xdp("") 601 + sim.unset_xdp("") 602 + bpftool_prog_list_wait(expected=0) 603 + 604 + start_test("Test attempt to use a program for a wrong device...") 605 + sim2 = NetdevSim() 606 + sim2.set_xdp(obj, "offload") 607 + pin_file, pinned = pin_prog("/sys/fs/bpf/tmp") 608 + 609 + ret, _ = sim.set_xdp(pinned, "offload", fail=False) 610 + fail(ret == 0, "Pinned program loaded for a different device accepted") 611 + sim2.remove() 612 + ret, _ = sim.set_xdp(pinned, "offload", fail=False) 613 + fail(ret == 0, "Pinned program loaded for a removed device accepted") 614 + rm(pin_file) 615 + bpftool_prog_list_wait(expected=0) 616 + 617 + start_test("Test mixing of TC and XDP...") 618 + sim.tc_add_ingress() 619 + sim.set_xdp(obj, "offload") 620 + ret, _ = sim.cls_bpf_add_filter(obj, skip_sw=True, fail=False) 621 + fail(ret == 0, "Loading TC when XDP active should fail") 622 + sim.unset_xdp("offload") 623 + sim.wait_for_flush() 624 + 625 + sim.cls_bpf_add_filter(obj, skip_sw=True) 626 + ret, _ = sim.set_xdp(obj, "offload", fail=False) 627 + fail(ret == 0, "Loading XDP when TC active should fail") 628 + 629 + start_test("Test binding TC from pinned...") 630 + pin_file, pinned = pin_prog("/sys/fs/bpf/tmp") 631 + sim.tc_flush_filters(bound=1, total=1) 632 + sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True) 633 + sim.tc_flush_filters(bound=1, total=1) 634 + 635 + start_test("Test binding XDP from pinned...") 636 + sim.set_xdp(obj, "offload") 637 + pin_file, pinned = pin_prog("/sys/fs/bpf/tmp2", idx=1) 638 + 639 + sim.set_xdp(pinned, "offload", force=True) 640 + sim.unset_xdp("offload") 641 + sim.set_xdp(pinned, "offload", force=True) 642 + sim.unset_xdp("offload") 643 + 644 + start_test("Test offload of wrong type fails...") 645 + ret, _ = sim.cls_bpf_add_filter(pinned, da=True, skip_sw=True, fail=False) 646 + fail(ret == 0, "Managed to attach XDP program to TC") 647 + 648 + start_test("Test asking for TC offload of two filters...") 649 + sim.cls_bpf_add_filter(obj, da=True, skip_sw=True) 650 + sim.cls_bpf_add_filter(obj, da=True, skip_sw=True) 651 + # The above will trigger a splat until TC cls_bpf drivers are fixed 652 + 653 + sim.tc_flush_filters(bound=2, total=2) 654 + 655 + start_test("Test if netdev removal waits for translation...") 656 + delay_msec = 500 657 + sim.dfs["bpf_bind_verifier_delay"] = delay_msec 658 + start = time.time() 659 + cmd_line = "tc filter add dev %s ingress bpf %s da skip_sw" % \ 660 + (sim['ifname'], obj) 661 + tc_proc = cmd(cmd_line, background=True, fail=False) 662 + # Wait for the verifier to start 663 + while sim.dfs_num_bound_progs() <= 2: 664 + pass 665 + sim.remove() 666 + end = time.time() 667 + ret, _ = cmd_result(tc_proc, fail=False) 668 + time_diff = end - start 669 + log("Time", "start:\t%s\nend:\t%s\ndiff:\t%s" % (start, end, time_diff)) 670 + 671 + fail(ret == 0, "Managed to load TC filter on a unregistering device") 672 + delay_sec = delay_msec * 0.001 673 + fail(time_diff < delay_sec, "Removal process took %s, expected %s" % 674 + (time_diff, delay_sec)) 675 + 676 + print("%s: OK" % (os.path.basename(__file__))) 677 + 678 + finally: 679 + log("Clean up...", "", level=1) 680 + log_level_inc() 681 + clean_up()