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