summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakub Kicinski <kuba@kernel.org>2025-11-12 17:19:42 +0300
committerJakub Kicinski <kuba@kernel.org>2025-11-12 17:19:42 +0300
commit4001bda0cc911fcdd3dde36963a17f4eac173d7d (patch)
tree151c207b18a413e648b0237f1fee6583c02fc460
parentfc6aa0e470e092873eddb213d996a8beee86bf4d (diff)
parent99f932c9050911fab8ac82455fc4d74b1c0200af (diff)
downloadlinux-4001bda0cc911fcdd3dde36963a17f4eac173d7d.tar.xz
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>
-rwxr-xr-xtools/testing/selftests/vsock/vmtest.sh346
1 files changed, 233 insertions, 113 deletions
diff --git a/tools/testing/selftests/vsock/vmtest.sh b/tools/testing/selftests/vsock/vmtest.sh
index 8ceeb8a7894f..c7b270dd77a9 100755
--- a/tools/testing/selftests/vsock/vmtest.sh
+++ b/tools/testing/selftests/vsock/vmtest.sh
@@ -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
+ prefix="${LOG_PREFIX:-}"
+
if [[ "$#" -eq 0 ]]; then
- __log_stdin | tee -a "${LOG}" > ${redirect}
+ if [[ -n "${prefix}" ]]; then
+ awk -v prefix="${prefix}" '{printf "%s: %s\n", prefix, $0}'
+ else
+ cat
+ fi
else
- __log_args "$@" | tee -a "${LOG}" > ${redirect}
- fi
-}
-
-log_setup() {
- log "setup" "$@"
+ 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}"
+ return "${KSFT_PASS}"
+}
- ${VSOCK_TEST} \
- --mode=client \
- --control-host=127.0.0.1 \
- --peer-cid="${VSOCK_CID}" \
- --control-port="${TEST_HOST_PORT}" 2>&1 | log_host "${testname}"
+test_vm_client_host_server() {
+ if ! host_vsock_test "server" "${VSOCK_CID}" "${TEST_HOST_PORT_LISTENER}"; then
+ return "${KSFT_FAIL}"
+ fi
- return $?
+ if ! vm_vsock_test "10.0.2.2" 2 "${TEST_HOST_PORT_LISTENER}"; then
+ return "${KSFT_FAIL}"
+ fi
+
+ return "${KSFT_PASS}"
}
-test_vm_client_host_server() {
- local testname="${FUNCNAME[0]#test_}"
+test_vm_loopback() {
+ local port=60000 # non-forwarded local port
- ${VSOCK_TEST} \
- --mode "server" \
- --control-port "${TEST_HOST_PORT_LISTENER}" \
- --peer-cid "${VSOCK_CID}" 2>&1 | log_host "${testname}" &
+ vm_ssh -- modprobe vsock_loopback &> /dev/null || :
- host_wait_for_listener
+ if ! vm_vsock_test "server" 1 "${port}"; then
+ return "${KSFT_FAIL}"
+ fi
- 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}"
+ if ! vm_vsock_test "127.0.0.1" 1 "${port}"; then
+ return "${KSFT_FAIL}"
+ fi
- return $?
+ return "${KSFT_PASS}"
}
-test_vm_loopback() {
- local testname="${FUNCNAME[0]#test_}"
- local port=60000 # non-forwarded local port
+shared_vm_test() {
+ local tname
+
+ tname="${1}"
+
+ for testname in "${USE_SHARED_VM[@]}"; do
+ if [[ "${tname}" == "${testname}" ]]; then
+ return 0
+ fi
+ done
- vm_ssh -- "${VSOCK_TEST}" \
- --mode=server \
- --control-port="${port}" \
- --peer-cid=1 2>&1 | log_guest "${testname}" &
+ return 1
+}
- vm_wait_for_listener "${port}"
+shared_vm_tests_requested() {
+ for arg in "$@"; do
+ if shared_vm_test "${arg}"; then
+ return 0
+ fi
+ done
- vm_ssh -- "${VSOCK_TEST}" \
- --mode=client \
- --control-host="127.0.0.1" \
- --control-port="${port}" \
- --peer-cid=1 2>&1 | log_guest "${testname}"
+ return 1
+}
- return $?
+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_test() {
+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}"