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