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