summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNataliia Solomko <natalirs1985@gmail.com>2026-02-16 16:53:29 +0200
committerNataliia Solomko <natalirs1985@gmail.com>2026-02-17 11:33:56 +0200
commit2414c156cc21bfe51b65f8726f186c55b533bb97 (patch)
tree815f9d77ce7ade89371695252aeb1d7af5580890
parent82461ce7856aaf91d1b8a85ca45080e26783fc5e (diff)
downloadvyos-1x-2414c156cc21bfe51b65f8726f186c55b533bb97.tar.gz
vyos-1x-2414c156cc21bfe51b65f8726f186c55b533bb97.zip
vpp: T8268: Unify CPU settings into a single 'cpu-cores' node under 'resource-allocation'
Replace legacy VPP CPU settings (main-core, skip-cores, workers, corelist-workers) with a single resource-allocation cpu-cores option. CPU assignment is now handled automatically: two cores are reserved for the system, the VPP main thread is placed on the first available core, and the remaining allocated cores are used as workers. This simplifies configuration and ensures consistent CPU 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)