summaryrefslogtreecommitdiffstats
path: root/src/spdk/test/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/spdk/test/common')
-rw-r--r--src/spdk/test/common/applications.sh24
-rwxr-xr-xsrc/spdk/test/common/autotest_common.sh1350
-rw-r--r--src/spdk/test/common/config/README.md104
-rw-r--r--src/spdk/test/common/config/pkgdep/apt-get100
-rw-r--r--src/spdk/test/common/config/pkgdep/dnf72
-rw-r--r--src/spdk/test/common/config/pkgdep/git325
-rw-r--r--src/spdk/test/common/config/pkgdep/pacman62
-rw-r--r--src/spdk/test/common/config/pkgdep/pkg27
-rw-r--r--src/spdk/test/common/config/pkgdep/swupd21
-rw-r--r--src/spdk/test/common/config/pkgdep/yum67
-rw-r--r--src/spdk/test/common/config/vm_setup.conf12
-rwxr-xr-xsrc/spdk/test/common/config/vm_setup.sh176
-rw-r--r--src/spdk/test/common/lib/nvme/common_stubs.h117
-rw-r--r--src/spdk/test/common/lib/test_env.c637
-rw-r--r--src/spdk/test/common/lib/test_rdma.c49
-rw-r--r--src/spdk/test/common/lib/test_sock.c70
-rw-r--r--src/spdk/test/common/lib/ut_multithread.c214
-rw-r--r--src/spdk/test/common/skipped_build_files.txt60
-rw-r--r--src/spdk/test/common/skipped_tests.txt73
19 files changed, 3560 insertions, 0 deletions
diff --git a/src/spdk/test/common/applications.sh b/src/spdk/test/common/applications.sh
new file mode 100644
index 000000000..041af2932
--- /dev/null
+++ b/src/spdk/test/common/applications.sh
@@ -0,0 +1,24 @@
+# Default set of apps used in functional testing
+
+_root=$(readlink -f "$(dirname "${BASH_SOURCE[0]}")")
+_root=${_root%/test/common}
+_app_dir=$_root/build/bin
+_test_app_dir=$_root/test/app
+
+VHOST_FUZZ_APP=("$_test_app_dir/fuzz/vhost_fuzz/vhost_fuzz")
+ISCSI_APP=("$_app_dir/iscsi_tgt")
+NVMF_APP=("$_app_dir/nvmf_tgt")
+VHOST_APP=("$_app_dir/vhost")
+DD_APP=("$_app_dir/spdk_dd")
+
+# Check if apps should execute under debug flags
+if [[ -e $_root/include/spdk/config.h ]]; then
+ if [[ $(< "$_root/include/spdk/config.h") == *"#define SPDK_CONFIG_DEBUG"* ]] \
+ && ((SPDK_AUTOTEST_DEBUG_APPS)); then
+ VHOST_FUZZ_APP+=("--logflag=all")
+ ISCSI_APP+=("--logflag=all")
+ NVMF_APP+=("--logflag=all")
+ VHOST_APP+=("--logflag=all")
+ DD_APP+=("--logflag=all")
+ fi
+fi
diff --git a/src/spdk/test/common/autotest_common.sh b/src/spdk/test/common/autotest_common.sh
new file mode 100755
index 000000000..32cd4e32a
--- /dev/null
+++ b/src/spdk/test/common/autotest_common.sh
@@ -0,0 +1,1350 @@
+#!/usr/bin/env bash
+
+function xtrace_disable() {
+ if [ "$XTRACE_DISABLED" != "yes" ]; then
+ PREV_BASH_OPTS="$-"
+ if [[ "$PREV_BASH_OPTS" == *"x"* ]]; then
+ XTRACE_DISABLED="yes"
+ fi
+ set +x
+ elif [ -z $XTRACE_NESTING_LEVEL ]; then
+ XTRACE_NESTING_LEVEL=1
+ else
+ XTRACE_NESTING_LEVEL=$((++XTRACE_NESTING_LEVEL))
+ fi
+}
+
+xtrace_disable
+set -e
+shopt -s expand_aliases
+
+source "$rootdir/test/common/applications.sh"
+if [[ -e $rootdir/test/common/build_config.sh ]]; then
+ source "$rootdir/test/common/build_config.sh"
+elif [[ -e $rootdir/mk/config.mk ]]; then
+ build_config=$(< "$rootdir/mk/config.mk")
+ source <(echo "${build_config//\?=/=}")
+else
+ source "$rootdir/CONFIG"
+fi
+
+# Dummy function to be called after restoring xtrace just so that it appears in the
+# xtrace log. This way we can consistently track when xtrace is enabled/disabled.
+function xtrace_enable() {
+ # We have to do something inside a function in bash, and calling any command
+ # (even `:`) will produce an xtrace entry, so we just define another function.
+ function xtrace_dummy() { :; }
+}
+
+# Keep it as alias to avoid xtrace_enable backtrace always pointing to xtrace_restore.
+# xtrace_enable will appear as called directly from the user script, from the same line
+# that "called" xtrace_restore.
+alias xtrace_restore='if [ -z $XTRACE_NESTING_LEVEL ]; then
+ if [[ "$PREV_BASH_OPTS" == *"x"* ]]; then
+ XTRACE_DISABLED="no"; PREV_BASH_OPTS=""; set -x; xtrace_enable;
+ fi
+else
+ XTRACE_NESTING_LEVEL=$((--XTRACE_NESTING_LEVEL));
+ if [ $XTRACE_NESTING_LEVEL -eq "0" ]; then
+ unset XTRACE_NESTING_LEVEL
+ fi
+fi'
+
+: ${RUN_NIGHTLY:=0}
+export RUN_NIGHTLY
+
+# Set defaults for missing test config options
+: ${SPDK_AUTOTEST_DEBUG_APPS:=0}
+export SPDK_AUTOTEST_DEBUG_APPS
+: ${SPDK_RUN_VALGRIND=0}
+export SPDK_RUN_VALGRIND
+: ${SPDK_RUN_FUNCTIONAL_TEST=0}
+export SPDK_RUN_FUNCTIONAL_TEST
+: ${SPDK_TEST_UNITTEST=0}
+export SPDK_TEST_UNITTEST
+: ${SPDK_TEST_AUTOBUILD=0}
+export SPDK_TEST_AUTOBUILD
+: ${SPDK_TEST_ISAL=0}
+export SPDK_TEST_ISAL
+: ${SPDK_TEST_ISCSI=0}
+export SPDK_TEST_ISCSI
+: ${SPDK_TEST_ISCSI_INITIATOR=0}
+export SPDK_TEST_ISCSI_INITIATOR
+: ${SPDK_TEST_NVME=0}
+export SPDK_TEST_NVME
+: ${SPDK_TEST_NVME_CLI=0}
+export SPDK_TEST_NVME_CLI
+: ${SPDK_TEST_NVME_CUSE=0}
+export SPDK_TEST_NVME_CUSE
+: ${SPDK_TEST_NVMF=0}
+export SPDK_TEST_NVMF
+: ${SPDK_TEST_NVMF_TRANSPORT="rdma"}
+export SPDK_TEST_NVMF_TRANSPORT
+: ${SPDK_TEST_RBD=0}
+export SPDK_TEST_RBD
+: ${SPDK_TEST_VHOST=0}
+export SPDK_TEST_VHOST
+: ${SPDK_TEST_BLOCKDEV=0}
+export SPDK_TEST_BLOCKDEV
+: ${SPDK_TEST_IOAT=0}
+export SPDK_TEST_IOAT
+: ${SPDK_TEST_BLOBFS=0}
+export SPDK_TEST_BLOBFS
+: ${SPDK_TEST_VHOST_INIT=0}
+export SPDK_TEST_VHOST_INIT
+: ${SPDK_TEST_PMDK=0}
+export SPDK_TEST_PMDK
+: ${SPDK_TEST_LVOL=0}
+export SPDK_TEST_LVOL
+: ${SPDK_TEST_JSON=0}
+export SPDK_TEST_JSON
+: ${SPDK_TEST_REDUCE=0}
+export SPDK_TEST_REDUCE
+: ${SPDK_TEST_VPP=0}
+export SPDK_TEST_VPP
+: ${SPDK_RUN_ASAN=0}
+export SPDK_RUN_ASAN
+: ${SPDK_RUN_UBSAN=0}
+export SPDK_RUN_UBSAN
+: ${SPDK_RUN_INSTALLED_DPDK=0}
+export SPDK_RUN_INSTALLED_DPDK
+: ${SPDK_RUN_NON_ROOT=0}
+export SPDK_RUN_NON_ROOT
+: ${SPDK_TEST_CRYPTO=0}
+export SPDK_TEST_CRYPTO
+: ${SPDK_TEST_FTL=0}
+export SPDK_TEST_FTL
+: ${SPDK_TEST_OCF=0}
+export SPDK_TEST_OCF
+: ${SPDK_TEST_FTL_EXTENDED=0}
+export SPDK_TEST_FTL_EXTENDED
+: ${SPDK_TEST_VMD=0}
+export SPDK_TEST_VMD
+: ${SPDK_TEST_OPAL=0}
+export SPDK_TEST_OPAL
+: ${SPDK_AUTOTEST_X=true}
+export SPDK_AUTOTEST_X
+: ${SPDK_TEST_RAID5=0}
+export SPDK_TEST_RAID5
+: ${SPDK_TEST_URING=0}
+export SPDK_TEST_URING
+
+# Export PYTHONPATH with addition of RPC framework. New scripts can be created
+# specific use cases for tests.
+export PYTHONPATH=$PYTHONPATH:$rootdir/scripts
+
+# Don't create Python .pyc files. When running with sudo these will be
+# created with root ownership and can cause problems when cleaning the repository.
+export PYTHONDONTWRITEBYTECODE=1
+
+# Export flag to skip the known bug that exists in librados
+# Bug is reported on ceph bug tracker with number 24078
+export ASAN_OPTIONS=new_delete_type_mismatch=0
+export UBSAN_OPTIONS='halt_on_error=1:print_stacktrace=1:abort_on_error=1'
+
+# Export LeakSanitizer option to use suppression file in order to prevent false positives
+# and known leaks in external executables or libraries from showing up.
+asan_suppression_file="/var/tmp/asan_suppression_file"
+sudo rm -rf "$asan_suppression_file"
+cat << EOL >> "$asan_suppression_file"
+# ASAN has some bugs around thread_local variables. We have a destructor in place
+# to free the thread contexts, but ASAN complains about the leak before those
+# destructors have a chance to run. So suppress this one specific leak using
+# LSAN_OPTIONS.
+leak:spdk_fs_alloc_thread_ctx
+
+# Suppress known leaks in fio project
+leak:$CONFIG_FIO_SOURCE_DIR/parse.c
+leak:$CONFIG_FIO_SOURCE_DIR/iolog.c
+leak:$CONFIG_FIO_SOURCE_DIR/init.c
+leak:$CONFIG_FIO_SOURCE_DIR/filesetup.c
+leak:fio_memalign
+leak:spdk_fio_io_u_init
+
+# Suppress leaks in libiscsi
+leak:libiscsi.so
+EOL
+
+# Suppress leaks in libfuse3
+echo "leak:libfuse3.so" >> "$asan_suppression_file"
+
+export LSAN_OPTIONS=suppressions="$asan_suppression_file"
+
+export DEFAULT_RPC_ADDR="/var/tmp/spdk.sock"
+
+if [ -z "$DEPENDENCY_DIR" ]; then
+ export DEPENDENCY_DIR=/home/sys_sgsw
+else
+ export DEPENDENCY_DIR
+fi
+
+# Export location of where all the SPDK binaries are
+export SPDK_BIN_DIR="$rootdir/build/bin"
+export SPDK_EXAMPLE_DIR="$rootdir/build/examples"
+
+# pass our valgrind desire on to unittest.sh
+if [ $SPDK_RUN_VALGRIND -eq 0 ]; then
+ export valgrind=''
+fi
+
+if [ "$(uname -s)" = "Linux" ]; then
+ MAKE="make"
+ MAKEFLAGS=${MAKEFLAGS:--j$(nproc)}
+ DPDK_LINUX_DIR=/usr/share/dpdk/x86_64-default-linuxapp-gcc
+ if [ -d $DPDK_LINUX_DIR ] && [ $SPDK_RUN_INSTALLED_DPDK -eq 1 ]; then
+ WITH_DPDK_DIR=$DPDK_LINUX_DIR
+ fi
+ # Override the default HUGEMEM in scripts/setup.sh to allocate 8GB in hugepages.
+ export HUGEMEM=8192
+elif [ "$(uname -s)" = "FreeBSD" ]; then
+ MAKE="gmake"
+ MAKEFLAGS=${MAKEFLAGS:--j$(sysctl -a | grep -E -i 'hw.ncpu' | awk '{print $2}')}
+ DPDK_FREEBSD_DIR=/usr/local/share/dpdk/x86_64-native-bsdapp-clang
+ if [ -d $DPDK_FREEBSD_DIR ] && [ $SPDK_RUN_INSTALLED_DPDK -eq 1 ]; then
+ WITH_DPDK_DIR=$DPDK_FREEBSD_DIR
+ fi
+ # FreeBSD runs a much more limited set of tests, so keep the default 2GB.
+ export HUGEMEM=2048
+else
+ echo "Unknown OS \"$(uname -s)\""
+ exit 1
+fi
+
+if [ -z "$output_dir" ]; then
+ if [ -z "$rootdir" ] || [ ! -d "$rootdir/../output" ]; then
+ output_dir=.
+ else
+ output_dir=$rootdir/../output
+ fi
+ export output_dir
+fi
+
+TEST_MODE=
+for i in "$@"; do
+ case "$i" in
+ --iso)
+ TEST_MODE=iso
+ ;;
+ --transport=*)
+ TEST_TRANSPORT="${i#*=}"
+ ;;
+ --sock=*)
+ TEST_SOCK="${i#*=}"
+ ;;
+ esac
+done
+
+# start rpc.py coprocess if it's not started yet
+if [[ -z $RPC_PIPE_PID ]] || ! kill -0 "$RPC_PIPE_PID" &> /dev/null; then
+ coproc RPC_PIPE { "$rootdir/scripts/rpc.py" --server; }
+ exec {RPC_PIPE_OUTPUT}<&${RPC_PIPE[0]} {RPC_PIPE_INPUT}>&${RPC_PIPE[1]}
+ # all descriptors will automatically close together with this bash
+ # process, this will make rpc.py stop reading and exit gracefully
+fi
+
+if [ $SPDK_TEST_VPP -eq 1 ]; then
+ VPP_PATH="/usr/local/src/vpp-19.04/build-root/install-vpp_debug-native/vpp/"
+ export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:${VPP_PATH}/lib/
+ export PATH=${PATH}:${VPP_PATH}/bin/
+fi
+
+function set_test_storage() {
+ [[ -v testdir ]] || return 0
+
+ local requested_size=$1 # bytes
+ local mount target_dir
+
+ local -A mounts fss sizes avails uses
+ local source fs size avail mount use
+
+ local storage_fallback storage_candidates
+ local storage_fallback_purge
+
+ storage_fallback_purge=("${TMPDIR:-/tmp}/spdk."??????)
+
+ if ((${#storage_fallback_purge[@]} > 0)); then
+ printf '* Purging old temporary test storage (%s)\n' \
+ "${storage_fallback_purge[*]}" >&2
+ rm -rf "${storage_fallback_purge[@]}"
+ fi
+
+ storage_fallback=$(mktemp -udt spdk.XXXXXX)
+ storage_candidates=(
+ "$testdir"
+ "$storage_fallback/tests/${testdir##*/}"
+ "$storage_fallback"
+ )
+
+ if [[ -n $ADD_TEST_STORAGE ]]; then
+ # List of dirs|mounts separated by whitespaces
+ storage_candidates+=($ADD_TEST_STORAGE)
+ fi
+
+ if [[ -n $DEDICATED_TEST_STORAGE ]]; then
+ # Single, dedicated dir|mount
+ storage_candidates=("$DEDICATED_TEST_STORAGE")
+ fi
+
+ mkdir -p "${storage_candidates[@]}"
+
+ # add some headroom - 64M
+ requested_size=$((requested_size + (64 << 20)))
+
+ while read -r source fs size use avail _ mount; do
+ mounts["$mount"]=$source fss["$mount"]=$fs
+ avails["$mount"]=$((avail * 1024)) sizes["$mount"]=$((size * 1024))
+ uses["$mount"]=$((use * 1024))
+ done < <(df -T | grep -v Filesystem)
+
+ printf '* Looking for test storage...\n' >&2
+
+ local target_space new_size
+ for target_dir in "${storage_candidates[@]}"; do
+ # FreeBSD's df is lacking the --output arg
+ # mount=$(df --output=target "$target_dir" | grep -v "Mounted on")
+ mount=$(df "$target_dir" | awk '$1 !~ /Filesystem/{print $6}')
+
+ target_space=${avails["$mount"]}
+ if ((target_space == 0 || target_space < requested_size)); then
+ continue
+ fi
+ if ((target_space >= requested_size)); then
+ # For in-memory fs, and / make sure our requested size won't fill most of the space.
+ if [[ ${fss["$mount"]} == tmpfs ]] || [[ ${fss["$mount"]} == ramfs ]] || [[ $mount == / ]]; then
+ new_size=$((uses["$mount"] + requested_size))
+ if ((new_size * 100 / sizes["$mount"] > 95)); then
+ continue
+ fi
+ fi
+ fi
+ export SPDK_TEST_STORAGE=$target_dir
+ printf '* Found test storage at %s\n' "$SPDK_TEST_STORAGE" >&2
+ return 0
+ done
+ printf '* Test storage is not available\n'
+ return 1
+}
+
+function get_config_params() {
+ xtrace_disable
+ config_params='--enable-debug --enable-werror'
+
+ # for options with dependencies but no test flag, set them here
+ if [ -f /usr/include/infiniband/verbs.h ]; then
+ config_params+=' --with-rdma'
+ fi
+
+ if [ $(uname -s) == "FreeBSD" ]; then
+ intel="hw.model: Intel"
+ cpu_vendor=$(sysctl -a | grep hw.model | cut -c 1-15)
+ else
+ intel="GenuineIntel"
+ cpu_vendor=$(grep -i 'vendor' /proc/cpuinfo --max-count=1)
+ fi
+ if [[ "$cpu_vendor" != *"$intel"* ]]; then
+ config_params+=" --without-idxd"
+ else
+ config_params+=" --with-idxd"
+ fi
+
+ if [[ -d $CONFIG_FIO_SOURCE_DIR ]]; then
+ config_params+=" --with-fio=$CONFIG_FIO_SOURCE_DIR"
+ fi
+
+ if [ -d ${DEPENDENCY_DIR}/vtune_codes ]; then
+ config_params+=' --with-vtune='${DEPENDENCY_DIR}'/vtune_codes'
+ fi
+
+ if [ -d /usr/include/iscsi ]; then
+ libiscsi_version=$(grep LIBISCSI_API_VERSION /usr/include/iscsi/iscsi.h | head -1 | awk '{print $3}' | awk -F '(' '{print $2}' | awk -F ')' '{print $1}')
+ if [ $libiscsi_version -ge 20150621 ]; then
+ config_params+=' --with-iscsi-initiator'
+ fi
+ fi
+
+ if [ $SPDK_TEST_UNITTEST -eq 0 ]; then
+ config_params+=' --disable-unit-tests'
+ fi
+
+ if [ $SPDK_TEST_NVME_CUSE -eq 1 ]; then
+ config_params+=' --with-nvme-cuse'
+ fi
+
+ # for options with both dependencies and a test flag, set them here
+ if [ -f /usr/include/libpmemblk.h ] && [ $SPDK_TEST_PMDK -eq 1 ]; then
+ config_params+=' --with-pmdk'
+ fi
+
+ if [ -f /usr/include/libpmem.h ] && [ $SPDK_TEST_REDUCE -eq 1 ]; then
+ if [ $SPDK_TEST_ISAL -eq 1 ]; then
+ config_params+=' --with-reduce'
+ fi
+ fi
+
+ if [ -d /usr/include/rbd ] && [ -d /usr/include/rados ] && [ $SPDK_TEST_RBD -eq 1 ]; then
+ config_params+=' --with-rbd'
+ fi
+
+ if [ $SPDK_TEST_VPP -eq 1 ]; then
+ config_params+=" --with-vpp=${VPP_PATH}"
+ fi
+
+ # for options with no required dependencies, just test flags, set them here
+ if [ $SPDK_TEST_CRYPTO -eq 1 ]; then
+ config_params+=' --with-crypto'
+ fi
+
+ if [ $SPDK_TEST_OCF -eq 1 ]; then
+ config_params+=" --with-ocf"
+ fi
+
+ if [ $SPDK_RUN_UBSAN -eq 1 ]; then
+ config_params+=' --enable-ubsan'
+ fi
+
+ if [ $SPDK_RUN_ASAN -eq 1 ]; then
+ config_params+=' --enable-asan'
+ fi
+
+ if [ "$(uname -s)" = "Linux" ]; then
+ config_params+=' --enable-coverage'
+ fi
+
+ if [ $SPDK_TEST_ISAL -eq 0 ]; then
+ config_params+=' --without-isal'
+ fi
+
+ if [ $SPDK_TEST_BLOBFS -eq 1 ]; then
+ if [[ -d /usr/include/fuse3 ]] || [[ -d /usr/local/include/fuse3 ]]; then
+ config_params+=' --with-fuse'
+ fi
+ fi
+
+ if [ $SPDK_TEST_RAID5 -eq 1 ]; then
+ config_params+=' --with-raid5'
+ fi
+
+ # Check whether liburing library header exists
+ if [ -f /usr/include/liburing/io_uring.h ] && [ $SPDK_TEST_URING -eq 1 ]; then
+ config_params+=' --with-uring'
+ fi
+
+ # By default, --with-dpdk is not set meaning the SPDK build will use the DPDK submodule.
+ # If a DPDK installation is found in a well-known location though, WITH_DPDK_DIR will be
+ # set which will override the default and use that DPDK installation instead.
+ if [ -n "$WITH_DPDK_DIR" ]; then
+ config_params+=" --with-dpdk=$WITH_DPDK_DIR"
+ fi
+
+ echo "$config_params"
+ xtrace_restore
+}
+
+function rpc_cmd() {
+ xtrace_disable
+ local rsp rc
+
+ echo "$@" >&$RPC_PIPE_INPUT
+ while read -t 5 -ru $RPC_PIPE_OUTPUT rsp; do
+ if [[ $rsp == "**STATUS="* ]]; then
+ break
+ fi
+ echo "$rsp"
+ done
+
+ rc=${rsp#*=}
+ xtrace_restore
+ [[ $rc == 0 ]]
+}
+
+function rpc_cmd_simple_data_json() {
+
+ local elems="$1[@]" elem
+ local -gA jq_out=()
+ local jq val
+
+ local lvs=(
+ "uuid"
+ "name"
+ "base_bdev"
+ "total_data_clusters"
+ "free_clusters"
+ "block_size"
+ "cluster_size"
+ )
+
+ local bdev=(
+ "name"
+ "aliases[0]"
+ "block_size"
+ "num_blocks"
+ "uuid"
+ "product_name"
+ )
+
+ [[ -v $elems ]] || return 1
+
+ for elem in "${!elems}"; do
+ jq="${jq:+$jq,\"\\n\",}\"$elem\",\" \",.[0].$elem"
+ done
+ jq+=',"\n"'
+
+ shift
+ while read -r elem val; do
+ jq_out["$elem"]=$val
+ done < <(rpc_cmd "$@" | jq -jr "$jq")
+ ((${#jq_out[@]} > 0)) || return 1
+}
+
+# invert error code of any command and also trigger ERR on 0 (unlike bash ! prefix)
+function NOT() {
+ if "$@"; then
+ return 1
+ fi
+}
+
+function timing() {
+ direction="$1"
+ testname="$2"
+
+ now=$(date +%s)
+
+ if [ "$direction" = "enter" ]; then
+ export timing_stack="${timing_stack};${now}"
+ export test_stack="${test_stack};${testname}"
+ else
+ touch "$output_dir/timing.txt"
+ child_time=$(grep "^${test_stack:1};" $output_dir/timing.txt | awk '{s+=$2} END {print s}')
+
+ start_time=$(echo "$timing_stack" | sed -e 's@^.*;@@')
+ timing_stack=$(echo "$timing_stack" | sed -e 's@;[^;]*$@@')
+
+ elapsed=$((now - start_time - child_time))
+ echo "${test_stack:1} $elapsed" >> $output_dir/timing.txt
+
+ test_stack=$(echo "$test_stack" | sed -e 's@;[^;]*$@@')
+ fi
+}
+
+function timing_enter() {
+ xtrace_disable
+ timing "enter" "$1"
+ xtrace_restore
+}
+
+function timing_exit() {
+ xtrace_disable
+ timing "exit" "$1"
+ xtrace_restore
+}
+
+function timing_finish() {
+ flamegraph='/usr/local/FlameGraph/flamegraph.pl'
+ if [ -x "$flamegraph" ]; then
+ "$flamegraph" \
+ --title 'Build Timing' \
+ --nametype 'Step:' \
+ --countname seconds \
+ $output_dir/timing.txt \
+ > $output_dir/timing.svg
+ fi
+}
+
+function create_test_list() {
+ xtrace_disable
+ # First search all scripts in main SPDK directory.
+ completion=$(grep -shI -d skip --include="*.sh" -e "run_test " $rootdir/*)
+ # Follow up with search in test directory recursively.
+ completion+=$(grep -rshI --include="*.sh" --exclude="autotest_common.sh" -e "run_test " $rootdir/test)
+ printf "%s" "$completion" | grep -v "#" \
+ | sed 's/^.*run_test/run_test/' | awk '{print $2}' \
+ | sed 's/\"//g' | sort > $output_dir/all_tests.txt || true
+ xtrace_restore
+}
+
+function gdb_attach() {
+ gdb -q --batch \
+ -ex 'handle SIGHUP nostop pass' \
+ -ex 'handle SIGQUIT nostop pass' \
+ -ex 'handle SIGPIPE nostop pass' \
+ -ex 'handle SIGALRM nostop pass' \
+ -ex 'handle SIGTERM nostop pass' \
+ -ex 'handle SIGUSR1 nostop pass' \
+ -ex 'handle SIGUSR2 nostop pass' \
+ -ex 'handle SIGCHLD nostop pass' \
+ -ex 'set print thread-events off' \
+ -ex 'cont' \
+ -ex 'thread apply all bt' \
+ -ex 'quit' \
+ --tty=/dev/stdout \
+ -p $1
+}
+
+function process_core() {
+ ret=0
+ while IFS= read -r -d '' core; do
+ exe=$(eu-readelf -n "$core" | grep psargs | sed "s/.*psargs: \([^ \'\" ]*\).*/\1/")
+ if [[ ! -f "$exe" ]]; then
+ exe=$(eu-readelf -n "$core" | grep -oP -m1 "$exe.+")
+ fi
+ echo "exe for $core is $exe"
+ if [[ -n "$exe" ]]; then
+ if hash gdb &> /dev/null; then
+ gdb -batch -ex "thread apply all bt full" $exe $core
+ fi
+ cp $exe $output_dir
+ fi
+ mv $core $output_dir
+ chmod a+r $output_dir/$core
+ ret=1
+ done < <(find . -type f \( -name 'core\.?[0-9]*' -o -name '*.core' \) -print0)
+ return $ret
+}
+
+function process_shm() {
+ type=$1
+ id=$2
+ if [ "$type" = "--pid" ]; then
+ id="pid${id}"
+ elif [ "$type" = "--id" ]; then
+ id="${id}"
+ else
+ echo "Please specify to search for pid or shared memory id."
+ return 1
+ fi
+
+ shm_files=$(find /dev/shm -name "*.${id}" -printf "%f\n")
+
+ if [[ -z $shm_files ]]; then
+ echo "SHM File for specified PID or shared memory id: ${id} not found!"
+ return 1
+ fi
+ for n in $shm_files; do
+ tar -C /dev/shm/ -cvzf $output_dir/${n}_shm.tar.gz ${n}
+ done
+ return 0
+}
+
+function waitforlisten() {
+ # $1 = process pid
+ if [ -z "$1" ]; then
+ exit 1
+ fi
+
+ local rpc_addr="${2:-$DEFAULT_RPC_ADDR}"
+
+ echo "Waiting for process to start up and listen on UNIX domain socket $rpc_addr..."
+ # turn off trace for this loop
+ xtrace_disable
+ local ret=0
+ local i
+ for ((i = 40; i != 0; i--)); do
+ # if the process is no longer running, then exit the script
+ # since it means the application crashed
+ if ! kill -s 0 $1; then
+ echo "ERROR: process (pid: $1) is no longer running"
+ ret=1
+ break
+ fi
+
+ if $rootdir/scripts/rpc.py -t 1 -s "$rpc_addr" rpc_get_methods &> /dev/null; then
+ break
+ fi
+
+ sleep 0.5
+ done
+
+ xtrace_restore
+ if ((i == 0)); then
+ echo "ERROR: timeout while waiting for process (pid: $1) to start listening on '$rpc_addr'"
+ ret=1
+ fi
+ return $ret
+}
+
+function waitfornbd() {
+ local nbd_name=$1
+ local i
+
+ for ((i = 1; i <= 20; i++)); do
+ if grep -q -w $nbd_name /proc/partitions; then
+ break
+ else
+ sleep 0.1
+ fi
+ done
+
+ # The nbd device is now recognized as a block device, but there can be
+ # a small delay before we can start I/O to that block device. So loop
+ # here trying to read the first block of the nbd block device to a temp
+ # file. Note that dd returns success when reading an empty file, so we
+ # need to check the size of the output file instead.
+ for ((i = 1; i <= 20; i++)); do
+ dd if=/dev/$nbd_name of="$SPDK_TEST_STORAGE/nbdtest" bs=4096 count=1 iflag=direct
+ size=$(stat -c %s "$SPDK_TEST_STORAGE/nbdtest")
+ rm -f "$SPDK_TEST_STORAGE/nbdtest"
+ if [ "$size" != "0" ]; then
+ return 0
+ else
+ sleep 0.1
+ fi
+ done
+
+ return 1
+}
+
+function waitforbdev() {
+ local bdev_name=$1
+ local i
+
+ for ((i = 1; i <= 20; i++)); do
+ if $rpc_py bdev_get_bdevs | jq -r '.[] .name' | grep -qw $bdev_name; then
+ return 0
+ fi
+
+ if $rpc_py bdev_get_bdevs | jq -r '.[] .aliases' | grep -qw $bdev_name; then
+ return 0
+ fi
+
+ sleep 0.1
+ done
+
+ return 1
+}
+
+function make_filesystem() {
+ local fstype=$1
+ local dev_name=$2
+ local i=0
+ local force
+
+ if [ $fstype = ext4 ]; then
+ force=-F
+ else
+ force=-f
+ fi
+
+ while ! mkfs.${fstype} $force ${dev_name}; do
+ if [ $i -ge 15 ]; then
+ return 1
+ fi
+ i=$((i + 1))
+ sleep 1
+ done
+
+ return 0
+}
+
+function killprocess() {
+ # $1 = process pid
+ if [ -z "$1" ]; then
+ exit 1
+ fi
+
+ if kill -0 $1; then
+ if [ $(uname) = Linux ]; then
+ process_name=$(ps --no-headers -o comm= $1)
+ else
+ process_name=$(ps -c -o command $1 | tail -1)
+ fi
+ if [ "$process_name" = "sudo" ]; then
+ # kill the child process, which is the actual app
+ # (assume $1 has just one child)
+ local child
+ child="$(pgrep -P $1)"
+ echo "killing process with pid $child"
+ kill $child
+ else
+ echo "killing process with pid $1"
+ kill $1
+ fi
+
+ # wait for the process regardless if its the dummy sudo one
+ # or the actual app - it should terminate anyway
+ wait $1
+ else
+ # the process is not there anymore
+ echo "Process with pid $1 is not found"
+ exit 1
+ fi
+}
+
+function iscsicleanup() {
+ echo "Cleaning up iSCSI connection"
+ iscsiadm -m node --logout || true
+ iscsiadm -m node -o delete || true
+ rm -rf /var/lib/iscsi/nodes/*
+}
+
+function stop_iscsi_service() {
+ if cat /etc/*-release | grep Ubuntu; then
+ service open-iscsi stop
+ else
+ service iscsid stop
+ fi
+}
+
+function start_iscsi_service() {
+ if cat /etc/*-release | grep Ubuntu; then
+ service open-iscsi start
+ else
+ service iscsid start
+ fi
+}
+
+function rbd_setup() {
+ # $1 = monitor ip address
+ # $2 = name of the namespace
+ if [ -z "$1" ]; then
+ echo "No monitor IP address provided for ceph"
+ exit 1
+ fi
+ if [ -n "$2" ]; then
+ if ip netns list | grep "$2"; then
+ NS_CMD="ip netns exec $2"
+ else
+ echo "No namespace $2 exists"
+ exit 1
+ fi
+ fi
+
+ if hash ceph; then
+ export PG_NUM=128
+ export RBD_POOL=rbd
+ export RBD_NAME=foo
+ $NS_CMD $rootdir/scripts/ceph/stop.sh || true
+ $NS_CMD $rootdir/scripts/ceph/start.sh $1
+
+ $NS_CMD ceph osd pool create $RBD_POOL $PG_NUM || true
+ $NS_CMD rbd create $RBD_NAME --size 1000
+ fi
+}
+
+function rbd_cleanup() {
+ if hash ceph; then
+ $rootdir/scripts/ceph/stop.sh || true
+ rm -f /var/tmp/ceph_raw.img
+ fi
+}
+
+function nvme_cli_build() {
+ if [[ -z "${DEPENDENCY_DIR}" ]]; then
+ echo DEPENDENCY_DIR not defined!
+ exit 1
+ fi
+
+ spdk_nvme_cli="${DEPENDENCY_DIR}/nvme-cli"
+
+ if [[ ! -d $spdk_nvme_cli ]]; then
+ echo "nvme-cli repository not found at $spdk_nvme_cli; skipping tests."
+ exit 1
+ fi
+
+ if ! grep -q "DEF_VER=v1.6" $spdk_nvme_cli/NVME-VERSION-GEN; then
+ echo "SPDK supports only \"spdk/nvme-cli\" project on \"spdk-1.6\" branch."
+ exit 1
+ fi
+
+ # Build against the version of SPDK under test
+ pushd $spdk_nvme_cli
+
+ # Remove and recreate git index in case it became corrupted
+ if ! git clean -dfx; then
+ rm -f .git/index
+ git clean -dfx
+ git reset --hard
+ fi
+
+ rm -f "$spdk_nvme_cli/spdk"
+ ln -sf "$rootdir" "$spdk_nvme_cli/spdk"
+
+ make -j$(nproc) LDFLAGS="$(make -s -C $spdk_nvme_cli/spdk ldflags)"
+ popd
+}
+
+function _start_stub() {
+ # Disable ASLR for multi-process testing. SPDK does support using DPDK multi-process,
+ # but ASLR can still be unreliable in some cases.
+ # We will reenable it again after multi-process testing is complete in kill_stub().
+ # Save current setting so it can be restored upon calling kill_stub().
+ _randomize_va_space=$(< /proc/sys/kernel/randomize_va_space)
+ echo 0 > /proc/sys/kernel/randomize_va_space
+ $rootdir/test/app/stub/stub $1 &
+ stubpid=$!
+ echo Waiting for stub to ready for secondary processes...
+ while ! [ -e /var/run/spdk_stub0 ]; do
+ # If stub dies while we wait, bail
+ [[ -e /proc/$stubpid ]] || return 1
+ sleep 1s
+ done
+ echo done.
+}
+
+function start_stub() {
+ if ! _start_stub "$@"; then
+ echo "stub failed" >&2
+ return 1
+ fi
+}
+
+function kill_stub() {
+ if [[ -e /proc/$stubpid ]]; then
+ kill $1 $stubpid
+ wait $stubpid
+ fi 2> /dev/null || :
+ rm -f /var/run/spdk_stub0
+ # Re-enable ASLR now that we are done with multi-process testing
+ # Note: "1" enables ASLR w/o randomizing data segments, "2" adds data segment
+ # randomizing and is the default on all recent Linux kernels
+ echo "${_randomize_va_space:-2}" > /proc/sys/kernel/randomize_va_space
+}
+
+function run_test() {
+ if [ $# -le 1 ]; then
+ echo "Not enough parameters"
+ echo "usage: run_test test_name test_script [script_params]"
+ exit 1
+ fi
+
+ xtrace_disable
+ local test_name="$1"
+ shift
+
+ if [ -n "$test_domain" ]; then
+ export test_domain="${test_domain}.${test_name}"
+ else
+ export test_domain="$test_name"
+ fi
+
+ timing_enter $test_name
+ echo "************************************"
+ echo "START TEST $test_name"
+ echo "************************************"
+ xtrace_restore
+ time "$@"
+ xtrace_disable
+ echo "************************************"
+ echo "END TEST $test_name"
+ echo "************************************"
+ timing_exit $test_name
+
+ export test_domain=${test_domain%"$test_name"}
+ if [ -n "$test_domain" ]; then
+ export test_domain=${test_domain%?}
+ fi
+
+ if [ -z "$test_domain" ]; then
+ echo "top_level $test_name" >> $output_dir/test_completions.txt
+ else
+ echo "$test_domain $test_name" >> $output_dir/test_completions.txt
+ fi
+ xtrace_restore
+}
+
+function skip_run_test_with_warning() {
+ echo "WARNING: $1"
+ echo "Test run may fail if run with autorun.sh"
+ echo "Please check your $rootdir/test/common/skipped_tests.txt"
+}
+
+function print_backtrace() {
+ # if errexit is not enabled, don't print a backtrace
+ [[ "$-" =~ e ]] || return 0
+
+ local args=("${BASH_ARGV[@]}")
+
+ xtrace_disable
+ echo "========== Backtrace start: =========="
+ echo ""
+ for i in $(seq 1 $((${#FUNCNAME[@]} - 1))); do
+ local func="${FUNCNAME[$i]}"
+ local line_nr="${BASH_LINENO[$((i - 1))]}"
+ local src="${BASH_SOURCE[$i]}"
+ local bt="" cmdline=()
+
+ if [[ -f $src ]]; then
+ bt=$(nl -w 4 -ba -nln $src | grep -B 5 -A 5 "^${line_nr}[^0-9]" \
+ | sed "s/^/ /g" | sed "s/^ $line_nr /=> $line_nr /g")
+ fi
+
+ # If extdebug set the BASH_ARGC[i], try to fetch all the args
+ if ((BASH_ARGC[i] > 0)); then
+ # Use argc as index to reverse the stack
+ local argc=${BASH_ARGC[i]} arg
+ for arg in "${args[@]::BASH_ARGC[i]}"; do
+ cmdline[argc--]="[\"$arg\"]"
+ done
+ args=("${args[@]:BASH_ARGC[i]}")
+ fi
+
+ echo "in $src:$line_nr -> $func($(
+ IFS=","
+ printf '%s\n' "${cmdline[*]:-[]}"
+ ))"
+ echo " ..."
+ echo "${bt:-backtrace unavailable}"
+ echo " ..."
+ done
+ echo ""
+ echo "========== Backtrace end =========="
+ xtrace_restore
+ return 0
+}
+
+function waitforserial() {
+ local i=0
+ local nvme_device_counter=1
+ if [[ -n "$2" ]]; then
+ nvme_device_counter=$2
+ fi
+
+ while [ $(lsblk -l -o NAME,SERIAL | grep -c $1) -lt $nvme_device_counter ]; do
+ [ $i -lt 15 ] || break
+ i=$((i + 1))
+ echo "Waiting for devices"
+ sleep 1
+ done
+
+ if [[ $(lsblk -l -o NAME,SERIAL | grep -c $1) -lt $nvme_device_counter ]]; then
+ return 1
+ fi
+
+ return 0
+}
+
+function waitforserial_disconnect() {
+ local i=0
+ while lsblk -o NAME,SERIAL | grep -q -w $1; do
+ [ $i -lt 15 ] || break
+ i=$((i + 1))
+ echo "Waiting for disconnect devices"
+ sleep 1
+ done
+
+ if lsblk -l -o NAME | grep -q -w $1; then
+ return 1
+ fi
+
+ return 0
+}
+
+function waitforblk() {
+ local i=0
+ while ! lsblk -l -o NAME | grep -q -w $1; do
+ [ $i -lt 15 ] || break
+ i=$((i + 1))
+ sleep 1
+ done
+
+ if ! lsblk -l -o NAME | grep -q -w $1; then
+ return 1
+ fi
+
+ return 0
+}
+
+function waitforblk_disconnect() {
+ local i=0
+ while lsblk -l -o NAME | grep -q -w $1; do
+ [ $i -lt 15 ] || break
+ i=$((i + 1))
+ sleep 1
+ done
+
+ if lsblk -l -o NAME | grep -q -w $1; then
+ return 1
+ fi
+
+ return 0
+}
+
+function waitforfile() {
+ local i=0
+ while [ ! -e $1 ]; do
+ [ $i -lt 200 ] || break
+ i=$((i + 1))
+ sleep 0.1
+ done
+
+ if [ ! -e $1 ]; then
+ return 1
+ fi
+
+ return 0
+}
+
+function fio_config_gen() {
+ local config_file=$1
+ local workload=$2
+ local bdev_type=$3
+ local fio_dir=$CONFIG_FIO_SOURCE_DIR
+
+ if [ -e "$config_file" ]; then
+ echo "Configuration File Already Exists!: $config_file"
+ return 1
+ fi
+
+ if [ -z "$workload" ]; then
+ workload=randrw
+ fi
+
+ touch $1
+
+ cat > $1 << EOL
+[global]
+thread=1
+group_reporting=1
+direct=1
+norandommap=1
+percentile_list=50:99:99.9:99.99:99.999
+time_based=1
+ramp_time=0
+EOL
+
+ if [ "$workload" == "verify" ]; then
+ cat <<- EOL >> $config_file
+ verify=sha1
+ verify_backlog=1024
+ rw=randwrite
+ EOL
+
+ # To avoid potential data race issue due to the AIO device
+ # flush mechanism, add the flag to serialize the writes.
+ # This is to fix the intermittent IO failure issue of #935
+ if [ "$bdev_type" == "AIO" ]; then
+ if [[ $($fio_dir/fio --version) == *"fio-3"* ]]; then
+ echo "serialize_overlap=1" >> $config_file
+ fi
+ fi
+ elif [ "$workload" == "trim" ]; then
+ echo "rw=trimwrite" >> $config_file
+ else
+ echo "rw=$workload" >> $config_file
+ fi
+}
+
+function fio_bdev() {
+ # Setup fio binary cmd line
+ local fio_dir=$CONFIG_FIO_SOURCE_DIR
+ local bdev_plugin="$rootdir/build/fio/spdk_bdev"
+
+ # Preload AddressSanitizer library to fio if fio_plugin was compiled with it
+ local asan_lib
+ asan_lib=$(ldd $bdev_plugin | grep libasan | awk '{print $3}')
+
+ LD_PRELOAD="$asan_lib $bdev_plugin" "$fio_dir"/fio "$@"
+}
+
+function fio_nvme() {
+ # Setup fio binary cmd line
+ local fio_dir=$CONFIG_FIO_SOURCE_DIR
+ local nvme_plugin="$rootdir/build/fio/spdk_nvme"
+
+ # Preload AddressSanitizer library to fio if fio_plugin was compiled with it
+ asan_lib=$(ldd $nvme_plugin | grep libasan | awk '{print $3}')
+
+ LD_PRELOAD="$asan_lib $nvme_plugin" "$fio_dir"/fio "$@"
+}
+
+function get_lvs_free_mb() {
+ local lvs_uuid=$1
+ local lvs_info
+ local fc
+ local cs
+ lvs_info=$($rpc_py bdev_lvol_get_lvstores)
+ fc=$(jq ".[] | select(.uuid==\"$lvs_uuid\") .free_clusters" <<< "$lvs_info")
+ cs=$(jq ".[] | select(.uuid==\"$lvs_uuid\") .cluster_size" <<< "$lvs_info")
+
+ # Change to MB's
+ free_mb=$((fc * cs / 1024 / 1024))
+ echo "$free_mb"
+}
+
+function get_bdev_size() {
+ local bdev_name=$1
+ local bdev_info
+ local bs
+ local nb
+ bdev_info=$($rpc_py bdev_get_bdevs -b $bdev_name)
+ bs=$(jq ".[] .block_size" <<< "$bdev_info")
+ nb=$(jq ".[] .num_blocks" <<< "$bdev_info")
+
+ # Change to MB's
+ bdev_size=$((bs * nb / 1024 / 1024))
+ echo "$bdev_size"
+}
+
+function autotest_cleanup() {
+ $rootdir/scripts/setup.sh reset
+ $rootdir/scripts/setup.sh cleanup
+ if [ $(uname -s) = "Linux" ]; then
+ if grep -q '#define SPDK_CONFIG_IGB_UIO_DRIVER 1' $rootdir/include/spdk/config.h; then
+ [[ -e /sys/module/igb_uio ]] && rmmod igb_uio
+ else
+ modprobe -r uio_pci_generic
+ fi
+ fi
+ rm -rf "$asan_suppression_file"
+}
+
+function freebsd_update_contigmem_mod() {
+ if [ $(uname) = FreeBSD ]; then
+ kldunload contigmem.ko || true
+ if [ -n "$WITH_DPDK_DIR" ]; then
+ echo "Warning: SPDK only works on FreeBSD with patches that only exist in SPDK's dpdk submodule"
+ cp -f "$WITH_DPDK_DIR/kmod/contigmem.ko" /boot/modules/
+ cp -f "$WITH_DPDK_DIR/kmod/contigmem.ko" /boot/kernel/
+ cp -f "$WITH_DPDK_DIR/kmod/nic_uio.ko" /boot/modules/
+ cp -f "$WITH_DPDK_DIR/kmod/nic_uio.ko" /boot/kernel/
+ else
+ cp -f "$rootdir/dpdk/build/kmod/contigmem.ko" /boot/modules/
+ cp -f "$rootdir/dpdk/build/kmod/contigmem.ko" /boot/kernel/
+ cp -f "$rootdir/dpdk/build/kmod/nic_uio.ko" /boot/modules/
+ cp -f "$rootdir/dpdk/build/kmod/nic_uio.ko" /boot/kernel/
+ fi
+ fi
+}
+
+function get_nvme_name_from_bdf() {
+ blkname=()
+
+ nvme_devs=$(lsblk -d --output NAME | grep "^nvme") || true
+ if [ -z "$nvme_devs" ]; then
+ return
+ fi
+ for dev in $nvme_devs; do
+ link_name=$(readlink /sys/block/$dev/device/device) || true
+ if [ -z "$link_name" ]; then
+ link_name=$(readlink /sys/block/$dev/device)
+ fi
+ bdf=$(basename "$link_name")
+ if [ "$bdf" = "$1" ]; then
+ blkname+=($dev)
+ fi
+ done
+
+ printf '%s\n' "${blkname[@]}"
+}
+
+function get_nvme_ctrlr_from_bdf() {
+ bdf_sysfs_path=$(readlink -f /sys/class/nvme/nvme* | grep "$1/nvme/nvme")
+ if [[ -z "$bdf_sysfs_path" ]]; then
+ return
+ fi
+
+ printf '%s\n' "$(basename $bdf_sysfs_path)"
+}
+
+# Get BDF addresses of all NVMe drives currently attached to
+# uio-pci-generic or vfio-pci
+function get_nvme_bdfs() {
+ xtrace_disable
+ bdfs=$(jq -r .config[].params.traddr <<< $($rootdir/scripts/gen_nvme.sh --json))
+ if [[ -z $bdfs ]]; then
+ echo "No devices to test on!"
+ exit 1
+ fi
+ echo "$bdfs"
+ xtrace_restore
+}
+
+# Same as function above, but just get the first disks BDF address
+function get_first_nvme_bdf() {
+ head -1 <<< "$(get_nvme_bdfs)"
+}
+
+function nvme_namespace_revert() {
+ $rootdir/scripts/setup.sh
+ sleep 1
+ bdfs=$(get_nvme_bdfs)
+
+ $rootdir/scripts/setup.sh reset
+ sleep 1
+
+ for bdf in $bdfs; do
+ nvme_ctrlr=/dev/$(get_nvme_ctrlr_from_bdf ${bdf})
+ if [[ -z "$nvme_ctrlr" ]]; then
+ continue
+ fi
+
+ # Check Optional Admin Command Support for Namespace Management
+ oacs=$(nvme id-ctrl ${nvme_ctrlr} | grep oacs | cut -d: -f2)
+ oacs_ns_manage=$((oacs & 0x8))
+
+ if [[ "$oacs_ns_manage" -ne 0 ]]; then
+ # This assumes every NVMe controller contains single namespace,
+ # encompassing Total NVM Capacity and formatted as 512 block size.
+ # 512 block size is needed for test/vhost/vhost_boot.sh to
+ # succesfully run.
+
+ unvmcap=$(nvme id-ctrl ${nvme_ctrlr} | grep unvmcap | cut -d: -f2)
+ if [[ "$unvmcap" -eq 0 ]]; then
+ # All available space already used
+ continue
+ fi
+ tnvmcap=$(nvme id-ctrl ${nvme_ctrlr} | grep tnvmcap | cut -d: -f2)
+ blksize=512
+
+ size=$((tnvmcap / blksize))
+
+ nvme detach-ns ${nvme_ctrlr} -n 0xffffffff -c 0 || true
+ nvme delete-ns ${nvme_ctrlr} -n 0xffffffff || true
+ nvme create-ns ${nvme_ctrlr} -s ${size} -c ${size} -b ${blksize}
+ nvme attach-ns ${nvme_ctrlr} -n 1 -c 0
+ nvme reset ${nvme_ctrlr}
+ waitforblk "${nvme_ctrlr}n1"
+ fi
+ done
+}
+
+# Get BDFs based on device ID, such as 0x0a54
+function get_nvme_bdfs_by_id() {
+ local bdfs=()
+
+ for bdf in $(get_nvme_bdfs); do
+ device=$(cat /sys/bus/pci/devices/$bdf/device) || true
+ if [[ "$device" == "$1" ]]; then
+ bdfs+=($bdf)
+ fi
+ done
+
+ printf '%s\n' "${bdfs[@]}"
+}
+
+function opal_revert_cleanup() {
+ # The OPAL CI tests is only used for P4510 devices.
+ mapfile -t bdfs < <(get_nvme_bdfs_by_id 0x0a54)
+ if [[ -z ${bdfs[0]} ]]; then
+ return 0
+ fi
+
+ $SPDK_BIN_DIR/spdk_tgt &
+ spdk_tgt_pid=$!
+ waitforlisten $spdk_tgt_pid
+
+ for bdf in "${bdfs[@]}"; do
+ $rootdir/scripts/rpc.py bdev_nvme_attach_controller -b "nvme0" -t "pcie" -a ${bdf}
+ # Ignore if this fails.
+ $rootdir/scripts/rpc.py bdev_nvme_opal_revert -b nvme0 -p test || true
+ done
+
+ killprocess $spdk_tgt_pid
+}
+
+# Define temp storage for all the tests. Look for 2GB at minimum
+set_test_storage "${TEST_MIN_STORAGE_SIZE:-$((1 << 31))}"
+
+set -o errtrace
+shopt -s extdebug
+trap "trap - ERR; print_backtrace >&2" ERR
+
+PS4=' \t \$ '
+if $SPDK_AUTOTEST_X; then
+ # explicitly enable xtraces, overriding any tracking information.
+ unset XTRACE_DISABLED
+ unset XTRACE_NESTING_LEVEL
+ set -x
+ xtrace_enable
+else
+ xtrace_restore
+fi
diff --git a/src/spdk/test/common/config/README.md b/src/spdk/test/common/config/README.md
new file mode 100644
index 000000000..26a587709
--- /dev/null
+++ b/src/spdk/test/common/config/README.md
@@ -0,0 +1,104 @@
+# Virtual Test Configuration
+
+This readme and the associated bash script, vm_setup.sh, are intended to assist developers in quickly
+preparing a virtual test environment on which to run the SPDK validation tests rooted at autorun.sh.
+This file contains basic information about SPDK environment requirements, an introduction to the
+autorun-spdk.conf files used to moderate which tests are run by autorun.sh, and step-by-step instructions
+for spinning up a VM capable of running the SPDK test suite.
+There is no need for external hardware to run these tests. The linux kernel comes with the drivers necessary
+to emulate an RDMA enabled NIC. NVMe controllers can also be virtualized in emulators such as QEMU.
+
+## VM Envronment Requirements (Host)
+
+- 8 GiB of RAM (for DPDK)
+- Enable intel_kvm on the host machine from the bios.
+- Enable nesting for VMs in kernel command line (for vhost tests).
+ - In `/etc/default/grub` append the following to the GRUB_CMDLINE_LINUX line: intel_iommu=on kvm-intel.nested=1.
+
+## VM Specs
+
+When creating the user during the fedora installation, it is best to use the name sys_sgsw. Efforts are being made
+to remove all references to this user, or files specific to this user from the codebase, but there are still some
+trailing references to it.
+
+## Autorun-spdk.conf
+
+Every machine that runs the autotest scripts should include a file titled autorun-spdk.conf in the home directory
+of the user that will run them. This file consists of several lines of the form 'variable_name=0/1'. autorun.sh sources
+this file each time it is run, and determines which tests to attempt based on which variables are defined in the
+configuration file. For a full list of the variable declarations available for autorun-spdk.conf, please see
+`test/common/autotest_common.sh` starting at line 13.
+
+## Steps for Configuring the VM
+
+1. Download a fresh Fedora 26 image.
+2. Perform the installation of Fedora 26 server.
+3. Create an admin user sys_sgsw (enabling passwordless sudo for this account will make life easier during the tests).
+4. Run the vm_setup.sh script which will install all proper dependencies.
+5. Modify the autorun-spdk.conf file in the home directory.
+6. Reboot the VM.
+7. Run autorun.sh for SPDK. Any output files will be placed in `~/spdk_repo/output/`.
+
+## Additional Steps for Preparing the Vhost Tests
+
+The Vhost tests also require the creation of a second virtual machine nested inside of the test VM.
+Please follow the directions below to complete that installation. Note that host refers to the Fedora VM
+created above and guest or VM refer to the Ubuntu VM created in this section.
+
+1. Follow instructions from spdk/scripts/vagrant/README.md
+ - install all needed packages mentioned in "Mac OSX Setup" or "Windows 10 Setup" sections
+ - follow steps from "Configure Vagrant" section
+
+2. Use Vagrant scripts located in spdk/scripts/vagrant to automatically generate
+ VM image to use in SPDK vhost tests.
+ Example command:
+ ~~~{.sh}
+ spdk/scripts/vagrant/create_vhost_vm.sh --move-to-def-dirs ubuntu16
+ ~~~
+ This command will:
+ - Download a Ubuntu 16.04 image file
+ - upgrade the system and install needed dependencies (fio, sg3-utils, bc)
+ - add entry to VM's ~/.ssh/autorized_keys
+ - add appropriate options to GRUB command line and update grub
+ - convert the image to .qcow2 format
+ - move .qcow2 file and ssh keys to default locations used by vhost test scripts
+
+Alternatively it is possible to create the VM image manually using following steps:
+
+1. Create an image file for the VM. It does not have to be large, about 3.5G should suffice.
+2. Create an ssh keypair for host-guest communications (performed on the host):
+ - Generate an ssh keypair with the name spdk_vhost_id_rsa and save it in `/root/.ssh`.
+ - Make sure that only root has read access to the private key.
+3. Install the OS in the VM image (performed on guest):
+ - Use the latest Ubuntu server (Currently 16.04 LTS).
+ - When partitioning the disk, make one partion that consumes the whole disk mounted at /. Do not encrypt the disk or enable LVM.
+ - Choose the OpenSSH server packages during install.
+4. Post installation configuration (performed on guest):
+ - Run the following commands to enable all necessary dependencies:
+ ~~~{.sh}
+ sudo apt update
+ sudo apt upgrade
+ sudo apt install fio sg3-utils bc
+ ~~~
+ - Enable the root user: "sudo passwd root -> root".
+ - Enable root login over ssh: vim `/etc/ssh/sshd_config` -> PermitRootLogin=yes.
+ - Disable DNS for ssh: `/etc/ssh/sshd_config` -> UseDNS=no.
+ - Add the spdk_vhost key to root's known hosts: `/root/.ssh/authorized_keys` -> add spdk_vhost_id_rsa.pub key to authorized keys.
+ Remember to save the private key in `~/.ssh/spdk_vhost_id_rsa` on the host.
+ - Change the grub boot options for the guest as follows:
+ - Add "console=ttyS0 earlyprintk=ttyS0" to the boot options in `/etc/default/grub` (for serial output redirect).
+ - Add "scsi_mod.use_blk_mq=1" to boot options in `/etc/default/grub`.
+ ~~~{.sh}
+ sudo update-grub
+ ~~~
+ - Reboot the VM.
+ - Remove any unnecessary packages (this is to make booting the VM faster):
+ ~~~{.sh}
+ apt purge snapd
+ apt purge Ubuntu-core-launcher
+ apt purge squashfs-tools
+ apt purge unattended-upgrades
+ ~~~
+5. Copy the fio binary from the guest location `/usr/bin/fio` to the host location `/home/sys_sgsw/fio_ubuntu`.
+6. Place the guest VM in the host at the following location: `/home/sys_sgsw/vhost_vm_image.qcow2`.
+7. On the host, edit the `~/autorun-spdk.conf` file to include the following line: SPDK_TEST_VHOST=1.
diff --git a/src/spdk/test/common/config/pkgdep/apt-get b/src/spdk/test/common/config/pkgdep/apt-get
new file mode 100644
index 000000000..a1630620d
--- /dev/null
+++ b/src/spdk/test/common/config/pkgdep/apt-get
@@ -0,0 +1,100 @@
+package_manager=apt-get
+
+update() {
+ sudo "$package_manager" update
+}
+
+install() {
+ (( $# )) || return 0
+
+ sudo "$package_manager" install -y "$@"
+}
+
+upgrade() {
+ sudo "$package_manager" update
+ sudo "$package_manager" upgrade -y
+}
+
+
+pre_install() {
+ echo "Package perl-open is not available at Ubuntu repositories" >&2
+
+ update
+
+ if [[ $INSTALL_TSOCKS == true ]]; then
+ install tsocks
+ fi
+
+ # asan an ubsan have to be installed together to not mix up gcc versions
+ if install libasan5; then
+ install libubsan1
+ else
+ echo "Latest libasan5 is not available" >&2
+ echo " installing libasan2 and corresponding libubsan0" >&2
+ install libasan2
+ install libubsan0
+ fi
+ if ! install rdma-core; then
+ echo "Package rdma-core is avaliable at Ubuntu 18 [universe] repositorium" >&2
+ install rdmacm-utils
+ install ibverbs-utils
+ else
+ LIBRXE_INSTALL=false
+ fi
+ if ! install libpmempool1; then
+ echo "Package libpmempool1 is available at Ubuntu 18 [universe] repositorium" >&2
+ fi
+ if ! install clang-tools; then
+ echo "Package clang-tools is available at Ubuntu 18 [universe] repositorium" >&2
+ fi
+ if ! install --no-install-suggests --no-install-recommends open-isns-utils; then
+ echo "Package open-isns-utils is available at Ubuntu 18 [universe] repositorium" >&2
+ fi
+
+ # Package name for Ubuntu 18 is targetcli-fb but for Ubuntu 16 it's targetcli
+ if ! install targetcli-fb; then
+ install targetcli
+ fi
+
+ # On Ubuntu 20.04 (focal) btrfs-tools are available under different name - btrfs-progs
+ if ! install btrfs-tools; then
+ install btrfs-progs
+ fi
+}
+
+packages=(
+ valgrind
+ jq
+ nvme-cli
+ ceph
+ gdb
+ fio
+ librbd-dev
+ linux-headers-generic
+ libgflags-dev
+ autoconf
+ automake
+ libtool
+ libmount-dev
+ open-iscsi
+ libglib2.0-dev
+ libpixman-1-dev
+ astyle
+ elfutils
+ libelf-dev
+ flex
+ bison
+ libswitch-perl
+ gdisk
+ socat
+ sshfs
+ sshpass
+ python3-pandas
+ bc
+ smartmontools
+ wget
+)
+
+if [[ $OSID != ubuntu ]]; then
+ echo "Located apt-get package manager, but it was tested for Ubuntu only"
+fi
diff --git a/src/spdk/test/common/config/pkgdep/dnf b/src/spdk/test/common/config/pkgdep/dnf
new file mode 100644
index 000000000..b009f106e
--- /dev/null
+++ b/src/spdk/test/common/config/pkgdep/dnf
@@ -0,0 +1,72 @@
+package_manager=dnf
+
+upgrade() {
+ sudo "$package_manager" upgrade -y
+}
+
+install() {
+ (($#)) || return 0
+
+ sudo "$package_manager" install -y "$@"
+}
+
+packages=(
+ valgrind
+ jq
+ nvme-cli
+ ceph
+ gdb
+ fio
+ librbd-devel
+ kernel-devel
+ gflags-devel
+ libasan
+ libubsan
+ autoconf
+ automake
+ libtool
+ libmount-devel
+ iscsi-initiator-utils
+ isns-utils-devel
+ pmempool
+ perl-open
+ glib2-devel
+ pixman-devel
+ astyle-devel
+ elfutils
+ libabigail
+ elfutils-libelf-devel
+ flex
+ bison
+ targetcli
+ perl-Switch
+ librdmacm-utils
+ libibverbs-utils
+ gdisk
+ socat
+ sshfs
+ sshpass
+ python3-pandas
+ btrfs-progs
+ rpm-build
+ iptables
+ clang-analyzer
+ bc
+ kernel-modules-extra
+ systemd-devel
+ smartmontools
+ wget
+)
+
+pre_install() {
+ if [[ $INTSALL_TSOCKS == true ]]; then
+ # currently, tsocks package is retired in fedora 31, so don't exit in case
+ # installation failed
+ # FIXME: Review when fedora starts to successfully build this package again.
+ install tsocks || echo "Installation of the tsocks package failed, proxy may not be available"
+ fi
+}
+
+if [[ $OSID != fedora ]]; then
+ echo "Located dnf package manager, but it was tested for Fedora only"
+fi
diff --git a/src/spdk/test/common/config/pkgdep/git b/src/spdk/test/common/config/pkgdep/git
new file mode 100644
index 000000000..f46183ac8
--- /dev/null
+++ b/src/spdk/test/common/config/pkgdep/git
@@ -0,0 +1,325 @@
+function install_spdk() {
+ mkdir -p "$GIT_REPOS/spdk_repo/output" || echo "Can not create spdk_repo/output directory."
+
+ if [[ -d $GIT_REPOS/spdk_repo/spdk ]]; then
+ echo "spdk source already present, not cloning"
+ else
+ git -C "$GIT_REPOS/spdk_repo" clone "${GIT_REPO_SPDK}"
+ fi
+ git -C "$GIT_REPOS/spdk_repo/spdk" config submodule.dpdk.url "${GIT_REPO_DPDK}"
+ git -C "$GIT_REPOS/spdk_repo/spdk" config submodule.intel-ipsec-mb.url "${GIT_REPO_INTEL_IPSEC_MB}"
+ git -C "$GIT_REPOS/spdk_repo/spdk" submodule update --init --recursive
+}
+
+function install_refspdk() {
+ local last_release
+ local output_dir
+ local config_params
+ local rootdir
+
+ # Create a reference SPDK build for ABI tests
+ git -C "$GIT_REPOS/spdk_repo/spdk" fetch --tags
+ last_release=$(git -C "$GIT_REPOS/spdk_repo/spdk" tag | sort --version-sort | grep -v rc | tail -n1)
+ output_dir="$GIT_REPOS/spdk_$(tr . _ < <(tr -d '[:alpha:]' <<< $last_release))"
+
+ if [[ ! -d $output_dir ]]; then
+ cp -r "$GIT_REPOS/spdk_repo/spdk" "$output_dir"
+ fi
+
+ git -C "$output_dir" checkout "$last_release"
+ git -C "$output_dir" submodule update --init
+
+ cat > $HOME/autorun-spdk.conf <<- EOF
+ SPDK_BUILD_SHARED_OBJECT=1
+ SPDK_TEST_AUTOBUILD=1
+ SPDK_TEST_UNITTEST=1
+ SPDK_TEST_BLOCKDEV=1
+ SPDK_TEST_PMDK=1
+ SPDK_TEST_ISAL=1
+ SPDK_TEST_REDUCE=1
+ SPDK_TEST_CRYPTO=1
+ SPDK_TEST_FTL=1
+ SPDK_TEST_OCF=1
+ SPDK_TEST_RAID5=1
+ SPDK_TEST_RBD=1
+ SPDK_RUN_ASAN=1
+ SPDK_RUN_UBSAN=1
+ EOF
+
+ mkdir -p $HOME/output
+
+ (
+ rootdir="$output_dir"
+ source $HOME/autorun-spdk.conf
+ source $output_dir/test/common/autotest_common.sh
+
+ # Prepare separate, fixed, cmdline for the FreeBSD, Issue #1397.
+ if [[ $OSID == freebsd ]]; then
+ config_params="--enable-debug --enable-werror"
+ config_params+=" --with-idxd --with-fio=/usr/src/fio"
+ config_params+=" --disable-unit-tests --without-isal"
+ MAKE=gmake
+ else
+ config_params="$(get_config_params)"
+ fi
+ $output_dir/configure $(echo $config_params | sed 's/--enable-coverage//g')
+ if [[ $OSID != freebsd ]]; then
+ $MAKE -C $output_dir $MAKEFLAGS include/spdk/config.h
+ CONFIG_OCF_PATH="$output_dir/ocf" $MAKE -C $output_dir/lib/env_ocf $MAKEFLAGS exportlib O=$output_dir/build/ocf.a
+ $output_dir/configure $config_params --with-ocf=$output_dir/build/ocf.a --with-shared
+ fi
+ $MAKE -C $output_dir $MAKEFLAGS
+ )
+}
+
+function install_qat() {
+
+ kernel_maj=$(uname -r | cut -d'.' -f1)
+ kernel_min=$(uname -r | cut -d'.' -f2)
+
+ if [[ -e /sys/module/qat_c62x ]]; then
+ sudo modprobe -r qat_c62x || :
+ fi
+ if [[ -d $GIT_REPOS/QAT ]]; then
+ sudo rm -rf "$GIT_REPOS/QAT"
+ fi
+
+ mkdir "$GIT_REPOS/QAT"
+
+ tar -C "$GIT_REPOS/QAT" -xzof - < <(wget -O- "$DRIVER_LOCATION_QAT")
+
+ #The driver version 1.7.l.4.3.0-00033 contains a reference to a deprecated function. Remove it so the build won't fail.
+ if [ $kernel_maj -le 4 ]; then
+ if [ $kernel_min -le 17 ]; then
+ sudo sed -i 's/rdtscll(timestamp);/timestamp = rdtsc_ordered();/g' \
+ "$GIT_REPOS/QAT/quickassist/utilities/osal/src/linux/kernel_space/OsalServices.c" || true
+ fi
+ fi
+
+ (cd "$GIT_REPOS/QAT" && sudo ./configure --enable-icp-sriov=host && sudo make install)
+
+ if sudo service qat_service start; then
+ echo "failed to start the qat service. Something may be wrong with your device or package."
+ fi
+}
+
+function install_rocksdb() {
+ # Rocksdb is installed for use with the blobfs tests.
+ if [ ! -d /usr/src/rocksdb ]; then
+ git clone "${GIT_REPO_ROCKSDB}" "$GIT_REPOS/rocksdb"
+ git -C "$GIT_REPOS/rocksdb" checkout spdk-v5.6.1
+ sudo mv "$GIT_REPOS/rocksdb" /usr/src/
+ else
+ sudo git -C /usr/src/rocksdb checkout spdk-v5.6.1
+ echo "rocksdb already in /usr/src. Not checking out again"
+ fi
+}
+
+function install_fio() {
+ # This version of fio is installed in /usr/src/fio to enable
+ # building the spdk fio plugin.
+ local fio_version="fio-3.19"
+
+ if [ ! -d /usr/src/fio ]; then
+ if [ ! -d fio ]; then
+ git clone "${GIT_REPO_FIO}" "$GIT_REPOS/fio"
+ sudo mv "$GIT_REPOS/fio" /usr/src/
+ else
+ sudo mv "$GIT_REPOS/fio" /usr/src/
+ fi
+ (
+ git -C /usr/src/fio checkout master \
+ && git -C /usr/src/fio pull \
+ && git -C /usr/src/fio checkout $fio_version \
+ && if [ $OSID == 'freebsd' ]; then
+ gmake -C /usr/src/fio -j${jobs} \
+ && sudo gmake -C /usr/src/fio install
+ else
+ make -C /usr/src/fio -j${jobs} \
+ && sudo make -C /usr/src/fio install
+ fi
+ )
+ else
+ echo "fio already in /usr/src/fio. Not installing"
+ fi
+}
+
+function install_flamegraph() {
+ # Flamegraph is used when printing out timing graphs for the tests.
+ if [ ! -d /usr/local/FlameGraph ]; then
+ git clone "${GIT_REPO_FLAMEGRAPH}" "$GIT_REPOS/FlameGraph"
+ mkdir -p /usr/local
+ sudo mv "$GIT_REPOS/FlameGraph" /usr/local/FlameGraph
+ else
+ echo "flamegraph already installed. Skipping"
+ fi
+}
+
+function install_qemu() {
+ # Two versions of QEMU are used in the tests.
+ # Stock QEMU is used for vhost. A special fork
+ # is used to test OCSSDs. Install both.
+
+ # Forked QEMU
+ SPDK_QEMU_BRANCH=spdk-5.0.0
+ mkdir -p "$GIT_REPOS/qemu"
+ if [[ ! -d $GIT_REPOS/qemu/$SPDK_QEMU_BRANCH ]]; then
+ git clone "${GIT_REPO_QEMU}" -b "$SPDK_QEMU_BRANCH" "$GIT_REPOS/qemu/$SPDK_QEMU_BRANCH"
+ else
+ echo "qemu already checked out. Skipping"
+ fi
+
+ declare -a opt_params=("--prefix=/usr/local/qemu/$SPDK_QEMU_BRANCH")
+ if ((gcc_version >= 9)); then
+ # GCC 9 fails to compile Qemu due to some old warnings which were not detected by older versions.
+ opt_params+=("--extra-cflags=-Wno-error=stringop-truncation -Wno-error=deprecated-declarations -Wno-error=incompatible-pointer-types -Wno-error=format-truncation")
+ opt_params+=("--disable-glusterfs")
+ fi
+
+ # Most tsocks proxies rely on a configuration file in /etc/tsocks.conf.
+ # If using tsocks, please make sure to complete this config before trying to build qemu.
+ if [[ $INSTALL_TSOCKS == true && $NO_TSOCKS != true ]]; then
+ if hash tsocks 2> /dev/null; then
+ opt_params+=("--with-git='tsocks git'")
+ fi
+ fi
+
+ sed -i s@git://git.qemu.org/@https://github.com/qemu/@g "$GIT_REPOS/qemu/$SPDK_QEMU_BRANCH/.gitmodules"
+ sed -i s@git://git.qemu.org/@https://github.com/qemu/@g "$GIT_REPOS/qemu/$SPDK_QEMU_BRANCH/.git/config"
+ sed -i s@git://git.qemu-project.org/@https://github.com/qemu/@g "$GIT_REPOS/qemu/$SPDK_QEMU_BRANCH/.gitmodules"
+ sed -i s@git://git.qemu-project.org/@https://github.com/qemu/@g "$GIT_REPOS/qemu/$SPDK_QEMU_BRANCH/.git/config"
+ # The qemu configure script places several output files in the CWD.
+ (cd "$GIT_REPOS/qemu/$SPDK_QEMU_BRANCH" && ./configure "${opt_params[@]}" --target-list="x86_64-softmmu" --enable-kvm --enable-linux-aio --enable-numa)
+
+ make -C "$GIT_REPOS/qemu/$SPDK_QEMU_BRANCH" -j${jobs}
+ sudo make -C "$GIT_REPOS/qemu/$SPDK_QEMU_BRANCH" install
+}
+
+function install_nvmecli() {
+ SPDK_NVME_CLI_BRANCH=spdk-1.6
+ if [[ ! -d $GIT_REPOS/nvme-cli ]]; then
+ git clone "${GIT_REPO_SPDK_NVME_CLI}" -b "$SPDK_NVME_CLI_BRANCH" "$GIT_REPOS/nvme-cli"
+ else
+ echo "nvme-cli already checked out. Skipping"
+ fi
+ if [ ! -d "/usr/local/src/nvme-cli" ]; then
+ # Changes required for SPDK are already merged on top of
+ # nvme-cli, however not released yet.
+ # Support for SPDK should be released in nvme-cli >1.11.1
+ if [[ ! -d $GIT_REPOS/nvme-cli-cuse ]]; then
+ git clone "https://github.com/linux-nvme/nvme-cli.git" "$GIT_REPOS/nvme-cli-cuse"
+ fi
+ git -C "$GIT_REPOS/nvme-cli-cuse" checkout "e770466615096a6d41f038a28819b00bc3078e1d"
+ make -C "$GIT_REPOS/nvme-cli-cuse"
+ sudo mv "$GIT_REPOS/nvme-cli-cuse" /usr/local/src/nvme-cli
+ fi
+}
+
+function install_libiscsi() {
+ # We currently don't make any changes to the libiscsi repository for our tests, but it is possible that we will need
+ # to later. Cloning from git is just future proofing the machines.
+ if [[ ! -d $GIT_REPOS/libiscsi ]]; then
+ git clone "${GIT_REPO_LIBISCSI}" "$GIT_REPOS/libiscsi"
+ else
+ echo "libiscsi already checked out. Skipping"
+ fi
+ (cd "$GIT_REPOS/libiscsi" && ./autogen.sh && ./configure --prefix=/usr/local/libiscsi)
+ make -C "$GIT_REPOS/libiscsi" -j${jobs}
+ sudo make -C "$GIT_REPOS/libiscsi" install
+}
+
+function install_git() {
+ install zlib-devel curl-devel
+ tar -C "$GIT_REPOS" -xzof <(wget -qO- "$GIT_REPO_GIT")
+ (cd "$GIT_REPOS/git-$GIT_VERSION" \
+ && make configure \
+ && ./configure --prefix=/usr/local/git \
+ && sudo make -j${jobs} install)
+ sudo sh -c "echo 'export PATH=/usr/local/git/bin:$PATH' >> /etc/bashrc"
+ export "PATH=/usr/local/git/bin:$PATH"
+}
+
+function install_extra_pkgs() {
+ if [[ $INSTALL_QAT == true ]]; then
+ install libudev-devel || install libudev-dev
+ fi
+
+ if [[ $INSTALL_QEMU == true ]]; then
+ install qemu-system-x86 qemu-img \
+ || install qemu-system-x86 qemu-utils \
+ || install qemu
+ fi
+}
+
+GIT_VERSION=2.25.1
+: ${GIT_REPO_SPDK=https://github.com/spdk/spdk.git}
+export GIT_REPO_SPDK
+: ${GIT_REPO_DPDK=https://github.com/spdk/dpdk.git}
+export GIT_REPO_DPDK
+: ${GIT_REPO_ROCKSDB=https://review.spdk.io/spdk/rocksdb}
+export GIT_REPO_ROCKSDB
+: ${GIT_REPO_FIO=http://git.kernel.dk/fio.git}
+export GIT_REPO_FIO
+: ${GIT_REPO_FLAMEGRAPH=https://github.com/brendangregg/FlameGraph.git}
+export GIT_REPO_FLAMEGRAPH
+: ${GIT_REPO_QEMU=https://github.com/spdk/qemu}
+export GIT_REPO_QEMU
+: ${GIT_REPO_LIBISCSI=https://github.com/sahlberg/libiscsi}
+export GIT_REPO_LIBISCSI
+: ${GIT_REPO_SPDK_NVME_CLI=https://github.com/spdk/nvme-cli}
+export GIT_REPO_SPDK_NVME_CLI
+: ${GIT_REPO_INTEL_IPSEC_MB=https://github.com/spdk/intel-ipsec-mb.git}
+export GIT_REPO_INTEL_IPSEC_MB
+: ${DRIVER_LOCATION_QAT=https://01.org/sites/default/files/downloads//qat1.7.l.4.9.0-00008.tar.gz}
+export DRIVER_LOCATION_QAT
+: ${GIT_REPO_GIT=https://github.com/git/git/archive/v${GIT_VERSION}.tar.gz}
+export GIT_REPO_GIT
+GIT_REPOS=${GIT_REPOS:-$HOME}
+
+gcc_version=$(gcc -dumpversion) gcc_version=${gcc_version%%.*}
+if [[ $ID == centos ]] && (( VERSION_ID == 7 )); then
+ # install proper version of the git first
+ install_git
+fi
+
+IFS="," read -ra conf_env <<< "$CONF"
+for conf in "${conf_env[@]}"; do
+ export "INSTALL_${conf^^}=true"
+done
+sources=(install_refspdk)
+
+if [[ $OS == FreeBSD ]]; then
+ jobs=$(($(sysctl -n hw.ncpu) * 2))
+else
+ jobs=$(($(nproc) * 2))
+ sources+=(
+ install_libiscsi
+ install_nvmecli
+ install_qat
+ install_rocksdb
+ install_flamegraph
+ install_qemu
+ )
+fi
+sources+=(install_fio)
+
+sudo mkdir -p /usr/{,local}/src
+sudo mkdir -p "$GIT_REPOS"
+
+install_extra_pkgs
+
+if [[ $INSTALL_REFSPDK == true ]]; then
+ # Serialize builds as refspdk depends on spdk
+ install_spdk
+ install_refspdk
+else
+ sources+=(install_spdk)
+fi
+
+for source in "${sources[@]}"; do
+ source_conf=${source^^}
+ if [[ ${!source_conf} == true ]]; then
+ "$source" &
+ fi
+done
+wait
diff --git a/src/spdk/test/common/config/pkgdep/pacman b/src/spdk/test/common/config/pkgdep/pacman
new file mode 100644
index 000000000..43d3db2f5
--- /dev/null
+++ b/src/spdk/test/common/config/pkgdep/pacman
@@ -0,0 +1,62 @@
+package_manager=pacman
+
+upgrade() {
+ sudo "$package_manager" -Syu --noconfirm --needed
+}
+
+install() {
+ (($#)) || return 0
+
+ sudo "$package_manager" -Sy --noconfirm --needed "$@"
+}
+
+pre_install() {
+ if [[ $INTSALL_TSOCKS == true ]]; then
+ install tsocks
+ fi
+}
+
+packages=(
+ valgrind
+ jq
+ nvme-cli
+ ceph
+ gdb
+ fio
+ linux-headers
+ gflags
+ autoconf
+ automake
+ libtool
+ libutil-linux
+ libiscsi
+ open-isns
+ glib2
+ pixman
+ flex
+ bison
+ elfutils
+ libelf
+ astyle
+ gptfdisk
+ socat
+ sshfs
+ sshpass
+ python-pandas
+ btrfs-progs
+ iptables
+ clang
+ bc
+ perl-switch
+ open-iscsi
+ smartmontools
+ parted
+ wget
+)
+
+# TODO:
+# These are either missing or require some other installation method
+# than pacman:
+# librbd-devel
+# perl-open
+# targetcli
diff --git a/src/spdk/test/common/config/pkgdep/pkg b/src/spdk/test/common/config/pkgdep/pkg
new file mode 100644
index 000000000..3f3f41725
--- /dev/null
+++ b/src/spdk/test/common/config/pkgdep/pkg
@@ -0,0 +1,27 @@
+package_manager=pkg
+
+upgrade() {
+ sudo "$package_manager" upgrade -y
+}
+
+install() {
+ (($#)) || return 0
+
+ sudo "$package_manager" install -y "$@"
+}
+
+packages=(
+ pciutils
+ jq
+ gdb
+ fio
+ p5-extutils-pkgconfig
+ libtool
+ flex
+ bison
+ gdisk
+ socat
+ sshpass
+ py37-pandas
+ wget
+)
diff --git a/src/spdk/test/common/config/pkgdep/swupd b/src/spdk/test/common/config/pkgdep/swupd
new file mode 100644
index 000000000..c1d2a8a6b
--- /dev/null
+++ b/src/spdk/test/common/config/pkgdep/swupd
@@ -0,0 +1,21 @@
+package_manager=swupd
+
+upgrade() {
+ sudo "$package_manager" update -y
+}
+
+install() {
+ (($#)) || return 0
+
+ sudo "$package_manager" bundle-add -y "$@"
+}
+
+packages=(
+ jq
+)
+
+pre_install() {
+ if [[ $INTSALL_TSOCKS == true ]]; then
+ install tsocks || echo "Installation of the tsocks package failed, proxy may not be available"
+ fi
+}
diff --git a/src/spdk/test/common/config/pkgdep/yum b/src/spdk/test/common/config/pkgdep/yum
new file mode 100644
index 000000000..32e89bc15
--- /dev/null
+++ b/src/spdk/test/common/config/pkgdep/yum
@@ -0,0 +1,67 @@
+package_manager=yum
+
+upgrade() {
+ sudo "$package_manager" upgrade -y
+}
+
+install() {
+ (($#)) || return 0
+
+ sudo "$package_manager" install -y "$@"
+}
+
+packages=(
+ pciutils
+ valgrind
+ jq
+ nvme-cli
+ gdb
+ fio
+ librbd-devel
+ kernel-devel
+ gflags-devel
+ libasan
+ libubsan
+ autoconf
+ automake
+ libtool
+ libmount-devel
+ iscsi-initiator-utils
+ isns-utils-devel pmempool
+ perl-open
+ glib2-devel
+ pixman-devel
+ astyle-devel
+ elfutils
+ elfutils-libelf-devel
+ flex
+ bison
+ targetcli
+ perl-Switch
+ librdmacm-utils
+ libibverbs-utils
+ gdisk
+ socat
+ sshfs
+ sshpass
+ python3-pandas
+ rpm-build
+ iptables
+ clang-analyzer
+ bc
+ kernel-modules-extra
+ systemd-devel
+ python3
+ wget
+)
+
+pre_install() {
+ if [[ $ID == centos ]] && (( VERSION_ID == 8 )); then
+ "$package_manager" update -y --refresh
+ fi
+
+ install nbd || {
+ wget -O nbd.rpm https://download-ib01.fedoraproject.org/pub/epel/7/x86_64/Packages/n/nbd-3.14-2.el7.x86_64.rpm
+ install nbd.rpm
+ }
+}
diff --git a/src/spdk/test/common/config/vm_setup.conf b/src/spdk/test/common/config/vm_setup.conf
new file mode 100644
index 000000000..a8e58d82a
--- /dev/null
+++ b/src/spdk/test/common/config/vm_setup.conf
@@ -0,0 +1,12 @@
+# This configuration file is provided for reference purposes.
+GIT_REPO_SPDK=https://review.spdk.io/gerrit/spdk/spdk
+GIT_REPO_DPDK=https://github.com/spdk/dpdk.git
+GIT_REPO_OPEN_ISCSI=https://github.com/open-iscsi/open-iscsi
+GIT_REPO_ROCKSDB=https://review.gerrithub.io/spdk/rocksdb
+GIT_REPO_FIO=http://git.kernel.dk/fio.git
+GIT_REPO_FLAMEGRAPH=https://github.com/brendangregg/FlameGraph.git
+GIT_REPO_QEMU=https://github.com/spdk/qemu
+GIT_REPO_VPP=https://gerrit.fd.io/r/vpp
+GIT_REPO_LIBISCSI=https://github.com/sahlberg/libiscsi
+GIT_REPO_SPDK_NVME_CLI=https://github.com/spdk/nvme-cli
+DRIVER_LOCATION_QAT=https://01.org/sites/default/files/downloads/intelr-quickassist-technology/qat1.7.l.4.3.0-00033.tar.gz
diff --git a/src/spdk/test/common/config/vm_setup.sh b/src/spdk/test/common/config/vm_setup.sh
new file mode 100755
index 000000000..b2b3a8fc1
--- /dev/null
+++ b/src/spdk/test/common/config/vm_setup.sh
@@ -0,0 +1,176 @@
+#!/usr/bin/env bash
+
+# Virtual Machine environment requirements:
+# 8 GiB of RAM (for DPDK)
+# enable intel_kvm on your host machine
+
+# The purpose of this script is to provide a simple procedure for spinning up a new
+# virtual test environment capable of running our whole test suite. This script, when
+# applied to a fresh install of fedora 26 or ubuntu 16,18 server will install all of the
+# necessary dependencies to run almost the complete test suite. The main exception being VHost.
+# Vhost requires the configuration of a second virtual machine. instructions for how to configure
+# that vm are included in the file TEST_ENV_SETUP_README inside this repository
+
+# it is important to enable nesting for vms in kernel command line of your machine for the vhost tests.
+# in /etc/default/grub
+# append the following to the GRUB_CMDLINE_LINUX line
+# intel_iommu=on kvm-intel.nested=1
+
+# We have made a lot of progress with removing hardcoded paths from the tests,
+
+sudo() {
+ "$(type -P sudo)" -E "$@"
+}
+
+set -e
+
+VM_SETUP_PATH=$(readlink -f ${BASH_SOURCE%/*})
+
+UPGRADE=false
+INSTALL=false
+CONF="rocksdb,fio,flamegraph,tsocks,qemu,libiscsi,nvmecli,qat,spdk,refspdk"
+
+if [[ -e /etc/os-release ]]; then
+ source /etc/os-release
+fi
+
+if [ $(uname -s) == "FreeBSD" ]; then
+ OSID="freebsd"
+ OSVERSION=$(freebsd-version | cut -d. -f1)
+else
+ OSID=$(source /etc/os-release && echo $ID)
+ OSVERSION=$(source /etc/os-release && echo $VERSION_ID)
+fi
+
+function usage() {
+ echo "This script is intended to automate the environment setup for a linux virtual machine."
+ echo "Please run this script as your regular user. The script will make calls to sudo as needed."
+ echo ""
+ echo "./vm_setup.sh"
+ echo " -h --help"
+ echo " -u --upgrade Run $package_manager upgrade"
+ echo " -i --install-deps Install $package_manager based dependencies"
+ echo " -t --test-conf List of test configurations to enable (${CONF})"
+ echo " -c --conf-path Path to configuration file"
+ echo " -d --dir-git Path to where git sources should be saved"
+ echo " -s --disable-tsocks Disable use of tsocks"
+ exit 0
+}
+
+vmsetupdir=$(readlink -f "$(dirname "$0")")
+rootdir=$(readlink -f "$vmsetupdir/../../../")
+
+managers=("$vmsetupdir/pkgdep/"*)
+# Get package manager #
+if hash dnf &> /dev/null; then
+ source "$vmsetupdir/pkgdep/dnf"
+elif hash yum &> /dev/null; then
+ source "$vmsetupdir/pkgdep/yum"
+elif hash apt-get &> /dev/null; then
+ source "$vmsetupdir/pkgdep/apt-get"
+elif hash pacman &> /dev/null; then
+ source "$vmsetupdir/pkgdep/pacman"
+elif hash pkg &> /dev/null; then
+ source "$vmsetupdir/pkgdep/pkg"
+elif hash swupd &> /dev/null; then
+ source "$vmsetupdir/pkgdep/swupd"
+else
+ package_manager="undefined"
+fi
+
+# Parse input arguments #
+while getopts 'd:siuht:c:-:' optchar; do
+ case "$optchar" in
+ -)
+ case "$OPTARG" in
+ help) usage ;;
+ upgrade) UPGRADE=true ;;
+ install-deps) INSTALL=true ;;
+ test-conf=*) CONF="${OPTARG#*=}" ;;
+ conf-path=*) CONF_PATH="${OPTARG#*=}" ;;
+ dir-git=*) GIT_REPOS="${OPTARG#*=}" ;;
+ disable-tsocks) NO_TSOCKS=true ;;
+ *)
+ echo "Invalid argument '$OPTARG'"
+ usage
+ ;;
+ esac
+ ;;
+ h) usage ;;
+ u) UPGRADE=true ;;
+ i) INSTALL=true ;;
+ t) CONF="$OPTARG" ;;
+ c) CONF_PATH="$OPTARG" ;;
+ d) GIT_REPOS="$OPTARG" ;;
+ s) NO_TSOCKS=true ;;
+ *)
+ echo "Invalid argument '$OPTARG'"
+ usage
+ ;;
+ esac
+done
+
+if [[ "$package_manager" == "undefined" ]]; then
+ echo "Supported package manager not found. Script supports:"
+ printf ' * %s\n' "${managers[@]##*/}"
+ exit 1
+fi
+
+if [ -n "$CONF_PATH" ]; then
+ if [ ! -f "$CONF_PATH" ]; then
+ echo Configuration file does not exist: "$CONF_PATH"
+ exit 1
+ else
+ source "$CONF_PATH"
+ fi
+fi
+
+if $UPGRADE; then
+ upgrade
+fi
+
+if $INSTALL; then
+ sudo "$rootdir/scripts/pkgdep.sh" --all
+ pre_install
+ install "${packages[@]}"
+fi
+
+source "$vmsetupdir/pkgdep/git"
+
+# create autorun-spdk.conf in home folder. This is sourced by the autotest_common.sh file.
+# By setting any one of the values below to 0, you can skip that specific test. If you are
+# using your autotest platform to do sanity checks before uploading to the build pool, it is
+# probably best to only run the tests that you believe your changes have modified along with
+# Scanbuild and check format. This is because running the whole suite of tests in series can
+# take ~40 minutes to complete.
+if [ ! -e ~/autorun-spdk.conf ]; then
+ cat > ~/autorun-spdk.conf << EOF
+# assign a value of 1 to all of the pertinent tests
+SPDK_RUN_VALGRIND=1
+SPDK_TEST_CRYPTO=1
+SPDK_RUN_FUNCTIONAL_TEST=1
+SPDK_TEST_AUTOBUILD=1
+SPDK_TEST_UNITTEST=1
+SPDK_TEST_ISCSI=1
+SPDK_TEST_ISCSI_INITIATOR=1
+SPDK_TEST_NVME=1
+SPDK_TEST_NVME_CLI=1
+SPDK_TEST_NVMF=1
+SPDK_TEST_RBD=1
+SPDK_TEST_BLOCKDEV=1
+SPDK_TEST_BLOBFS=1
+SPDK_TEST_PMDK=1
+SPDK_TEST_LVOL=1
+SPDK_TEST_JSON=1
+SPDK_RUN_ASAN=1
+SPDK_RUN_UBSAN=1
+# doesn't work on vm
+SPDK_TEST_IOAT=0
+# requires some extra configuration. see TEST_ENV_SETUP_README
+SPDK_TEST_VHOST=0
+SPDK_TEST_VHOST_INIT=0
+# Not configured here
+SPDK_RUN_INSTALLED_DPDK=0
+
+EOF
+fi
diff --git a/src/spdk/test/common/lib/nvme/common_stubs.h b/src/spdk/test/common/lib/nvme/common_stubs.h
new file mode 100644
index 000000000..1dc22a162
--- /dev/null
+++ b/src/spdk/test/common/lib/nvme/common_stubs.h
@@ -0,0 +1,117 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "common/lib/test_env.c"
+
+const char *
+spdk_nvme_transport_id_trtype_str(enum spdk_nvme_transport_type trtype)
+{
+ switch (trtype) {
+ case SPDK_NVME_TRANSPORT_PCIE:
+ return "PCIe";
+ case SPDK_NVME_TRANSPORT_RDMA:
+ return "RDMA";
+ case SPDK_NVME_TRANSPORT_FC:
+ return "FC";
+ default:
+ return NULL;
+ }
+}
+
+int
+spdk_nvme_transport_id_populate_trstring(struct spdk_nvme_transport_id *trid, const char *trstring)
+{
+ int len, i;
+
+ if (trstring == NULL) {
+ return -EINVAL;
+ }
+
+ len = strnlen(trstring, SPDK_NVMF_TRSTRING_MAX_LEN);
+ if (len == SPDK_NVMF_TRSTRING_MAX_LEN) {
+ return -EINVAL;
+ }
+
+ /* cast official trstring to uppercase version of input. */
+ for (i = 0; i < len; i++) {
+ trid->trstring[i] = toupper(trstring[i]);
+ }
+ return 0;
+}
+
+DEFINE_STUB(nvme_request_check_timeout, int, (struct nvme_request *req, uint16_t cid,
+ struct spdk_nvme_ctrlr_process *active_proc, uint64_t now_tick), 0);
+DEFINE_STUB_V(nvme_ctrlr_destruct_finish, (struct spdk_nvme_ctrlr *ctrlr));
+DEFINE_STUB(nvme_ctrlr_construct, int, (struct spdk_nvme_ctrlr *ctrlr), 0);
+DEFINE_STUB_V(nvme_ctrlr_destruct, (struct spdk_nvme_ctrlr *ctrlr));
+DEFINE_STUB_V(nvme_ctrlr_init_cap, (struct spdk_nvme_ctrlr *ctrlr,
+ const union spdk_nvme_cap_register *cap,
+ const union spdk_nvme_vs_register *vs));
+DEFINE_STUB(nvme_ctrlr_get_vs, int, (struct spdk_nvme_ctrlr *ctrlr,
+ union spdk_nvme_vs_register *vs), 0);
+DEFINE_STUB(nvme_ctrlr_get_cap, int, (struct spdk_nvme_ctrlr *ctrlr,
+ union spdk_nvme_cap_register *cap), 0);
+DEFINE_STUB(nvme_qpair_init, int, (struct spdk_nvme_qpair *qpair, uint16_t id,
+ struct spdk_nvme_ctrlr *ctrlr,
+ enum spdk_nvme_qprio qprio,
+ uint32_t num_requests), 0);
+DEFINE_STUB_V(nvme_qpair_deinit, (struct spdk_nvme_qpair *qpair));
+DEFINE_STUB_V(spdk_nvme_transport_register, (const struct spdk_nvme_transport_ops *ops));
+DEFINE_STUB(nvme_transport_ctrlr_connect_qpair, int, (struct spdk_nvme_ctrlr *ctrlr,
+ struct spdk_nvme_qpair *qpair), 0);
+DEFINE_STUB(nvme_ctrlr_get_current_process, struct spdk_nvme_ctrlr_process *,
+ (struct spdk_nvme_ctrlr *ctrlr), (struct spdk_nvme_ctrlr_process *)(uintptr_t)0x1);
+DEFINE_STUB(nvme_ctrlr_add_process, int, (struct spdk_nvme_ctrlr *ctrlr, void *devhandle), 0);
+DEFINE_STUB_V(spdk_nvme_trid_populate_transport, (struct spdk_nvme_transport_id *trid,
+ enum spdk_nvme_transport_type trtype));
+DEFINE_STUB(nvme_get_transport, const struct spdk_nvme_transport *, (const char *transport_name),
+ NULL);
+DEFINE_STUB(spdk_nvme_qpair_process_completions, int32_t, (struct spdk_nvme_qpair *qpair,
+ uint32_t max_completions), 0);
+
+/* Fabric transports only */
+DEFINE_STUB_V(nvme_ctrlr_disconnect_qpair, (struct spdk_nvme_qpair *qpair));
+DEFINE_STUB(nvme_fabric_ctrlr_set_reg_4, int, (struct spdk_nvme_ctrlr *ctrlr, uint32_t offset,
+ uint32_t value), 0);
+DEFINE_STUB(nvme_fabric_ctrlr_set_reg_8, int, (struct spdk_nvme_ctrlr *ctrlr, uint32_t offset,
+ uint64_t value), 0);
+DEFINE_STUB(nvme_fabric_ctrlr_get_reg_4, int, (struct spdk_nvme_ctrlr *ctrlr, uint32_t offset,
+ uint32_t *value), 0);
+DEFINE_STUB(nvme_fabric_ctrlr_get_reg_8, int, (struct spdk_nvme_ctrlr *ctrlr, uint32_t offset,
+ uint64_t *value), 0);
+DEFINE_STUB(nvme_fabric_ctrlr_scan, int, (struct spdk_nvme_probe_ctx *probe_ctx,
+ bool direct_connect), 0);
+DEFINE_STUB(nvme_fabric_qpair_connect, int, (struct spdk_nvme_qpair *qpair, uint32_t num_entries),
+ 0);
+DEFINE_STUB_V(nvme_transport_ctrlr_disconnect_qpair, (struct spdk_nvme_ctrlr *ctrlr,
+ struct spdk_nvme_qpair *qpair));
+DEFINE_STUB(nvme_poll_group_disconnect_qpair, int, (struct spdk_nvme_qpair *qpair), 0);
diff --git a/src/spdk/test/common/lib/test_env.c b/src/spdk/test/common/lib/test_env.c
new file mode 100644
index 000000000..5e2912b5c
--- /dev/null
+++ b/src/spdk/test/common/lib/test_env.c
@@ -0,0 +1,637 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "spdk/stdinc.h"
+
+#include "spdk_internal/mock.h"
+
+#include "spdk/env.h"
+#include "spdk/queue.h"
+#include "spdk/util.h"
+
+static uint32_t g_ut_num_cores;
+static bool *g_ut_cores;
+
+void allocate_cores(uint32_t num_cores);
+void free_cores(void);
+
+DEFINE_STUB(spdk_process_is_primary, bool, (void), true)
+DEFINE_STUB(spdk_memzone_lookup, void *, (const char *name), NULL)
+DEFINE_STUB_V(spdk_pci_driver_register, (const char *name, struct spdk_pci_id *id_table,
+ uint32_t flags));
+DEFINE_STUB(spdk_pci_nvme_get_driver, struct spdk_pci_driver *, (void), NULL)
+DEFINE_STUB(spdk_pci_ioat_get_driver, struct spdk_pci_driver *, (void), NULL)
+DEFINE_STUB(spdk_pci_virtio_get_driver, struct spdk_pci_driver *, (void), NULL)
+
+void
+allocate_cores(uint32_t num_cores)
+{
+ uint32_t i;
+
+ g_ut_num_cores = num_cores;
+
+ g_ut_cores = calloc(num_cores, sizeof(bool));
+ assert(g_ut_cores != NULL);
+
+ for (i = 0; i < num_cores; i++) {
+ g_ut_cores[i] = true;
+ }
+}
+
+void
+free_cores(void)
+{
+ free(g_ut_cores);
+ g_ut_cores = NULL;
+ g_ut_num_cores = 0;
+}
+
+static uint32_t
+ut_get_next_core(uint32_t i)
+{
+ i++;
+
+ while (i < g_ut_num_cores) {
+ if (!g_ut_cores[i]) {
+ i++;
+ continue;
+ }
+ break;
+ }
+
+ if (i < g_ut_num_cores) {
+ return i;
+ } else {
+ return UINT32_MAX;
+ }
+}
+
+uint32_t
+spdk_env_get_first_core(void)
+{
+ return ut_get_next_core(-1);
+}
+
+uint32_t
+spdk_env_get_next_core(uint32_t prev_core)
+{
+ return ut_get_next_core(prev_core);
+}
+
+uint32_t
+spdk_env_get_core_count(void)
+{
+ return g_ut_num_cores;
+}
+
+uint32_t
+spdk_env_get_last_core(void)
+{
+ uint32_t i;
+ uint32_t last_core = UINT32_MAX;
+
+ SPDK_ENV_FOREACH_CORE(i) {
+ last_core = i;
+ }
+
+ return last_core;
+}
+
+DEFINE_RETURN_MOCK(spdk_env_get_current_core, uint32_t);
+uint32_t
+spdk_env_get_current_core(void)
+{
+ HANDLE_RETURN_MOCK(spdk_env_get_current_core);
+
+ return UINT32_MAX;
+}
+
+DEFINE_RETURN_MOCK(spdk_env_get_socket_id, uint32_t);
+uint32_t
+spdk_env_get_socket_id(uint32_t core)
+{
+ HANDLE_RETURN_MOCK(spdk_env_get_socket_id);
+
+ return SPDK_ENV_SOCKET_ID_ANY;
+}
+
+/*
+ * These mocks don't use the DEFINE_STUB macros because
+ * their default implementation is more complex.
+ */
+
+DEFINE_RETURN_MOCK(spdk_memzone_reserve, void *);
+void *
+spdk_memzone_reserve(const char *name, size_t len, int socket_id, unsigned flags)
+{
+ HANDLE_RETURN_MOCK(spdk_memzone_reserve);
+
+ return malloc(len);
+}
+
+DEFINE_RETURN_MOCK(spdk_memzone_reserve_aligned, void *);
+void *
+spdk_memzone_reserve_aligned(const char *name, size_t len, int socket_id,
+ unsigned flags, unsigned align)
+{
+ HANDLE_RETURN_MOCK(spdk_memzone_reserve_aligned);
+
+ return malloc(len);
+}
+
+DEFINE_RETURN_MOCK(spdk_malloc, void *);
+void *
+spdk_malloc(size_t size, size_t align, uint64_t *phys_addr, int socket_id, uint32_t flags)
+{
+ HANDLE_RETURN_MOCK(spdk_malloc);
+
+ void *buf = NULL;
+
+ if (align == 0) {
+ align = 8;
+ }
+
+ if (posix_memalign(&buf, align, size)) {
+ return NULL;
+ }
+ if (phys_addr) {
+ *phys_addr = (uint64_t)buf;
+ }
+
+ return buf;
+}
+
+DEFINE_RETURN_MOCK(spdk_zmalloc, void *);
+void *
+spdk_zmalloc(size_t size, size_t align, uint64_t *phys_addr, int socket_id, uint32_t flags)
+{
+ HANDLE_RETURN_MOCK(spdk_zmalloc);
+
+ void *buf = spdk_malloc(size, align, phys_addr, -1, 1);
+
+ if (buf != NULL) {
+ memset(buf, 0, size);
+ }
+ return buf;
+}
+
+DEFINE_RETURN_MOCK(spdk_dma_malloc, void *);
+void *
+spdk_dma_malloc(size_t size, size_t align, uint64_t *phys_addr)
+{
+ HANDLE_RETURN_MOCK(spdk_dma_malloc);
+
+ return spdk_malloc(size, align, phys_addr, -1, 1);
+}
+
+DEFINE_RETURN_MOCK(spdk_realloc, void *);
+void *
+spdk_realloc(void *buf, size_t size, size_t align)
+{
+ HANDLE_RETURN_MOCK(spdk_realloc);
+
+ return realloc(buf, size);
+}
+
+DEFINE_RETURN_MOCK(spdk_dma_zmalloc, void *);
+void *
+spdk_dma_zmalloc(size_t size, size_t align, uint64_t *phys_addr)
+{
+ HANDLE_RETURN_MOCK(spdk_dma_zmalloc);
+
+ return spdk_zmalloc(size, align, phys_addr, -1, 1);
+}
+
+DEFINE_RETURN_MOCK(spdk_dma_malloc_socket, void *);
+void *
+spdk_dma_malloc_socket(size_t size, size_t align, uint64_t *phys_addr, int socket_id)
+{
+ HANDLE_RETURN_MOCK(spdk_dma_malloc_socket);
+
+ return spdk_dma_malloc(size, align, phys_addr);
+}
+
+DEFINE_RETURN_MOCK(spdk_dma_zmalloc_socket, void *);
+void *
+spdk_dma_zmalloc_socket(size_t size, size_t align, uint64_t *phys_addr, int socket_id)
+{
+ HANDLE_RETURN_MOCK(spdk_dma_zmalloc_socket);
+
+ return spdk_dma_zmalloc(size, align, phys_addr);
+}
+
+DEFINE_RETURN_MOCK(spdk_dma_realloc, void *);
+void *
+spdk_dma_realloc(void *buf, size_t size, size_t align, uint64_t *phys_addr)
+{
+ HANDLE_RETURN_MOCK(spdk_dma_realloc);
+
+ return realloc(buf, size);
+}
+
+void
+spdk_free(void *buf)
+{
+ /* fix for false-positives in *certain* static analysis tools. */
+ assert((uintptr_t)buf != UINTPTR_MAX);
+ free(buf);
+}
+
+void
+spdk_dma_free(void *buf)
+{
+ return spdk_free(buf);
+}
+
+#ifndef UNIT_TEST_NO_VTOPHYS
+DEFINE_RETURN_MOCK(spdk_vtophys, uint64_t);
+uint64_t
+spdk_vtophys(void *buf, uint64_t *size)
+{
+ HANDLE_RETURN_MOCK(spdk_vtophys);
+
+ return (uintptr_t)buf;
+}
+#endif
+
+void
+spdk_memzone_dump(FILE *f)
+{
+ return;
+}
+
+DEFINE_RETURN_MOCK(spdk_memzone_free, int);
+int
+spdk_memzone_free(const char *name)
+{
+ HANDLE_RETURN_MOCK(spdk_memzone_free);
+
+ return 0;
+}
+
+struct test_mempool {
+ size_t count;
+ size_t ele_size;
+};
+
+DEFINE_RETURN_MOCK(spdk_mempool_create, struct spdk_mempool *);
+struct spdk_mempool *
+spdk_mempool_create(const char *name, size_t count,
+ size_t ele_size, size_t cache_size, int socket_id)
+{
+ struct test_mempool *mp;
+
+ HANDLE_RETURN_MOCK(spdk_mempool_create);
+
+ mp = calloc(1, sizeof(*mp));
+ if (mp == NULL) {
+ return NULL;
+ }
+
+ mp->count = count;
+ mp->ele_size = ele_size;
+
+ return (struct spdk_mempool *)mp;
+}
+
+void
+spdk_mempool_free(struct spdk_mempool *_mp)
+{
+ struct test_mempool *mp = (struct test_mempool *)_mp;
+
+ free(mp);
+}
+
+DEFINE_RETURN_MOCK(spdk_mempool_get, void *);
+void *
+spdk_mempool_get(struct spdk_mempool *_mp)
+{
+ struct test_mempool *mp = (struct test_mempool *)_mp;
+ size_t ele_size = 0x10000;
+ void *buf;
+
+ HANDLE_RETURN_MOCK(spdk_mempool_get);
+
+ if (mp && mp->count == 0) {
+ return NULL;
+ }
+
+ if (mp) {
+ ele_size = mp->ele_size;
+ }
+
+ if (posix_memalign(&buf, 64, spdk_align32pow2(ele_size))) {
+ return NULL;
+ } else {
+ if (mp) {
+ mp->count--;
+ }
+ return buf;
+ }
+}
+
+int
+spdk_mempool_get_bulk(struct spdk_mempool *mp, void **ele_arr, size_t count)
+{
+ for (size_t i = 0; i < count; i++) {
+ ele_arr[i] = spdk_mempool_get(mp);
+ if (ele_arr[i] == NULL) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+void
+spdk_mempool_put(struct spdk_mempool *_mp, void *ele)
+{
+ struct test_mempool *mp = (struct test_mempool *)_mp;
+
+ if (mp) {
+ mp->count++;
+ }
+ free(ele);
+}
+
+void
+spdk_mempool_put_bulk(struct spdk_mempool *mp, void **ele_arr, size_t count)
+{
+ for (size_t i = 0; i < count; i++) {
+ spdk_mempool_put(mp, ele_arr[i]);
+ }
+}
+
+DEFINE_RETURN_MOCK(spdk_mempool_count, size_t);
+size_t
+spdk_mempool_count(const struct spdk_mempool *_mp)
+{
+ struct test_mempool *mp = (struct test_mempool *)_mp;
+
+ HANDLE_RETURN_MOCK(spdk_mempool_count);
+
+ if (mp) {
+ return mp->count;
+ } else {
+ return 1024;
+ }
+}
+
+struct spdk_ring_ele {
+ void *ele;
+ TAILQ_ENTRY(spdk_ring_ele) link;
+};
+
+struct spdk_ring {
+ TAILQ_HEAD(, spdk_ring_ele) elements;
+ pthread_mutex_t lock;
+ size_t count;
+};
+
+DEFINE_RETURN_MOCK(spdk_ring_create, struct spdk_ring *);
+struct spdk_ring *
+spdk_ring_create(enum spdk_ring_type type, size_t count, int socket_id)
+{
+ struct spdk_ring *ring;
+
+ HANDLE_RETURN_MOCK(spdk_ring_create);
+
+ ring = calloc(1, sizeof(*ring));
+ if (!ring) {
+ return NULL;
+ }
+
+ if (pthread_mutex_init(&ring->lock, NULL)) {
+ free(ring);
+ return NULL;
+ }
+
+ TAILQ_INIT(&ring->elements);
+ return ring;
+}
+
+void
+spdk_ring_free(struct spdk_ring *ring)
+{
+ struct spdk_ring_ele *ele, *tmp;
+
+ if (!ring) {
+ return;
+ }
+
+ TAILQ_FOREACH_SAFE(ele, &ring->elements, link, tmp) {
+ free(ele);
+ }
+
+ pthread_mutex_destroy(&ring->lock);
+ free(ring);
+}
+
+DEFINE_RETURN_MOCK(spdk_ring_enqueue, size_t);
+size_t
+spdk_ring_enqueue(struct spdk_ring *ring, void **objs, size_t count,
+ size_t *free_space)
+{
+ struct spdk_ring_ele *ele;
+ size_t i;
+
+ HANDLE_RETURN_MOCK(spdk_ring_enqueue);
+
+ pthread_mutex_lock(&ring->lock);
+
+ for (i = 0; i < count; i++) {
+ ele = calloc(1, sizeof(*ele));
+ if (!ele) {
+ break;
+ }
+
+ ele->ele = objs[i];
+ TAILQ_INSERT_TAIL(&ring->elements, ele, link);
+ ring->count++;
+ }
+
+ pthread_mutex_unlock(&ring->lock);
+ return i;
+}
+
+DEFINE_RETURN_MOCK(spdk_ring_dequeue, size_t);
+size_t
+spdk_ring_dequeue(struct spdk_ring *ring, void **objs, size_t count)
+{
+ struct spdk_ring_ele *ele, *tmp;
+ size_t i = 0;
+
+ HANDLE_RETURN_MOCK(spdk_ring_dequeue);
+
+ if (count == 0) {
+ return 0;
+ }
+
+ pthread_mutex_lock(&ring->lock);
+
+ TAILQ_FOREACH_SAFE(ele, &ring->elements, link, tmp) {
+ TAILQ_REMOVE(&ring->elements, ele, link);
+ ring->count--;
+ objs[i] = ele->ele;
+ free(ele);
+ i++;
+ if (i >= count) {
+ break;
+ }
+ }
+
+ pthread_mutex_unlock(&ring->lock);
+ return i;
+
+}
+
+
+DEFINE_RETURN_MOCK(spdk_ring_count, size_t);
+size_t
+spdk_ring_count(struct spdk_ring *ring)
+{
+ HANDLE_RETURN_MOCK(spdk_ring_count);
+ return ring->count;
+}
+
+DEFINE_RETURN_MOCK(spdk_get_ticks, uint64_t);
+uint64_t
+spdk_get_ticks(void)
+{
+ HANDLE_RETURN_MOCK(spdk_get_ticks);
+
+ return ut_spdk_get_ticks;
+}
+
+DEFINE_RETURN_MOCK(spdk_get_ticks_hz, uint64_t);
+uint64_t
+spdk_get_ticks_hz(void)
+{
+ HANDLE_RETURN_MOCK(spdk_get_ticks_hz);
+
+ return 1000000;
+}
+
+void
+spdk_delay_us(unsigned int us)
+{
+ /* spdk_get_ticks_hz is 1000000, meaning 1 tick per us. */
+ ut_spdk_get_ticks += us;
+}
+
+#ifndef UNIT_TEST_NO_PCI_ADDR
+DEFINE_RETURN_MOCK(spdk_pci_addr_parse, int);
+int
+spdk_pci_addr_parse(struct spdk_pci_addr *addr, const char *bdf)
+{
+ unsigned domain, bus, dev, func;
+
+ HANDLE_RETURN_MOCK(spdk_pci_addr_parse);
+
+ if (addr == NULL || bdf == NULL) {
+ return -EINVAL;
+ }
+
+ if ((sscanf(bdf, "%x:%x:%x.%x", &domain, &bus, &dev, &func) == 4) ||
+ (sscanf(bdf, "%x.%x.%x.%x", &domain, &bus, &dev, &func) == 4)) {
+ /* Matched a full address - all variables are initialized */
+ } else if (sscanf(bdf, "%x:%x:%x", &domain, &bus, &dev) == 3) {
+ func = 0;
+ } else if ((sscanf(bdf, "%x:%x.%x", &bus, &dev, &func) == 3) ||
+ (sscanf(bdf, "%x.%x.%x", &bus, &dev, &func) == 3)) {
+ domain = 0;
+ } else if ((sscanf(bdf, "%x:%x", &bus, &dev) == 2) ||
+ (sscanf(bdf, "%x.%x", &bus, &dev) == 2)) {
+ domain = 0;
+ func = 0;
+ } else {
+ return -EINVAL;
+ }
+
+ if (bus > 0xFF || dev > 0x1F || func > 7) {
+ return -EINVAL;
+ }
+
+ addr->domain = domain;
+ addr->bus = bus;
+ addr->dev = dev;
+ addr->func = func;
+
+ return 0;
+}
+
+DEFINE_RETURN_MOCK(spdk_pci_addr_fmt, int);
+int
+spdk_pci_addr_fmt(char *bdf, size_t sz, const struct spdk_pci_addr *addr)
+{
+ int rc;
+
+ HANDLE_RETURN_MOCK(spdk_pci_addr_fmt);
+
+ rc = snprintf(bdf, sz, "%04x:%02x:%02x.%x",
+ addr->domain, addr->bus,
+ addr->dev, addr->func);
+
+ if (rc > 0 && (size_t)rc < sz) {
+ return 0;
+ }
+
+ return -1;
+}
+
+DEFINE_RETURN_MOCK(spdk_pci_addr_compare, int);
+int
+spdk_pci_addr_compare(const struct spdk_pci_addr *a1, const struct spdk_pci_addr *a2)
+{
+ HANDLE_RETURN_MOCK(spdk_pci_addr_compare);
+
+ if (a1->domain > a2->domain) {
+ return 1;
+ } else if (a1->domain < a2->domain) {
+ return -1;
+ } else if (a1->bus > a2->bus) {
+ return 1;
+ } else if (a1->bus < a2->bus) {
+ return -1;
+ } else if (a1->dev > a2->dev) {
+ return 1;
+ } else if (a1->dev < a2->dev) {
+ return -1;
+ } else if (a1->func > a2->func) {
+ return 1;
+ } else if (a1->func < a2->func) {
+ return -1;
+ }
+
+ return 0;
+}
+#endif
diff --git a/src/spdk/test/common/lib/test_rdma.c b/src/spdk/test/common/lib/test_rdma.c
new file mode 100644
index 000000000..109862fe6
--- /dev/null
+++ b/src/spdk/test/common/lib/test_rdma.c
@@ -0,0 +1,49 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (c) Intel Corporation. All rights reserved.
+ * Copyright (c) 2020 Mellanox Technologies LTD. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "spdk/stdinc.h"
+
+#include "spdk_internal/rdma.h"
+#include "spdk_internal/mock.h"
+
+DEFINE_STUB(spdk_rdma_qp_create, struct spdk_rdma_qp *, (struct rdma_cm_id *cm_id,
+ struct spdk_rdma_qp_init_attr *qp_attr), NULL);
+DEFINE_STUB(spdk_rdma_qp_accept, int, (struct spdk_rdma_qp *spdk_rdma_qp,
+ struct rdma_conn_param *conn_param), 0);
+DEFINE_STUB(spdk_rdma_qp_complete_connect, int, (struct spdk_rdma_qp *spdk_rdma_qp), 0);
+DEFINE_STUB_V(spdk_rdma_qp_destroy, (struct spdk_rdma_qp *spdk_rdma_qp));
+DEFINE_STUB(spdk_rdma_qp_disconnect, int, (struct spdk_rdma_qp *spdk_rdma_qp), 0);
+DEFINE_STUB(spdk_rdma_qp_queue_send_wrs, bool, (struct spdk_rdma_qp *spdk_rdma_qp,
+ struct ibv_send_wr *first), true);
+DEFINE_STUB(spdk_rdma_qp_flush_send_wrs, int, (struct spdk_rdma_qp *spdk_rdma_qp,
+ struct ibv_send_wr **bad_wr), 0);
diff --git a/src/spdk/test/common/lib/test_sock.c b/src/spdk/test/common/lib/test_sock.c
new file mode 100644
index 000000000..d2c83b732
--- /dev/null
+++ b/src/spdk/test/common/lib/test_sock.c
@@ -0,0 +1,70 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "spdk/stdinc.h"
+
+#include "spdk_internal/sock.h"
+#include "spdk_internal/mock.h"
+
+DEFINE_STUB(spdk_sock_getaddr, int, (struct spdk_sock *sock, char *saddr, int slen, uint16_t *sport,
+ char *caddr, int clen, uint16_t *cport), 0);
+DEFINE_STUB(spdk_sock_connect, struct spdk_sock *, (const char *ip, int port, char *impl_name),
+ NULL);
+DEFINE_STUB(spdk_sock_connect_ext, struct spdk_sock *, (const char *ip, int port, char *impl_name,
+ struct spdk_sock_opts *opts), NULL);
+DEFINE_STUB(spdk_sock_listen, struct spdk_sock *, (const char *ip, int port, char *impl_name),
+ NULL);
+DEFINE_STUB(spdk_sock_listen_ext, struct spdk_sock *, (const char *ip, int port, char *impl_name,
+ struct spdk_sock_opts *opts), NULL);
+DEFINE_STUB_V(spdk_sock_get_default_opts, (struct spdk_sock_opts *opts));
+DEFINE_STUB(spdk_sock_accept, struct spdk_sock *, (struct spdk_sock *sock), NULL);
+DEFINE_STUB(spdk_sock_close, int, (struct spdk_sock **sock), 0);
+DEFINE_STUB(spdk_sock_recv, ssize_t, (struct spdk_sock *sock, void *buf, size_t len), 0);
+DEFINE_STUB(spdk_sock_writev, ssize_t, (struct spdk_sock *sock, struct iovec *iov, int iovcnt), 0);
+DEFINE_STUB(spdk_sock_readv, ssize_t, (struct spdk_sock *sock, struct iovec *iov, int iovcnt), 0);
+DEFINE_STUB(spdk_sock_set_recvlowat, int, (struct spdk_sock *sock, int nbytes), 0);
+DEFINE_STUB(spdk_sock_set_recvbuf, int, (struct spdk_sock *sock, int sz), 0);
+DEFINE_STUB(spdk_sock_set_sendbuf, int, (struct spdk_sock *sock, int sz), 0);
+DEFINE_STUB_V(spdk_sock_writev_async, (struct spdk_sock *sock, struct spdk_sock_request *req));
+DEFINE_STUB(spdk_sock_flush, int, (struct spdk_sock *sock), 0);
+DEFINE_STUB(spdk_sock_is_ipv6, bool, (struct spdk_sock *sock), false);
+DEFINE_STUB(spdk_sock_is_ipv4, bool, (struct spdk_sock *sock), true);
+DEFINE_STUB(spdk_sock_is_connected, bool, (struct spdk_sock *sock), true);
+DEFINE_STUB(spdk_sock_group_create, struct spdk_sock_group *, (void *ctx), NULL);
+DEFINE_STUB(spdk_sock_group_add_sock, int, (struct spdk_sock_group *group, struct spdk_sock *sock,
+ spdk_sock_cb cb_fn, void *cb_arg), 0);
+DEFINE_STUB(spdk_sock_group_remove_sock, int, (struct spdk_sock_group *group,
+ struct spdk_sock *sock), 0);
+DEFINE_STUB(spdk_sock_group_poll, int, (struct spdk_sock_group *group), 0);
+DEFINE_STUB(spdk_sock_group_poll_count, int, (struct spdk_sock_group *group, int max_events), 0);
+DEFINE_STUB(spdk_sock_group_close, int, (struct spdk_sock_group **group), 0);
diff --git a/src/spdk/test/common/lib/ut_multithread.c b/src/spdk/test/common/lib/ut_multithread.c
new file mode 100644
index 000000000..30b78f74d
--- /dev/null
+++ b/src/spdk/test/common/lib/ut_multithread.c
@@ -0,0 +1,214 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "spdk_cunit.h"
+#include "spdk/thread.h"
+#include "spdk_internal/mock.h"
+#include "spdk_internal/thread.h"
+
+#include "common/lib/test_env.c"
+
+static uint32_t g_ut_num_threads;
+
+int allocate_threads(int num_threads);
+void free_threads(void);
+void poll_threads(void);
+bool poll_thread(uintptr_t thread_id);
+bool poll_thread_times(uintptr_t thread_id, uint32_t max_polls);
+
+struct ut_msg {
+ spdk_msg_fn fn;
+ void *ctx;
+ TAILQ_ENTRY(ut_msg) link;
+};
+
+struct ut_thread {
+ struct spdk_thread *thread;
+ struct spdk_io_channel *ch;
+};
+
+struct ut_thread *g_ut_threads;
+
+#define INVALID_THREAD 0x1000
+
+static uint64_t g_ut_thread_id = INVALID_THREAD;
+
+static void
+set_thread(uintptr_t thread_id)
+{
+ g_ut_thread_id = thread_id;
+ if (thread_id == INVALID_THREAD) {
+ spdk_set_thread(NULL);
+ } else {
+ spdk_set_thread(g_ut_threads[thread_id].thread);
+ }
+
+}
+
+int
+allocate_threads(int num_threads)
+{
+ struct spdk_thread *thread;
+ uint32_t i;
+
+ spdk_thread_lib_init(NULL, 0);
+
+ g_ut_num_threads = num_threads;
+
+ g_ut_threads = calloc(num_threads, sizeof(*g_ut_threads));
+ assert(g_ut_threads != NULL);
+
+ for (i = 0; i < g_ut_num_threads; i++) {
+ set_thread(i);
+ thread = spdk_thread_create(NULL, NULL);
+ assert(thread != NULL);
+ g_ut_threads[i].thread = thread;
+ }
+
+ set_thread(INVALID_THREAD);
+ return 0;
+}
+
+void
+free_threads(void)
+{
+ uint32_t i, num_threads;
+ struct spdk_thread *thread;
+
+ for (i = 0; i < g_ut_num_threads; i++) {
+ set_thread(i);
+ thread = g_ut_threads[i].thread;
+ spdk_thread_exit(thread);
+ }
+
+ num_threads = g_ut_num_threads;
+
+ while (num_threads != 0) {
+ for (i = 0; i < g_ut_num_threads; i++) {
+ set_thread(i);
+ thread = g_ut_threads[i].thread;
+ if (thread == NULL) {
+ continue;
+ }
+
+ if (spdk_thread_is_exited(thread)) {
+ g_ut_threads[i].thread = NULL;
+ num_threads--;
+ spdk_thread_destroy(thread);
+ } else {
+ spdk_thread_poll(thread, 0, 0);
+ }
+ }
+ }
+
+ g_ut_num_threads = 0;
+ free(g_ut_threads);
+ g_ut_threads = NULL;
+
+ spdk_thread_lib_fini();
+}
+
+bool
+poll_thread_times(uintptr_t thread_id, uint32_t max_polls)
+{
+ bool busy = false;
+ struct ut_thread *thread = &g_ut_threads[thread_id];
+ uintptr_t original_thread_id;
+ uint32_t polls_executed = 0;
+ uint64_t now;
+
+ if (max_polls == 0) {
+ /* If max_polls is set to 0,
+ * poll until no operation is pending. */
+ return poll_thread(thread_id);
+ }
+ assert(thread_id != (uintptr_t)INVALID_THREAD);
+ assert(thread_id < g_ut_num_threads);
+
+ original_thread_id = g_ut_thread_id;
+ set_thread(INVALID_THREAD);
+
+ now = spdk_get_ticks();
+ while (polls_executed < max_polls) {
+ if (spdk_thread_poll(thread->thread, 1, now) > 0) {
+ busy = true;
+ }
+ now = spdk_thread_get_last_tsc(thread->thread);
+ polls_executed++;
+ }
+
+ set_thread(original_thread_id);
+
+ return busy;
+}
+
+bool
+poll_thread(uintptr_t thread_id)
+{
+ bool busy = false;
+ struct ut_thread *thread = &g_ut_threads[thread_id];
+ uintptr_t original_thread_id;
+ uint64_t now;
+
+ assert(thread_id != (uintptr_t)INVALID_THREAD);
+ assert(thread_id < g_ut_num_threads);
+
+ original_thread_id = g_ut_thread_id;
+ set_thread(INVALID_THREAD);
+
+ now = spdk_get_ticks();
+ while (spdk_thread_poll(thread->thread, 0, now) > 0) {
+ now = spdk_thread_get_last_tsc(thread->thread);
+ busy = true;
+ }
+
+ set_thread(original_thread_id);
+
+ return busy;
+}
+
+void
+poll_threads(void)
+{
+ while (true) {
+ bool busy = false;
+
+ for (uint32_t i = 0; i < g_ut_num_threads; i++) {
+ busy = busy || poll_thread(i);
+ }
+
+ if (!busy) {
+ break;
+ }
+ }
+}
diff --git a/src/spdk/test/common/skipped_build_files.txt b/src/spdk/test/common/skipped_build_files.txt
new file mode 100644
index 000000000..dca967681
--- /dev/null
+++ b/src/spdk/test/common/skipped_build_files.txt
@@ -0,0 +1,60 @@
+# Not configured to test vtune.
+lib/bdev/vtune
+
+# Not configured to test VPP
+module/sock/vpp/vpp
+
+# Not configured to test rocksdb env file
+lib/rocksdb/env_spdk.cc
+
+# Not configured to test FC
+lib/nvmf/fc
+lib/nvmf/fc_ls
+test/unit/lib/nvmf/fc.c/fc_ut
+test/unit/lib/nvmf/fc_ls.c/fc_ls_ut
+
+# Not configured for Neon testing
+lib/util/base64_neon
+
+# Not configured for mlx5 dv testing
+lib/rdma/rdma_mlx5_dv
+
+# Files related to testing our internal vhost implementation.
+lib/rte_vhost/fd_man
+lib/rte_vhost/socket
+lib/rte_vhost/vhost
+lib/rte_vhost/vhost_user
+lib/vhost/vhost_nvme
+lib/virtio/vhost_user
+
+# Cuse related files, enable when ready.
+lib/nvme/nvme_cuse
+module/bdev/nvme/bdev_nvme_cuse_rpc
+test/nvme/cuse/cuse
+
+# Currently we don't have this plumbed for testing, enable when ready.
+module/bdev/uring/bdev_uring
+module/bdev/uring/bdev_uring_rpc
+module/sock/uring/uring
+
+# Currently not testing blobfs_fuse, enable when ready.
+module/blobfs/bdev/blobfs_fuse
+test/blobfs/fuse/fuse
+
+
+# These files all represent c files that are only compiled by direct inclusion in other files.
+test/common/lib/test_env
+test/common/lib/test_sock
+test/common/lib/ut_multithread
+test/common/lib/test_rdma
+test/unit/lib/blob/bs_dev_common
+test/unit/lib/blob/bs_scheduler
+test/unit/lib/ftl/common/utils
+test/unit/lib/iscsi/common
+test/unit/lib/json_mock
+test/unit/lib/sock/uring.c/uring_ut
+
+# These files are in the external_code directory which doesn't get compiled with SPDK.
+test/external_code/hello_world/hello_bdev
+test/external_code/passthru/vbdev_passthru
+test/external_code/passthru/vbdev_passthru_rpc
diff --git a/src/spdk/test/common/skipped_tests.txt b/src/spdk/test/common/skipped_tests.txt
new file mode 100644
index 000000000..d96957f2b
--- /dev/null
+++ b/src/spdk/test/common/skipped_tests.txt
@@ -0,0 +1,73 @@
+# This file represents the tests we are intentionally skipping in CI testing.
+
+# cases
+ftl_dirty_shutdown
+ftl_fio_basic
+ftl_fio_extended
+ftl_restore_nv_cache
+
+# Waiting for test refactor
+iscsi_tgt_fio_remote_nvme
+
+# VPP deprecated with 20.07
+iscsi_tgt_vpp
+
+# Waiting on significant test rewrite
+nvme_opal
+nvme_opal_bdevio
+nvme_opal_bdevperf
+nvme_opal_spdk_tgt
+
+# CI doesn't have FC hardware
+nvmf_fc
+spdkcli_nvmf_fc
+unittest_nvmf_fc
+unittest_nvmf_fc_ls
+
+# Enable after cuse tests switch to physical devices
+nvme_ns_manage_cuse
+
+# These tests are currently only run manually
+vhost_blk_fs_integrity
+vhost_blk_hot_remove
+vhost_scsi_hot_remove
+vhost_hotplug
+
+# Waiting on hardware
+vmd
+vmd_bdev_svc
+vmd_fio
+vmd_hello_world
+vmd_identify
+vmd_perf
+
+# nightly tests
+bdev_fio_rw_verify_ext
+bdev_fio_trim_ext
+bdev_reset
+iscsi_tgt_digest
+iscsi_tgt_data_digest
+iscsi_tgt_pmem
+iscsi_tgt_ext4test
+iscsi_tgt_digests
+iscsi_tgt_multiconnection
+iscsi_tgt_fuzz
+nvmf_fuzz
+nvmf_multiconnection
+nvmf_initiator_timeout
+vhost_blk_2core_2ctrl
+vhost_blk_1core_2ctrl
+vhost_blk_fs_integrity
+vhost_blk_integrity
+vhost_blk_nightly
+vhost_lvol_integrity_1core_1ctrl
+vhost_migration
+vhost_migration_tc1
+vhost_migration_tc2
+vhost_readonly
+vhost_scsi_fs_integrity
+vhost_scsi_integrity
+vhost_scsi_nightly
+vhost_scsi_2core_2ctrl
+vhost_scsi_1core_2ctrl
+vhost_scsi_1core_1ctrl