1#!/usr/bin/env bash 2# SPDX-License-Identifier: BSD-3-Clause 3# Copyright (C) 2018 Intel Corporation 4# All rights reserved. 5# 6rootdir=$(readlink -f $(dirname $0)/../..) 7source "$rootdir/test/common/autotest_common.sh" 8source "$rootdir/test/nvmf/common.sh" 9 10if [[ $SPDK_TEST_ISCSI -eq 1 ]]; then 11 source "$rootdir/test/iscsi_tgt/common.sh" 12fi 13 14if [[ $SPDK_TEST_VHOST -ne 1 && $SPDK_TEST_VHOST_INIT -eq 1 ]]; then 15 SPDK_TEST_VHOST=1 16 echo "WARNING: Virtio initiator JSON_config test requires vhost target." 17 echo " Setting SPDK_TEST_VHOST=1 for duration of current script." 18fi 19 20if ((SPDK_TEST_BLOCKDEV + \ 21 SPDK_TEST_ISCSI + \ 22 SPDK_TEST_NVMF + \ 23 SPDK_TEST_VHOST + \ 24 SPDK_TEST_VHOST_INIT + \ 25 SPDK_TEST_PMDK + \ 26 SPDK_TEST_RBD == 0)); then 27 echo "WARNING: No tests are enabled so not running JSON configuration tests" 28 exit 0 29fi 30 31declare -A app_pid=([target]="" [initiator]="") 32declare -A app_socket=([target]='/var/tmp/spdk_tgt.sock' [initiator]='/var/tmp/spdk_initiator.sock') 33declare -A app_params=([target]='-m 0x1 -s 1024' [initiator]='-m 0x2 -g -u -s 1024') 34declare -A configs_path=([target]="$rootdir/spdk_tgt_config.json" [initiator]="$rootdir/spdk_initiator_config.json") 35 36function tgt_rpc() { 37 $rootdir/scripts/rpc.py -s "${app_socket[target]}" "$@" 38} 39 40function initiator_rpc() { 41 $rootdir/scripts/rpc.py -s "${app_socket[initiator]}" "$@" 42} 43 44last_event_id=0 45 46function tgt_check_notification_types() { 47 timing_enter "${FUNCNAME[0]}" 48 49 local ret=0 50 local enabled_types=("bdev_register" "bdev_unregister") 51 52 local get_types=($(tgt_rpc notify_get_types | jq -r '.[]')) 53 if [[ ${enabled_types[*]} != "${get_types[*]}" ]]; then 54 echo "ERROR: expected types: ${enabled_types[*]}, but got: ${get_types[*]}" 55 ret=1 56 fi 57 58 timing_exit "${FUNCNAME[0]}" 59 return $ret 60} 61 62get_notifications() { 63 local ev_type ev_ctx event_id 64 65 while IFS=":" read -r ev_type ev_ctx event_id; do 66 echo "$ev_type:$ev_ctx" 67 done < <(tgt_rpc notify_get_notifications -i "$last_event_id" | jq -r '.[] | "\(.type):\(.ctx):\(.id)"') 68} 69 70function tgt_check_notifications() { 71 local events_to_check 72 local recorded_events 73 74 events_to_check=("$@") 75 recorded_events=($(get_notifications)) 76 77 # These should be in order hence compare entire arrays 78 if [[ ${events_to_check[*]} != "${recorded_events[*]}" ]]; then 79 cat <<- ERROR 80 Expected events did not match. 81 82 Expected: 83 $(printf ' %s\n' "${events_to_check[@]}") 84 Recorded: 85 $(printf ' %s\n' "${recorded_events[@]}") 86 ERROR 87 return 1 88 fi 89 90 cat <<- INFO 91 Expected events matched: 92 $(printf ' %s\n' "${recorded_events[@]}") 93 INFO 94} 95 96# $1 - target / initiator 97# $2..$n app parameters 98function json_config_test_start_app() { 99 local app=$1 100 shift 101 102 [[ -n "${#app_socket[$app]}" ]] # Check app type 103 [[ -z "${app_pid[$app]}" ]] # Assert if app is not running 104 105 local app_extra_params="" 106 if [[ $SPDK_TEST_VHOST -eq 1 || $SPDK_TEST_VHOST_INIT -eq 1 ]]; then 107 # If PWD is nfs/sshfs we can't create UNIX sockets there. Always use safe location instead. 108 app_extra_params='-S /var/tmp' 109 fi 110 111 $SPDK_BIN_DIR/spdk_tgt ${app_params[$app]} ${app_extra_params} -r ${app_socket[$app]} "$@" & 112 app_pid[$app]=$! 113 114 echo "Waiting for $app to run..." 115 waitforlisten ${app_pid[$app]} ${app_socket[$app]} 116 echo "" 117} 118 119# $1 - target / initiator 120function json_config_test_shutdown_app() { 121 local app=$1 122 123 # Check app type && assert app was started 124 [[ -n "${#app_socket[$app]}" ]] 125 [[ -n "${app_pid[$app]}" ]] 126 127 # spdk_kill_instance RPC will trigger ASAN 128 kill -SIGINT ${app_pid[$app]} 129 130 for ((i = 0; i < 30; i++)); do 131 if ! kill -0 ${app_pid[$app]} 2> /dev/null; then 132 app_pid[$app]= 133 break 134 fi 135 sleep 0.5 136 done 137 138 if [[ -n "${app_pid[$app]}" ]]; then 139 echo "SPDK $app shutdown timeout" 140 return 1 141 fi 142 143 echo "SPDK $app shutdown done" 144} 145 146function create_bdev_subsystem_config() { 147 timing_enter "${FUNCNAME[0]}" 148 149 local expected_notifications=() 150 151 # Consider multiple nvme devices loaded into the subsystem prior running 152 # the tests. 153 expected_notifications+=($(get_notifications)) 154 155 if [[ $SPDK_TEST_BLOCKDEV -eq 1 ]]; then 156 local lvol_store_base_bdev=Nvme0n1 157 158 tgt_rpc bdev_split_create $lvol_store_base_bdev 2 159 tgt_rpc bdev_split_create Malloc0 3 160 tgt_rpc bdev_malloc_create 8 4096 --name Malloc3 161 tgt_rpc bdev_passthru_create -b Malloc3 -p PTBdevFromMalloc3 162 163 tgt_rpc bdev_null_create Null0 32 512 164 165 tgt_rpc bdev_malloc_create 32 512 --name Malloc0 166 tgt_rpc bdev_malloc_create 16 4096 --name Malloc1 167 168 expected_notifications+=( 169 bdev_register:${lvol_store_base_bdev}p1 170 bdev_register:${lvol_store_base_bdev}p0 171 bdev_register:Malloc3 172 bdev_register:PTBdevFromMalloc3 173 bdev_register:Null0 174 bdev_register:Malloc0 175 bdev_register:Malloc0p2 176 bdev_register:Malloc0p1 177 bdev_register:Malloc0p0 178 bdev_register:Malloc1 179 ) 180 181 if [[ $(uname -s) = Linux ]]; then 182 # This AIO bdev must be large enough to be used as LVOL store 183 dd if=/dev/zero of="$SPDK_TEST_STORAGE/sample_aio" bs=1024 count=102400 184 tgt_rpc bdev_aio_create "$SPDK_TEST_STORAGE/sample_aio" aio_disk 1024 185 expected_notifications+=(bdev_register:aio_disk) 186 fi 187 188 # For LVOLs use split to check for proper order of initialization. 189 # If LVOLs configuration will be reordered (eg moved before splits or AIO/NVMe) 190 # it should fail loading JSON config from file. 191 tgt_rpc bdev_lvol_create_lvstore -c 1048576 ${lvol_store_base_bdev}p0 lvs_test 192 193 expected_notifications+=( 194 "bdev_register:$(tgt_rpc bdev_lvol_create -l lvs_test lvol0 32)" 195 "bdev_register:$(tgt_rpc bdev_lvol_create -l lvs_test -t lvol1 32)" 196 "bdev_register:$(tgt_rpc bdev_lvol_snapshot lvs_test/lvol0 snapshot0)" 197 "bdev_register:$(tgt_rpc bdev_lvol_clone lvs_test/snapshot0 clone0)" 198 ) 199 fi 200 201 if [[ $SPDK_TEST_CRYPTO -eq 1 ]]; then 202 tgt_rpc bdev_malloc_create 8 1024 --name MallocForCryptoBdev 203 if [[ $(lspci -d:37c8 | wc -l) -eq 0 ]]; then 204 local crypto_driver=crypto_aesni_mb 205 else 206 local crypto_driver=crypto_qat 207 fi 208 209 tgt_rpc bdev_crypto_create MallocForCryptoBdev CryptoMallocBdev $crypto_driver 01234567891234560123456789123456 210 expected_notifications+=( 211 bdev_register:MallocForCryptoBdev 212 bdev_register:CryptoMallocBdev 213 ) 214 fi 215 216 if [[ $SPDK_TEST_PMDK -eq 1 ]]; then 217 pmem_pool_file=$(mktemp /tmp/pool_file1.XXXXX) 218 rm -f $pmem_pool_file 219 tgt_rpc bdev_pmem_create_pool $pmem_pool_file 128 4096 220 tgt_rpc bdev_pmem_create -n pmem1 $pmem_pool_file 221 expected_notifications+=(bdev_register:pmem1) 222 fi 223 224 if [[ $SPDK_TEST_RBD -eq 1 ]]; then 225 rbd_setup 127.0.0.1 226 tgt_rpc bdev_rbd_create $RBD_POOL $RBD_NAME 4096 227 expected_notifications+=(bdev_register:Ceph0) 228 fi 229 230 tgt_check_notifications "${expected_notifications[@]}" 231 232 timing_exit "${FUNCNAME[0]}" 233} 234 235function cleanup_bdev_subsystem_config() { 236 timing_enter "${FUNCNAME[0]}" 237 238 if [[ $SPDK_TEST_BLOCKDEV -eq 1 ]]; then 239 tgt_rpc bdev_lvol_delete lvs_test/clone0 240 tgt_rpc bdev_lvol_delete lvs_test/lvol0 241 tgt_rpc bdev_lvol_delete lvs_test/snapshot0 242 tgt_rpc bdev_lvol_delete_lvstore -l lvs_test 243 fi 244 245 if [[ $(uname -s) = Linux ]]; then 246 rm -f "$SPDK_TEST_STORAGE/sample_aio" 247 fi 248 249 if [[ $SPDK_TEST_PMDK -eq 1 && -n "$pmem_pool_file" && -f "$pmem_pool_file" ]]; then 250 tgt_rpc bdev_pmem_delete pmem1 251 tgt_rpc bdev_pmem_delete_pool $pmem_pool_file 252 rm -f $pmem_pool_file 253 fi 254 255 if [[ $SPDK_TEST_RBD -eq 1 ]]; then 256 rbd_cleanup 257 fi 258 259 timing_exit "${FUNCNAME[0]}" 260} 261 262function create_vhost_subsystem_config() { 263 timing_enter "${FUNCNAME[0]}" 264 265 tgt_rpc bdev_malloc_create 64 1024 --name MallocForVhost0 266 tgt_rpc bdev_split_create MallocForVhost0 8 267 268 tgt_rpc vhost_create_scsi_controller VhostScsiCtrlr0 269 tgt_rpc vhost_scsi_controller_add_target VhostScsiCtrlr0 0 MallocForVhost0p3 270 tgt_rpc vhost_scsi_controller_add_target VhostScsiCtrlr0 -1 MallocForVhost0p4 271 tgt_rpc vhost_controller_set_coalescing VhostScsiCtrlr0 1 100 272 273 tgt_rpc vhost_create_blk_controller VhostBlkCtrlr0 MallocForVhost0p5 274 275 timing_exit "${FUNCNAME[0]}" 276} 277 278function create_iscsi_subsystem_config() { 279 timing_enter "${FUNCNAME[0]}" 280 tgt_rpc bdev_malloc_create 64 1024 --name MallocForIscsi0 281 tgt_rpc iscsi_create_portal_group $PORTAL_TAG 127.0.0.1:$ISCSI_PORT 282 tgt_rpc iscsi_create_initiator_group $INITIATOR_TAG $INITIATOR_NAME $NETMASK 283 tgt_rpc iscsi_create_target_node Target3 Target3_alias 'MallocForIscsi0:0' $PORTAL_TAG:$INITIATOR_TAG 64 -d 284 timing_exit "${FUNCNAME[0]}" 285} 286 287function create_nvmf_subsystem_config() { 288 timing_enter "${FUNCNAME[0]}" 289 290 NVMF_FIRST_TARGET_IP="127.0.0.1" 291 if [[ $SPDK_TEST_NVMF_TRANSPORT == "rdma" ]]; then 292 TEST_TRANSPORT=$SPDK_TEST_NVMF_TRANSPORT nvmftestinit 293 fi 294 295 if [[ -z $NVMF_FIRST_TARGET_IP ]]; then 296 echo "Error: no NIC for nvmf test" 297 return 1 298 fi 299 300 tgt_rpc bdev_malloc_create 8 512 --name MallocForNvmf0 301 tgt_rpc bdev_malloc_create 4 1024 --name MallocForNvmf1 302 303 tgt_rpc nvmf_create_transport -t $SPDK_TEST_NVMF_TRANSPORT -u 8192 -c 0 304 tgt_rpc nvmf_create_subsystem nqn.2016-06.io.spdk:cnode1 -a -s SPDK00000000000001 305 tgt_rpc nvmf_subsystem_add_ns nqn.2016-06.io.spdk:cnode1 MallocForNvmf0 306 tgt_rpc nvmf_subsystem_add_ns nqn.2016-06.io.spdk:cnode1 MallocForNvmf1 307 tgt_rpc nvmf_subsystem_add_listener nqn.2016-06.io.spdk:cnode1 -t $SPDK_TEST_NVMF_TRANSPORT -a $NVMF_FIRST_TARGET_IP -s "$NVMF_PORT" 308 309 timing_exit "${FUNCNAME[0]}" 310} 311 312function create_virtio_initiator_config() { 313 timing_enter "${FUNCNAME[0]}" 314 initiator_rpc bdev_virtio_attach_controller -t user -a /var/tmp/VhostScsiCtrlr0 -d scsi VirtioScsiCtrlr0 315 initiator_rpc bdev_virtio_attach_controller -t user -a /var/tmp/VhostBlkCtrlr0 -d blk VirtioBlk0 316 timing_exit "${FUNCNAME[0]}" 317} 318 319function json_config_test_init() { 320 timing_enter "${FUNCNAME[0]}" 321 timing_enter json_config_setup_target 322 323 json_config_test_start_app target --wait-for-rpc 324 325 #TODO: global subsystem params 326 327 # Load nvme configuration. The load_config will issue framework_start_init automatically 328 ( 329 $rootdir/scripts/gen_nvme.sh --json-with-subsystems 330 ) | tgt_rpc load_config 331 332 tgt_check_notification_types 333 334 if [[ $SPDK_TEST_BLOCKDEV -eq 1 ]]; then 335 create_bdev_subsystem_config 336 fi 337 338 if [[ $SPDK_TEST_VHOST -eq 1 ]]; then 339 create_vhost_subsystem_config 340 fi 341 342 if [[ $SPDK_TEST_ISCSI -eq 1 ]]; then 343 create_iscsi_subsystem_config 344 fi 345 346 if [[ $SPDK_TEST_NVMF -eq 1 ]]; then 347 create_nvmf_subsystem_config 348 fi 349 timing_exit json_config_setup_target 350 351 if [[ $SPDK_TEST_VHOST_INIT -eq 1 ]]; then 352 json_config_test_start_app initiator 353 create_virtio_initiator_config 354 fi 355 356 tgt_rpc bdev_malloc_create 8 512 --name MallocBdevForConfigChangeCheck 357 358 timing_exit "${FUNCNAME[0]}" 359} 360 361function json_config_test_fini() { 362 timing_enter "${FUNCNAME[0]}" 363 local ret=0 364 365 if [[ -n "${app_pid[initiator]}" ]]; then 366 killprocess ${app_pid[initiator]} 367 fi 368 369 if [[ -n "${app_pid[target]}" ]]; then 370 371 # Remove any artifacts we created (files, lvol etc) 372 cleanup_bdev_subsystem_config 373 374 # SPDK_TEST_NVMF: Should we clear something? 375 killprocess ${app_pid[target]} 376 fi 377 378 rm -f "${configs_path[@]}" 379 timing_exit "${FUNCNAME[0]}" 380 return $ret 381} 382 383function json_config_clear() { 384 [[ -n "${#app_socket[$1]}" ]] # Check app type 385 $rootdir/test/json_config/clear_config.py -s ${app_socket[$1]} clear_config 386 387 # Check if config is clean. 388 # Global params can't be cleared so need to filter them out. 389 local config_filter="$rootdir/test/json_config/config_filter.py" 390 391 # RPC's used to cleanup configuration (e.g. to delete split and nvme bdevs) 392 # complete immediately and they don't wait for the unregister callback. 393 # It causes that configuration may not be fully cleaned at this moment and 394 # we should to wait a while. (See github issue #789) 395 count=100 396 while [ $count -gt 0 ]; do 397 $rootdir/scripts/rpc.py -s "${app_socket[$1]}" save_config | $config_filter -method delete_global_parameters | $config_filter -method check_empty && break 398 count=$((count - 1)) 399 sleep 0.1 400 done 401 402 if [ $count -eq 0 ]; then 403 return 1 404 fi 405} 406 407on_error_exit() { 408 set -x 409 set +e 410 print_backtrace 411 trap - ERR 412 echo "Error on $1 - $2" 413 json_config_test_fini 414 exit 1 415} 416 417trap 'on_error_exit "${FUNCNAME}" "${LINENO}"' ERR 418echo "INFO: JSON configuration test init" 419json_config_test_init 420 421tgt_rpc save_config > ${configs_path[target]} 422 423echo "INFO: shutting down applications..." 424if [[ $SPDK_TEST_VHOST_INIT -eq 1 ]]; then 425 initiator_rpc save_config > ${configs_path[initiator]} 426 json_config_clear initiator 427 json_config_test_shutdown_app initiator 428fi 429 430json_config_clear target 431json_config_test_shutdown_app target 432 433echo "INFO: relaunching applications..." 434json_config_test_start_app target --json ${configs_path[target]} 435if [[ $SPDK_TEST_VHOST_INIT -eq 1 ]]; then 436 json_config_test_start_app initiator --json ${configs_path[initiator]} 437fi 438 439echo "INFO: Checking if target configuration is the same..." 440$rootdir/test/json_config/json_diff.sh <(tgt_rpc save_config) "${configs_path[target]}" 441if [[ $SPDK_TEST_VHOST_INIT -eq 1 ]]; then 442 echo "INFO: Checking if virtio initiator configuration is the same..." 443 $rootdir/test/json_config/json_diff.sh <(initiator_rpc save_config) "${configs_path[initiator]}" 444fi 445 446echo "INFO: changing configuration and checking if this can be detected..." 447# Self test to check if configuration diff can be detected. 448tgt_rpc bdev_malloc_delete MallocBdevForConfigChangeCheck 449if $rootdir/test/json_config/json_diff.sh <(tgt_rpc save_config) "${configs_path[target]}" > /dev/null; then 450 echo "ERROR: intentional configuration difference not detected!" 451 false 452else 453 echo "INFO: configuration change detected." 454fi 455 456json_config_test_fini 457 458echo "INFO: Success" 459