xref: /illumos-gate/usr/src/test/zfs-tests/tests/functional/mmp/mmp.kshlib (revision d3b5f56344d8bfcdd6cfb82446af0e5e55ad9ebe)
1#
2# CDDL HEADER START
3#
4# The contents of this file are subject to the terms of the
5# Common Development and Distribution License (the "License").
6# You may not use this file except in compliance with the License.
7#
8# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9# or http://www.opensolaris.org/os/licensing.
10# See the License for the specific language governing permissions
11# and limitations under the License.
12#
13# When distributing Covered Code, include this CDDL HEADER in each
14# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15# If applicable, add the following below this CDDL HEADER, with the
16# fields enclosed by brackets "[]" replaced with your own identifying
17# information: Portions Copyright [yyyy] [name of copyright owner]
18#
19# CDDL HEADER END
20#
21
22#
23# Copyright (c) 2017 by Lawrence Livermore National Security, LLC.
24# Use is subject to license terms.
25# Copyright 2019 Joyent, Inc.
26#
27
28. $STF_SUITE/include/libtest.shlib
29. $STF_SUITE/tests/functional/mmp/mmp.cfg
30
31
32function check_pool_import # pool opts token keyword
33{
34	typeset pool=${1:-$MMP_POOL}
35	typeset opts=$2
36	typeset token=$3
37	typeset keyword=$4
38
39	zpool import $opts 2>&1 | \
40	    nawk -v token="$token:" '($1==token) {print $0}' | \
41	    grep -i "$keyword" > /dev/null 2>&1
42
43	return $?
44}
45
46function is_pool_imported # pool opts
47{
48	typeset pool=${1:-$MMP_POOL}
49	typeset opts=$2
50
51	check_pool_import "$pool" "$opts" "status" \
52	    "The pool is currently imported"
53	return $?
54}
55
56function wait_pool_imported # pool opts
57{
58	typeset pool=${1:-$MMP_POOL}
59	typeset opts=$2
60
61	while is_pool_imported "$pool" "$opts"; do
62		log_must sleep 5
63	done
64
65	return 0
66}
67
68function try_pool_import # pool opts message
69{
70	typeset pool=${1:-$MMP_POOL}
71	typeset opts=$2
72	typeset msg=$3
73
74	zpool import $opts $pool 2>&1 | grep -i "$msg"
75
76	return $?
77}
78
79function chr2ascii
80{
81	case "$1" in
82	0)	asc="30";;
83	1)	asc="31";;
84	2)	asc="32";;
85	3)	asc="33";;
86	4)	asc="34";;
87	5)	asc="35";;
88	6)	asc="36";;
89	7)	asc="37";;
90	8)	asc="38";;
91	9)	asc="39";;
92	a)	asc="61";;
93	b)	asc="62";;
94	c)	asc="63";;
95	d)	asc="64";;
96	e)	asc="65";;
97	f)	asc="66";;
98	esac
99}
100
101function mmp_set_hostid
102{
103        typeset hostid=$1
104
105	case "$(uname)" in
106	Linux)
107	        a=${hostid:6:2}
108		b=${hostid:4:2}
109		c=${hostid:2:2}
110		d=${hostid:0:2}
111
112		printf "\\x$a\\x$b\\x$c\\x$d" >$HOSTID_FILE
113
114		if [ $(hostid) != "$hostid" ]; then
115			return 1
116		fi
117		;;
118	SunOS)
119		#
120		# Given a hostid in hex, we have to convert to decimal, then
121		# save the ascii string representation in the kernel. The
122		# 'hostid' command will get the decimal SI_HW_SERIAL value via
123		# sysinfo, then print that as an 8 digit hex number.
124		#
125		typeset dec=$(mdb -e "$hostid=E" | sed -e 's/ *//g')
126		typeset len=$(echo $dec | awk '{print length($0)}')
127		if [[ $len -lt 0 || $len -gt 10 ]]; then
128			return
129		fi
130		typeset pos=0
131		while [[ $pos -lt $len ]]; do
132			chr2ascii ${dec:$pos:1}
133			echo "hw_serial+${pos}/v $asc" | mdb -kw >/dev/null 2>&1
134			pos=$(($pos + 1))
135		done
136		echo "hw_serial+${pos}/v 0" | mdb -kw >/dev/null 2>&1
137		;;
138	esac
139
140        return 0
141}
142
143function mmp_clear_hostid
144{
145	case "$(uname)" in
146	Linux)	rm -f $HOSTID_FILE;;
147	SunOS)	mmp_set_hostid "00000000";;
148	esac
149}
150
151function mmp_pool_create_simple # pool dir
152{
153	typeset pool=${1:-$MMP_POOL}
154	typeset dir=${2:-$MMP_DIR}
155
156	log_must mkdir -p $dir
157	log_must rm -f $dir/*
158	log_must truncate -s $MINVDEVSIZE $dir/vdev1 $dir/vdev2
159
160	log_must mmp_set_hostid $HOSTID1
161	log_must zpool create -f -o cachefile=$MMP_CACHE $pool \
162	    mirror $dir/vdev1 $dir/vdev2
163	log_must zpool set multihost=on $pool
164}
165
166function mmp_pool_create # pool dir
167{
168	typeset pool=${1:-$MMP_POOL}
169	typeset dir=${2:-$MMP_DIR}
170	typeset opts="-VVVVV -T120 -M -k0 -f $dir -E -p $pool"
171
172	mmp_pool_create_simple $pool $dir
173
174	log_must mv $MMP_CACHE ${MMP_CACHE}.stale
175	log_must zpool export $pool
176	log_must mmp_set_hostid $HOSTID2
177
178	log_note "Starting ztest in the background as hostid $HOSTID1"
179	log_must eval "ZFS_HOSTID=$HOSTID1 /usr/bin/ztest $opts >$MMP_ZTEST_LOG 2>&1 &"
180
181	while ! is_pool_imported "$pool" "-d $dir"; do
182		log_must pgrep ztest
183		log_must sleep 5
184	done
185}
186
187function mmp_pool_destroy # pool dir
188{
189	typeset pool=${1:-$MMP_POOL}
190	typeset dir=${2:-$MMP_DIR}
191
192	ZTESTPID=$(pgrep ztest)
193	if [ -n "$ZTESTPID" ]; then
194		log_must kill $ZTESTPID
195		wait $ZTESTPID
196	fi
197
198	if poolexists $pool; then
199		destroy_pool $pool
200        fi
201
202	log_must rm -f $dir/*
203	mmp_clear_hostid
204}
205
206function mmp_pool_set_hostid # pool hostid
207{
208	typeset pool=$1
209	typeset hostid=$2
210
211	log_must mmp_set_hostid $hostid
212	log_must zpool export $pool
213	log_must zpool import $pool
214
215	return 0
216}
217
218# Return the number of seconds the activity check portion of the import process
219# will take.  Does not include the time to find devices and assemble the
220# preliminary pool configuration passed into the kernel.
221function seconds_mmp_waits_for_activity
222{
223	typeset import_intervals=$(get_tunable zfs_multihost_import_intervals)
224	typeset interval=$(get_tunable zfs_multihost_interval)
225	typeset seconds=$((interval*import_intervals/1000))
226
227	echo $seconds
228}
229
230function import_no_activity_check # pool opts
231{
232	typeset pool=$1
233	typeset opts=$2
234
235	typeset max_duration=$(seconds_mmp_waits_for_activity)
236
237	SECONDS=0
238	zpool import $opts $pool
239	typeset rc=$?
240
241	if [[ $SECONDS -gt $max_duration ]]; then
242		log_fail "unexpected activity check (${SECONDS}s gt \
243$max_duration)"
244	fi
245
246	return $rc
247}
248
249function import_activity_check # pool opts
250{
251	typeset pool=$1
252	typeset opts=$2
253
254	typeset min_duration=$(seconds_mmp_waits_for_activity)
255
256	SECONDS=0
257	zpool import $opts $pool
258	typeset rc=$?
259
260	if [[ $SECONDS -le $min_duration ]]; then
261		log_fail "expected activity check (${SECONDS}s le \
262$min_duration)"
263	fi
264
265	return $rc
266}
267
268function clear_mmp_history
269{
270	log_must set_tunable64 zfs_multihost_history $MMP_HISTORY_OFF
271	log_must set_tunable64 zfs_multihost_history $MMP_HISTORY
272}
273
274function count_skipped_mmp_writes # pool duration
275{
276	typeset pool=$1
277	typeset -i duration=$2
278	typeset hist_path="/proc/spl/kstat/zfs/$pool/multihost"
279
280	sleep $duration
281	awk 'BEGIN {count=0}; $NF == "-" {count++}; END {print count};' "$hist_path"
282}
283
284function count_mmp_writes # pool duration
285{
286	typeset pool=$1
287	typeset -i duration=$2
288	typeset hist_path="/proc/spl/kstat/zfs/$pool/multihost"
289
290	log_must sleep $duration
291	awk 'BEGIN {count=0}; $NF != "-" {count++}; END {print count};' "$hist_path"
292}
293