xref: /linux/tools/testing/selftests/ublk/test_common.sh (revision 15ecd83dc06277385ad71dc7ea26911d9a79acaf)
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	sleep 2
282	if [ "${kill_server}" = "yes" ]; then
283		local state
284		state=$(__ublk_kill_daemon "${dev_id}" "DEAD")
285		if [ "$state" != "DEAD" ]; then
286			echo "device isn't dead($state) after killing daemon"
287			return 255
288		fi
289	fi
290	if ! __remove_ublk_dev_return "${dev_id}"; then
291		echo "delete dev ${dev_id} failed"
292		return 255
293	fi
294	wait
295}
296
297run_io_and_remove()
298{
299	local size=$1
300	local dev_id
301	shift 1
302
303	dev_id=$(_add_ublk_dev "$@")
304	_check_add_dev "$TID" $?
305
306	[ "$UBLK_TEST_QUIET" -eq 0 ] && echo "run ublk IO vs. remove device(ublk add $*)"
307	if ! __run_io_and_remove "$dev_id" "${size}" "no"; then
308		echo "/dev/ublkc$dev_id isn't removed"
309		exit 255
310	fi
311}
312
313run_io_and_kill_daemon()
314{
315	local size=$1
316	local dev_id
317	shift 1
318
319	dev_id=$(_add_ublk_dev "$@")
320	_check_add_dev "$TID" $?
321
322	[ "$UBLK_TEST_QUIET" -eq 0 ] && echo "run ublk IO vs kill ublk server(ublk add $*)"
323	if ! __run_io_and_remove "$dev_id" "${size}" "yes"; then
324		echo "/dev/ublkc$dev_id isn't removed res ${res}"
325		exit 255
326	fi
327}
328
329run_io_and_recover()
330{
331	local action=$1
332	local state
333	local dev_id
334
335	shift 1
336	dev_id=$(_add_ublk_dev "$@")
337	_check_add_dev "$TID" $?
338
339	fio --name=job1 --filename=/dev/ublkb"${dev_id}" --ioengine=libaio \
340		--rw=randread --iodepth=256 --size="${size}" --numjobs=4 \
341		--runtime=20 --time_based > /dev/null 2>&1 &
342	sleep 4
343
344	if [ "$action" == "kill_daemon" ]; then
345		state=$(__ublk_kill_daemon "${dev_id}" "QUIESCED")
346	elif [ "$action" == "quiesce_dev" ]; then
347		state=$(__ublk_quiesce_dev "${dev_id}" "QUIESCED")
348	fi
349	if [ "$state" != "QUIESCED" ]; then
350		echo "device isn't quiesced($state) after $action"
351		return 255
352	fi
353
354	state=$(_recover_ublk_dev -n "$dev_id" "$@")
355	if [ "$state" != "LIVE" ]; then
356		echo "faile to recover to LIVE($state)"
357		return 255
358	fi
359
360	if ! __remove_ublk_dev_return "${dev_id}"; then
361		echo "delete dev ${dev_id} failed"
362		return 255
363	fi
364	wait
365}
366
367
368_ublk_test_top_dir()
369{
370	cd "$(dirname "$0")" && pwd
371}
372
373UBLK_PROG=$(_ublk_test_top_dir)/kublk
374UBLK_TEST_QUIET=1
375UBLK_TEST_SHOW_RESULT=1
376UBLK_BACKFILES=()
377export UBLK_PROG
378export UBLK_TEST_QUIET
379export UBLK_TEST_SHOW_RESULT
380