xref: /freebsd-src/sys/contrib/openzfs/tests/zfs-tests/include/blkdev.shlib (revision dd21556857e8d40f66bf5ad54754d9d52669ebf7)
1eda14cbcSMatt Macy#
2eda14cbcSMatt Macy# This file and its contents are supplied under the terms of the
3eda14cbcSMatt Macy# Common Development and Distribution License ("CDDL"), version 1.0.
4eda14cbcSMatt Macy# You may only use this file in accordance with the terms of version
5eda14cbcSMatt Macy# 1.0 of the CDDL.
6eda14cbcSMatt Macy#
7eda14cbcSMatt Macy# A full copy of the text of the CDDL should have accompanied this
8eda14cbcSMatt Macy# source.  A copy of the CDDL is also available via the Internet at
9eda14cbcSMatt Macy# http://www.illumos.org/license/CDDL.
10eda14cbcSMatt Macy#
11eda14cbcSMatt Macy
12eda14cbcSMatt Macy#
13eda14cbcSMatt Macy# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
14eda14cbcSMatt Macy# Use is subject to license terms.
15eda14cbcSMatt Macy# Copyright (c) 2012, 2019 by Delphix. All rights reserved.
16eda14cbcSMatt Macy# Copyright 2016 Nexenta Systems, Inc.
17eda14cbcSMatt Macy# Copyright (c) 2016, 2017 by Intel Corporation. All rights reserved.
18eda14cbcSMatt Macy# Copyright (c) 2017 Lawrence Livermore National Security, LLC.
19eda14cbcSMatt Macy# Copyright (c) 2017 Datto Inc.
20eda14cbcSMatt Macy# Copyright (c) 2017 Open-E, Inc. All Rights Reserved.
21eda14cbcSMatt Macy# Copyright 2019 Richard Elling
22eda14cbcSMatt Macy#
23eda14cbcSMatt Macy
24eda14cbcSMatt Macy#
25eda14cbcSMatt Macy# Returns SCSI host number for the given disk
26eda14cbcSMatt Macy#
27eda14cbcSMatt Macyfunction get_scsi_host #disk
28eda14cbcSMatt Macy{
29eda14cbcSMatt Macy	typeset disk=$1
30eda14cbcSMatt Macy	ls /sys/block/${disk}/device/scsi_device | cut -d : -f 1
31eda14cbcSMatt Macy}
32eda14cbcSMatt Macy
33eda14cbcSMatt Macy#
34eda14cbcSMatt Macy# Cause a scan of all scsi host adapters by default
35eda14cbcSMatt Macy#
36eda14cbcSMatt Macy# $1 optional host number
37eda14cbcSMatt Macy#
38eda14cbcSMatt Macyfunction scan_scsi_hosts
39eda14cbcSMatt Macy{
40eda14cbcSMatt Macy	typeset hostnum=${1}
41eda14cbcSMatt Macy
42eda14cbcSMatt Macy	if is_linux; then
43eda14cbcSMatt Macy		if [[ -z $hostnum ]]; then
44eda14cbcSMatt Macy			for host in /sys/class/scsi_host/host*; do
45eda14cbcSMatt Macy				log_must eval "echo '- - -' > $host/scan"
46eda14cbcSMatt Macy			done
47eda14cbcSMatt Macy		else
48716fd348SMartin Matuska			log_note "/sys/class/scsi_host/host$hostnum/scan"
49eda14cbcSMatt Macy			log_must eval \
50eda14cbcSMatt Macy			    "echo '- - -' > /sys/class/scsi_host/host$hostnum/scan"
51eda14cbcSMatt Macy		fi
52eda14cbcSMatt Macy	fi
53eda14cbcSMatt Macy}
54eda14cbcSMatt Macy
55eda14cbcSMatt Macy#
56eda14cbcSMatt Macy# Wait for newly created block devices to have their minors created.
57eda14cbcSMatt Macy# Additional arguments can be passed to udevadm trigger, with the expected
58eda14cbcSMatt Macy# arguments to typically be a block device pathname. This is useful when
59eda14cbcSMatt Macy# checking waiting on a specific device to settle rather than triggering
60eda14cbcSMatt Macy# all devices and waiting for them all to settle.
61eda14cbcSMatt Macy#
62eda14cbcSMatt Macy# The udevadm settle timeout can be 120 or 180 seconds by default for
63eda14cbcSMatt Macy# some distros. If a long delay is experienced, it could be due to some
64eda14cbcSMatt Macy# strangeness in a malfunctioning device that isn't related to the devices
65eda14cbcSMatt Macy# under test. To help debug this condition, a notice is given if settle takes
66eda14cbcSMatt Macy# too long.
67eda14cbcSMatt Macy#
68eda14cbcSMatt Macy# Note: there is no meaningful return code if udevadm fails. Consumers
69eda14cbcSMatt Macy# should not expect a return code (do not call as argument to log_must)
70eda14cbcSMatt Macy#
71eda14cbcSMatt Macyfunction block_device_wait
72eda14cbcSMatt Macy{
73eda14cbcSMatt Macy	if is_linux; then
746ba2210eSMartin Matuska		udevadm trigger $* 2>/dev/null
75eda14cbcSMatt Macy		typeset start=$SECONDS
76eda14cbcSMatt Macy		udevadm settle
77eda14cbcSMatt Macy		typeset elapsed=$((SECONDS - start))
78eda14cbcSMatt Macy		[[ $elapsed > 60 ]] && \
79eda14cbcSMatt Macy		    log_note udevadm settle time too long: $elapsed
80eda14cbcSMatt Macy	elif is_freebsd; then
81eda14cbcSMatt Macy		if [[ ${#@} -eq 0 ]]; then
82eda14cbcSMatt Macy			# Do something that has to go through the geom event
83eda14cbcSMatt Macy			# queue to complete.
84eda14cbcSMatt Macy			sysctl kern.geom.conftxt >/dev/null
85eda14cbcSMatt Macy			return
86eda14cbcSMatt Macy		fi
87eda14cbcSMatt Macy	fi
88eda14cbcSMatt Macy	# Poll for the given paths to appear, but give up eventually.
89eda14cbcSMatt Macy	typeset -i i
90eda14cbcSMatt Macy	for (( i = 0; i < 5; ++i )); do
91eda14cbcSMatt Macy		typeset missing=false
92eda14cbcSMatt Macy		typeset dev
93eda14cbcSMatt Macy		for dev in "${@}"; do
9421b492edSMartin Matuska			if ! [[ -e $dev ]]; then
95eda14cbcSMatt Macy				missing=true
96eda14cbcSMatt Macy				break
97eda14cbcSMatt Macy			fi
98eda14cbcSMatt Macy		done
99eda14cbcSMatt Macy		if ! $missing; then
100eda14cbcSMatt Macy			break
101eda14cbcSMatt Macy		fi
102eda14cbcSMatt Macy		sleep ${#@}
103eda14cbcSMatt Macy	done
104eda14cbcSMatt Macy}
105eda14cbcSMatt Macy
106eda14cbcSMatt Macy#
107eda14cbcSMatt Macy# Check if the given device is physical device
108eda14cbcSMatt Macy#
109eda14cbcSMatt Macyfunction is_physical_device #device
110eda14cbcSMatt Macy{
111eda14cbcSMatt Macy	typeset device=${1#$DEV_DSKDIR/}
112eda14cbcSMatt Macy	device=${device#$DEV_RDSKDIR/}
113eda14cbcSMatt Macy
114eda14cbcSMatt Macy	if is_linux; then
115eda14cbcSMatt Macy		is_disk_device "$DEV_DSKDIR/$device" && \
116716fd348SMartin Matuska		[ -f /sys/module/loop/parameters/max_part ]
117eda14cbcSMatt Macy	elif is_freebsd; then
118eda14cbcSMatt Macy		is_disk_device "$DEV_DSKDIR/$device" && \
119716fd348SMartin Matuska		echo $device | grep -qE \
120eda14cbcSMatt Macy		    -e '^a?da[0-9]+$' \
121eda14cbcSMatt Macy		    -e '^md[0-9]+$' \
122eda14cbcSMatt Macy		    -e '^mfid[0-9]+$' \
123eda14cbcSMatt Macy		    -e '^nda[0-9]+$' \
124eda14cbcSMatt Macy		    -e '^nvd[0-9]+$' \
125eda14cbcSMatt Macy		    -e '^vtbd[0-9]+$'
126eda14cbcSMatt Macy	else
127716fd348SMartin Matuska		echo $device | grep -qE "^c[0-F]+([td][0-F]+)+$"
128eda14cbcSMatt Macy	fi
129eda14cbcSMatt Macy}
130eda14cbcSMatt Macy
131eda14cbcSMatt Macy#
132eda14cbcSMatt Macy# Check if the given device is a real device (ie SCSI device)
133eda14cbcSMatt Macy#
134eda14cbcSMatt Macyfunction is_real_device #disk
135eda14cbcSMatt Macy{
136eda14cbcSMatt Macy	typeset disk=$1
137eda14cbcSMatt Macy	[[ -z $disk ]] && log_fail "No argument for disk given."
138eda14cbcSMatt Macy
139eda14cbcSMatt Macy	if is_linux; then
140eda14cbcSMatt Macy		lsblk $DEV_RDSKDIR/$disk -o TYPE 2>/dev/null | \
141716fd348SMartin Matuska		    grep -q disk
142eda14cbcSMatt Macy	fi
143eda14cbcSMatt Macy}
144eda14cbcSMatt Macy
145eda14cbcSMatt Macy#
146eda14cbcSMatt Macy# Check if the given device is a loop device
147eda14cbcSMatt Macy#
148eda14cbcSMatt Macyfunction is_loop_device #disk
149eda14cbcSMatt Macy{
150eda14cbcSMatt Macy	typeset disk=$1
151eda14cbcSMatt Macy	[[ -z $disk ]] && log_fail "No argument for disk given."
152eda14cbcSMatt Macy
153eda14cbcSMatt Macy	if is_linux; then
154eda14cbcSMatt Macy		lsblk $DEV_RDSKDIR/$disk -o TYPE 2>/dev/null | \
155716fd348SMartin Matuska		    grep -q loop
156eda14cbcSMatt Macy	fi
157eda14cbcSMatt Macy}
158eda14cbcSMatt Macy
159eda14cbcSMatt Macy#
160eda14cbcSMatt Macy# Linux:
161eda14cbcSMatt Macy# Check if the given device is a multipath device and if there is a symbolic
162eda14cbcSMatt Macy# link to a device mapper and to a disk
163eda14cbcSMatt Macy# Currently no support for dm devices alone without multipath
164eda14cbcSMatt Macy#
165eda14cbcSMatt Macy# FreeBSD:
166eda14cbcSMatt Macy# Check if the given device is a gmultipath device.
167eda14cbcSMatt Macy#
168eda14cbcSMatt Macy# Others:
169eda14cbcSMatt Macy# No multipath detection.
170eda14cbcSMatt Macy#
171eda14cbcSMatt Macyfunction is_mpath_device #disk
172eda14cbcSMatt Macy{
173eda14cbcSMatt Macy	typeset disk=$1
174eda14cbcSMatt Macy	[[ -z $disk ]] && log_fail "No argument for disk given."
175eda14cbcSMatt Macy
176eda14cbcSMatt Macy	if is_linux; then
177716fd348SMartin Matuska		if lsblk $DEV_MPATHDIR/$disk -o TYPE 2>/dev/null | \
178716fd348SMartin Matuska		   grep -q mpath; then
179eda14cbcSMatt Macy			readlink $DEV_MPATHDIR/$disk > /dev/null 2>&1
180eda14cbcSMatt Macy		else
181716fd348SMartin Matuska			false
182eda14cbcSMatt Macy		fi
183eda14cbcSMatt Macy	elif is_freebsd; then
184eda14cbcSMatt Macy		is_disk_device $DEV_MPATHDIR/$disk
185eda14cbcSMatt Macy	else
186eda14cbcSMatt Macy		false
187eda14cbcSMatt Macy	fi
188eda14cbcSMatt Macy}
189eda14cbcSMatt Macy
190eda14cbcSMatt Macy#
191eda14cbcSMatt Macy# Check if the given path is the appropriate sort of device special node.
192eda14cbcSMatt Macy#
193eda14cbcSMatt Macyfunction is_disk_device #path
194eda14cbcSMatt Macy{
195eda14cbcSMatt Macy	typeset path=$1
196eda14cbcSMatt Macy
197eda14cbcSMatt Macy	if is_freebsd; then
198eda14cbcSMatt Macy		# FreeBSD doesn't have block devices, only character devices.
199eda14cbcSMatt Macy		test -c $path
200eda14cbcSMatt Macy	else
201eda14cbcSMatt Macy		test -b $path
202eda14cbcSMatt Macy	fi
203eda14cbcSMatt Macy}
204eda14cbcSMatt Macy
205eda14cbcSMatt Macy# Set the slice prefix for disk partitioning depending
206eda14cbcSMatt Macy# on whether the device is a real, multipath, or loop device.
207eda14cbcSMatt Macy# Currently all disks have to be of the same type, so only
208eda14cbcSMatt Macy# checks first disk to determine slice prefix.
209eda14cbcSMatt Macy#
210eda14cbcSMatt Macyfunction set_slice_prefix
211eda14cbcSMatt Macy{
212eda14cbcSMatt Macy	typeset disk
213eda14cbcSMatt Macy	typeset -i i=0
214eda14cbcSMatt Macy
215eda14cbcSMatt Macy	if is_linux; then
216eda14cbcSMatt Macy		while (( i < $DISK_ARRAY_NUM )); do
217716fd348SMartin Matuska			disk="$(echo $DISKS | awk '{print $(i + 1)}')"
218716fd348SMartin Matuska			if is_mpath_device $disk && ! echo $disk | awk 'substr($1,18,1) ~ /^[[:digit:]]+$/ {exit 1}' || is_real_device $disk; then
219eda14cbcSMatt Macy				export SLICE_PREFIX=""
220eda14cbcSMatt Macy				return 0
221716fd348SMartin Matuska			elif is_mpath_device $disk || is_loop_device $disk; then
222eda14cbcSMatt Macy				export SLICE_PREFIX="p"
223eda14cbcSMatt Macy				return 0
224eda14cbcSMatt Macy			else
225eda14cbcSMatt Macy				log_fail "$disk not supported for partitioning."
226eda14cbcSMatt Macy			fi
227eda14cbcSMatt Macy			(( i = i + 1))
228eda14cbcSMatt Macy		done
229eda14cbcSMatt Macy	fi
230eda14cbcSMatt Macy}
231eda14cbcSMatt Macy
232eda14cbcSMatt Macy#
233eda14cbcSMatt Macy# Set the directory path of the listed devices in $DISK_ARRAY_NUM
234eda14cbcSMatt Macy# Currently all disks have to be of the same type, so only
235eda14cbcSMatt Macy# checks first disk to determine device directory
236eda14cbcSMatt Macy# default = /dev (linux)
237eda14cbcSMatt Macy# real disk = /dev (linux)
238eda14cbcSMatt Macy# multipath device = /dev/mapper (linux)
239eda14cbcSMatt Macy#
240eda14cbcSMatt Macyfunction set_device_dir
241eda14cbcSMatt Macy{
242eda14cbcSMatt Macy	typeset disk
243eda14cbcSMatt Macy	typeset -i i=0
244eda14cbcSMatt Macy
245eda14cbcSMatt Macy	if is_linux; then
246eda14cbcSMatt Macy		while (( i < $DISK_ARRAY_NUM )); do
247716fd348SMartin Matuska			disk="$(echo $DISKS | awk '{print $(i + 1)}')"
248eda14cbcSMatt Macy			if is_mpath_device $disk; then
249eda14cbcSMatt Macy				export DEV_DSKDIR=$DEV_MPATHDIR
250eda14cbcSMatt Macy				return 0
251eda14cbcSMatt Macy			else
252eda14cbcSMatt Macy				export DEV_DSKDIR=$DEV_RDSKDIR
253eda14cbcSMatt Macy				return 0
254eda14cbcSMatt Macy			fi
255eda14cbcSMatt Macy			(( i = i + 1))
256eda14cbcSMatt Macy		done
257eda14cbcSMatt Macy	else
258eda14cbcSMatt Macy		export DEV_DSKDIR=$DEV_RDSKDIR
259eda14cbcSMatt Macy	fi
260eda14cbcSMatt Macy}
261eda14cbcSMatt Macy
262eda14cbcSMatt Macy#
263eda14cbcSMatt Macy# Get the directory path of given device
264eda14cbcSMatt Macy#
265eda14cbcSMatt Macyfunction get_device_dir #device
266eda14cbcSMatt Macy{
267eda14cbcSMatt Macy	typeset device=$1
268eda14cbcSMatt Macy
269eda14cbcSMatt Macy	if ! is_freebsd && ! is_physical_device $device; then
270eda14cbcSMatt Macy		if [[ $device != "/" ]]; then
271eda14cbcSMatt Macy			device=${device%/*}
272eda14cbcSMatt Macy		fi
273eda14cbcSMatt Macy		if is_disk_device "$DEV_DSKDIR/$device"; then
274eda14cbcSMatt Macy			device="$DEV_DSKDIR"
275eda14cbcSMatt Macy		fi
276eda14cbcSMatt Macy		echo $device
277eda14cbcSMatt Macy	else
278eda14cbcSMatt Macy		echo "$DEV_DSKDIR"
279eda14cbcSMatt Macy	fi
280eda14cbcSMatt Macy}
281eda14cbcSMatt Macy
282eda14cbcSMatt Macy#
283eda14cbcSMatt Macy# Get persistent name for given disk
284eda14cbcSMatt Macy#
285eda14cbcSMatt Macyfunction get_persistent_disk_name #device
286eda14cbcSMatt Macy{
287eda14cbcSMatt Macy	typeset device=$1
288eda14cbcSMatt Macy
289eda14cbcSMatt Macy	if is_linux; then
290eda14cbcSMatt Macy		if is_real_device $device; then
291716fd348SMartin Matuska			udevadm info -q all -n $DEV_DSKDIR/$device \
292525fe93dSMartin Matuska			    | awk '/disk\/by-id/ {print $2; exit}' | cut -d/ -f3-
293eda14cbcSMatt Macy		elif is_mpath_device $device; then
294716fd348SMartin Matuska			udevadm info -q all -n $DEV_DSKDIR/$device \
295716fd348SMartin Matuska			    | awk '/disk\/by-id\/dm-uuid/ {print $2; exit}' \
296716fd348SMartin Matuska			    | cut -d/ -f3
297eda14cbcSMatt Macy		else
298eda14cbcSMatt Macy			echo $device
299eda14cbcSMatt Macy		fi
300eda14cbcSMatt Macy	else
301eda14cbcSMatt Macy		echo $device
302eda14cbcSMatt Macy	fi
303eda14cbcSMatt Macy}
304eda14cbcSMatt Macy
305eda14cbcSMatt Macy#
306eda14cbcSMatt Macy# Online or offline a disk on the system
307eda14cbcSMatt Macy#
308eda14cbcSMatt Macy# First checks state of disk. Test will fail if disk is not properly onlined
309eda14cbcSMatt Macy# or offlined. Online is a full rescan of SCSI disks by echoing to every
310eda14cbcSMatt Macy# host entry.
311eda14cbcSMatt Macy#
312eda14cbcSMatt Macyfunction on_off_disk # disk state{online,offline} host
313eda14cbcSMatt Macy{
314eda14cbcSMatt Macy	typeset disk=$1
315eda14cbcSMatt Macy	typeset state=$2
316eda14cbcSMatt Macy	typeset host=$3
317eda14cbcSMatt Macy
318eda14cbcSMatt Macy	[[ -z $disk ]] || [[ -z $state ]] &&  \
319eda14cbcSMatt Macy	    log_fail "Arguments invalid or missing"
320eda14cbcSMatt Macy
321eda14cbcSMatt Macy	if is_linux; then
322eda14cbcSMatt Macy		if [[ $state == "offline" ]] && ( is_mpath_device $disk ); then
323716fd348SMartin Matuska			dm_name="$(readlink $DEV_DSKDIR/$disk | cut -d/ -f2)"
324716fd348SMartin Matuska			dep="$(ls /sys/block/${dm_name}/slaves | awk '{print $1}')"
325eda14cbcSMatt Macy			while [[ -n $dep ]]; do
326eda14cbcSMatt Macy				#check if disk is online
327716fd348SMartin Matuska				if lsscsi | grep -qF $dep; then
328eda14cbcSMatt Macy					dep_dir="/sys/block/${dm_name}"
329eda14cbcSMatt Macy					dep_dir+="/slaves/${dep}/device"
330eda14cbcSMatt Macy					ss="${dep_dir}/state"
331eda14cbcSMatt Macy					sd="${dep_dir}/delete"
332eda14cbcSMatt Macy					log_must eval "echo 'offline' > ${ss}"
333eda14cbcSMatt Macy					log_must eval "echo '1' > ${sd}"
334716fd348SMartin Matuska					if lsscsi | grep -qF $dep; then
335716fd348SMartin Matuska						log_fail "Offlining $disk failed"
336eda14cbcSMatt Macy					fi
337eda14cbcSMatt Macy				fi
338716fd348SMartin Matuska				dep="$(ls /sys/block/$dm_name/slaves 2>/dev/null | awk '{print $1}')"
339eda14cbcSMatt Macy			done
340eda14cbcSMatt Macy		elif [[ $state == "offline" ]] && ( is_real_device $disk ); then
341eda14cbcSMatt Macy			#check if disk is online
342716fd348SMartin Matuska			if lsscsi | grep -qF $disk; then
343eda14cbcSMatt Macy				dev_state="/sys/block/$disk/device/state"
344eda14cbcSMatt Macy				dev_delete="/sys/block/$disk/device/delete"
345eda14cbcSMatt Macy				log_must eval "echo 'offline' > ${dev_state}"
346eda14cbcSMatt Macy				log_must eval "echo '1' > ${dev_delete}"
347716fd348SMartin Matuska				if lsscsi | grep -qF $disk; then
348716fd348SMartin Matuska					log_fail "Offlining $disk failed"
349eda14cbcSMatt Macy				fi
350eda14cbcSMatt Macy			else
351eda14cbcSMatt Macy				log_note "$disk is already offline"
352eda14cbcSMatt Macy			fi
353eda14cbcSMatt Macy		elif [[ $state == "online" ]]; then
354eda14cbcSMatt Macy			#force a full rescan
355eda14cbcSMatt Macy			scan_scsi_hosts $host
356eda14cbcSMatt Macy			block_device_wait
357eda14cbcSMatt Macy			if is_mpath_device $disk; then
358716fd348SMartin Matuska				dm_name="$(readlink $DEV_DSKDIR/$disk | cut -d/ -f2)"
359716fd348SMartin Matuska				dep="$(ls /sys/block/$dm_name/slaves | awk '{print $1}')"
360716fd348SMartin Matuska				if lsscsi | grep -qF $dep; then
361eda14cbcSMatt Macy					log_fail "Onlining $disk failed"
362eda14cbcSMatt Macy				fi
363eda14cbcSMatt Macy			elif is_real_device $disk; then
364eda14cbcSMatt Macy				block_device_wait
365eda14cbcSMatt Macy				typeset -i retries=0
366716fd348SMartin Matuska				while ! lsscsi | grep -qF $disk; do
367eda14cbcSMatt Macy					if (( $retries > 2 )); then
368eda14cbcSMatt Macy						log_fail "Onlining $disk failed"
369eda14cbcSMatt Macy						break
370eda14cbcSMatt Macy					fi
371eda14cbcSMatt Macy					(( ++retries ))
372eda14cbcSMatt Macy					sleep 1
373eda14cbcSMatt Macy				done
374eda14cbcSMatt Macy			else
375eda14cbcSMatt Macy				log_fail "$disk is not a real dev"
376eda14cbcSMatt Macy			fi
377eda14cbcSMatt Macy		else
378eda14cbcSMatt Macy			log_fail "$disk failed to $state"
379eda14cbcSMatt Macy		fi
380eda14cbcSMatt Macy	fi
381eda14cbcSMatt Macy}
382eda14cbcSMatt Macy
383eda14cbcSMatt Macy#
384eda14cbcSMatt Macy# Simulate disk removal
385eda14cbcSMatt Macy#
386eda14cbcSMatt Macyfunction remove_disk #disk
387eda14cbcSMatt Macy{
388eda14cbcSMatt Macy	typeset disk=$1
389eda14cbcSMatt Macy	on_off_disk $disk "offline"
390eda14cbcSMatt Macy	block_device_wait
391eda14cbcSMatt Macy}
392eda14cbcSMatt Macy
393eda14cbcSMatt Macy#
394eda14cbcSMatt Macy# Simulate disk insertion for the given SCSI host
395eda14cbcSMatt Macy#
396eda14cbcSMatt Macyfunction insert_disk #disk scsi_host
397eda14cbcSMatt Macy{
398eda14cbcSMatt Macy	typeset disk=$1
399eda14cbcSMatt Macy	typeset scsi_host=$2
400eda14cbcSMatt Macy	on_off_disk $disk "online" $scsi_host
401eda14cbcSMatt Macy	block_device_wait
402eda14cbcSMatt Macy}
403eda14cbcSMatt Macy
404eda14cbcSMatt Macy#
405eda14cbcSMatt Macy# Load scsi_debug module with specified parameters
406eda14cbcSMatt Macy# $blksz can be either one of: < 512b | 512e | 4Kn >
407eda14cbcSMatt Macy#
408eda14cbcSMatt Macyfunction load_scsi_debug # dev_size_mb add_host num_tgts max_luns blksz
409eda14cbcSMatt Macy{
410eda14cbcSMatt Macy	typeset devsize=$1
411eda14cbcSMatt Macy	typeset hosts=$2
412eda14cbcSMatt Macy	typeset tgts=$3
413eda14cbcSMatt Macy	typeset luns=$4
414eda14cbcSMatt Macy	typeset blksz=$5
415eda14cbcSMatt Macy
416eda14cbcSMatt Macy	[[ -z $devsize ]] || [[ -z $hosts ]] || [[ -z $tgts ]] || \
417eda14cbcSMatt Macy	    [[ -z $luns ]] || [[ -z $blksz ]] && \
418eda14cbcSMatt Macy	    log_fail "Arguments invalid or missing"
419eda14cbcSMatt Macy
420eda14cbcSMatt Macy	case "$5" in
421eda14cbcSMatt Macy		'512b')
422eda14cbcSMatt Macy			typeset sector=512
423eda14cbcSMatt Macy			typeset blkexp=0
424eda14cbcSMatt Macy		;;
425eda14cbcSMatt Macy		'512e')
426eda14cbcSMatt Macy			typeset sector=512
427eda14cbcSMatt Macy			typeset blkexp=3
428eda14cbcSMatt Macy		;;
429eda14cbcSMatt Macy		'4Kn')
430eda14cbcSMatt Macy			typeset sector=4096
431eda14cbcSMatt Macy			typeset blkexp=0
432eda14cbcSMatt Macy		;;
433eda14cbcSMatt Macy		*) log_fail "Unsupported blksz value: $5" ;;
434eda14cbcSMatt Macy	esac
435eda14cbcSMatt Macy
436eda14cbcSMatt Macy	if is_linux; then
437716fd348SMartin Matuska		modprobe -n scsi_debug ||
438716fd348SMartin Matuska			log_unsupported "Platform does not have scsi_debug module"
439716fd348SMartin Matuska		if lsmod | grep -q scsi_debug; then
440eda14cbcSMatt Macy			log_fail "scsi_debug module already installed"
441eda14cbcSMatt Macy		else
442eda14cbcSMatt Macy			log_must modprobe scsi_debug dev_size_mb=$devsize \
443eda14cbcSMatt Macy			    add_host=$hosts num_tgts=$tgts max_luns=$luns \
444eda14cbcSMatt Macy			    sector_size=$sector physblk_exp=$blkexp
445eda14cbcSMatt Macy			block_device_wait
446716fd348SMartin Matuska			if ! lsscsi | grep -q scsi_debug; then
447eda14cbcSMatt Macy				log_fail "scsi_debug module install failed"
448eda14cbcSMatt Macy			fi
449eda14cbcSMatt Macy		fi
450eda14cbcSMatt Macy	fi
451eda14cbcSMatt Macy}
452eda14cbcSMatt Macy
453eda14cbcSMatt Macy#
454eda14cbcSMatt Macy# Unload scsi_debug module, if needed.
455eda14cbcSMatt Macy#
456eda14cbcSMatt Macyfunction unload_scsi_debug
457eda14cbcSMatt Macy{
458eda14cbcSMatt Macy	log_must_retry "in use" 5 modprobe -r scsi_debug
459eda14cbcSMatt Macy}
460eda14cbcSMatt Macy
461eda14cbcSMatt Macy#
462eda14cbcSMatt Macy# Get scsi_debug device name.
463eda14cbcSMatt Macy# Returns basename of scsi_debug device (for example "sdb").
464eda14cbcSMatt Macy#
465eda14cbcSMatt Macyfunction get_debug_device
466eda14cbcSMatt Macy{
467eda14cbcSMatt Macy	for i in {1..10} ; do
468716fd348SMartin Matuska		val=$(lsscsi | awk '/scsi_debug/ {print $6; exit}' | cut -d/ -f3)
469eda14cbcSMatt Macy
470eda14cbcSMatt Macy		# lsscsi can take time to settle
471eda14cbcSMatt Macy		if [ "$val" != "-" ] ; then
472eda14cbcSMatt Macy			break
473eda14cbcSMatt Macy		fi
474eda14cbcSMatt Macy		sleep 1
475eda14cbcSMatt Macy	done
476eda14cbcSMatt Macy	echo "$val"
477eda14cbcSMatt Macy}
478eda14cbcSMatt Macy
479eda14cbcSMatt Macy#
480eda14cbcSMatt Macy# Get actual devices used by the pool (i.e. linux sdb1 not sdb).
481eda14cbcSMatt Macy#
482eda14cbcSMatt Macyfunction get_pool_devices #testpool #devdir
483eda14cbcSMatt Macy{
484eda14cbcSMatt Macy	typeset testpool=$1
485eda14cbcSMatt Macy	typeset devdir=$2
486eda14cbcSMatt Macy	typeset out=""
487eda14cbcSMatt Macy
488716fd348SMartin Matuska	case "$UNAME" in
489716fd348SMartin Matuska	Linux|FreeBSD)
490716fd348SMartin Matuska		zpool status -P $testpool | awk -v d="$devdir" '$1 ~ d {sub(d "/", ""); printf("%s ", $1)}'
491716fd348SMartin Matuska		;;
492716fd348SMartin Matuska	esac
493eda14cbcSMatt Macy}
494eda14cbcSMatt Macy
495eda14cbcSMatt Macy#
496eda14cbcSMatt Macy# Write to standard out giving the level, device name, offset and length
497eda14cbcSMatt Macy# of all blocks in an input file. The offset and length are in units of
498eda14cbcSMatt Macy# 512 byte blocks. In the case of mirrored vdevs, only the first
499eda14cbcSMatt Macy# device is listed, as the levels, blocks and offsets will be the same
500eda14cbcSMatt Macy# on other devices. Note that this function only works with mirrored
501eda14cbcSMatt Macy# or non-redundant pools, not raidz.
502eda14cbcSMatt Macy#
503eda14cbcSMatt Macy# The output of this function can be used to introduce corruption at
504eda14cbcSMatt Macy# varying levels of indirection.
505eda14cbcSMatt Macy#
506eda14cbcSMatt Macyfunction list_file_blocks # input_file
507eda14cbcSMatt Macy{
508eda14cbcSMatt Macy	typeset input_file=$1
509eda14cbcSMatt Macy
510eda14cbcSMatt Macy	[[ -f $input_file ]] || log_fail "Couldn't find $input_file"
511eda14cbcSMatt Macy
512eda14cbcSMatt Macy	typeset ds="$(zfs list -H -o name $input_file)"
513eda14cbcSMatt Macy	typeset pool="${ds%%/*}"
514eda14cbcSMatt Macy	typeset objnum="$(get_objnum $input_file)"
515eda14cbcSMatt Macy
516eda14cbcSMatt Macy	#
517eda14cbcSMatt Macy	# Establish a mapping between vdev ids as shown in a DVA and the
5187877fdebSMatt Macy	# pathnames they correspond to in ${VDEV_MAP[][]}.
5197877fdebSMatt Macy	#
5207877fdebSMatt Macy	# The vdev bits in a DVA refer to the top level vdev id.
5217877fdebSMatt Macy	# ${VDEV_MAP[$id]} is an array of the vdev paths within that vdev.
522eda14cbcSMatt Macy	#
523eda14cbcSMatt Macy	eval $(zdb -C $pool | awk '
5247877fdebSMatt Macy	    BEGIN { printf "typeset -a VDEV_MAP;" }
5257877fdebSMatt Macy	    function subscript(s) {
5267877fdebSMatt Macy	        # "[#]" is more convenient than the bare "#"
5277877fdebSMatt Macy	        match(s, /\[[0-9]*\]/)
5287877fdebSMatt Macy		return substr(s, RSTART, RLENGTH)
5297877fdebSMatt Macy	    }
5307877fdebSMatt Macy	    id && !/^                / {
5317877fdebSMatt Macy	        # left a top level vdev
5327877fdebSMatt Macy	        id = 0
5337877fdebSMatt Macy	    }
5347877fdebSMatt Macy	    id && $1 ~ /^path:$/ {
5357877fdebSMatt Macy	        # found a vdev path; save it in the map
5367877fdebSMatt Macy	        printf "VDEV_MAP%s%s=%s;", id, child, $2
537eda14cbcSMatt Macy	    }
538eda14cbcSMatt Macy	    /^            children/ {
5397877fdebSMatt Macy	        # entering a top level vdev
5407877fdebSMatt Macy	        id = subscript($0)
5417877fdebSMatt Macy		child = "[0]" # default in case there is no nested vdev
5427877fdebSMatt Macy		printf "typeset -a VDEV_MAP%s;", id
543eda14cbcSMatt Macy	    }
5447877fdebSMatt Macy	    /^                children/ {
5457877fdebSMatt Macy	        # entering a nested vdev (e.g. child of a top level mirror)
5467877fdebSMatt Macy	        child = subscript($0)
547eda14cbcSMatt Macy	    }
5487877fdebSMatt Macy	')
549eda14cbcSMatt Macy
550eda14cbcSMatt Macy	#
551eda14cbcSMatt Macy	# The awk below parses the output of zdb, printing out the level
552eda14cbcSMatt Macy	# of each block along with vdev id, offset and length. The last
553eda14cbcSMatt Macy	# two are converted to decimal in the while loop. 4M is added to
554eda14cbcSMatt Macy	# the offset to compensate for the first two labels and boot
555eda14cbcSMatt Macy	# block. Lastly, the offset and length are printed in units of
5567877fdebSMatt Macy	# 512B blocks for ease of use with dd.
557eda14cbcSMatt Macy	#
5587877fdebSMatt Macy	typeset level vdev path offset length
559e92ffd9bSMartin Matuska	sync_all_pools true
560*dd215568SMartin Matuska	zdb -dddddd $ds $objnum | awk '
5617877fdebSMatt Macy	    /^$/ { looking = 0 }
5627877fdebSMatt Macy	    looking {
5637877fdebSMatt Macy	        level = $2
5647877fdebSMatt Macy	        field = 3
5657877fdebSMatt Macy	        while (split($field, dva, ":") == 3) {
5667877fdebSMatt Macy
567*dd215568SMartin Matuska	            print level, int(dva[1]), "0x"dva[2], "0x"dva[3]
5687877fdebSMatt Macy
5697877fdebSMatt Macy	            ++field
5707877fdebSMatt Macy	        }
5717877fdebSMatt Macy	    }
572eda14cbcSMatt Macy	    /^Indirect blocks:/ { looking = 1 }
5737877fdebSMatt Macy	' | \
5747877fdebSMatt Macy	while read level vdev offset length; do
5757877fdebSMatt Macy		for path in ${VDEV_MAP[$vdev][@]}; do
576*dd215568SMartin Matuska			echo "$level $path $(( ($offset + (4<<20)) / 512 ))" \
577*dd215568SMartin Matuska			    "$(( $length / 512 ))"
5787877fdebSMatt Macy		done
579eda14cbcSMatt Macy	done 2>/dev/null
580eda14cbcSMatt Macy}
581eda14cbcSMatt Macy
582eda14cbcSMatt Macyfunction corrupt_blocks_at_level # input_file corrupt_level
583eda14cbcSMatt Macy{
584eda14cbcSMatt Macy	typeset input_file=$1
585eda14cbcSMatt Macy	typeset corrupt_level="L${2:-0}"
586eda14cbcSMatt Macy	typeset level path offset length
587eda14cbcSMatt Macy
588eda14cbcSMatt Macy	[[ -f $input_file ]] || log_fail "Couldn't find $input_file"
589eda14cbcSMatt Macy
590eda14cbcSMatt Macy	if is_freebsd; then
591eda14cbcSMatt Macy		# Temporarily allow corrupting an inuse device.
592eda14cbcSMatt Macy		debugflags=$(sysctl -n kern.geom.debugflags)
593eda14cbcSMatt Macy		sysctl kern.geom.debugflags=16
594eda14cbcSMatt Macy	fi
595eda14cbcSMatt Macy
596eda14cbcSMatt Macy	list_file_blocks $input_file | \
597eda14cbcSMatt Macy	while read level path offset length; do
598eda14cbcSMatt Macy		if [[ $level = $corrupt_level ]]; then
599eda14cbcSMatt Macy			log_must dd if=/dev/urandom of=$path bs=512 \
600eda14cbcSMatt Macy			    count=$length seek=$offset conv=notrunc
601eda14cbcSMatt Macy		fi
602eda14cbcSMatt Macy	done
603eda14cbcSMatt Macy
604eda14cbcSMatt Macy	if is_freebsd; then
605eda14cbcSMatt Macy		sysctl kern.geom.debugflags=$debugflags
606eda14cbcSMatt Macy	fi
607eda14cbcSMatt Macy
608eda14cbcSMatt Macy	# This is necessary for pools made of loop devices.
609eda14cbcSMatt Macy	sync
610eda14cbcSMatt Macy}
611dae17134SMartin Matuska
612dae17134SMartin Matuskafunction corrupt_label_checksum # label_number vdev_path
613dae17134SMartin Matuska{
614dae17134SMartin Matuska	typeset label_size=$((256*1024))
615dae17134SMartin Matuska	typeset vdev_size=$(stat_size ${2})
616dae17134SMartin Matuska	typeset -a offsets=("$((128*1024 - 32))" \
617dae17134SMartin Matuska	    "$(($label_size + (128*1024 - 32)))" \
618dae17134SMartin Matuska	    "$(($vdev_size - $label_size - (128*1024 + 32)))" \
619dae17134SMartin Matuska	    "$(($vdev_size - (128*1024 + 32)))")
620dae17134SMartin Matuska
621dae17134SMartin Matuska	dd if=/dev/urandom of=${2} seek=${offsets[$1]} bs=1 count=32 \
622dae17134SMartin Matuska	    conv=notrunc
623dae17134SMartin Matuska}
624