summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorViacheslav Hletenko <v.gletenko@vyos.io>2026-02-17 16:05:48 +0200
committerGitHub <noreply@github.com>2026-02-17 16:05:48 +0200
commit9785f396ba1e82b6b359e42af7da03828b4ff6ed (patch)
tree3ca807b3c1210e871fca08c10f167be751c16e7c
parent33752c9a41f2e79c1ef3884188968d0f48bc79d8 (diff)
parent2414c156cc21bfe51b65f8726f186c55b533bb97 (diff)
downloadvyos-1x-9785f396ba1e82b6b359e42af7da03828b4ff6ed.tar.gz
vyos-1x-9785f396ba1e82b6b359e42af7da03828b4ff6ed.zip
Merge pull request #4993 from natali-rs1985/T8268
vpp: T8268: Unify CPU settings into a single 'cpu-cores' node under 'resource-allocation'
-rw-r--r--data/templates/vpp/startup.conf.j23
-rw-r--r--interface-definitions/vpp.xml.in81
-rw-r--r--python/vyos/vpp/config_resource_checks/cpu.py76
-rw-r--r--python/vyos/vpp/config_resource_checks/memory.py11
-rw-r--r--python/vyos/vpp/config_verify.py116
-rwxr-xr-xsmoketest/scripts/cli/test_vpp.py133
-rwxr-xr-xsrc/conf_mode/vpp.py81
-rw-r--r--src/migration-scripts/vpp/7-to-835
8 files changed, 150 insertions, 386 deletions
diff --git a/data/templates/vpp/startup.conf.j2 b/data/templates/vpp/startup.conf.j2
index 218e399d4..b93b53bbd 100644
--- a/data/templates/vpp/startup.conf.j2
+++ b/data/templates/vpp/startup.conf.j2
@@ -18,9 +18,6 @@ cpu {
{% if cpu.main_core is vyos_defined %}
main-core {{ cpu.main_core }}
{% endif %}
-{% if cpu.corelist_workers is vyos_defined %}
- corelist-workers {{ cpu.corelist_workers | join(',') }}
-{% endif %}
{% if cpu.skip_cores is vyos_defined %}
skip-cores {{ cpu.skip_cores }}
{% endif %}
diff --git a/interface-definitions/vpp.xml.in b/interface-definitions/vpp.xml.in
index 70e3e102c..6c9440302 100644
--- a/interface-definitions/vpp.xml.in
+++ b/interface-definitions/vpp.xml.in
@@ -527,6 +527,26 @@
<help>VPP settings</help>
</properties>
<children>
+ <node name="resource-allocation">
+ <properties>
+ <help>Resource allocation settings</help>
+ </properties>
+ <children>
+ <leafNode name="cpu-cores">
+ <properties>
+ <help>Create worker threads (including main-core)</help>
+ <valueHelp>
+ <format>u32:1-512</format>
+ <description>Worker threads</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-512"/>
+ </constraint>
+ </properties>
+ <defaultValue>1</defaultValue>
+ </leafNode>
+ </children>
+ </node>
<node name="buffers">
<properties>
<help>Buffer settings</help>
@@ -572,67 +592,6 @@
</leafNode>
</children>
</node>
- <node name="cpu">
- <properties>
- <help>CPU settings</help>
- </properties>
- <children>
- <leafNode name="corelist-workers">
- <properties>
- <help>List of cores worker threads</help>
- <valueHelp>
- <format>&lt;id&gt;</format>
- <description>CPU core id</description>
- </valueHelp>
- <valueHelp>
- <format>&lt;idN&gt;-&lt;idM&gt;</format>
- <description>CPU core id range (use '-' as delimiter)</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--allow-range --range 0-512"/>
- </constraint>
- <constraintErrorMessage>not a valid CPU core value or range</constraintErrorMessage>
- <multi/>
- </properties>
- </leafNode>
- <leafNode name="main-core">
- <properties>
- <help>Main core</help>
- <valueHelp>
- <format>u32:0-512</format>
- <description>Assign main thread to specific core</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 0-512"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="skip-cores">
- <properties>
- <help>Skip cores</help>
- <valueHelp>
- <format>u32:1-512</format>
- <description>Skip cores</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-512"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="workers">
- <properties>
- <help>Create worker threads</help>
- <valueHelp>
- <format>u32:0-4294967295</format>
- <description>Worker threads</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 0-512"/>
- </constraint>
- </properties>
- </leafNode>
- </children>
- </node>
<tagNode name="interface">
<properties>
<help>Interface</help>
diff --git a/python/vyos/vpp/config_resource_checks/cpu.py b/python/vyos/vpp/config_resource_checks/cpu.py
deleted file mode 100644
index b319608e4..000000000
--- a/python/vyos/vpp/config_resource_checks/cpu.py
+++ /dev/null
@@ -1,76 +0,0 @@
-# Used for validating estimated CPU/physical cores use
-#
-# Copyright (C) VyOS Inc.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# 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, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-from vyos.utils.cpu import get_available_cpus, get_core_count
-
-from vyos.vpp.config_resource_checks.resource_defaults import default_resource_map
-
-
-# Get default value for reserved cpu cores
-reserved_cpus = default_resource_map.get('reserved_cpu_cores')
-
-
-def available_cores_count(cpu_settings: dict) -> int:
- core_count = get_core_count()
-
- if cpu_settings.get('main_core'):
- core_count -= 1
-
- skip_cores = int(cpu_settings.get('skip_cores', 0))
- # The default settings assume that
- # at least 2 CPU cores should remain reserved for system use
- # (only in case of current runtime is not smoke test)
- if skip_cores < reserved_cpus:
- core_count -= reserved_cpus
- else:
- core_count -= skip_cores
-
- return core_count
-
-
-def available_cores_list(skip_cores: int) -> list:
- # Available cores are all CPU cores without first N skipped cores that will not be used
- # Get all available physical cores - use set to filter out unique values
- cpu_cores = set(map(lambda el: el['cpu'], get_available_cpus()))
- cpu_cores = list(cpu_cores)
-
- return cpu_cores[skip_cores:]
-
-
-def worker_cores_list(iface: str, worker_ranges: list) -> list:
- all_core_numbers = []
- for worker_range in worker_ranges:
- core_numbers = worker_range.split('-')
-
- if int(core_numbers[0]) > int(core_numbers[-1]):
- raise ValueError(
- f'Range for "{iface} workers {worker_range}" is not correct'
- )
-
- all_core_numbers.extend(range(int(core_numbers[0]), int(core_numbers[-1]) + 1))
-
- # Check for duplicates
- duplicates = set(
- [str(x) for n, x in enumerate(all_core_numbers) if x in all_core_numbers[:n]]
- )
- if duplicates:
- raise ValueError(
- f'Some workers in "{iface} workers" are duplicated: #{",".join(list(duplicates))}'
- )
-
- return all_core_numbers
diff --git a/python/vyos/vpp/config_resource_checks/memory.py b/python/vyos/vpp/config_resource_checks/memory.py
index 03fe097e9..f1ae6f7bc 100644
--- a/python/vyos/vpp/config_resource_checks/memory.py
+++ b/python/vyos/vpp/config_resource_checks/memory.py
@@ -174,10 +174,11 @@ def total_memory_required(settings: dict) -> dict:
return memory
-def buffers_required(settings: dict, workers) -> int:
+def buffers_required(settings: dict) -> int:
"""
Calculate total VPP buffer requirements based on interface settings and workers.
"""
+ workers = int(settings['resource_allocation']['cpu_cores'])
buffers_total = 0
for ifname, iface_config in settings.get('interface', {}).items():
# Do not include XDP interfaces in buffer calculations.
@@ -185,14 +186,14 @@ def buffers_required(settings: dict, workers) -> int:
# so buffer requirements cannot be derived from descriptors here.
# Buffers for XDP are handled internally by the kernel/XDP layer,
# not by VPP’s buffer allocator.
- if iface_config.get('driver') == 'xdp':
- continue
+ # if iface_config.get('driver') == 'xdp':
+ # continue
+
dpdk_options = iface_config.get('dpdk_options', {})
rx_queues = int(dpdk_options.get('num_rx_queues', 1))
rx_desc = int(dpdk_options.get('num_rx_desc'))
# default TX queues is equal to number of worker threads
- # plus 1 main thread
- tx_queues = int(dpdk_options.get('num_tx_queues', workers + 1))
+ tx_queues = int(dpdk_options.get('num_tx_queues', workers))
tx_desc = int(dpdk_options.get('num_tx_desc'))
# buffers for RX/TX queues for interface
diff --git a/python/vyos/vpp/config_verify.py b/python/vyos/vpp/config_verify.py
index bcc10257f..f8bfb214a 100644
--- a/python/vyos/vpp/config_verify.py
+++ b/python/vyos/vpp/config_verify.py
@@ -22,7 +22,7 @@ from vyos import ConfigError
from vyos.base import Warning
from vyos.utils.cpu import get_core_count as total_core_count, get_cpus
-from vyos.vpp.config_resource_checks import cpu as cpu_checks, memory as mem_checks
+from vyos.vpp.config_resource_checks import memory as mem_checks
from vyos.vpp.config_resource_checks.resource_defaults import default_resource_map
from vyos.vpp.utils import human_memory_to_bytes, bytes_to_human_memory
@@ -183,17 +183,13 @@ def verify_dev_driver(driver_type: str, driver: str) -> bool:
return False
-def create_cpu_error_message(
- cpus_required: int, cpus_available: int = None, skip_cores: int = 0
-) -> str:
+def create_cpu_error_message(cpus_required: int, cpus_available: int = None) -> str:
cpu_info = get_cpus()
logical_cores = sum(
[int(s.get('siblings')) if 'siblings' in s else 1 for s in cpu_info]
)
reserved_cpus = default_resource_map.get('reserved_cpu_cores')
- if skip_cores > reserved_cpus:
- reserved_cpus = skip_cores
available_str = (
(
@@ -320,100 +316,19 @@ def verify_vpp_memory(config: dict):
)
-def verify_vpp_settings_cpu_skip_cores(skip_cores: int):
- cpu_cores = total_core_count()
-
- # The number of skipped cores must not be greater than
- # available CPU cores in the system - 1 for main thread
- if skip_cores > (cpu_cores - 1):
- raise ConfigError(
- f'The system does not have enough available CPUs to skip '
- f'(reduce "cpu skip-cores" to {cpu_cores} or less)'
- )
-
-
-def verify_vpp_settings_cpu(settings: dict):
- """
- Verify 'cpu main-core' is set if worker-related settings are used.
- `set vpp settings cpu workers` and `set vpp settings cpu corelist-workers`
- are mutually exclusive!
- """
- worker_related = ('corelist_workers', 'workers', 'skip_cores')
-
- if any(key in settings for key in worker_related) and 'main_core' not in settings:
- raise ConfigError('"cpu main-core" is required but not set!')
-
- if 'corelist_workers' in settings and 'workers' in settings:
- raise ConfigError(
- '"cpu corelist-workers" and "cpu workers" cannot be used at the same time!'
- )
-
-
-def verify_vpp_cpu_main_core(cpu_settings: dict) -> None:
- """Check that the main core is available"""
- skip_cores = int(cpu_settings.get('skip_cores', 0))
- available_cores = cpu_checks.available_cores_list(skip_cores)
- main_core = int(cpu_settings['main_core'])
-
- if main_core not in available_cores:
- raise ConfigError(
- 'Cannot set main core for VPP process: '
- f'CPU#{main_core} is not available.'
- )
-
-
-def verify_vpp_settings_cpu_workers(cpu_settings: dict):
+def verify_vpp_cpu_cores(cpu_cores: int):
"""
Verify that the system has enough available CPU cores
to run a given amount of worker processes (1 worker/core)
"""
- workers = int(cpu_settings.get('workers', 0))
- available_cores = cpu_checks.available_cores_count(cpu_settings)
-
- if workers > available_cores:
- raise ConfigError(
- f'Not enough free physical CPU cores for {workers} VPP workers '.ljust(72)
- + create_cpu_error_message(
- workers, available_cores, cpu_settings.get('skip_cores', 0)
- )
- )
-
-
-def verify_vpp_settings_cpu_corelist_workers(cpu_settings: dict):
- """
- Verify that the CPU cores provided to the config are free and can be used by VPP
- """
- workers = cpu_settings.get('corelist_workers')
- main_core = int(cpu_settings.get('main_core'))
- skip_cores = int(cpu_settings.get('skip_cores', 0))
- available_cores = cpu_checks.available_cores_list(skip_cores)
- try:
- all_core_nums = cpu_checks.worker_cores_list(
- iface='cpu corelist', worker_ranges=workers
- )
- except ValueError as e:
- raise ConfigError(str(e))
-
- error_msg = 'Cannot set VPP "cpu corelist-workers": '.ljust(72)
-
- if main_core in all_core_nums:
- raise ConfigError(
- f'CPU#{main_core} is set as main core and should not '
- 'be included to the corelist-workers'
- )
-
- invalid_cores = [str(el) for el in all_core_nums if el not in available_cores]
- if invalid_cores:
- raise ConfigError(error_msg + f'CPU# {",".join(invalid_cores)} not available.')
+ total_cores = total_core_count()
+ reserved_cpus = default_resource_map.get('reserved_cpu_cores')
+ available_cores = total_cores - reserved_cpus
- available_cores_count = cpu_checks.available_cores_count(cpu_settings)
- if len(all_core_nums) > available_cores_count:
+ if cpu_cores > available_cores:
raise ConfigError(
- error_msg
- + 'Not enough free physical CPUs in the system.'.ljust(72)
- + create_cpu_error_message(
- len(all_core_nums), available_cores_count, skip_cores
- )
+ f'Not enough free physical CPU cores for {cpu_cores} "cpu-cores" '.ljust(72)
+ + create_cpu_error_message(cpu_cores, available_cores)
)
@@ -441,12 +356,12 @@ def verify_vpp_interfaces_dpdk_num_queues(qtype: str, num_queues: int, workers:
if num_queues > workers:
raise ConfigError(
- f'The number of {qtype} queues cannot be greater than the number of configured VPP workers: '
- f'workers: {workers}, queues: {num_queues}'
+ f'The number of {qtype} queues cannot be greater than the number of configured VPP "cpu-cores": '
+ f'cpu-cores: {workers}, queues: {num_queues}'
)
-def verify_routes_count(settings: dict, workers: int):
+def verify_routes_count(settings: dict):
"""
Maximum routes count depending on main heap size,
statistics segment size and workers
@@ -454,12 +369,13 @@ def verify_routes_count(settings: dict, workers: int):
counters = 2 # 2 counters for each route
bytes = 16 # each counter consumes 16 bytes
statseg_scale = 2
+ cpu_cores = int(settings['resource_allocation']['cpu_cores'])
statseg_size = settings['statseg']['size']
statseg_size_in_bytes = human_memory_to_bytes(statseg_size)
main_heap = settings['memory']['main_heap_size']
main_heap_in_gb = human_memory_to_bytes(main_heap) >> 30
- formula = (workers + 1) * counters * bytes * statseg_scale
+ formula = cpu_cores * counters * bytes * statseg_scale
routes_count_statseg = statseg_size_in_bytes / formula
routes_count_statseg = round(routes_count_statseg / 1_000_000, 2)
routes_count_mh = main_heap_in_gb * 2
@@ -472,10 +388,10 @@ def verify_routes_count(settings: dict, workers: int):
)
-def verify_vpp_buffers(settings: dict, workers: int):
+def verify_vpp_buffers(settings: dict):
buffers_configured = int(settings['buffers']['buffers_per_numa'])
- buffers_required = mem_checks.buffers_required(settings, workers)
+ buffers_required = mem_checks.buffers_required(settings)
if buffers_required > buffers_configured:
raise ConfigError(
diff --git a/smoketest/scripts/cli/test_vpp.py b/smoketest/scripts/cli/test_vpp.py
index 6cc70a4ac..e6264bbfc 100755
--- a/smoketest/scripts/cli/test_vpp.py
+++ b/smoketest/scripts/cli/test_vpp.py
@@ -26,6 +26,7 @@ from json import loads
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
+from vyos.utils.cpu import get_available_cpus
from vyos.utils.process import process_named_running
from vyos.utils.file import read_file
from vyos.utils.process import rc_cmd
@@ -33,6 +34,7 @@ from vyos.utils.system import sysctl_read
from vyos.system import image
from vyos.vpp import VPPControl
from vyos.vpp.utils import vpp_iface_name_transform
+from vyos.vpp.config_resource_checks.resource_defaults import default_resource_map
PROCESS_NAME = 'vpp_main'
VPP_CONF = '/run/vpp/vpp.conf'
@@ -81,6 +83,14 @@ def get_address(interface):
return ip_address
+def get_vpp_cpu_allocation():
+ reserved_cpus = default_resource_map.get('reserved_cpu_cores')
+ # Get sorted list of available CPU IDs
+ available = sorted({cpu['cpu'] for cpu in get_available_cpus()})
+ main_core = available[reserved_cpus] # first non-reserved CPU
+ return reserved_cpus, main_core
+
+
class TestVPP(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
@@ -116,18 +126,10 @@ class TestVPP(VyOSUnitTestSHIM.TestCase):
super().tearDown()
def test_01_vpp_basic(self):
- main_core = '0'
poll_sleep = '0'
mtu = '2500'
+ skip_cores, main_core = get_vpp_cpu_allocation()
- # Main core must be verified
- # expect raise ConfigError
- self.cli_set(base_path + ['settings', 'cpu', 'main-core', '99'])
-
- with self.assertRaises(ConfigSessionError):
- self.cli_commit()
-
- self.cli_set(base_path + ['settings', 'cpu', 'main-core', main_core])
self.cli_set(base_path + ['settings', 'poll-sleep-usec', poll_sleep])
# commit changes
@@ -135,6 +137,7 @@ class TestVPP(VyOSUnitTestSHIM.TestCase):
config_entries = (
f'poll-sleep-usec {poll_sleep}',
+ f'skip-cores {skip_cores}',
f'main-core {main_core}',
'plugin default { disable }',
'plugin dpdk_plugin.so { enable }',
@@ -1128,11 +1131,10 @@ class TestVPP(VyOSUnitTestSHIM.TestCase):
dpdk_options = {
'num-rx-desc': '512',
'num-tx-desc': '512',
- 'num-rx-queues': '1',
- 'num-tx-queues': '1',
+ 'num-rx-queues': '2',
+ 'num-tx-queues': '2',
}
- main_core = '0'
- workers = '1'
+ cpu_cores = '2'
base_interface_path = base_path + ['settings', 'interface', interface]
@@ -1144,8 +1146,9 @@ class TestVPP(VyOSUnitTestSHIM.TestCase):
with self.assertRaises(ConfigSessionError):
self.cli_commit()
- self.cli_set(base_path + ['settings', 'cpu', 'main-core', main_core])
- self.cli_set(base_path + ['settings', 'cpu', 'workers', workers])
+ self.cli_set(
+ base_path + ['settings', 'resource-allocation', 'cpu-cores', cpu_cores]
+ )
# # DPDK driver expect only dpdk-options and not xdp-options to be set
# # expect raise ConfigError
@@ -1165,77 +1168,25 @@ class TestVPP(VyOSUnitTestSHIM.TestCase):
for option, value in dpdk_options.items():
self.assertIn(f'{option} {value}', config)
- def test_11_vpp_cpu_settings(self):
- main_core = '2'
- workers = '1'
- skip_cores = '1'
-
- self.cli_set(base_path + ['settings', 'cpu', 'workers', workers])
-
- # "cpu workers" reqiures main-core to be set
- # expect raise ConfigError
- with self.assertRaises(ConfigSessionError):
- self.cli_commit()
+ def test_11_vpp_cpu_cores(self):
+ cpu_cores = '2'
+ skip_cores, main_core = get_vpp_cpu_allocation()
- self.cli_set(base_path + ['settings', 'cpu', 'main-core', main_core])
-
- self.cli_set(base_path + ['settings', 'cpu', 'skip-cores', '99'])
-
- # "cpu skip-cores" cannot be more than number of available CPUs - 1
+ # verify 'cpu-cores' are set not correctly
# expect raise ConfigError
+ self.cli_set(base_path + ['settings', 'resource-allocation', 'cpu-cores', '99'])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
- self.cli_set(base_path + ['settings', 'cpu', 'skip-cores', skip_cores])
-
- self.cli_commit()
-
- config_entries = (
- f'skip-cores {skip_cores}',
- f'main-core {main_core}',
- f'workers {workers}',
- 'dev 0000:00:00.0',
+ self.cli_set(
+ base_path + ['settings', 'resource-allocation', 'cpu-cores', cpu_cores]
)
-
- # Check configured options
- config = read_file(VPP_CONF)
- for config_entry in config_entries:
- self.assertIn(config_entry, config)
-
- def test_12_vpp_cpu_corelist_workers(self):
- main_core = '0'
- corelist_workers = ['3']
-
- for worker in corelist_workers:
- self.cli_set(base_path + ['settings', 'cpu', 'corelist-workers', worker])
-
- # "cpu corelist-workers" reqiures main-core to be set
- # expect raise ConfigError
- with self.assertRaises(ConfigSessionError):
- self.cli_commit()
-
- self.cli_set(base_path + ['settings', 'cpu', 'main-core', main_core])
-
- # corelist-workers and workers cannot be used at the same time
- # expect raise ConfigError
- self.cli_set(base_path + ['settings', 'cpu', 'workers', '2'])
- with self.assertRaises(ConfigSessionError):
- self.cli_commit()
- self.cli_delete(base_path + ['settings', 'cpu', 'workers'])
-
- # verify corelist-workers are set not correctly
- # expect raise ConfigError
- self.cli_set(base_path + ['settings', 'cpu', 'corelist-workers', '99-101'])
- with self.assertRaises(ConfigSessionError):
- self.cli_commit()
-
- self.cli_delete(base_path + ['settings', 'cpu', 'corelist-workers', '99-101'])
-
self.cli_commit()
config_entries = (
- f'main-core {main_core}',
- f'corelist-workers {",".join(corelist_workers)}',
+ f'skip-cores {skip_cores}', # reserved cpus skipped for system use
+ f'main-core {main_core}', # first available core is set as main-core
+ f'workers {int(cpu_cores) - 1}',
'dev 0000:00:00.0',
)
@@ -1244,7 +1195,7 @@ class TestVPP(VyOSUnitTestSHIM.TestCase):
for config_entry in config_entries:
self.assertIn(config_entry, config)
- def test_13_1_buffer_page_size(self):
+ def test_12_1_buffer_page_size(self):
sizes = ['4K', '2M']
for size in sizes:
self.cli_set(base_path + ['settings', 'buffers', 'page-size', size])
@@ -1253,7 +1204,7 @@ class TestVPP(VyOSUnitTestSHIM.TestCase):
conf = get_vpp_config()
self.assertEqual(conf['buffers']['page-size'], size)
- def test_13_2_statseg_page_size(self):
+ def test_12_2_statseg_page_size(self):
sizes = ['4K', '2M']
for size in sizes:
self.cli_set(base_path + ['settings', 'statseg', 'page-size', size])
@@ -1262,7 +1213,7 @@ class TestVPP(VyOSUnitTestSHIM.TestCase):
conf = get_vpp_config()
self.assertEqual(conf['statseg']['page-size'], size)
- def test_13_3_mem_page_size(self):
+ def test_12_3_mem_page_size(self):
sizes = ['4K', '2M']
for size in sizes:
self.cli_set(
@@ -1273,7 +1224,7 @@ class TestVPP(VyOSUnitTestSHIM.TestCase):
conf = get_vpp_config()
self.assertEqual(conf['memory']['main-heap-page-size'], size)
- def test_14_vpp_ipsec_xfrm_nl(self):
+ def test_13_vpp_ipsec_xfrm_nl(self):
base_lcp = base_path + ['settings', 'lcp']
batch_delay = '250'
batch_size = '150'
@@ -1299,7 +1250,7 @@ class TestVPP(VyOSUnitTestSHIM.TestCase):
for config_entry in config_entries:
self.assertIn(config_entry, config)
- def test_15_1_vpp_cgnat(self):
+ def test_14_1_vpp_cgnat(self):
base_cgnat = base_path + ['nat', 'cgnat']
iface_out = 'eth0'
iface_inside = 'eth1'
@@ -1338,7 +1289,7 @@ class TestVPP(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'tcp transitory timeout: {timeout_tcp_trans}sec', out)
self.assertIn(f'icmp timeout: {timeout_icmp}sec', out)
- def test_15_2_vpp_cgnat_bond_with_vifs(self):
+ def test_14_2_vpp_cgnat_bond_with_vifs(self):
base_cgnat = base_path + ['nat', 'cgnat']
base_kernel = base_path + ['kernel-interfaces']
base_bond = base_path + ['interfaces', 'bonding']
@@ -1382,7 +1333,7 @@ class TestVPP(VyOSUnitTestSHIM.TestCase):
lines = out.split('\n')
self.assertTrue(len(lines) == 3)
- def test_16_vpp_nat44(self):
+ def test_15_vpp_nat44(self):
base_nat = base_path + ['nat', 'nat44']
exclude_local_addr = '100.64.0.52'
exclude_local_port = '22'
@@ -1479,7 +1430,7 @@ class TestVPP(VyOSUnitTestSHIM.TestCase):
out = vpp.api.nat44_show_running_config().forwarding_enabled
self.assertTrue(out)
- def test_17_vpp_sflow(self):
+ def test_16_vpp_sflow(self):
base_sflow = ['system', 'sflow']
sampling_rate = '1500'
polling_interval = '55'
@@ -1540,7 +1491,7 @@ class TestVPP(VyOSUnitTestSHIM.TestCase):
_, out = rc_cmd('sudo vppctl show sflow')
self.assertIn('interfaces enabled: 0', out)
- def test_18_resource_limits(self):
+ def test_17_resource_limits(self):
max_map_count = '100000'
shmmax = '55555555555555'
hr_path = ['system', 'option', 'resource-limits']
@@ -1567,7 +1518,7 @@ class TestVPP(VyOSUnitTestSHIM.TestCase):
self.assertEqual(sysctl_read('vm.max_map_count'), '65530')
self.assertEqual(sysctl_read('kernel.shmmax'), '8589934592')
- def test_19_vpp_pppoe_mapping(self):
+ def test_18_vpp_pppoe_mapping(self):
config_file = '/run/accel-pppd/pppoe.conf'
pool = "TEST-POOL"
vni = '23'
@@ -1617,7 +1568,7 @@ class TestVPP(VyOSUnitTestSHIM.TestCase):
self.cli_delete(['interfaces', 'ethernet', interface, 'vif'])
self.cli_commit()
- def test_20_kernel_options_hugepages(self):
+ def test_19_kernel_options_hugepages(self):
default_hp_size = '2M'
hp_size_1g = '1G'
hp_size_2m = '2M'
@@ -1650,7 +1601,7 @@ class TestVPP(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' hugepagesz={hp_size_1g} hugepages={hp_count_1g}', tmp)
self.assertIn(f' hugepagesz={hp_size_2m} hugepages={hp_count_2m}', tmp)
- def test_21_static_arp(self):
+ def test_20_static_arp(self):
host = '192.0.2.10'
mac = '00:01:02:03:04:0a'
path_static_arp = ['protocols', 'static', 'arp']
@@ -1674,7 +1625,7 @@ class TestVPP(VyOSUnitTestSHIM.TestCase):
self.cli_delete(path_static_arp)
- def test_22_1_vpp_ipfix(self):
+ def test_21_1_vpp_ipfix(self):
base_ipfix = base_path + ['ipfix']
base_collector = base_ipfix + ['collector']
collector_ip = '127.0.0.2'
@@ -1761,7 +1712,7 @@ class TestVPP(VyOSUnitTestSHIM.TestCase):
len(non_default_exporters), 0, 'Exporters not cleaned up properly'
)
- def test_22_2_vpp_ipfix_bond(self):
+ def test_21_2_vpp_ipfix_bond(self):
base_ipfix = base_path + ['ipfix']
base_bond = base_path + ['interfaces', 'bonding']
iface_bond = 'bond0'
diff --git a/src/conf_mode/vpp.py b/src/conf_mode/vpp.py
index 2634eae44..c961132d8 100755
--- a/src/conf_mode/vpp.py
+++ b/src/conf_mode/vpp.py
@@ -35,6 +35,7 @@ from vyos.ifconfig import Section
from vyos.logger import getLogger
from vyos.template import render
from vyos.utils.boot import boot_configuration_complete
+from vyos.utils.cpu import get_available_cpus
from vyos.utils.kernel import check_kmod
from vyos.utils.kernel import unload_kmod
from vyos.utils.kernel import list_loaded_modules
@@ -49,11 +50,7 @@ from vyos.vpp.config_verify import (
verify_dev_driver,
verify_vpp_minimum_cpus,
verify_vpp_minimum_memory,
- verify_vpp_settings_cpu,
- verify_vpp_settings_cpu_corelist_workers,
- verify_vpp_cpu_main_core,
- verify_vpp_settings_cpu_skip_cores,
- verify_vpp_settings_cpu_workers,
+ verify_vpp_cpu_cores,
verify_vpp_memory,
verify_vpp_statseg_size,
verify_vpp_interfaces_dpdk_num_queues,
@@ -62,6 +59,7 @@ from vyos.vpp.config_verify import (
verify_vpp_buffers,
)
from vyos.vpp.config_resource_checks import memory
+from vyos.vpp.config_resource_checks.resource_defaults import default_resource_map
from vyos.vpp.config_filter import iface_filter_eth
from vyos.vpp.utils import EthtoolGDrvinfo
from vyos.vpp.configdb import JSONStorage
@@ -152,25 +150,28 @@ def _unload_module(module_name: str):
raise
-def _get_workers_count(cpu_settings: dict) -> int:
- if 'corelist_workers' in cpu_settings:
- corelist_workers = []
- for worker_range in cpu_settings['corelist_workers']:
- core_numbers = worker_range.split('-')
- corelist_workers.extend(
- range(int(core_numbers[0]), int(core_numbers[-1]) + 1)
- )
+def _configure_vpp_cpu_settings(config: dict):
+ """Configure VPP CPU settings: main-core, workers and skip-cores based on 'cpu-cores'"""
+ cpu_cores = int(config['settings']['resource_allocation']['cpu_cores'])
+ reserved_cpus = default_resource_map.get('reserved_cpu_cores')
- return len(corelist_workers)
+ # Get sorted list of available CPU IDs
+ available = sorted({cpu['cpu'] for cpu in get_available_cpus()})
- return int(cpu_settings.get('workers', 0))
+ if reserved_cpus < len(available):
+ main_core = available[reserved_cpus] # first non-reserved CPU
+ config['settings']['cpu'] = {
+ 'main_core': str(main_core),
+ 'skip_cores': str(reserved_cpus),
+ }
+ if cpu_cores > 1:
+ config['settings']['cpu']['workers'] = str(cpu_cores - 1)
def _normalize_buffers(config: dict):
"""Replace 'auto' buffers_per_numa with calculated value"""
if config['settings']['buffers']['buffers_per_numa'] == 'auto':
- workers = _get_workers_count(config['settings'].get('cpu', {}))
- buffers = memory.buffers_required(config['settings'], workers)
+ buffers = memory.buffers_required(config['settings'])
config['settings']['buffers']['buffers_per_numa'] = str(buffers)
@@ -398,15 +399,18 @@ def get_config(config=None):
)
if 'zero-copy' in iface_config['xdp_options']:
xdp_api_params['mode'] = 'zero-copy'
- if iface_config.get('rx_mode') in ('interrupt', 'adaptive') and any(
- key in config['settings'].get('cpu', {})
- for key in ('workers', 'corelist_workers')
+ if (
+ iface_config.get('rx_mode') in ('interrupt', 'adaptive')
+ and int(config['settings']['resource_allocation']['cpu_cores'])
+ > 1
):
xdp_api_params['flags'] = 'no_syscall_lock'
iface_config['xdp_api_params'] = xdp_api_params
# Buffer normalization (auto → computed)
_normalize_buffers(config)
+ # Configure VPP main-core and workers 'cpu-cores' settings
+ _configure_vpp_cpu_settings(config)
if removed_ifaces:
config['removed_ifaces'] = removed_ifaces
@@ -507,38 +511,15 @@ def verify(config):
raise ConfigError(f'Interface {iface} does not exist or is not Ethernet!')
# Resource usage checks
- cpu_settings = config['settings'].get('cpu', {})
-
- if not any(key in cpu_settings for key in ('workers', 'corelist_workers')):
- verify_vpp_minimum_cpus()
-
- if 'cpu' in config['settings']:
- # Check whether the workers and corelist-workers are configured properly
- verify_vpp_settings_cpu(cpu_settings)
-
- # Check if there are enough CPU cores to skip according to config
- if 'skip_cores' in cpu_settings:
- skip_cores = int(cpu_settings['skip_cores'])
- verify_vpp_settings_cpu_skip_cores(skip_cores)
-
- # Check if there are enough CPU cores to add workers
- if 'workers' in cpu_settings:
- verify_vpp_settings_cpu_workers(cpu_settings)
-
- if 'main_core' in cpu_settings:
- verify_vpp_cpu_main_core(cpu_settings)
-
- # Check the CPU main core not falling to the corelist-workers
- if 'corelist_workers' in cpu_settings:
- verify_vpp_settings_cpu_corelist_workers(cpu_settings)
-
- workers = _get_workers_count(config['settings'].get('cpu', {}))
+ cpu_cores = int(config['settings']['resource_allocation']['cpu_cores'])
+ verify_vpp_minimum_cpus()
+ verify_vpp_cpu_cores(cpu_cores)
verify_vpp_main_heap_size(config['settings'])
verify_vpp_statseg_size(config['settings'])
# Check buffers
- verify_vpp_buffers(config['settings'], workers)
+ verify_vpp_buffers(config['settings'])
# Check if available memory is enough for current VPP config
verify_vpp_memory(config)
@@ -579,13 +560,13 @@ def verify(config):
if 'num_rx_queues' in iface_config['dpdk_options']:
rx_queues = int(iface_config['dpdk_options']['num_rx_queues'])
verify_vpp_interfaces_dpdk_num_queues(
- qtype='receive', num_queues=rx_queues, workers=workers
+ qtype='receive', num_queues=rx_queues, workers=cpu_cores
)
if 'num_tx_queues' in iface_config['dpdk_options']:
tx_queues = int(iface_config['dpdk_options']['num_tx_queues'])
verify_vpp_interfaces_dpdk_num_queues(
- qtype='transmit', num_queues=tx_queues, workers=workers
+ qtype='transmit', num_queues=tx_queues, workers=cpu_cores
)
# RX-mode verification
@@ -652,7 +633,7 @@ def verify(config):
f'Interface {iface_config["iface_name"]} is an xconnect member and cannot be removed'
)
- verify_routes_count(config['settings'], workers)
+ verify_routes_count(config['settings'])
def generate(config):
diff --git a/src/migration-scripts/vpp/7-to-8 b/src/migration-scripts/vpp/7-to-8
index fa105a6b5..0b2f81b4c 100644
--- a/src/migration-scripts/vpp/7-to-8
+++ b/src/migration-scripts/vpp/7-to-8
@@ -20,6 +20,8 @@
# Drop settings for nat workers (T8254)
#
# Delete 'ipsec' node and all settings and replace it with single 'ipsec-acceleration' flag (T8262)
+#
+# Unify CPU settings into a single 'cpu-cores' node under 'resource-allocation' (T8268)
from vyos.configtree import ConfigTree
@@ -71,6 +73,38 @@ def _migrate_vpp_ipsec(config: ConfigTree) -> None:
config.set(base + ['ipsec-acceleration'])
config.delete(base + ['ipsec'])
+def _migrate_vpp_cpu(config: ConfigTree) -> None:
+ settings_path = ['vpp', 'settings']
+ cpu_path = settings_path + ['cpu']
+
+ if not config.exists(cpu_path):
+ # Nothing to do
+ return
+
+ # get number of configured workers
+ workers = 0
+
+ if config.exists(cpu_path + ['workers']):
+ workers += int(config.return_value(cpu_path + ['workers']))
+ # add main-core to total workers
+ workers += 1
+
+ if config.exists(cpu_path + ['corelist-workers']):
+ def _count_range(item: str) -> int:
+ if '-' in item:
+ start, end = map(int, item.split('-'))
+ return end - start + 1
+ return 1
+
+ tmp = config.return_values(cpu_path + ['corelist-workers'])
+ workers = sum(_count_range(item) for item in tmp) + 1 # + main core
+
+ # set 'resource-allocation cpu-cores'
+ if workers:
+ config.set(settings_path + ['resource-allocation', 'cpu-cores'], value=str(workers))
+
+ config.delete(cpu_path)
+
def migrate(config: ConfigTree) -> None:
if not config.exists(['vpp']):
@@ -81,3 +115,4 @@ def migrate(config: ConfigTree) -> None:
_migrate_vpp_log(config)
_migrate_vpp_nat44(config)
_migrate_vpp_ipsec(config)
+ _migrate_vpp_cpu(config)