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, 2017 by Delphix. All rights reserved.
18#
19
20export REMOVEDISK=${DISKS%% *}
21export NOTREMOVEDISK=${DISKS##* }
22
23#
24# Waits for the pool to finish a removal.
25#
26function wait_for_removal # pool
27{
28	typeset pool=$1
29	typeset callback=$2
30
31	log_must zpool wait -t remove $pool
32
33	#
34	# The pool state changes before the TXG finishes syncing; wait for
35	# the removal to be completed on disk.
36	#
37	sync_pool $pool
38
39	log_must is_pool_removed $pool
40	return 0
41}
42
43#
44# Removes the specified disk from its respective pool and
45# runs the callback while the removal is in progress.
46#
47# This function is mainly used to test how other operations
48# interact with device removal. After the callback is done,
49# the removal is unpaused and we wait for it to finish.
50#
51# Example usage:
52#
53#    attempt_during_removal $TESTPOOL $DISK dd if=/dev/urandom \
54#        of=/$TESTPOOL/file count=1
55#
56function attempt_during_removal # pool disk callback [args]
57{
58	typeset pool=$1
59	typeset disk=$2
60	typeset callback=$3
61
62	shift 3
63	log_onexit_push set_tunable32 REMOVAL_SUSPEND_PROGRESS 0
64	set_tunable32 REMOVAL_SUSPEND_PROGRESS 1
65
66	log_must zpool remove $pool $disk
67
68	#
69	# We want to make sure that the removal started
70	# before issuing the callback.
71	#
72	sync_pool $pool
73	log_must is_pool_removing $pool
74
75	log_must $callback "$@"
76
77	#
78	# Ensure that we still haven't finished the removal
79	# as expected.
80	#
81	log_must is_pool_removing $pool
82
83	set_tunable32 REMOVAL_SUSPEND_PROGRESS 0
84	log_onexit_pop
85
86	log_must wait_for_removal $pool
87	log_mustnot vdevs_in_pool $pool $disk
88	return 0
89}
90
91function random_write # file write_size
92{
93	typeset file=$1
94	typeset block_size=$2
95	typeset file_size=$(stat_size $file 2>/dev/null)
96	typeset nblocks=$((file_size / block_size))
97
98	[[ -w $file ]] || return 1
99
100	dd if=/dev/urandom of=$file conv=notrunc \
101	    bs=$block_size count=1 seek=$((RANDOM % nblocks)) >/dev/null 2>&1
102}
103
104function start_random_writer # file
105{
106	typeset file=$1
107	(
108		log_note "Starting writer for $file"
109		# This will fail when we destroy the pool.
110		while random_write $file $((2**12)); do
111			:
112		done
113		log_note "Stopping writer for $file"
114	) &
115}
116
117function test_removal_with_operation # callback [args]
118{
119	#
120	# To ensure that the removal takes a while, we fragment the pool
121	# by writing random blocks and continue to do during the removal.
122	#
123	log_must mkfile 1g $TESTDIR/$TESTFILE0
124	for i in $(seq $((2**10))); do
125		random_write $TESTDIR/$TESTFILE0 $((2**12)) || \
126		    log_fail "Could not write to $TESTDIR/$TESTFILE0."
127	done
128	start_random_writer $TESTDIR/$TESTFILE0 1g
129	killpid=$!
130
131	log_must attempt_during_removal $TESTPOOL $REMOVEDISK "$@"
132	log_mustnot vdevs_in_pool $TESTPOOL $REMOVEDISK
133	log_must zdb -cd $TESTPOOL
134
135	kill $killpid
136	wait
137
138	verify_pool $TESTPOOL
139}
140
141#
142# Kill the background job use by the test_removal_with_operation function.
143#
144function test_removal_with_operation_kill
145{
146	kill $killpid
147	wait $killpid
148	return 0
149}
150