From 5e63479840b7c9b0261a44583e673649db952e81 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. This feature only support the kernel with 'stack_order' attribute. 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")