mirror_zfs/cmd/zpool_layout/zpool_layout
Ned Bass fa417e57a6 Call udevadm trigger more safely
Some udev hooks are not designed to be idempotent, so calling udevadm
trigger outside of the distribution's initialization scripts can have
unexpected (and potentially dangerous) side effects.  For example, the
system time may change or devices may appear multiple times.  See Ubuntu
launchpad bug 320200 and this mailing list post for more details:

https://lists.ubuntu.com/archives/ubuntu-devel/2009-January/027260.html

To avoid these problems we call udevadm trigger with --action=change
--subsystem-match=block.  The first argument tells udev just to refresh
devices, and make sure everything's as it should be.  The second
argument limits the scope to block devices, so devices belonging to
other subsystems cannot be affected.

This doesn't fix the problem on older udev implementations that don't
provide udevadm but instead have udevtrigger as a standalone program.
In this case the above options aren't available so there's no way to
call call udevtrigger safely.  But we can live with that since this
issue only exists in optional test and helper scripts, and most
zfs-on-linux users are running newer systems anyways.
2011-04-05 13:00:51 -07:00

161 lines
4.0 KiB
Bash
Executable File

#!/bin/bash
#
# Set BUSES and PORTS to match the topology of your system. As each
# port is enumerated it will be assigned the next channel name. The
# current script enumerates each port on a bus before moving on to
# enumerate the next bus.
#
# Every distribution, version of udev, and type of attached storage
# seems to result in slightly different formatting of the by-path
# name. For this reason you may need to adjust the parsing below
# to suit your needs. This is one of the reasons to use a custom
# /etc/zfs/zdev.conf file, it allows the by-path naming convertion
# to change and still keep the simple <channel><rank> naming.
#
AWK=${AWK:-/bin/awk}
CONFIG=${CONFIG:-/etc/zfs/zdev.conf}
BUSES=( 01 02 03 )
PORTS=( 4 0 )
CHANNELS=( A B C D E F G H I J K L M N O P Q R S T U V W X Y Z )
TRIGGER="no"
MAPPING=linux
usage() {
cat << EOF
Usage: zpool_layout [-th] [-c file] [-b buses] [-p ports] [-n channels] [-m map]
-c Alternate config file [default=${CONFIG}]
-b Enumerate buses [default="${BUSES[*]}"]
-p Enumerate ports [default="${PORTS[*]}"]
-n Channel names [default="A..Z"]
-t Trigger and wait for udev to settle [default=${TRIGGER}]
-m Slot mapping [default=${MAPPING}]
-h Show this message
EOF
exit 0
}
while getopts 'c:b:p:n:m:th' OPTION; do
case ${OPTION} in
c)
CONFIG=${OPTARG}
;;
b)
BUSES=(${OPTARG})
;;
p)
PORTS=(${OPTARG})
;;
n)
CHANNELS=(${OPTARG})
;;
m)
MAPPING=`readlink -e ${OPTARG}`
;;
t)
TRIGGER=yes
;;
h)
usage
;;
esac
done
# Verify mapping file exists if specified.
# Linux-Slot Custom-Slot
if [ ${MAPPING} != "linux" ] && [ ! -e ${MAPPING} ]; then
echo "Error: Mapping file '${MAPPING}' does not exist"
exit 1
fi
# Save stdout as fd #8, then redirect stdout to the config file.
exec 8>&1
exec >${CONFIG}
pushd /dev/disk/by-path >/dev/null
map_slot() {
local LINUX_SLOT=$1
local MAPPED_SLOT=
if [ ${MAPPING} = "linux" ]; then
MAPPED_SLOT=${LINUX_SLOT}
else
MAPPED_SLOT=`${AWK} "\\$1 == ${LINUX_SLOT} && !/^#/ \
{ print \\$2; exit }" $MAPPING`
fi
printf "%d" ${MAPPED_SLOT}
}
# Generate comment header.
echo "#"
echo "# Custom /dev/disk/by-path to /dev/disk/zpool mapping, "
echo "# based of the following physical cable layout."
echo "#"
# Generate host port layout table for comment header.
echo "# ------------------ Host Port Layout ---------------------"
echo -n "# "
for (( i=0; i<${#BUSES[*]}; i++ )); do
printf "%-8d" ${BUSES[$i]}
done
echo
for (( i=0, k=0; i<${#PORTS[*]}; i++ )); do
printf "# Port %-2d " ${PORTS[$i]}
for (( j=0; j<${#BUSES[*]}; j++, k++ )); do
let k=$j*${#PORTS[*]}+$i
printf "%-8s" ${CHANNELS[$k]}
done
echo
done
echo "#"
# Generate channel/disk layout table for comment header.
echo "# ----------------- Channel/Disk Layout -------------------"
echo "# Channel Disks"
for (( i=0, k=0; i<${#BUSES[*]}; i++ )); do
for (( j=0; j<${#PORTS[*]}; j++, k++ )); do
printf "# %-9s" ${CHANNELS[$k]}
ls *:${BUSES[$i]}:*:${PORTS[$j]}* 2>/dev/null | \
cut -f7 -d'-' | sort -u -n | tr '\n' ','
echo
done
done
echo "#"
# Generate mapping from <channel><rank> to by-path name.
for (( i=0, k=0; i<${#BUSES[*]}; i++ )); do
for (( j=0; j<${#PORTS[*]}; j++, k++ )); do
BYPATH=(`ls *:${BUSES[$i]}:*:${PORTS[$j]}* 2>/dev/null | \
grep -v part | sort -n -k7 -t'-' | cut -f1-6 -d'-'`)
SLOTS=(`ls *:${BUSES[$i]}:*:${PORTS[$j]}* 2>/dev/null | \
grep -v part | sort -n -k7 -t'-' | cut -f7 -d'-'`)
TMP_FILE=`mktemp`
for (( l=0; l<${#SLOTS[*]}; l++ )); do
MAPPED_SLOT=`map_slot ${SLOTS[$l]}`
printf "%s%d\t%s-%d\n" \
${CHANNELS[$k]} ${MAPPED_SLOT} \
${BYPATH[$l]} ${SLOTS[$l]} >>${TMP_FILE}
done
echo
echo -n "# Channel ${CHANNELS[$k]}, "
echo "Bus ${BUSES[$i]}, Port ${PORTS[$j]}"
cat ${TMP_FILE} | sort -n -k2 -t${CHANNELS[$k]}
rm -f ${TMP_FILE}
done
done
# Restore stdout from fd #8 and close fd #8.
exec 1>&8 8>&-
popd >/dev/null
if [ ${TRIGGER} = "yes" ]; then
udevadm trigger --action=change --subsystem-match=block
udevadm settle
fi
exit 0