From ef8cf8d12cb7600378721a48f6b540608da815a6 Mon Sep 17 00:00:00 2001 From: Vlad Zolotarov Date: Fri, 5 Jul 2024 15:52:23 -0400 Subject: [PATCH 1/3] scripts/perftune.py: refactor __learn_slaves() function Take the code that discovers slaves for a given NIC into a separate method. --- scripts/perftune.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/scripts/perftune.py b/scripts/perftune.py index fe418c5c5c2..56a4a9a2e5b 100755 --- a/scripts/perftune.py +++ b/scripts/perftune.py @@ -743,11 +743,29 @@ def __check_dev_is_bond_iface(self): bond_dict[nic] = any([re.search(nic, line) for line in open('/sys/class/net/bonding_masters', 'r').readlines()]) return bond_dict + def __learn_slaves_one(self, nic): + """ + Learn underlying physical devices a given NIC + + :param nic: An interface to search slaves for + """ + slaves_list = [] + + if self.nic_is_bond_iface(nic): + slaves_list = list(itertools.chain.from_iterable( + [line.split() for line in open("/sys/class/net/{}/bonding/slaves".format(nic), 'r').readlines()])) + + return slaves_list + def __learn_slaves(self): + """ + Resolve underlying physical devices for interfaces we are requested to configure + """ slaves_list_per_nic = {} for nic in self.nics: - if self.nic_is_bond_iface(nic): - slaves_list_per_nic[nic] = list(itertools.chain.from_iterable([line.split() for line in open("/sys/class/net/{}/bonding/slaves".format(nic), 'r').readlines()])) + current_slaves = self.__learn_slaves_one(nic) + if current_slaves: + slaves_list_per_nic[nic] = current_slaves return slaves_list_per_nic From 1604104a5111c76bf3f1add9be15175d7ec64f5d Mon Sep 17 00:00:00 2001 From: Vlad Zolotarov Date: Fri, 5 Jul 2024 17:14:33 -0400 Subject: [PATCH 2/3] scripts/perftune.py: improve discovery of bond device slaves Slaves of a bond device may be bond device themselves. We need to discover corresponding physical devices since this is what we should be configuring. --- scripts/perftune.py | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/scripts/perftune.py b/scripts/perftune.py index 56a4a9a2e5b..2823cf13aaa 100755 --- a/scripts/perftune.py +++ b/scripts/perftune.py @@ -575,7 +575,7 @@ def __init__(self, args): self.nics=args.nics - self.__nic_is_bond_iface = self.__check_dev_is_bond_iface() + self.__nic_is_bond_iface = NetPerfTuner.__get_bond_ifaces() self.__slaves = self.__learn_slaves() # check that self.nics contain a HW device or a bonding interface @@ -606,7 +606,7 @@ def tune(self): fwriteln_and_log('/proc/sys/net/ipv4/tcp_max_syn_backlog', '4096') def nic_is_bond_iface(self, nic): - return self.__nic_is_bond_iface[nic] + return self.__nic_is_bond_iface.get(nic, False) def nic_exists(self, nic): return self.__iface_exists(nic) @@ -732,15 +732,16 @@ def __iface_exists(self, iface): def __dev_is_hw_iface(self, iface): return os.path.exists("/sys/class/net/{}/device".format(iface)) - def __check_dev_is_bond_iface(self): - bond_dict = {} + @staticmethod + def __get_bond_ifaces(): if not os.path.exists('/sys/class/net/bonding_masters'): - for nic in self.nics: - bond_dict[nic] = False - #return False for every nic - return bond_dict - for nic in self.nics: - bond_dict[nic] = any([re.search(nic, line) for line in open('/sys/class/net/bonding_masters', 'r').readlines()]) + return {} + + bond_dict = {} + for line in open('/sys/class/net/bonding_masters', 'r').readlines(): + for nic in line.split(): + bond_dict[nic] = True + return bond_dict def __learn_slaves_one(self, nic): @@ -749,12 +750,19 @@ def __learn_slaves_one(self, nic): :param nic: An interface to search slaves for """ - slaves_list = [] + slaves_list = set() if self.nic_is_bond_iface(nic): - slaves_list = list(itertools.chain.from_iterable( + top_slaves_list = set(itertools.chain.from_iterable( [line.split() for line in open("/sys/class/net/{}/bonding/slaves".format(nic), 'r').readlines()])) + # Slaves can be themselves bonded devices: let's descend (DFS) all the way down to get physical devices + for s in top_slaves_list: + if self.nic_is_bond_iface(s): + slaves_list |= self.__learn_slaves_one(s) + else: + slaves_list.add(s) + return slaves_list def __learn_slaves(self): @@ -765,7 +773,7 @@ def __learn_slaves(self): for nic in self.nics: current_slaves = self.__learn_slaves_one(nic) if current_slaves: - slaves_list_per_nic[nic] = current_slaves + slaves_list_per_nic[nic] = list(current_slaves) return slaves_list_per_nic From e07b9cd2fba40bcacc11e36758a8ded634979e5b Mon Sep 17 00:00:00 2001 From: Vlad Zolotarov Date: Fri, 5 Jul 2024 20:19:15 -0400 Subject: [PATCH 3/3] scripts/perftune.py: add support for tweaking VLAN interfaces Handle VLAN interfaces similar to bond ones: discover the underlying physical devices and tweak them. Fixes #765 --- scripts/perftune.py | 61 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/scripts/perftune.py b/scripts/perftune.py index 2823cf13aaa..22c019d99e0 100755 --- a/scripts/perftune.py +++ b/scripts/perftune.py @@ -576,9 +576,10 @@ def __init__(self, args): self.nics=args.nics self.__nic_is_bond_iface = NetPerfTuner.__get_bond_ifaces() + self.__nic_is_vlan_iface = NetPerfTuner.__get_vlan_ifaces() self.__slaves = self.__learn_slaves() - # check that self.nics contain a HW device or a bonding interface + # check that self.nics contain a HW device or a supported composite interface self.__check_nics() # Fetch IRQs related info @@ -595,8 +596,8 @@ def tune(self): perftune_print("Setting a physical interface {}...".format(nic)) self.__setup_one_hw_iface(nic) else: - perftune_print("Setting {} bonding interface...".format(nic)) - self.__setup_bonding_iface(nic) + perftune_print(f"Setting a {nic} {'bond' if self.nic_is_bond_iface(nic) else 'VLAN'} interface...") + self.__setup_composite_iface(nic) # Increase the socket listen() backlog fwriteln_and_log('/proc/sys/net/core/somaxconn', '4096') @@ -608,6 +609,12 @@ def tune(self): def nic_is_bond_iface(self, nic): return self.__nic_is_bond_iface.get(nic, False) + def nic_is_vlan_iface(self, nic): + return self.__nic_is_vlan_iface.get(nic, False) + + def nic_is_composite_iface(self, nic): + return self.nic_is_bond_iface(nic) or self.nic_is_vlan_iface(nic) + def nic_exists(self, nic): return self.__iface_exists(nic) @@ -617,8 +624,8 @@ def nic_is_hw_iface(self, nic): def slaves(self, nic): """ Returns an iterator for all slaves of the nic. - If agrs.nic is not a bonding interface an attempt to use the returned iterator - will immediately raise a StopIteration exception - use __dev_is_bond_iface() check to avoid this. + If agrs.nic is not a composite interface an attempt to use the returned iterator + will immediately raise a StopIteration exception - use nic_is_composite_iface() check to avoid this. """ return iter(self.__slaves[nic]) @@ -646,7 +653,7 @@ def __check_nics(self): for nic in self.nics: if not self.nic_exists(nic): raise Exception("Device {} does not exist".format(nic)) - if not self.nic_is_hw_iface(nic) and not self.nic_is_bond_iface(nic): + if not self.nic_is_hw_iface(nic) and not self.nic_is_composite_iface(nic): raise Exception("Not supported virtual device {}".format(nic)) def __get_irqs_one(self, iface): @@ -744,6 +751,12 @@ def __get_bond_ifaces(): return bond_dict + @staticmethod + def __get_vlan_ifaces(): + # Each VLAN interface is going to have a corresponding entry in /proc/net/vlan/ directory + return {pathlib.PurePath(pathlib.Path(f)).name: True + for f in filter(lambda vlan_name: vlan_name != "/proc/net/vlan/config", glob.glob("/proc/net/vlan/*"))} + def __learn_slaves_one(self, nic): """ Learn underlying physical devices a given NIC @@ -751,17 +764,31 @@ def __learn_slaves_one(self, nic): :param nic: An interface to search slaves for """ slaves_list = set() + top_slaves_list = set() if self.nic_is_bond_iface(nic): top_slaves_list = set(itertools.chain.from_iterable( [line.split() for line in open("/sys/class/net/{}/bonding/slaves".format(nic), 'r').readlines()])) - - # Slaves can be themselves bonded devices: let's descend (DFS) all the way down to get physical devices - for s in top_slaves_list: - if self.nic_is_bond_iface(s): - slaves_list |= self.__learn_slaves_one(s) - else: - slaves_list.add(s) + elif self.nic_is_vlan_iface(nic): + # VLAN interfaces have a symbolic link 'lower_' under + # /sys/class/net/. + # + # For example: + # + # lrwxrwxrwx 1 root root 0 Jul 5 18:38 lower_eno1 -> ../../../pci0000:00/0000:00:1f.6/net/eno1/ + # + top_slaves_list = set([pathlib.PurePath(pathlib.Path(f).resolve()).name + for f in glob.glob(f"/sys/class/net/{nic}/lower_*")]) + + # Slaves can be themselves bond or VLAN devices: let's descend (DFS) all the way down to get physical devices. + # Bond slaves can't be VLAN interfaces but VLAN interface parent device can be a bond interface. + # Bond slaves can also be bonds. + # For simplicity let's not discriminate. + for s in top_slaves_list: + if self.nic_is_composite_iface(s): + slaves_list |= self.__learn_slaves_one(s) + else: + slaves_list.add(s) return slaves_list @@ -972,7 +999,7 @@ def __learn_irqs(self): """ nic_irq_dict={} for nic in self.nics: - if self.nic_is_bond_iface(nic): + if self.nic_is_composite_iface(nic): for slave in filter(self.__dev_is_hw_iface, self.slaves(nic)): nic_irq_dict[slave] = self.__learn_irqs_one(slave) else: @@ -1064,7 +1091,11 @@ def __setup_one_hw_iface(self, iface): self.__setup_rps(iface, self.cpu_mask) self.__setup_xps(iface) - def __setup_bonding_iface(self, nic): + def __setup_composite_iface(self, nic): + """ + Set up the interface which is a bond or a VLAN interface + :param nic: name of a composite interface to set up + """ for slave in self.slaves(nic): if self.__dev_is_hw_iface(slave): perftune_print("Setting up {}...".format(slave))