~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/net/bpf_offload.py

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php