Skip to content

Commit

Permalink
Merge 'perftune.py: add support for VLAN interfaces' from Vladislav Z…
Browse files Browse the repository at this point in the history
…olotarov

Add a long waited support for VLAN interfaces.

Tested manually with:
* bond interface on top of a physical device
* bond interface on top of another bond interface
* VLAN interface on top of a physical device
* VLAN interface on top of a bond interface

Closes scylladb#2333

* https://github.com/scylladb/seastar:
  scripts/perftune.py: add support for tweaking VLAN interfaces
  scripts/perftune.py: improve discovery of bond device slaves
  scripts/perftune.py: refactor __learn_slaves() function
  • Loading branch information
xemul committed Jul 6, 2024
2 parents 7fe7d95 + e07b9cd commit 76d696c
Showing 1 changed file with 77 additions and 20 deletions.
97 changes: 77 additions & 20 deletions scripts/perftune.py
Original file line number Diff line number Diff line change
Expand Up @@ -575,10 +575,11 @@ 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.__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
Expand All @@ -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')
Expand All @@ -606,7 +607,13 @@ 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_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)
Expand All @@ -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])

Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -732,22 +739,68 @@ 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

@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
: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()]))
elif self.nic_is_vlan_iface(nic):
# VLAN interfaces have a symbolic link 'lower_<parent_interface_name>' under
# /sys/class/net/<VLAN interface name>.
#
# 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

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] = list(current_slaves)

return slaves_list_per_nic

Expand Down Expand Up @@ -946,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:
Expand Down Expand Up @@ -1038,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))
Expand Down

0 comments on commit 76d696c

Please sign in to comment.