diff --git a/tuned-2.25.1-bootc-kargs.patch b/tuned-2.25.1-bootc-kargs.patch new file mode 100644 index 0000000..64a3b83 --- /dev/null +++ b/tuned-2.25.1-bootc-kargs.patch @@ -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?= +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?= +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): diff --git a/tuned.spec b/tuned.spec index f27b637..6f26007 100644 --- a/tuned.spec +++ b/tuned.spec @@ -43,7 +43,7 @@ Summary: A dynamic adaptive system tuning daemon Name: tuned Version: 2.25.1 -Release: 1%{?prerel1}%{?dist} +Release: 2%{?prerel1}%{?dist} License: GPLv2+ Source0: https://github.com/redhat-performance/%{name}/archive/v%{version}%{?prerel2}/%{name}-%{version}%{?prerel2}.tar.gz # RHEL-9 specific recommend.conf: @@ -110,6 +110,7 @@ Requires: python3-syspurpose %endif # Revert default profile directory migration only applicable for RHEL-10+ Patch0: tuned-2.25.0-revert-profile-migration.patch +Patch1: tuned-2.25.1-bootc-kargs.patch %description The tuned package contains a daemon that tunes system settings dynamically. @@ -630,6 +631,11 @@ fi %config(noreplace) %{_sysconfdir}/tuned/ppd.conf %changelog +* Thu Jun 05 2025 Pavol Žáčik - 2.25.1-2 +- Fix handling of kernel arguments on bootc systems + resolves: RHEL-45836 + resolves: RHEL-86814 + * Tue Feb 4 2025 Jaroslav Škarvada - 2.25.1-1 - new release - rebased tuned to latest upstream