1#!/bin/bash 2# SPDX-License-Identifier: GPL-2.0 3 4# Derive TID from script name: test_<type>_<num>.sh -> <type>_<num> 5# Can be overridden in test script after sourcing this file 6TID=$(basename "$0" .sh) 7TID=${TID#test_} 8 9UBLK_SKIP_CODE=4 10 11_have_program() { 12 if command -v "$1" >/dev/null 2>&1; then 13 return 0 14 fi 15 return 1 16} 17 18# Sleep with awareness of parallel execution. 19# Usage: _ublk_sleep <normal_secs> <parallel_secs> 20_ublk_sleep() { 21 if [ "${JOBS:-1}" -gt 1 ]; then 22 sleep "$2" 23 else 24 sleep "$1" 25 fi 26} 27 28_get_disk_dev_t() { 29 local dev_id=$1 30 local dev 31 local major 32 local minor 33 34 dev=/dev/ublkb"${dev_id}" 35 major="0x"$(stat -c '%t' "$dev") 36 minor="0x"$(stat -c '%T' "$dev") 37 38 echo $(( (major & 0xfff) << 20 | (minor & 0xfffff) )) 39} 40 41_get_disk_size() 42{ 43 lsblk -b -o SIZE -n "$1" 44} 45 46_run_fio_verify_io() { 47 fio --name=verify --rw=randwrite --direct=1 --ioengine=libaio \ 48 --bs=8k --iodepth=32 --verify=crc32c --do_verify=1 \ 49 --verify_state_save=0 "$@" > /dev/null 50} 51 52_create_backfile() { 53 local index=$1 54 local new_size=$2 55 local old_file 56 local new_file 57 58 old_file="${UBLK_BACKFILES[$index]}" 59 [ -f "$old_file" ] && rm -f "$old_file" 60 61 new_file=$(mktemp ${UBLK_TEST_DIR}/ublk_file_"${new_size}"_XXXXX) 62 truncate -s "${new_size}" "${new_file}" 63 UBLK_BACKFILES["$index"]="$new_file" 64} 65 66_remove_files() { 67 local file 68 69 for file in "${UBLK_BACKFILES[@]}"; do 70 [ -f "$file" ] && rm -f "$file" 71 done 72 [ -f "$UBLK_TMP" ] && rm -f "$UBLK_TMP" 73} 74 75_create_tmp_dir() { 76 local my_file; 77 78 my_file=$(mktemp -d ${UBLK_TEST_DIR}/ublk_dir_XXXXX) 79 echo "$my_file" 80} 81 82_remove_tmp_dir() { 83 local dir=$1 84 85 [ -d "$dir" ] && rmdir "$dir" 86} 87 88_mkfs_mount_test() 89{ 90 local dev=$1 91 local err_code=0 92 local mnt_dir; 93 94 mnt_dir=$(_create_tmp_dir) 95 mkfs.ext4 -F "$dev" > /dev/null 2>&1 96 err_code=$? 97 if [ $err_code -ne 0 ]; then 98 return $err_code 99 fi 100 101 mount -t ext4 "$dev" "$mnt_dir" > /dev/null 2>&1 102 umount "$dev" 103 err_code=$? 104 _remove_tmp_dir "$mnt_dir" 105 if [ $err_code -ne 0 ]; then 106 return $err_code 107 fi 108} 109 110_check_root() { 111 local ksft_skip=4 112 113 if [ $UID != 0 ]; then 114 echo please run this as root >&2 115 exit $ksft_skip 116 fi 117} 118 119_get_ublk_dev_state() { 120 ${UBLK_PROG} list -n "$1" | grep "state" | awk '{print $11}' 121} 122 123_get_ublk_daemon_pid() { 124 ${UBLK_PROG} list -n "$1" | grep "pid" | awk '{print $7}' 125} 126 127_prep_test() { 128 _check_root 129 local type=$1 130 shift 1 131 modprobe ublk_drv > /dev/null 2>&1 132 local base_dir=${TMPDIR:-./ublktest-dir} 133 mkdir -p "$base_dir" 134 UBLK_TEST_DIR=$(mktemp -d ${base_dir}/${TID}.XXXXXX) 135 UBLK_TMP=$(mktemp ${UBLK_TEST_DIR}/ublk_test_XXXXX) 136 [ "$UBLK_TEST_QUIET" -eq 0 ] && echo "ublk $type: $*" 137 echo "ublk selftest: $TID starting at $(date '+%F %T')" | tee /dev/kmsg 138} 139 140_remove_test_files() 141{ 142 local files=$* 143 144 for file in ${files}; do 145 [ -f "${file}" ] && rm -f "${file}" 146 done 147} 148 149_show_result() 150{ 151 if [ "$UBLK_TEST_SHOW_RESULT" -ne 0 ]; then 152 if [ "$2" -eq 0 ]; then 153 echo "$1 : [PASS]" 154 elif [ "$2" -eq 4 ]; then 155 echo "$1 : [SKIP]" 156 else 157 echo "$1 : [FAIL]" 158 fi 159 fi 160 if [ "$2" -ne 0 ]; then 161 _remove_files 162 exit "$2" 163 fi 164 return 0 165} 166 167# don't call from sub-shell, otherwise can't exit 168_check_add_dev() 169{ 170 local tid=$1 171 local code=$2 172 173 if [ "${code}" -ne 0 ]; then 174 _show_result "${tid}" "${code}" 175 fi 176} 177 178_cleanup_test() { 179 if [ -f "${UBLK_TEST_DIR}/.ublk_devs" ]; then 180 while read -r dev_id; do 181 ${UBLK_PROG} del -n "${dev_id}" 182 done < "${UBLK_TEST_DIR}/.ublk_devs" 183 rm -f "${UBLK_TEST_DIR}/.ublk_devs" 184 fi 185 186 _remove_files 187 rmdir ${UBLK_TEST_DIR} 188 echo "ublk selftest: $TID done at $(date '+%F %T')" | tee /dev/kmsg 189} 190 191_have_feature() 192{ 193 if $UBLK_PROG "features" | grep "$1" > /dev/null 2>&1; then 194 return 0 195 fi 196 return 1 197} 198 199_create_ublk_dev() { 200 local dev_id; 201 local cmd=$1 202 local settle=$2 203 204 shift 2 205 206 if [ ! -c /dev/ublk-control ]; then 207 return ${UBLK_SKIP_CODE} 208 fi 209 if echo "$@" | grep -q "\-z"; then 210 if ! _have_feature "ZERO_COPY"; then 211 return ${UBLK_SKIP_CODE} 212 fi 213 fi 214 215 if ! dev_id=$("${UBLK_PROG}" "$cmd" "$@" | grep "dev id" | awk -F '[ :]' '{print $3}'); then 216 echo "fail to add ublk dev $*" 217 return 255 218 fi 219 220 if [ "$settle" = "yes" ]; then 221 udevadm settle --timeout=20 222 fi 223 224 if [[ "$dev_id" =~ ^[0-9]+$ ]]; then 225 echo "$dev_id" >> "${UBLK_TEST_DIR}/.ublk_devs" 226 echo "${dev_id}" 227 else 228 return 255 229 fi 230} 231 232_add_ublk_dev() { 233 _create_ublk_dev "add" "yes" "$@" 234} 235 236_add_ublk_dev_no_settle() { 237 _create_ublk_dev "add" "no" "$@" 238} 239 240_recover_ublk_dev() { 241 local dev_id 242 local state 243 244 dev_id=$(_create_ublk_dev "recover" "yes" "$@") 245 for ((j=0;j<100;j++)); do 246 state=$(_get_ublk_dev_state "${dev_id}") 247 [ "$state" == "LIVE" ] && break 248 sleep 1 249 done 250 echo "$state" 251} 252 253# quiesce device and return ublk device state 254__ublk_quiesce_dev() 255{ 256 local dev_id=$1 257 local exp_state=$2 258 local state 259 260 if ! ${UBLK_PROG} quiesce -n "${dev_id}"; then 261 state=$(_get_ublk_dev_state "${dev_id}") 262 return "$state" 263 fi 264 265 for ((j=0;j<100;j++)); do 266 state=$(_get_ublk_dev_state "${dev_id}") 267 [ "$state" == "$exp_state" ] && break 268 sleep 1 269 done 270 echo "$state" 271} 272 273# kill the ublk daemon and return ublk device state 274__ublk_kill_daemon() 275{ 276 local dev_id=$1 277 local exp_state=$2 278 local daemon_pid 279 local state 280 281 daemon_pid=$(_get_ublk_daemon_pid "${dev_id}") 282 state=$(_get_ublk_dev_state "${dev_id}") 283 284 for ((j=0;j<100;j++)); do 285 [ "$state" == "$exp_state" ] && break 286 kill -9 "$daemon_pid" > /dev/null 2>&1 287 sleep 1 288 state=$(_get_ublk_dev_state "${dev_id}") 289 done 290 echo "$state" 291} 292 293_ublk_del_dev() { 294 local dev_id=$1 295 296 ${UBLK_PROG} del -n "${dev_id}" 297 298 # Remove from tracking file 299 if [ -f "${UBLK_TEST_DIR}/.ublk_devs" ]; then 300 sed -i "/^${dev_id}$/d" "${UBLK_TEST_DIR}/.ublk_devs" 301 fi 302} 303 304__remove_ublk_dev_return() { 305 local dev_id=$1 306 307 _ublk_del_dev "${dev_id}" 308 local res=$? 309 udevadm settle --timeout=20 310 return ${res} 311} 312 313__run_io_and_remove() 314{ 315 local dev_id=$1 316 local size=$2 317 local kill_server=$3 318 319 fio --name=job1 --filename=/dev/ublkb"${dev_id}" --ioengine=libaio \ 320 --rw=randrw --norandommap --iodepth=256 --size="${size}" --numjobs="$(nproc)" \ 321 --runtime=20 --time_based > /dev/null 2>&1 & 322 fio --name=batchjob --filename=/dev/ublkb"${dev_id}" --ioengine=io_uring \ 323 --rw=randrw --norandommap --iodepth=256 --size="${size}" \ 324 --numjobs="$(nproc)" --runtime=20 --time_based \ 325 --iodepth_batch_submit=32 --iodepth_batch_complete_min=32 \ 326 --force_async=7 > /dev/null 2>&1 & 327 sleep 2 328 if [ "${kill_server}" = "yes" ]; then 329 local state 330 state=$(__ublk_kill_daemon "${dev_id}" "DEAD") 331 if [ "$state" != "DEAD" ]; then 332 echo "device isn't dead($state) after killing daemon" 333 return 255 334 fi 335 fi 336 if ! __remove_ublk_dev_return "${dev_id}"; then 337 echo "delete dev ${dev_id} failed" 338 return 255 339 fi 340 wait 341} 342 343run_io_and_remove() 344{ 345 local size=$1 346 local dev_id 347 shift 1 348 349 dev_id=$(_add_ublk_dev "$@") 350 _check_add_dev "$TID" $? 351 352 [ "$UBLK_TEST_QUIET" -eq 0 ] && echo "run ublk IO vs. remove device(ublk add $*)" 353 if ! __run_io_and_remove "$dev_id" "${size}" "no"; then 354 echo "/dev/ublkc$dev_id isn't removed" 355 exit 255 356 fi 357} 358 359run_io_and_kill_daemon() 360{ 361 local size=$1 362 local dev_id 363 shift 1 364 365 dev_id=$(_add_ublk_dev "$@") 366 _check_add_dev "$TID" $? 367 368 [ "$UBLK_TEST_QUIET" -eq 0 ] && echo "run ublk IO vs kill ublk server(ublk add $*)" 369 if ! __run_io_and_remove "$dev_id" "${size}" "yes"; then 370 echo "/dev/ublkc$dev_id isn't removed res ${res}" 371 exit 255 372 fi 373} 374 375run_io_and_recover() 376{ 377 local size=$1 378 local action=$2 379 local state 380 local dev_id 381 382 shift 2 383 dev_id=$(_add_ublk_dev "$@") 384 _check_add_dev "$TID" $? 385 386 fio --name=job1 --filename=/dev/ublkb"${dev_id}" --ioengine=libaio \ 387 --rw=randread --iodepth=256 --size="${size}" --numjobs=4 \ 388 --runtime=20 --time_based > /dev/null 2>&1 & 389 sleep 4 390 391 if [ "$action" == "kill_daemon" ]; then 392 state=$(__ublk_kill_daemon "${dev_id}" "QUIESCED") 393 elif [ "$action" == "quiesce_dev" ]; then 394 state=$(__ublk_quiesce_dev "${dev_id}" "QUIESCED") 395 fi 396 if [ "$state" != "QUIESCED" ]; then 397 echo "device isn't quiesced($state) after $action" 398 return 255 399 fi 400 401 state=$(_recover_ublk_dev -n "$dev_id" "$@") 402 if [ "$state" != "LIVE" ]; then 403 echo "faile to recover to LIVE($state)" 404 return 255 405 fi 406 407 if ! __remove_ublk_dev_return "${dev_id}"; then 408 echo "delete dev ${dev_id} failed" 409 return 255 410 fi 411 wait 412} 413 414 415_ublk_test_top_dir() 416{ 417 cd "$(dirname "$0")" && pwd 418} 419 420METADATA_SIZE_PROG="$(_ublk_test_top_dir)/metadata_size" 421 422_get_metadata_size() 423{ 424 local dev_id=$1 425 local field=$2 426 427 "$METADATA_SIZE_PROG" "/dev/ublkb$dev_id" | grep "$field" | grep -o "[0-9]*" 428} 429 430UBLK_PROG=$(_ublk_test_top_dir)/kublk 431UBLK_TEST_QUIET=1 432UBLK_TEST_SHOW_RESULT=1 433UBLK_BACKFILES=() 434export UBLK_PROG 435export UBLK_TEST_QUIET 436export UBLK_TEST_SHOW_RESULT 437