summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/conf_mode/system_option.py27
-rwxr-xr-xsrc/conf_mode/vpp.py33
-rw-r--r--src/tests/test_utils.py38
3 files changed, 85 insertions, 13 deletions
diff --git a/src/conf_mode/system_option.py b/src/conf_mode/system_option.py
index ac6e7768d..003e8c9f5 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 bbf1cb523..5022b24f6 100644
--- a/src/tests/test_utils.py
+++ b/src/tests/test_utils.py
@@ -48,3 +48,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')