OSDN Git Service

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