Merge branch 'selftests-vsock-refactor-and-improve-vmtest-infrastructure'
Bobby Eshleman says: ==================== selftests/vsock: refactor and improve vmtest infrastructure This patch series refactors the vsock selftest VM infrastructure to improve test run times, improve logging, and prepare for future tests which make heavy usage of these refactored functions and have new requirements such as simultaneous QEMU processes. ==================== Link: https://patch.msgid.link/20251108-vsock-selftests-fixes-and-improvements-v4-0-d5e8d6c87289@meta.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
@@ -7,6 +7,8 @@
|
||||
# * virtme-ng
|
||||
# * busybox-static (used by virtme-ng)
|
||||
# * qemu (used by virtme-ng)
|
||||
#
|
||||
# shellcheck disable=SC2317,SC2119
|
||||
|
||||
readonly SCRIPT_DIR="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
|
||||
readonly KERNEL_CHECKOUT=$(realpath "${SCRIPT_DIR}"/../../../../)
|
||||
@@ -22,8 +24,9 @@ readonly SSH_HOST_PORT=2222
|
||||
readonly VSOCK_CID=1234
|
||||
readonly WAIT_PERIOD=3
|
||||
readonly WAIT_PERIOD_MAX=60
|
||||
readonly WAIT_TOTAL=$(( WAIT_PERIOD * WAIT_PERIOD_MAX ))
|
||||
readonly QEMU_PIDFILE=$(mktemp /tmp/qemu_vsock_vmtest_XXXX.pid)
|
||||
readonly WAIT_QEMU=5
|
||||
readonly PIDFILE_TEMPLATE=/tmp/vsock_vmtest_XXXX.pid
|
||||
declare -A PIDFILES
|
||||
|
||||
# virtme-ng offers a netdev for ssh when using "--ssh", but we also need a
|
||||
# control port forwarded for vsock_test. Because virtme-ng doesn't support
|
||||
@@ -33,12 +36,6 @@ readonly QEMU_PIDFILE=$(mktemp /tmp/qemu_vsock_vmtest_XXXX.pid)
|
||||
# add the kernel cmdline options that virtme-init uses to setup the interface.
|
||||
readonly QEMU_TEST_PORT_FWD="hostfwd=tcp::${TEST_HOST_PORT}-:${TEST_GUEST_PORT}"
|
||||
readonly QEMU_SSH_PORT_FWD="hostfwd=tcp::${SSH_HOST_PORT}-:${SSH_GUEST_PORT}"
|
||||
readonly QEMU_OPTS="\
|
||||
-netdev user,id=n0,${QEMU_TEST_PORT_FWD},${QEMU_SSH_PORT_FWD} \
|
||||
-device virtio-net-pci,netdev=n0 \
|
||||
-device vhost-vsock-pci,guest-cid=${VSOCK_CID} \
|
||||
--pidfile ${QEMU_PIDFILE} \
|
||||
"
|
||||
readonly KERNEL_CMDLINE="\
|
||||
virtme.dhcp net.ifnames=0 biosdevname=0 \
|
||||
virtme.ssh virtme_ssh_channel=tcp virtme_ssh_user=$USER \
|
||||
@@ -51,6 +48,8 @@ readonly TEST_DESCS=(
|
||||
"Run vsock_test using the loopback transport in the VM."
|
||||
)
|
||||
|
||||
readonly USE_SHARED_VM=(vm_server_host_client vm_client_host_server vm_loopback)
|
||||
|
||||
VERBOSE=0
|
||||
|
||||
usage() {
|
||||
@@ -84,21 +83,33 @@ die() {
|
||||
exit "${KSFT_FAIL}"
|
||||
}
|
||||
|
||||
check_result() {
|
||||
local rc arg
|
||||
|
||||
rc=$1
|
||||
arg=$2
|
||||
|
||||
cnt_total=$(( cnt_total + 1 ))
|
||||
|
||||
if [[ ${rc} -eq ${KSFT_PASS} ]]; then
|
||||
cnt_pass=$(( cnt_pass + 1 ))
|
||||
echo "ok ${cnt_total} ${arg}"
|
||||
elif [[ ${rc} -eq ${KSFT_SKIP} ]]; then
|
||||
cnt_skip=$(( cnt_skip + 1 ))
|
||||
echo "ok ${cnt_total} ${arg} # SKIP"
|
||||
elif [[ ${rc} -eq ${KSFT_FAIL} ]]; then
|
||||
cnt_fail=$(( cnt_fail + 1 ))
|
||||
echo "not ok ${cnt_total} ${arg} # exit=${rc}"
|
||||
fi
|
||||
}
|
||||
|
||||
vm_ssh() {
|
||||
ssh -q -o UserKnownHostsFile=/dev/null -p ${SSH_HOST_PORT} localhost "$@"
|
||||
return $?
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
if [[ -s "${QEMU_PIDFILE}" ]]; then
|
||||
pkill -SIGTERM -F "${QEMU_PIDFILE}" > /dev/null 2>&1
|
||||
fi
|
||||
|
||||
# If failure occurred during or before qemu start up, then we need
|
||||
# to clean this up ourselves.
|
||||
if [[ -e "${QEMU_PIDFILE}" ]]; then
|
||||
rm "${QEMU_PIDFILE}"
|
||||
fi
|
||||
terminate_pidfiles "${!PIDFILES[@]}"
|
||||
}
|
||||
|
||||
check_args() {
|
||||
@@ -147,7 +158,7 @@ check_vng() {
|
||||
local version
|
||||
local ok
|
||||
|
||||
tested_versions=("1.33" "1.36")
|
||||
tested_versions=("1.33" "1.36" "1.37")
|
||||
version="$(vng --version)"
|
||||
|
||||
ok=0
|
||||
@@ -188,10 +199,37 @@ handle_build() {
|
||||
popd &>/dev/null
|
||||
}
|
||||
|
||||
create_pidfile() {
|
||||
local pidfile
|
||||
|
||||
pidfile=$(mktemp "${PIDFILE_TEMPLATE}")
|
||||
PIDFILES["${pidfile}"]=1
|
||||
|
||||
echo "${pidfile}"
|
||||
}
|
||||
|
||||
terminate_pidfiles() {
|
||||
local pidfile
|
||||
|
||||
for pidfile in "$@"; do
|
||||
if [[ -s "${pidfile}" ]]; then
|
||||
pkill -SIGTERM -F "${pidfile}" > /dev/null 2>&1
|
||||
fi
|
||||
|
||||
if [[ -e "${pidfile}" ]]; then
|
||||
rm -f "${pidfile}"
|
||||
fi
|
||||
|
||||
unset "PIDFILES[${pidfile}]"
|
||||
done
|
||||
}
|
||||
|
||||
vm_start() {
|
||||
local pidfile=$1
|
||||
local logfile=/dev/null
|
||||
local verbose_opt=""
|
||||
local kernel_opt=""
|
||||
local qemu_opts=""
|
||||
local qemu
|
||||
|
||||
qemu=$(command -v "${QEMU}")
|
||||
@@ -201,6 +239,13 @@ vm_start() {
|
||||
logfile=/dev/stdout
|
||||
fi
|
||||
|
||||
qemu_opts="\
|
||||
-netdev user,id=n0,${QEMU_TEST_PORT_FWD},${QEMU_SSH_PORT_FWD} \
|
||||
-device virtio-net-pci,netdev=n0 \
|
||||
-device vhost-vsock-pci,guest-cid=${VSOCK_CID} \
|
||||
--pidfile ${pidfile}
|
||||
"
|
||||
|
||||
if [[ "${BUILD}" -eq 1 ]]; then
|
||||
kernel_opt="${KERNEL_CHECKOUT}"
|
||||
fi
|
||||
@@ -209,16 +254,14 @@ vm_start() {
|
||||
--run \
|
||||
${kernel_opt} \
|
||||
${verbose_opt} \
|
||||
--qemu-opts="${QEMU_OPTS}" \
|
||||
--qemu-opts="${qemu_opts}" \
|
||||
--qemu="${qemu}" \
|
||||
--user root \
|
||||
--append "${KERNEL_CMDLINE}" \
|
||||
--rw &> ${logfile} &
|
||||
|
||||
if ! timeout ${WAIT_TOTAL} \
|
||||
bash -c 'while [[ ! -s '"${QEMU_PIDFILE}"' ]]; do sleep 1; done; exit 0'; then
|
||||
die "failed to boot VM"
|
||||
fi
|
||||
timeout "${WAIT_QEMU}" \
|
||||
bash -c 'while [[ ! -s '"${pidfile}"' ]]; do sleep 1; done; exit 0'
|
||||
}
|
||||
|
||||
vm_wait_for_ssh() {
|
||||
@@ -251,9 +294,11 @@ wait_for_listener()
|
||||
|
||||
# for tcp protocol additionally check the socket state
|
||||
[ "${protocol}" = "tcp" ] && pattern="${pattern}0A"
|
||||
|
||||
for i in $(seq "${max_intervals}"); do
|
||||
if awk '{print $2" "$4}' /proc/net/"${protocol}"* | \
|
||||
grep -q "${pattern}"; then
|
||||
if awk -v pattern="${pattern}" \
|
||||
'BEGIN {rc=1} $2" "$4 ~ pattern {rc=0} END {exit rc}' \
|
||||
/proc/net/"${protocol}"*; then
|
||||
break
|
||||
fi
|
||||
sleep "${interval}"
|
||||
@@ -270,113 +315,196 @@ EOF
|
||||
}
|
||||
|
||||
host_wait_for_listener() {
|
||||
wait_for_listener "${TEST_HOST_PORT_LISTENER}" "${WAIT_PERIOD}" "${WAIT_PERIOD_MAX}"
|
||||
local port=$1
|
||||
|
||||
wait_for_listener "${port}" "${WAIT_PERIOD}" "${WAIT_PERIOD_MAX}"
|
||||
}
|
||||
|
||||
__log_stdin() {
|
||||
cat | awk '{ printf "%s:\t%s\n","'"${prefix}"'", $0 }'
|
||||
vm_vsock_test() {
|
||||
local host=$1
|
||||
local cid=$2
|
||||
local port=$3
|
||||
local rc
|
||||
|
||||
# log output and use pipefail to respect vsock_test errors
|
||||
set -o pipefail
|
||||
if [[ "${host}" != server ]]; then
|
||||
vm_ssh -- "${VSOCK_TEST}" \
|
||||
--mode=client \
|
||||
--control-host="${host}" \
|
||||
--peer-cid="${cid}" \
|
||||
--control-port="${port}" \
|
||||
2>&1 | log_guest
|
||||
rc=$?
|
||||
else
|
||||
vm_ssh -- "${VSOCK_TEST}" \
|
||||
--mode=server \
|
||||
--peer-cid="${cid}" \
|
||||
--control-port="${port}" \
|
||||
2>&1 | log_guest &
|
||||
rc=$?
|
||||
|
||||
if [[ $rc -ne 0 ]]; then
|
||||
set +o pipefail
|
||||
return $rc
|
||||
fi
|
||||
|
||||
vm_wait_for_listener "${port}"
|
||||
rc=$?
|
||||
fi
|
||||
set +o pipefail
|
||||
|
||||
return $rc
|
||||
}
|
||||
|
||||
__log_args() {
|
||||
echo "$*" | awk '{ printf "%s:\t%s\n","'"${prefix}"'", $0 }'
|
||||
host_vsock_test() {
|
||||
local host=$1
|
||||
local cid=$2
|
||||
local port=$3
|
||||
local rc
|
||||
|
||||
# log output and use pipefail to respect vsock_test errors
|
||||
set -o pipefail
|
||||
if [[ "${host}" != server ]]; then
|
||||
${VSOCK_TEST} \
|
||||
--mode=client \
|
||||
--peer-cid="${cid}" \
|
||||
--control-host="${host}" \
|
||||
--control-port="${port}" 2>&1 | log_host
|
||||
rc=$?
|
||||
else
|
||||
${VSOCK_TEST} \
|
||||
--mode=server \
|
||||
--peer-cid="${cid}" \
|
||||
--control-port="${port}" 2>&1 | log_host &
|
||||
rc=$?
|
||||
|
||||
if [[ $rc -ne 0 ]]; then
|
||||
set +o pipefail
|
||||
return $rc
|
||||
fi
|
||||
|
||||
host_wait_for_listener "${port}"
|
||||
rc=$?
|
||||
fi
|
||||
set +o pipefail
|
||||
|
||||
return $rc
|
||||
}
|
||||
|
||||
log() {
|
||||
local prefix="$1"
|
||||
local redirect
|
||||
local prefix
|
||||
|
||||
shift
|
||||
local redirect=
|
||||
if [[ ${VERBOSE} -eq 0 ]]; then
|
||||
redirect=/dev/null
|
||||
else
|
||||
redirect=/dev/stdout
|
||||
fi
|
||||
|
||||
if [[ "$#" -eq 0 ]]; then
|
||||
__log_stdin | tee -a "${LOG}" > ${redirect}
|
||||
else
|
||||
__log_args "$@" | tee -a "${LOG}" > ${redirect}
|
||||
fi
|
||||
}
|
||||
prefix="${LOG_PREFIX:-}"
|
||||
|
||||
log_setup() {
|
||||
log "setup" "$@"
|
||||
if [[ "$#" -eq 0 ]]; then
|
||||
if [[ -n "${prefix}" ]]; then
|
||||
awk -v prefix="${prefix}" '{printf "%s: %s\n", prefix, $0}'
|
||||
else
|
||||
cat
|
||||
fi
|
||||
else
|
||||
if [[ -n "${prefix}" ]]; then
|
||||
echo "${prefix}: " "$@"
|
||||
else
|
||||
echo "$@"
|
||||
fi
|
||||
fi | tee -a "${LOG}" > "${redirect}"
|
||||
}
|
||||
|
||||
log_host() {
|
||||
local testname=$1
|
||||
|
||||
shift
|
||||
log "test:${testname}:host" "$@"
|
||||
LOG_PREFIX=host log "$@"
|
||||
}
|
||||
|
||||
log_guest() {
|
||||
local testname=$1
|
||||
|
||||
shift
|
||||
log "test:${testname}:guest" "$@"
|
||||
LOG_PREFIX=guest log "$@"
|
||||
}
|
||||
|
||||
test_vm_server_host_client() {
|
||||
local testname="${FUNCNAME[0]#test_}"
|
||||
if ! vm_vsock_test "server" 2 "${TEST_GUEST_PORT}"; then
|
||||
return "${KSFT_FAIL}"
|
||||
fi
|
||||
|
||||
vm_ssh -- "${VSOCK_TEST}" \
|
||||
--mode=server \
|
||||
--control-port="${TEST_GUEST_PORT}" \
|
||||
--peer-cid=2 \
|
||||
2>&1 | log_guest "${testname}" &
|
||||
if ! host_vsock_test "127.0.0.1" "${VSOCK_CID}" "${TEST_HOST_PORT}"; then
|
||||
return "${KSFT_FAIL}"
|
||||
fi
|
||||
|
||||
vm_wait_for_listener "${TEST_GUEST_PORT}"
|
||||
|
||||
${VSOCK_TEST} \
|
||||
--mode=client \
|
||||
--control-host=127.0.0.1 \
|
||||
--peer-cid="${VSOCK_CID}" \
|
||||
--control-port="${TEST_HOST_PORT}" 2>&1 | log_host "${testname}"
|
||||
|
||||
return $?
|
||||
return "${KSFT_PASS}"
|
||||
}
|
||||
|
||||
test_vm_client_host_server() {
|
||||
local testname="${FUNCNAME[0]#test_}"
|
||||
if ! host_vsock_test "server" "${VSOCK_CID}" "${TEST_HOST_PORT_LISTENER}"; then
|
||||
return "${KSFT_FAIL}"
|
||||
fi
|
||||
|
||||
${VSOCK_TEST} \
|
||||
--mode "server" \
|
||||
--control-port "${TEST_HOST_PORT_LISTENER}" \
|
||||
--peer-cid "${VSOCK_CID}" 2>&1 | log_host "${testname}" &
|
||||
if ! vm_vsock_test "10.0.2.2" 2 "${TEST_HOST_PORT_LISTENER}"; then
|
||||
return "${KSFT_FAIL}"
|
||||
fi
|
||||
|
||||
host_wait_for_listener
|
||||
|
||||
vm_ssh -- "${VSOCK_TEST}" \
|
||||
--mode=client \
|
||||
--control-host=10.0.2.2 \
|
||||
--peer-cid=2 \
|
||||
--control-port="${TEST_HOST_PORT_LISTENER}" 2>&1 | log_guest "${testname}"
|
||||
|
||||
return $?
|
||||
return "${KSFT_PASS}"
|
||||
}
|
||||
|
||||
test_vm_loopback() {
|
||||
local testname="${FUNCNAME[0]#test_}"
|
||||
local port=60000 # non-forwarded local port
|
||||
|
||||
vm_ssh -- "${VSOCK_TEST}" \
|
||||
--mode=server \
|
||||
--control-port="${port}" \
|
||||
--peer-cid=1 2>&1 | log_guest "${testname}" &
|
||||
vm_ssh -- modprobe vsock_loopback &> /dev/null || :
|
||||
|
||||
vm_wait_for_listener "${port}"
|
||||
if ! vm_vsock_test "server" 1 "${port}"; then
|
||||
return "${KSFT_FAIL}"
|
||||
fi
|
||||
|
||||
vm_ssh -- "${VSOCK_TEST}" \
|
||||
--mode=client \
|
||||
--control-host="127.0.0.1" \
|
||||
--control-port="${port}" \
|
||||
--peer-cid=1 2>&1 | log_guest "${testname}"
|
||||
if ! vm_vsock_test "127.0.0.1" 1 "${port}"; then
|
||||
return "${KSFT_FAIL}"
|
||||
fi
|
||||
|
||||
return $?
|
||||
return "${KSFT_PASS}"
|
||||
}
|
||||
|
||||
run_test() {
|
||||
shared_vm_test() {
|
||||
local tname
|
||||
|
||||
tname="${1}"
|
||||
|
||||
for testname in "${USE_SHARED_VM[@]}"; do
|
||||
if [[ "${tname}" == "${testname}" ]]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
shared_vm_tests_requested() {
|
||||
for arg in "$@"; do
|
||||
if shared_vm_test "${arg}"; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
run_shared_vm_tests() {
|
||||
local arg
|
||||
|
||||
for arg in "$@"; do
|
||||
if ! shared_vm_test "${arg}"; then
|
||||
continue
|
||||
fi
|
||||
|
||||
run_shared_vm_test "${arg}"
|
||||
check_result "$?" "${arg}"
|
||||
done
|
||||
}
|
||||
|
||||
run_shared_vm_test() {
|
||||
local host_oops_cnt_before
|
||||
local host_warn_cnt_before
|
||||
local vm_oops_cnt_before
|
||||
@@ -399,31 +527,32 @@ run_test() {
|
||||
|
||||
host_oops_cnt_after=$(dmesg | grep -i 'Oops' | wc -l)
|
||||
if [[ ${host_oops_cnt_after} -gt ${host_oops_cnt_before} ]]; then
|
||||
echo "FAIL: kernel oops detected on host" | log_host "${name}"
|
||||
echo "FAIL: kernel oops detected on host" | log_host
|
||||
rc=$KSFT_FAIL
|
||||
fi
|
||||
|
||||
host_warn_cnt_after=$(dmesg --level=warn | grep -c -i 'vsock')
|
||||
if [[ ${host_warn_cnt_after} -gt ${host_warn_cnt_before} ]]; then
|
||||
echo "FAIL: kernel warning detected on host" | log_host "${name}"
|
||||
echo "FAIL: kernel warning detected on host" | log_host
|
||||
rc=$KSFT_FAIL
|
||||
fi
|
||||
|
||||
vm_oops_cnt_after=$(vm_ssh -- dmesg | grep -i 'Oops' | wc -l)
|
||||
if [[ ${vm_oops_cnt_after} -gt ${vm_oops_cnt_before} ]]; then
|
||||
echo "FAIL: kernel oops detected on vm" | log_host "${name}"
|
||||
echo "FAIL: kernel oops detected on vm" | log_host
|
||||
rc=$KSFT_FAIL
|
||||
fi
|
||||
|
||||
vm_warn_cnt_after=$(vm_ssh -- dmesg --level=warn | grep -c -i 'vsock')
|
||||
if [[ ${vm_warn_cnt_after} -gt ${vm_warn_cnt_before} ]]; then
|
||||
echo "FAIL: kernel warning detected on vm" | log_host "${name}"
|
||||
echo "FAIL: kernel warning detected on vm" | log_host
|
||||
rc=$KSFT_FAIL
|
||||
fi
|
||||
|
||||
return "${rc}"
|
||||
}
|
||||
|
||||
BUILD=0
|
||||
QEMU="qemu-system-$(uname -m)"
|
||||
|
||||
while getopts :hvsq:b o
|
||||
@@ -452,30 +581,21 @@ handle_build
|
||||
|
||||
echo "1..${#ARGS[@]}"
|
||||
|
||||
log_setup "Booting up VM"
|
||||
vm_start
|
||||
vm_wait_for_ssh
|
||||
log_setup "VM booted up"
|
||||
|
||||
cnt_pass=0
|
||||
cnt_fail=0
|
||||
cnt_skip=0
|
||||
cnt_total=0
|
||||
for arg in "${ARGS[@]}"; do
|
||||
run_test "${arg}"
|
||||
rc=$?
|
||||
if [[ ${rc} -eq $KSFT_PASS ]]; then
|
||||
cnt_pass=$(( cnt_pass + 1 ))
|
||||
echo "ok ${cnt_total} ${arg}"
|
||||
elif [[ ${rc} -eq $KSFT_SKIP ]]; then
|
||||
cnt_skip=$(( cnt_skip + 1 ))
|
||||
echo "ok ${cnt_total} ${arg} # SKIP"
|
||||
elif [[ ${rc} -eq $KSFT_FAIL ]]; then
|
||||
cnt_fail=$(( cnt_fail + 1 ))
|
||||
echo "not ok ${cnt_total} ${arg} # exit=$rc"
|
||||
fi
|
||||
cnt_total=$(( cnt_total + 1 ))
|
||||
done
|
||||
|
||||
if shared_vm_tests_requested "${ARGS[@]}"; then
|
||||
log_host "Booting up VM"
|
||||
pidfile="$(create_pidfile)"
|
||||
vm_start "${pidfile}"
|
||||
vm_wait_for_ssh
|
||||
log_host "VM booted up"
|
||||
|
||||
run_shared_vm_tests "${ARGS[@]}"
|
||||
terminate_pidfiles "${pidfile}"
|
||||
fi
|
||||
|
||||
echo "SUMMARY: PASS=${cnt_pass} SKIP=${cnt_skip} FAIL=${cnt_fail}"
|
||||
echo "Log: ${LOG}"
|
||||
|
||||
Reference in New Issue
Block a user