1254721Semaste# SPDX-License-Identifier: GPL-2.0 2254721Semaste 3254721Semasteimport json 4254721Semasteimport os 5254721Semasteimport random 6254721Semasteimport re 7254721Semasteimport time 8254721Semastefrom .utils import cmd, ip 9254721Semaste 10254721Semaste 11254721Semasteclass NetdevSim: 12254721Semaste """ 13254721Semaste Class for netdevsim netdevice and its attributes. 14254721Semaste """ 15254721Semaste 16254721Semaste def __init__(self, nsimdev, port_index, ifname, ns=None): 17254721Semaste # In case udev renamed the netdev to according to new schema, 18254721Semaste # check if the name matches the port_index. 19254721Semaste nsimnamere = re.compile(r"eni\d+np(\d+)") 20254721Semaste match = nsimnamere.match(ifname) 21254721Semaste if match and int(match.groups()[0]) != port_index + 1: 22254721Semaste raise Exception("netdevice name mismatches the expected one") 23254721Semaste 24254721Semaste self.ifname = ifname 25254721Semaste self.nsimdev = nsimdev 26254721Semaste self.port_index = port_index 27254721Semaste self.ns = ns 28254721Semaste self.dfs_dir = "%s/ports/%u/" % (nsimdev.dfs_dir, port_index) 29254721Semaste ret = ip("-j link show dev %s" % ifname, ns=ns) 30254721Semaste self.dev = json.loads(ret.stdout)[0] 31254721Semaste self.ifindex = self.dev["ifindex"] 32254721Semaste 33254721Semaste def dfs_write(self, path, val): 34254721Semaste self.nsimdev.dfs_write(f'ports/{self.port_index}/' + path, val) 35254721Semaste 36254721Semaste 37254721Semasteclass NetdevSimDev: 38254721Semaste """ 39254721Semaste Class for netdevsim bus device and its attributes. 40254721Semaste """ 41254721Semaste @staticmethod 42254721Semaste def ctrl_write(path, val): 43254721Semaste fullpath = os.path.join("/sys/bus/netdevsim/", path) 44254721Semaste with open(fullpath, "w") as f: 45254721Semaste f.write(val) 46254721Semaste 47254721Semaste def dfs_write(self, path, val): 48254721Semaste fullpath = os.path.join(f"/sys/kernel/debug/netdevsim/netdevsim{self.addr}/", path) 49254721Semaste with open(fullpath, "w") as f: 50254721Semaste f.write(val) 51254721Semaste 52254721Semaste def __init__(self, port_count=1, queue_count=1, ns=None): 53254721Semaste # nsim will spawn in init_net, we'll set to actual ns once we switch it there 54254721Semaste self.ns = None 55254721Semaste 56254721Semaste if not os.path.exists("/sys/bus/netdevsim"): 57254721Semaste cmd("modprobe netdevsim") 58254721Semaste 59254721Semaste addr = random.randrange(1 << 15) 60254721Semaste while True: 61254721Semaste try: 62254721Semaste self.ctrl_write("new_device", "%u %u %u" % (addr, port_count, queue_count)) 63254721Semaste except OSError as e: 64254721Semaste if e.errno == errno.ENOSPC: 65254721Semaste addr = random.randrange(1 << 15) 66254721Semaste continue 67254721Semaste raise e 68254721Semaste break 69254721Semaste self.addr = addr 70254721Semaste 71254721Semaste # As probe of netdevsim device might happen from a workqueue, 72254721Semaste # so wait here until all netdevs appear. 73254721Semaste self.wait_for_netdevs(port_count) 74254721Semaste 75254721Semaste if ns: 76254721Semaste cmd(f"devlink dev reload netdevsim/netdevsim{addr} netns {ns.name}") 77254721Semaste self.ns = ns 78254721Semaste 79254721Semaste cmd("udevadm settle", ns=self.ns) 80254721Semaste ifnames = self.get_ifnames() 81254721Semaste 82254721Semaste self.dfs_dir = "/sys/kernel/debug/netdevsim/netdevsim%u/" % addr 83254721Semaste 84254721Semaste self.nsims = [] 85254721Semaste for port_index in range(port_count): 86254721Semaste self.nsims.append(self._make_port(port_index, ifnames[port_index])) 87254721Semaste 88254721Semaste self.removed = False 89254721Semaste 90254721Semaste def __enter__(self): 91254721Semaste return self 92254721Semaste 93254721Semaste def __exit__(self, ex_type, ex_value, ex_tb): 94254721Semaste """ 95254721Semaste __exit__ gets called at the end of a "with" block. 96254721Semaste """ 97254721Semaste self.remove() 98254721Semaste 99254721Semaste def _make_port(self, port_index, ifname): 100254721Semaste return NetdevSim(self, port_index, ifname, self.ns) 101254721Semaste 102254721Semaste def get_ifnames(self): 103254721Semaste ifnames = [] 104254721Semaste listdir = cmd(f"ls /sys/bus/netdevsim/devices/netdevsim{self.addr}/net/", 105254721Semaste ns=self.ns).stdout.split() 106254721Semaste for ifname in listdir: 107254721Semaste ifnames.append(ifname) 108254721Semaste ifnames.sort() 109254721Semaste return ifnames 110254721Semaste 111254721Semaste def wait_for_netdevs(self, port_count): 112254721Semaste timeout = 5 113254721Semaste timeout_start = time.time() 114254721Semaste 115254721Semaste while True: 116254721Semaste try: 117254721Semaste ifnames = self.get_ifnames() 118254721Semaste except FileNotFoundError as e: 119254721Semaste ifnames = [] 120254721Semaste if len(ifnames) == port_count: 121254721Semaste break 122254721Semaste if time.time() < timeout_start + timeout: 123254721Semaste continue 124254721Semaste raise Exception("netdevices did not appear within timeout") 125254721Semaste 126254721Semaste def remove(self): 127254721Semaste if not self.removed: 128254721Semaste self.ctrl_write("del_device", "%u" % (self.addr, )) 129254721Semaste self.removed = True 130254721Semaste 131254721Semaste def remove_nsim(self, nsim): 132254721Semaste self.nsims.remove(nsim) 133254721Semaste self.ctrl_write("devices/netdevsim%u/del_port" % (self.addr, ), 134254721Semaste "%u" % (nsim.port_index, )) 135254721Semaste