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