From 6659f332dc3b7177860236ebcdd4e09b2c0cc256 Mon Sep 17 00:00:00 2001 From: Wardenjohn Date: Sat, 12 Oct 2024 11:15:16 +0800 Subject: [PATCH] kpatch: Add function to adjust new sysfs attribute 'stack_order' of livepatch Add function of 'kpatch list' to adjust kernel new attribute 'stack_order' of livepatch of kernel v6.14 or later. Now, using 'kpatch list' can output the enabling function in the system and the relationship from the enabling function to its object and its related module. For older kernel, which is not support 'stack_order' attribute, if there are just one klp module loaded in the system, we support to output the active functions. However, if there are more than one klp module loaded, we can not output the active functions becase the information without 'stack_order' is not accurate. Suggested-by: Joe Lawrence Signed-off-by: Wardenjohn --- kpatch/kpatch | 105 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 79 insertions(+), 26 deletions(-) diff --git a/kpatch/kpatch b/kpatch/kpatch index 0e94a5d73..154e1f81b 100755 --- a/kpatch/kpatch +++ b/kpatch/kpatch @@ -55,7 +55,7 @@ usage () { echo >&2 usage_cmd "info " "show information about a patch module" echo >&2 - usage_cmd "list" "list installed patch modules" + usage_cmd "list" "list installed patch modules, and list the enabling functions and its relationship from patch module to the function enabling in the system if 'stack_order' attribute is supported." echo >&2 usage_cmd "signal" "signal/poke any process stalling the current patch transition. This is only useful on systems that have the sysfs livepatch signal interface. On other systems, the signaling should be done automatically by the OS and this subcommand is a no-op." echo >&2 @@ -446,6 +446,82 @@ get_module_version() { MODVER="${MODVER/ */}" } +show_enabled_function() { + declare -A function_map + for module_dir in "$SYSFS"/*; do + if [[ ! -d "$module_dir" ]] || \ + [[ ! -e "$module_dir/stack_order" ]]; then + continue + fi + stack_order=$(cat "$module_dir/stack_order") + module_name=$(basename "$module_dir") + for obj_dir in "$module_dir"/*/; do + obj_name=$(basename "$obj_dir") + for func_dir in "$obj_dir"/*; do + if [[ ! -d "$func_dir" ]]; then + continue + fi + # we should take pos into account. + func_name_and_pos=$(basename "$func_dir") + key="$obj_name:$func_name_and_pos" + if [[ -z "${function_map[$key]}" ]]; then + function_map[$key]="$stack_order:$module_name" + else + # Update the map only iff this livepatch has a + # higher stack_order value + IFS=':' read -r recorded_order module_name <<< "${function_map[$key]}" + if [[ $recorded_order -lt $stack_order ]]; then + function_map[$key]="$stack_order:$module_name:$obj_name" + fi + fi + done + done + + done + + # Pretty print the function map if it has any contents + if [[ ${#function_map[@]} -ne 0 ]]; then + echo "" + echo "Currently livepatched functions:" + declare -a output_data=("Module Object Function/Occurrence") + for key in "${!function_map[@]}"; do + IFS=':' read -r stack_order module_name obj_name <<< "${function_map[$key]}" + obj_name=${key%%:*} + func_name_and_pos=${key##*:} + output_data+=("$module_name $obj_name $func_name_and_pos") + done + printf "%s\n" "${output_data[@]}" | column -t + fi +} + +print_patch_info() { + echo "Loaded patch modules:" + for module in "$SYSFS"/*; do + if [[ -e "$module" ]]; then + modname=$(basename "$module") + if [[ "$(cat "$module/enabled" 2>/dev/null)" -eq 1 ]]; then + in_transition "$modname" && state="enabling..." \ + || state="enabled" + else + in_transition "$modname" && state="disabling..." \ + || state="disabled" + fi + echo "$modname [$state]" + fi + done + show_stalled_processes + echo "" + echo "Installed patch modules:" + for kdir in "$INSTALLDIR"/*; do + [[ -e "$kdir" ]] || continue + for module in "$kdir"/*.ko; do + [[ -e "$module" ]] || continue + mod_name "$module" + echo "$MODNAME ($(basename "$kdir"))" + done + done +} + unset MODULE # Initialize the $SYSFS var. This only works if the core module has been @@ -593,31 +669,8 @@ case "$1" in "list") [[ "$#" -ne 1 ]] && usage - echo "Loaded patch modules:" - for module in "$SYSFS"/*; do - if [[ -e "$module" ]]; then - modname=$(basename "$module") - if [[ "$(cat "$module/enabled" 2>/dev/null)" -eq 1 ]]; then - in_transition "$modname" && state="enabling..." \ - || state="enabled" - else - in_transition "$modname" && state="disabling..." \ - || state="disabled" - fi - echo "$modname [$state]" - fi - done - show_stalled_processes - echo "" - echo "Installed patch modules:" - for kdir in "$INSTALLDIR"/*; do - [[ -e "$kdir" ]] || continue - for module in "$kdir"/*.ko; do - [[ -e "$module" ]] || continue - mod_name "$module" - echo "$MODNAME ($(basename "$kdir"))" - done - done + print_patch_info + show_enabled_function ;; "info")