vdev_id: Support daisy-chained JBODs in multipath mode
Within function sas_handler() userspace commands like '/usr/sbin/multipath' have been replaced with sourcing device details from within sysfs which reduced a significant amount of overhead and processing time. Multiple JBOD enclosures and their order are sourced from the bsg driver (/sys/class/enclosure) to isolate chassis top-level expanders, which are then dynamically indexed based on host channel of the multipath subordinate disk member device being processed. Additionally added a "mixed" mode for slot identification for environments where a ZFS server system may contain SAS disk slots where there is no expander (direct connect to HBA) while an attached external JBOD with an expander have different slot identifier methods. How Has This Been Tested? ~~~~~~~~~~~~~~~~~~~~~~~~~ Testing was performed on a AMD EPYC based dual-server high-availability multipath environment with multiple HBAs per ZFS server and four SAS JBODs. The two primary JBODs were multipath/cross-connected between the two ZFS-HA servers. The secondary JBODs were daisy-chained off of the primary JBODs using aligned SAS expander channels (JBOD-0 expanderA--->JBOD-1 expanderA, JBOD-0 expanderB--->JBOD-1 expanderB, etc). Pools were created, exported and re-imported, imported globally with 'zpool import -a -d /dev/disk/by-vdev'. Low level udev debug outputs were traced to isolate and resolve errors. Result: ~~~~~~~ Initial testing of a previous version of this change showed how reliance on userspace utilities like '/usr/sbin/multipath' and '/usr/bin/lsscsi' were exacerbated by increasing numbers of disks and JBODs. With four 60-disk SAS JBODs and 240 disks the time to process a udevadm trigger was 3 minutes 30 seconds during which nearly all CPU cores were above 80% utilization. By switching reliance on userspace utilities to sysfs in this version, the udevadm trigger processing time was reduced to 12.2 seconds and negligible CPU load. This patch also fixes few shellcheck complains. Reviewed-by: Gabriel A. Devenyi <gdevenyi@gmail.com> Reviewed-by: Tony Hutter <hutter2@llnl.gov> Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Co-authored-by: Jeff Johnson <jeff.johnson@aeoncomputing.com> Signed-off-by: Jeff Johnson <jeff.johnson@aeoncomputing.com> Signed-off-by: Arshad Hussain <arshad.hussain@aeoncomputing.com> Closes #11526
This commit is contained in:
parent
47ada9e5ed
commit
bdd43a2396
|
@ -79,6 +79,34 @@
|
||||||
# channel 86:00.0 1 A
|
# channel 86:00.0 1 A
|
||||||
# channel 86:00.0 0 B
|
# channel 86:00.0 0 B
|
||||||
|
|
||||||
|
# #
|
||||||
|
# # Example vdev_id.conf - multipath / multijbod-daisychaining
|
||||||
|
# #
|
||||||
|
#
|
||||||
|
# multipath yes
|
||||||
|
# multijbod yes
|
||||||
|
#
|
||||||
|
# # PCI_ID HBA PORT CHANNEL NAME
|
||||||
|
# channel 85:00.0 1 A
|
||||||
|
# channel 85:00.0 0 B
|
||||||
|
# channel 86:00.0 1 A
|
||||||
|
# channel 86:00.0 0 B
|
||||||
|
|
||||||
|
# #
|
||||||
|
# # Example vdev_id.conf - multipath / mixed
|
||||||
|
# #
|
||||||
|
#
|
||||||
|
# multipath yes
|
||||||
|
# slot mix
|
||||||
|
#
|
||||||
|
# # PCI_ID HBA PORT CHANNEL NAME
|
||||||
|
# channel 85:00.0 3 A
|
||||||
|
# channel 85:00.0 2 B
|
||||||
|
# channel 86:00.0 3 A
|
||||||
|
# channel 86:00.0 2 B
|
||||||
|
# channel af:00.0 0 C
|
||||||
|
# channel af:00.0 1 C
|
||||||
|
|
||||||
# #
|
# #
|
||||||
# # Example vdev_id.conf - alias
|
# # Example vdev_id.conf - alias
|
||||||
# #
|
# #
|
||||||
|
@ -92,9 +120,10 @@ PATH=/bin:/sbin:/usr/bin:/usr/sbin
|
||||||
CONFIG=/etc/zfs/vdev_id.conf
|
CONFIG=/etc/zfs/vdev_id.conf
|
||||||
PHYS_PER_PORT=
|
PHYS_PER_PORT=
|
||||||
DEV=
|
DEV=
|
||||||
MULTIPATH=
|
|
||||||
TOPOLOGY=
|
TOPOLOGY=
|
||||||
BAY=
|
BAY=
|
||||||
|
ENCL_ID=""
|
||||||
|
UNIQ_ENCL_ID=""
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
cat << EOF
|
cat << EOF
|
||||||
|
@ -107,6 +136,7 @@ Usage: vdev_id [-h]
|
||||||
-e Create enclose device symlinks only (/dev/by-enclosure)
|
-e Create enclose device symlinks only (/dev/by-enclosure)
|
||||||
-g Storage network topology [default="$TOPOLOGY"]
|
-g Storage network topology [default="$TOPOLOGY"]
|
||||||
-m Run in multipath mode
|
-m Run in multipath mode
|
||||||
|
-j Run in multijbod mode
|
||||||
-p number of phy's per switch port [default=$PHYS_PER_PORT]
|
-p number of phy's per switch port [default=$PHYS_PER_PORT]
|
||||||
-h show this summary
|
-h show this summary
|
||||||
EOF
|
EOF
|
||||||
|
@ -117,12 +147,12 @@ map_slot() {
|
||||||
LINUX_SLOT=$1
|
LINUX_SLOT=$1
|
||||||
CHANNEL=$2
|
CHANNEL=$2
|
||||||
|
|
||||||
MAPPED_SLOT=`awk "\\$1 == \"slot\" && \\$2 == ${LINUX_SLOT} && \
|
MAPPED_SLOT=$(awk '$1 == "slot" && $2 == "${LINUX_SLOT}" && \
|
||||||
\\$4 ~ /^${CHANNEL}$|^$/ { print \\$3; exit }" $CONFIG`
|
$4 ~ /^${CHANNEL}$|^$/ { print $3; exit}' $CONFIG)
|
||||||
if [ -z "$MAPPED_SLOT" ] ; then
|
if [ -z "$MAPPED_SLOT" ] ; then
|
||||||
MAPPED_SLOT=$LINUX_SLOT
|
MAPPED_SLOT=$LINUX_SLOT
|
||||||
fi
|
fi
|
||||||
printf "%d" ${MAPPED_SLOT}
|
printf "%d" "${MAPPED_SLOT}"
|
||||||
}
|
}
|
||||||
|
|
||||||
map_channel() {
|
map_channel() {
|
||||||
|
@ -132,40 +162,120 @@ map_channel() {
|
||||||
|
|
||||||
case $TOPOLOGY in
|
case $TOPOLOGY in
|
||||||
"sas_switch")
|
"sas_switch")
|
||||||
MAPPED_CHAN=`awk "\\$1 == \"channel\" && \\$2 == ${PORT} \
|
MAPPED_CHAN=$(awk -v port="$PORT" \
|
||||||
{ print \\$3; exit }" $CONFIG`
|
'$1 == "channel" && $2 == ${PORT} \
|
||||||
|
{ print $3; exit }' $CONFIG)
|
||||||
;;
|
;;
|
||||||
"sas_direct"|"scsi")
|
"sas_direct"|"scsi")
|
||||||
MAPPED_CHAN=`awk "\\$1 == \"channel\" && \
|
MAPPED_CHAN=$(awk -v pciID="$PCI_ID" -v port="$PORT" \
|
||||||
\\$2 == \"${PCI_ID}\" && \\$3 == ${PORT} \
|
'$1 == "channel" && $2 == pciID && $3 == port \
|
||||||
{ print \\$4; exit }" $CONFIG`
|
{print $4}' $CONFIG)
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
printf "%s" ${MAPPED_CHAN}
|
printf "%s" "${MAPPED_CHAN}"
|
||||||
|
}
|
||||||
|
|
||||||
|
get_encl_id() {
|
||||||
|
set -- $(echo $1)
|
||||||
|
count=$#
|
||||||
|
|
||||||
|
i=1
|
||||||
|
while [ $i -le $count ] ; do
|
||||||
|
d=$(eval echo '$'{$i})
|
||||||
|
id=$(cat "/sys/class/enclosure/${d}/id")
|
||||||
|
ENCL_ID="${ENCL_ID} $id"
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
get_uniq_encl_id() {
|
||||||
|
for uuid in ${ENCL_ID}; do
|
||||||
|
found=0
|
||||||
|
|
||||||
|
for count in ${UNIQ_ENCL_ID}; do
|
||||||
|
if [ $count = $uuid ]; then
|
||||||
|
found=1
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ $found -eq 0 ]; then
|
||||||
|
UNIQ_ENCL_ID="${UNIQ_ENCL_ID} $uuid"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# map_jbod explainer: The bsg driver knows the difference between a SAS
|
||||||
|
# expander and fanout expander. Use hostX instance along with top-level
|
||||||
|
# (whole enclosure) expander instances in /sys/class/enclosure and
|
||||||
|
# matching a field in an array of expanders, using the index of the
|
||||||
|
# matched array field as the enclosure instance, thereby making jbod IDs
|
||||||
|
# dynamic. Avoids reliance on high overhead userspace commands like
|
||||||
|
# multipath and lsscsi and instead uses existing sysfs data. $HOSTCHAN
|
||||||
|
# variable derived from devpath gymnastics in sas_handler() function.
|
||||||
|
map_jbod() {
|
||||||
|
DEVEXP=$(ls -l "/sys/block/$DEV/device/" | grep enclos | awk -F/ '{print $(NF-1) }')
|
||||||
|
DEV=$1
|
||||||
|
|
||||||
|
# Use "set --" to create index values (Arrays)
|
||||||
|
set -- $(ls -l /sys/class/enclosure | grep -v "^total" | awk '{print $9}')
|
||||||
|
# Get count of total elements
|
||||||
|
JBOD_COUNT=$#
|
||||||
|
JBOD_ITEM=$*
|
||||||
|
|
||||||
|
# Build JBODs (enclosure) id from sys/class/enclosure/<dev>/id
|
||||||
|
get_encl_id "$JBOD_ITEM"
|
||||||
|
# Different expander instances for each paths.
|
||||||
|
# Filter out and keep only unique id.
|
||||||
|
get_uniq_encl_id
|
||||||
|
|
||||||
|
# Identify final 'mapped jbod'
|
||||||
|
j=0
|
||||||
|
for count in ${UNIQ_ENCL_ID}; do
|
||||||
|
i=1
|
||||||
|
j=$((j + 1))
|
||||||
|
while [ $i -le $JBOD_COUNT ] ; do
|
||||||
|
d=$(eval echo '$'{$i})
|
||||||
|
id=$(cat "/sys/class/enclosure/${d}/id")
|
||||||
|
if [ "$d" = "$DEVEXP" ] && [ $id = $count ] ; then
|
||||||
|
MAPPED_JBOD=$j
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
i=$((i + 1))
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
printf "%d" "${MAPPED_JBOD}"
|
||||||
}
|
}
|
||||||
|
|
||||||
sas_handler() {
|
sas_handler() {
|
||||||
if [ -z "$PHYS_PER_PORT" ] ; then
|
if [ -z "$PHYS_PER_PORT" ] ; then
|
||||||
PHYS_PER_PORT=`awk "\\$1 == \"phys_per_port\" \
|
PHYS_PER_PORT=$(awk '$1 == "phys_per_port" \
|
||||||
{print \\$2; exit}" $CONFIG`
|
{print $2; exit}' $CONFIG)
|
||||||
fi
|
fi
|
||||||
PHYS_PER_PORT=${PHYS_PER_PORT:-4}
|
PHYS_PER_PORT=${PHYS_PER_PORT:-4}
|
||||||
if ! echo $PHYS_PER_PORT | grep -q -E '^[0-9]+$' ; then
|
|
||||||
|
if ! echo "$PHYS_PER_PORT" | grep -q -E '^[0-9]+$' ; then
|
||||||
echo "Error: phys_per_port value $PHYS_PER_PORT is non-numeric"
|
echo "Error: phys_per_port value $PHYS_PER_PORT is non-numeric"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z "$MULTIPATH_MODE" ] ; then
|
if [ -z "$MULTIPATH_MODE" ] ; then
|
||||||
MULTIPATH_MODE=`awk "\\$1 == \"multipath\" \
|
MULTIPATH_MODE=$(awk '$1 == "multipath" \
|
||||||
{print \\$2; exit}" $CONFIG`
|
{print $2; exit}' $CONFIG)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$MULTIJBOD_MODE" ] ; then
|
||||||
|
MULTIJBOD_MODE=$(awk '$1 == "multijbod" \
|
||||||
|
{print $2; exit}' $CONFIG)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Use first running component device if we're handling a dm-mpath device
|
# Use first running component device if we're handling a dm-mpath device
|
||||||
if [ "$MULTIPATH_MODE" = "yes" ] ; then
|
if [ "$MULTIPATH_MODE" = "yes" ] ; then
|
||||||
# If udev didn't tell us the UUID via DM_NAME, check /dev/mapper
|
# If udev didn't tell us the UUID via DM_NAME, check /dev/mapper
|
||||||
if [ -z "$DM_NAME" ] ; then
|
if [ -z "$DM_NAME" ] ; then
|
||||||
DM_NAME=`ls -l --full-time /dev/mapper |
|
DM_NAME=$(ls -l --full-time /dev/mapper |
|
||||||
awk "/\/$DEV$/{print \\$9}"`
|
grep "$DEV"$ | awk '{print $9}')
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# For raw disks udev exports DEVTYPE=partition when
|
# For raw disks udev exports DEVTYPE=partition when
|
||||||
|
@ -175,28 +285,41 @@ sas_handler() {
|
||||||
# we have to append the -part suffix directly in the
|
# we have to append the -part suffix directly in the
|
||||||
# helper.
|
# helper.
|
||||||
if [ "$DEVTYPE" != "partition" ] ; then
|
if [ "$DEVTYPE" != "partition" ] ; then
|
||||||
PART=`echo $DM_NAME | awk -Fp '/p/{print "-part"$2}'`
|
PART=$(echo "$DM_NAME" | awk -Fp '/p/{print "-part"$2}')
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Strip off partition information.
|
# Strip off partition information.
|
||||||
DM_NAME=`echo $DM_NAME | sed 's/p[0-9][0-9]*$//'`
|
DM_NAME=$(echo "$DM_NAME" | sed 's/p[0-9][0-9]*$//')
|
||||||
if [ -z "$DM_NAME" ] ; then
|
if [ -z "$DM_NAME" ] ; then
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Get the raw scsi device name from multipath -ll. Strip off
|
# Utilize DM device name to gather subordinate block devices
|
||||||
# leading pipe symbols to make field numbering consistent.
|
# using sysfs to avoid userspace utilities
|
||||||
DEV=`multipath -ll $DM_NAME |
|
DMDEV=$(ls -l --full-time /dev/mapper | grep $DM_NAME |
|
||||||
awk '/running/{gsub("^[|]"," "); print $3 ; exit}'`
|
awk '{gsub("../", " "); print $NF}')
|
||||||
|
|
||||||
|
# Use sysfs pointers in /sys/block/dm-X/slaves because using
|
||||||
|
# userspace tools creates lots of overhead and should be avoided
|
||||||
|
# whenever possible. Use awk to isolate lowest instance of
|
||||||
|
# sd device member in dm device group regardless of string
|
||||||
|
# length.
|
||||||
|
DEV=$(ls "/sys/block/$DMDEV/slaves" | awk '
|
||||||
|
{ len=sprintf ("%20s",length($0)); gsub(/ /,0,str); a[NR]=len "_" $0; }
|
||||||
|
END {
|
||||||
|
asort(a)
|
||||||
|
print substr(a[1],22)
|
||||||
|
}')
|
||||||
|
|
||||||
if [ -z "$DEV" ] ; then
|
if [ -z "$DEV" ] ; then
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if echo $DEV | grep -q ^/devices/ ; then
|
if echo "$DEV" | grep -q ^/devices/ ; then
|
||||||
sys_path=$DEV
|
sys_path=$DEV
|
||||||
else
|
else
|
||||||
sys_path=`udevadm info -q path -p /sys/block/$DEV 2>/dev/null`
|
sys_path=$(udevadm info -q path -p "/sys/block/$DEV" 2>/dev/null)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Use positional parameters as an ad-hoc array
|
# Use positional parameters as an ad-hoc array
|
||||||
|
@ -206,84 +329,104 @@ sas_handler() {
|
||||||
|
|
||||||
# Get path up to /sys/.../hostX
|
# Get path up to /sys/.../hostX
|
||||||
i=1
|
i=1
|
||||||
while [ $i -le $num_dirs ] ; do
|
|
||||||
d=$(eval echo \${$i})
|
while [ $i -le "$num_dirs" ] ; do
|
||||||
|
d=$(eval echo '$'{$i})
|
||||||
scsi_host_dir="$scsi_host_dir/$d"
|
scsi_host_dir="$scsi_host_dir/$d"
|
||||||
echo $d | grep -q -E '^host[0-9]+$' && break
|
echo "$d" | grep -q -E '^host[0-9]+$' && break
|
||||||
i=$(($i + 1))
|
i=$((i + 1))
|
||||||
done
|
done
|
||||||
|
|
||||||
if [ $i = $num_dirs ] ; then
|
# Lets grab the SAS host channel number and save it for JBOD sorting later
|
||||||
|
HOSTCHAN=$(echo "$d" | awk -F/ '{ gsub("host","",$NF); print $NF}')
|
||||||
|
|
||||||
|
if [ $i = "$num_dirs" ] ; then
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
PCI_ID=$(eval echo \${$(($i -1))} | awk -F: '{print $2":"$3}')
|
PCI_ID=$(eval echo '$'{$((i -1))} | awk -F: '{print $2":"$3}')
|
||||||
|
|
||||||
# In sas_switch mode, the directory four levels beneath
|
# In sas_switch mode, the directory four levels beneath
|
||||||
# /sys/.../hostX contains symlinks to phy devices that reveal
|
# /sys/.../hostX contains symlinks to phy devices that reveal
|
||||||
# the switch port number. In sas_direct mode, the phy links one
|
# the switch port number. In sas_direct mode, the phy links one
|
||||||
# directory down reveal the HBA port.
|
# directory down reveal the HBA port.
|
||||||
port_dir=$scsi_host_dir
|
port_dir=$scsi_host_dir
|
||||||
|
|
||||||
case $TOPOLOGY in
|
case $TOPOLOGY in
|
||||||
"sas_switch") j=$(($i + 4)) ;;
|
"sas_switch") j=$((i + 4)) ;;
|
||||||
"sas_direct") j=$(($i + 1)) ;;
|
"sas_direct") j=$((i + 1)) ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
i=$(($i + 1))
|
i=$((i + 1))
|
||||||
|
|
||||||
while [ $i -le $j ] ; do
|
while [ $i -le $j ] ; do
|
||||||
port_dir="$port_dir/$(eval echo \${$i})"
|
port_dir="$port_dir/$(eval echo '$'{$i})"
|
||||||
i=$(($i + 1))
|
i=$((i + 1))
|
||||||
done
|
done
|
||||||
|
|
||||||
PHY=`ls -d $port_dir/phy* 2>/dev/null | head -1 | awk -F: '{print $NF}'`
|
PHY=$(ls -d "$port_dir"/phy* 2>/dev/null | head -1 | awk -F: '{print $NF}')
|
||||||
if [ -z "$PHY" ] ; then
|
if [ -z "$PHY" ] ; then
|
||||||
PHY=0
|
PHY=0
|
||||||
fi
|
fi
|
||||||
PORT=$(( $PHY / $PHYS_PER_PORT ))
|
PORT=$((PHY / PHYS_PER_PORT))
|
||||||
|
|
||||||
# Look in /sys/.../sas_device/end_device-X for the bay_identifier
|
# Look in /sys/.../sas_device/end_device-X for the bay_identifier
|
||||||
# attribute.
|
# attribute.
|
||||||
end_device_dir=$port_dir
|
end_device_dir=$port_dir
|
||||||
while [ $i -lt $num_dirs ] ; do
|
|
||||||
d=$(eval echo \${$i})
|
while [ $i -lt "$num_dirs" ] ; do
|
||||||
|
d=$(eval echo '$'{$i})
|
||||||
end_device_dir="$end_device_dir/$d"
|
end_device_dir="$end_device_dir/$d"
|
||||||
if echo $d | grep -q '^end_device' ; then
|
if echo "$d" | grep -q '^end_device' ; then
|
||||||
end_device_dir="$end_device_dir/sas_device/$d"
|
end_device_dir="$end_device_dir/sas_device/$d"
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
i=$(($i + 1))
|
i=$((i + 1))
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# Add 'mix' slot type for environments where dm-multipath devices
|
||||||
|
# include end-devices connected via SAS expanders or direct connection
|
||||||
|
# to SAS HBA. A mixed connectivity environment such as pool devices
|
||||||
|
# contained in a SAS JBOD and spare drives or log devices directly
|
||||||
|
# connected in a server backplane without expanders in the I/O path.
|
||||||
SLOT=
|
SLOT=
|
||||||
|
|
||||||
case $BAY in
|
case $BAY in
|
||||||
"bay")
|
"bay")
|
||||||
SLOT=`cat $end_device_dir/bay_identifier 2>/dev/null`
|
SLOT=$(cat "$end_device_dir/bay_identifier" 2>/dev/null)
|
||||||
|
;;
|
||||||
|
"mix")
|
||||||
|
if [ $(cat "$end_device_dir/bay_identifier" 2>/dev/null) ] ; then
|
||||||
|
SLOT=$(cat "$end_device_dir/bay_identifier" 2>/dev/null)
|
||||||
|
else
|
||||||
|
SLOT=$(cat "$end_device_dir/phy_identifier" 2>/dev/null)
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
"phy")
|
"phy")
|
||||||
SLOT=`cat $end_device_dir/phy_identifier 2>/dev/null`
|
SLOT=$(cat "$end_device_dir/phy_identifier" 2>/dev/null)
|
||||||
;;
|
;;
|
||||||
"port")
|
"port")
|
||||||
d=$(eval echo \${$i})
|
d=$(eval echo '$'{$i})
|
||||||
SLOT=`echo $d | sed -e 's/^.*://'`
|
SLOT=$(echo "$d" | sed -e 's/^.*://')
|
||||||
;;
|
;;
|
||||||
"id")
|
"id")
|
||||||
i=$(($i + 1))
|
i=$((i + 1))
|
||||||
d=$(eval echo \${$i})
|
d=$(eval echo '$'{$i})
|
||||||
SLOT=`echo $d | sed -e 's/^.*://'`
|
SLOT=$(echo "$d" | sed -e 's/^.*://')
|
||||||
;;
|
;;
|
||||||
"lun")
|
"lun")
|
||||||
i=$(($i + 2))
|
i=$((i + 2))
|
||||||
d=$(eval echo \${$i})
|
d=$(eval echo '$'{$i})
|
||||||
SLOT=`echo $d | sed -e 's/^.*://'`
|
SLOT=$(echo "$d" | sed -e 's/^.*://')
|
||||||
;;
|
;;
|
||||||
"ses")
|
"ses")
|
||||||
# look for this SAS path in all SCSI Enclosure Services
|
# look for this SAS path in all SCSI Enclosure Services
|
||||||
# (SES) enclosures
|
# (SES) enclosures
|
||||||
sas_address=`cat $end_device_dir/sas_address 2>/dev/null`
|
sas_address=$(cat "$end_device_dir/sas_address" 2>/dev/null)
|
||||||
enclosures=`lsscsi -g | \
|
enclosures=$(lsscsi -g | \
|
||||||
sed -n -e '/enclosu/s/^.* \([^ ][^ ]*\) *$/\1/p'`
|
sed -n -e '/enclosu/s/^.* \([^ ][^ ]*\) *$/\1/p')
|
||||||
for enclosure in $enclosures; do
|
for enclosure in $enclosures; do
|
||||||
set -- $(sg_ses -p aes $enclosure | \
|
set -- $(sg_ses -p aes "$enclosure" | \
|
||||||
awk "/device slot number:/{slot=\$12} \
|
awk "/device slot number:/{slot=\$12} \
|
||||||
/SAS address: $sas_address/\
|
/SAS address: $sas_address/\
|
||||||
{print slot}")
|
{print slot}")
|
||||||
|
@ -298,42 +441,55 @@ sas_handler() {
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
CHAN=`map_channel $PCI_ID $PORT`
|
if [ "$MULTIJBOD_MODE" = "yes" ] ; then
|
||||||
SLOT=`map_slot $SLOT $CHAN`
|
CHAN=$(map_channel "$PCI_ID" "$PORT")
|
||||||
|
SLOT=$(map_slot "$SLOT" "$CHAN")
|
||||||
|
JBOD=$(map_jbod "$DEV")
|
||||||
|
|
||||||
if [ -z "$CHAN" ] ; then
|
if [ -z "$CHAN" ] ; then
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
echo ${CHAN}${SLOT}${PART}
|
echo "${CHAN}"-"${JBOD}"-"${SLOT}${PART}"
|
||||||
|
else
|
||||||
|
CHAN=$(map_channel "$PCI_ID" "$PORT")
|
||||||
|
SLOT=$(map_slot "$SLOT" "$CHAN")
|
||||||
|
|
||||||
|
if [ -z "$CHAN" ] ; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
echo "${CHAN}${SLOT}${PART}"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
scsi_handler() {
|
scsi_handler() {
|
||||||
if [ -z "$FIRST_BAY_NUMBER" ] ; then
|
if [ -z "$FIRST_BAY_NUMBER" ] ; then
|
||||||
FIRST_BAY_NUMBER=`awk "\\$1 == \"first_bay_number\" \
|
FIRST_BAY_NUMBER=$(awk '$1 == "first_bay_number" \
|
||||||
{print \\$2; exit}" $CONFIG`
|
{print $2; exit}' $CONFIG)
|
||||||
fi
|
fi
|
||||||
FIRST_BAY_NUMBER=${FIRST_BAY_NUMBER:-0}
|
FIRST_BAY_NUMBER=${FIRST_BAY_NUMBER:-0}
|
||||||
|
|
||||||
if [ -z "$PHYS_PER_PORT" ] ; then
|
if [ -z "$PHYS_PER_PORT" ] ; then
|
||||||
PHYS_PER_PORT=`awk "\\$1 == \"phys_per_port\" \
|
PHYS_PER_PORT=$(awk '$1 == "phys_per_port" \
|
||||||
{print \\$2; exit}" $CONFIG`
|
{print $2; exit}' $CONFIG)
|
||||||
fi
|
fi
|
||||||
PHYS_PER_PORT=${PHYS_PER_PORT:-4}
|
PHYS_PER_PORT=${PHYS_PER_PORT:-4}
|
||||||
if ! echo $PHYS_PER_PORT | grep -q -E '^[0-9]+$' ; then
|
|
||||||
|
if ! echo "$PHYS_PER_PORT" | grep -q -E '^[0-9]+$' ; then
|
||||||
echo "Error: phys_per_port value $PHYS_PER_PORT is non-numeric"
|
echo "Error: phys_per_port value $PHYS_PER_PORT is non-numeric"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z "$MULTIPATH_MODE" ] ; then
|
if [ -z "$MULTIPATH_MODE" ] ; then
|
||||||
MULTIPATH_MODE=`awk "\\$1 == \"multipath\" \
|
MULTIPATH_MODE=$(awk '$1 == "multipath" \
|
||||||
{print \\$2; exit}" $CONFIG`
|
{print $2; exit}' $CONFIG)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Use first running component device if we're handling a dm-mpath device
|
# Use first running component device if we're handling a dm-mpath device
|
||||||
if [ "$MULTIPATH_MODE" = "yes" ] ; then
|
if [ "$MULTIPATH_MODE" = "yes" ] ; then
|
||||||
# If udev didn't tell us the UUID via DM_NAME, check /dev/mapper
|
# If udev didn't tell us the UUID via DM_NAME, check /dev/mapper
|
||||||
if [ -z "$DM_NAME" ] ; then
|
if [ -z "$DM_NAME" ] ; then
|
||||||
DM_NAME=`ls -l --full-time /dev/mapper |
|
DM_NAME=$(ls -l --full-time /dev/mapper |
|
||||||
awk "/\/$DEV$/{print \\$9}"`
|
grep "$DEV"$ | awk '{print $9}')
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# For raw disks udev exports DEVTYPE=partition when
|
# For raw disks udev exports DEVTYPE=partition when
|
||||||
|
@ -343,28 +499,28 @@ scsi_handler() {
|
||||||
# we have to append the -part suffix directly in the
|
# we have to append the -part suffix directly in the
|
||||||
# helper.
|
# helper.
|
||||||
if [ "$DEVTYPE" != "partition" ] ; then
|
if [ "$DEVTYPE" != "partition" ] ; then
|
||||||
PART=`echo $DM_NAME | awk -Fp '/p/{print "-part"$2}'`
|
PART=$(echo "$DM_NAME" | awk -Fp '/p/{print "-part"$2}')
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Strip off partition information.
|
# Strip off partition information.
|
||||||
DM_NAME=`echo $DM_NAME | sed 's/p[0-9][0-9]*$//'`
|
DM_NAME=$(echo "$DM_NAME" | sed 's/p[0-9][0-9]*$//')
|
||||||
if [ -z "$DM_NAME" ] ; then
|
if [ -z "$DM_NAME" ] ; then
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Get the raw scsi device name from multipath -ll. Strip off
|
# Get the raw scsi device name from multipath -ll. Strip off
|
||||||
# leading pipe symbols to make field numbering consistent.
|
# leading pipe symbols to make field numbering consistent.
|
||||||
DEV=`multipath -ll $DM_NAME |
|
DEV=$(multipath -ll "$DM_NAME" |
|
||||||
awk '/running/{gsub("^[|]"," "); print $3 ; exit}'`
|
awk '/running/{gsub("^[|]"," "); print $3 ; exit}')
|
||||||
if [ -z "$DEV" ] ; then
|
if [ -z "$DEV" ] ; then
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if echo $DEV | grep -q ^/devices/ ; then
|
if echo "$DEV" | grep -q ^/devices/ ; then
|
||||||
sys_path=$DEV
|
sys_path=$DEV
|
||||||
else
|
else
|
||||||
sys_path=`udevadm info -q path -p /sys/block/$DEV 2>/dev/null`
|
sys_path=$(udevadm info -q path -p "/sys/block/$DEV" 2>/dev/null)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# expect sys_path like this, for example:
|
# expect sys_path like this, for example:
|
||||||
|
@ -377,44 +533,47 @@ scsi_handler() {
|
||||||
|
|
||||||
# Get path up to /sys/.../hostX
|
# Get path up to /sys/.../hostX
|
||||||
i=1
|
i=1
|
||||||
while [ $i -le $num_dirs ] ; do
|
|
||||||
d=$(eval echo \${$i})
|
while [ $i -le "$num_dirs" ] ; do
|
||||||
|
d=$(eval echo '$'{$i})
|
||||||
scsi_host_dir="$scsi_host_dir/$d"
|
scsi_host_dir="$scsi_host_dir/$d"
|
||||||
echo $d | grep -q -E '^host[0-9]+$' && break
|
|
||||||
i=$(($i + 1))
|
echo "$d" | grep -q -E '^host[0-9]+$' && break
|
||||||
|
i=$((i + 1))
|
||||||
done
|
done
|
||||||
|
|
||||||
if [ $i = $num_dirs ] ; then
|
if [ $i = "$num_dirs" ] ; then
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
PCI_ID=$(eval echo \${$(($i -1))} | awk -F: '{print $2":"$3}')
|
PCI_ID=$(eval echo '$'{$((i -1))} | awk -F: '{print $2":"$3}')
|
||||||
|
|
||||||
# In scsi mode, the directory two levels beneath
|
# In scsi mode, the directory two levels beneath
|
||||||
# /sys/.../hostX reveals the port and slot.
|
# /sys/.../hostX reveals the port and slot.
|
||||||
port_dir=$scsi_host_dir
|
port_dir=$scsi_host_dir
|
||||||
j=$(($i + 2))
|
j=$((i + 2))
|
||||||
|
|
||||||
i=$(($i + 1))
|
i=$((i + 1))
|
||||||
while [ $i -le $j ] ; do
|
while [ $i -le $j ] ; do
|
||||||
port_dir="$port_dir/$(eval echo \${$i})"
|
port_dir="$port_dir/$(eval echo '$'{$i})"
|
||||||
i=$(($i + 1))
|
i=$((i + 1))
|
||||||
done
|
done
|
||||||
|
|
||||||
set -- $(echo $port_dir | sed -e 's/^.*:\([^:]*\):\([^:]*\)$/\1 \2/')
|
set -- $(echo "$port_dir" | sed -e 's/^.*:\([^:]*\):\([^:]*\)$/\1 \2/')
|
||||||
PORT=$1
|
PORT=$1
|
||||||
SLOT=$(($2 + $FIRST_BAY_NUMBER))
|
SLOT=$(($2 + FIRST_BAY_NUMBER))
|
||||||
|
|
||||||
if [ -z "$SLOT" ] ; then
|
if [ -z "$SLOT" ] ; then
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
CHAN=`map_channel $PCI_ID $PORT`
|
CHAN=$(map_channel "$PCI_ID" "$PORT")
|
||||||
SLOT=`map_slot $SLOT $CHAN`
|
SLOT=$(map_slot "$SLOT" "$CHAN")
|
||||||
|
|
||||||
if [ -z "$CHAN" ] ; then
|
if [ -z "$CHAN" ] ; then
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
echo ${CHAN}${SLOT}${PART}
|
echo "${CHAN}${SLOT}${PART}"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Figure out the name for the enclosure symlink
|
# Figure out the name for the enclosure symlink
|
||||||
|
@ -425,7 +584,7 @@ enclosure_handler () {
|
||||||
|
|
||||||
# Get the enclosure ID ("0:0:0:0")
|
# Get the enclosure ID ("0:0:0:0")
|
||||||
ENC=$(basename $(readlink -m "/sys/$DEVPATH/../.."))
|
ENC=$(basename $(readlink -m "/sys/$DEVPATH/../.."))
|
||||||
if [ ! -d /sys/class/enclosure/$ENC ] ; then
|
if [ ! -d "/sys/class/enclosure/$ENC" ] ; then
|
||||||
# Not an enclosure, bail out
|
# Not an enclosure, bail out
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
@ -433,14 +592,14 @@ enclosure_handler () {
|
||||||
# Get the long sysfs device path to our enclosure. Looks like:
|
# Get the long sysfs device path to our enclosure. Looks like:
|
||||||
# /devices/pci0000:00/0000:00:03.0/0000:05:00.0/host0/port-0:0/ ... /enclosure/0:0:0:0
|
# /devices/pci0000:00/0000:00:03.0/0000:05:00.0/host0/port-0:0/ ... /enclosure/0:0:0:0
|
||||||
|
|
||||||
ENC_DEVICE=$(readlink /sys/class/enclosure/$ENC)
|
ENC_DEVICE=$(readlink "/sys/class/enclosure/$ENC")
|
||||||
|
|
||||||
# Grab the full path to the hosts port dir:
|
# Grab the full path to the hosts port dir:
|
||||||
# /devices/pci0000:00/0000:00:03.0/0000:05:00.0/host0/port-0:0
|
# /devices/pci0000:00/0000:00:03.0/0000:05:00.0/host0/port-0:0
|
||||||
PORT_DIR=$(echo $ENC_DEVICE | grep -Eo '.+host[0-9]+/port-[0-9]+:[0-9]+')
|
PORT_DIR=$(echo "$ENC_DEVICE" | grep -Eo '.+host[0-9]+/port-[0-9]+:[0-9]+')
|
||||||
|
|
||||||
# Get the port number
|
# Get the port number
|
||||||
PORT_ID=$(echo $PORT_DIR | grep -Eo "[0-9]+$")
|
PORT_ID=$(echo "$PORT_DIR" | grep -Eo "[0-9]+$")
|
||||||
|
|
||||||
# The PCI directory is two directories up from the port directory
|
# The PCI directory is two directories up from the port directory
|
||||||
# /sys/devices/pci0000:00/0000:00:03.0/0000:05:00.0
|
# /sys/devices/pci0000:00/0000:00:03.0/0000:05:00.0
|
||||||
|
@ -450,8 +609,8 @@ enclosure_handler () {
|
||||||
PCI_ID=$(echo "$PCI_ID_LONG" | sed -r 's/^[0-9]+://g')
|
PCI_ID=$(echo "$PCI_ID_LONG" | sed -r 's/^[0-9]+://g')
|
||||||
|
|
||||||
# Name our device according to vdev_id.conf (like "L0" or "U1").
|
# Name our device according to vdev_id.conf (like "L0" or "U1").
|
||||||
NAME=$(awk "/channel/{if (\$1 == \"channel\" && \$2 == \"$PCI_ID\" && \
|
NAME=$(awk '/channel/{if ($1 == "channel" && $2 == "$PCI_ID" && \
|
||||||
\$3 == \"$PORT_ID\") {print \$4int(count[\$4])}; count[\$4]++}" $CONFIG)
|
$3 == "$PORT_ID") {print ${4}int(count[$4])}; count[$4]++}' $CONFIG)
|
||||||
|
|
||||||
echo "${NAME}"
|
echo "${NAME}"
|
||||||
}
|
}
|
||||||
|
@ -487,9 +646,9 @@ alias_handler () {
|
||||||
# ambiguity seems unavoidable, so devices using this facility
|
# ambiguity seems unavoidable, so devices using this facility
|
||||||
# must not use such names.
|
# must not use such names.
|
||||||
DM_PART=
|
DM_PART=
|
||||||
if echo $DM_NAME | grep -q -E 'p[0-9][0-9]*$' ; then
|
if echo "$DM_NAME" | grep -q -E 'p[0-9][0-9]*$' ; then
|
||||||
if [ "$DEVTYPE" != "partition" ] ; then
|
if [ "$DEVTYPE" != "partition" ] ; then
|
||||||
DM_PART=`echo $DM_NAME | awk -Fp '/p/{print "-part"$2}'`
|
DM_PART=$(echo "$DM_NAME" | awk -Fp '/p/{print "-part"$2}')
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -497,21 +656,25 @@ alias_handler () {
|
||||||
for link in $DEVLINKS ; do
|
for link in $DEVLINKS ; do
|
||||||
# Remove partition information to match key of top-level device.
|
# Remove partition information to match key of top-level device.
|
||||||
if [ -n "$DM_PART" ] ; then
|
if [ -n "$DM_PART" ] ; then
|
||||||
link=`echo $link | sed 's/p[0-9][0-9]*$//'`
|
link=$(echo "$link" | sed 's/p[0-9][0-9]*$//')
|
||||||
fi
|
fi
|
||||||
# Check both the fully qualified and the base name of link.
|
# Check both the fully qualified and the base name of link.
|
||||||
for l in $link `basename $link` ; do
|
for l in $link $(basename "$link") ; do
|
||||||
alias=`awk "\\$1 == \"alias\" && \\$3 == \"${l}\" \
|
if [ ! -z "$l" ]; then
|
||||||
{ print \\$2; exit }" $CONFIG`
|
alias=$(awk -v var="$l" '($1 == "alias") && \
|
||||||
|
($3 == var) \
|
||||||
|
{ print $2; exit }' $CONFIG)
|
||||||
if [ -n "$alias" ] ; then
|
if [ -n "$alias" ] ; then
|
||||||
echo ${alias}${DM_PART}
|
echo "${alias}${DM_PART}"
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
while getopts 'c:d:eg:mp:h' OPTION; do
|
# main
|
||||||
|
while getopts 'c:d:eg:jmp:h' OPTION; do
|
||||||
case ${OPTION} in
|
case ${OPTION} in
|
||||||
c)
|
c)
|
||||||
CONFIG=${OPTARG}
|
CONFIG=${OPTARG}
|
||||||
|
@ -524,7 +687,9 @@ while getopts 'c:d:eg:mp:h' OPTION; do
|
||||||
# create the enclosure device symlinks only. We also need
|
# create the enclosure device symlinks only. We also need
|
||||||
# "enclosure_symlinks yes" set in vdev_id.config to actually create the
|
# "enclosure_symlinks yes" set in vdev_id.config to actually create the
|
||||||
# symlink.
|
# symlink.
|
||||||
ENCLOSURE_MODE=$(awk '{if ($1 == "enclosure_symlinks") print $2}' $CONFIG)
|
ENCLOSURE_MODE=$(awk '{if ($1 == "enclosure_symlinks") \
|
||||||
|
print $2}' "$CONFIG")
|
||||||
|
|
||||||
if [ "$ENCLOSURE_MODE" != "yes" ] ; then
|
if [ "$ENCLOSURE_MODE" != "yes" ] ; then
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
@ -535,6 +700,9 @@ while getopts 'c:d:eg:mp:h' OPTION; do
|
||||||
p)
|
p)
|
||||||
PHYS_PER_PORT=${OPTARG}
|
PHYS_PER_PORT=${OPTARG}
|
||||||
;;
|
;;
|
||||||
|
j)
|
||||||
|
MULTIJBOD_MODE=yes
|
||||||
|
;;
|
||||||
m)
|
m)
|
||||||
MULTIPATH_MODE=yes
|
MULTIPATH_MODE=yes
|
||||||
;;
|
;;
|
||||||
|
@ -544,7 +712,7 @@ while getopts 'c:d:eg:mp:h' OPTION; do
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
if [ ! -r $CONFIG ] ; then
|
if [ ! -r "$CONFIG" ] ; then
|
||||||
echo "Error: Config file \"$CONFIG\" not found"
|
echo "Error: Config file \"$CONFIG\" not found"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
@ -555,11 +723,11 @@ if [ -z "$DEV" ] && [ -z "$ENCLOSURE_MODE" ] ; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z "$TOPOLOGY" ] ; then
|
if [ -z "$TOPOLOGY" ] ; then
|
||||||
TOPOLOGY=`awk "\\$1 == \"topology\" {print \\$2; exit}" $CONFIG`
|
TOPOLOGY=$(awk '($1 == "topology") {print $2; exit}' "$CONFIG")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z "$BAY" ] ; then
|
if [ -z "$BAY" ] ; then
|
||||||
BAY=`awk "\\$1 == \"slot\" {print \\$2; exit}" $CONFIG`
|
BAY=$(awk '($1 == "slot") {print $2; exit}' "$CONFIG")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
TOPOLOGY=${TOPOLOGY:-sas_direct}
|
TOPOLOGY=${TOPOLOGY:-sas_direct}
|
||||||
|
@ -572,7 +740,7 @@ if [ "$ENCLOSURE_MODE" = "yes" ] && [ "$TOPOLOGY" = "sas_direct" ] ; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Just create the symlinks to the enclosure devices and then exit.
|
# Just create the symlinks to the enclosure devices and then exit.
|
||||||
ENCLOSURE_PREFIX=$(awk '/enclosure_symlinks_prefix/{print $2}' $CONFIG)
|
ENCLOSURE_PREFIX=$(awk '/enclosure_symlinks_prefix/{print $2}' "$CONFIG")
|
||||||
if [ -z "$ENCLOSURE_PREFIX" ] ; then
|
if [ -z "$ENCLOSURE_PREFIX" ] ; then
|
||||||
ENCLOSURE_PREFIX="enc"
|
ENCLOSURE_PREFIX="enc"
|
||||||
fi
|
fi
|
||||||
|
@ -582,16 +750,16 @@ if [ "$ENCLOSURE_MODE" = "yes" ] && [ "$TOPOLOGY" = "sas_direct" ] ; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# First check if an alias was defined for this device.
|
# First check if an alias was defined for this device.
|
||||||
ID_VDEV=`alias_handler`
|
ID_VDEV=$(alias_handler)
|
||||||
|
|
||||||
if [ -z "$ID_VDEV" ] ; then
|
if [ -z "$ID_VDEV" ] ; then
|
||||||
BAY=${BAY:-bay}
|
BAY=${BAY:-bay}
|
||||||
case $TOPOLOGY in
|
case $TOPOLOGY in
|
||||||
sas_direct|sas_switch)
|
sas_direct|sas_switch)
|
||||||
ID_VDEV=`sas_handler`
|
ID_VDEV=$(sas_handler)
|
||||||
;;
|
;;
|
||||||
scsi)
|
scsi)
|
||||||
ID_VDEV=`scsi_handler`
|
ID_VDEV=$(scsi_handler)
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Error: unknown topology $TOPOLOGY"
|
echo "Error: unknown topology $TOPOLOGY"
|
||||||
|
|
Loading…
Reference in New Issue