diff options
author | Christian Poessinger <christian@poessinger.com> | 2021-08-30 21:29:22 +0200 |
---|---|---|
committer | Christian Poessinger <christian@poessinger.com> | 2021-08-30 21:38:44 +0200 |
commit | a515212f4efb08846df04405f31a828edcd63552 (patch) | |
tree | 2a8e70b70cf5e8e0d3753e04bc9d3a4f6be8ac03 | |
parent | 717894ece10532007ca44118bf63abe200239685 (diff) | |
download | vyos-1x-a515212f4efb08846df04405f31a828edcd63552.tar.gz vyos-1x-a515212f4efb08846df04405f31a828edcd63552.zip |
ethernet: T3619: fix VyOS 1.2 -> 1.3 performance degradation
An analysis of the code base from VyOS 1.2 -> 1.3 -> 1.4 revealed the following
"root-cause"
VyOS 1.2 uses the "old" node.def file format for:
* Generic Segmentation Offloading
* Generic Receive Offloading
So if any of the above settings is available on the configuration CLI, the
node.def file will be executed - this is how it works.
By default, this CLI option is not enabled in VyOS 1.2 - but the Linux Kernel
enables offloading "under the hood" by default for GRO, GSO... which will boost
the performance for users magically.
With the rewrite in VyOS 1.3 of all the interface related code T1579, and
especially T1637 this was moved to a new approach. There is now only one handler
script which is called whenever a user changes something under the interfaces
ethernet tree. The Full CLI configuration is assembled by get_interface_dict() -
a wrapper for get_config_dict() which abstracts and works for all of our
interface types - single source design.
The problem now comes into play when the gathered configuration is actually
written to the hardware, as there is no GSO, GRO or foo-offloading setting
defined - we behave as instructed and disable the offloading.
So the real bug originates from VyOS 1.2 and the old Vyatta codebase, but the
recent XML Python rewrites brought that one up to light.
Solution:
A configuration migration script will be provided starting with VyOS 1.3 which
will read in the CLI configuration of the ethernet interfaces and if not
enabled, will query the adapter if offloading is supported at all, and if so,
will enable the CLI nodes.
One might say that this will "blow" the CLI configuration but it only represents
the truth - which was masked in VyOS 1.2.
-rwxr-xr-x | src/migration-scripts/interfaces/20-to-21 | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/src/migration-scripts/interfaces/20-to-21 b/src/migration-scripts/interfaces/20-to-21 new file mode 100755 index 000000000..9210330d6 --- /dev/null +++ b/src/migration-scripts/interfaces/20-to-21 @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# T3619: mirror Linux Kernel defaults for ethernet offloading options into VyOS +# CLI. See https://phabricator.vyos.net/T3619#102254 for all the details. + +from sys import argv + +from vyos.ethtool import Ethtool +from vyos.configtree import ConfigTree + +if (len(argv) < 1): + print("Must specify file name!") + exit(1) + +file_name = argv[1] +with open(file_name, 'r') as f: + config_file = f.read() + +base = ['interfaces', 'ethernet'] +config = ConfigTree(config_file) + +if not config.exists(base): + exit(0) + +for ifname in config.list_nodes(base): + eth = Ethtool(ifname) + + # If GRO is enabled by the Kernel - we reflect this on the CLI. If GRO is + # enabled via CLI but not supported by the NIC - we remove it from the CLI + configured = config.exists(base + [ifname, 'offload', 'gro']) + enabled, fixed = eth.get_generic_receive_offload() + if configured and fixed: + config.delete(base + [ifname, 'offload', 'gro']) + elif enabled and not fixed: + config.set(base + [ifname, 'offload', 'gro']) + + # If GSO is enabled by the Kernel - we reflect this on the CLI. If GSO is + # enabled via CLI but not supported by the NIC - we remove it from the CLI + configured = config.exists(base + [ifname, 'offload', 'gso']) + enabled, fixed = eth.get_generic_segmentation_offload() + if configured and fixed: + config.delete(base + [ifname, 'offload', 'gso']) + elif enabled and not fixed: + config.set(base + [ifname, 'offload', 'gso']) + + # If LRO is enabled by the Kernel - we reflect this on the CLI. If LRO is + # enabled via CLI but not supported by the NIC - we remove it from the CLI + configured = config.exists(base + [ifname, 'offload', 'lro']) + enabled, fixed = eth.get_large_receive_offload() + if configured and fixed: + config.delete(base + [ifname, 'offload', 'lro']) + elif enabled and not fixed: + config.set(base + [ifname, 'offload', 'lro']) + + # If SG is enabled by the Kernel - we reflect this on the CLI. If SG is + # enabled via CLI but not supported by the NIC - we remove it from the CLI + configured = config.exists(base + [ifname, 'offload', 'sg']) + enabled, fixed = eth.get_scatter_gather() + if configured and fixed: + config.delete(base + [ifname, 'offload', 'sg']) + elif enabled and not fixed: + config.set(base + [ifname, 'offload', 'sg']) + + # If TSO is enabled by the Kernel - we reflect this on the CLI. If TSO is + # enabled via CLI but not supported by the NIC - we remove it from the CLI + configured = config.exists(base + [ifname, 'offload', 'tso']) + enabled, fixed = eth.get_tcp_segmentation_offload() + if configured and fixed: + config.delete(base + [ifname, 'offload', 'tso']) + 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: + 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: + f.write(config.to_string()) +except OSError as e: + print("Failed to save the modified config: {}".format(e)) + exit(1) |