From 638c48ae3e6b2682222240b60c713def970bd0df Mon Sep 17 00:00:00 2001 From: Philipp Rudo Date: Wed, 14 Jan 2026 15:54:25 +0100 Subject: [PATCH] kdump-lib-initramfs: Fix performance regression in kdump_get_conf_val Rewriting kdump_get_conf_val in Bash lead to a massive performance regression. On my test system starting the kdump service took $ time kdumpctl start real 0m13.134s user 0m8.828s sys 0m7.450s which is ~20 times slower compared to kdump-utils-1.0.59-1.fc44 with $ time kdumpctl start real 0m0.641s user 0m0.208s sys 0m0.538s Looking at the traces shows that this is caused because Bash now has to handle the whole kdump.conf, including the extensive comment at the start, every time kdump_get_conf_val is called. This is done multiple times when starting the kdump service and is often cloaked by other functions, e.g. is_ssh_dump_target() or get_save_path(). To fix the issue remove comments and empty lines in a regex again so that the Bash code only has to handle valid config entries. With this change alone the performance is almost as good as the original version with $ time kdumpctl start real 0m0.780s user 0m0.330s sys 0m0.604s In the long run it would make sense to also reduce the number of calls to kdump_get_conf_val. This patch also fixes the issue that subsequent blanks are replaced by a single space. Usually this is not an issue but there are corner cases, e.g. in printf-like format strings passed as an argument, where the new behaviour is undesirable. Fixes: d81109c ("kdump-lib-initramfs: rewrite kdump_get_conf_val") Signed-off-by: Philipp Rudo --- kdump-lib-initramfs.sh | 66 ++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/kdump-lib-initramfs.sh b/kdump-lib-initramfs.sh index 6f5d6db6..6c64106f 100755 --- a/kdump-lib-initramfs.sh +++ b/kdump-lib-initramfs.sh @@ -30,38 +30,40 @@ kdump_get_conf_val() _found="" [ -f "$KDUMP_CONFIG_FILE" ] || return - while read -r _line; do - _line="$(echo "$_line" | tr -s "[:blank:]" " ")" - case "$_line" in - "" | \#*) - continue - ;; - *\#*) - _line="${_line%%\#*}" - _line="${_line% }" - ;; - esac - - _opt=${_line%% *} - _val=${_line#* } - - case "$_val" in - \"*\") - # Remove quotes - _val="${_val#\"}" - _val="${_val%\"}" - ;; - esac - - if [ -z "$_to_find" ]; then - echo "$_opt $_val" - elif echo "$_opt" | grep -q -E "^($_to_find)$"; then - # make sure to only return the last match to mirror the - # old behavior - _found="$_val" - fi - done < "$KDUMP_CONFIG_FILE" - [ -n "$_found" ] && echo "$_found" + + # On lines that are _not_ comments or empty remove... + # Note: The additional braces {} are required as piping into a while + # loop creates a sub-shell. So without the braces $_found would only be + # set inside the loop but empty outside of it. + grep -Ev -e '^\s*#' -e '^\s*$' "$KDUMP_CONFIG_FILE" | { + while read -r _opt _val; do + # ...trailing comments... + case "$_val" in + *\#*) + _val="${_val%%#*}" + _val="${_val%"${_val##*[![:space:]]}"}" + ;; + esac + + # ...quotes + case "$_val" in + \"*\") + _val="${_val#\"}" + _val="${_val%\"}" + ;; + esac + + if [ -z "$_to_find" ]; then + echo "$_opt $_val" + elif echo "$_opt" | grep -q -E "^($_to_find)$"; then + # make sure to only return the last match to + # mirror the old behavior + _found="$_val" + fi + done + + [ -n "$_found" ] && echo "$_found" + } # make sure we return 0 even when a option isn't set return 0