Blame SOURCES/0002-backport-kpatch-script-livepatch-fixups-753.patch

4707dc
From 2d9606d9b864eec8d81de6952b8816284dec0032 Mon Sep 17 00:00:00 2001
4707dc
From: Joe Lawrence <joe.lawrence@redhat.com>
4707dc
Date: Thu, 16 Nov 2017 14:21:22 -0500
4707dc
Subject: [PATCH] backport "kpatch script - livepatch fixups #753"
4707dc
4707dc
This patchset improves livepatch support, most notably adding a
4707dc
wait-for-transition loop when (un)loading patch modules and signaling of
4707dc
tasks (with a SIGSTOP/SIGCONT combo) that are stalling the transition.
4707dc
4707dc
commit a2fbce15872167cfeb2bee4dcf66c33e29e4dfe2
4707dc
Author: Joe Lawrence <joe.lawrence@redhat.com>
4707dc
Date:   Wed Nov 15 10:44:42 2017 -0500
4707dc
4707dc
    kpatch: don't complain about missing livepatch .kpatch.checksum
4707dc
4707dc
    The verify_module_checksum() function reads a kpatch-specific ELF
4707dc
    section to compare on-disk and in-memory kernel modules.  The function
4707dc
    only reports a miscompare if the .kpatch.checksum section actually
4707dc
    exists.  Livepatches don't have such section, so throw away any "Section
4707dc
    '.kpatch.checksum' was not dumped because it does not exist!" warnings
4707dc
    from readelf.
4707dc
4707dc
    Signed-off-by: Joe Lawrence <joe.lawrence@redhat.com>
4707dc
4707dc
commit fb0bc53eb7540460bc8222313d436b7537aa9952
4707dc
Author: Joe Lawrence <joe.lawrence@redhat.com>
4707dc
Date:   Wed Nov 15 10:44:42 2017 -0500
4707dc
4707dc
    kpatch: show transitioning patches and stalled tasks
4707dc
4707dc
    In 'kpatch list' output, show the current patch state: enabled,
4707dc
    disabled, and livepatch mid-transition states enabling... and
4707dc
    disabling...
4707dc
4707dc
    Also provide a list of any tasks that are stalling a livepatch
4707dc
    transition.
4707dc
4707dc
    Signed-off-by: Joe Lawrence <joe.lawrence@redhat.com>
4707dc
4707dc
commit 3582e10e42fed7a08b5cf1ce0b470723428b1386
4707dc
Author: Joe Lawrence <joe.lawrence@redhat.com>
4707dc
Date:   Wed Nov 15 10:44:42 2017 -0500
4707dc
4707dc
    kpatch: signal stalled processes
4707dc
4707dc
    Add a "signal" command line option that iterates over all processes that
4707dc
    may be holding up the current livepatch transition.  Send such processes
4707dc
    a SIGSTOP / SIGCONT combination to try and expedite the transition.
4707dc
4707dc
    Signed-off-by: Joe Lawrence <joe.lawrence@redhat.com>
4707dc
4707dc
commit 52c12cbad6cd0ca35f892a0b07519b41326ea91e
4707dc
Author: Joe Lawrence <joe.lawrence@redhat.com>
4707dc
Date:   Wed Nov 15 10:44:42 2017 -0500
4707dc
4707dc
    kpatch: wait for livepatch transitions, poke stragglers
4707dc
4707dc
    When loading a livepatch, wait for the patching transition to complete
4707dc
    within a reasonable timeframe, then poke any stalled tasks with a
4707dc
    signal.  If the transition is still taking too long, reverse the patch
4707dc
    and unload the livepatch.
4707dc
4707dc
    When re-enabling a livepatch, do the same wait and signaling.  If the
4707dc
    expected time expires, disable the livepatch.
4707dc
4707dc
    When unloading a livepatch, perform the wait/signaling, but only emit an
4707dc
    error message if the transition exceeds the time limit.
4707dc
4707dc
    Signed-off-by: Joe Lawrence <joe.lawrence@redhat.com>
4707dc
4707dc
Signed-off-by: Joe Lawrence <joe.lawrence@redhat.com>
4707dc
diff --git a/kpatch/kpatch b/kpatch/kpatch
4707dc
index 5998fbc1ba72..a2ac607f6e31 100755
4707dc
--- a/kpatch/kpatch
4707dc
+++ b/kpatch/kpatch
4707dc
@@ -26,6 +26,8 @@
4707dc
 INSTALLDIR=/var/lib/kpatch
4707dc
 SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))"
4707dc
 VERSION="0.4.0"
4707dc
+POST_ENABLE_WAIT=5	# seconds
4707dc
+POST_SIGNAL_WAIT=60	# seconds
4707dc
 
4707dc
 usage_cmd() {
4707dc
 	printf '   %-20s\n      %s\n' "$1" "$2" >&2
4707dc
@@ -49,6 +51,8 @@ usage () {
4707dc
 	echo >&2
4707dc
 	usage_cmd "list" "list installed patch modules"
4707dc
 	echo >&2
4707dc
+	usage_cmd "signal" "signal/poke any process stalling the current patch transition"
4707dc
+	echo >&2
4707dc
 	usage_cmd "version" "display the kpatch version"
4707dc
 	exit 1
4707dc
 }
4707dc
@@ -145,7 +149,7 @@ verify_module_checksum () {
4707dc
 	modname=$(get_module_name $1)
4707dc
 	[[ -z $modname ]] && return 1
4707dc
 
4707dc
-	checksum=$(readelf -p .kpatch.checksum $1 | grep '\[.*\]' | awk '{print $3}')
4707dc
+	checksum="$(readelf -p .kpatch.checksum "$1" 2>&1 | grep '\[.*\]' | awk '{print $3}')"
4707dc
 
4707dc
 	# Fail checksum match only if both exist and diverge
4707dc
 	if [[ ! -z $checksum ]] && [[ -e "$SYSFS/${modname}/checksum" ]] ; then
4707dc
@@ -156,6 +160,119 @@ verify_module_checksum () {
4707dc
 	return 0
4707dc
 }
4707dc
 
4707dc
+in_transition() {
4707dc
+	local moddir="$SYSFS/$1"
4707dc
+	[[ $(cat "$moddir/transition" 2>/dev/null) == "1" ]] && return 0
4707dc
+	return 1
4707dc
+}
4707dc
+
4707dc
+is_stalled() {
4707dc
+	local module="$1"
4707dc
+	local pid="$2"
4707dc
+	local patch_enabled
4707dc
+	local patch_state
4707dc
+
4707dc
+	patch_enabled="$(cat "$SYSFS/$module/enabled" 2>/dev/null)"
4707dc
+	patch_state="$(cat "/proc/$pid/patch_state" 2>/dev/null)"
4707dc
+
4707dc
+	# No patch transition in progress
4707dc
+	[[ "$patch_state" == "-1" ]] && return 1
4707dc
+
4707dc
+	[[ -z "$patch_enabled" ]] || [[ -z "$patch_state" ]] && return 1
4707dc
+
4707dc
+	# Stalls can be determined if the process state does not match
4707dc
+	# the transition target (ie, "enabled" and "patched", "disabled"
4707dc
+	# and "unpatched").  The state value enumerations match, so we
4707dc
+	# can just compare them directly:
4707dc
+	[[ "$patch_enabled" != "$patch_state" ]] && return 0
4707dc
+	return 1
4707dc
+}
4707dc
+
4707dc
+get_transition_patch() {
4707dc
+	local module
4707dc
+	local modname
4707dc
+	for module in "$SYSFS"/*; do
4707dc
+		modname=$(basename "$module")
4707dc
+		if in_transition "$modname" ; then
4707dc
+			echo "$modname"
4707dc
+			return
4707dc
+		fi
4707dc
+	done
4707dc
+}
4707dc
+
4707dc
+show_stalled_processes() {
4707dc
+	local module
4707dc
+	local proc_task
4707dc
+	local tid
4707dc
+
4707dc
+	module=$(get_transition_patch)
4707dc
+	[[ -z "$module" ]] && return
4707dc
+
4707dc
+	echo ""
4707dc
+	echo "Stalled processes:"
4707dc
+	for proc_task in /proc/[0-9]*/task/[0-9]*; do
4707dc
+		tid=${proc_task#*/task/}
4707dc
+		is_stalled "$module" "$tid" && echo "$tid $(cat "$proc_task"/comm 2>/dev/null)"
4707dc
+	done
4707dc
+}
4707dc
+
4707dc
+signal_stalled_processes() {
4707dc
+	local module
4707dc
+	local proc_task
4707dc
+	local tid
4707dc
+
4707dc
+	module=$(get_transition_patch)
4707dc
+	[[ -z "$module" ]] && return
4707dc
+
4707dc
+	if [[ -e "/sys/kernel/livepatch/$module/signal" ]] ; then
4707dc
+		echo 1 > "/sys/kernel/livepatch/$module/signal"
4707dc
+	else
4707dc
+		for proc_task in /proc/[0-9]*/task/[0-9]*; do
4707dc
+			tid=${proc_task#*/task/}
4707dc
+			if is_stalled "$module" "$tid" ; then
4707dc
+				if [[ "$tid" -eq "$$" ]] ; then
4707dc
+					echo "skipping pid $tid $(cat "$proc_task"/comm 2>/dev/null)"
4707dc
+				else
4707dc
+					echo "signaling pid $tid $(cat "$proc_task"/comm 2>/dev/null)"
4707dc
+					kill -SIGSTOP "$tid"
4707dc
+					sleep .1
4707dc
+					kill -SIGCONT "$tid"
4707dc
+				fi
4707dc
+			fi
4707dc
+		done
4707dc
+	fi
4707dc
+}
4707dc
+
4707dc
+wait_for_patch_transition() {
4707dc
+	local module="$1"
4707dc
+	local i
4707dc
+
4707dc
+	in_transition "$module" || return 0
4707dc
+
4707dc
+	echo "waiting (up to $POST_ENABLE_WAIT seconds) for patch transition to complete..."
4707dc
+	for (( i=0; i
4707dc
+		if ! in_transition "$module" ; then
4707dc
+			echo "transition complete ($i seconds)"
4707dc
+			return 0
4707dc
+		fi
4707dc
+		sleep 1s
4707dc
+	done
4707dc
+
4707dc
+	echo "patch transition has stalled, signaling stalled process(es):"
4707dc
+	signal_stalled_processes
4707dc
+
4707dc
+	echo "waiting (up to $POST_SIGNAL_WAIT seconds) for patch transition to complete..."
4707dc
+	for (( i=0; i
4707dc
+		if ! in_transition "$module" ; then
4707dc
+			echo "transition complete ($i seconds)"
4707dc
+			return 0
4707dc
+		fi
4707dc
+		sleep 1s
4707dc
+	done
4707dc
+
4707dc
+	return 1
4707dc
+}
4707dc
+
4707dc
 load_module () {
4707dc
 	local module="$1"
4707dc
 
4707dc
@@ -180,6 +297,13 @@ load_module () {
4707dc
 			if verify_module_checksum $module; then # same checksum
4707dc
 				echo "module already loaded, re-enabling"
4707dc
 				echo 1 > ${moddir}/enabled || die "failed to re-enable module $modname"
4707dc
+				if ! wait_for_patch_transition "$modname" ; then
4707dc
+					echo "module $modname did not complete its transition, disabling..."
4707dc
+					echo 0 > "${moddir}/enabled" || die "failed to disable module $modname"
4707dc
+					wait_for_patch_transition "$modname"
4707dc
+					die "error: failed to re-enable module $modname (transition stalled), patch disabled"
4707dc
+				fi
4707dc
+
4707dc
 				return
4707dc
 			else
4707dc
 				die "error: cannot re-enable patch module $modname, cannot verify checksum match"
4707dc
@@ -210,6 +334,12 @@ load_module () {
4707dc
 		fi
4707dc
 	done
4707dc
 
4707dc
+	if ! wait_for_patch_transition "$modname" ; then
4707dc
+		echo "module $modname did not complete its transition, unloading..."
4707dc
+		unload_module "$modname"
4707dc
+		die "error: failed to load module $modname (transition stalled)"
4707dc
+	fi
4707dc
+
4707dc
 	return 0
4707dc
 }
4707dc
 
4707dc
@@ -223,6 +353,11 @@ unload_module () {
4707dc
 		echo 0 > $ENABLED || die "can't disable $PATCH"
4707dc
 	fi
4707dc
 
4707dc
+	if ! wait_for_patch_transition "$PATCH" ; then
4707dc
+		die "error: failed to unload module $PATCH (transition stalled)"
4707dc
+	fi
4707dc
+
4707dc
+
4707dc
 	echo "unloading patch module: $PATCH"
4707dc
 	# ignore any error here because rmmod can fail if the module used
4707dc
 	# KPATCH_FORCE_UNSAFE.
4707dc
@@ -352,10 +487,19 @@ case "$1" in
4707dc
 	[[ "$#" -ne 1 ]] && usage
4707dc
 	echo "Loaded patch modules:"
4707dc
 	for module in $SYSFS/*; do
4707dc
-		if [[ -e $module ]] && [[ $(cat $module/enabled) -eq 1 ]]; then
4707dc
-			echo $(basename "$module")
4707dc
+		if [[ -e "$module" ]]; then
4707dc
+			modname=$(basename "$module")
4707dc
+			if [[ "$(cat "$module/enabled" 2>/dev/null)" -eq 1 ]]; then
4707dc
+				in_transition "$modname" && state="enabling..." \
4707dc
+							 || state="enabled"
4707dc
+			else
4707dc
+				in_transition "$modname" && state="disabling..." \
4707dc
+							 || state="disabled"
4707dc
+			fi
4707dc
+			echo "$modname [$state]"
4707dc
 		fi
4707dc
 	done
4707dc
+	show_stalled_processes
4707dc
 	echo ""
4707dc
 	echo "Installed patch modules:"
4707dc
 	for kdir in $INSTALLDIR/*; do
4707dc
@@ -376,6 +520,11 @@ case "$1" in
4707dc
 	modinfo "$MODULE" || die "failed to get info for module $PATCH"
4707dc
 	;;
4707dc
 
4707dc
+"signal")
4707dc
+	[[ "$#" -ne 1 ]] && usage
4707dc
+	signal_stalled_processes
4707dc
+	;;
4707dc
+
4707dc
 "help"|"-h"|"--help")
4707dc
 	usage
4707dc
 	;;
4707dc
-- 
4707dc
1.8.3.1