1# 2# This file and its contents are supplied under the terms of the 3# Common Development and Distribution License ("CDDL"), version 1.0. 4# You may only use this file in accordance with the terms of version 5# 1.0 of the CDDL. 6# 7# A full copy of the text of the CDDL should have accompanied this 8# source. A copy of the CDDL is also available via the Internet at 9# http://www.illumos.org/license/CDDL. 10# 11 12# 13# Copyright 2009 Sun Microsystems, Inc. All rights reserved. 14# Use is subject to license terms. 15# Copyright (c) 2012, 2016 by Delphix. All rights reserved. 16# Copyright 2016 Nexenta Systems, Inc. 17# Copyright (c) 2016, 2017 by Intel Corporation. All rights reserved. 18# Copyright (c) 2017 Lawrence Livermore National Security, LLC. 19# Copyright (c) 2017 Datto Inc. 20# Copyright (c) 2017 Open-E, Inc. All Rights Reserved. 21# 22 23# 24# Returns SCSI host number for the given disk 25# 26function get_scsi_host #disk 27{ 28 typeset disk=$1 29 ls /sys/block/${disk}/device/scsi_device | cut -d : -f 1 30} 31 32# 33# Cause a scan of all scsi host adapters by default 34# 35# $1 optional host number 36# 37function scan_scsi_hosts 38{ 39 typeset hostnum=${1} 40 41 if is_linux; then 42 if [[ -z $hostnum ]]; then 43 for host in /sys/class/scsi_host/host*; do 44 log_must eval "echo '- - -' > $host/scan" 45 done 46 else 47 log_must eval \ 48 "echo /sys/class/scsi_host/host$hostnum/scan" \ 49 > /dev/null 50 log_must eval \ 51 "echo '- - -' > /sys/class/scsi_host/host$hostnum/scan" 52 fi 53 fi 54} 55 56# 57# Wait for newly created block devices to have their minors created. 58# 59function block_device_wait 60{ 61 if is_linux; then 62 udevadm trigger 63 udevadm settle 64 fi 65} 66 67# 68# Check if the given device is physical device 69# 70function is_physical_device #device 71{ 72 typeset device=${1#$DEV_DSKDIR} 73 device=${device#$DEV_RDSKDIR} 74 75 if is_linux; then 76 [[ -b "$DEV_DSKDIR/$device" ]] && \ 77 [[ -f /sys/module/loop/parameters/max_part ]] 78 return $? 79 else 80 echo $device | egrep "^c[0-F]+([td][0-F]+)+$" > /dev/null 2>&1 81 return $? 82 fi 83} 84 85# 86# Check if the given device is a real device (ie SCSI device) 87# 88function is_real_device #disk 89{ 90 typeset disk=$1 91 [[ -z $disk ]] && log_fail "No argument for disk given." 92 93 if is_linux; then 94 lsblk $DEV_RDSKDIR/$disk -o TYPE 2>/dev/null | \ 95 egrep disk >/dev/null 96 return $? 97 fi 98} 99 100# 101# Check if the given device is a loop device 102# 103function is_loop_device #disk 104{ 105 typeset disk=$1 106 [[ -z $disk ]] && log_fail "No argument for disk given." 107 108 if is_linux; then 109 lsblk $DEV_RDSKDIR/$disk -o TYPE 2>/dev/null | \ 110 egrep loop >/dev/null 111 return $? 112 fi 113} 114 115# 116# Check if the given device is a multipath device and if there is a symbolic 117# link to a device mapper and to a disk 118# Currently no support for dm devices alone without multipath 119# 120function is_mpath_device #disk 121{ 122 typeset disk=$1 123 [[ -z $disk ]] && log_fail "No argument for disk given." 124 125 if is_linux; then 126 lsblk $DEV_MPATHDIR/$disk -o TYPE 2>/dev/null | \ 127 egrep mpath >/dev/null 128 if (($? == 0)); then 129 readlink $DEV_MPATHDIR/$disk > /dev/null 2>&1 130 return $? 131 else 132 return $? 133 fi 134 fi 135} 136 137# Set the slice prefix for disk partitioning depending 138# on whether the device is a real, multipath, or loop device. 139# Currently all disks have to be of the same type, so only 140# checks first disk to determine slice prefix. 141# 142function set_slice_prefix 143{ 144 typeset disk 145 typeset -i i=0 146 147 if is_linux; then 148 while (( i < $DISK_ARRAY_NUM )); do 149 disk="$(echo $DISKS | nawk '{print $(i + 1)}')" 150 if ( is_mpath_device $disk ) && [[ -z $(echo $disk | awk 'substr($1,18,1)\ 151 ~ /^[[:digit:]]+$/') ]] || ( is_real_device $disk ); then 152 export SLICE_PREFIX="" 153 return 0 154 elif ( is_mpath_device $disk || is_loop_device \ 155 $disk ); then 156 export SLICE_PREFIX="p" 157 return 0 158 else 159 log_fail "$disk not supported for partitioning." 160 fi 161 (( i = i + 1)) 162 done 163 fi 164} 165 166# 167# Set the directory path of the listed devices in $DISK_ARRAY_NUM 168# Currently all disks have to be of the same type, so only 169# checks first disk to determine device directory 170# default = /dev (linux) 171# real disk = /dev (linux) 172# multipath device = /dev/mapper (linux) 173# 174function set_device_dir 175{ 176 typeset disk 177 typeset -i i=0 178 179 if is_linux; then 180 while (( i < $DISK_ARRAY_NUM )); do 181 disk="$(echo $DISKS | nawk '{print $(i + 1)}')" 182 if is_mpath_device $disk; then 183 export DEV_DSKDIR=$DEV_MPATHDIR 184 return 0 185 else 186 export DEV_DSKDIR=$DEV_RDSKDIR 187 return 0 188 fi 189 (( i = i + 1)) 190 done 191 else 192 export DEV_DSKDIR=$DEV_RDSKDIR 193 fi 194} 195 196# 197# Get the directory path of given device 198# 199function get_device_dir #device 200{ 201 typeset device=$1 202 203 if ! $(is_physical_device $device) ; then 204 if [[ $device != "/" ]]; then 205 device=${device%/*} 206 fi 207 if [[ -b "$DEV_DSKDIR/$device" ]]; then 208 device="$DEV_DSKDIR" 209 fi 210 echo $device 211 else 212 echo "$DEV_DSKDIR" 213 fi 214} 215 216# 217# Get persistent name for given disk 218# 219function get_persistent_disk_name #device 220{ 221 typeset device=$1 222 typeset dev_id 223 224 if is_linux; then 225 if is_real_device $device; then 226 dev_id="$(udevadm info -q all -n $DEV_DSKDIR/$device \ 227 | egrep disk/by-id | nawk '{print $2; exit}' \ 228 | nawk -F / '{print $3}')" 229 echo $dev_id 230 elif is_mpath_device $device; then 231 dev_id="$(udevadm info -q all -n $DEV_DSKDIR/$device \ 232 | egrep disk/by-id/dm-uuid \ 233 | nawk '{print $2; exit}' \ 234 | nawk -F / '{print $3}')" 235 echo $dev_id 236 else 237 echo $device 238 fi 239 else 240 echo $device 241 fi 242} 243 244# 245# Online or offline a disk on the system 246# 247# First checks state of disk. Test will fail if disk is not properly onlined 248# or offlined. Online is a full rescan of SCSI disks by echoing to every 249# host entry. 250# 251function on_off_disk # disk state{online,offline} host 252{ 253 typeset disk=$1 254 typeset state=$2 255 typeset host=$3 256 257 [[ -z $disk ]] || [[ -z $state ]] && \ 258 log_fail "Arguments invalid or missing" 259 260 if is_linux; then 261 if [[ $state == "offline" ]] && ( is_mpath_device $disk ); then 262 dm_name="$(readlink $DEV_DSKDIR/$disk \ 263 | nawk -F / '{print $2}')" 264 slave="$(ls /sys/block/${dm_name}/slaves \ 265 | nawk '{print $1}')" 266 while [[ -n $slave ]]; do 267 #check if disk is online 268 lsscsi | egrep $slave > /dev/null 269 if (($? == 0)); then 270 slave_dir="/sys/block/${dm_name}" 271 slave_dir+="/slaves/${slave}/device" 272 ss="${slave_dir}/state" 273 sd="${slave_dir}/delete" 274 log_must eval "echo 'offline' > ${ss}" 275 log_must eval "echo '1' > ${sd}" 276 lsscsi | egrep $slave > /dev/null 277 if (($? == 0)); then 278 log_fail "Offlining" \ 279 "$disk failed" 280 fi 281 fi 282 slave="$(ls /sys/block/$dm_name/slaves \ 283 2>/dev/null | nawk '{print $1}')" 284 done 285 elif [[ $state == "offline" ]] && ( is_real_device $disk ); then 286 #check if disk is online 287 lsscsi | egrep $disk > /dev/null 288 if (($? == 0)); then 289 dev_state="/sys/block/$disk/device/state" 290 dev_delete="/sys/block/$disk/device/delete" 291 log_must eval "echo 'offline' > ${dev_state}" 292 log_must eval "echo '1' > ${dev_delete}" 293 lsscsi | egrep $disk > /dev/null 294 if (($? == 0)); then 295 log_fail "Offlining $disk" \ 296 "failed" 297 fi 298 else 299 log_note "$disk is already offline" 300 fi 301 elif [[ $state == "online" ]]; then 302 #force a full rescan 303 scan_scsi_hosts $host 304 block_device_wait 305 if is_mpath_device $disk; then 306 dm_name="$(readlink $DEV_DSKDIR/$disk \ 307 | nawk -F / '{print $2}')" 308 slave="$(ls /sys/block/$dm_name/slaves \ 309 | nawk '{print $1}')" 310 lsscsi | egrep $slave > /dev/null 311 if (($? != 0)); then 312 log_fail "Onlining $disk failed" 313 fi 314 elif is_real_device $disk; then 315 block_device_wait 316 typeset -i retries=0 317 while ! lsscsi | egrep -q $disk; do 318 if (( $retries > 2 )); then 319 log_fail "Onlining $disk failed" 320 break 321 fi 322 (( ++retries )) 323 sleep 1 324 done 325 else 326 log_fail "$disk is not a real dev" 327 fi 328 else 329 log_fail "$disk failed to $state" 330 fi 331 fi 332} 333 334# 335# Simulate disk removal 336# 337function remove_disk #disk 338{ 339 typeset disk=$1 340 on_off_disk $disk "offline" 341 block_device_wait 342} 343 344# 345# Simulate disk insertion for the given SCSI host 346# 347function insert_disk #disk scsi_host 348{ 349 typeset disk=$1 350 typeset scsi_host=$2 351 on_off_disk $disk "online" $scsi_host 352 block_device_wait 353} 354 355# 356# Load scsi_debug module with specified parameters 357# $blksz can be either one of: < 512b | 512e | 4Kn > 358# 359function load_scsi_debug # dev_size_mb add_host num_tgts max_luns blksz 360{ 361 typeset devsize=$1 362 typeset hosts=$2 363 typeset tgts=$3 364 typeset luns=$4 365 typeset blksz=$5 366 367 [[ -z $devsize ]] || [[ -z $hosts ]] || [[ -z $tgts ]] || \ 368 [[ -z $luns ]] || [[ -z $blksz ]] && \ 369 log_fail "Arguments invalid or missing" 370 371 case "$5" in 372 '512b') 373 typeset sector=512 374 typeset blkexp=0 375 ;; 376 '512e') 377 typeset sector=512 378 typeset blkexp=3 379 ;; 380 '4Kn') 381 typeset sector=4096 382 typeset blkexp=0 383 ;; 384 *) log_fail "Unsupported blksz value: $5" ;; 385 esac 386 387 if is_linux; then 388 modprobe -n scsi_debug 389 if (($? != 0)); then 390 log_unsupported "Platform does not have scsi_debug" 391 "module" 392 fi 393 lsmod | egrep scsi_debug > /dev/null 394 if (($? == 0)); then 395 log_fail "scsi_debug module already installed" 396 else 397 log_must modprobe scsi_debug dev_size_mb=$devsize \ 398 add_host=$hosts num_tgts=$tgts max_luns=$luns \ 399 sector_size=$sector physblk_exp=$blkexp 400 block_device_wait 401 lsscsi | egrep scsi_debug > /dev/null 402 if (($? == 1)); then 403 log_fail "scsi_debug module install failed" 404 fi 405 fi 406 fi 407} 408 409# 410# Unload scsi_debug module, if needed. 411# 412function unload_scsi_debug 413{ 414 log_must_retry "in use" 5 modprobe -r scsi_debug 415} 416 417# 418# Get scsi_debug device name. 419# Returns basename of scsi_debug device (for example "sdb"). 420# 421function get_debug_device 422{ 423 for i in {1..10} ; do 424 val=$(lsscsi | nawk '/scsi_debug/ {print $6; exit}' | cut -d / -f3) 425 426 # lsscsi can take time to settle 427 if [ "$val" != "-" ] ; then 428 break 429 fi 430 sleep 1 431 done 432 echo "$val" 433} 434 435# 436# Get actual devices used by the pool (i.e. linux sdb1 not sdb). 437# 438function get_pool_devices #testpool #devdir 439{ 440 typeset testpool=$1 441 typeset devdir=$2 442 typeset out="" 443 444 if is_linux; then 445 out=$(zpool status -P $testpool |grep ${devdir} | awk '{print $1}') 446 out=$(echo $out | sed -e "s|${devdir}/||g" | tr '\n' ' ') 447 fi 448 echo $out 449} 450