summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/templates/vpp/sysctl.conf.j215
-rw-r--r--python/vyos/utils/system.py82
-rwxr-xr-xsrc/conf_mode/vpp.py49
3 files changed, 112 insertions, 34 deletions
diff --git a/data/templates/vpp/sysctl.conf.j2 b/data/templates/vpp/sysctl.conf.j2
deleted file mode 100644
index 2207e2e38..000000000
--- a/data/templates/vpp/sysctl.conf.j2
+++ /dev/null
@@ -1,15 +0,0 @@
-# Number of 2MB hugepages desired
-vm.nr_hugepages=1024
-
-# Must be greater than or equal to (2 * vm.nr_hugepages).
-vm.max_map_count=3096
-
-# All groups allowed to access hugepages
-vm.hugetlb_shm_group=0
-
-# Shared Memory Max must be greater or equal to the total size of hugepages.
-# For 2MB pages, TotalHugepageSize = vm.nr_hugepages * 2 * 1024 * 1024
-# If the existing kernel.shmmax setting (cat /proc/sys/kernel/shmmax)
-# is greater than the calculated TotalHugepageSize then set this parameter
-# to current shmmax value.
-kernel.shmmax=2147483648
diff --git a/python/vyos/utils/system.py b/python/vyos/utils/system.py
new file mode 100644
index 000000000..7102d5985
--- /dev/null
+++ b/python/vyos/utils/system.py
@@ -0,0 +1,82 @@
+# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+from subprocess import run
+
+
+def sysctl_read(name: str) -> str:
+ """Read and return current value of sysctl() option
+
+ Args:
+ name (str): sysctl key name
+
+ Returns:
+ str: sysctl key value
+ """
+ tmp = run(['sysctl', '-nb', name], capture_output=True)
+ return tmp.stdout.decode()
+
+
+def sysctl_write(name: str, value: str | int) -> bool:
+ """Change value via sysctl()
+
+ Args:
+ name (str): sysctl key name
+ value (str | int): sysctl key value
+
+ Returns:
+ bool: True if changed, False otherwise
+ """
+ # convert other types to string before comparison
+ if not isinstance(value, str):
+ value = str(value)
+ # do not change anything if a value is already configured
+ if sysctl_read(name) == value:
+ return True
+ # return False if sysctl call failed
+ if run(['sysctl', '-wq', f'{name}={value}']).returncode != 0:
+ return False
+ # compare old and new values
+ # sysctl may apply value, but its actual value will be
+ # different from requested
+ if sysctl_read(name) == value:
+ return True
+ # False in other cases
+ return False
+
+
+def sysctl_apply(sysctl_dict: dict[str, str], revert: bool = True) -> bool:
+ """Apply sysctl values.
+
+ Args:
+ sysctl_dict (dict[str, str]): dictionary with sysctl keys with values
+ revert (bool, optional): Revert to original values if new were not
+ applied. Defaults to True.
+
+ Returns:
+ bool: True if all params configured properly, False in other cases
+ """
+ # get current values
+ sysctl_original: dict[str, str] = {}
+ for key_name in sysctl_dict.keys():
+ sysctl_original[key_name] = sysctl_read(key_name)
+ # apply new values and revert in case one of them was not applied
+ for key_name, value in sysctl_dict.items():
+ if not sysctl_write(key_name, value):
+ if revert:
+ sysctl_apply(sysctl_original, revert=False)
+ return False
+ # everything applied
+ return True
diff --git a/src/conf_mode/vpp.py b/src/conf_mode/vpp.py
index dc13f4e60..87ebc3ea9 100755
--- a/src/conf_mode/vpp.py
+++ b/src/conf_mode/vpp.py
@@ -15,7 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
-import psutil
+from psutil import virtual_memory
from pathlib import Path
from re import search as re_search, MULTILINE as re_M
@@ -26,6 +26,7 @@ from vyos.configdict import dict_merge
from vyos.configdict import node_changed
from vyos.ifconfig import Section
from vyos.util import call, rc_cmd, boot_configuration_complete
+from vyos.utils.system import sysctl_read, sysctl_apply
from vyos.template import render
from vyos.xml import defaults
@@ -39,10 +40,10 @@ airbag.enable()
service_name = 'vpp'
service_conf = Path(f'/run/vpp/{service_name}.conf')
systemd_override = '/run/systemd/system/vpp.service.d/10-override.conf'
-sysctl_vpp = '/etc/sysctl.d/80-vpp.conf'
-# Min memory 6GB (2GB reserved for vpp)
-MIN_TOTAL_MEMORY = 6
+# Free memory required for VPP
+# 2 GB for hugepages + 1 GB for other services
+MIN_AVAILABLE_MEMORY: int = 3 * 1024**3
def _get_pci_address_by_interface(iface) -> str:
@@ -64,7 +65,6 @@ def _get_pci_address_by_interface(iface) -> str:
raise ConfigError(f'Cannot find PCI address for interface {iface}')
-
def get_config(config=None):
if config:
conf = config
@@ -131,32 +131,45 @@ def verify(config):
return None
if 'interface' not in config:
- raise ConfigError(f'"interface" is required but not set!')
+ raise ConfigError('"interface" is required but not set!')
if 'cpu' in config:
- if 'corelist_workers' in config['cpu'] and 'main_core' not in config['cpu']:
- raise ConfigError(f'"cpu main-core" is required but not set!')
+ if 'corelist_workers' in config['cpu'] and 'main_core' not in config[
+ 'cpu']:
+ raise ConfigError('"cpu main-core" is required but not set!')
- memory = psutil.virtual_memory()
- memory_total = round(memory.total / (1024 ** 3), 2)
- if memory_total < MIN_TOTAL_MEMORY:
+ memory_available: int = virtual_memory().available
+ if memory_available < MIN_AVAILABLE_MEMORY:
raise ConfigError(
- f'Not enough installed memory {memory_total}GB! '
- f'The minimum required memory is {MIN_TOTAL_MEMORY}GB.'
- )
+ 'Not enough free memory to start VPP:\n'
+ f'available: {round(memory_available / 1024**3, 1)}GB\n'
+ f'required: {round(MIN_AVAILABLE_MEMORY / 1024**3, 1)}GB')
def generate(config):
if not config or (len(config) == 1 and 'removed_ifaces' in config):
# Remove old config and return
service_conf.unlink(missing_ok=True)
- if os.path.isfile(sysctl_vpp):
- os.unlink(sysctl_vpp)
return None
render(service_conf, 'vpp/startup.conf.j2', config)
render(systemd_override, 'vpp/override.conf.j2', config)
- render(sysctl_vpp, 'vpp/sysctl.conf.j2', config)
+
+ # apply default sysctl values from
+ # https://github.com/FDio/vpp/blob/v23.06/src/vpp/conf/80-vpp.conf
+ sysctl_config: dict[str, str] = {
+ 'vm.nr_hugepages': '1024',
+ 'vm.max_map_count': '3096',
+ 'vm.hugetlb_shm_group': '0',
+ 'kernel.shmmax': '2147483648'
+ }
+ # we do not want to reduce `kernel.shmmax`
+ kernel_shmnax_current: str = sysctl_read('kernel.shmmax')
+ if int(kernel_shmnax_current) > int(sysctl_config['kernel.shmmax']):
+ sysctl_config['kernel.shmmax'] = kernel_shmnax_current
+
+ if not sysctl_apply(sysctl_config):
+ raise ConfigError('Cannot configure sysctl parameters for VPP')
return None
@@ -168,8 +181,6 @@ def apply(config):
call('systemctl daemon-reload')
call(f'systemctl restart {service_name}.service')
- call(f'sysctl -qp {sysctl_vpp}')
-
# Initialize interfaces removed from VPP
for iface in config.get('removed_ifaces', []):
host_control = HostControl()