1# SPDX-License-Identifier: GPL-2.0 2 3import json 4import os 5import random 6import re 7import time 8from .utils import cmd, ip 9 10 11class NetdevSim: 12 """ 13 Class for netdevsim netdevice and its attributes. 14 """ 15 16 def __init__(self, nsimdev, port_index, ifname, ns=None): 17 # In case udev renamed the netdev to according to new schema, 18 # check if the name matches the port_index. 19 nsimnamere = re.compile(r"eni\d+np(\d+)") 20 match = nsimnamere.match(ifname) 21 if match and int(match.groups()[0]) != port_index + 1: 22 raise Exception("netdevice name mismatches the expected one") 23 24 self.nsimdev = nsimdev 25 self.port_index = port_index 26 ret = ip("-j link show dev %s" % ifname, ns=ns) 27 self.dev = json.loads(ret.stdout)[0] 28 29 def dfs_write(self, path, val): 30 self.nsimdev.dfs_write(f'ports/{self.port_index}/' + path, val) 31 32 33class NetdevSimDev: 34 """ 35 Class for netdevsim bus device and its attributes. 36 """ 37 @staticmethod 38 def ctrl_write(path, val): 39 fullpath = os.path.join("/sys/bus/netdevsim/", path) 40 with open(fullpath, "w") as f: 41 f.write(val) 42 43 def dfs_write(self, path, val): 44 fullpath = os.path.join(f"/sys/kernel/debug/netdevsim/netdevsim{self.addr}/", path) 45 with open(fullpath, "w") as f: 46 f.write(val) 47 48 def __init__(self, port_count=1, ns=None): 49 # nsim will spawn in init_net, we'll set to actual ns once we switch it there 50 self.ns = None 51 52 if not os.path.exists("/sys/bus/netdevsim"): 53 cmd("modprobe netdevsim") 54 55 addr = random.randrange(1 << 15) 56 while True: 57 try: 58 self.ctrl_write("new_device", "%u %u" % (addr, port_count)) 59 except OSError as e: 60 if e.errno == errno.ENOSPC: 61 addr = random.randrange(1 << 15) 62 continue 63 raise e 64 break 65 self.addr = addr 66 67 # As probe of netdevsim device might happen from a workqueue, 68 # so wait here until all netdevs appear. 69 self.wait_for_netdevs(port_count) 70 71 if ns: 72 cmd(f"devlink dev reload netdevsim/netdevsim{addr} netns {ns.name}") 73 self.ns = ns 74 75 cmd("udevadm settle", ns=self.ns) 76 ifnames = self.get_ifnames() 77 78 self.dfs_dir = "/sys/kernel/debug/netdevsim/netdevsim%u/" % addr 79 80 self.nsims = [] 81 for port_index in range(port_count): 82 self.nsims.append(NetdevSim(self, port_index, ifnames[port_index], 83 ns=ns)) 84 85 def get_ifnames(self): 86 ifnames = [] 87 listdir = cmd(f"ls /sys/bus/netdevsim/devices/netdevsim{self.addr}/net/", 88 ns=self.ns).stdout.split() 89 for ifname in listdir: 90 ifnames.append(ifname) 91 ifnames.sort() 92 return ifnames 93 94 def wait_for_netdevs(self, port_count): 95 timeout = 5 96 timeout_start = time.time() 97 98 while True: 99 try: 100 ifnames = self.get_ifnames() 101 except FileNotFoundError as e: 102 ifnames = [] 103 if len(ifnames) == port_count: 104 break 105 if time.time() < timeout_start + timeout: 106 continue 107 raise Exception("netdevices did not appear within timeout") 108 109 def remove(self): 110 self.ctrl_write("del_device", "%u" % (self.addr, )) 111 112 def remove_nsim(self, nsim): 113 self.nsims.remove(nsim) 114 self.ctrl_write("devices/netdevsim%u/del_port" % (self.addr, ), 115 "%u" % (nsim.port_index, )) 116