Changeset View
Changeset View
Standalone View
Standalone View
sys/contrib/openzfs/cmd/vdev_id/vdev_id
Show First 20 Lines • Show All 74 Lines • ▼ Show 20 Lines | |||||
# | # | ||||
# # PCI_ID HBA PORT CHANNEL NAME | # # PCI_ID HBA PORT CHANNEL NAME | ||||
# channel 85:00.0 1 A | # channel 85:00.0 1 A | ||||
# channel 85:00.0 0 B | # channel 85:00.0 0 B | ||||
# 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 | ||||
# # | # # | ||||
# | # | ||||
# # by-vdev | # # by-vdev | ||||
# # name fully qualified or base name of device link | # # name fully qualified or base name of device link | ||||
# alias d1 /dev/disk/by-id/wwn-0x5000c5002de3b9ca | # alias d1 /dev/disk/by-id/wwn-0x5000c5002de3b9ca | ||||
# alias d2 wwn-0x5000c5002def789e | # alias d2 wwn-0x5000c5002def789e | ||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin | 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 | ||||
Usage: vdev_id [-h] | Usage: vdev_id [-h] | ||||
vdev_id <-d device> [-c config_file] [-p phys_per_port] | vdev_id <-d device> [-c config_file] [-p phys_per_port] | ||||
[-g sas_direct|sas_switch|scsi] [-m] | [-g sas_direct|sas_switch|scsi] [-m] | ||||
-c specify name of an alternative config file [default=$CONFIG] | -c specify name of an alternative config file [default=$CONFIG] | ||||
-d specify basename of device (i.e. sda) | -d specify basename of device (i.e. sda) | ||||
-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 | ||||
exit 0 | exit 0 | ||||
} | } | ||||
map_slot() { | 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() { | ||||
MAPPED_CHAN= | MAPPED_CHAN= | ||||
PCI_ID=$1 | PCI_ID=$1 | ||||
PORT=$2 | PORT=$2 | ||||
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 | fi | ||||
if [ -z "$MULTIJBOD_MODE" ] ; then | |||||
MULTIJBOD_MODE=$(awk '$1 == "multijbod" \ | |||||
{print $2; exit}' $CONFIG) | |||||
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 | ||||
# handling partitions, and the rules can be written to | # handling partitions, and the rules can be written to | ||||
# take advantage of this to append a -part suffix. For | # take advantage of this to append a -part suffix. For | ||||
# dm devices we get DEVTYPE=disk even for partitions so | # dm devices we get DEVTYPE=disk even for partitions so | ||||
# 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 | ||||
set -- $(echo "$sys_path" | tr / ' ') | set -- $(echo "$sys_path" | tr / ' ') | ||||
num_dirs=$# | num_dirs=$# | ||||
scsi_host_dir="/sys" | scsi_host_dir="/sys" | ||||
# 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}") | ||||
SLOT=$1 | SLOT=$1 | ||||
if [ -n "$SLOT" ] ; then | if [ -n "$SLOT" ] ; then | ||||
break | break | ||||
fi | fi | ||||
done | done | ||||
;; | ;; | ||||
esac | esac | ||||
if [ -z "$SLOT" ] ; then | if [ -z "$SLOT" ] ; then | ||||
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 | ||||
# handling partitions, and the rules can be written to | # handling partitions, and the rules can be written to | ||||
# take advantage of this to append a -part suffix. For | # take advantage of this to append a -part suffix. For | ||||
# dm devices we get DEVTYPE=disk even for partitions so | # dm devices we get DEVTYPE=disk even for partitions so | ||||
# 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: | ||||
# /devices/pci0000:00/0000:00:0b.0/0000:09:00.0/0000:0a:05.0/0000:0c:00.0/host3/target3:1:0/3:1:0:21/block/sdv | # /devices/pci0000:00/0000:00:0b.0/0000:09:00.0/0000:0a:05.0/0000:0c:00.0/host3/target3:1:0/3:1:0:21/block/sdv | ||||
# Use positional parameters as an ad-hoc array | # Use positional parameters as an ad-hoc array | ||||
set -- $(echo "$sys_path" | tr / ' ') | set -- $(echo "$sys_path" | tr / ' ') | ||||
num_dirs=$# | num_dirs=$# | ||||
scsi_host_dir="/sys" | scsi_host_dir="/sys" | ||||
# 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 | ||||
enclosure_handler () { | enclosure_handler () { | ||||
# We get all the info we need from udev's DEVPATH variable: | # We get all the info we need from udev's DEVPATH variable: | ||||
# | # | ||||
# DEVPATH=/sys/devices/pci0000:00/0000:00:03.0/0000:05:00.0/host0/subsystem/devices/0:0:0:0/scsi_generic/sg0 | # DEVPATH=/sys/devices/pci0000:00/0000:00:03.0/0000:05:00.0/host0/subsystem/devices/0:0:0:0/scsi_generic/sg0 | ||||
# 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 | ||||
# 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 | ||||
PCI_ID_LONG=$(basename $(readlink -m "/sys/$PORT_DIR/../..")) | PCI_ID_LONG=$(basename $(readlink -m "/sys/$PORT_DIR/../..")) | ||||
# Strip down the PCI address from 0000:05:00.0 to 05:00.0 | # Strip down the PCI address from 0000:05:00.0 to 05:00.0 | ||||
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}" | ||||
} | } | ||||
alias_handler () { | alias_handler () { | ||||
# Special handling is needed to correctly append a -part suffix | # Special handling is needed to correctly append a -part suffix | ||||
# to partitions of device mapper devices. The DEVTYPE attribute | # to partitions of device mapper devices. The DEVTYPE attribute | ||||
# is normally set to "disk" instead of "partition" in this case, | # is normally set to "disk" instead of "partition" in this case, | ||||
Show All 19 Lines | alias_handler () { | ||||
# /dev/disk/by-vdev/A0-part2 -> ../../dm-3 | # /dev/disk/by-vdev/A0-part2 -> ../../dm-3 | ||||
# | # | ||||
# Warning: The following grep pattern will misidentify whole-disk | # Warning: The following grep pattern will misidentify whole-disk | ||||
# devices whose names end with 'p' followed by a string of | # devices whose names end with 'p' followed by a string of | ||||
# digits as partitions, causing alias creation to fail. This | # digits as partitions, causing alias creation to fail. This | ||||
# 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 | ||||
# DEVLINKS attribute must have been populated by already-run udev rules. | # DEVLINKS attribute must have been populated by already-run udev rules. | ||||
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} | ||||
;; | ;; | ||||
d) | d) | ||||
DEV=${OPTARG} | DEV=${OPTARG} | ||||
;; | ;; | ||||
e) | e) | ||||
# When udev sees a scsi_generic device, it calls this script with -e to | # When udev sees a scsi_generic device, it calls this script with -e to | ||||
# 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 | ||||
;; | ;; | ||||
g) | g) | ||||
TOPOLOGY=$OPTARG | TOPOLOGY=$OPTARG | ||||
;; | ;; | ||||
p) | p) | ||||
PHYS_PER_PORT=${OPTARG} | PHYS_PER_PORT=${OPTARG} | ||||
;; | ;; | ||||
j) | |||||
MULTIJBOD_MODE=yes | |||||
;; | |||||
m) | m) | ||||
MULTIPATH_MODE=yes | MULTIPATH_MODE=yes | ||||
;; | ;; | ||||
h) | h) | ||||
usage | usage | ||||
;; | ;; | ||||
esac | esac | ||||
done | done | ||||
if [ ! -r $CONFIG ] ; then | if [ ! -r "$CONFIG" ] ; then | ||||
echo "Error: Config file \"$CONFIG\" not found" | |||||
exit 0 | exit 0 | ||||
fi | fi | ||||
if [ -z "$DEV" ] && [ -z "$ENCLOSURE_MODE" ] ; then | if [ -z "$DEV" ] && [ -z "$ENCLOSURE_MODE" ] ; then | ||||
echo "Error: missing required option -d" | echo "Error: missing required option -d" | ||||
exit 1 | exit 1 | ||||
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} | ||||
# Should we create /dev/by-enclosure symlinks? | # Should we create /dev/by-enclosure symlinks? | ||||
if [ "$ENCLOSURE_MODE" = "yes" ] && [ "$TOPOLOGY" = "sas_direct" ] ; then | if [ "$ENCLOSURE_MODE" = "yes" ] && [ "$TOPOLOGY" = "sas_direct" ] ; then | ||||
ID_ENCLOSURE=$(enclosure_handler) | ID_ENCLOSURE=$(enclosure_handler) | ||||
if [ -z "$ID_ENCLOSURE" ] ; then | if [ -z "$ID_ENCLOSURE" ] ; then | ||||
exit 0 | exit 0 | ||||
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 | ||||
echo "ID_ENCLOSURE=$ID_ENCLOSURE" | echo "ID_ENCLOSURE=$ID_ENCLOSURE" | ||||
echo "ID_ENCLOSURE_PATH=by-enclosure/$ENCLOSURE_PREFIX-$ID_ENCLOSURE" | echo "ID_ENCLOSURE_PATH=by-enclosure/$ENCLOSURE_PREFIX-$ID_ENCLOSURE" | ||||
exit 0 | exit 0 | ||||
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" | ||||
exit 1 | exit 1 | ||||
;; | ;; | ||||
esac | esac | ||||
fi | fi | ||||
if [ -n "$ID_VDEV" ] ; then | if [ -n "$ID_VDEV" ] ; then | ||||
echo "ID_VDEV=${ID_VDEV}" | echo "ID_VDEV=${ID_VDEV}" | ||||
echo "ID_VDEV_PATH=disk/by-vdev/${ID_VDEV}" | echo "ID_VDEV_PATH=disk/by-vdev/${ID_VDEV}" | ||||
fi | fi |