1#!/bin/ksh -p
2# SPDX-License-Identifier: CDDL-1.0
3
4#
5# This file and its contents are supplied under the terms of the
6# Common Development and Distribution License ("CDDL"), version 1.0.
7# You may only use this file in accordance with the terms of version
8# 1.0 of the CDDL.
9#
10# A full copy of the text of the CDDL should have accompanied this
11# source.  A copy of the CDDL is also available via the Internet at
12# http://www.illumos.org/license/CDDL.
13#
14
15#
16# Copyright (c) 2017 by Delphix. All rights reserved.
17#
18
19. $STF_SUITE/tests/functional/cli_root/zpool_import/zpool_import.kshlib
20
21#
22# DESCRIPTION:
23#	It should be possible to rewind a pool beyond a configuration change.
24#
25# STRATEGY:
26#	1. Create a pool.
27#	2. Generate files and remember their hashsum.
28#	3. Note last synced txg.
29#	4. Take a snapshot to make sure old blocks are not overwritten.
30#	5. Perform zpool add/attach/detach/remove operation.
31#	6. Change device paths if requested and re-import pool.
32#	7. Checkpoint the pool as one last attempt to preserve old blocks.
33#	8. Overwrite the files.
34#	9. Export the pool.
35#	10. Verify that we can rewind the pool to the noted txg.
36#	11. Verify that the files are readable and retain their old data.
37#
38# DISCLAIMER:
39#	This test can fail since nothing guarantees that old MOS blocks aren't
40#	overwritten. Snapshots protect datasets and data files but not the MOS.
41#	sync_some_data_a_few_times interleaves file data and MOS data for a few
42#	txgs, thus increasing the odds that some txgs will have their MOS data
43#	left untouched.
44#
45
46verify_runnable "global"
47
48function custom_cleanup
49{
50	set_vdev_validate_skip 0
51	cleanup
52	log_must set_tunable64 VDEV_MIN_MS_COUNT 16
53}
54
55log_onexit custom_cleanup
56
57function test_common
58{
59	typeset poolcreate="$1"
60	typeset addvdevs="$2"
61	typeset attachargs="${3:-}"
62	typeset detachvdev="${4:-}"
63	typeset removevdev="${5:-}"
64	typeset finalpool="${6:-}"
65	typeset retval=1
66
67	typeset poolcheck="$poolcreate"
68
69	log_must zpool create $TESTPOOL1 $poolcreate
70
71	log_must generate_data $TESTPOOL1 $MD5FILE
72
73	# syncing a few times while writing new data increases the odds that MOS
74	# metadata for some of the txgs will survive
75	log_must sync_some_data_a_few_times $TESTPOOL1
76	typeset txg
77	txg=$(get_last_txg_synced $TESTPOOL1)
78	log_must zfs snapshot -r $TESTPOOL1@snap1
79
80	#
81	# Perform config change operations
82	#
83	if [[ -n $addvdevs ]]; then
84		log_must zpool add -f $TESTPOOL1 $addvdevs
85	fi
86	if [[ -n $attachargs ]]; then
87		log_must zpool attach $TESTPOOL1 $attachargs
88	fi
89	if [[ -n $detachvdev ]]; then
90		log_must zpool detach $TESTPOOL1 $detachvdev
91	fi
92	if [[ -n $removevdev ]]; then
93		[[ -z $finalpool ]] &&
94		    log_fail "Must provide final pool status!"
95		log_must zpool remove $TESTPOOL1 $removevdev
96		log_must wait_for_pool_config $TESTPOOL1 "$finalpool"
97	fi
98	if [[ -n $pathstochange ]]; then
99		#
100		# Change device paths and re-import pool to update labels
101		#
102		zpool export $TESTPOOL1
103		for dev in $pathstochange; do
104			log_must mv $dev "${dev}_new"
105			poolcheck=$(echo "$poolcheck" | \
106			    sed "s:$dev:${dev}_new:g")
107		done
108		zpool import -d $DEVICE_DIR $TESTPOOL1
109	fi
110
111	#
112	# In an attempt to leave MOS data untouched so extreme
113	# rewind is successful during import we checkpoint the
114	# pool and hope that these MOS data are part of the
115	# checkpoint (e.g they stay around). If this goes as
116	# expected, then extreme rewind should rewind back even
117	# further than the time that we took the checkpoint.
118	#
119	# Note that, ideally we would want to take a checkpoint
120	# right after we record the txg we plan to rewind to.
121	# But since we can't attach, detach or remove devices
122	# while having a checkpoint, we take it after the
123	# operation that changes the config.
124	#
125	# However, it is possible the MOS data was overwritten
126	# in which case the pool will either be unimportable, or
127	# may have been rewound prior to the data being written.
128	# In which case an error is returned and test_common()
129	# is retried by the caller to minimize false positives.
130	#
131	log_must zpool checkpoint $TESTPOOL1
132
133	log_must overwrite_data $TESTPOOL1 ""
134
135	log_must zpool export $TESTPOOL1
136
137	if zpool import -d $DEVICE_DIR -T $txg $TESTPOOL1; then
138		verify_data_hashsums $MD5FILE && retval=0
139
140		log_must check_pool_config $TESTPOOL1 "$poolcheck"
141		log_must zpool destroy $TESTPOOL1
142	fi
143
144	# Cleanup
145	if [[ -n $pathstochange ]]; then
146		for dev in $pathstochange; do
147			log_must mv "${dev}_new" $dev
148		done
149	fi
150	# Fast way to clear vdev labels
151	log_must zpool create -f $TESTPOOL2 $VDEV0 $VDEV1 $VDEV2 $VDEV3 $VDEV4
152	log_must zpool destroy $TESTPOOL2
153
154	log_note ""
155	return $retval
156}
157
158function test_add_vdevs
159{
160	typeset poolcreate="$1"
161	typeset addvdevs="$2"
162
163	log_note "$0: pool '$poolcreate', add $addvdevs."
164
165	for retry in $(seq 1 5); do
166		test_common "$poolcreate" "$addvdevs" && return
167		log_note "Retry $retry / 5 for test_add_vdevs()"
168	done
169
170	log_fail "Exhausted all 5 retries for test_add_vdevs()"
171}
172
173function test_attach_vdev
174{
175	typeset poolcreate="$1"
176	typeset attachto="$2"
177	typeset attachvdev="$3"
178
179	log_note "$0: pool '$poolcreate', attach $attachvdev to $attachto."
180
181	for retry in $(seq 1 5); do
182		test_common "$poolcreate" "" "$attachto $attachvdev" && return
183		log_note "Retry $retry / 5 for test_attach_vdev()"
184	done
185
186	log_fail "Exhausted all 5 retries for test_attach_vdev()"
187}
188
189function test_detach_vdev
190{
191	typeset poolcreate="$1"
192	typeset detachvdev="$2"
193
194	log_note "$0: pool '$poolcreate', detach $detachvdev."
195
196	for retry in $(seq 1 5); do
197		test_common "$poolcreate" "" "" "$detachvdev" && return
198		log_note "Retry $retry / 5 for test_detach_vdev()"
199	done
200
201	log_fail "Exhausted all 5 retries for test_detach_vdev()"
202}
203
204function test_attach_detach_vdev
205{
206	typeset poolcreate="$1"
207	typeset attachto="$2"
208	typeset attachvdev="$3"
209	typeset detachvdev="$4"
210
211	log_note "$0: pool '$poolcreate', attach $attachvdev to $attachto," \
212	    "then detach $detachvdev."
213
214	for retry in $(seq 1 5); do
215		test_common "$poolcreate" "" "$attachto $attachvdev" \
216		    "$detachvdev" && return
217		log_note "Retry $retry / 5 for test_attach_detach_vdev()"
218	done
219
220	log_fail "Exhausted all 5 retries for test_attach_detach_vdev()"
221}
222
223function test_remove_vdev
224{
225	typeset poolcreate="$1"
226	typeset removevdev="$2"
227	typeset finalpool="$3"
228
229	log_note "$0: pool '$poolcreate', remove $removevdev."
230
231	for retry in $(seq 1 5); do
232		test_common "$poolcreate" "" "" "" "$removevdev" \
233		    "$finalpool" && return
234		log_note "Retry $retry / 5 for test_remove_vdev()"
235	done
236
237	log_fail "Exhausted all 5 retries for test_remove_vdev()"
238}
239
240# Record txg history
241is_linux && log_must set_tunable32 TXG_HISTORY 100
242
243# Make the devices bigger to reduce chances of overwriting MOS metadata.
244increase_device_sizes $(( FILE_SIZE * 4 ))
245
246# Increase the number of metaslabs for small pools temporarily to
247# reduce the chance of reusing a metaslab that holds old MOS metadata.
248log_must set_tunable64 VDEV_MIN_MS_COUNT 150
249
250# Part of the rewind test is to see how it reacts to path changes
251typeset pathstochange="$VDEV0 $VDEV1 $VDEV2 $VDEV3"
252
253log_note " == test rewind after device addition == "
254
255test_add_vdevs "$VDEV0" "$VDEV1"
256test_add_vdevs "$VDEV0 $VDEV1" "$VDEV2"
257test_add_vdevs "$VDEV0" "$VDEV1 $VDEV2"
258test_add_vdevs "mirror $VDEV0 $VDEV1" "mirror $VDEV2 $VDEV3"
259test_add_vdevs "$VDEV0" "raidz $VDEV1 $VDEV2 $VDEV3"
260test_add_vdevs "$VDEV0" "draid $VDEV1 $VDEV2 $VDEV3"
261test_add_vdevs "$VDEV0" "log $VDEV1"
262test_add_vdevs "$VDEV0 log $VDEV1" "$VDEV2"
263
264log_note " == test rewind after device attach == "
265
266test_attach_vdev "$VDEV0" "$VDEV0" "$VDEV1"
267test_attach_vdev "mirror $VDEV0 $VDEV1" "$VDEV0" "$VDEV2"
268test_attach_vdev "$VDEV0 $VDEV1" "$VDEV0" "$VDEV2"
269
270log_note " == test rewind after device removal == "
271
272# Once we remove a device it will be overlooked in the device scan, so we must
273# preserve its original path
274pathstochange="$VDEV0 $VDEV2"
275test_remove_vdev "$VDEV0 $VDEV1 $VDEV2" "$VDEV1" "$VDEV0 $VDEV2"
276
277#
278# Path change and detach are incompatible. Detach changes the guid of the vdev
279# so we have no direct way to link the new path to an existing vdev.
280#
281pathstochange=""
282
283log_note " == test rewind after device detach == "
284
285test_detach_vdev "mirror $VDEV0 $VDEV1" "$VDEV1"
286test_detach_vdev "mirror $VDEV0 $VDEV1 mirror $VDEV2 $VDEV3" "$VDEV1"
287test_detach_vdev "$VDEV0 log mirror $VDEV1 $VDEV2" "$VDEV2"
288
289log_note " == test rewind after device attach followed by device detach == "
290
291#
292# We need to disable vdev validation since once we detach VDEV1, VDEV0 will
293# inherit the mirror tvd's guid and lose its original guid.
294#
295set_vdev_validate_skip 1
296test_attach_detach_vdev "$VDEV0" "$VDEV0" "$VDEV1" "$VDEV1"
297set_vdev_validate_skip 0
298
299log_pass "zpool import rewind after configuration change passed."
300