If kpatch fails to disable a patch, retry a few times

This is similar to how loading of the patches works now. Needed mostly
for the "old" kpatch, i.e. for the kernels that do not support livepatch.

If the patched functions are currently used, loading of the patch fails
with "Device or resource busy" error. kpatch script then retries the
operation several times.

In some cases, it could be convenient to do the same thing when
unloading or simply disabling the patches. One of the use cases is when
it is needed to replace a loaded cumulative patch with its previous
version, esp. if the patches have patch/unpatch hooks. It is often more
reliable to disable the loaded patches first and then load the new
patch. Disable operation may fail due to activeness safety check - so
let us retry it a few times.

v2:
As suggested in PR #790, disable_patch() no longer returns a value but
rather calls die() at the point of error.

Signed-off-by: Evgenii Shatokhin <eshatokhin@virtuozzo.com>
This commit is contained in:
Evgenii Shatokhin 2018-02-27 11:36:00 +03:00
parent 2d0fd42c64
commit 23f4e7554d

View File

@ -348,19 +348,44 @@ load_module () {
return 0
}
disable_patch () {
local modname="$1"
local enabled="$SYSFS/$modname/enabled"
[[ -e "$enabled" ]] || die "patch module $1 is not loaded"
if [[ "$(cat "$enabled")" -eq 1 ]]; then
echo "disabling patch module: $modname"
local i=0
while true; do
out="$(export LC_ALL=C; sh -c "echo 0 > $enabled" 2>&1)"
[[ -z "$out" ]] && break
echo "$out" 1>&2
if [[ ! "$out" =~ "Device or resource busy" ]]; then
die "failed to disable module $modname"
fi
# "Device or resource busy" means the activeness safety check
# failed. Retry in a few seconds.
i=$((i+1))
if [[ $i = $MAX_LOAD_ATTEMPTS ]]; then
die "failed to disable module $modname"
else
warn "retrying..."
sleep $RETRY_INTERVAL
fi
done
fi
if ! wait_for_patch_transition "$modname" ; then
die "transition stalled for $modname"
fi
}
unload_module () {
PATCH="${1//-/_}"
PATCH="${PATCH%.ko}"
ENABLED="$SYSFS/$PATCH/enabled"
[[ -e "$ENABLED" ]] || die "patch module $1 is not loaded"
if [[ "$(cat "$ENABLED")" -eq 1 ]]; then
echo "disabling patch module: $PATCH"
echo 0 > "$ENABLED" || die "can't disable $PATCH"
fi
if ! wait_for_patch_transition "$PATCH" ; then
die "error: failed to unload module $PATCH (transition stalled)"
fi
disable_patch "$PATCH"
echo "unloading patch module: $PATCH"
# ignore any error here because rmmod can fail if the module used