summaryrefslogtreecommitdiff
path: root/usr/src/test/zfs-tests/tests/functional/removal/removal.kshlib
blob: 05488572c79cf39abce43e921c97b005eeb16279 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#
# CDDL HEADER START
#
# This file and its contents are supplied under the terms of the
# Common Development and Distribution License ("CDDL"), version 1.0.
# You may only use this file in accordance with the terms of version
# 1.0 of the CDDL.
#
# A full copy of the text of the CDDL should have accompanied this
# source.  A copy of the CDDL is also available via the Internet at
# http://www.illumos.org/license/CDDL.
#
# CDDL HEADER END
#

#
# Copyright (c) 2014, 2017 by Delphix. All rights reserved.
#

export REMOVEDISK=${DISKS%% *}
export NOTREMOVEDISK=${DISKS##* }

#
# Waits for the pool to finish a removal.
#
function wait_for_removal # pool
{
	typeset pool=$1
	typeset callback=$2

	while is_pool_removing $pool; do
		sleep 1
	done

	#
	# The pool state changes before the TXG finishes syncing; wait for
	# the removal to be completed on disk.
	#
	sync

	log_must is_pool_removed $pool
	return 0
}

#
# Removes the specified disk from its respective pool and
# runs the callback while the removal is in progress.
#
# This function is mainly used to test how other operations
# interact with device removal. After the callback is done,
# the removal is unpaused and we wait for it to finish.
#
# Example usage:
#
#    attempt_during_removal $TESTPOOL $DISK dd if=/dev/urandom \
#        of=/$TESTPOOL/file count=1
#
function attempt_during_removal # pool disk callback [args]
{
	typeset pool=$1
	typeset disk=$2
	typeset callback=$3

	shift 3
	mdb_ctf_set_int zfs_removal_suspend_progress 0t1

	log_must zpool remove $pool $disk

	#
	# We want to make sure that the removal started
	# before issuing the callback.
	#
	sync
	log_must is_pool_removing $pool

	log_must $callback "$@"

	#
	# Ensure that we still haven't finished the removal
	# as expected.
	#
	log_must is_pool_removing $pool

	mdb_ctf_set_int zfs_removal_suspend_progress 0t0

	log_must wait_for_removal $pool
	log_mustnot vdevs_in_pool $pool $disk
	return 0
}

function indirect_vdev_mapping_size # pool
{
	typeset pool=$1
	zdb -P $pool | grep 'indirect vdev' | \
	    sed -E 's/.*\(([0-9]+) in memory\).*/\1/g'
}

function random_write # file write_size
{
	typeset file=$1
	typeset block_size=$2
	typeset file_size=$(stat -c%s $file 2>/dev/null)
	typeset nblocks=$((file_size / block_size))

	[[ -w $file ]] || return 1

	dd if=/dev/urandom of=$file conv=notrunc \
	    bs=$block_size count=1 seek=$((RANDOM % nblocks)) >/dev/null 2>&1
}

function start_random_writer # file
{
	typeset file=$1
	(
		log_note "Starting writer for $file"
		# This will fail when we destroy the pool.
		while random_write $file $((2**12)); do
			:
		done
		log_note "Stopping writer for $file"
	) &
}

function set_min_bytes # bytes
{
	typeset bytes=$1
	echo "zfs_condense_min_mapping_bytes/W 0t$bytes" | \
	    mdb -kw
}

function test_removal_with_operation # callback [args]
{
	#
	# To ensure that the removal takes a while, we fragment the pool
	# by writing random blocks and continue to do during the removal.
	#
	log_must mkfile 1g $TESTDIR/$TESTFILE0
	for i in $(seq $((2**10))); do
		random_write $TESTDIR/$TESTFILE0 $((2**12)) || \
		    log_fail "Could not write to $TESTDIR/$TESTFILE0."
	done
	start_random_writer $TESTDIR/$TESTFILE0 1g
	killpid=$!

	log_must attempt_during_removal $TESTPOOL $REMOVEDISK "$@"
	log_mustnot vdevs_in_pool $TESTPOOL $REMOVEDISK
	log_must zdb -cd $TESTPOOL

	kill $killpid
	wait
}