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 /python | |
| 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 'python')
| -rw-r--r-- | python/vyos/utils/convert.py | 53 | ||||
| -rw-r--r-- | python/vyos/vpp/config_verify.py | 73 |
2 files changed, 106 insertions, 20 deletions
diff --git a/python/vyos/utils/convert.py b/python/vyos/utils/convert.py index ea07f9514..bdc9fd549 100644 --- a/python/vyos/utils/convert.py +++ b/python/vyos/utils/convert.py @@ -261,3 +261,56 @@ def encode_to_base64(input_string): # Decode the base64 bytes back to a string return encoded_string.decode('utf-8') + + +def range_str_to_list(data: str) -> list[int]: + """ + Convert a string of ranges to a sorted list of unique integers + + Args: + data (str): Comma-separated ranges, e.g. '1-3,5,7-9' + + Returns: + Sorted list of unique integers, e.g. ``[1, 2, 3, 5, 7, 8, 9]``. + """ + if not data: + return [] + result = [] + for part in data.split(','): + if '-' in part: + start, end = part.split('-') + result.extend(range(int(start), int(end) + 1)) + else: + result.append(int(part)) + return sorted(set(result)) + + +def list_to_range_str(nums: list[int]) -> str: + """ + Convert a list of integers to a compact string of ranges + + Args: + nums: List of integers, e.g. [1, 2, 3, 5, 7, 8, 9]. + Duplicates are removed and the list is sorted internally. + + Returns: + Compact range string, e.g. '1-3,5,7-9'. + Returns '' for an empty input. + """ + nums = sorted(set(nums)) + if not nums: + return '' + + ranges = [] + start = end = nums[0] + + for n in nums[1:]: + if n == end + 1: + end = n + else: + ranges.append(str(start) if start == end else f'{start}-{end}') + start = end = n + + ranges.append(str(start) if start == end else f'{start}-{end}') + + return ','.join(ranges) diff --git a/python/vyos/vpp/config_verify.py b/python/vyos/vpp/config_verify.py index da8e3d24b..228fcbc09 100644 --- a/python/vyos/vpp/config_verify.py +++ b/python/vyos/vpp/config_verify.py @@ -20,8 +20,10 @@ import psutil from vyos import ConfigError from vyos.base import Warning +from vyos.utils.convert import range_str_to_list from vyos.utils.cpu import get_core_count as total_core_count, get_cpus from vyos.utils.dict import dict_search +from vyos.utils.file import read_file from vyos.vpp.config_resource_checks import memory as mem_checks from vyos.vpp.config_resource_checks.resource_defaults import default_resource_map @@ -54,6 +56,9 @@ _VPP_MEMBER_INTERFACE_REFS = [ _VPP_INTERFACE_REFS = _VPP_FEATURE_INTERFACE_REFS + _VPP_MEMBER_INTERFACE_REFS +# Line width used by ConfigError for message formatting +LINE_WIDTH = 72 + def vpp_interface_in_use( iface: str, config: dict, match_vlans: bool = False, refs: list = None @@ -217,12 +222,12 @@ def create_cpu_error_message(cpus_required: int, cpus_available: int = None) -> available_str = ( ( - '---'.ljust(72) - + 'Reserved:'.ljust(72) - + f'For system: {reserved_cpus}'.ljust(72) - + f'VPP main thread: 1'.ljust(72) - + '---'.ljust(72) - + 'Available:'.ljust(72) + '---'.ljust(LINE_WIDTH) + + 'Reserved:'.ljust(LINE_WIDTH) + + f'For system: {reserved_cpus}'.ljust(LINE_WIDTH) + + f'VPP main thread: 1'.ljust(LINE_WIDTH) + + '---'.ljust(LINE_WIDTH) + + 'Available:'.ljust(LINE_WIDTH) + f'Physical cores: {max(cpus_available, 0)}' ) if cpus_available is not None @@ -230,13 +235,13 @@ def create_cpu_error_message(cpus_required: int, cpus_available: int = None) -> ) message = ( - '---'.ljust(72) - + 'Total in the system:'.ljust(72) - + f'Physical cores: {total_core_count()}'.ljust(72) - + f'Logical cores: {logical_cores}'.ljust(72) - + '---'.ljust(72) - + 'Required:'.ljust(72) - + f'Physical cores: {cpus_required}'.ljust(72) + '---'.ljust(LINE_WIDTH) + + 'Total in the system:'.ljust(LINE_WIDTH) + + f'Physical cores: {total_core_count()}'.ljust(LINE_WIDTH) + + f'Logical cores: {logical_cores}'.ljust(LINE_WIDTH) + + '---'.ljust(LINE_WIDTH) + + 'Required:'.ljust(LINE_WIDTH) + + f'Physical cores: {cpus_required}'.ljust(LINE_WIDTH) + available_str ) @@ -251,7 +256,7 @@ def verify_vpp_minimum_cpus(): min_cpus = default_resource_map.get('min_cpus') if total_core_count() < min_cpus: raise ConfigError( - 'This system does not meet minimal requirements for VPP. '.ljust(72) + 'This system does not meet minimal requirements for VPP. '.ljust(LINE_WIDTH) + create_cpu_error_message(min_cpus) ) @@ -329,10 +334,10 @@ def verify_vpp_memory(config: dict): if errors: raise ConfigError( - 'Not enough free memory to start VPP! '.ljust(72) - + '. '.join([line.ljust(72) for line in errors.values()]) + 'Not enough free memory to start VPP! '.ljust(LINE_WIDTH) + + '. '.join([line.ljust(LINE_WIDTH) for line in errors.values()]) + ( - 'To add HugePages memory please use command '.ljust(72) + 'To add HugePages memory please use command '.ljust(LINE_WIDTH) + '"set system option kernel memory hugepage-size ..." and reboot!' if any(k in errors for k in ('2M', '1G')) else '' @@ -342,8 +347,19 @@ def verify_vpp_memory(config: 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) + Verify that the system has enough available and isolated CPU cores. + + Checks performed: + 1. The host has enough physical cores (minus reserved) for the requested + cpu_cores count. + 2. The kernel actually has at least cpu_cores isolated CPUs + (read from /sys/devices/system/cpu/isolated). + + Args: + cpu_cores: Total number of VPP CPU cores (1 main + N-1 workers). + + Raises: + ConfigError: When any of the checks fail. """ total_cores = total_core_count() reserved_cpus = default_resource_map.get('reserved_cpu_cores') @@ -351,10 +367,27 @@ def verify_vpp_cpu_cores(cpu_cores: int): if cpu_cores > available_cores: raise ConfigError( - f'Not enough free physical CPU cores for {cpu_cores} "cpu-cores" '.ljust(72) + f'Not enough free physical CPU cores for {cpu_cores} "cpu-cores" '.ljust( + LINE_WIDTH + ) + create_cpu_error_message(cpu_cores, available_cores) ) + # Read the set of CPUs the running kernel has actually isolated + isolated = read_file('/sys/devices/system/cpu/isolated') + isolated_cpus = range_str_to_list(isolated) + + if cpu_cores > len(isolated_cpus): + raise ConfigError( + 'Not enough isolated CPU cores available: '.ljust(LINE_WIDTH) + + f'{cpu_cores} requested, but only {len(isolated_cpus)} isolated' + f'{f" (CPU#{isolated})" if len(isolated_cpus) > 0 else ""}. '.ljust( + LINE_WIDTH + ) + + 'To isolate CPUs please use command '.ljust(LINE_WIDTH) + + '"set system option kernel cpu isolate-cpus ..." save and reboot!' + ) + def verify_vpp_statseg_size(settings: dict): statseg_size = mem_checks.statseg_size(settings) |
