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