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