xref: /linux/tools/testing/selftests/ublk/test_common.sh (revision 6d8854216ebb60959ddb6f4ea4123bd449ba6cf6)
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
182	shift 1
183
184	if [ ! -c /dev/ublk-control ]; then
185		return ${UBLK_SKIP_CODE}
186	fi
187	if echo "$@" | grep -q "\-z"; then
188		if ! _have_feature "ZERO_COPY"; then
189			return ${UBLK_SKIP_CODE}
190		fi
191	fi
192
193	if ! dev_id=$("${UBLK_PROG}" "$cmd" "$@" | grep "dev id" | awk -F '[ :]' '{print $3}'); then
194		echo "fail to add ublk dev $*"
195		return 255
196	fi
197	udevadm settle
198
199	if [[ "$dev_id" =~ ^[0-9]+$ ]]; then
200		echo "${dev_id}"
201	else
202		return 255
203	fi
204}
205
206_add_ublk_dev() {
207	_create_ublk_dev "add" "$@"
208}
209
210_recover_ublk_dev() {
211	local dev_id
212	local state
213
214	dev_id=$(_create_ublk_dev "recover" "$@")
215	for ((j=0;j<20;j++)); do
216		state=$(_get_ublk_dev_state "${dev_id}")
217		[ "$state" == "LIVE" ] && break
218		sleep 1
219	done
220	echo "$state"
221}
222
223# quiesce device and return ublk device state
224__ublk_quiesce_dev()
225{
226	local dev_id=$1
227	local exp_state=$2
228	local state
229
230	if ! ${UBLK_PROG} quiesce -n "${dev_id}"; then
231		state=$(_get_ublk_dev_state "${dev_id}")
232		return "$state"
233	fi
234
235	for ((j=0;j<50;j++)); do
236		state=$(_get_ublk_dev_state "${dev_id}")
237		[ "$state" == "$exp_state" ] && break
238		sleep 1
239	done
240	echo "$state"
241}
242
243# kill the ublk daemon and return ublk device state
244__ublk_kill_daemon()
245{
246	local dev_id=$1
247	local exp_state=$2
248	local daemon_pid
249	local state
250
251	daemon_pid=$(_get_ublk_daemon_pid "${dev_id}")
252	state=$(_get_ublk_dev_state "${dev_id}")
253
254	for ((j=0;j<50;j++)); do
255		[ "$state" == "$exp_state" ] && break
256		kill -9 "$daemon_pid" > /dev/null 2>&1
257		sleep 1
258		state=$(_get_ublk_dev_state "${dev_id}")
259	done
260	echo "$state"
261}
262
263__remove_ublk_dev_return() {
264	local dev_id=$1
265
266	${UBLK_PROG} del -n "${dev_id}"
267	local res=$?
268	udevadm settle
269	return ${res}
270}
271
272__run_io_and_remove()
273{
274	local dev_id=$1
275	local size=$2
276	local kill_server=$3
277
278	fio --name=job1 --filename=/dev/ublkb"${dev_id}" --ioengine=libaio \
279		--rw=randrw --norandommap --iodepth=256 --size="${size}" --numjobs="$(nproc)" \
280		--runtime=20 --time_based > /dev/null 2>&1 &
281	fio --name=batchjob --filename=/dev/ublkb"${dev_id}" --ioengine=io_uring \
282		--rw=randrw --norandommap --iodepth=256 --size="${size}" \
283		--numjobs="$(nproc)" --runtime=20 --time_based \
284		--iodepth_batch_submit=32 --iodepth_batch_complete_min=32 \
285		--force_async=7 > /dev/null 2>&1 &
286	sleep 2
287	if [ "${kill_server}" = "yes" ]; then
288		local state
289		state=$(__ublk_kill_daemon "${dev_id}" "DEAD")
290		if [ "$state" != "DEAD" ]; then
291			echo "device isn't dead($state) after killing daemon"
292			return 255
293		fi
294	fi
295	if ! __remove_ublk_dev_return "${dev_id}"; then
296		echo "delete dev ${dev_id} failed"
297		return 255
298	fi
299	wait
300}
301
302run_io_and_remove()
303{
304	local size=$1
305	local dev_id
306	shift 1
307
308	dev_id=$(_add_ublk_dev "$@")
309	_check_add_dev "$TID" $?
310
311	[ "$UBLK_TEST_QUIET" -eq 0 ] && echo "run ublk IO vs. remove device(ublk add $*)"
312	if ! __run_io_and_remove "$dev_id" "${size}" "no"; then
313		echo "/dev/ublkc$dev_id isn't removed"
314		exit 255
315	fi
316}
317
318run_io_and_kill_daemon()
319{
320	local size=$1
321	local dev_id
322	shift 1
323
324	dev_id=$(_add_ublk_dev "$@")
325	_check_add_dev "$TID" $?
326
327	[ "$UBLK_TEST_QUIET" -eq 0 ] && echo "run ublk IO vs kill ublk server(ublk add $*)"
328	if ! __run_io_and_remove "$dev_id" "${size}" "yes"; then
329		echo "/dev/ublkc$dev_id isn't removed res ${res}"
330		exit 255
331	fi
332}
333
334run_io_and_recover()
335{
336	local action=$1
337	local state
338	local dev_id
339
340	shift 1
341	dev_id=$(_add_ublk_dev "$@")
342	_check_add_dev "$TID" $?
343
344	fio --name=job1 --filename=/dev/ublkb"${dev_id}" --ioengine=libaio \
345		--rw=randread --iodepth=256 --size="${size}" --numjobs=4 \
346		--runtime=20 --time_based > /dev/null 2>&1 &
347	sleep 4
348
349	if [ "$action" == "kill_daemon" ]; then
350		state=$(__ublk_kill_daemon "${dev_id}" "QUIESCED")
351	elif [ "$action" == "quiesce_dev" ]; then
352		state=$(__ublk_quiesce_dev "${dev_id}" "QUIESCED")
353	fi
354	if [ "$state" != "QUIESCED" ]; then
355		echo "device isn't quiesced($state) after $action"
356		return 255
357	fi
358
359	state=$(_recover_ublk_dev -n "$dev_id" "$@")
360	if [ "$state" != "LIVE" ]; then
361		echo "faile to recover to LIVE($state)"
362		return 255
363	fi
364
365	if ! __remove_ublk_dev_return "${dev_id}"; then
366		echo "delete dev ${dev_id} failed"
367		return 255
368	fi
369	wait
370}
371
372
373_ublk_test_top_dir()
374{
375	cd "$(dirname "$0")" && pwd
376}
377
378UBLK_PROG=$(_ublk_test_top_dir)/kublk
379UBLK_TEST_QUIET=1
380UBLK_TEST_SHOW_RESULT=1
381UBLK_BACKFILES=()
382export UBLK_PROG
383export UBLK_TEST_QUIET
384export UBLK_TEST_SHOW_RESULT
385