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