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