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