xref: /spdk/test/common/autotest_common.sh (revision 7025ceb9c119a6da0b6ee2013b6ae94b51fac2df)
1#!/usr/bin/env bash
2#  SPDX-License-Identifier: BSD-3-Clause
3#  Copyright (C) 2015 Intel Corporation
4#  All rights reserved.
5#  Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
6#
7rpc_py=rpc_cmd
8
9function xtrace_disable() {
10	set +x
11	X_STACK+=("${FUNCNAME[*]}") # push
12}
13
14function xtrace_restore() {
15	# unset'ing foo[-1] under older Bash (4.2 -> Centos7) won't work, hence the dance
16	unset -v "X_STACK[${#X_STACK[@]} - 1 < 0 ? 0 : ${#X_STACK[@]} - 1]" # pop
17	if ((${#X_STACK[@]} == 0)); then
18		set -x
19	fi
20}
21
22function xtrace_disable_per_cmd() { eval "$* ${BASH_XTRACEFD}> /dev/null"; }
23
24function xtrace_fd() {
25	if [[ -n ${BASH_XTRACEFD:-} && -e /proc/self/fd/$BASH_XTRACEFD ]]; then
26		# Close it first to make sure it's sane
27		exec {BASH_XTRACEFD}>&-
28	fi
29	exec {BASH_XTRACEFD}>&2
30
31	xtrace_restore
32}
33
34set -e
35shopt -s nullglob
36shopt -s extglob
37
38if [ -z "${output_dir:-}" ]; then
39	mkdir -p "$rootdir/../output"
40	export output_dir="$rootdir/../output"
41fi
42
43if [[ -e $rootdir/test/common/build_config.sh ]]; then
44	source "$rootdir/test/common/build_config.sh"
45elif [[ -e $rootdir/mk/config.mk ]]; then
46	build_config=$(< "$rootdir/mk/config.mk")
47	source <(echo "${build_config//\?=/=}")
48else
49	source "$rootdir/CONFIG"
50fi
51
52# Source scripts after the config so that the definitions are available.
53source "$rootdir/test/common/applications.sh"
54source "$rootdir/scripts/common.sh"
55source "$rootdir/scripts/perf/pm/common"
56
57: ${RUN_NIGHTLY:=0}
58export RUN_NIGHTLY
59
60# Set defaults for missing test config options
61: ${SPDK_AUTOTEST_DEBUG_APPS:=0}
62export SPDK_AUTOTEST_DEBUG_APPS
63: ${SPDK_RUN_VALGRIND=0}
64export SPDK_RUN_VALGRIND
65: ${SPDK_RUN_FUNCTIONAL_TEST=0}
66export SPDK_RUN_FUNCTIONAL_TEST
67: ${SPDK_TEST_UNITTEST=0}
68export SPDK_TEST_UNITTEST
69: ${SPDK_TEST_AUTOBUILD=""}
70export SPDK_TEST_AUTOBUILD
71: ${SPDK_TEST_RELEASE_BUILD=0}
72export SPDK_TEST_RELEASE_BUILD
73: ${SPDK_TEST_ISAL=0}
74export SPDK_TEST_ISAL
75: ${SPDK_TEST_ISCSI=0}
76export SPDK_TEST_ISCSI
77: ${SPDK_TEST_ISCSI_INITIATOR=0}
78export SPDK_TEST_ISCSI_INITIATOR
79: ${SPDK_TEST_NVME=0}
80export SPDK_TEST_NVME
81: ${SPDK_TEST_NVME_PMR=0}
82export SPDK_TEST_NVME_PMR
83: ${SPDK_TEST_NVME_BP=0}
84export SPDK_TEST_NVME_BP
85: ${SPDK_TEST_NVME_CLI=0}
86export SPDK_TEST_NVME_CLI
87: ${SPDK_TEST_NVME_CUSE=0}
88export SPDK_TEST_NVME_CUSE
89: ${SPDK_TEST_NVME_FDP=0}
90export SPDK_TEST_NVME_FDP
91: ${SPDK_TEST_NVMF=0}
92export SPDK_TEST_NVMF
93: ${SPDK_TEST_VFIOUSER=0}
94export SPDK_TEST_VFIOUSER
95: ${SPDK_TEST_VFIOUSER_QEMU=0}
96export SPDK_TEST_VFIOUSER_QEMU
97: ${SPDK_TEST_FUZZER=0}
98export SPDK_TEST_FUZZER
99: ${SPDK_TEST_FUZZER_SHORT=0}
100export SPDK_TEST_FUZZER_SHORT
101: ${SPDK_TEST_NVMF_TRANSPORT="rdma"}
102export SPDK_TEST_NVMF_TRANSPORT
103: ${SPDK_TEST_RBD=0}
104export SPDK_TEST_RBD
105: ${SPDK_TEST_VHOST=0}
106export SPDK_TEST_VHOST
107: ${SPDK_TEST_BLOCKDEV=0}
108export SPDK_TEST_BLOCKDEV
109: ${SPDK_TEST_IOAT=0}
110export SPDK_TEST_IOAT
111: ${SPDK_TEST_BLOBFS=0}
112export SPDK_TEST_BLOBFS
113: ${SPDK_TEST_VHOST_INIT=0}
114export SPDK_TEST_VHOST_INIT
115: ${SPDK_TEST_LVOL=0}
116export SPDK_TEST_LVOL
117: ${SPDK_TEST_VBDEV_COMPRESS=0}
118export SPDK_TEST_VBDEV_COMPRESS
119: ${SPDK_RUN_ASAN=0}
120export SPDK_RUN_ASAN
121: ${SPDK_RUN_UBSAN=0}
122export SPDK_RUN_UBSAN
123: ${SPDK_RUN_EXTERNAL_DPDK=""}
124export SPDK_RUN_EXTERNAL_DPDK
125: ${SPDK_RUN_NON_ROOT=0}
126export SPDK_RUN_NON_ROOT
127: ${SPDK_TEST_CRYPTO=0}
128export SPDK_TEST_CRYPTO
129: ${SPDK_TEST_FTL=0}
130export SPDK_TEST_FTL
131: ${SPDK_TEST_OCF=0}
132export SPDK_TEST_OCF
133: ${SPDK_TEST_VMD=0}
134export SPDK_TEST_VMD
135: ${SPDK_TEST_OPAL=0}
136export SPDK_TEST_OPAL
137: ${SPDK_TEST_NATIVE_DPDK}
138export SPDK_TEST_NATIVE_DPDK
139: ${SPDK_AUTOTEST_X=true}
140export SPDK_AUTOTEST_X
141: ${SPDK_TEST_RAID5=0}
142export SPDK_TEST_RAID5
143: ${SPDK_TEST_URING=0}
144export SPDK_TEST_URING
145: ${SPDK_TEST_USDT=0}
146export SPDK_TEST_USDT
147: ${SPDK_TEST_USE_IGB_UIO:=0}
148export SPDK_TEST_USE_IGB_UIO
149: ${SPDK_TEST_SCHEDULER:=0}
150export SPDK_TEST_SCHEDULER
151: ${SPDK_TEST_SCANBUILD:=0}
152export SPDK_TEST_SCANBUILD
153: ${SPDK_TEST_NVMF_NICS:=}
154export SPDK_TEST_NVMF_NICS
155: ${SPDK_TEST_SMA=0}
156export SPDK_TEST_SMA
157: ${SPDK_TEST_DAOS=0}
158export SPDK_TEST_DAOS
159: ${SPDK_TEST_XNVME:=0}
160export SPDK_TEST_XNVME
161: ${SPDK_TEST_ACCEL_DSA=0}
162export SPDK_TEST_ACCEL_DSA
163: ${SPDK_TEST_ACCEL_IAA=0}
164export SPDK_TEST_ACCEL_IAA
165# Comma-separated list of fuzzer targets matching test/fuzz/llvm/$target
166: ${SPDK_TEST_FUZZER_TARGET:=}
167export SPDK_TEST_FUZZER_TARGET
168: ${SPDK_TEST_NVMF_MDNS=0}
169export SPDK_TEST_NVMF_MDNS
170: ${SPDK_JSONRPC_GO_CLIENT=0}
171export SPDK_JSONRPC_GO_CLIENT
172
173# always test with SPDK shared objects.
174export SPDK_LIB_DIR="$rootdir/build/lib"
175export DPDK_LIB_DIR="${SPDK_RUN_EXTERNAL_DPDK:-$rootdir/dpdk/build}/lib"
176export VFIO_LIB_DIR="$rootdir/build/libvfio-user/usr/local/lib"
177export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$SPDK_LIB_DIR:$DPDK_LIB_DIR:$VFIO_LIB_DIR
178
179# Tell setup.sh to wait for block devices upon each reset
180export PCI_BLOCK_SYNC_ON_RESET=yes
181
182# Export PYTHONPATH with addition of RPC framework. New scripts can be created
183# specific use cases for tests.
184export PYTHONPATH=$PYTHONPATH:$rootdir/python
185
186# Don't create Python .pyc files. When running with sudo these will be
187# created with root ownership and can cause problems when cleaning the repository.
188export PYTHONDONTWRITEBYTECODE=1
189
190# Export new_delete_type_mismatch to skip the known bug that exists in librados
191# https://tracker.ceph.com/issues/24078
192export ASAN_OPTIONS=new_delete_type_mismatch=0:disable_coredump=0:abort_on_error=1:use_sigaltstack=0
193export UBSAN_OPTIONS='halt_on_error=1:print_stacktrace=1:abort_on_error=1:disable_coredump=0:exitcode=134'
194
195# Export LeakSanitizer option to use suppression file in order to prevent false positives
196# and known leaks in external executables or libraries from showing up.
197asan_suppression_file="/var/tmp/asan_suppression_file"
198rm -rf "$asan_suppression_file" 2> /dev/null || sudo rm -rf "$asan_suppression_file"
199cat << EOL >> "$asan_suppression_file"
200# ASAN has some bugs around thread_local variables.  We have a destructor in place
201# to free the thread contexts, but ASAN complains about the leak before those
202# destructors have a chance to run.  So suppress this one specific leak using
203# LSAN_OPTIONS.
204leak:spdk_fs_alloc_thread_ctx
205
206# Suppress known leaks in fio project
207leak:$CONFIG_FIO_SOURCE_DIR/parse.c
208leak:$CONFIG_FIO_SOURCE_DIR/iolog.c
209leak:$CONFIG_FIO_SOURCE_DIR/init.c
210leak:$CONFIG_FIO_SOURCE_DIR/filesetup.c
211leak:fio_memalign
212leak:spdk_fio_io_u_init
213# Suppress leaks in gperftools-libs from fio
214leak:libtcmalloc_minimal.so
215
216# Suppress leaks in libiscsi
217leak:libiscsi.so
218
219# Suppress leaks in libcrypto
220# Below is caused by openssl 3.0.8 leaks
221leak:libcrypto.so
222EOL
223
224# Suppress leaks in libfuse3
225echo "leak:libfuse3.so" >> "$asan_suppression_file"
226
227export LSAN_OPTIONS=suppressions="$asan_suppression_file"
228
229export DEFAULT_RPC_ADDR="/var/tmp/spdk.sock"
230
231if [ -z "${DEPENDENCY_DIR:-}" ]; then
232	export DEPENDENCY_DIR=/var/spdk/dependencies
233else
234	export DEPENDENCY_DIR
235fi
236
237# Export location of where all the SPDK binaries are
238export SPDK_BIN_DIR="$rootdir/build/bin"
239export SPDK_EXAMPLE_DIR="$rootdir/build/examples"
240
241# for vhost, vfio-user tests
242export QEMU_BIN=${QEMU_BIN:-}
243export VFIO_QEMU_BIN=${VFIO_QEMU_BIN:-}
244
245export AR_TOOL=$rootdir/scripts/ar-xnvme-fixer
246
247# For testing nvmes which are attached to some sort of a fanout switch in the CI pool
248export UNBIND_ENTIRE_IOMMU_GROUP=${UNBIND_ENTIRE_IOMMU_GROUP:-no}
249
250# pass our valgrind desire on to unittest.sh
251if [ $SPDK_RUN_VALGRIND -eq 0 ]; then
252	export valgrind=''
253else
254	# unset all DEBUGINFOD_* vars that may affect our valgrind instance
255	unset -v "${!DEBUGINFOD_@}"
256fi
257
258if [ "$(uname -s)" = "Linux" ]; then
259	HUGEMEM=${HUGEMEM:-4096}
260	export CLEAR_HUGE=yes
261	if [[ $SPDK_TEST_CRYPTO -eq 1 || $SPDK_TEST_VBDEV_COMPRESS -eq 1 ]]; then
262		# Make sure that memory is distributed across all NUMA nodes - by default, all goes to
263		# node0, but if QAT devices are attached to a different node, all of their VFs will end
264		# up under that node too and memory needs to be available there for the tests.
265		export HUGE_EVEN_ALLOC=yes
266	fi
267
268	MAKE="make"
269	MAKEFLAGS=${MAKEFLAGS:--j$(nproc)}
270elif [ "$(uname -s)" = "FreeBSD" ]; then
271	MAKE="gmake"
272	MAKEFLAGS=${MAKEFLAGS:--j$(sysctl -a | grep -E -i 'hw.ncpu' | awk '{print $2}')}
273	# FreeBSD runs a much more limited set of tests, so keep the default 2GB.
274	HUGEMEM=${HUGEMEM:-2048}
275elif [ "$(uname -s)" = "Windows" ]; then
276	MAKE="make"
277	MAKEFLAGS=${MAKEFLAGS:--j$(nproc)}
278	# Keep the default 2GB for Windows.
279	HUGEMEM=${HUGEMEM:-2048}
280else
281	echo "Unknown OS \"$(uname -s)\""
282	exit 1
283fi
284
285export HUGEMEM=$HUGEMEM
286
287NO_HUGE=()
288TEST_MODE=
289for i in "$@"; do
290	case "$i" in
291		--iso)
292			TEST_MODE=iso
293			;;
294		--transport=*)
295			TEST_TRANSPORT="${i#*=}"
296			;;
297		--sock=*)
298			TEST_SOCK="${i#*=}"
299			;;
300		--no-hugepages)
301			NO_HUGE=(--no-huge -s 1024)
302			;;
303	esac
304done
305
306# start rpc.py coprocess if it's not started yet
307if [[ -z ${RPC_PIPE_PID:-} ]] || ! kill -0 "$RPC_PIPE_PID" &> /dev/null; then
308	# Include list to all known plugins we use in the tests
309	PYTHONPATH+=":$rootdir/test/rpc_plugins"
310	coproc RPC_PIPE { PYTHONPATH="$PYTHONPATH" "$rootdir/scripts/rpc.py" --server; }
311	exec {RPC_PIPE_OUTPUT}<&${RPC_PIPE[0]} {RPC_PIPE_INPUT}>&${RPC_PIPE[1]}
312	# all descriptors will automatically close together with this bash
313	# process, this will make rpc.py stop reading and exit gracefully
314fi
315
316function set_test_storage() {
317	[[ -v testdir ]] || return 0
318
319	local requested_size=$1 # bytes
320	local mount target_dir
321
322	local -A mounts fss sizes avails uses
323	local source fs size avail mount use
324
325	local storage_fallback storage_candidates
326
327	storage_fallback=$(mktemp -udt spdk.XXXXXX)
328	storage_candidates=(
329		"$testdir"
330		"$storage_fallback/tests/${testdir##*/}"
331		"$storage_fallback"
332	)
333
334	if [[ -n ${ADD_TEST_STORAGE:-} ]]; then
335		# List of dirs|mounts separated by whitespaces
336		storage_candidates+=($ADD_TEST_STORAGE)
337	fi
338
339	if [[ -n ${DEDICATED_TEST_STORAGE:-} ]]; then
340		# Single, dedicated dir|mount
341		storage_candidates=("$DEDICATED_TEST_STORAGE")
342	fi
343
344	mkdir -p "${storage_candidates[@]}"
345
346	# add some headroom - 64M
347	requested_size=$((requested_size + (64 << 20)))
348
349	while read -r source fs size use avail _ mount; do
350		mounts["$mount"]=$source fss["$mount"]=$fs
351		avails["$mount"]=$((avail * 1024)) sizes["$mount"]=$((size * 1024))
352		uses["$mount"]=$((use * 1024))
353	done < <(df -T | grep -v Filesystem)
354
355	printf '* Looking for test storage...\n' >&2
356
357	local target_space new_size
358	for target_dir in "${storage_candidates[@]}"; do
359		# FreeBSD's df is lacking the --output arg
360		# mount=$(df --output=target "$target_dir" | grep -v "Mounted on")
361		mount=$(df "$target_dir" | awk '$1 !~ /Filesystem/{print $6}')
362
363		target_space=${avails["$mount"]}
364		if ((target_space == 0 || target_space < requested_size)); then
365			continue
366		fi
367		if ((target_space >= requested_size)); then
368			# For in-memory fs, and / make sure our requested size won't fill most of the space.
369			if [[ ${fss["$mount"]} == tmpfs ]] || [[ ${fss["$mount"]} == ramfs ]] || [[ $mount == / ]]; then
370				new_size=$((uses["$mount"] + requested_size))
371				if ((new_size * 100 / sizes["$mount"] > 95)); then
372					continue
373				fi
374			fi
375		fi
376		export SPDK_TEST_STORAGE=$target_dir
377		printf '* Found test storage at %s\n' "$SPDK_TEST_STORAGE" >&2
378		return 0
379	done
380	printf '* Test storage is not available\n'
381	return 1
382}
383
384function get_config_params() {
385	xtrace_disable
386	config_params='--enable-debug --enable-werror'
387
388	# for options with dependencies but no test flag, set them here
389	if [ -f /usr/include/infiniband/verbs.h ]; then
390		config_params+=' --with-rdma'
391	fi
392
393	if [ $SPDK_TEST_USDT -eq 1 ]; then
394		config_params+=" --with-usdt"
395	fi
396
397	if [ $(uname -s) == "FreeBSD" ]; then
398		intel="hw.model: Intel"
399		cpu_vendor=$(sysctl -a | grep hw.model | cut -c 1-15)
400	else
401		intel="GenuineIntel"
402		cpu_vendor=$(grep -i 'vendor' /proc/cpuinfo --max-count=1)
403	fi
404	if [[ "$cpu_vendor" != *"$intel"* ]]; then
405		config_params+=" --without-idxd"
406	else
407		config_params+=" --with-idxd"
408	fi
409
410	if [[ -d $CONFIG_FIO_SOURCE_DIR ]]; then
411		config_params+=" --with-fio=$CONFIG_FIO_SOURCE_DIR"
412	fi
413
414	if [ -d ${DEPENDENCY_DIR}/vtune_codes ]; then
415		config_params+=' --with-vtune='${DEPENDENCY_DIR}'/vtune_codes'
416	fi
417
418	if [ -d /usr/include/iscsi ]; then
419		[[ $(< /usr/include/iscsi/iscsi.h) =~ "define LIBISCSI_API_VERSION ("([0-9]+)")" ]] \
420			&& libiscsi_version=${BASH_REMATCH[1]}
421		if ((libiscsi_version >= 20150621)); then
422			config_params+=' --with-iscsi-initiator'
423		fi
424	fi
425
426	if [[ $SPDK_TEST_UNITTEST -eq 0 && \
427		$SPDK_TEST_SCANBUILD -eq 0 && -z \
428		${SPDK_TEST_AUTOBUILD:-} ]]; then
429		config_params+=' --disable-unit-tests'
430	fi
431
432	if [ -f /usr/include/libpmem.h ] && [ $SPDK_TEST_VBDEV_COMPRESS -eq 1 ]; then
433		if ge "$(nasm --version | awk '{print $3}')" 2.14 && [[ $SPDK_TEST_ISAL -eq 1 ]]; then
434			config_params+=' --with-vbdev-compress --with-dpdk-compressdev'
435		fi
436	fi
437
438	if [ -d /usr/include/rbd ] && [ -d /usr/include/rados ] && [ $SPDK_TEST_RBD -eq 1 ]; then
439		config_params+=' --with-rbd'
440	fi
441
442	# for options with no required dependencies, just test flags, set them here
443	if [ $SPDK_TEST_CRYPTO -eq 1 ]; then
444		config_params+=' --with-crypto'
445	fi
446
447	if [ $SPDK_TEST_OCF -eq 1 ]; then
448		config_params+=" --with-ocf"
449	fi
450
451	if [ $SPDK_RUN_UBSAN -eq 1 ]; then
452		config_params+=' --enable-ubsan'
453	fi
454
455	if [ $SPDK_RUN_ASAN -eq 1 ]; then
456		config_params+=' --enable-asan'
457	fi
458
459	if [ "$(uname -s)" = "Linux" ]; then
460		config_params+=' --enable-coverage'
461	fi
462
463	if [ $SPDK_TEST_BLOBFS -eq 1 ]; then
464		if [[ -d /usr/include/fuse3 ]] || [[ -d /usr/local/include/fuse3 ]]; then
465			config_params+=' --with-fuse'
466		fi
467	fi
468
469	if [[ -f /usr/include/liburing/io_uring.h && -f /usr/include/linux/ublk_cmd.h ]]; then
470		config_params+=' --with-ublk'
471	fi
472
473	if [ $SPDK_TEST_RAID5 -eq 1 ]; then
474		config_params+=' --with-raid5f'
475	fi
476
477	if [ $SPDK_TEST_VFIOUSER -eq 1 ] || [ $SPDK_TEST_VFIOUSER_QEMU -eq 1 ] || [ $SPDK_TEST_SMA -eq 1 ]; then
478		config_params+=' --with-vfio-user'
479	fi
480
481	# Check whether liburing library header exists
482	if [ -f /usr/include/liburing/io_uring.h ] && [ $SPDK_TEST_URING -eq 1 ]; then
483		config_params+=' --with-uring'
484	fi
485
486	if [ -n "${SPDK_RUN_EXTERNAL_DPDK:-}" ]; then
487		config_params+=" --with-dpdk=$SPDK_RUN_EXTERNAL_DPDK"
488	fi
489
490	if [[ $SPDK_TEST_SMA -eq 1 ]]; then
491		config_params+=' --with-sma'
492		config_params+=' --with-crypto'
493	fi
494
495	if [ -f /usr/include/daos.h ] && [ $SPDK_TEST_DAOS -eq 1 ]; then
496		config_params+=' --with-daos'
497	fi
498
499	# Make the xnvme module available for the tests
500	if [[ $SPDK_TEST_XNVME -eq 1 ]]; then
501		config_params+=' --with-xnvme'
502	fi
503
504	if [[ $SPDK_TEST_FUZZER -eq 1 ]]; then
505		config_params+=" $(get_fuzzer_target_config)"
506	fi
507
508	if [[ $SPDK_TEST_NVMF_MDNS -eq 1 ]]; then
509		config_params+=' --with-avahi'
510	fi
511
512	if [[ $SPDK_JSONRPC_GO_CLIENT -eq 1 ]]; then
513		config_params+=' --with-golang'
514	fi
515
516	echo "$config_params"
517	xtrace_restore
518}
519
520function get_fuzzer_target_config() {
521	local -A fuzzer_targets_to_config=()
522	local config target
523
524	fuzzer_targets_to_config["vfio"]="--with-vfio-user"
525	for target in $(get_fuzzer_targets); do
526		[[ -n ${fuzzer_targets_to_config["$target"]:-} ]] || continue
527		config+=("${fuzzer_targets_to_config["$target"]}")
528	done
529
530	if ((${#config[@]} > 0)); then
531		echo "${config[*]}"
532	fi
533}
534
535function get_fuzzer_targets() {
536	local fuzzers=()
537
538	if [[ -n ${SPDK_TEST_FUZZER_TARGET:-} ]]; then
539		IFS="," read -ra fuzzers <<< "$SPDK_TEST_FUZZER_TARGET"
540	else
541		fuzzers=("$rootdir/test/fuzz/llvm/"*)
542		fuzzers=("${fuzzers[@]##*/}")
543	fi
544
545	echo "${fuzzers[*]}"
546}
547
548function rpc_cmd() {
549	xtrace_disable
550	local rsp rc=1
551	local stdin cmd cmds_number=0 status_number=0 status
552
553	if (($#)); then
554		cmds_number=1
555		echo "$@" >&$RPC_PIPE_INPUT
556	elif [[ ! -t 0 ]]; then
557		mapfile -t stdin <&0
558		cmds_number=${#stdin[@]}
559		printf '%s\n' "${stdin[@]}" >&$RPC_PIPE_INPUT
560	else
561		return 0
562	fi
563
564	while read -t "${RPC_PIPE_TIMEOUT:-15}" -ru $RPC_PIPE_OUTPUT rsp; do
565		if [[ $rsp == "**STATUS="* ]]; then
566			status[${rsp#*=}]=$rsp
567			if ((++status_number == cmds_number)); then
568				break
569			fi
570			continue
571		fi
572		echo "$rsp"
573	done
574
575	rc=${!status[*]}
576	xtrace_restore
577	[[ $rc == 0 ]]
578}
579
580function rpc_cmd_simple_data_json() {
581
582	local elems="$1[@]" elem
583	local -gA jq_out=()
584	local jq val
585
586	local lvs=(
587		"uuid"
588		"name"
589		"base_bdev"
590		"total_data_clusters"
591		"free_clusters"
592		"block_size"
593		"cluster_size"
594	)
595
596	local bdev=(
597		"name"
598		"aliases[0]"
599		"block_size"
600		"num_blocks"
601		"uuid"
602		"product_name"
603		"supported_io_types.read"
604		"supported_io_types.write"
605		"driver_specific.lvol.clone"
606		"driver_specific.lvol.base_snapshot"
607		"driver_specific.lvol.esnap_clone"
608		"driver_specific.lvol.external_snapshot_name"
609	)
610
611	[[ -v $elems ]] || return 1
612
613	for elem in "${!elems}"; do
614		jq="${jq:+$jq,\"\\n\",}\"$elem\",\" \",.[0].$elem"
615	done
616	jq+=',"\n"'
617
618	shift
619	while read -r elem val; do
620		jq_out["$elem"]=$val
621	done < <(rpc_cmd "$@" | jq -jr "$jq")
622	((${#jq_out[@]} > 0)) || return 1
623}
624
625function valid_exec_arg() {
626	local arg=$1
627	# First argument must be the executable so do some basic sanity checks first. For bash, this
628	# covers two basic cases where es == 126 || es == 127 so catch them early on and fail hard
629	# if needed.
630	case "$(type -t "$arg")" in
631		builtin | function) ;;
632		file) arg=$(type -P "$arg") && [[ -x $arg ]] ;;
633		*) return 1 ;;
634	esac
635}
636
637function NOT() {
638	local es=0
639
640	valid_exec_arg "$@" || return 1
641	"$@" || es=$?
642
643	# Logic looks like so:
644	#  - return false if command exit successfully
645	#  - return false if command exit after receiving a core signal (FIXME: or any signal?)
646	#  - return true if command exit with an error
647
648	# This naively assumes that the process doesn't exit with > 128 on its own.
649	if ((es > 128)); then
650		es=$((es & ~128))
651		case "$es" in
652			3) ;&       # SIGQUIT
653			4) ;&       # SIGILL
654			6) ;&       # SIGABRT
655			8) ;&       # SIGFPE
656			9) ;&       # SIGKILL
657			11) es=0 ;; # SIGSEGV
658			*) es=1 ;;
659		esac
660	elif [[ -n ${EXIT_STATUS:-} ]] && ((es != EXIT_STATUS)); then
661		es=0
662	fi
663
664	# invert error code of any command and also trigger ERR on 0 (unlike bash ! prefix)
665	((!es == 0))
666}
667
668function timing() {
669	direction="$1"
670	testname="$2"
671
672	now=$(date +%s)
673
674	if [ "$direction" = "enter" ]; then
675		export timing_stack="${timing_stack:-};${now}"
676		export test_stack="${test_stack:-};${testname}"
677	else
678		touch "$output_dir/timing.txt"
679		child_time=$(grep "^${test_stack:1};" $output_dir/timing.txt | awk '{s+=$2} END {print s}')
680
681		start_time=$(echo "$timing_stack" | sed -e 's@^.*;@@')
682		timing_stack=$(echo "$timing_stack" | sed -e 's@;[^;]*$@@')
683
684		elapsed=$((now - start_time - child_time))
685		echo "${test_stack:1} $elapsed" >> $output_dir/timing.txt
686
687		test_stack=$(echo "$test_stack" | sed -e 's@;[^;]*$@@')
688	fi
689}
690
691function timing_cmd() (
692	# The use-case here is this: ts=$(timing_cmd echo bar). Since stdout is always redirected
693	# to a pipe handling the $(), lookup the stdin's device and determine if it's sane to send
694	# cmd's output to it. If not, just null it.
695
696	[[ -t 0 ]] && exec {cmd_out}>&0 || exec {cmd_out}> /dev/null
697
698	local time=0 TIMEFORMAT=%2R # seconds
699
700	# We redirect cmd's std{out,err} to a separate fd dup'ed to stdin's device (or /dev/null) to
701	# catch only output from the time builtin - output from the actual cmd would be still visible,
702	# but $() will return just the time's data, hence making it possible to just do:
703	#  time_of_super_verbose_cmd=$(timing_cmd super_verbose_cmd)
704	time=$({ time "$@" >&"$cmd_out" 2>&1; } 2>&1)
705
706	echo "$time"
707)
708
709function timing_enter() {
710	xtrace_disable
711	timing "enter" "$1"
712	xtrace_restore
713}
714
715function timing_exit() {
716	xtrace_disable
717	timing "exit" "$1"
718	xtrace_restore
719}
720
721function timing_finish() {
722	flamegraph='/usr/local/FlameGraph/flamegraph.pl'
723	if [ -x "$flamegraph" ]; then
724		"$flamegraph" \
725			--title 'Build Timing' \
726			--nametype 'Step:' \
727			--countname seconds \
728			$output_dir/timing.txt \
729			> $output_dir/timing.svg
730	fi
731}
732
733function create_test_list() {
734	xtrace_disable
735	# First search all scripts in main SPDK directory.
736	completion=$(grep -shI -d skip --include="*.sh" -e "run_test " $rootdir/*)
737	# Follow up with search in test directory recursively.
738	completion+=$'\n'$(grep -rshI --include="*.sh" --exclude="*autotest_common.sh" -e "run_test " $rootdir/test)
739	printf "%s" "$completion" | grep -v "#" \
740		| sed 's/^.*run_test/run_test/' | awk '{print $2}' \
741		| sed 's/\"//g' | sort > $output_dir/all_tests.txt || true
742	xtrace_restore
743}
744
745function gdb_attach() {
746	gdb -q --batch \
747		-ex 'handle SIGHUP nostop pass' \
748		-ex 'handle SIGQUIT nostop pass' \
749		-ex 'handle SIGPIPE nostop pass' \
750		-ex 'handle SIGALRM nostop pass' \
751		-ex 'handle SIGTERM nostop pass' \
752		-ex 'handle SIGUSR1 nostop pass' \
753		-ex 'handle SIGUSR2 nostop pass' \
754		-ex 'handle SIGCHLD nostop pass' \
755		-ex 'set print thread-events off' \
756		-ex 'cont' \
757		-ex 'thread apply all bt' \
758		-ex 'quit' \
759		--tty=/dev/stdout \
760		-p $1
761}
762
763function process_core() {
764	# Note that this always was racy as we can't really sync with the kernel
765	# to see if there's any core queued up for writing. We could check if
766	# collector is running and wait for it explicitly, but it doesn't seem
767	# to be worth the effort. So assume that if we are being called via
768	# trap, as in, when some error has occurred, wait up to 10s for any
769	# potential cores. If we are called just for cleanup at the very end,
770	# don't wait since all the tests ended successfully, hence having any
771	# critical cores lying around is unlikely.
772	((autotest_es != 0)) && sleep 10
773
774	local coredumps core
775
776	coredumps=("$output_dir/coredumps/"*.bt.txt)
777
778	((${#coredumps[@]} > 0)) || return 0
779	chmod -R a+r "$output_dir/coredumps"
780
781	for core in "${coredumps[@]}"; do
782		cat <<- BT
783			##### CORE BT ${core##*/} #####
784
785			$(<"$core")
786
787			--
788		BT
789	done
790	return 1
791}
792
793function process_shm() {
794	type=$1
795	id=$2
796	if [ "$type" = "--pid" ]; then
797		id="pid${id}"
798	fi
799
800	shm_files=$(find /dev/shm -name "*.${id}" -printf "%f\n")
801
802	if [[ -z ${shm_files:-} ]]; then
803		echo "SHM File for specified PID or shared memory id: ${id} not found!"
804		return 1
805	fi
806	for n in $shm_files; do
807		tar -C /dev/shm/ -cvzf $output_dir/${n}_shm.tar.gz ${n}
808	done
809	return 0
810}
811
812# Parameters:
813# $1 - process pid
814# $2 - rpc address (optional)
815# $3 - max retries (optional)
816function waitforlisten() {
817	if [ -z "${1:-}" ]; then
818		exit 1
819	fi
820
821	local rpc_addr="${2:-$DEFAULT_RPC_ADDR}"
822	local max_retries=${3:-100}
823
824	echo "Waiting for process to start up and listen on UNIX domain socket $rpc_addr..."
825	# turn off trace for this loop
826	xtrace_disable
827	local ret=0
828	local i
829	for ((i = max_retries; i != 0; i--)); do
830		# if the process is no longer running, then exit the script
831		#  since it means the application crashed
832		if ! kill -s 0 $1; then
833			echo "ERROR: process (pid: $1) is no longer running"
834			ret=1
835			break
836		fi
837
838		if $rootdir/scripts/rpc.py -t 1 -s "$rpc_addr" rpc_get_methods &> /dev/null; then
839			break
840		fi
841
842		sleep 0.5
843	done
844
845	xtrace_restore
846	if ((i == 0)); then
847		echo "ERROR: timeout while waiting for process (pid: $1) to start listening on '$rpc_addr'"
848		ret=1
849	fi
850	return $ret
851}
852
853function waitfornbd() {
854	local nbd_name=$1
855	local i
856
857	for ((i = 1; i <= 20; i++)); do
858		if grep -q -w $nbd_name /proc/partitions; then
859			break
860		else
861			sleep 0.1
862		fi
863	done
864
865	# The nbd device is now recognized as a block device, but there can be
866	#  a small delay before we can start I/O to that block device.  So loop
867	#  here trying to read the first block of the nbd block device to a temp
868	#  file.  Note that dd returns success when reading an empty file, so we
869	#  need to check the size of the output file instead.
870	for ((i = 1; i <= 20; i++)); do
871		dd if=/dev/$nbd_name of="$SPDK_TEST_STORAGE/nbdtest" bs=4096 count=1 iflag=direct
872		size=$(stat -c %s "$SPDK_TEST_STORAGE/nbdtest")
873		rm -f "$SPDK_TEST_STORAGE/nbdtest"
874		if [ "$size" != "0" ]; then
875			return 0
876		else
877			sleep 0.1
878		fi
879	done
880
881	return 1
882}
883
884function waitforbdev() {
885	local bdev_name=$1
886	local bdev_timeout=$2
887	local i
888	[[ -z ${bdev_timeout:-} ]] && bdev_timeout=2000 # ms
889
890	$rpc_py bdev_wait_for_examine
891
892	if $rpc_py bdev_get_bdevs -b $bdev_name -t $bdev_timeout; then
893		return 0
894	fi
895
896	return 1
897}
898
899function waitforcondition() {
900	local cond=$1
901	local max=${2:-10}
902	while ((max--)); do
903		if eval $cond; then
904			return 0
905		fi
906		sleep 1
907	done
908	return 1
909}
910
911function make_filesystem() {
912	local fstype=$1
913	local dev_name=$2
914	local i=0
915	local force
916
917	if [ $fstype = ext4 ]; then
918		force=-F
919	else
920		force=-f
921	fi
922
923	while ! mkfs.${fstype} $force ${dev_name}; do
924		if [ $i -ge 15 ]; then
925			return 1
926		fi
927		i=$((i + 1))
928		sleep 1
929	done
930
931	return 0
932}
933
934function killprocess() {
935	# $1 = process pid
936	if [ -z "${1:-}" ]; then
937		return 1
938	fi
939
940	if kill -0 $1; then
941		if [ $(uname) = Linux ]; then
942			process_name=$(ps --no-headers -o comm= $1)
943		else
944			process_name=$(ps -c -o command $1 | tail -1)
945		fi
946		if [ "$process_name" = "sudo" ]; then
947			# kill the child process, which is the actual app
948			# (assume $1 has just one child)
949			local child
950			child="$(pgrep -P $1)"
951			echo "killing process with pid $child"
952			kill $child
953		else
954			echo "killing process with pid $1"
955			kill $1
956		fi
957
958		# wait for the process regardless if its the dummy sudo one
959		# or the actual app - it should terminate anyway
960		wait $1
961	else
962		# the process is not there anymore
963		echo "Process with pid $1 is not found"
964	fi
965}
966
967function iscsicleanup() {
968	echo "Cleaning up iSCSI connection"
969	iscsiadm -m node --logout || true
970	iscsiadm -m node -o delete || true
971	rm -rf /var/lib/iscsi/nodes/*
972}
973
974function stop_iscsi_service() {
975	if cat /etc/*-release | grep Ubuntu; then
976		service open-iscsi stop
977	else
978		service iscsid stop
979	fi
980}
981
982function start_iscsi_service() {
983	if cat /etc/*-release | grep Ubuntu; then
984		service open-iscsi start
985	else
986		service iscsid start
987	fi
988}
989
990function rbd_setup() {
991	# $1 = monitor ip address
992	# $2 = name of the namespace
993	if [ -z "${1:-}" ]; then
994		echo "No monitor IP address provided for ceph"
995		exit 1
996	fi
997	if [ -n "${2:-}" ]; then
998		if ip netns list | grep "$2"; then
999			NS_CMD="ip netns exec $2"
1000		else
1001			echo "No namespace $2 exists"
1002			exit 1
1003		fi
1004	fi
1005
1006	if hash ceph; then
1007		export PG_NUM=128
1008		export RBD_POOL=rbd
1009		export RBD_NAME=foo
1010		$NS_CMD $rootdir/scripts/ceph/stop.sh || true
1011		$NS_CMD $rootdir/scripts/ceph/start.sh $1
1012
1013		$NS_CMD ceph osd pool create $RBD_POOL $PG_NUM || true
1014		$NS_CMD rbd create $RBD_NAME --size 1000
1015	fi
1016}
1017
1018function rbd_cleanup() {
1019	if hash ceph; then
1020		$rootdir/scripts/ceph/stop.sh || true
1021		rm -f /var/tmp/ceph_raw.img
1022	fi
1023}
1024
1025function daos_setup() {
1026	# $1 = pool name
1027	# $2 = cont name
1028	if [ -z "${1:-}" ]; then
1029		echo "No pool name provided"
1030		exit 1
1031	fi
1032	if [ -z "${2:-}" ]; then
1033		echo "No cont name provided"
1034		exit 1
1035	fi
1036
1037	dmg pool create --size=10G $1 || true
1038	daos container create --type=POSIX --label=$2 $1 || true
1039}
1040
1041function daos_cleanup() {
1042	local pool=${1:-testpool}
1043	local cont=${2:-testcont}
1044
1045	daos container destroy -f $pool $cont || true
1046	sudo dmg pool destroy -f $pool || true
1047}
1048
1049function _start_stub() {
1050	# Disable ASLR for multi-process testing.  SPDK does support using DPDK multi-process,
1051	# but ASLR can still be unreliable in some cases.
1052	# We will re-enable it again after multi-process testing is complete in kill_stub().
1053	# Save current setting so it can be restored upon calling kill_stub().
1054	_randomize_va_space=$(< /proc/sys/kernel/randomize_va_space)
1055	echo 0 > /proc/sys/kernel/randomize_va_space
1056	$rootdir/test/app/stub/stub $1 &
1057	stubpid=$!
1058	echo Waiting for stub to ready for secondary processes...
1059	while ! [ -e /var/run/spdk_stub0 ]; do
1060		# If stub dies while we wait, bail
1061		[[ -e /proc/$stubpid ]] || return 1
1062		sleep 1s
1063	done
1064	echo done.
1065}
1066
1067function start_stub() {
1068	if ! _start_stub "$@"; then
1069		echo "stub failed" >&2
1070		return 1
1071	fi
1072}
1073
1074function kill_stub() {
1075	if [[ -e /proc/$stubpid ]]; then
1076		kill $1 $stubpid
1077		wait $stubpid
1078	fi 2> /dev/null || :
1079	rm -f /var/run/spdk_stub0
1080	# Re-enable ASLR now that we are done with multi-process testing
1081	# Note: "1" enables ASLR w/o randomizing data segments, "2" adds data segment
1082	#  randomizing and is the default on all recent Linux kernels
1083	echo "${_randomize_va_space:-2}" > /proc/sys/kernel/randomize_va_space
1084}
1085
1086function run_test() {
1087	if [ $# -le 1 ]; then
1088		echo "Not enough parameters"
1089		echo "usage: run_test test_name test_script [script_params]"
1090		exit 1
1091	fi
1092
1093	xtrace_disable
1094	local test_name="$1" pid
1095	shift
1096
1097	if [ -n "${test_domain:-}" ]; then
1098		export test_domain="${test_domain}.${test_name}"
1099	else
1100		export test_domain="$test_name"
1101	fi
1102
1103	# Signal our daemons to update the test tag
1104	update_tag_monitor_resources "$test_domain"
1105
1106	timing_enter $test_name
1107	echo "************************************"
1108	echo "START TEST $test_name"
1109	echo "************************************"
1110	xtrace_restore
1111	time "$@"
1112	xtrace_disable
1113	echo "************************************"
1114	echo "END TEST $test_name"
1115	echo "************************************"
1116	timing_exit $test_name
1117
1118	export test_domain=${test_domain%"$test_name"}
1119	if [ -n "$test_domain" ]; then
1120		export test_domain=${test_domain%?}
1121	fi
1122
1123	if [ -z "${test_domain:-}" ]; then
1124		echo "top_level $test_name" >> $output_dir/test_completions.txt
1125	else
1126		echo "$test_domain $test_name" >> $output_dir/test_completions.txt
1127	fi
1128	xtrace_restore
1129}
1130
1131function skip_run_test_with_warning() {
1132	echo "WARNING: $1"
1133	echo "Test run may fail if run with autorun.sh"
1134	echo "Please check your $rootdir/test/common/skipped_tests.txt"
1135}
1136
1137function print_backtrace() {
1138	# if errexit is not enabled, don't print a backtrace
1139	[[ "$-" =~ e ]] || return 0
1140
1141	local args=("${BASH_ARGV[@]}")
1142
1143	xtrace_disable
1144	# Reset IFS in case we were called from an environment where it was modified
1145	IFS=" "$'\t'$'\n'
1146	echo "========== Backtrace start: =========="
1147	echo ""
1148	for ((i = 1; i < ${#FUNCNAME[@]}; i++)); do
1149		local func="${FUNCNAME[$i]}"
1150		local line_nr="${BASH_LINENO[$((i - 1))]}"
1151		local src="${BASH_SOURCE[$i]}"
1152		local bt="" cmdline=()
1153
1154		if [[ -f $src ]]; then
1155			bt=$(nl -w 4 -ba -nln $src | grep -B 5 -A 5 "^${line_nr}[^0-9]" \
1156				| sed "s/^/   /g" | sed "s/^   $line_nr /=> $line_nr /g")
1157		fi
1158
1159		# If extdebug set the BASH_ARGC[i], try to fetch all the args
1160		if ((BASH_ARGC[i] > 0)); then
1161			# Use argc as index to reverse the stack
1162			local argc=${BASH_ARGC[i]} arg
1163			for arg in "${args[@]::BASH_ARGC[i]}"; do
1164				cmdline[argc--]="[\"$arg\"]"
1165			done
1166			args=("${args[@]:BASH_ARGC[i]}")
1167		fi
1168
1169		echo "in $src:$line_nr -> $func($(
1170			IFS=","
1171			printf '%s\n' "${cmdline[*]:-[]}"
1172		))"
1173		echo "     ..."
1174		echo "${bt:-backtrace unavailable}"
1175		echo "     ..."
1176	done
1177	echo ""
1178	echo "========== Backtrace end =========="
1179	xtrace_restore
1180	return 0
1181}
1182
1183function waitforserial() {
1184	local i=0
1185	local nvme_device_counter=1 nvme_devices=0
1186	if [[ -n "${2:-}" ]]; then
1187		nvme_device_counter=$2
1188	fi
1189
1190	# Wait initially for min 2s to make sure all devices are ready for use.
1191	sleep 2
1192	while ((i++ <= 15)); do
1193		nvme_devices=$(lsblk -l -o NAME,SERIAL | grep -c "$1")
1194		((nvme_devices == nvme_device_counter)) && return 0
1195		if ((nvme_devices > nvme_device_counter)); then
1196			echo "$nvme_device_counter device(s) expected, found $nvme_devices" >&2
1197		fi
1198		echo "Waiting for devices"
1199		sleep 1
1200	done
1201	return 1
1202}
1203
1204function waitforserial_disconnect() {
1205	local i=0
1206	while lsblk -o NAME,SERIAL | grep -q -w $1; do
1207		[ $i -lt 15 ] || break
1208		i=$((i + 1))
1209		echo "Waiting for disconnect devices"
1210		sleep 1
1211	done
1212
1213	if lsblk -l -o NAME,SERIAL | grep -q -w $1; then
1214		return 1
1215	fi
1216
1217	return 0
1218}
1219
1220function waitforblk() {
1221	local i=0
1222	while ! lsblk -l -o NAME | grep -q -w $1; do
1223		[ $i -lt 15 ] || break
1224		i=$((i + 1))
1225		sleep 1
1226	done
1227
1228	if ! lsblk -l -o NAME | grep -q -w $1; then
1229		return 1
1230	fi
1231
1232	return 0
1233}
1234
1235function waitforblk_disconnect() {
1236	local i=0
1237	while lsblk -l -o NAME | grep -q -w $1; do
1238		[ $i -lt 15 ] || break
1239		i=$((i + 1))
1240		sleep 1
1241	done
1242
1243	if lsblk -l -o NAME | grep -q -w $1; then
1244		return 1
1245	fi
1246
1247	return 0
1248}
1249
1250function waitforfile() {
1251	local i=0
1252	while [ ! -e $1 ]; do
1253		[ $i -lt 200 ] || break
1254		i=$((i + 1))
1255		sleep 0.1
1256	done
1257
1258	if [ ! -e $1 ]; then
1259		return 1
1260	fi
1261
1262	return 0
1263}
1264
1265function fio_config_gen() {
1266	local config_file=$1
1267	local workload=$2
1268	local bdev_type=$3
1269	local env_context=$4
1270	local fio_dir=$CONFIG_FIO_SOURCE_DIR
1271
1272	if [ -e "$config_file" ]; then
1273		echo "Configuration File Already Exists!: $config_file"
1274		return 1
1275	fi
1276
1277	if [ -z "${workload:-}" ]; then
1278		workload=randrw
1279	fi
1280
1281	if [ -n "$env_context" ]; then
1282		env_context="env_context=$env_context"
1283	fi
1284
1285	touch $1
1286
1287	cat > $1 << EOL
1288[global]
1289thread=1
1290$env_context
1291group_reporting=1
1292direct=1
1293norandommap=1
1294percentile_list=50:99:99.9:99.99:99.999
1295time_based=1
1296ramp_time=0
1297EOL
1298
1299	if [ "$workload" == "verify" ]; then
1300		cat <<- EOL >> $config_file
1301			verify=sha1
1302			verify_backlog=1024
1303			rw=randwrite
1304		EOL
1305
1306		# To avoid potential data race issue due to the AIO device
1307		# flush mechanism, add the flag to serialize the writes.
1308		# This is to fix the intermittent IO failure issue of #935
1309		if [ "$bdev_type" == "AIO" ]; then
1310			if [[ $($fio_dir/fio --version) == *"fio-3"* ]]; then
1311				echo "serialize_overlap=1" >> $config_file
1312			fi
1313		fi
1314	elif [ "$workload" == "trim" ]; then
1315		echo "rw=trimwrite" >> $config_file
1316	else
1317		echo "rw=$workload" >> $config_file
1318	fi
1319}
1320
1321function fio_plugin() {
1322	# Setup fio binary cmd line
1323	local fio_dir=$CONFIG_FIO_SOURCE_DIR
1324	# gcc and clang uses different sanitizer libraries
1325	local sanitizers=(libasan libclang_rt.asan)
1326	local plugin=$1
1327	shift
1328
1329	local asan_lib=
1330	for sanitizer in "${sanitizers[@]}"; do
1331		asan_lib=$(ldd $plugin | grep $sanitizer | awk '{print $3}')
1332		if [[ -n "${asan_lib:-}" ]]; then
1333			break
1334		fi
1335	done
1336
1337	# Preload the sanitizer library to fio if fio_plugin was compiled with it
1338	LD_PRELOAD="$asan_lib $plugin" "$fio_dir"/fio "$@"
1339}
1340
1341function fio_bdev() {
1342	fio_plugin "$rootdir/build/fio/spdk_bdev" "$@"
1343}
1344
1345function fio_nvme() {
1346	fio_plugin "$rootdir/build/fio/spdk_nvme" "$@"
1347}
1348
1349function get_lvs_free_mb() {
1350	local lvs_uuid=$1
1351	local lvs_info
1352	local fc
1353	local cs
1354	lvs_info=$($rpc_py bdev_lvol_get_lvstores)
1355	fc=$(jq ".[] | select(.uuid==\"$lvs_uuid\") .free_clusters" <<< "$lvs_info")
1356	cs=$(jq ".[] | select(.uuid==\"$lvs_uuid\") .cluster_size" <<< "$lvs_info")
1357
1358	# Change to MB's
1359	free_mb=$((fc * cs / 1024 / 1024))
1360	echo "$free_mb"
1361}
1362
1363function get_bdev_size() {
1364	local bdev_name=$1
1365	local bdev_info
1366	local bs
1367	local nb
1368	bdev_info=$($rpc_py bdev_get_bdevs -b $bdev_name)
1369	bs=$(jq ".[] .block_size" <<< "$bdev_info")
1370	nb=$(jq ".[] .num_blocks" <<< "$bdev_info")
1371
1372	# Change to MB's
1373	bdev_size=$((bs * nb / 1024 / 1024))
1374	echo "$bdev_size"
1375}
1376
1377function autotest_cleanup() {
1378	local autotest_es=$?
1379	xtrace_disable
1380
1381	# Slurp at_app_exit() so we can kill all lingering vhost and qemu processes
1382	# in one swing. We do this in a subshell as vhost/common.sh is too eager to
1383	# do some extra work which we don't care about in this context.
1384	# shellcheck source=/dev/null
1385	vhost_reap() (source "$rootdir/test/vhost/common.sh" &> /dev/null || return 0 && at_app_exit)
1386
1387	# catch any stray core files and kill all remaining SPDK processes. Update
1388	# autotest_es in case autotest reported success but cores and/or processes
1389	# were left behind regardless.
1390
1391	process_core || autotest_es=1
1392	reap_spdk_processes || autotest_es=1
1393	vhost_reap || autotest_es=1
1394
1395	$rootdir/scripts/setup.sh reset
1396	$rootdir/scripts/setup.sh cleanup
1397	if [ $(uname -s) = "Linux" ]; then
1398		modprobe -r uio_pci_generic
1399	fi
1400	rm -rf "$asan_suppression_file"
1401	if [[ -n ${old_core_pattern:-} ]]; then
1402		echo "$old_core_pattern" > /proc/sys/kernel/core_pattern
1403	fi
1404	if [[ -e /proc/$udevadm_pid/status ]]; then
1405		kill "$udevadm_pid" || :
1406	fi
1407
1408	local storage_fallback_purge=("${TMPDIR:-/tmp}/spdk."??????)
1409
1410	if ((${#storage_fallback_purge[@]} > 0)); then
1411		rm -rf "${storage_fallback_purge[@]}"
1412	fi
1413
1414	if ((autotest_es)); then
1415		if [[ $(uname) == FreeBSD ]]; then
1416			ps aux
1417		elif [[ $(uname) == Linux ]]; then
1418			# Get more detailed view
1419			grep . /proc/[0-9]*/status
1420			# Dump some extra info into kernel log
1421			echo "######## Autotest Cleanup Dump ########" > /dev/kmsg
1422			# Show cpus backtraces
1423			echo l > /proc/sysrq-trigger
1424			# Show mem usage
1425			echo m > /proc/sysrq-trigger
1426			# show task states
1427			echo t > /proc/sysrq-trigger
1428			# show blocked tasks
1429			echo w > /proc/sysrq-trigger
1430
1431		fi > "$output_dir/proc_list.txt" 2>&1 || :
1432	fi
1433
1434	stop_monitor_resources
1435
1436	xtrace_restore
1437	return $autotest_es
1438}
1439
1440function freebsd_update_contigmem_mod() {
1441	if [ $(uname) = FreeBSD ]; then
1442		kldunload contigmem.ko || true
1443		if [ -n "${SPDK_RUN_EXTERNAL_DPDK:-}" ]; then
1444			cp -f "$SPDK_RUN_EXTERNAL_DPDK/kmod/contigmem.ko" /boot/modules/
1445			cp -f "$SPDK_RUN_EXTERNAL_DPDK/kmod/contigmem.ko" /boot/kernel/
1446			cp -f "$SPDK_RUN_EXTERNAL_DPDK/kmod/nic_uio.ko" /boot/modules/
1447			cp -f "$SPDK_RUN_EXTERNAL_DPDK/kmod/nic_uio.ko" /boot/kernel/
1448		else
1449			cp -f "$rootdir/dpdk/build/kmod/contigmem.ko" /boot/modules/
1450			cp -f "$rootdir/dpdk/build/kmod/contigmem.ko" /boot/kernel/
1451			cp -f "$rootdir/dpdk/build/kmod/nic_uio.ko" /boot/modules/
1452			cp -f "$rootdir/dpdk/build/kmod/nic_uio.ko" /boot/kernel/
1453		fi
1454	fi
1455}
1456
1457function freebsd_set_maxsock_buf() {
1458	# FreeBSD needs 4MB maxsockbuf size to pass socket unit tests.
1459	# Otherwise tests fail due to ENOBUFS when trying to do setsockopt(SO_RCVBUF|SO_SNDBUF).
1460	# See https://github.com/spdk/spdk/issues/2943
1461	if [[ $(uname) = FreeBSD ]] && (($(sysctl -n kern.ipc.maxsockbuf) < 4194304)); then
1462		sysctl kern.ipc.maxsockbuf=4194304
1463	fi
1464}
1465
1466function get_nvme_name_from_bdf() {
1467	blkname=()
1468
1469	nvme_devs=$(lsblk -d --output NAME | grep "^nvme") || true
1470	if [ -z "${nvme_devs:-}" ]; then
1471		return
1472	fi
1473	for dev in $nvme_devs; do
1474		link_name=$(readlink /sys/block/$dev/device/device) || true
1475		if [ -z "${link_name:-}" ]; then
1476			link_name=$(readlink /sys/block/$dev/device)
1477		fi
1478		bdf=$(basename "$link_name")
1479		if [ "$bdf" = "$1" ]; then
1480			blkname+=($dev)
1481		fi
1482	done
1483
1484	printf '%s\n' "${blkname[@]}"
1485}
1486
1487function get_nvme_ctrlr_from_bdf() {
1488	bdf_sysfs_path=$(readlink -f /sys/class/nvme/nvme* | grep "$1/nvme/nvme")
1489	if [[ -z "${bdf_sysfs_path:-}" ]]; then
1490		return
1491	fi
1492
1493	printf '%s\n' "$(basename $bdf_sysfs_path)"
1494}
1495
1496# Get BDF addresses of all NVMe drives currently attached to
1497# uio-pci-generic or vfio-pci
1498function get_nvme_bdfs() {
1499	local bdfs=()
1500	bdfs=($("$rootdir/scripts/gen_nvme.sh" | jq -r '.config[].params.traddr'))
1501	if ((${#bdfs[@]} == 0)); then
1502		echo "No bdevs found" >&2
1503		return 1
1504	fi
1505	printf '%s\n' "${bdfs[@]}"
1506}
1507
1508# Same as function above, but just get the first disks BDF address
1509function get_first_nvme_bdf() {
1510	local bdfs=()
1511	bdfs=($(get_nvme_bdfs))
1512
1513	echo "${bdfs[0]}"
1514}
1515
1516function nvme_namespace_revert() {
1517	$rootdir/scripts/setup.sh
1518	sleep 1
1519	local bdfs=()
1520	bdfs=($(get_nvme_bdfs))
1521
1522	$rootdir/scripts/setup.sh reset
1523
1524	for bdf in "${bdfs[@]}"; do
1525		nvme_ctrlr=/dev/$(get_nvme_ctrlr_from_bdf ${bdf})
1526		if [[ -z "${nvme_ctrlr:-}" ]]; then
1527			continue
1528		fi
1529
1530		# Check Optional Admin Command Support for Namespace Management
1531		oacs=$(nvme id-ctrl ${nvme_ctrlr} | grep oacs | cut -d: -f2)
1532		oacs_ns_manage=$((oacs & 0x8))
1533
1534		if [[ "$oacs_ns_manage" -ne 0 ]]; then
1535			# This assumes every NVMe controller contains single namespace,
1536			# encompassing Total NVM Capacity and formatted as 512 block size.
1537			# 512 block size is needed for test/vhost/vhost_boot.sh to
1538			# successfully run.
1539
1540			unvmcap=$(nvme id-ctrl ${nvme_ctrlr} | grep unvmcap | cut -d: -f2)
1541			if [[ "$unvmcap" -eq 0 ]]; then
1542				# All available space already used
1543				continue
1544			fi
1545			tnvmcap=$(nvme id-ctrl ${nvme_ctrlr} | grep tnvmcap | cut -d: -f2)
1546			cntlid=$(nvme id-ctrl ${nvme_ctrlr} | grep cntlid | cut -d: -f2)
1547			blksize=512
1548
1549			size=$((tnvmcap / blksize))
1550
1551			nvme detach-ns ${nvme_ctrlr} -n 0xffffffff -c $cntlid || true
1552			nvme delete-ns ${nvme_ctrlr} -n 0xffffffff || true
1553			nvme create-ns ${nvme_ctrlr} -s ${size} -c ${size} -b ${blksize}
1554			nvme attach-ns ${nvme_ctrlr} -n 1 -c $cntlid
1555			nvme reset ${nvme_ctrlr}
1556			waitforfile "${nvme_ctrlr}n1"
1557		fi
1558	done
1559}
1560
1561# Get BDFs based on device ID, such as 0x0a54
1562function get_nvme_bdfs_by_id() {
1563	local bdfs=()
1564
1565	for bdf in $(get_nvme_bdfs); do
1566		device=$(cat /sys/bus/pci/devices/$bdf/device) || true
1567		if [[ "$device" == "$1" ]]; then
1568			bdfs+=($bdf)
1569		fi
1570	done
1571
1572	printf '%s\n' "${bdfs[@]}"
1573}
1574
1575function opal_revert_cleanup() {
1576	# The OPAL CI tests is only used for P4510 devices.
1577	mapfile -t bdfs < <(get_nvme_bdfs_by_id 0x0a54)
1578	if [[ -z ${bdfs[0]:-} ]]; then
1579		return 0
1580	fi
1581
1582	$SPDK_BIN_DIR/spdk_tgt &
1583	spdk_tgt_pid=$!
1584	waitforlisten $spdk_tgt_pid
1585
1586	bdf_id=0
1587	for bdf in "${bdfs[@]}"; do
1588		$rootdir/scripts/rpc.py bdev_nvme_attach_controller -b "nvme"${bdf_id} -t "pcie" -a ${bdf}
1589		# Ignore if this fails.
1590		$rootdir/scripts/rpc.py bdev_nvme_opal_revert -b "nvme"${bdf_id} -p test || true
1591		((++bdf_id))
1592	done
1593
1594	killprocess $spdk_tgt_pid
1595}
1596
1597function pap() {
1598	while read -r file; do
1599		cat <<- FILE
1600			--- $file ---
1601			$(<"$file")
1602			--- $file ---
1603		FILE
1604		rm -f "$file"
1605	done < <(find "$@" -type f | sort -u)
1606}
1607
1608function get_proc_paths() {
1609	case "$(uname -s)" in
1610		Linux) # ps -e -opid,exe <- not supported under {centos7,rocky8}'s procps-ng
1611			local pid exe
1612			for pid in /proc/[0-9]*; do
1613				exe=$(readlink "$pid/exe") || continue
1614				exe=${exe/ (deleted)/}
1615				echo "${pid##*/} $exe"
1616			done
1617			;;
1618		FreeeBSD) procstat -ab | awk '{print $1, $4}' ;;
1619	esac
1620}
1621
1622exec_files() { file "$@" | awk -F: '/ELF.+executable/{print $1}'; }
1623
1624function reap_spdk_processes() {
1625	local bins test_bins procs
1626	local spdk_procs spdk_pids
1627
1628	mapfile -t test_bins < <(find "$rootdir"/test/{app,env,event} -type f)
1629	mapfile -t bins < <(
1630		exec_files "${test_bins[@]}"
1631		readlink -f "$SPDK_BIN_DIR/"* "$SPDK_EXAMPLE_DIR/"*
1632	)
1633
1634	mapfile -t spdk_procs < <(get_proc_paths | grep -E "$(
1635		IFS="|"
1636		echo "${bins[*]#$rootdir/}"
1637	)" || true)
1638	((${#spdk_procs[@]} > 0)) || return 0
1639
1640	printf '%s is still up, killing\n' "${spdk_procs[@]}" >&2
1641	mapfile -t spdk_pids < <(printf '%s\n' "${spdk_procs[@]}" | awk '{print $1}')
1642
1643	kill -SIGKILL "${spdk_pids[@]}" 2> /dev/null || :
1644	return 1
1645}
1646
1647function is_block_zoned() {
1648	local device=$1
1649
1650	[[ -e /sys/block/$device/queue/zoned ]] || return 1
1651	[[ $(< "/sys/block/$device/queue/zoned") != none ]]
1652}
1653
1654function get_zoned_devs() {
1655	local -gA zoned_devs=()
1656	local nvme bdf
1657
1658	for nvme in /sys/block/nvme*; do
1659		if is_block_zoned "${nvme##*/}"; then
1660			zoned_devs["${nvme##*/}"]=$(< "$nvme/device/address")
1661		fi
1662	done
1663}
1664
1665# Define temp storage for all the tests. Look for 2GB at minimum
1666set_test_storage "${TEST_MIN_STORAGE_SIZE:-$((1 << 31))}"
1667
1668set -o errtrace
1669shopt -s extdebug
1670trap "trap - ERR; print_backtrace >&2" ERR
1671
1672PS4=' \t	-- ${BASH_SOURCE#${BASH_SOURCE%/*/*}/}@${LINENO} -- \$ '
1673if $SPDK_AUTOTEST_X; then
1674	# explicitly enable xtraces, overriding any tracking information.
1675	xtrace_fd
1676else
1677	xtrace_disable
1678fi
1679