xref: /spdk/test/bdev/bdev_raid.sh (revision 12fbe739a31b09aff0d05f354d4f3bbef99afc55)
1#!/usr/bin/env bash
2#  SPDX-License-Identifier: BSD-3-Clause
3#  Copyright (C) 2019 Intel Corporation
4#  All rights reserved.
5#
6testdir=$(readlink -f $(dirname $0))
7rootdir=$(readlink -f $testdir/../..)
8rpc_server=/var/tmp/spdk-raid.sock
9tmp_file=$SPDK_TEST_STORAGE/raidrandtest
10
11source $rootdir/test/common/autotest_common.sh
12source $testdir/nbd_common.sh
13
14rpc_py="$rootdir/scripts/rpc.py -s $rpc_server"
15
16function raid_unmap_data_verify() {
17	if hash blkdiscard; then
18		local nbd=$1
19		local rpc_server=$2
20		local blksize
21		blksize=$(lsblk -o LOG-SEC $nbd | grep -v LOG-SEC | cut -d ' ' -f 5)
22		local rw_blk_num=4096
23		local rw_len=$((blksize * rw_blk_num))
24		local unmap_blk_offs=(0 1028 321)
25		local unmap_blk_nums=(128 2035 456)
26		local unmap_off
27		local unmap_len
28
29		# data write
30		dd if=/dev/urandom of=$tmp_file bs=$blksize count=$rw_blk_num
31		dd if=$tmp_file of=$nbd bs=$blksize count=$rw_blk_num oflag=direct
32		blockdev --flushbufs $nbd
33
34		# confirm random data is written correctly in raid0 device
35		cmp -b -n $rw_len $tmp_file $nbd
36
37		for ((i = 0; i < ${#unmap_blk_offs[@]}; i++)); do
38			unmap_off=$((blksize * ${unmap_blk_offs[$i]}))
39			unmap_len=$((blksize * ${unmap_blk_nums[$i]}))
40
41			# data unmap on tmp_file
42			dd if=/dev/zero of=$tmp_file bs=$blksize seek=${unmap_blk_offs[$i]} count=${unmap_blk_nums[$i]} conv=notrunc
43
44			# data unmap on raid bdev
45			blkdiscard -o $unmap_off -l $unmap_len $nbd
46			blockdev --flushbufs $nbd
47
48			# data verify after unmap
49			cmp -b -n $rw_len $tmp_file $nbd
50		done
51	fi
52
53	return 0
54}
55
56function on_error_exit() {
57	if [ -n "$raid_pid" ]; then
58		killprocess $raid_pid
59	fi
60
61	rm -f $tmp_file
62	print_backtrace
63	exit 1
64}
65
66function configure_raid_bdev() {
67	local raid_level=$1
68	rm -rf $testdir/rpcs.txt
69
70	cat <<- EOL >> $testdir/rpcs.txt
71		bdev_malloc_create 32 512 -b Base_1
72		bdev_malloc_create 32 512 -b Base_2
73		bdev_raid_create -z 64 -r $raid_level -b "Base_1 Base_2" -n raid
74	EOL
75	$rpc_py < $testdir/rpcs.txt
76
77	rm -rf $testdir/rpcs.txt
78}
79
80function raid_function_test() {
81	local raid_level=$1
82	if [ $(uname -s) = Linux ] && modprobe -n nbd; then
83		local nbd=/dev/nbd0
84		local raid_bdev
85
86		modprobe nbd
87		$rootdir/test/app/bdev_svc/bdev_svc -r $rpc_server -i 0 -L bdev_raid &
88		raid_pid=$!
89		echo "Process raid pid: $raid_pid"
90		waitforlisten $raid_pid $rpc_server
91
92		configure_raid_bdev $raid_level
93		raid_bdev=$($rpc_py bdev_raid_get_bdevs online | jq -r '.[0]["name"] | select(.)')
94		if [ $raid_bdev = "" ]; then
95			echo "No raid0 device in SPDK app"
96			return 1
97		fi
98
99		nbd_start_disks $rpc_server $raid_bdev $nbd
100		count=$(nbd_get_count $rpc_server)
101		if [ $count -ne 1 ]; then
102			return 1
103		fi
104
105		raid_unmap_data_verify $nbd $rpc_server
106
107		nbd_stop_disks $rpc_server $nbd
108		count=$(nbd_get_count $rpc_server)
109		if [ $count -ne 0 ]; then
110			return 1
111		fi
112
113		killprocess $raid_pid
114	else
115		echo "skipping bdev raid tests."
116	fi
117
118	return 0
119}
120
121function verify_raid_bdev_state() (
122	set +x
123	local raid_bdev_name=$1
124	local expected_state=$2
125	local raid_level=$3
126	local strip_size=$4
127	local raid_bdev
128	local raid_bdev_info
129	local num_base_bdevs
130	local num_base_bdevs_discovered
131	local tmp
132
133	raid_bdev=$($rpc_py bdev_raid_get_bdevs all | jq -r '.[0] | select(.)')
134	if [ -z "$raid_bdev" ]; then
135		echo "No raid device in SPDK app"
136		return 1
137	fi
138
139	raid_bdev_info=$($rpc_py bdev_raid_get_bdevs $expected_state | jq -r ".[] | select(.name == \"$raid_bdev_name\")")
140	if [ -z "$raid_bdev_info" ]; then
141		echo "$raid_bdev_name is not in $expected_state state"
142		return 1
143	fi
144
145	tmp=$(echo $raid_bdev_info | jq -r '.state')
146	if [ "$tmp" != $expected_state ]; then
147		echo "incorrect state: $tmp, expected: $expected_state"
148		return 1
149	fi
150
151	tmp=$(echo $raid_bdev_info | jq -r '.raid_level')
152	if [ "$tmp" != $raid_level ]; then
153		echo "incorrect level: $tmp, expected: $raid_level"
154		return 1
155	fi
156
157	tmp=$(echo $raid_bdev_info | jq -r '.strip_size_kb')
158	if [ "$tmp" != $strip_size ]; then
159		echo "incorrect strip size: $tmp, expected: $strip_size"
160		return 1
161	fi
162
163	num_base_bdevs=$(echo $raid_bdev_info | jq -r '.base_bdevs_list | length')
164	tmp=$(echo $raid_bdev_info | jq -r '.num_base_bdevs')
165	if [ "$num_base_bdevs" != "$tmp" ]; then
166		echo "incorrect num_base_bdevs: $tmp, expected: $num_base_bdevs"
167		return 1
168	fi
169
170	num_base_bdevs_discovered=$(echo $raid_bdev_info | jq -r '[.base_bdevs_list[] | strings] | length')
171	tmp=$(echo $raid_bdev_info | jq -r '.num_base_bdevs_discovered')
172	if [ "$num_base_bdevs_discovered" != "$tmp" ]; then
173		echo "incorrect num_base_bdevs_discovered: $tmp, expected: $num_base_bdevs_discovered"
174		return 1
175	fi
176)
177
178function has_redundancy() {
179	case $1 in
180		"raid1" | "raid5f") return 0 ;;
181		*) return 1 ;;
182	esac
183}
184
185function raid_state_function_test() {
186	local raid_level=$1
187	local num_base_bdevs=$2
188	local raid_bdev
189	local base_bdevs=($(for ((i = 1; i <= num_base_bdevs; i++)); do echo BaseBdev$i; done))
190	local raid_bdev_name="Existed_Raid"
191	local strip_size
192	local strip_size_create_arg
193
194	if [ $raid_level != "raid1" ]; then
195		strip_size=64
196		strip_size_create_arg="-z $strip_size"
197	else
198		strip_size=0
199	fi
200
201	$rootdir/test/app/bdev_svc/bdev_svc -r $rpc_server -i 0 -L bdev_raid &
202	raid_pid=$!
203	echo "Process raid pid: $raid_pid"
204	waitforlisten $raid_pid $rpc_server
205
206	# Step1: create a RAID bdev with no base bdevs
207	# Expect state: CONFIGURING
208	$rpc_py bdev_raid_create $strip_size_create_arg -r $raid_level -b "${base_bdevs[*]}" -n $raid_bdev_name
209	if ! verify_raid_bdev_state $raid_bdev_name "configuring" $raid_level $strip_size; then
210		return 1
211	fi
212	$rpc_py bdev_raid_delete $raid_bdev_name
213
214	# Step2: create one base bdev and add to the RAID bdev
215	# Expect state: CONFIGURING
216	$rpc_py bdev_raid_create $strip_size_create_arg -r $raid_level -b "${base_bdevs[*]}" -n $raid_bdev_name
217	$rpc_py bdev_malloc_create 32 512 -b ${base_bdevs[0]}
218	waitforbdev ${base_bdevs[0]}
219	if ! verify_raid_bdev_state $raid_bdev_name "configuring" $raid_level $strip_size; then
220		return 1
221	fi
222	$rpc_py bdev_raid_delete $raid_bdev_name
223
224	# Step3: create remaining base bdevs and add to the RAID bdev
225	# Expect state: ONLINE
226	$rpc_py bdev_raid_create $strip_size_create_arg -r $raid_level -b "${base_bdevs[*]}" -n $raid_bdev_name
227	for ((i = 1; i < num_base_bdevs; i++)); do
228		if ! verify_raid_bdev_state $raid_bdev_name "configuring" $raid_level $strip_size; then
229			return 1
230		fi
231		$rpc_py bdev_malloc_create 32 512 -b ${base_bdevs[$i]}
232		waitforbdev ${base_bdevs[$i]}
233	done
234	if ! verify_raid_bdev_state $raid_bdev_name "online" $raid_level $strip_size; then
235		return 1
236	fi
237
238	# Step4: delete one base bdev from the RAID bdev
239	$rpc_py bdev_malloc_delete ${base_bdevs[0]}
240	local expected_state
241	if ! has_redundancy $raid_level; then
242		expected_state="offline"
243	else
244		expected_state="online"
245	fi
246	if ! verify_raid_bdev_state $raid_bdev_name $expected_state $raid_level $strip_size; then
247		return 1
248	fi
249
250	# Step5: delete remaining base bdevs from the RAID bdev
251	# Expect state: removed from system
252	for ((i = 1; i < num_base_bdevs; i++)); do
253		raid_bdev=$($rpc_py bdev_raid_get_bdevs all | jq -r '.[0]["name"]')
254		if [ "$raid_bdev" != $raid_bdev_name ]; then
255			echo "$raid_bdev_name removed before all base bdevs were deleted"
256			return 1
257		fi
258		$rpc_py bdev_malloc_delete ${base_bdevs[$i]}
259	done
260	raid_bdev=$($rpc_py bdev_raid_get_bdevs all | jq -r '.[0]["name"] | select(.)')
261	if [ -n "$raid_bdev" ]; then
262		echo "$raid_bdev_name is not removed"
263		return 1
264	fi
265
266	killprocess $raid_pid
267
268	return 0
269}
270
271function raid0_resize_test() {
272	local blksize=512
273	local bdev_size_mb=32
274	local new_bdev_size_mb=$((bdev_size_mb * 2))
275	local blkcnt
276	local raid_size_mb
277	local new_raid_size_mb
278
279	$rootdir/test/app/bdev_svc/bdev_svc -r $rpc_server -i 0 -L bdev_raid &
280	raid_pid=$!
281	echo "Process raid pid: $raid_pid"
282	waitforlisten $raid_pid $rpc_server
283
284	$rpc_py bdev_null_create Base_1 $bdev_size_mb $blksize
285	$rpc_py bdev_null_create Base_2 $bdev_size_mb $blksize
286
287	$rpc_py bdev_raid_create -z 64 -r 0 -b "Base_1 Base_2" -n Raid
288
289	# Resize Base_1 first.
290	$rpc_py bdev_null_resize Base_1 $new_bdev_size_mb
291
292	# The size of Raid should not be changed.
293	blkcnt=$($rpc_py bdev_get_bdevs -b Raid | jq '.[].num_blocks')
294	raid_size_mb=$((blkcnt * blksize / 1048576))
295	if [ $raid_size_mb != $((bdev_size_mb * 2)) ]; then
296		echo "resize failed"
297		return 1
298	fi
299
300	# Resize Base_2 next.
301	$rpc_py bdev_null_resize Base_2 $new_bdev_size_mb
302
303	# The size of Raid should be updated to the expected value.
304	blkcnt=$($rpc_py bdev_get_bdevs -b Raid | jq '.[].num_blocks')
305	raid_size_mb=$((blkcnt * blksize / 1048576))
306	if [ $raid_size_mb != $((new_bdev_size_mb * 2)) ]; then
307		echo "resize failed"
308		return 1
309	fi
310
311	killprocess $raid_pid
312
313	return 0
314}
315
316trap 'on_error_exit;' ERR
317
318raid_function_test raid0
319raid_function_test concat
320raid0_resize_test
321
322for n in {2..4}; do
323	for level in raid0 concat raid1; do
324		raid_state_function_test $level $n
325	done
326done
327
328if [ "$CONFIG_RAID5F" == y ]; then
329	for n in {3..4}; do
330		raid_state_function_test raid5f $n
331	done
332fi
333
334rm -f $tmp_file
335