Compare commits

..

No commits in common. "c8" and "c10s" have entirely different histories.
c8 ... c10s

17 changed files with 837 additions and 747 deletions

1
.fmf/version Normal file
View File

@ -0,0 +1 @@
1

2
.gitignore vendored
View File

@ -1 +1 @@
SOURCES/tuned-2.22.1.tar.gz
/tuned-*.tar.gz

View File

@ -1 +0,0 @@
60e206fe73ea537e64141f92b331f65879766f97 SOURCES/tuned-2.22.1.tar.gz

View File

@ -1,80 +0,0 @@
diff --git a/profiles/cpu-partitioning/script.sh b/profiles/cpu-partitioning/script.sh
index ec422ca..cb378b7 100755
--- a/profiles/cpu-partitioning/script.sh
+++ b/profiles/cpu-partitioning/script.sh
@@ -2,6 +2,38 @@
. /usr/lib/tuned/functions
+no_balance_cpus_file=$STORAGE/no-balance-cpus.txt
+
+change_sd_balance_bit()
+{
+ local set_bit=$1
+ local flags_cur=
+ local file=
+ local cpu=
+
+ for cpu in $(cat $no_balance_cpus_file); do
+ for file in $(find /proc/sys/kernel/sched_domain/cpu$cpu -name flags -print); do
+ flags_cur=$(cat $file)
+ if [ $set_bit -eq 1 ]; then
+ flags_cur=$((flags_cur | 0x1))
+ else
+ flags_cur=$((flags_cur & 0xfffe))
+ fi
+ echo $flags_cur > $file
+ done
+ done
+}
+
+disable_balance_domains()
+{
+ change_sd_balance_bit 0
+}
+
+enable_balance_domains()
+{
+ change_sd_balance_bit 1
+}
+
start() {
mkdir -p "${TUNED_tmpdir}/etc/systemd"
mkdir -p "${TUNED_tmpdir}/usr/lib/dracut/hooks/pre-udev"
@@ -9,6 +41,9 @@ start() {
cp 00-tuned-pre-udev.sh "${TUNED_tmpdir}/usr/lib/dracut/hooks/pre-udev/"
setup_kvm_mod_low_latency
disable_ksm
+
+ echo "$TUNED_no_balance_cores_expanded" | sed 's/,/ /g' > $no_balance_cpus_file
+ disable_balance_domains
return "$?"
}
@@ -18,6 +53,7 @@ stop() {
teardown_kvm_mod_low_latency
enable_ksm
fi
+ enable_balance_domains
return "$?"
}
diff --git a/profiles/cpu-partitioning/tuned.conf b/profiles/cpu-partitioning/tuned.conf
index 11f03cf..a682c9c 100644
--- a/profiles/cpu-partitioning/tuned.conf
+++ b/profiles/cpu-partitioning/tuned.conf
@@ -35,8 +35,6 @@ no_balance_cores_expanded=${f:cpulist_unpack:${no_balance_cores}}
# Fail if isolated_cores contains CPUs which are not online
assert2=${f:assertion:isolated_cores contains online CPU(s):${isolated_cores_expanded}:${isolated_cores_online_expanded}}
-cmd_isolcpus=${f:regex_search_ternary:${no_balance_cores}:\s*[0-9]: isolcpus=${no_balance_cores}:}
-
[sysfs]
/sys/bus/workqueue/devices/writeback/cpumask = ${not_isolated_cpumask}
/sys/devices/virtual/workqueue/cpumask = ${not_isolated_cpumask}
@@ -62,4 +60,4 @@ priority=10
initrd_remove_dir=True
initrd_dst_img=tuned-initrd.img
initrd_add_dir=${tmpdir}
-cmdline_cpu_part=+nohz=on${cmd_isolcpus} nohz_full=${isolated_cores} rcu_nocbs=${isolated_cores} tuned.non_isolcpus=${not_isolated_cpumask} intel_pstate=disable nosoftlockup
+cmdline_cpu_part=+nohz=on nohz_full=${isolated_cores} rcu_nocbs=${isolated_cores} tuned.non_isolcpus=${not_isolated_cpumask} intel_pstate=disable nosoftlockup

View File

@ -1,118 +0,0 @@
diff --git a/tuned/consts.py b/tuned/consts.py
index 3749363..3b41ed9 100644
--- a/tuned/consts.py
+++ b/tuned/consts.py
@@ -1,4 +1,8 @@
import logging
+import string
+
+NAMES_ALLOWED_CHARS = string.ascii_letters + string.digits + " !@'+-.,/:;_$&*()%<=>?#[]{|}^~" + '"'
+NAMES_MAX_LENGTH = 4096
GLOBAL_CONFIG_FILE = "/etc/tuned/tuned-main.conf"
ACTIVE_PROFILE_FILE = "/etc/tuned/active_profile"
diff --git a/tuned/daemon/controller.py b/tuned/daemon/controller.py
index 6a59a1d..94e9022 100644
--- a/tuned/daemon/controller.py
+++ b/tuned/daemon/controller.py
@@ -182,6 +182,8 @@ class Controller(tuned.exports.interfaces.ExportableInterface):
def switch_profile(self, profile_name, caller = None):
if caller == "":
return (False, "Unauthorized")
+ if not self._cmd.is_valid_name(profile_name):
+ return (False, "Invalid profile_name")
return self._switch_profile(profile_name, True)
@exports.export("", "(bs)")
@@ -255,8 +257,8 @@ class Controller(tuned.exports.interfaces.ExportableInterface):
@exports.export("s", "(bsss)")
def profile_info(self, profile_name, caller = None):
- if caller == "":
- return tuple(False, "", "", "")
+ if caller == "" or not self._cmd.is_valid_name(profile_name):
+ return (False, "", "", "")
if profile_name is None or profile_name == "":
profile_name = self.active_profile()
return tuple(self._daemon.profile_loader.profile_locator.get_profile_attrs(profile_name, [consts.PROFILE_ATTR_SUMMARY, consts.PROFILE_ATTR_DESCRIPTION], [""]))
@@ -287,7 +289,7 @@ class Controller(tuned.exports.interfaces.ExportableInterface):
dictionary -- {plugin_name: {parameter_name: default_value}}
"""
if caller == "":
- return False
+ return {}
plugins = {}
for plugin_class in self._daemon.get_all_plugins():
plugin_name = plugin_class.__module__.split(".")[-1].split("_", 1)[1]
@@ -300,8 +302,8 @@ class Controller(tuned.exports.interfaces.ExportableInterface):
@exports.export("s","s")
def get_plugin_documentation(self, plugin_name, caller = None):
"""Return docstring of plugin's class"""
- if caller == "":
- return False
+ if caller == "" or not self._cmd.is_valid_name(plugin_name):
+ return ""
return self._daemon.get_plugin_documentation(str(plugin_name))
@exports.export("s","a{ss}")
@@ -314,8 +316,8 @@ class Controller(tuned.exports.interfaces.ExportableInterface):
Return:
dictionary -- {parameter_name: hint}
"""
- if caller == "":
- return False
+ if caller == "" or not self._cmd.is_valid_name(plugin_name):
+ return {}
return self._daemon.get_plugin_hints(str(plugin_name))
@exports.export("s", "b")
@@ -328,7 +330,7 @@ class Controller(tuned.exports.interfaces.ExportableInterface):
Return:
bool -- True on success
"""
- if caller == "":
+ if caller == "" or not self._cmd.is_valid_name(path):
return False
if self._daemon._application and self._daemon._application._unix_socket_exporter:
self._daemon._application._unix_socket_exporter.register_signal_path(path)
@@ -342,6 +344,10 @@ class Controller(tuned.exports.interfaces.ExportableInterface):
def instance_acquire_devices(self, devices, instance_name, caller = None):
if caller == "":
return (False, "Unauthorized")
+ if not self._cmd.is_valid_name(devices):
+ return (False, "Invalid devices")
+ if not self._cmd.is_valid_name(instance_name):
+ return (False, "Invalid instance_name")
found = False
for instance_target in self._daemon._unit_manager.instances:
if instance_target.name == instance_name:
@@ -388,6 +394,8 @@ class Controller(tuned.exports.interfaces.ExportableInterface):
"""
if caller == "":
return (False, "Unauthorized", [])
+ if not self._cmd.is_valid_name(plugin_name):
+ return (False, "Invalid plugin_name", [])
if plugin_name != "" and plugin_name not in self.get_all_plugins().keys():
rets = "Plugin '%s' does not exist" % plugin_name
log.error(rets)
@@ -411,6 +419,8 @@ class Controller(tuned.exports.interfaces.ExportableInterface):
"""
if caller == "":
return (False, "Unauthorized", [])
+ if not self._cmd.is_valid_name(instance_name):
+ return (False, "Invalid instance_name", [])
for instance in self._daemon._unit_manager.instances:
if instance.name == instance_name:
return (True, "OK", sorted(list(instance.processed_devices)))
diff --git a/tuned/utils/commands.py b/tuned/utils/commands.py
index ce51fc0..38d95ef 100644
--- a/tuned/utils/commands.py
+++ b/tuned/utils/commands.py
@@ -544,3 +544,7 @@ class commands:
import string
trans = string.maketrans(source_chars, dest_chars)
return text.translate(trans)
+
+ # Checks if name contains only valid characters and has valid length or is empty string or None
+ def is_valid_name(self, name):
+ return not name or (all(c in consts.NAMES_ALLOWED_CHARS for c in name) and len(name) <= consts.NAMES_MAX_LENGTH)

View File

@ -1,88 +0,0 @@
diff --git a/profiles/latency-performance/tuned.conf b/profiles/latency-performance/tuned.conf
index 1dec690..e592138 100644
--- a/profiles/latency-performance/tuned.conf
+++ b/profiles/latency-performance/tuned.conf
@@ -35,3 +35,17 @@ vm.dirty_background_ratio=3
# 100 tells the kernel to aggressively swap processes out of physical memory
# and move them to swap cache
vm.swappiness=10
+
+[scheduler]
+runtime=0
+# ktune sysctl settings for rhel6 servers, maximizing i/o throughput
+#
+# Minimal preemption granularity for CPU-bound tasks:
+# (default: 1 msec# (1 + ilog(ncpus)), units: nanoseconds)
+sched_min_granularity_ns = 3000000
+sched_wakeup_granularity_ns = 4000000
+
+# The total time the scheduler will consider a migrated process
+# "cache hot" and thus less likely to be re-migrated
+# (system default is 500000, i.e. 0.5 ms)
+sched_migration_cost_ns = 5000000
diff --git a/profiles/sap-hana/tuned.conf b/profiles/sap-hana/tuned.conf
index aeecf53..1b15ea3 100644
--- a/profiles/sap-hana/tuned.conf
+++ b/profiles/sap-hana/tuned.conf
@@ -20,3 +20,8 @@ kernel.numa_balancing = 0
vm.dirty_ratio = 40
vm.dirty_background_ratio = 10
vm.swappiness = 10
+
+[scheduler]
+runtime=0
+sched_min_granularity_ns = 3000000
+sched_wakeup_granularity_ns = 4000000
diff --git a/profiles/throughput-performance/tuned.conf b/profiles/throughput-performance/tuned.conf
index e4e832f..3d9c42f 100644
--- a/profiles/throughput-performance/tuned.conf
+++ b/profiles/throughput-performance/tuned.conf
@@ -67,9 +67,33 @@ vm.swappiness=10
# on older kernels
net.core.somaxconn=>2048
+[scheduler]
+runtime=0
+# ktune sysctl settings for rhel6 servers, maximizing i/o throughput
+#
+# Minimal preemption granularity for CPU-bound tasks:
+# (default: 1 msec# (1 + ilog(ncpus)), units: nanoseconds)
+sched_min_granularity_ns = 10000000
+
+# SCHED_OTHER wake-up granularity.
+# (default: 1 msec# (1 + ilog(ncpus)), units: nanoseconds)
+#
+# This option delays the preemption effects of decoupled workloads
+# and reduces their over-scheduling. Synchronous workloads will still
+# have immediate wakeup/sleep latencies.
+sched_wakeup_granularity_ns = 15000000
+
# Marvell ThunderX
[sysctl.thunderx]
type=sysctl
uname_regex=aarch64
cpuinfo_regex=${thunderx_cpuinfo_regex}
kernel.numa_balancing=0
+
+# AMD
+[scheduler.amd]
+type=scheduler
+uname_regex=x86_64
+cpuinfo_regex=${amd_cpuinfo_regex}
+runtime=0
+sched_migration_cost_ns=5000000
diff --git a/profiles/virtual-host/tuned.conf b/profiles/virtual-host/tuned.conf
index 5301d9f..24d0fb4 100644
--- a/profiles/virtual-host/tuned.conf
+++ b/profiles/virtual-host/tuned.conf
@@ -14,3 +14,10 @@ vm.dirty_background_ratio = 5
[cpu]
# Setting C3 state sleep mode/power savings
force_latency=cstate.id_no_zero:3|70
+
+[scheduler]
+runtime=0
+# The total time the scheduler will consider a migrated process
+# "cache hot" and thus less likely to be re-migrated
+# (system default is 500000, i.e. 0.5 ms)
+sched_migration_cost_ns = 5000000

View File

@ -1,11 +0,0 @@
diff --git a/profiles/postgresql/tuned.conf b/profiles/postgresql/tuned.conf
index 88da8e4..4fd3810 100644
--- a/profiles/postgresql/tuned.conf
+++ b/profiles/postgresql/tuned.conf
@@ -55,3 +55,6 @@ sched_min_granularity_ns = 10000000
# "cache hot" and thus less likely to be re-migrated
# (system default is 500000, i.e. 0.5 ms)
sched_migration_cost_ns = 50000000
+
+[scheduler.amd]
+enabled=false

View File

@ -1,54 +0,0 @@
From 7557cf975282326cdbfe55b7b803d8075ff37cba Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jaroslav=20=C5=A0karvada?= <jskarvad@redhat.com>
Date: Tue, 12 Mar 2024 20:25:43 +0100
Subject: [PATCH] epyc-eda: added new profile for EDA compute workloads on AMD
EPYC CPUs
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Jaroslav Škarvada <jskarvad@redhat.com>
---
man/tuned-profiles.7 | 4 ++++
profiles/epyc-eda/tuned.conf | 14 ++++++++++++++
2 files changed, 18 insertions(+)
create mode 100644 profiles/epyc-eda/tuned.conf
diff --git a/man/tuned-profiles.7 b/man/tuned-profiles.7
index 10cad7b..600e8bb 100644
--- a/man/tuned-profiles.7
+++ b/man/tuned-profiles.7
@@ -141,6 +141,10 @@ profiles (e.g. throughput\-performance profile), example:
Profile optimized for AWS EC2 instances. It is based on the
throughput\-performance profile.
+.TP
+.BI "epyc-eda"
+Profile optimized for EDA compute workloads on AMD EPYC CPUs.
+
.SH "FILES"
.nf
.I /etc/tuned/*
diff --git a/profiles/epyc-eda/tuned.conf b/profiles/epyc-eda/tuned.conf
new file mode 100644
index 0000000..482d404
--- /dev/null
+++ b/profiles/epyc-eda/tuned.conf
@@ -0,0 +1,14 @@
+#
+# tuned configuration
+#
+
+[main]
+summary=Optimize for EDA compute workloads on AMD EPYC CPUs
+description=Configures virtual memory, CPU governors, and network settings for EDA compute workloads.
+include=throughput-performance
+
+# AMD
+[scheduler.amd]
+type=scheduler
+#Allow processes to rapidly move between cores to avoid idle time and maximize CPU usage
+sched_migration_cost_ns=10000
--
2.44.0

View File

@ -1,28 +0,0 @@
From 04ead944fdf640ed986331179e533542efc934c7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jaroslav=20=C5=A0karvada?= <jskarvad@redhat.com>
Date: Mon, 8 Apr 2024 11:03:47 +0200
Subject: [PATCH] sap-netweaver: increased vm.max_map_count
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Resolves: RHEL-31757
Signed-off-by: Jaroslav Škarvada <jskarvad@redhat.com>
---
profiles/sap-netweaver/tuned.conf | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/profiles/sap-netweaver/tuned.conf b/profiles/sap-netweaver/tuned.conf
index a1cfd17..81c4d44 100644
--- a/profiles/sap-netweaver/tuned.conf
+++ b/profiles/sap-netweaver/tuned.conf
@@ -10,4 +10,4 @@ include=throughput-performance
kernel.sem = 32000 1024000000 500 32000
kernel.shmall = 18446744073692774399
kernel.shmmax = 18446744073692774399
-vm.max_map_count = 2000000
+vm.max_map_count = 2147483647
--
2.44.0

View File

@ -1,97 +0,0 @@
diff --git a/tuned/plugins/plugin_disk.py b/tuned/plugins/plugin_disk.py
index 1438e35..d6feb06 100644
--- a/tuned/plugins/plugin_disk.py
+++ b/tuned/plugins/plugin_disk.py
@@ -100,19 +100,20 @@ class DiskPlugin(hotplug.Plugin):
self._devices_supported = True
self._use_hdparm = True
self._free_devices = set()
- self._hdparm_apm_devices = set()
+ self._hdparm_apm_device_support = dict()
for device in self._hardware_inventory.get_devices("block"):
if self._device_is_supported(device):
self._free_devices.add(device.sys_name)
- if self._use_hdparm and self._is_hdparm_apm_supported(device.sys_name):
- self._hdparm_apm_devices.add(device.sys_name)
-
self._assigned_devices = set()
def _get_device_objects(self, devices):
return [self._hardware_inventory.get_device("block", x) for x in devices]
def _is_hdparm_apm_supported(self, device):
+ if not self._use_hdparm:
+ return False
+ if device in self._hdparm_apm_device_support:
+ return self._hdparm_apm_device_support[device]
(rc, out, err_msg) = self._cmd.execute(["hdparm", "-C", "/dev/%s" % device], \
no_errors = [errno.ENOENT], return_err=True)
if rc == -errno.ENOENT:
@@ -122,10 +123,13 @@ class DiskPlugin(hotplug.Plugin):
elif rc:
log.info("Device '%s' not supported by hdparm" % device)
log.debug("(rc: %s, msg: '%s')" % (rc, err_msg))
+ self._hdparm_apm_device_support[device] = False
return False
elif "unknown" in out:
log.info("Driver for device '%s' does not support apm command" % device)
+ self._hdparm_apm_device_support[device] = False
return False
+ self._hdparm_apm_device_support[device] = True
return True
@classmethod
@@ -232,7 +236,7 @@ class DiskPlugin(hotplug.Plugin):
return not "standby" in out and not "sleeping" in out
def _instance_update_dynamic(self, instance, device):
- if not device in self._hdparm_apm_devices:
+ if not self._is_hdparm_apm_supported(device):
return
load = instance._load_monitor.get_device_load(device)
if load is None:
@@ -315,7 +319,7 @@ class DiskPlugin(hotplug.Plugin):
# At the moment we support dynamic tuning just for devices compatible with hdparm apm commands
# If in future will be added new functionality not connected to this command,
# it is needed to change it here
- if device not in self._hdparm_apm_devices:
+ if not self._is_hdparm_apm_supported(device):
log.info("There is no dynamic tuning available for device '%s' at time" % device)
else:
super(DiskPlugin, self)._instance_apply_dynamic(instance, device)
@@ -350,7 +354,7 @@ class DiskPlugin(hotplug.Plugin):
@command_set("apm", per_device=True)
def _set_apm(self, value, device, sim, remove):
- if device not in self._hdparm_apm_devices:
+ if not self._is_hdparm_apm_supported(device):
if not sim:
log.info("apm option is not supported for device '%s'" % device)
return None
@@ -366,7 +370,7 @@ class DiskPlugin(hotplug.Plugin):
@command_get("apm")
def _get_apm(self, device, ignore_missing=False):
- if device not in self._hdparm_apm_devices:
+ if not self._is_hdparm_apm_supported(device):
if not ignore_missing:
log.info("apm option is not supported for device '%s'" % device)
return None
@@ -390,7 +394,7 @@ class DiskPlugin(hotplug.Plugin):
@command_set("spindown", per_device=True)
def _set_spindown(self, value, device, sim, remove):
- if device not in self._hdparm_apm_devices:
+ if not self._is_hdparm_apm_supported(device):
if not sim:
log.info("spindown option is not supported for device '%s'" % device)
return None
@@ -406,7 +410,7 @@ class DiskPlugin(hotplug.Plugin):
@command_get("spindown")
def _get_spindown(self, device, ignore_missing=False):
- if device not in self._hdparm_apm_devices:
+ if not self._is_hdparm_apm_supported(device):
if not ignore_missing:
log.info("spindown option is not supported for device '%s'" % device)
return None

1
ci.fmf Normal file
View File

@ -0,0 +1 @@
resultsdb-testcase: separate

24
gating.yaml Normal file
View File

@ -0,0 +1,24 @@
--- !Policy
product_versions:
- fedora-*
decision_context: bodhi_update_push_testing
subject_type: koji_build
rules:
- !PassingTestCaseRule {test_case_name: fedora-ci.koji-build./plans/tier1-public.functional}
#Rawhide
--- !Policy
product_versions:
- fedora-*
decision_context: bodhi_update_push_stable
subject_type: koji_build
rules:
- !PassingTestCaseRule {test_case_name: fedora-ci.koji-build./plans/tier1-public.functional}
#gating rhel
--- !Policy
product_versions:
- rhel-*
decision_context: osci_compose_gate
rules:
- !PassingTestCaseRule {test_case_name: osci.brew-build./plans/all.functional}

6
plans/all.fmf Normal file
View File

@ -0,0 +1,6 @@
summary: Test plan with all Fedora tests
discover:
how: fmf
url: https://github.com/redhat-performance/tuned.git
execute:
how: tmt

1
sources Normal file
View File

@ -0,0 +1 @@
SHA512 (tuned-2.25.1.tar.gz) = fa5ac9d818d11b118fb7c26db28993b704f590070edbece570fee1a6c60a1f5f850b711683c45b46f33d9b056a84e43ced2c4c1ee58e9ef3d1fd035a4c1d4de4

View File

@ -0,0 +1,311 @@
From 91cb383ad7339873da3480463061500a308ba825 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavol=20=C5=BD=C3=A1=C4=8Dik?= <zacik.pa@gmail.com>
Date: Fri, 31 Jan 2025 12:43:08 +0100
Subject: [PATCH 1/2] bootloader: Simplify tuning of rpm-ostree kargs
The bootloader plugin claims that it does not mess
with existing kernel parameters; make sure we adhere
to that also for rpm-ostree systems:
1. Only append a new karg if the same key=value pair
does not already appear in kernel parameters. If we would
duplicate it, it would not be possible to determine which
one to delete when unapplying the profile.
2. Do not delete existing key=value pairs when the profile
adds a karg with another value for the key. A single key can
appear multiple times in the kernel parameter list with different
values.
Also make sure new kargs are added exactly in the order in which
they appear in the profile. This is especially important for kargs
related to hugepages.
Resolves: RHEL-45836
---
tuned/consts.py | 1 -
tuned/plugins/plugin_bootloader.py | 142 +++++++++++------------------
2 files changed, 53 insertions(+), 90 deletions(-)
diff --git a/tuned/consts.py b/tuned/consts.py
index 577ad9583..a92c78060 100644
--- a/tuned/consts.py
+++ b/tuned/consts.py
@@ -43,7 +43,6 @@
INITRD_IMAGE_DIR = "/boot"
BOOT_CMDLINE_TUNED_VAR = "TUNED_BOOT_CMDLINE"
BOOT_CMDLINE_INITRD_ADD_VAR = "TUNED_BOOT_INITRD_ADD"
-BOOT_CMDLINE_KARGS_DELETED_VAR = "TUNED_BOOT_KARGS_DELETED"
BOOT_CMDLINE_FILE = "/etc/tuned/bootcmdline"
PETITBOOT_DETECT_DIR = "/sys/firmware/opal"
MACHINE_ID_FILE = "/etc/machine-id"
diff --git a/tuned/plugins/plugin_bootloader.py b/tuned/plugins/plugin_bootloader.py
index 4c66189fb..07a66d265 100644
--- a/tuned/plugins/plugin_bootloader.py
+++ b/tuned/plugins/plugin_bootloader.py
@@ -209,24 +209,6 @@ def _get_config_options(cls):
"skip_grub_config": None,
}
- @staticmethod
- def _options_to_dict(options, omit=""):
- """
- Returns dict created from options
- e.g.: _options_to_dict("A=A A=B A B=A C=A", "A=B B=A B=B") returns {'A': ['A', None], 'C': ['A']}
- """
- d = {}
- omit = omit.split()
- for o in options.split():
- if o not in omit:
- arr = o.split('=', 1)
- d.setdefault(arr[0], []).append(arr[1] if len(arr) > 1 else None)
- return d
-
- @staticmethod
- def _dict_to_options(d):
- return " ".join([k + "=" + v1 if v1 is not None else k for k, v in d.items() for v1 in v])
-
def _rpm_ostree_status(self):
"""
Returns status of rpm-ostree transactions or None if not run on rpm-ostree system
@@ -241,68 +223,45 @@ def _rpm_ostree_status(self):
return None
return splited[1]
- def _wait_till_idle(self):
+ def _wait_till_rpm_ostree_idle(self):
+ """Check that rpm-ostree is idle, allowing some waiting time."""
sleep_cycles = 10
sleep_secs = 1.0
- for i in range(sleep_cycles):
+ for _ in range(sleep_cycles):
if self._rpm_ostree_status() == "idle":
return True
sleep(sleep_secs)
- if self._rpm_ostree_status() == "idle":
- return True
- return False
+ return self._rpm_ostree_status() == "idle"
+
+ def _get_rpm_ostree_kargs(self):
+ """Retrieve the output of rpm-ostree kargs, i.e., current default kernel arguments."""
+ if not self._wait_till_rpm_ostree_idle():
+ log.error("Error getting rpm-ostree kargs: rpm-ostree is busy")
+ return None
+ (rc, out, err) = self._cmd.execute(["rpm-ostree", "kargs"], return_err=True)
+ if out:
+ log.debug("rpm-ostree kargs: %s" % out)
+ if rc != 0:
+ log.error("Error getting rpm-ostree kargs: %s" % err)
+ return None
+ return out
- def _rpm_ostree_kargs(self, append={}, delete={}):
+ def _modify_rpm_ostree_kargs(self, delete_kargs=[], append_kargs=[]):
"""
- Method for appending or deleting rpm-ostree karg
- returns None if rpm-ostree not present or is run on not ostree system
- or tuple with new kargs, appended kargs and deleted kargs
+ Modify (delete and append) kernel arguments in a rpm-ostree system.
+ Return a boolean indicating whether the operation was successful.
"""
- (rc, out, err) = self._cmd.execute(['rpm-ostree', 'kargs'], return_err=True)
- log.debug("rpm-ostree output stdout:\n%s\nstderr:\n%s" % (out, err))
- if rc != 0:
- return None, None, None
- kargs = self._options_to_dict(out)
-
- if not self._wait_till_idle():
- log.error("Cannot wait for transaction end")
- return None, None, None
-
- deleted = {}
- delete_params = self._dict_to_options(delete).split()
- # Deleting kargs, e.g. deleting added kargs by profile
- for k, val in delete.items():
- for v in val:
- kargs[k].remove(v)
- deleted[k] = val
-
- appended = {}
- append_params = self._dict_to_options(append).split()
- # Appending kargs, e.g. new kargs by profile or restoring kargs replaced by profile
- for k, val in append.items():
- if kargs.get(k):
- # If there is karg that we add with new value we want to delete it
- # and store old value for restoring after profile unload
- log.debug("adding rpm-ostree kargs %s: %s for delete" % (k, kargs[k]))
- deleted.setdefault(k, []).extend(kargs[k])
- delete_params.extend([k + "=" + v if v is not None else k for v in kargs[k]])
- kargs[k] = []
- kargs.setdefault(k, []).extend(val)
- appended[k] = val
-
- if append_params == delete_params:
- log.info("skipping rpm-ostree kargs - append == deleting (%s)" % append_params)
- return kargs, appended, deleted
-
- log.info("rpm-ostree kargs - appending: '%s'; deleting: '%s'" % (append_params, delete_params))
- (rc, _, err) = self._cmd.execute(['rpm-ostree', 'kargs'] +
- ['--append=%s' % v for v in append_params] +
- ['--delete=%s' % v for v in delete_params], return_err=True)
+ if not self._wait_till_rpm_ostree_idle():
+ log.error("Error modifying rpm-ostree kargs: rpm-ostree is busy")
+ return False
+ (rc, _, err) = self._cmd.execute(
+ ["rpm-ostree", "kargs"] +
+ ["--delete=%s" % karg for karg in delete_kargs] +
+ ["--append=%s" % karg for karg in append_kargs], return_err=True)
if rc != 0:
- log.error("Something went wrong with rpm-ostree kargs\n%s" % (err))
- return self._options_to_dict(out), None, None
- else:
- return kargs, appended, deleted
+ log.error("Error modifying rpm-ostree kargs: %s" % err)
+ return False
+ return True
def _get_effective_options(self, options):
"""Merge provided options with plugin default options and merge all cmdline.* options."""
@@ -368,18 +327,16 @@ def _remove_grub2_tuning(self):
log.info("removing initrd image '%s'" % self._initrd_dst_img_val)
self._cmd.unlink(self._initrd_dst_img_val)
- def _get_rpm_ostree_changes(self):
+ def _get_appended_rpm_ostree_kargs(self):
+ """Return the list of kernel arguments that were appended by this profile (in a rpm-ostree system)."""
f = self._cmd.read_file(consts.BOOT_CMDLINE_FILE)
appended = re.search(consts.BOOT_CMDLINE_TUNED_VAR + r"=\"(.*)\"", f, flags=re.MULTILINE)
- appended = appended[1] if appended else ""
- deleted = re.search(consts.BOOT_CMDLINE_KARGS_DELETED_VAR + r"=\"(.*)\"", f, flags=re.MULTILINE)
- deleted = deleted[1] if deleted else ""
- return appended, deleted
+ return appended[1].split() if appended else []
def _remove_rpm_ostree_tuning(self):
- appended, deleted = self._get_rpm_ostree_changes()
- self._rpm_ostree_kargs(append=self._options_to_dict(deleted), delete=self._options_to_dict(appended))
- self._patch_bootcmdline({consts.BOOT_CMDLINE_TUNED_VAR: "", consts.BOOT_CMDLINE_KARGS_DELETED_VAR: ""})
+ """Remove kernel parameter tuning in a rpm-ostree system."""
+ self._modify_rpm_ostree_kargs(delete_kargs=self._get_appended_rpm_ostree_kargs())
+ self._patch_bootcmdline({consts.BOOT_CMDLINE_TUNED_VAR: ""})
def _instance_unapply_static(self, instance, rollback = consts.ROLLBACK_SOFT):
if rollback == consts.ROLLBACK_FULL and not self._skip_grub_config_val:
@@ -489,14 +446,22 @@ def _grub2_cfg_patch(self, d):
return True
def _rpm_ostree_update(self):
- appended, _ = self._get_rpm_ostree_changes()
- _cmdline_dict = self._options_to_dict(self._cmdline_val, appended)
- if not _cmdline_dict:
- return None
- (_, _, d) = self._rpm_ostree_kargs(append=_cmdline_dict)
- if d is None:
+ """Apply kernel parameter tuning in a rpm-ostree system."""
+ if self._get_appended_rpm_ostree_kargs():
+ # The kargs are already set in /etc/tuned/bootcmldine,
+ # we are likely post-reboot and done.
+ return
+ profile_kargs = self._cmdline_val.split()
+ active_kargs = self._get_rpm_ostree_kargs()
+ if active_kargs is None:
+ log.error("Not updating kernel arguments, could not read the current ones.")
return
- self._patch_bootcmdline({consts.BOOT_CMDLINE_TUNED_VAR : self._cmdline_val, consts.BOOT_CMDLINE_KARGS_DELETED_VAR : self._dict_to_options(d)})
+ # Only append key=value pairs that do not yet appear in kernel parameters,
+ # otherwise we would not be able to restore the cmdline to the previous state
+ # via rpm-ostree kargs --delete.
+ kargs_to_append = [karg for karg in profile_kargs if karg not in active_kargs.split()]
+ if self._modify_rpm_ostree_kargs(append_kargs=kargs_to_append):
+ self._patch_bootcmdline({consts.BOOT_CMDLINE_TUNED_VAR : " ".join(kargs_to_append)})
def _grub2_update(self):
self._grub2_cfg_patch({consts.GRUB2_TUNED_VAR : self._cmdline_val, consts.GRUB2_TUNED_INITRD_VAR : self._initrd_val})
@@ -646,11 +611,10 @@ def _cmdline(self, enabling, value, verify, ignore_missing, instance):
v = self._variables.expand(self._cmd.unquote(value))
if verify:
if self._rpm_ostree:
- rpm_ostree_kargs = self._rpm_ostree_kargs()[0]
- cmdline = self._dict_to_options(rpm_ostree_kargs)
+ cmdline = self._get_rpm_ostree_kargs()
else:
cmdline = self._cmd.read_file("/proc/cmdline")
- if len(cmdline) == 0:
+ if cmdline is None or len(cmdline) == 0:
return None
cmdline_set = set(cmdline.split())
value_set = set(v.split())
From 3e05cb783ee10ea78efb113c54bd2ad7d73a0e2e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavol=20=C5=BD=C3=A1=C4=8Dik?= <zacik.pa@gmail.com>
Date: Fri, 16 May 2025 15:13:25 +0200
Subject: [PATCH 2/2] bootloader: Remove previously appended rpm-ostree kargs
If TuneD-appended kargs are present and do not match
the kargs to be appended, we should remove them. This
can happen if there was no profile rollback, e.g.,
when using the no-daemon mode.
Resolves: RHEL-86814
---
tuned/plugins/plugin_bootloader.py | 24 ++++++++++++++++--------
1 file changed, 16 insertions(+), 8 deletions(-)
diff --git a/tuned/plugins/plugin_bootloader.py b/tuned/plugins/plugin_bootloader.py
index 07a66d265..2711e116f 100644
--- a/tuned/plugins/plugin_bootloader.py
+++ b/tuned/plugins/plugin_bootloader.py
@@ -105,7 +105,7 @@ class BootloaderPlugin(base.Plugin):
----
[main]
include=profile_1
-
+
[bootloader]
cmdline_profile_2=-quiet
----
@@ -114,7 +114,7 @@ class BootloaderPlugin(base.Plugin):
----
[main]
include=profile_1
-
+
[bootloader]
cmdline_profile_1=-quiet
----
@@ -447,20 +447,28 @@ def _grub2_cfg_patch(self, d):
def _rpm_ostree_update(self):
"""Apply kernel parameter tuning in a rpm-ostree system."""
- if self._get_appended_rpm_ostree_kargs():
- # The kargs are already set in /etc/tuned/bootcmldine,
- # we are likely post-reboot and done.
- return
+ appended_kargs = self._get_appended_rpm_ostree_kargs()
profile_kargs = self._cmdline_val.split()
active_kargs = self._get_rpm_ostree_kargs()
if active_kargs is None:
log.error("Not updating kernel arguments, could not read the current ones.")
return
+ # Ignore kargs previously appended by TuneD, these will be removed later.
+ non_tuned_kargs = active_kargs.split()
+ for karg in appended_kargs:
+ non_tuned_kargs.remove(karg)
# Only append key=value pairs that do not yet appear in kernel parameters,
# otherwise we would not be able to restore the cmdline to the previous state
# via rpm-ostree kargs --delete.
- kargs_to_append = [karg for karg in profile_kargs if karg not in active_kargs.split()]
- if self._modify_rpm_ostree_kargs(append_kargs=kargs_to_append):
+ kargs_to_append = [karg for karg in profile_kargs if karg not in non_tuned_kargs]
+ if appended_kargs == kargs_to_append:
+ # The correct kargs are already set in /etc/tuned/bootcmldine,
+ # we are likely post-reboot and done.
+ log.info("Kernel arguments already set, not updating.")
+ return
+ # If there are kargs in /etc/bootcmdline and they do not match
+ # the requested ones, there was no rollback, so remove them now.
+ if self._modify_rpm_ostree_kargs(delete_kargs=appended_kargs, append_kargs=kargs_to_append):
self._patch_bootcmdline({consts.BOOT_CMDLINE_TUNED_VAR : " ".join(kargs_to_append)})
def _grub2_update(self):

File diff suppressed because it is too large Load Diff