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