From e2727f911ddfa701fcd9e374f9491ce1f0b9e326 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 | 118 +++++++++++++++++++++++++++++++++++++++----------- man/kpatch.1 | 2 +- 2 files changed, 93 insertions(+), 27 deletions(-) diff --git a/kpatch/kpatch b/kpatch/kpatch index 0e94a5d73..267a0af71 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. If the system supports the livepatch 'stack_order' sysfs attribute, provide the list of currently livepatched functions" 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,95 @@ get_module_version() { MODVER="${MODVER/ */}" } +show_enabled_function() { + declare -A function_map + declare -A transition_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") + transition=$(cat "$module_dir/transition") + for obj_dir in "$module_dir"/*; do + if [[ ! -d $obj_dir ]]; then + continue; + fi + 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 [[ $transition -eq 1 ]]; then + transition_map[$module_name]="transition" + fi + + 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 _ <<< "${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##*:} + if [[ -n ${transition_map[$module_name]} ]]; then + output_data+=("$module_name $obj_name $func_name_and_pos (${transition_map[$module_name]})") + fi + 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 +682,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") diff --git a/man/kpatch.1 b/man/kpatch.1 index 51c981166..adfccd713 100644 --- a/man/kpatch.1 +++ b/man/kpatch.1 @@ -33,7 +33,7 @@ info show information about a patch module list - list installed patch modules + list installed patch modules. If the system supports the livepatch 'stack_order' sysfs attribute, provide the list of currently livepatched functions signal signal/poke any process stalling the current patch transition.