summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
authorNataliia Solomko <natalirs1985@gmail.com>2025-09-24 11:56:19 +0300
committerNataliia Solomko <natalirs1985@gmail.com>2025-10-31 18:20:41 +0200
commit73b7431862d8da2e8063a6062f29fd0f65fdd210 (patch)
tree74d4084b991e127ee03b1becf84b34379f6725a3 /python
parent89e4e09ece8e54303f3d5e1f1ab4927e2351b24a (diff)
downloadvyos-1x-73b7431862d8da2e8063a6062f29fd0f65fdd210.tar.gz
vyos-1x-73b7431862d8da2e8063a6062f29fd0f65fdd210.zip
T7789: T7661: VPP prevent failing to set XDP driver on clouds
Some cloud NICs (ena, gve) fail to load XDP if all RX queues are configured. To avoid this, we limit the number of queues to half of the maximum supported by the driver.
Diffstat (limited to 'python')
-rw-r--r--python/vyos/ethtool.py23
-rw-r--r--python/vyos/ifconfig/ethernet.py17
-rw-r--r--python/vyos/vpp/control_host.py38
-rw-r--r--python/vyos/vpp/control_vpp.py2
4 files changed, 79 insertions, 1 deletions
diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py
index 6c362163c..22e066def 100644
--- a/python/vyos/ethtool.py
+++ b/python/vyos/ethtool.py
@@ -59,6 +59,7 @@ class Ethtool:
_ring_buffer = None
_driver_name = None
_flow_control = None
+ _channels = ''
def __init__(self, ifname):
# Get driver used for interface
@@ -111,6 +112,11 @@ class Ethtool:
if not bool(err):
self._flow_control = loads(out)[0]
+ # Get information about NIC channels
+ out, err = popen(f'ethtool --show-channels {ifname}')
+ if not bool(err):
+ self._channels = out.lower()
+
def check_auto_negotiation_supported(self):
""" Check if the NIC supports changing auto-negotiation """
return self._base_settings['supports-auto-negotiation']
@@ -199,3 +205,20 @@ class Ethtool:
'flow-control settings!')
return 'on' if bool(self._flow_control['autonegotiate']) else 'off'
+
+ def get_channels(self, rx_tx_comb):
+ """
+ Get both the pre-set maximum and current value for a given channel type.
+
+ Args:
+ rx_tx_comb (str): Channel type, one of "rx", "tx", or "combined".
+
+ Returns:
+ list[int]: [maximum, current] values for the channel,
+ or an empty list if not supported.
+ """
+ if rx_tx_comb not in ['rx', 'tx', 'combined']:
+ raise ValueError('Channel type must be either "rx", "tx" or "combined"')
+ matches = re.findall(rf'{rx_tx_comb}:\s+(\d+)', self._channels)
+
+ return [int(value) for value in matches]
diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py
index 6c7b230ca..70da837b9 100644
--- a/python/vyos/ifconfig/ethernet.py
+++ b/python/vyos/ifconfig/ethernet.py
@@ -455,6 +455,23 @@ class EthernetIf(Interface):
print(f'could not set "{rx_tx}" ring-buffer for {ifname}')
return output
+ def set_channels(self, rx_tx_comb, queues):
+ """
+ Example:
+ >>> from vyos.ifconfig import EthernetIf
+ >>> i = EthernetIf('eth0')
+ >>> i.set_channels('rx', 2)
+ """
+ ifname = self.config['ifname']
+ cmd = f'ethtool --set-channels {ifname} {rx_tx_comb} {queues}'
+ 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 "{rx_tx_comb}" channel for {ifname}')
+ return output
+
def set_switchdev(self, enable):
ifname = self.config['ifname']
addr, code = self._popen(
diff --git a/python/vyos/vpp/control_host.py b/python/vyos/vpp/control_host.py
index db1a492c9..2ebfd180b 100644
--- a/python/vyos/vpp/control_host.py
+++ b/python/vyos/vpp/control_host.py
@@ -24,6 +24,7 @@ from time import sleep
from pyroute2 import IPRoute
from vyos.ethtool import Ethtool
+from vyos.ifconfig import EthernetIf
from vyos.vpp.utils import EthtoolGDrvinfo
@@ -376,3 +377,40 @@ def flush_ip(iface_name: str) -> None:
"""
iproute = IPRoute()
iproute.flush_addr(label=iface_name)
+
+
+def get_eth_channels(iface_name: str) -> dict:
+ """
+ Get the current hardware queue counts for channels of an interface using ethtool.
+
+ Args:
+ iface_name (str): name of an interface
+
+ Returns:
+ dict: Mapping of channel types to their current values:
+ - 'rx' (int | None): RX channel count.
+ - 'tx' (int | None): TX channel count.
+ - 'combined' (int | None): Combined channel count.
+ Returns None if the channel type is not supported.
+ """
+ ethtool = Ethtool(iface_name)
+
+ channels = {}
+ for channel in ['rx', 'tx', 'combined']:
+ queues_list = ethtool.get_channels(channel)
+ channels[channel] = queues_list[-1] if (len(queues_list) == 2) else None
+
+ return channels
+
+
+def set_eth_channels(iface_name: str, channels: dict) -> None:
+ """Configure the number of RX, TX, or combined channels for an interface.
+
+ Args:
+ iface_name (str): name of an interface
+ channels (dict): channels to set
+ """
+ interface = EthernetIf(iface_name)
+ for channel, value in channels.items():
+ if value:
+ interface.set_channels(channel, value)
diff --git a/python/vyos/vpp/control_vpp.py b/python/vyos/vpp/control_vpp.py
index cb8da50a6..2ee253b45 100644
--- a/python/vyos/vpp/control_vpp.py
+++ b/python/vyos/vpp/control_vpp.py
@@ -351,7 +351,7 @@ class VPPControl:
self,
host_if: str,
name: str,
- rxq_num: int = 0,
+ rxq_num: int = 65535,
rxq_size: int = 0,
txq_size: int = 0,
mode: Literal['auto', 'copy', 'zero-copy'] = 'auto',