Merge branch 'linus' of git://git.kernel.org/pub/scm/linux/kernel/git/herbert/crypto-2.6
[linux-2.6-block.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 stat
26 import string
27 import struct
28 import subprocess
29 import time
30 import traceback
31
32 logfile = None
33 log_level = 1
34 skip_extack = False
35 bpf_test_dir = os.path.dirname(os.path.realpath(__file__))
36 pp = pprint.PrettyPrinter()
37 devs = [] # devices we created for clean up
38 files = [] # files to be removed
39 netns = [] # net namespaces to be removed
40
41 def log_get_sec(level=0):
42     return "*" * (log_level + level)
43
44 def log_level_inc(add=1):
45     global log_level
46     log_level += add
47
48 def log_level_dec(sub=1):
49     global log_level
50     log_level -= sub
51
52 def log_level_set(level):
53     global log_level
54     log_level = level
55
56 def 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
75 def 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
82 def 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
91 def start_test(msg):
92     log(msg, "", level=1)
93     log_level_inc()
94     print(msg)
95
96 def 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
111 def 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
138 def rm(f):
139     cmd("rm -f %s" % (f))
140     if f in files:
141         files.remove(f)
142
143 def 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
168 def 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
172 def 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
184 def 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
196 def 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
204 def 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
212 def 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
227 def 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
233 def 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
237 def ethtool(dev, opt, args, fail=True):
238     return cmd("ethtool %s %s %s" % (opt, dev["ifname"], args), fail=fail)
239
240 def bpf_obj(name, sec=".text", path=bpf_test_dir,):
241     return "obj %s sec %s" % (os.path.join(path, name), sec)
242
243 def bpf_pinned(name):
244     return "pinned %s" % (name)
245
246 def bpf_bytecode(bytecode):
247     return "bytecode \"%s\"" % (bytecode)
248
249 def 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
258 def 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
264 def 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
278 class 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
334 class 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
423 class 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 ################################################################################
584 def 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
596 def 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
604 def 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
612 def 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
627 def 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
649 def 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
656 def check_extack_nsim(output, reference, args):
657     check_extack(output, "netdevsim: " + reference, args)
658
659 def 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
663 def 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
670 def 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
679 def 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
690 def 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
754 parser = argparse.ArgumentParser()
755 parser.add_argument("--log", help="output verbose log to given file")
756 args = parser.parse_args()
757 if args.log:
758     logfile = open(args.log, 'w+')
759     logfile.write("# -*-Org-*-")
760
761 log("Prepare...", "", level=1)
762 log_level_inc()
763
764 # Check permissions
765 skip(os.getuid() != 0, "test must be run as root")
766
767 # Check tools
768 ret, progs = bpftool("prog", fail=False)
769 skip(ret != 0, "bpftool not installed")
770 base_progs = progs
771 _, base_maps = bpftool("map")
772
773 # Check netdevsim
774 ret, out = cmd("modprobe netdevsim", fail=False)
775 skip(ret != 0, "netdevsim module could not be loaded")
776
777 # Check debugfs
778 _, out = cmd("mount")
779 if out.find("/sys/kernel/debug type debugfs") == -1:
780     cmd("mount -t debugfs none /sys/kernel/debug")
781
782 # Check samples are compiled
783 samples = ["sample_ret0.o", "sample_map_ret0.o"]
784 for 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)
792 if 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
798 ns = mknetns()
799 skip(ns is None, "Could not create a net namespace")
800 cmd("ip netns delete %s" % (ns))
801 netns = []
802
803 try:
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
1395 finally:
1396     log("Clean up...", "", level=1)
1397     log_level_inc()
1398     clean_up()