xref: /linux/tools/testing/selftests/ublk/test_common.sh (revision 6a069876eb1402478900ee0eb7d7fe276bb1f4e3)
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