xref: /illumos-gate/usr/src/test/zfs-tests/tests/functional/removal/removal.kshlib (revision bf5d9f18edeb77c14df996d367853599bdd43fd1)
1#
2# CDDL HEADER START
3#
4# This file and its contents are supplied under the terms of the
5# Common Development and Distribution License ("CDDL"), version 1.0.
6# You may only use this file in accordance with the terms of version
7# 1.0 of the CDDL.
8#
9# A full copy of the text of the CDDL should have accompanied this
10# source.  A copy of the CDDL is also available via the Internet at
11# http://www.illumos.org/license/CDDL.
12#
13# CDDL HEADER END
14#
15
16#
17# Copyright (c) 2014, 2016 by Delphix. All rights reserved.
18#
19
20export REMOVEDISK=${DISKS%% *}
21export NOTREMOVEDISK=${DISKS##* }
22
23#
24# Waits for the pool to finish a removal. If an optional callback is given,
25# execute it every 0.5s.
26#
27# Example usage:
28#
29#    wait_for_removal $TESTPOOL dd if=/dev/urandom of=/$TESTPOOL/file count=1
30#
31function wait_for_removal # pool [callback args]
32{
33	typeset pool=$1
34	typeset callback=$2
35
36	[[ -n $callback ]] && shift 2
37
38	while is_pool_removing $pool; do
39		[[ -z $callback ]] || log_must $callback "$@"
40		sleep 0.5
41	done
42
43	#
44	# The pool state changes before the TXG finishes syncing; wait for
45	# the removal to be completed on disk.
46	#
47	sync
48
49	log_must is_pool_removed $pool
50	return 0
51}
52
53function indirect_vdev_mapping_size # pool
54{
55	typeset pool=$1
56	zdb -P $pool | grep 'indirect vdev' | \
57	    sed -E 's/.*\(([0-9]+) in memory\).*/\1/g'
58}
59
60function random_write # file write_size
61{
62	typeset file=$1
63	typeset block_size=$2
64	typeset file_size=$(stat -c%s $file 2>/dev/null)
65	typeset nblocks=$((file_size / block_size))
66
67	[[ -w $file ]] || return 1
68
69	dd if=/dev/urandom of=$file conv=notrunc \
70	    bs=$block_size count=1 seek=$((RANDOM % nblocks)) >/dev/null 2>&1
71}
72
73_test_removal_with_operation_count=0
74function _test_removal_with_operation_cb # real_callback
75{
76	typeset real_callback=$1
77
78	$real_callback $_test_removal_with_operation_count || \
79	    log_fail $real_callback "failed after" \
80		$_test_removal_with_operation_count "iterations"
81
82	(( _test_removal_with_operation_count++ ))
83
84	log_note "Callback called $((_test_removal_with_operation_count)) times"
85
86	return 0
87}
88
89function start_random_writer # file
90{
91	typeset file=$1
92	(
93		log_note "Starting writer for $file"
94		# This will fail when we destroy the pool.
95		while random_write $file $((2**12)); do
96			:
97		done
98		log_note "Stopping writer for $file"
99	) &
100}
101
102function set_min_bytes # bytes
103{
104	typeset bytes=$1
105	echo "zfs_condense_min_mapping_bytes/W 0t$bytes" | \
106	    mdb -kw
107}
108
109#
110# The callback should be a function that takes as input the number of
111# iterations and the given arguments.
112#
113function test_removal_with_operation # callback [count]
114{
115	typeset operation=$1
116	typeset count=$2
117
118	[[ -n $count ]] || count=0
119
120	#
121	# To ensure that the removal takes a while, we fragment the pool
122	# by writing random blocks and continue to do during the removal.
123	#
124	log_must mkfile 1g $TESTDIR/$TESTFILE0
125	for i in $(seq $((2**10))); do
126		random_write $TESTDIR/$TESTFILE0 $((2**12)) || \
127		    log_fail "Could not write to $TESTDIR/$TESTFILE0."
128	done
129	start_random_writer $TESTDIR/$TESTFILE0 1g
130	killpid=$!
131
132	log_must zpool remove $TESTPOOL $REMOVEDISK
133	log_must wait_for_removal $TESTPOOL \
134	    _test_removal_with_operation_cb $operation
135	log_mustnot vdevs_in_pool $TESTPOOL $REMOVEDISK
136	log_must zdb -cd $TESTPOOL
137
138	kill $killpid
139	wait
140
141	#
142	# We would love to assert that the callback happened *during* the
143	# removal, but we don't have the ability to be confident of that
144	# (via limiting bandwidth, etc.) yet. Instead, we try again.
145	#
146	if (( $_test_removal_with_operation_count <= 1 )); then
147		(( count <= 5 )) || log_fail "Attempted test too many times."
148
149		log_note "Callback only called" \
150		    $_test_removal_with_operation_count \
151		    "times, trying again."
152		default_setup_noexit "$DISKS"
153		test_removal_with_operation $operation $((count + 1))
154	fi
155}
156