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