diff options
| author | Nataliia Solomko <natalirs1985@gmail.com> | 2026-04-14 18:07:41 +0300 |
|---|---|---|
| committer | Nataliia Solomko <natalirs1985@gmail.com> | 2026-04-21 16:45:43 +0300 |
| commit | 9cb6f4c001fa5d4f4ed26850fc99dde4c3692c8f (patch) | |
| tree | 5309c08fd789a8560426c1d45e45050c1ad8ac22 /src | |
| parent | d9c7cf3bc0e76661f48bd0ce13acfc7be182a326 (diff) | |
| download | vyos-1x-9cb6f4c001fa5d4f4ed26850fc99dde4c3692c8f.tar.gz vyos-1x-9cb6f4c001fa5d4f4ed26850fc99dde4c3692c8f.zip | |
vpp: T8460: Use isolated cpus for VPP cpu-cores
VPP CPU core assignment now uses kernel-isolated CPUs (from /sys/devices/system/cpu/isolated) with explicit corelist-workers instead of computing offsets from available cores with skip-cores/workers. Added validation that enough CPUs are actually isolated before VPP starts, and that isolate-cpus config only references existing CPU IDs. Moved smoketest for kernel option 'isolate-cpus' to test_vpp.py
Diffstat (limited to 'src')
| -rwxr-xr-x | src/conf_mode/system_option.py | 27 | ||||
| -rwxr-xr-x | src/conf_mode/vpp.py | 33 | ||||
| -rw-r--r-- | src/tests/test_utils.py | 38 |
3 files changed, 85 insertions, 13 deletions
diff --git a/src/conf_mode/system_option.py b/src/conf_mode/system_option.py index 41b9a23bb..b7487de8a 100755 --- a/src/conf_mode/system_option.py +++ b/src/conf_mode/system_option.py @@ -28,7 +28,10 @@ from vyos.configverify import verify_interface_exists from vyos.system import grub_util from vyos.template import render from vyos.utils.boot import boot_configuration_complete +from vyos.utils.convert import range_str_to_list +from vyos.utils.convert import list_to_range_str from vyos.utils.cpu import get_cpus +from vyos.utils.cpu import get_available_cpus from vyos.utils.dict import dict_search from vyos.utils.file import write_file from vyos.utils.file import read_file @@ -44,6 +47,7 @@ from vyos import ConfigError from vyos import airbag from vyos.vpp.config_resource_checks import memory as mem_check +from vyos.vpp.config_resource_checks.resource_defaults import default_resource_map airbag.enable() @@ -239,6 +243,29 @@ def verify(options): f'AMD pstate driver cannot be used with "{cpu_vendor}" CPU!' ) + isolate_cpus = dict_search('kernel.cpu.isolate_cpus', options) + if isolate_cpus: + available_cores = sorted({int(cpu['cpu']) for cpu in get_available_cpus()}) + cpus_list = range_str_to_list(isolate_cpus) + reserved_cpus = default_resource_map.get('reserved_cpu_cores') + + cpus_available = len(available_cores) - reserved_cpus + if len(cpus_list) > cpus_available: + raise ConfigError( + f'Cannot isolate {len(cpus_list)} CPUs ({isolate_cpus}): ' + f'only {cpus_available} of {len(available_cores)} physical cores ' + f'are available ({reserved_cpus} reserved for the system)' + ) + + not_available = [cpu for cpu in cpus_list if cpu not in available_cores] + if not_available: + not_available_str = list_to_range_str(not_available) + available_str = list_to_range_str(available_cores) + raise ConfigError( + f'CPU(s) {not_available_str} do not exist on this system. ' + f'Available CPUs: {available_str}' + ) + _, hp_memory_bytes = _get_total_hugepages_and_memory( options['kernel'].get('memory', {}) ) diff --git a/src/conf_mode/vpp.py b/src/conf_mode/vpp.py index 33a4023af..342d58fca 100755 --- a/src/conf_mode/vpp.py +++ b/src/conf_mode/vpp.py @@ -38,8 +38,10 @@ 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.convert import range_str_to_list +from vyos.utils.convert import list_to_range_str from vyos.utils.dict import dict_search +from vyos.utils.file import read_file from vyos.utils.kernel import check_kmod from vyos.utils.kernel import unload_kmod from vyos.utils.kernel import list_loaded_modules @@ -62,7 +64,6 @@ 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.utils import cli_ethernet_with_vifs_ifaces @@ -168,21 +169,27 @@ def _unload_module(module_name: str): def _configure_vpp_cpu_settings(config: dict): - """Configure VPP CPU settings: main-core, workers and skip-cores based on 'cpu-cores'""" + """Configure VPP CPU settings: main-core and corelist-workers based on 'cpu-cores'. + + Reads the actually-isolated CPUs from the running kernel + (/sys/devices/system/cpu/isolated) and assigns: + - main_core: the first isolated CPU (index 0) + - corelist_workers: the next (cpu_cores - 1) isolated CPUs + """ cpu_cores = int(config['settings']['resource_allocation']['cpu_cores']) - reserved_cpus = default_resource_map.get('reserved_cpu_cores') + # Use the system's actual isolated CPUs, not config values which may + # require a reboot to take effect + isolated = read_file('/sys/devices/system/cpu/isolated') + cpus_isolated = range_str_to_list(isolated) - # Get sorted list of available CPU IDs - available = sorted({cpu['cpu'] for cpu in get_available_cpus()}) + if cpu_cores <= len(cpus_isolated): + # First isolated CPU is the VPP main thread; remaining are workers + config['settings']['cpu'] = {'main_core': str(cpus_isolated[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) + config['settings']['cpu']['corelist_workers'] = list_to_range_str( + cpus_isolated[1:cpu_cores] + ) def _normalize_buffers(config: dict): diff --git a/src/tests/test_utils.py b/src/tests/test_utils.py index 9cf6f7a1e..5712836c4 100644 --- a/src/tests/test_utils.py +++ b/src/tests/test_utils.py @@ -37,3 +37,41 @@ class TestVyOSUtils(TestCase): self.assertEqual(list_strip(lst, rsb, right=True), ['a', 'b', 'c']) self.assertEqual(list_strip(lst, non), []) self.assertEqual(list_strip(sub, lst), []) + + def test_range_str_to_list(self): + from vyos.utils.convert import range_str_to_list + + # basic cases + self.assertEqual(range_str_to_list('1-3'), [1, 2, 3]) + self.assertEqual(range_str_to_list('1-3,5,7-8'), [1, 2, 3, 5, 7, 8]) + self.assertEqual(range_str_to_list('3'), [3]) + # empty string + self.assertEqual(range_str_to_list(''), []) + # unordered input + self.assertEqual(range_str_to_list('5,1-3,4'), [1, 2, 3, 4, 5]) + self.assertEqual(range_str_to_list('7-9,1-3'), [1, 2, 3, 7, 8, 9]) + # overlapping ranges + self.assertEqual(range_str_to_list('1-5,3-7'), [1, 2, 3, 4, 5, 6, 7]) + self.assertEqual(range_str_to_list('1-3,2-4,3-5'), [1, 2, 3, 4, 5]) + # duplicated values + self.assertEqual(range_str_to_list('1,1,2,2,3'), [1, 2, 3]) + self.assertEqual(range_str_to_list('5,1-3,2,3'), [1, 2, 3, 5]) + # adjacent ranges + self.assertEqual(range_str_to_list('1-3,4-6'), [1, 2, 3, 4, 5, 6]) + + def test_list_to_range_str(self): + from vyos.utils.convert import list_to_range_str + + # basic cases + self.assertEqual(list_to_range_str([1, 2, 3]), '1-3') + self.assertEqual(list_to_range_str([1, 2, 3, 5, 7, 8]), '1-3,5,7-8') + self.assertEqual(list_to_range_str([1, 3]), '1,3') + self.assertEqual(list_to_range_str([3]), '3') + # empty list + self.assertEqual(list_to_range_str([]), '') + # unordered input + self.assertEqual(list_to_range_str([5, 1, 2, 3, 4]), '1-5') + self.assertEqual(list_to_range_str([7, 8, 9, 1, 2, 3]), '1-3,7-9') + # duplicated values + self.assertEqual(list_to_range_str([1, 1, 2, 2, 3, 3]), '1-3') + self.assertEqual(list_to_range_str([5, 1, 2, 2, 3, 5]), '1-3,5') |
