Blame 0106-kpatch-build-support-Linux-5.19-.cmd-file-format.patch

642cd7
From 1d7e8a74bb6c282ebe6c9193e4bd0218ec689e46 Mon Sep 17 00:00:00 2001
642cd7
From: Josh Poimboeuf <jpoimboe@redhat.com>
642cd7
Date: Wed, 17 Aug 2022 12:21:04 -0700
642cd7
Subject: [PATCH 106/108] kpatch-build: support Linux 5.19 .cmd file format
642cd7
642cd7
Rewrite kobj_find() to deal with Linux 5.19, where the .cmd files use
642cd7
object file paths relative to the .cmd file rather than relative to the
642cd7
root of the kernel tree.
642cd7
642cd7
While at it, add several performance enhancements to prevent all
642cd7
currently known deep finds.
642cd7
642cd7
This is all quite fiddly.  But it works.
642cd7
642cd7
Fixes #1277.
642cd7
642cd7
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
642cd7
---
642cd7
 kpatch-build/kpatch-build | 243 +++++++++++++++++++++++++++++---------
642cd7
 1 file changed, 188 insertions(+), 55 deletions(-)
642cd7
642cd7
diff --git a/kpatch-build/kpatch-build b/kpatch-build/kpatch-build
642cd7
index 5435a19..bec8010 100755
642cd7
--- a/kpatch-build/kpatch-build
642cd7
+++ b/kpatch-build/kpatch-build
642cd7
@@ -434,82 +434,215 @@ find_special_section_data() {
642cd7
 	return
642cd7
 }
642cd7
 
642cd7
-filter_parent_obj()
642cd7
-{
642cd7
-  local dir="${1}"
642cd7
-  local file="${2}"
642cd7
+# path of file, relative to dir
642cd7
+# adapted from https://stackoverflow.com/a/24848739
642cd7
+relpath() {
642cd7
+	local file="$1"
642cd7
+	local dir="$2"
642cd7
+
642cd7
+	local filedir
642cd7
+	local common
642cd7
+	local result
642cd7
+
642cd7
+	filedir="$(dirname "$(readlink -f "$file")")"
642cd7
+	common="$(readlink -f "$dir")"
642cd7
+
642cd7
+	if [[ "$filedir" = "$common" ]]; then
642cd7
+		basename "$file"
642cd7
+		return
642cd7
+	fi
642cd7
+
642cd7
+	while [[ "${filedir#$common/}" = "$filedir" ]]; do
642cd7
+		common="$(dirname "$common")"
642cd7
+		result="../$result"
642cd7
+	done
642cd7
 
642cd7
-  grep -v "\.mod\.cmd$" | grep -Fv "${dir}/.${file}.cmd"
642cd7
+	result="${result}${filedir#$common/}"
642cd7
+	echo "${result}/$(basename "$file")"
642cd7
 }
642cd7
 
642cd7
-find_parent_obj() {
642cd7
-	dir="$(dirname "$1")"
642cd7
-	absdir="$(readlink -f "$dir")"
642cd7
-	pwddir="$(readlink -f .)"
642cd7
-	pdir="${absdir#$pwddir/}"
642cd7
-	file="$(basename "$1")"
642cd7
-	grepname="${1%.o}"
642cd7
-	grepname="$grepname\\.o"
642cd7
-	if [[ "$DEEP_FIND" -eq 1 ]]; then
642cd7
-		num=0
642cd7
-		if [[ -n "$last_deep_find" ]]; then
642cd7
-			parent="$(grep -lw "$grepname" "$last_deep_find"/.*.cmd | filter_parent_obj "${pdir}" "${file}" | head -n1)"
642cd7
-			num="$(grep -lw "$grepname" "$last_deep_find"/.*.cmd | filter_parent_obj "${pdir}" "${file}" | wc -l)"
642cd7
+cmd_file_to_o_file() {
642cd7
+	local parent="$1"
642cd7
+
642cd7
+	# convert cmd file name to corresponding .o
642cd7
+	parent_dir="$(dirname "$parent")"
642cd7
+	parent_dir="${parent_dir#./}"
642cd7
+	parent="$(basename "$parent")"
642cd7
+	parent="${parent#.}"
642cd7
+	parent="${parent%.cmd}"
642cd7
+	parent="$parent_dir/$parent"
642cd7
+
642cd7
+	[[ -f $parent ]] || die "can't find $parent associated with $1"
642cd7
+
642cd7
+	echo "$parent"
642cd7
+}
642cd7
+
642cd7
+get_parent_from_parents() {
642cd7
+	local parents=("$@")
642cd7
+
642cd7
+	[[ ${#parents[@]} -eq 0 ]] && PARENT="" && return
642cd7
+	[[ ${#parents[@]} -eq 1 ]] && PARENT="${parents[0]}" && return
642cd7
+
642cd7
+	# multiple parents:
642cd7
+	local parent
642cd7
+	local mod_name="${parents[0]%.*}"
642cd7
+	local mod_file
642cd7
+	for parent in "${parents[@]}"; do
642cd7
+		# for modules, there can be multiple matches.  Some
642cd7
+		# combination of foo.o, foo.mod, and foo.ko, depending
642cd7
+		# on kernel version and whether the module is single or
642cd7
+		# multi-object.  Make sure a .mod and/or .ko exists, and no
642cd7
+		# more than one .mod/.ko exists.
642cd7
+
642cd7
+		[[ $parent = *.o ]] && continue
642cd7
+
642cd7
+		if [[ ${parent%.*} != "$mod_name" ]]; then
642cd7
+			mod_file=""
642cd7
+			break
642cd7
 		fi
642cd7
-		if [[ "$num" -eq 0 ]]; then
642cd7
-			parent="$(find . -name ".*.cmd" -print0 | xargs -0 grep -lw "$grepname" | filter_parent_obj "${pdir}" "${file}" | cut -c3- | head -n1)"
642cd7
-			num="$(find . -name ".*.cmd" -print0 | xargs -0 grep -lw "$grepname" | filter_parent_obj "${pdir}" "${file}" | wc -l)"
642cd7
-			[[ "$num" -eq 1 ]] && last_deep_find="$(dirname "$parent")"
642cd7
+
642cd7
+		if [[ $parent = *.mod || $parent = *.ko ]]; then
642cd7
+			mod_file=$parent
642cd7
+			continue
642cd7
 		fi
642cd7
+
642cd7
+		mod_file=""
642cd7
+		break
642cd7
+	done
642cd7
+
642cd7
+	if [[ -n $mod_file ]]; then
642cd7
+		PARENT="$mod_file"
642cd7
+		return
642cd7
+	fi
642cd7
+
642cd7
+	ERROR_IF_DIFF="multiple parent matches for $file: ${parents[*]}"
642cd7
+	PARENT="${parents[0]}"
642cd7
+}
642cd7
+
642cd7
+__find_parent_obj_in_dir() {
642cd7
+	local file="$1"
642cd7
+	local dir="$2"
642cd7
+
642cd7
+	declare -a parents
642cd7
+
642cd7
+	while IFS='' read -r parent; do
642cd7
+		parent="$(cmd_file_to_o_file "$parent")"
642cd7
+		[[ $parent -ef $file ]] && continue
642cd7
+		parents+=("$parent")
642cd7
+	done < <(grep -El "[ 	]${file/./\\.}([ 	\)]|$)" "$dir"/.*.cmd)
642cd7
+
642cd7
+	get_parent_from_parents "${parents[@]}"
642cd7
+}
642cd7
+
642cd7
+find_parent_obj_in_dir() {
642cd7
+	local file="$1"
642cd7
+	local dir="$2"
642cd7
+
642cd7
+	# make sure the dir has .cmd files
642cd7
+	if ! compgen -G "$dir"/.*.cmd > /dev/null; then
642cd7
+		PARENT=""
642cd7
+		return
642cd7
+	fi
642cd7
+
642cd7
+	# 5.19+: ../acp/acp_hw.o
642cd7
+	__find_parent_obj_in_dir "$(relpath "$file" "$dir")" "$dir"
642cd7
+	[[ -n $PARENT ]] && return
642cd7
+
642cd7
+	# pre-5.19 (and 5.19+ single-object modules):
642cd7
+	if [[ $file == $dir* ]]; then
642cd7
+		# arch/x86/kernel/smp.o
642cd7
+		__find_parent_obj_in_dir "$file" "$dir"
642cd7
 	else
642cd7
-		parent="$(grep -lw "$grepname" "$dir"/.*.cmd | filter_parent_obj "${dir}" "${file}" | head -n1)"
642cd7
-		num="$(grep -lw "$grepname" "$dir"/.*.cmd | filter_parent_obj "${dir}" "${file}" | wc -l)"
642cd7
+		# drivers/gpu/drm/amd/amdgpu/../acp/acp_hw.o
642cd7
+		__find_parent_obj_in_dir "$dir"/"$(relpath "$file" "$dir")" "$dir"
642cd7
 	fi
642cd7
+}
642cd7
 
642cd7
-	[[ "$num" -eq 0 ]] && PARENT="" && return
642cd7
-	[[ "$num" -gt 1 ]] && ERROR_IF_DIFF="two parent matches for $1"
642cd7
+find_parent_obj() {
642cd7
+	local file="$1"
642cd7
+
642cd7
+	# common case: look in same directory
642cd7
+	find_parent_obj_in_dir "$file" "$(dirname "$file")"
642cd7
+	[[ -n $PARENT ]] && return
642cd7
 
642cd7
-	dir="$(dirname "$parent")"
642cd7
-	PARENT="$(basename "$parent")"
642cd7
-	PARENT="${PARENT#.}"
642cd7
-	PARENT="${PARENT%.cmd}"
642cd7
-	[[ $dir != "." ]] && PARENT="$dir/$PARENT"
642cd7
-	[[ ! -e "$PARENT" ]] && die "ERROR: can't find parent $PARENT for $1"
642cd7
+	# if we previously had a successful deep find, try that dir first
642cd7
+	if [[ -n "$LAST_DEEP_FIND_DIR" ]]; then
642cd7
+		find_parent_obj_in_dir "$file" "$LAST_DEEP_FIND_DIR"
642cd7
+		[[ -n "$PARENT" ]] && return
642cd7
+	fi
642cd7
+
642cd7
+	# prevent known deep finds
642cd7
+	if [[ $file = drivers/gpu/drm/amd/* ]]; then
642cd7
+		find_parent_obj_in_dir "$file" "drivers/gpu/drm/amd/amdgpu"
642cd7
+		[[ -n "$PARENT" ]] && return
642cd7
+	fi
642cd7
+	if [[ $file = virt/kvm/* ]]; then
642cd7
+		find_parent_obj_in_dir "$file" "arch/x86/kvm"
642cd7
+		[[ -n "$PARENT" ]] && return
642cd7
+	fi
642cd7
+	if [[ $file = drivers/oprofile/* ]]; then
642cd7
+		find_parent_obj_in_dir "$file" "arch/x86/oprofile"
642cd7
+		[[ -n "$PARENT" ]] && return
642cd7
+	fi
642cd7
+
642cd7
+	# check higher-level dirs
642cd7
+	local dir
642cd7
+	dir="$(dirname "$file")"
642cd7
+	while [[ ! $dir -ef . ]]; do
642cd7
+		dir="$(dirname "$dir")"
642cd7
+		find_parent_obj_in_dir "$file" "$dir"
642cd7
+		[[ -n $PARENT ]] && return
642cd7
+	done
642cd7
+
642cd7
+	# slow path: search the entire tree ("deep find")
642cd7
+	echo 'doing "deep find" for parent object'
642cd7
+	declare -a parents
642cd7
+	while IFS= read -r -d '' dir; do
642cd7
+		find_parent_obj_in_dir "$file" "$dir"
642cd7
+		if [[ -n $PARENT ]]; then
642cd7
+			parents+=("$PARENT")
642cd7
+			LAST_DEEP_FIND_DIR="$dir"
642cd7
+		fi
642cd7
+	done < <(find . -type d -print0)
642cd7
+
642cd7
+	get_parent_from_parents "${parents[@]}"
642cd7
 }
642cd7
 
642cd7
+# find vmlinux or .ko associated with a .o file
642cd7
 find_kobj() {
642cd7
-	arg="$1"
642cd7
+	local file="$1"
642cd7
 
642cd7
 	if [[ -n $OOT_MODULE ]]; then
642cd7
 		KOBJFILE="$OOT_MODULE"
642cd7
 		return
642cd7
 	fi
642cd7
 
642cd7
-	KOBJFILE="$arg"
642cd7
-	DEEP_FIND=0
642cd7
+	KOBJFILE="$file"
642cd7
 	ERROR_IF_DIFF=
642cd7
 	while true; do
642cd7
+		case "$KOBJFILE" in
642cd7
+			*.mod)
642cd7
+				KOBJFILE=${PARENT/.mod/.ko}
642cd7
+				[[ -e $KOBJFILE ]] || die "can't find .ko for $PARENT"
642cd7
+				return
642cd7
+				;;
642cd7
+			*.ko)
642cd7
+				return
642cd7
+				;;
642cd7
+			*/built-in.o|\
642cd7
+			*/built-in.a|\
642cd7
+			arch/x86/kernel/ebda.o|\
642cd7
+			arch/x86/kernel/head*.o|\
642cd7
+			arch/x86/kernel/platform-quirks.o|\
642cd7
+			arch/x86/lib/lib.a|\
642cd7
+			lib/lib.a)
642cd7
+				KOBJFILE=vmlinux
642cd7
+				return
642cd7
+				;;
642cd7
+		esac
642cd7
+
642cd7
 		find_parent_obj "$KOBJFILE"
642cd7
-		[[ -n "$PARENT" ]] && DEEP_FIND=0
642cd7
-		if [[ -z "$PARENT" ]]; then
642cd7
-			[[ "$KOBJFILE" = *.ko ]] && return
642cd7
-			case "$KOBJFILE" in
642cd7
-				*/built-in.o|\
642cd7
-				*/built-in.a|\
642cd7
-				arch/x86/lib/lib.a|\
642cd7
-				arch/x86/kernel/head*.o|\
642cd7
-				arch/x86/kernel/ebda.o|\
642cd7
-				arch/x86/kernel/platform-quirks.o|\
642cd7
-				lib/lib.a)
642cd7
-					KOBJFILE=vmlinux
642cd7
-					return
642cd7
-			esac
642cd7
-			if [[ "$DEEP_FIND" -eq 0 ]]; then
642cd7
-				DEEP_FIND=1
642cd7
-				continue;
642cd7
-			fi
642cd7
-			die "invalid ancestor $KOBJFILE for $arg"
642cd7
-		fi
642cd7
+		[[ -z "$PARENT" ]] && die "invalid ancestor $KOBJFILE for $file"
642cd7
 		KOBJFILE="$PARENT"
642cd7
 	done
642cd7
 }
642cd7
-- 
642cd7
2.37.3
642cd7