summaryrefslogtreecommitdiffstats
path: root/tests/zfs-tests/include/blkdev.shlib
blob: 28ac1052c3d0cbdaeb4c1f6d38a97e4da434df45 (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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
#
# 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.
#

#
# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
# Copyright (c) 2012, 2016 by Delphix. All rights reserved.
# Copyright 2016 Nexenta Systems, Inc.
# Copyright (c) 2016, 2017 by Intel Corporation. All rights reserved.
# Copyright (c) 2017 Lawrence Livermore National Security, LLC.
# Copyright (c) 2017 Datto Inc.
# Copyright (c) 2017 Open-E, Inc. All Rights Reserved.
#

#
# Returns SCSI host number for the given disk
#
function get_scsi_host #disk
{
	typeset disk=$1
	ls /sys/block/${disk}/device/scsi_device | cut -d : -f 1
}

#
# Cause a scan of all scsi host adapters by default
#
# $1 optional host number
#
function scan_scsi_hosts
{
	typeset hostnum=${1}

	if is_linux; then
		if [[ -z $hostnum ]]; then
			for host in /sys/class/scsi_host/host*; do
				log_must eval "echo '- - -' > $host/scan"
			done
		else
			log_must eval \
			    "echo /sys/class/scsi_host/host$hostnum/scan" \
			    > /dev/null
			log_must eval \
			    "echo '- - -' > /sys/class/scsi_host/host$hostnum/scan"
		fi
	fi
}

#
# Wait for newly created block devices to have their minors created.
#
function block_device_wait
{
	if is_linux; then
		udevadm trigger
		udevadm settle
	fi
}

#
# Check if the given device is physical device
#
function is_physical_device #device
{
	typeset device=${1#$DEV_DSKDIR}
	device=${device#$DEV_RDSKDIR}

	if is_linux; then
		[[ -b "$DEV_DSKDIR/$device" ]] && \
		[[ -f /sys/module/loop/parameters/max_part ]]
		return $?
	else
		echo $device | egrep "^c[0-F]+([td][0-F]+)+$" > /dev/null 2>&1
		return $?
	fi
}

#
# Check if the given device is a real device (ie SCSI device)
#
function is_real_device #disk
{
	typeset disk=$1
	[[ -z $disk ]] && log_fail "No argument for disk given."

	if is_linux; then
		lsblk $DEV_RDSKDIR/$disk -o TYPE 2>/dev/null | \
		    egrep disk >/dev/null
		return $?
	fi
}

#
# Check if the given device is a loop device
#
function is_loop_device #disk
{
	typeset disk=$1
	[[ -z $disk ]] && log_fail "No argument for disk given."

	if is_linux; then
		lsblk $DEV_RDSKDIR/$disk -o TYPE 2>/dev/null | \
		    egrep loop >/dev/null
		return $?
	fi
}

#
# Check if the given device is a multipath device and if there is a sybolic
# link to a device mapper and to a disk
# Currently no support for dm devices alone without multipath
#
function is_mpath_device #disk
{
	typeset disk=$1
	[[ -z $disk ]] && log_fail "No argument for disk given."

	if is_linux; then
		lsblk $DEV_MPATHDIR/$disk -o TYPE 2>/dev/null | \
		   egrep mpath >/dev/null
		if (($? == 0)); then
			readlink $DEV_MPATHDIR/$disk > /dev/null 2>&1
			return $?
		else
			return $?
		fi
	fi
}

# Set the slice prefix for disk partitioning depending
# on whether the device is a real, multipath, or loop device.
# Currently all disks have to be of the same type, so only
# checks first disk to determine slice prefix.
#
function set_slice_prefix
{
	typeset disk
	typeset -i i=0

	if is_linux; then
		while (( i < $DISK_ARRAY_NUM )); do
			disk="$(echo $DISKS | nawk '{print $(i + 1)}')"
			if ( is_mpath_device $disk ) && [[ -z $(echo $disk | awk 'substr($1,18,1)\
			     ~ /^[[:digit:]]+$/') ]] || ( is_real_device $disk ); then
				export SLICE_PREFIX=""
				return 0
			elif ( is_mpath_device $disk || is_loop_device \
			    $disk ); then
				export SLICE_PREFIX="p"
				return 0
			else
				log_fail "$disk not supported for partitioning."
			fi
			(( i = i + 1))
		done
	fi
}

#
# Set the directory path of the listed devices in $DISK_ARRAY_NUM
# Currently all disks have to be of the same type, so only
# checks first disk to determine device directory
# default = /dev (linux)
# real disk = /dev (linux)
# multipath device = /dev/mapper (linux)
#
function set_device_dir
{
	typeset disk
	typeset -i i=0

	if is_linux; then
		while (( i < $DISK_ARRAY_NUM )); do
			disk="$(echo $DISKS | nawk '{print $(i + 1)}')"
			if is_mpath_device $disk; then
				export DEV_DSKDIR=$DEV_MPATHDIR
				return 0
			else
				export DEV_DSKDIR=$DEV_RDSKDIR
				return 0
			fi
			(( i = i + 1))
		done
	else
		export DEV_DSKDIR=$DEV_RDSKDIR
	fi
}

#
# Get the directory path of given device
#
function get_device_dir #device
{
	typeset device=$1

	if ! $(is_physical_device $device) ; then
		if [[ $device != "/" ]]; then
			device=${device%/*}
		fi
		if [[ -b "$DEV_DSKDIR/$device" ]]; then
			device="$DEV_DSKDIR"
		fi
		echo $device
	else
		echo "$DEV_DSKDIR"
	fi
}

#
# Get persistent name for given disk
#
function get_persistent_disk_name #device
{
	typeset device=$1
	typeset dev_id

	if is_linux; then
		if is_real_device $device; then
			dev_id="$(udevadm info -q all -n $DEV_DSKDIR/$device \
			    | egrep disk/by-id | nawk '{print $2; exit}' \
			    | nawk -F / '{print $3}')"
			echo $dev_id
		elif is_mpath_device $device; then
			dev_id="$(udevadm info -q all -n $DEV_DSKDIR/$device \
			    | egrep disk/by-id/dm-uuid \
			    | nawk '{print $2; exit}' \
			    | nawk -F / '{print $3}')"
			echo $dev_id
		else
			echo $device
		fi
	else
		echo $device
	fi
}

#
# Online or offline a disk on the system
#
# First checks state of disk. Test will fail if disk is not properly onlined
# or offlined. Online is a full rescan of SCSI disks by echoing to every
# host entry.
#
function on_off_disk # disk state{online,offline} host
{
	typeset disk=$1
	typeset state=$2
	typeset host=$3

	[[ -z $disk ]] || [[ -z $state ]] &&  \
	    log_fail "Arguments invalid or missing"

	if is_linux; then
		if [[ $state == "offline" ]] && ( is_mpath_device $disk ); then
			dm_name="$(readlink $DEV_DSKDIR/$disk \
			    | nawk -F / '{print $2}')"
			slave="$(ls /sys/block/${dm_name}/slaves \
			    | nawk '{print $1}')"
			while [[ -n $slave ]]; do
				#check if disk is online
				lsscsi | egrep $slave > /dev/null
				if (($? == 0)); then
					slave_dir="/sys/block/${dm_name}"
					slave_dir+="/slaves/${slave}/device"
					ss="${slave_dir}/state"
					sd="${slave_dir}/delete"
					log_must eval "echo 'offline' > ${ss}"
					log_must eval "echo '1' > ${sd}"
					lsscsi | egrep $slave > /dev/null
						if (($? == 0)); then
							log_fail "Offlining" \
							    "$disk failed"
						fi
				fi
				slave="$(ls /sys/block/$dm_name/slaves \
				    2>/dev/null | nawk '{print $1}')"
			done
		elif [[ $state == "offline" ]] && ( is_real_device $disk ); then
			#check if disk is online
			lsscsi | egrep $disk > /dev/null
			if (($? == 0)); then
				dev_state="/sys/block/$disk/device/state"
				dev_delete="/sys/block/$disk/device/delete"
				log_must eval "echo 'offline' > ${dev_state}"
				log_must eval "echo '1' > ${dev_delete}"
				lsscsi | egrep $disk > /dev/null
					if (($? == 0)); then
						log_fail "Offlining $disk" \
						    "failed"
					fi
			else
				log_note "$disk is already offline"
			fi
		elif [[ $state == "online" ]]; then
			#force a full rescan
			scan_scsi_hosts $host
			block_device_wait
			if is_mpath_device $disk; then
				dm_name="$(readlink $DEV_DSKDIR/$disk \
				    | nawk -F / '{print $2}')"
				slave="$(ls /sys/block/$dm_name/slaves \
				    | nawk '{print $1}')"
				lsscsi | egrep $slave > /dev/null
				if (($? != 0)); then
					log_fail "Onlining $disk failed"
				fi
			elif is_real_device $disk; then
				typeset -i retries=0
				while ! lsscsi | egrep -q $disk; do
					if (( $retries > 2 )); then
						log_fail "Onlining $disk failed"
						break
					fi
					(( ++retries ))
					sleep 1
				done
			else
				log_fail "$disk is not a real dev"
			fi
		else
			log_fail "$disk failed to $state"
		fi
	fi
}

#
# Simulate disk removal
#
function remove_disk #disk
{
	typeset disk=$1
	on_off_disk $disk "offline"
	block_device_wait
}

#
# Simulate disk insertion for the given SCSI host
#
function insert_disk #disk scsi_host
{
	typeset disk=$1
	typeset scsi_host=$2
	on_off_disk $disk "online" $scsi_host
	block_device_wait
}

#
# Load scsi_debug module with specified parameters
# $blksz can be either one of: < 512b | 512e | 4Kn >
#
function load_scsi_debug # dev_size_mb add_host num_tgts max_luns blksz
{
	typeset devsize=$1
	typeset hosts=$2
	typeset tgts=$3
	typeset luns=$4
	typeset blksz=$5

	[[ -z $devsize ]] || [[ -z $hosts ]] || [[ -z $tgts ]] || \
	    [[ -z $luns ]] || [[ -z $blksz ]] && \
	    log_fail "Arguments invalid or missing"

	case "$5" in
		'512b')
			typeset sector=512
			typeset blkexp=0
		;;
		'512e')
			typeset sector=512
			typeset blkexp=3
		;;
		'4Kn')
			typeset sector=4096
			typeset blkexp=0
		;;
		*) log_fail "Unsupported blksz value: $5" ;;
	esac

	if is_linux; then
		modprobe -n scsi_debug
		if (($? != 0)); then
			log_unsupported "Platform does not have scsi_debug"
			    "module"
		fi
		lsmod | egrep scsi_debug > /dev/null
		if (($? == 0)); then
			log_fail "scsi_debug module already installed"
		else
			log_must modprobe scsi_debug dev_size_mb=$devsize \
			    add_host=$hosts num_tgts=$tgts max_luns=$luns \
			    sector_size=$sector physblk_exp=$blkexp
			block_device_wait
			lsscsi | egrep scsi_debug > /dev/null
			if (($? == 1)); then
				log_fail "scsi_debug module install failed"
			fi
		fi
	fi
}

#
# Unload scsi_debug module, if needed.
#
function unload_scsi_debug
{
	if lsmod | grep scsi_debug >/dev/null; then
		log_must modprobe -r scsi_debug
	fi
}

#
# Get scsi_debug device name.
# Returns basename of scsi_debug device (for example "sdb").
#
function get_debug_device
{
	lsscsi | nawk '/scsi_debug/ {print $6; exit}' | cut -d / -f3
}