diff options
| author | Jakub Kicinski <kuba@kernel.org> | 2025-11-12 17:19:42 +0300 |
|---|---|---|
| committer | Jakub Kicinski <kuba@kernel.org> | 2025-11-12 17:19:42 +0300 |
| commit | 4001bda0cc911fcdd3dde36963a17f4eac173d7d (patch) | |
| tree | 151c207b18a413e648b0237f1fee6583c02fc460 | |
| parent | fc6aa0e470e092873eddb213d996a8beee86bf4d (diff) | |
| parent | 99f932c9050911fab8ac82455fc4d74b1c0200af (diff) | |
| download | linux-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-x | tools/testing/selftests/vsock/vmtest.sh | 346 |
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}" |
