diff options
-rw-r--r-- | interface-definitions/interfaces-ethernet.xml.in | 6 | ||||
-rw-r--r-- | python/vyos/ifconfig/ethernet.py | 35 | ||||
-rw-r--r-- | python/vyos/validate.py | 4 | ||||
-rwxr-xr-x | smoketest/scripts/cli/test_interfaces_ethernet.py | 25 | ||||
-rwxr-xr-x | src/conf_mode/interfaces-ethernet.py | 10 |
5 files changed, 74 insertions, 6 deletions
diff --git a/interface-definitions/interfaces-ethernet.xml.in b/interface-definitions/interfaces-ethernet.xml.in index 4c4fae673..be44072a6 100644 --- a/interface-definitions/interfaces-ethernet.xml.in +++ b/interface-definitions/interfaces-ethernet.xml.in @@ -77,6 +77,12 @@ <valueless/> </properties> </leafNode> + <leafNode name="rps"> + <properties> + <help>Enable Receive Packet Steering</help> + <valueless/> + </properties> + </leafNode> <leafNode name="sg"> <properties> <help>Enable Scatter-Gather</help> diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py index 1bec45879..7cf79bead 100644 --- a/python/vyos/ifconfig/ethernet.py +++ b/python/vyos/ifconfig/ethernet.py @@ -1,4 +1,4 @@ -# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2019-2021 VyOS maintainers and contributors <maintainers@vyos.io> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -17,9 +17,10 @@ import os import re from vyos.ifconfig.interface import Interface -from vyos.validate import assert_list from vyos.util import run from vyos.util import dict_search +from vyos.validate import assert_list +from vyos.validate import assert_range @Interface.register class EthernetIf(Interface): @@ -75,6 +76,13 @@ class EthernetIf(Interface): }, }} + _sysfs_set = {**Interface._sysfs_set, **{ + 'rps': { + 'validate': lambda cpu: assert_range(cpu, 0, 4294967295), + 'location': '/sys/class/net/{ifname}/queues/rx-0/rps_cpus', + }, + }} + def get_driver_name(self): """ Return the driver name used by NIC. Some NICs don't support all @@ -231,6 +239,26 @@ class EthernetIf(Interface): raise ValueError("Value out of range") return self.set_interface('gso', 'on' if state else 'off') + def set_rps(self, state): + if not isinstance(state, bool): + raise ValueError("Value out of range") + + rps_cpus = 0 + if state: + # enable RPS on all available CPUs, RPS works woth a CPU bitmask, + # where each bit represents a CPU (core/thread). The formula below + # expands to rps_cpus = 255 for a 8 core system + rps_cpus = (1 << os.cpu_count()) -1 + + # XXX: we should probably reserve one core when the system is under + # high preasure so we can still have a core left for housekeeping. + # This is done by masking out the lowst bit so CPU0 is spared from + # receive packet steering. + rps_cpus &= ~1 + + # send bitmask representation as hex string without leading '0x' + return self.set_interface('rps', f'{rps_cpus:x}') + def set_sg(self, state): """ Enable Scatter-Gather support. State can be either True or False. @@ -307,6 +335,9 @@ class EthernetIf(Interface): # GSO (generic segmentation offload) self.set_gso(dict_search('offload.gso', config) != None) + # RPS - Receive Packet Steering + self.set_rps(dict_search('offload.rps', config) != None) + # scatter-gather option self.set_sg(dict_search('offload.sg', config) != None) diff --git a/python/vyos/validate.py b/python/vyos/validate.py index 98bd40f74..acd6086ff 100644 --- a/python/vyos/validate.py +++ b/python/vyos/validate.py @@ -1,4 +1,4 @@ -# Copyright 2018-2020 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2018-2021 VyOS maintainers and contributors <maintainers@vyos.io> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -165,7 +165,7 @@ def assert_boolean(b): def assert_range(value, lower=0, count=3): - if int(value) not in range(lower,lower+count): + if int(value, 16) not in range(lower, lower+count): raise ValueError("Value out of range") diff --git a/smoketest/scripts/cli/test_interfaces_ethernet.py b/smoketest/scripts/cli/test_interfaces_ethernet.py index d5dcdc536..2d0a4827d 100755 --- a/smoketest/scripts/cli/test_interfaces_ethernet.py +++ b/smoketest/scripts/cli/test_interfaces_ethernet.py @@ -99,6 +99,31 @@ class EthernetInterfaceTest(BasicInterfaceTest.BaseTest): flags = f.read() self.assertEqual(int(flags, 16) & 1, 0) + def test_offloading_rps(self): + # enable RPS on all available CPUs, RPS works woth a CPU bitmask, + # where each bit represents a CPU (core/thread). The formula below + # expands to rps_cpus = 255 for a 8 core system + rps_cpus = (1 << os.cpu_count()) -1 + + # XXX: we should probably reserve one core when the system is under + # high preasure so we can still have a core left for housekeeping. + # This is done by masking out the lowst bit so CPU0 is spared from + # receive packet steering. + rps_cpus &= ~1 + + for interface in self._interfaces: + self.session.set(self._base_path + [interface, 'offload', 'rps']) + + self.session.commit() + + for interface in self._interfaces: + cpus = read_file('/sys/class/net/eth1/queues/rx-0/rps_cpus') + # remove the nasty ',' separation on larger strings + cpus = cpus.replace(',','') + cpus = int(cpus, 16) + + self.assertEqual(f'{cpus:x}', f'{rps_cpus:x}') + def test_eapol_support(self): for interface in self._interfaces: diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py index d8b637dd7..ed6396acf 100755 --- a/src/conf_mode/interfaces-ethernet.py +++ b/src/conf_mode/interfaces-ethernet.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2019-2020 VyOS maintainers and contributors +# Copyright (C) 2019-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 @@ -75,9 +75,15 @@ def verify(ethernet): verify_vrf(ethernet) verify_eapol(ethernet) + ifname = ethernet['ifname'] + # verify offloading capabilities + if 'offload' in ethernet and 'rps' in ethernet['offload']: + if not os.path.exists(f'/sys/class/net/{ifname}/queues/rx-0/rps_cpus'): + raise ConfigError('Interface does not suport RPS!') + # XDP requires multiple TX queues if 'xdp' in ethernet: - queues = glob('/sys/class/net/{ifname}/queues/tx-*'.format(**ethernet)) + queues = glob(f'/sys/class/net/{ifname}/queues/tx-*') if len(queues) < 2: raise ConfigError('XDP requires additional TX queues, too few available!') |