From 31169fa8a763e36f6276632139da46b1aca3a7af Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 28 Aug 2021 17:42:33 +0200 Subject: vyos.ifconfig: T3619: only set offloading options if supported by NIC In the past we always told ethtool to change the offloading settings, even if this was not supported by the underlaying driver. This commit will only change the offloading options if they differ from the current state of the NIC and only if it's supported by the NIC. If the NIC does not support setting the offloading options, a message will be displayed for the user: vyos@vyos# set interfaces ethernet eth2 offload gro vyos@vyos# commit [ interfaces ethernet eth2 ] Adapter does not support changing large-receive-offload settings! --- python/vyos/ifconfig/ethernet.py | 81 +++++++++++++++++++++++++++++++++------- 1 file changed, 67 insertions(+), 14 deletions(-) (limited to 'python/vyos/ifconfig/ethernet.py') diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py index 07b31a12a..fbff789eb 100644 --- a/python/vyos/ifconfig/ethernet.py +++ b/python/vyos/ifconfig/ethernet.py @@ -16,6 +16,7 @@ import os import re +from vyos.ethtool import Ethtool from vyos.ifconfig.interface import Interface from vyos.util import run from vyos.util import dict_search @@ -41,7 +42,7 @@ class EthernetIf(Interface): @staticmethod def feature(ifname, option, value): - run(f'ethtool -K {ifname} {option} {value}','ifconfig') + run(f'ethtool -K {ifname} {option} {value}') return False _command_set = {**Interface._command_set, **{ @@ -84,6 +85,10 @@ class EthernetIf(Interface): }, }} + def __init__(self, ifname, **kargs): + super().__init__(ifname, **kargs) + self.ethtool = Ethtool(ifname) + def get_driver_name(self): """ Return the driver name used by NIC. Some NICs don't support all @@ -228,8 +233,16 @@ class EthernetIf(Interface): >>> i.set_gro(True) """ if not isinstance(state, bool): - raise ValueError("Value out of range") - return self.set_interface('gro', 'on' if state else 'off') + raise ValueError('Value out of range') + + enabled, fixed = self.ethtool.get_generic_receive_offload() + if not fixed: + enabled = 'on' if enabled else 'off' + if enabled != state: + return self.set_interface('gro', 'on' if state else 'off') + + print('Adapter does not support changing generic-receive-offload settings!') + return False def set_gso(self, state): """ @@ -240,8 +253,16 @@ class EthernetIf(Interface): >>> i.set_gso(True) """ if not isinstance(state, bool): - raise ValueError("Value out of range") - return self.set_interface('gso', 'on' if state else 'off') + raise ValueError('Value out of range') + + enabled, fixed = self.ethtool.get_generic_segmentation_offload() + if not fixed: + enabled = 'on' if enabled else 'off' + if enabled != state: + return self.set_interface('gro', 'on' if state else 'off') + + print('Adapter does not support changing generic-segmentation-offload settings!') + return False def set_lro(self, state): """ @@ -252,12 +273,20 @@ class EthernetIf(Interface): >>> i.set_lro(True) """ if not isinstance(state, bool): - raise ValueError("Value out of range") - return self.set_interface('lro', 'on' if state else 'off') + raise ValueError('Value out of range') + + enabled, fixed = self.ethtool.get_large_receive_offload() + if not fixed: + enabled = 'on' if enabled else 'off' + if enabled != state: + return self.set_interface('gro', 'on' if state else 'off') + + print('Adapter does not support changing large-receive-offload settings!') + return False def set_rps(self, state): if not isinstance(state, bool): - raise ValueError("Value out of range") + raise ValueError('Value out of range') rps_cpus = '0' if state: @@ -282,8 +311,16 @@ class EthernetIf(Interface): >>> i.set_sg(True) """ if not isinstance(state, bool): - raise ValueError("Value out of range") - return self.set_interface('sg', 'on' if state else 'off') + raise ValueError('Value out of range') + + enabled, fixed = self.ethtool.get_scatter_gather() + if not fixed: + enabled = 'on' if enabled else 'off' + if enabled != state: + return self.set_interface('gro', 'on' if state else 'off') + + print('Adapter does not support changing scatter-gather settings!') + return False def set_tso(self, state): """ @@ -295,8 +332,16 @@ class EthernetIf(Interface): >>> i.set_tso(False) """ if not isinstance(state, bool): - raise ValueError("Value out of range") - return self.set_interface('tso', 'on' if state else 'off') + raise ValueError('Value out of range') + + enabled, fixed = self.ethtool.get_tcp_segmentation_offload() + if not fixed: + enabled = 'on' if enabled else 'off' + if enabled != state: + return self.set_interface('gro', 'on' if state else 'off') + + print('Adapter does not support changing tcp-segmentation-offload settings!') + return False def set_ufo(self, state): """ @@ -308,8 +353,16 @@ class EthernetIf(Interface): >>> i.set_udp_offload(True) """ if not isinstance(state, bool): - raise ValueError("Value out of range") - return self.set_interface('ufo', 'on' if state else 'off') + raise ValueError('Value out of range') + + enabled, fixed = self.ethtool.get_udp_fragmentation_offload() + if not fixed: + enabled = 'on' if enabled else 'off' + if enabled != state: + return self.set_interface('gro', 'on' if state else 'off') + + print('Adapter does not support changing udp-fragmentation-offload settings!') + return False def set_ring_buffer(self, b_type, b_size): """ -- cgit v1.2.3 From ce784a9fcb7199f87949f17777b7b736227c85b3 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 30 Aug 2021 21:25:38 +0200 Subject: vyos.ifconfig: T3619: only inform user about real offload change for invalid option Commit 31169fa8 ("vyos.ifconfig: T3619: only set offloading options if supported by NIC") added a warning for the user if an offload option was about to change that was not possible at all (harware limit). Unfortunately the warning was even displayed if nothing was done at all. This got corrected. --- python/vyos/ifconfig/ethernet.py | 57 ++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 32 deletions(-) (limited to 'python/vyos/ifconfig/ethernet.py') diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py index fbff789eb..696fec03b 100644 --- a/python/vyos/ifconfig/ethernet.py +++ b/python/vyos/ifconfig/ethernet.py @@ -236,12 +236,11 @@ class EthernetIf(Interface): raise ValueError('Value out of range') enabled, fixed = self.ethtool.get_generic_receive_offload() - if not fixed: - enabled = 'on' if enabled else 'off' - if enabled != state: + if enabled != state: + if not fixed: return self.set_interface('gro', 'on' if state else 'off') - - print('Adapter does not support changing generic-receive-offload settings!') + else: + print('Adapter does not support changing generic-receive-offload settings!') return False def set_gso(self, state): @@ -256,12 +255,11 @@ class EthernetIf(Interface): raise ValueError('Value out of range') enabled, fixed = self.ethtool.get_generic_segmentation_offload() - if not fixed: - enabled = 'on' if enabled else 'off' - if enabled != state: - return self.set_interface('gro', 'on' if state else 'off') - - print('Adapter does not support changing generic-segmentation-offload settings!') + if enabled != state: + if not fixed: + return self.set_interface('gso', 'on' if state else 'off') + else: + print('Adapter does not support changing generic-segmentation-offload settings!') return False def set_lro(self, state): @@ -276,12 +274,11 @@ class EthernetIf(Interface): raise ValueError('Value out of range') enabled, fixed = self.ethtool.get_large_receive_offload() - if not fixed: - enabled = 'on' if enabled else 'off' - if enabled != state: + if enabled != state: + if not fixed: return self.set_interface('gro', 'on' if state else 'off') - - print('Adapter does not support changing large-receive-offload settings!') + else: + print('Adapter does not support changing large-receive-offload settings!') return False def set_rps(self, state): @@ -314,12 +311,11 @@ class EthernetIf(Interface): raise ValueError('Value out of range') enabled, fixed = self.ethtool.get_scatter_gather() - if not fixed: - enabled = 'on' if enabled else 'off' - if enabled != state: + if enabled != state: + if not fixed: return self.set_interface('gro', 'on' if state else 'off') - - print('Adapter does not support changing scatter-gather settings!') + else: + print('Adapter does not support changing scatter-gather settings!') return False def set_tso(self, state): @@ -335,12 +331,11 @@ class EthernetIf(Interface): raise ValueError('Value out of range') enabled, fixed = self.ethtool.get_tcp_segmentation_offload() - if not fixed: - enabled = 'on' if enabled else 'off' - if enabled != state: + if enabled != state: + if not fixed: return self.set_interface('gro', 'on' if state else 'off') - - print('Adapter does not support changing tcp-segmentation-offload settings!') + else: + print('Adapter does not support changing tcp-segmentation-offload settings!') return False def set_ufo(self, state): @@ -356,12 +351,11 @@ class EthernetIf(Interface): raise ValueError('Value out of range') enabled, fixed = self.ethtool.get_udp_fragmentation_offload() - if not fixed: - enabled = 'on' if enabled else 'off' - if enabled != state: + if enabled != state: + if not fixed: return self.set_interface('gro', 'on' if state else 'off') - - print('Adapter does not support changing udp-fragmentation-offload settings!') + else: + print('Adapter does not support changing udp-fragmentation-offload settings!') return False def set_ring_buffer(self, b_type, b_size): @@ -381,7 +375,6 @@ class EthernetIf(Interface): print(f'could not set "{b_type}" ring-buffer for {ifname}') return output - def update(self, config): """ General helper function which works on a dictionary retrived by get_config_dict(). It's main intention is to consolidate the scattered -- cgit v1.2.3 From 705022319916222d78082114245c7639c073bd32 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 30 Aug 2021 21:36:52 +0200 Subject: ethernet: T3787: remove deprecated UDP fragmentation offloading option Deprecated in the Linux Kernel by commit 08a00fea6de277df12ccfadc21 ("net: Remove references to NETIF_F_UFO from ethtool."). (cherry picked from commit f5e46ee6cc2b6c1c1869e26beca4ccd5bf52b62f) --- interface-definitions/interfaces-ethernet.xml.in | 6 ----- python/vyos/ethtool.py | 3 --- python/vyos/ifconfig/ethernet.py | 28 ------------------------ src/migration-scripts/interfaces/20-to-21 | 12 ++++------ 4 files changed, 4 insertions(+), 45 deletions(-) (limited to 'python/vyos/ifconfig/ethernet.py') diff --git a/interface-definitions/interfaces-ethernet.xml.in b/interface-definitions/interfaces-ethernet.xml.in index ca076e3fa..ceeda12a0 100644 --- a/interface-definitions/interfaces-ethernet.xml.in +++ b/interface-definitions/interfaces-ethernet.xml.in @@ -104,12 +104,6 @@ - - - Enable UDP Fragmentation Offloading - - - diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py index 98985e972..5a5d84bed 100644 --- a/python/vyos/ethtool.py +++ b/python/vyos/ethtool.py @@ -122,9 +122,6 @@ class Ethtool: def get_tcp_segmentation_offload(self): return self._get_generic('tcp-segmentation-offload') - def get_udp_fragmentation_offload(self): - return self._get_generic('udp-fragmentation-offload') - def get_rx_buffer(self): # Configuration of RX ring-buffers is not supported on every device, # thus when it's impossible return None diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py index 696fec03b..7d29da8fc 100644 --- a/python/vyos/ifconfig/ethernet.py +++ b/python/vyos/ifconfig/ethernet.py @@ -71,11 +71,6 @@ class EthernetIf(Interface): 'possible': lambda i, v: EthernetIf.feature(i, 'tso', v), # 'shellcmd': 'ethtool -K {ifname} tso {value}', }, - 'ufo': { - 'validate': lambda v: assert_list(v, ['on', 'off']), - 'possible': lambda i, v: EthernetIf.feature(i, 'ufo', v), - # 'shellcmd': 'ethtool -K {ifname} ufo {value}', - }, }} _sysfs_set = {**Interface._sysfs_set, **{ @@ -338,26 +333,6 @@ class EthernetIf(Interface): print('Adapter does not support changing tcp-segmentation-offload settings!') return False - def set_ufo(self, state): - """ - Enable UDP fragmentation offloading. State can be either True or False. - - Example: - >>> from vyos.ifconfig import EthernetIf - >>> i = EthernetIf('eth0') - >>> i.set_udp_offload(True) - """ - if not isinstance(state, bool): - raise ValueError('Value out of range') - - enabled, fixed = self.ethtool.get_udp_fragmentation_offload() - if enabled != state: - if not fixed: - return self.set_interface('gro', 'on' if state else 'off') - else: - print('Adapter does not support changing udp-fragmentation-offload settings!') - return False - def set_ring_buffer(self, b_type, b_size): """ Example: @@ -403,9 +378,6 @@ class EthernetIf(Interface): # TSO (TCP segmentation offloading) self.set_tso(dict_search('offload.tso', config) != None) - # UDP fragmentation offloading - self.set_ufo(dict_search('offload.ufo', config) != None) - # Set physical interface speed and duplex if {'speed', 'duplex'} <= set(config): speed = config.get('speed') diff --git a/src/migration-scripts/interfaces/20-to-21 b/src/migration-scripts/interfaces/20-to-21 index 9210330d6..4b0e70d35 100755 --- a/src/migration-scripts/interfaces/20-to-21 +++ b/src/migration-scripts/interfaces/20-to-21 @@ -15,7 +15,8 @@ # along with this program. If not, see . # T3619: mirror Linux Kernel defaults for ethernet offloading options into VyOS -# CLI. See https://phabricator.vyos.net/T3619#102254 for all the details. +# CLI. See https://phabricator.vyos.net/T3619#102254 for all the details. +# T3787: Remove deprecated UDP fragmentation offloading option from sys import argv @@ -84,14 +85,9 @@ for ifname in config.list_nodes(base): elif enabled and not fixed: config.set(base + [ifname, 'offload', 'tso']) - # If UFO is enabled by the Kernel - we reflect this on the CLI. If UFO is - # enabled via CLI but not supported by the NIC - we remove it from the CLI - configured = config.exists(base + [ifname, 'offload', 'ufo']) - enabled, fixed = eth.get_udp_fragmentation_offload() - if configured and fixed: + # Remove deprecated UDP fragmentation offloading option + if config.exists(base + [ifname, 'offload', 'ufo']): config.delete(base + [ifname, 'offload', 'ufo']) - elif enabled and not fixed: - config.set(base + [ifname, 'offload', 'ufo']) try: with open(file_name, 'w') as f: -- cgit v1.2.3 From a086dc2c429aea9614ac7a9c735c6475c2d6da59 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 31 Aug 2021 12:22:36 +0200 Subject: vyos.ethtool: T3163: use long option names when calling the ethtool binray This makes understanding the code easier what is "really" called without opening the man page. --- op-mode-definitions/show-interfaces-ethernet.xml.in | 10 +++++----- python/vyos/ethtool.py | 2 +- python/vyos/ifconfig/ethernet.py | 7 +------ 3 files changed, 7 insertions(+), 12 deletions(-) (limited to 'python/vyos/ifconfig/ethernet.py') diff --git a/op-mode-definitions/show-interfaces-ethernet.xml.in b/op-mode-definitions/show-interfaces-ethernet.xml.in index fc79f44bf..6d50d6e90 100644 --- a/op-mode-definitions/show-interfaces-ethernet.xml.in +++ b/op-mode-definitions/show-interfaces-ethernet.xml.in @@ -23,19 +23,19 @@ Visually identify specified ethernet interface - echo "Blinking interface $4 for 30 seconds."; /sbin/ethtool --identify "$4" 30 + echo "Blinking interface $4 for 30 seconds."; ethtool --identify "$4" 30 Show physical device information for specified ethernet interface - /sbin/ethtool "$4"; /sbin/ethtool -i "$4" + ethtool "$4"; ethtool --driver "$4" Show physical device offloading capabilities - /sbin/ethtool -k "$4" | sed -e 1d -e '/fixed/d' -e 's/^\t*//g' -e 's/://' | column -t -s' ' + ethtool --show-features "$4" | sed -e 1d -e '/fixed/d' -e 's/^\t*//g' -e 's/://' | column -t -s' ' @@ -43,13 +43,13 @@ Show physical device statistics for specified ethernet interface - /sbin/ethtool -S "$4" + ethtool --statistics "$4" Show transceiver information from modules (e.g SFP+, QSFP) - /sbin/ethtool -m "$4" + ethtool --module-info "$4" diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py index 5a5d84bed..7dcb68346 100644 --- a/python/vyos/ethtool.py +++ b/python/vyos/ethtool.py @@ -65,7 +65,7 @@ class Ethtool: self._speed_duplex[speed].update({ duplex : ''}) # Now populate features dictionaty - out, err = popen(f'ethtool -k {ifname}') + out, err = popen(f'ethtool --show-features {ifname}') # skip the first line, it only says: "Features for eth0": for line in out.splitlines()[1:]: if ":" in line: diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py index 7d29da8fc..76ed3fd92 100644 --- a/python/vyos/ifconfig/ethernet.py +++ b/python/vyos/ifconfig/ethernet.py @@ -42,34 +42,29 @@ class EthernetIf(Interface): @staticmethod def feature(ifname, option, value): - run(f'ethtool -K {ifname} {option} {value}') + run(f'ethtool --features {ifname} {option} {value}') return False _command_set = {**Interface._command_set, **{ 'gro': { 'validate': lambda v: assert_list(v, ['on', 'off']), 'possible': lambda i, v: EthernetIf.feature(i, 'gro', v), - # 'shellcmd': 'ethtool -K {ifname} gro {value}', }, 'gso': { 'validate': lambda v: assert_list(v, ['on', 'off']), 'possible': lambda i, v: EthernetIf.feature(i, 'gso', v), - # 'shellcmd': 'ethtool -K {ifname} gso {value}', }, 'lro': { 'validate': lambda v: assert_list(v, ['on', 'off']), 'possible': lambda i, v: EthernetIf.feature(i, 'lro', v), - # 'shellcmd': 'ethtool -K {ifname} lro {value}', }, 'sg': { 'validate': lambda v: assert_list(v, ['on', 'off']), 'possible': lambda i, v: EthernetIf.feature(i, 'sg', v), - # 'shellcmd': 'ethtool -K {ifname} sg {value}', }, 'tso': { 'validate': lambda v: assert_list(v, ['on', 'off']), 'possible': lambda i, v: EthernetIf.feature(i, 'tso', v), - # 'shellcmd': 'ethtool -K {ifname} tso {value}', }, }} -- cgit v1.2.3 From 6f5fb5c503b5df96d0686002355da3633b1fc597 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 31 Aug 2021 21:28:08 +0200 Subject: vyos.ethtool: T3163: purify code to read current speed and duplex settings It makes no sense to have a parser for the ethtool value sin ethtool.py and ethernet.py - one instance ios more then enough! --- python/vyos/ethtool.py | 13 ++++++++++++- python/vyos/ifconfig/ethernet.py | 22 +++++----------------- 2 files changed, 17 insertions(+), 18 deletions(-) (limited to 'python/vyos/ifconfig/ethernet.py') diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py index 968e2e2a3..e803e28a1 100644 --- a/python/vyos/ethtool.py +++ b/python/vyos/ethtool.py @@ -44,6 +44,7 @@ class Ethtool: _speed_duplex = { } _ring_buffers = { } _driver_name = None + _auto_negotiation = None def __init__(self, ifname): # Get driver used for interface @@ -65,7 +66,6 @@ class Ethtool: reading = True if 'Supported pause frame use:' in line: reading = False - break if reading: for block in line.split(): if pattern.search(block): @@ -75,6 +75,15 @@ class Ethtool: self._speed_duplex.update({ speed : {}}) if duplex not in self._speed_duplex[speed]: self._speed_duplex[speed].update({ duplex : ''}) + if 'Auto-negotiation:' in line: + # Split the following string: Auto-negotiation: off + # we are only interested in off or on + tmp = line.split()[-1] + self._auto_negotiation = bool(tmp == 'on') + + if self._auto_negotiation == None: + raise ValueError(f'Could not determine auto-negotiation settings '\ + f'for interface {ifname}!') # Now populate features dictionaty out, err = popen(f'ethtool --show-features {ifname}') @@ -162,3 +171,5 @@ class Ethtool: return True return False + def get_auto_negotiation(self): + return self._auto_negotiation diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py index 76ed3fd92..d4fa3f655 100644 --- a/python/vyos/ifconfig/ethernet.py +++ b/python/vyos/ifconfig/ethernet.py @@ -20,6 +20,7 @@ from vyos.ethtool import Ethtool from vyos.ifconfig.interface import Interface from vyos.util import run from vyos.util import dict_search +from vyos.util import read_file from vyos.validate import assert_list @Interface.register @@ -181,32 +182,19 @@ class EthernetIf(Interface): # Get current speed and duplex settings: ifname = self.config['ifname'] - cmd = f'ethtool {ifname}' - tmp = self._cmd(cmd) - - if re.search("\tAuto-negotiation: on", tmp): + if self.ethtool.get_auto_negotiation(): if speed == 'auto' and duplex == 'auto': # bail out early as nothing is to change return else: # read in current speed and duplex settings - cur_speed = 0 - cur_duplex = '' - for line in tmp.splitlines(): - if line.lstrip().startswith("Speed:"): - non_decimal = re.compile(r'[^\d.]+') - cur_speed = non_decimal.sub('', line) - continue - - if line.lstrip().startswith("Duplex:"): - cur_duplex = line.split()[-1].lower() - break - + cur_speed = read_file(f'/sys/class/net/{ifname}/speed') + cur_duplex = read_file(f'/sys/class/net/{ifname}/duplex') if (cur_speed == speed) and (cur_duplex == duplex): # bail out early as nothing is to change return - cmd = f'ethtool -s {ifname}' + cmd = f'ethtool --change {ifname}' if speed == 'auto' or duplex == 'auto': cmd += ' autoneg on' else: -- cgit v1.2.3 From 29082959e0efc02462fba8560d6726096e8743e9 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 31 Aug 2021 21:50:05 +0200 Subject: ethernet: T3163: only change ring-buffer settings if required Only update the RX/TX ring-buffer settings if they are different from the ones currently programmed to the hardware. There is no need to write the same value to the hardware again - this could cause traffic disruption on some NICs. --- python/vyos/ethtool.py | 29 ++++++++++++++++++++++------- python/vyos/ifconfig/ethernet.py | 15 ++++++++++----- src/conf_mode/interfaces-ethernet.py | 4 ++-- 3 files changed, 34 insertions(+), 14 deletions(-) (limited to 'python/vyos/ifconfig/ethernet.py') diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py index e803e28a1..f5796358d 100644 --- a/python/vyos/ethtool.py +++ b/python/vyos/ethtool.py @@ -43,6 +43,7 @@ class Ethtool: # } _speed_duplex = { } _ring_buffers = { } + _ring_buffers_max = { } _driver_name = None _auto_negotiation = None @@ -99,10 +100,20 @@ class Ethtool: 'fixed' : fixed } - out, err = popen(f'ethtool -g {ifname}') + out, err = popen(f'ethtool --show-ring {ifname}') # We are only interested in line 2-5 which contains the device maximum # ringbuffers for line in out.splitlines()[2:6]: + if ':' in line: + key, value = [s.strip() for s in line.strip().split(":", 1)] + key = key.lower().replace(' ', '_') + # T3645: ethtool version used on Debian Bullseye changed the + # output format from 0 -> n/a. As we are only interested in the + # tx/rx keys we do not care about RX Mini/Jumbo. + if value.isdigit(): + self._ring_buffers_max[key] = int(value) + # Now we wan't to get the current RX/TX ringbuffer values - used for + for line in out.splitlines()[7:11]: if ':' in line: key, value = [s.strip() for s in line.strip().split(":", 1)] key = key.lower().replace(' ', '_') @@ -143,15 +154,19 @@ class Ethtool: def get_tcp_segmentation_offload(self): return self._get_generic('tcp-segmentation-offload') - def get_rx_buffer(self): - # Configuration of RX ring-buffers is not supported on every device, + def get_ring_buffer_max(self, rx_tx): + # Configuration of RX/TX ring-buffers is not supported on every device, # thus when it's impossible return None - return self._ring_buffers.get('rx', None) + if rx_tx not in ['rx', 'tx']: + ValueError('Ring-buffer type must be either "rx" or "tx"') + return self._ring_buffers_max.get(rx_tx, None) - def get_tx_buffer(self): - # Configuration of TX ring-buffers is not supported on every device, + def get_ring_buffer(self, rx_tx): + # Configuration of RX/TX ring-buffers is not supported on every device, # thus when it's impossible return None - return self._ring_buffers.get('tx', None) + if rx_tx not in ['rx', 'tx']: + ValueError('Ring-buffer type must be either "rx" or "tx"') + return self._ring_buffers.get(rx_tx, None) def check_speed_duplex(self, speed, duplex): """ Check if the passed speed and duplex combination is supported by diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py index d4fa3f655..e5da3ac25 100644 --- a/python/vyos/ifconfig/ethernet.py +++ b/python/vyos/ifconfig/ethernet.py @@ -316,21 +316,26 @@ class EthernetIf(Interface): print('Adapter does not support changing tcp-segmentation-offload settings!') return False - def set_ring_buffer(self, b_type, b_size): + def set_ring_buffer(self, rx_tx, size): """ Example: >>> from vyos.ifconfig import EthernetIf >>> i = EthernetIf('eth0') >>> i.set_ring_buffer('rx', '4096') """ + current_size = self.ethtool.get_ring_buffer(rx_tx) + if current_size == size: + # bail out early if nothing is about to change + return None + ifname = self.config['ifname'] - cmd = f'ethtool -G {ifname} {b_type} {b_size}' + cmd = f'ethtool --set-ring {ifname} {rx_tx} {size}' output, code = self._popen(cmd) # ethtool error codes: # 80 - value already setted # 81 - does not possible to set value if code and code != 80: - print(f'could not set "{b_type}" ring-buffer for {ifname}') + print(f'could not set "{rx_tx}" ring-buffer for {ifname}') return output def update(self, config): @@ -369,8 +374,8 @@ class EthernetIf(Interface): # Set interface ring buffer if 'ring_buffer' in config: - for b_type in config['ring_buffer']: - self.set_ring_buffer(b_type, config['ring_buffer'][b_type]) + for rx_tx, size in config['ring_buffer'].items(): + self.set_ring_buffer(rx_tx, size) # call base class first super().update(config) diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py index 889f4856f..f604f787c 100755 --- a/src/conf_mode/interfaces-ethernet.py +++ b/src/conf_mode/interfaces-ethernet.py @@ -89,11 +89,11 @@ def verify(ethernet): f'settings to: {speed}/{duplex}!') if 'ring_buffer' in ethernet: - max_rx = ethtool.get_rx_buffer() + max_rx = ethtool.get_ring_buffer_max('rx') if not max_rx: raise ConfigError('Driver does not support RX ring-buffer configuration!') - max_tx = ethtool.get_tx_buffer() + max_tx = ethtool.get_ring_buffer_max('tx') if not max_tx: raise ConfigError('Driver does not support TX ring-buffer configuration!') -- cgit v1.2.3 From 0229645c8248decb5664056df8aa5cd5dff41802 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 31 Aug 2021 23:03:01 +0200 Subject: vyos.ethtool: T3163: purify code to read and change flow-control settings It makes no sense to have a parser for the ethtool values in ethtool.py and ethernet.py - one instance ios more then enough! --- python/vyos/ethtool.py | 23 +++++++++++++++++ python/vyos/ifconfig/ethernet.py | 42 ++++++++----------------------- src/conf_mode/interfaces-ethernet.py | 4 +++ src/migration-scripts/interfaces/20-to-21 | 8 ++++++ 4 files changed, 45 insertions(+), 32 deletions(-) (limited to 'python/vyos/ifconfig/ethernet.py') diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py index f5796358d..87b9d7dd0 100644 --- a/python/vyos/ethtool.py +++ b/python/vyos/ethtool.py @@ -46,6 +46,8 @@ class Ethtool: _ring_buffers_max = { } _driver_name = None _auto_negotiation = None + _flow_control = None + _flow_control_enabled = None def __init__(self, ifname): # Get driver used for interface @@ -123,6 +125,15 @@ class Ethtool: if value.isdigit(): self._ring_buffers[key] = int(value) + # Get current flow control settings, but this is not supported by + # all NICs (e.g. vmxnet3 does not support is) + out, err = popen(f'ethtool --show-pause {ifname}') + if len(out.splitlines()) > 1: + self._flow_control = True + # read current flow control setting, this returns: + # ['Autonegotiate:', 'on'] + self._flow_control_enabled = out.splitlines()[1].split()[-1] + def _get_generic(self, feature): """ Generic method to read self._features and return a tuple for feature @@ -186,5 +197,17 @@ class Ethtool: return True return False + def check_flow_control(self): + """ Check if the NIC supports flow-control """ + if self._driver_name in ['vmxnet3', 'virtio_net', 'xen_netfront']: + return False + return self._flow_control + + def get_flow_control(self): + if self._flow_control_enabled == None: + raise ValueError('Interface does not support changing '\ + 'flow-control settings!') + return self._flow_control_enabled + def get_auto_negotiation(self): return self._auto_negotiation diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py index e5da3ac25..7bd269491 100644 --- a/python/vyos/ifconfig/ethernet.py +++ b/python/vyos/ifconfig/ethernet.py @@ -121,38 +121,16 @@ class EthernetIf(Interface): 'flow control settings!') return - # Get current flow control settings: - cmd = f'ethtool --show-pause {ifname}' - output, code = self._popen(cmd) - if code == 76: - # the interface does not support it - return '' - if code: - # never fail here as it prevent vyos to boot - print(f'unexpected return code {code} from {cmd}') - return '' - - # The above command returns - with tabs: - # - # Pause parameters for eth0: - # Autonegotiate: on - # RX: off - # TX: off - if re.search("Autonegotiate:\ton", output): - if enable == "on": - # flowcontrol is already enabled - no need to re-enable it again - # this will prevent the interface from flapping as applying the - # flow-control settings will take the interface down and bring - # it back up every time. - return '' - - # Assemble command executed on system. Unfortunately there is no way - # to change this setting via sysfs - cmd = f'ethtool --pause {ifname} autoneg {enable} tx {enable} rx {enable}' - output, code = self._popen(cmd) - if code: - print(f'could not set flowcontrol for {ifname}') - return output + current = self.ethtool.get_flow_control() + if current != enable: + # Assemble command executed on system. Unfortunately there is no way + # to change this setting via sysfs + cmd = f'ethtool --pause {ifname} autoneg {enable} tx {enable} rx {enable}' + output, code = self._popen(cmd) + if code: + print(f'Could not set flowcontrol for {ifname}') + return output + return None def set_speed_duplex(self, speed, duplex): """ diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py index f604f787c..81ed36bf2 100755 --- a/src/conf_mode/interfaces-ethernet.py +++ b/src/conf_mode/interfaces-ethernet.py @@ -88,6 +88,10 @@ def verify(ethernet): raise ConfigError(f'Adapter does not support changing speed and duplex '\ f'settings to: {speed}/{duplex}!') + if 'disable_flow_control' in ethernet: + if not ethtool.check_flow_control(): + raise ConfigError('Adapter does not support changing flow-control settings!') + if 'ring_buffer' in ethernet: max_rx = ethtool.get_ring_buffer_max('rx') if not max_rx: diff --git a/src/migration-scripts/interfaces/20-to-21 b/src/migration-scripts/interfaces/20-to-21 index bd89dcdb4..0bd858760 100755 --- a/src/migration-scripts/interfaces/20-to-21 +++ b/src/migration-scripts/interfaces/20-to-21 @@ -104,6 +104,14 @@ for ifname in config.list_nodes(base): config.delete(speed_path) config.delete(duplex_path) + # Also while processing the interface configuration, not all adapters support + # changing disabling flow-control - or change this setting. If disabling + # flow-control is not supported by the NIC, we remove the setting from CLI + flow_control_path = base + [ifname, 'disable-flow-control'] + if config.exists(flow_control_path): + if not eth.check_flow_control(): + config.delete(flow_control_path) + try: with open(file_name, 'w') as f: f.write(config.to_string()) -- cgit v1.2.3 From cc313b32ef26141c2c027e8543f78da1231cada2 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 10 Sep 2021 16:45:30 +0200 Subject: ethernet: T3802: check if driver supports changing flow-control settings (cherry picked from commit 1572edd2cef355710d1129907d3e49451a6c31d4) --- python/vyos/ethtool.py | 2 +- python/vyos/ifconfig/ethernet.py | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'python/vyos/ifconfig/ethernet.py') diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py index 609d83b5e..7e46969cf 100644 --- a/python/vyos/ethtool.py +++ b/python/vyos/ethtool.py @@ -46,7 +46,7 @@ class Ethtool: _ring_buffers_max = { } _driver_name = None _auto_negotiation = None - _flow_control = None + _flow_control = False _flow_control_enabled = None def __init__(self, ifname): diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py index 7bd269491..c4dfc7198 100644 --- a/python/vyos/ifconfig/ethernet.py +++ b/python/vyos/ifconfig/ethernet.py @@ -115,11 +115,10 @@ class EthernetIf(Interface): if enable not in ['on', 'off']: raise ValueError("Value out of range") - driver_name = self.get_driver_name() - if driver_name in ['vmxnet3', 'virtio_net', 'xen_netfront']: + if not self.ethtool.check_flow_control(): self._debug_msg(f'{driver_name} driver does not support changing '\ 'flow control settings!') - return + return False current = self.ethtool.get_flow_control() if current != enable: -- cgit v1.2.3 From 2abf6711e880463b9d9276b2fa9ea7b4ebcdc179 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 10 Sep 2021 16:46:55 +0200 Subject: ethernet: T3802: use only one implementation for get_driver_name() Move the two implementations to get the driver name of a NIC from ethernet.py and ethtool.py to only ethtool.py. (cherry picked from commit 07840977834816b69fa3b366817d90f44b5dc7a7) --- python/vyos/ethtool.py | 13 ++++++++----- python/vyos/ifconfig/ethernet.py | 28 +++------------------------- src/conf_mode/interfaces-ethernet.py | 2 +- 3 files changed, 12 insertions(+), 31 deletions(-) (limited to 'python/vyos/ifconfig/ethernet.py') diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py index 7e46969cf..4efc3a234 100644 --- a/python/vyos/ethtool.py +++ b/python/vyos/ethtool.py @@ -134,6 +134,12 @@ class Ethtool: # ['Autonegotiate:', 'on'] self._flow_control_enabled = out.splitlines()[1].split()[-1] + def get_auto_negotiation(self): + return self._auto_negotiation + + def get_driver_name(self): + return self._driver_name + def _get_generic(self, feature): """ Generic method to read self._features and return a tuple for feature @@ -189,7 +195,7 @@ class Ethtool: if duplex not in ['full', 'half']: raise ValueError(f'Value "{duplex}" for duplex is invalid!') - if self._driver_name in ['vmxnet3', 'virtio_net', 'xen_netfront']: + if self.get_driver_name() in ['vmxnet3', 'virtio_net', 'xen_netfront']: return False if speed in self._speed_duplex: @@ -199,7 +205,7 @@ class Ethtool: def check_flow_control(self): """ Check if the NIC supports flow-control """ - if self._driver_name in ['vmxnet3', 'virtio_net', 'xen_netfront']: + if self.get_driver_name() in ['vmxnet3', 'virtio_net', 'xen_netfront']: return False return self._flow_control @@ -208,6 +214,3 @@ class Ethtool: raise ValueError('Interface does not support changing '\ 'flow-control settings!') return self._flow_control_enabled - - def get_auto_negotiation(self): - return self._auto_negotiation diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py index c4dfc7198..78fec1fa2 100644 --- a/python/vyos/ifconfig/ethernet.py +++ b/python/vyos/ifconfig/ethernet.py @@ -80,25 +80,6 @@ class EthernetIf(Interface): super().__init__(ifname, **kargs) self.ethtool = Ethtool(ifname) - def get_driver_name(self): - """ - Return the driver name used by NIC. Some NICs don't support all - features e.g. changing link-speed, duplex - - Example: - >>> from vyos.ifconfig import EthernetIf - >>> i = EthernetIf('eth0') - >>> i.get_driver_name() - 'vmxnet3' - """ - ifname = self.config['ifname'] - sysfs_file = f'/sys/class/net/{ifname}/device/driver/module' - if os.path.exists(sysfs_file): - link = os.readlink(sysfs_file) - return os.path.basename(link) - else: - return None - def set_flow_control(self, enable): """ Changes the pause parameters of the specified Ethernet device. @@ -116,8 +97,7 @@ class EthernetIf(Interface): raise ValueError("Value out of range") if not self.ethtool.check_flow_control(): - self._debug_msg(f'{driver_name} driver does not support changing '\ - 'flow control settings!') + self._debug_msg(f'NIC driver does not support changing flow control settings!') return False current = self.ethtool.get_flow_control() @@ -151,10 +131,8 @@ class EthernetIf(Interface): if duplex not in ['auto', 'full', 'half']: raise ValueError("Value out of range (duplex)") - driver_name = self.get_driver_name() - if driver_name in ['vmxnet3', 'virtio_net', 'xen_netfront']: - self._debug_msg(f'{driver_name} driver does not support changing '\ - 'speed/duplex settings!') + if not self.ethtool.check_speed_duplex(speed, duplex): + self._debug_msg(f'NIC driver does not support changing speed/duplex settings!') return # Get current speed and duplex settings: diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py index 21a04f954..e7250fb49 100755 --- a/src/conf_mode/interfaces-ethernet.py +++ b/src/conf_mode/interfaces-ethernet.py @@ -126,7 +126,7 @@ def verify(ethernet): if not os.path.exists(f'/sys/class/net/{ifname}/queues/rx-0/rps_cpus'): raise ConfigError('Interface does not suport RPS!') - driver = EthernetIf(ifname).get_driver_name() + driver = ethtool.get_driver_name() # T3342 - Xen driver requires special treatment if driver == 'vif': if int(ethernet['mtu']) > 1500 and dict_search('offload.sg', ethernet) == None: -- cgit v1.2.3 From 7a264d9e17bd918c33879ba5cd70b00bb786726a Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 10 Sep 2021 16:47:42 +0200 Subject: ethernet: T3802: not all NICs support reading speed/duplex settings in all states Turns out an AX88179 USB 3.0 NIC does not support reading back the speed and duplex settings in every operating state. While the NIC is beeing initialized, reading the speed setting will return: $ cat /sys/class/net/eth6/speed cat: /sys/class/net/eth6/speed: Invalid argument Thus if this happens, we simply tell the system that the current NIC speed matches the requested speed and nothing is changed at this point in time. (cherry picked from commit e2b7e1766cc22c5cd718a5001be6336bdca92eec) --- python/vyos/ifconfig/ethernet.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'python/vyos/ifconfig/ethernet.py') diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py index 78fec1fa2..2e59a7afc 100644 --- a/python/vyos/ifconfig/ethernet.py +++ b/python/vyos/ifconfig/ethernet.py @@ -142,9 +142,12 @@ class EthernetIf(Interface): # bail out early as nothing is to change return else: - # read in current speed and duplex settings - cur_speed = read_file(f'/sys/class/net/{ifname}/speed') - cur_duplex = read_file(f'/sys/class/net/{ifname}/duplex') + # XXX: read in current speed and duplex settings + # There are some "nice" NICs like AX88179 which do not support + # reading the speed thus we simply fallback to the supplied speed + # to not cause any change here and raise an exception. + cur_speed = read_file(f'/sys/class/net/{ifname}/speed', speed) + cur_duplex = read_file(f'/sys/class/net/{ifname}/duplex', duplex) if (cur_speed == speed) and (cur_duplex == duplex): # bail out early as nothing is to change return -- cgit v1.2.3