1#!/bin/sh 2# 3# vdev_id: udev helper to generate user-friendly names for JBOD disks 4# 5# This script parses the file /etc/zfs/vdev_id.conf to map a 6# physical path in a storage topology to a channel name. The 7# channel name is combined with a disk enclosure slot number to 8# create an alias that reflects the physical location of the drive. 9# This is particularly helpful when it comes to tasks like replacing 10# failed drives. Slot numbers may also be re-mapped in case the 11# default numbering is unsatisfactory. The drive aliases will be 12# created as symbolic links in /dev/disk/by-vdev. 13# 14# The currently supported topologies are sas_direct and sas_switch. 15# A multipath mode is supported in which dm-mpath devices are 16# handled by examining the first-listed running component disk. In 17# multipath mode the configuration file should contain a channel 18# definition with the same name for each path to a given enclosure. 19# 20# The alias keyword provides a simple way to map already-existing 21# device symlinks to more convenient names. It is suitable for 22# small, static configurations or for sites that have some automated 23# way to generate the mapping file. 24# 25# 26# Some example configuration files are given below. 27 28# # 29# # Example vdev_id.conf - sas_direct. 30# # 31# 32# multipath no 33# topology sas_direct 34# phys_per_port 4 35# slot bay 36# 37# # PCI_ID HBA PORT CHANNEL NAME 38# channel 85:00.0 1 A 39# channel 85:00.0 0 B 40# channel 86:00.0 1 C 41# channel 86:00.0 0 D 42# 43# # Custom mapping for Channel A 44# 45# # Linux Mapped 46# # Slot Slot Channel 47# slot 1 7 A 48# slot 2 10 A 49# slot 3 3 A 50# slot 4 6 A 51# 52# # Default mapping for B, C, and D 53# slot 1 4 54# slot 2 2 55# slot 3 1 56# slot 4 3 57 58# # 59# # Example vdev_id.conf - sas_switch 60# # 61# 62# topology sas_switch 63# 64# # SWITCH PORT CHANNEL NAME 65# channel 1 A 66# channel 2 B 67# channel 3 C 68# channel 4 D 69 70# # 71# # Example vdev_id.conf - multipath 72# # 73# 74# multipath yes 75# 76# # PCI_ID HBA PORT CHANNEL NAME 77# channel 85:00.0 1 A 78# channel 85:00.0 0 B 79# channel 86:00.0 1 A 80# channel 86:00.0 0 B 81 82# # 83# # Example vdev_id.conf - multipath / multijbod-daisychaining 84# # 85# 86# multipath yes 87# multijbod yes 88# 89# # PCI_ID HBA PORT CHANNEL NAME 90# channel 85:00.0 1 A 91# channel 85:00.0 0 B 92# channel 86:00.0 1 A 93# channel 86:00.0 0 B 94 95# # 96# # Example vdev_id.conf - multipath / mixed 97# # 98# 99# multipath yes 100# slot mix 101# 102# # PCI_ID HBA PORT CHANNEL NAME 103# channel 85:00.0 3 A 104# channel 85:00.0 2 B 105# channel 86:00.0 3 A 106# channel 86:00.0 2 B 107# channel af:00.0 0 C 108# channel af:00.0 1 C 109 110# # 111# # Example vdev_id.conf - alias 112# # 113# 114# # by-vdev 115# # name fully qualified or base name of device link 116# alias d1 /dev/disk/by-id/wwn-0x5000c5002de3b9ca 117# alias d2 wwn-0x5000c5002def789e 118 119PATH=/bin:/sbin:/usr/bin:/usr/sbin 120CONFIG=/etc/zfs/vdev_id.conf 121PHYS_PER_PORT= 122DEV= 123TOPOLOGY= 124BAY= 125ENCL_ID="" 126UNIQ_ENCL_ID="" 127ZPAD=1 128 129usage() { 130 cat << EOF 131Usage: vdev_id [-h] 132 vdev_id <-d device> [-c config_file] [-p phys_per_port] 133 [-g sas_direct|sas_switch|scsi] [-m] 134 135 -c specify name of an alternative config file [default=$CONFIG] 136 -d specify basename of device (i.e. sda) 137 -e Create enclose device symlinks only (/dev/by-enclosure) 138 -g Storage network topology [default="$TOPOLOGY"] 139 -m Run in multipath mode 140 -j Run in multijbod mode 141 -p number of phy's per switch port [default=$PHYS_PER_PORT] 142 -h show this summary 143EOF 144 exit 1 145 # exit with error to avoid processing usage message by a udev rule 146} 147 148map_slot() { 149 LINUX_SLOT=$1 150 CHANNEL=$2 151 152 MAPPED_SLOT=$(awk -v linux_slot="$LINUX_SLOT" -v channel="$CHANNEL" \ 153 '$1 == "slot" && $2 == linux_slot && \ 154 ($4 ~ "^"channel"$" || $4 ~ /^$/) { print $3; exit}' $CONFIG) 155 if [ -z "$MAPPED_SLOT" ] ; then 156 MAPPED_SLOT=$LINUX_SLOT 157 fi 158 printf "%0${ZPAD}d" "${MAPPED_SLOT}" 159} 160 161map_channel() { 162 MAPPED_CHAN= 163 PCI_ID=$1 164 PORT=$2 165 166 case $TOPOLOGY in 167 "sas_switch") 168 MAPPED_CHAN=$(awk -v port="$PORT" \ 169 '$1 == "channel" && $2 == port \ 170 { print $3; exit }' $CONFIG) 171 ;; 172 "sas_direct"|"scsi") 173 MAPPED_CHAN=$(awk -v pciID="$PCI_ID" -v port="$PORT" \ 174 '$1 == "channel" && $2 == pciID && $3 == port \ 175 {print $4}' $CONFIG) 176 ;; 177 esac 178 printf "%s" "${MAPPED_CHAN}" 179} 180 181get_encl_id() { 182 set -- $(echo $1) 183 count=$# 184 185 i=1 186 while [ $i -le $count ] ; do 187 d=$(eval echo '$'{$i}) 188 id=$(cat "/sys/class/enclosure/${d}/id") 189 ENCL_ID="${ENCL_ID} $id" 190 i=$((i + 1)) 191 done 192} 193 194get_uniq_encl_id() { 195 for uuid in ${ENCL_ID}; do 196 found=0 197 198 for count in ${UNIQ_ENCL_ID}; do 199 if [ $count = $uuid ]; then 200 found=1 201 break 202 fi 203 done 204 205 if [ $found -eq 0 ]; then 206 UNIQ_ENCL_ID="${UNIQ_ENCL_ID} $uuid" 207 fi 208 done 209} 210 211# map_jbod explainer: The bsg driver knows the difference between a SAS 212# expander and fanout expander. Use hostX instance along with top-level 213# (whole enclosure) expander instances in /sys/class/enclosure and 214# matching a field in an array of expanders, using the index of the 215# matched array field as the enclosure instance, thereby making jbod IDs 216# dynamic. Avoids reliance on high overhead userspace commands like 217# multipath and lsscsi and instead uses existing sysfs data. $HOSTCHAN 218# variable derived from devpath gymnastics in sas_handler() function. 219map_jbod() { 220 DEVEXP=$(ls -l "/sys/block/$DEV/device/" | grep enclos | awk -F/ '{print $(NF-1) }') 221 DEV=$1 222 223 # Use "set --" to create index values (Arrays) 224 set -- $(ls -l /sys/class/enclosure | grep -v "^total" | awk '{print $9}') 225 # Get count of total elements 226 JBOD_COUNT=$# 227 JBOD_ITEM=$* 228 229 # Build JBODs (enclosure) id from sys/class/enclosure/<dev>/id 230 get_encl_id "$JBOD_ITEM" 231 # Different expander instances for each paths. 232 # Filter out and keep only unique id. 233 get_uniq_encl_id 234 235 # Identify final 'mapped jbod' 236 j=0 237 for count in ${UNIQ_ENCL_ID}; do 238 i=1 239 j=$((j + 1)) 240 while [ $i -le $JBOD_COUNT ] ; do 241 d=$(eval echo '$'{$i}) 242 id=$(cat "/sys/class/enclosure/${d}/id") 243 if [ "$d" = "$DEVEXP" ] && [ $id = $count ] ; then 244 MAPPED_JBOD=$j 245 break 246 fi 247 i=$((i + 1)) 248 done 249 done 250 251 printf "%d" "${MAPPED_JBOD}" 252} 253 254sas_handler() { 255 if [ -z "$PHYS_PER_PORT" ] ; then 256 PHYS_PER_PORT=$(awk '$1 == "phys_per_port" \ 257 {print $2; exit}' $CONFIG) 258 fi 259 PHYS_PER_PORT=${PHYS_PER_PORT:-4} 260 261 if ! echo "$PHYS_PER_PORT" | grep -q -E '^[0-9]+$' ; then 262 echo "Error: phys_per_port value $PHYS_PER_PORT is non-numeric" 263 exit 1 264 fi 265 266 if [ -z "$MULTIPATH_MODE" ] ; then 267 MULTIPATH_MODE=$(awk '$1 == "multipath" \ 268 {print $2; exit}' $CONFIG) 269 fi 270 271 if [ -z "$MULTIJBOD_MODE" ] ; then 272 MULTIJBOD_MODE=$(awk '$1 == "multijbod" \ 273 {print $2; exit}' $CONFIG) 274 fi 275 276 # Use first running component device if we're handling a dm-mpath device 277 if [ "$MULTIPATH_MODE" = "yes" ] ; then 278 # If udev didn't tell us the UUID via DM_NAME, check /dev/mapper 279 if [ -z "$DM_NAME" ] ; then 280 DM_NAME=$(ls -l --full-time /dev/mapper | 281 grep "$DEV"$ | awk '{print $9}') 282 fi 283 284 # For raw disks udev exports DEVTYPE=partition when 285 # handling partitions, and the rules can be written to 286 # take advantage of this to append a -part suffix. For 287 # dm devices we get DEVTYPE=disk even for partitions so 288 # we have to append the -part suffix directly in the 289 # helper. 290 if [ "$DEVTYPE" != "partition" ] ; then 291 # WWNs end with number -> p<partition>, alphabet -> <partition> 292 # '3' + (WWN length 16) = 17 293 PART=${DM_NAME:17} 294 if [[ $PART = "p"* ]]; then 295 # Match p[number], remove the 'p' and prepend "-part" 296 PART=$(echo "$DM_NAME" | 297 awk 'match($0,/p[0-9]+$/) {print "-part"substr($0,RSTART+1,RLENGTH-1)}') 298 elif [[ $PART != "" ]]; then 299 PART="-part"${PART} 300 fi 301 fi 302 303 # Strip off partition information. 304 DM_NAME=${DM_NAME:0:17} 305 if [ -z "$DM_NAME" ] ; then 306 return 307 fi 308 309 # Utilize DM device name to gather subordinate block devices 310 # using sysfs to avoid userspace utilities 311 312 # If our DEVNAME is something like /dev/dm-177, then we may be 313 # able to get our DMDEV from it. 314 DMDEV=$(echo $DEVNAME | sed 's;/dev/;;g') 315 if [ -n "$DMDEV" ]; then 316 DEV=$(ls /sys/block/$DMDEV/slaves) 317 for elm in "${DEV[@]}"; do 318 if [[ $elm == "dm-"* ]]; then 319 DMDEV=$elm 320 break 321 fi 322 done 323 fi 324 325 if [ ! -e /sys/block/$DMDEV/slaves/* ] ; then 326 # It's not there, try looking in /dev/mapper 327 DMDEV=$(ls -l --full-time /dev/mapper | grep $DM_NAME | 328 awk '{gsub("../", " "); print $NF}' | head -n 1) 329 fi 330 331 # Use sysfs pointers in /sys/block/dm-X/slaves because using 332 # userspace tools creates lots of overhead and should be avoided 333 # whenever possible. Use awk to isolate lowest instance of 334 # sd device member in dm device group regardless of string 335 # length. 336 DEV=$(ls "/sys/block/$DMDEV/slaves" | awk ' 337 { len=sprintf ("%20s",length($0)); gsub(/ /,0,str); a[NR]=len "_" $0; } 338 END { 339 asort(a) 340 print substr(a[1],22) 341 }') 342 343 if [ -z "$DEV" ] ; then 344 return 345 fi 346 fi 347 348 if echo "$DEV" | grep -q ^/devices/ ; then 349 sys_path=$DEV 350 else 351 sys_path=$(udevadm info -q path -p "/sys/block/$DEV" 2>/dev/null) 352 fi 353 354 # Use positional parameters as an ad-hoc array 355 set -- $(echo "$sys_path" | tr / ' ') 356 num_dirs=$# 357 scsi_host_dir="/sys" 358 359 # Get path up to /sys/.../hostX 360 i=1 361 362 while [ $i -le "$num_dirs" ] ; do 363 d=$(eval echo '$'{$i}) 364 scsi_host_dir="$scsi_host_dir/$d" 365 echo "$d" | grep -q -E '^host[0-9]+$' && break 366 i=$((i + 1)) 367 done 368 369 # Lets grab the SAS host channel number and save it for JBOD sorting later 370 HOSTCHAN=$(echo "$d" | awk -F/ '{ gsub("host","",$NF); print $NF}') 371 372 if [ $i = "$num_dirs" ] ; then 373 return 374 fi 375 376 PCI_ID=$(eval echo '$'{$((i -1))} | awk -F: '{print $2":"$3}') 377 378 # In sas_switch mode, the directory four levels beneath 379 # /sys/.../hostX contains symlinks to phy devices that reveal 380 # the switch port number. In sas_direct mode, the phy links one 381 # directory down reveal the HBA port. 382 port_dir=$scsi_host_dir 383 384 case $TOPOLOGY in 385 "sas_switch") j=$((i + 4)) ;; 386 "sas_direct") j=$((i + 1)) ;; 387 esac 388 389 i=$((i + 1)) 390 391 while [ $i -le $j ] ; do 392 port_dir="$port_dir/$(eval echo '$'{$i})" 393 i=$((i + 1)) 394 done 395 396 PHY=$(ls -vd "$port_dir"/phy* 2>/dev/null | head -1 | awk -F: '{print $NF}') 397 if [ -z "$PHY" ] ; then 398 PHY=0 399 fi 400 PORT=$((PHY / PHYS_PER_PORT)) 401 402 # Look in /sys/.../sas_device/end_device-X for the bay_identifier 403 # attribute. 404 end_device_dir=$port_dir 405 406 while [ $i -lt "$num_dirs" ] ; do 407 d=$(eval echo '$'{$i}) 408 end_device_dir="$end_device_dir/$d" 409 if echo "$d" | grep -q '^end_device' ; then 410 end_device_dir="$end_device_dir/sas_device/$d" 411 break 412 fi 413 i=$((i + 1)) 414 done 415 416 # Add 'mix' slot type for environments where dm-multipath devices 417 # include end-devices connected via SAS expanders or direct connection 418 # to SAS HBA. A mixed connectivity environment such as pool devices 419 # contained in a SAS JBOD and spare drives or log devices directly 420 # connected in a server backplane without expanders in the I/O path. 421 SLOT= 422 423 case $BAY in 424 "bay") 425 SLOT=$(cat "$end_device_dir/bay_identifier" 2>/dev/null) 426 ;; 427 "mix") 428 if [ $(cat "$end_device_dir/bay_identifier" 2>/dev/null) ] ; then 429 SLOT=$(cat "$end_device_dir/bay_identifier" 2>/dev/null) 430 else 431 SLOT=$(cat "$end_device_dir/phy_identifier" 2>/dev/null) 432 fi 433 ;; 434 "phy") 435 SLOT=$(cat "$end_device_dir/phy_identifier" 2>/dev/null) 436 ;; 437 "port") 438 d=$(eval echo '$'{$i}) 439 SLOT=$(echo "$d" | sed -e 's/^.*://') 440 ;; 441 "id") 442 i=$((i + 1)) 443 d=$(eval echo '$'{$i}) 444 SLOT=$(echo "$d" | sed -e 's/^.*://') 445 ;; 446 "lun") 447 i=$((i + 2)) 448 d=$(eval echo '$'{$i}) 449 SLOT=$(echo "$d" | sed -e 's/^.*://') 450 ;; 451 "bay_lun") 452 # Like 'bay' but with the LUN number appened. Added for SAS 453 # multi-actuator HDDs, where one physical drive has multiple 454 # LUNs, thus multiple logical drives share the same bay number 455 i=$((i + 2)) 456 d=$(eval echo '$'{$i}) 457 LUN="-lun$(echo "$d" | sed -e 's/^.*://')" 458 SLOT=$(cat "$end_device_dir/bay_identifier" 2>/dev/null) 459 ;; 460 "ses") 461 # look for this SAS path in all SCSI Enclosure Services 462 # (SES) enclosures 463 sas_address=$(cat "$end_device_dir/sas_address" 2>/dev/null) 464 enclosures=$(lsscsi -g | \ 465 sed -n -e '/enclosu/s/^.* \([^ ][^ ]*\) *$/\1/p') 466 for enclosure in $enclosures; do 467 set -- $(sg_ses -p aes "$enclosure" | \ 468 awk "/device slot number:/{slot=\$12} \ 469 /SAS address: $sas_address/\ 470 {print slot}") 471 SLOT=$1 472 if [ -n "$SLOT" ] ; then 473 break 474 fi 475 done 476 ;; 477 esac 478 if [ -z "$SLOT" ] ; then 479 return 480 fi 481 482 if [ "$MULTIJBOD_MODE" = "yes" ] ; then 483 CHAN=$(map_channel "$PCI_ID" "$PORT") 484 SLOT=$(map_slot "$SLOT" "$CHAN") 485 JBOD=$(map_jbod "$DEV") 486 487 if [ -z "$CHAN" ] ; then 488 return 489 fi 490 echo "${CHAN}"-"${JBOD}"-"${SLOT}${LUN}${PART}" 491 else 492 CHAN=$(map_channel "$PCI_ID" "$PORT") 493 SLOT=$(map_slot "$SLOT" "$CHAN") 494 495 if [ -z "$CHAN" ] ; then 496 return 497 fi 498 echo "${CHAN}${SLOT}${LUN}${PART}" 499 fi 500} 501 502scsi_handler() { 503 if [ -z "$FIRST_BAY_NUMBER" ] ; then 504 FIRST_BAY_NUMBER=$(awk '$1 == "first_bay_number" \ 505 {print $2; exit}' $CONFIG) 506 fi 507 FIRST_BAY_NUMBER=${FIRST_BAY_NUMBER:-0} 508 509 if [ -z "$PHYS_PER_PORT" ] ; then 510 PHYS_PER_PORT=$(awk '$1 == "phys_per_port" \ 511 {print $2; exit}' $CONFIG) 512 fi 513 PHYS_PER_PORT=${PHYS_PER_PORT:-4} 514 515 if ! echo "$PHYS_PER_PORT" | grep -q -E '^[0-9]+$' ; then 516 echo "Error: phys_per_port value $PHYS_PER_PORT is non-numeric" 517 exit 1 518 fi 519 520 if [ -z "$MULTIPATH_MODE" ] ; then 521 MULTIPATH_MODE=$(awk '$1 == "multipath" \ 522 {print $2; exit}' $CONFIG) 523 fi 524 525 # Use first running component device if we're handling a dm-mpath device 526 if [ "$MULTIPATH_MODE" = "yes" ] ; then 527 # If udev didn't tell us the UUID via DM_NAME, check /dev/mapper 528 if [ -z "$DM_NAME" ] ; then 529 DM_NAME=$(ls -l --full-time /dev/mapper | 530 grep "$DEV"$ | awk '{print $9}') 531 fi 532 533 # For raw disks udev exports DEVTYPE=partition when 534 # handling partitions, and the rules can be written to 535 # take advantage of this to append a -part suffix. For 536 # dm devices we get DEVTYPE=disk even for partitions so 537 # we have to append the -part suffix directly in the 538 # helper. 539 if [ "$DEVTYPE" != "partition" ] ; then 540 # WWNs end with number -> p<partition>, alphabet -> <partition> 541 # '3' + (WWN length 16) = 17 542 PART=${DM_NAME:17} 543 if [[ $PART = "p"* ]]; then 544 # Match p[number], remove the 'p' and prepend "-part" 545 PART=$(echo "$DM_NAME" | 546 awk 'match($0,/p[0-9]+$/) {print "-part"substr($0,RSTART+1,RLENGTH-1)}') 547 elif [[ $PART != "" ]]; then 548 PART="-part"${PART} 549 fi 550 fi 551 552 # Strip off partition information. 553 DM_NAME=${DM_NAME:0:17} 554 if [ -z "$DM_NAME" ] ; then 555 return 556 fi 557 558 # Get the raw scsi device name from multipath -ll. Strip off 559 # leading pipe symbols to make field numbering consistent. 560 DEV=$(multipath -ll "$DM_NAME" | 561 awk '/running/{gsub("^[|]"," "); print $3 ; exit}') 562 if [ -z "$DEV" ] ; then 563 return 564 fi 565 fi 566 567 if echo "$DEV" | grep -q ^/devices/ ; then 568 sys_path=$DEV 569 else 570 sys_path=$(udevadm info -q path -p "/sys/block/$DEV" 2>/dev/null) 571 fi 572 573 # expect sys_path like this, for example: 574 # /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 575 576 # Use positional parameters as an ad-hoc array 577 set -- $(echo "$sys_path" | tr / ' ') 578 num_dirs=$# 579 scsi_host_dir="/sys" 580 581 # Get path up to /sys/.../hostX 582 i=1 583 584 while [ $i -le "$num_dirs" ] ; do 585 d=$(eval echo '$'{$i}) 586 scsi_host_dir="$scsi_host_dir/$d" 587 588 echo "$d" | grep -q -E '^host[0-9]+$' && break 589 i=$((i + 1)) 590 done 591 592 if [ $i = "$num_dirs" ] ; then 593 return 594 fi 595 596 PCI_ID=$(eval echo '$'{$((i -1))} | awk -F: '{print $2":"$3}') 597 598 # In scsi mode, the directory two levels beneath 599 # /sys/.../hostX reveals the port and slot. 600 port_dir=$scsi_host_dir 601 j=$((i + 2)) 602 603 i=$((i + 1)) 604 while [ $i -le $j ] ; do 605 port_dir="$port_dir/$(eval echo '$'{$i})" 606 i=$((i + 1)) 607 done 608 609 set -- $(echo "$port_dir" | sed -e 's/^.*:\([^:]*\):\([^:]*\)$/\1 \2/') 610 PORT=$1 611 SLOT=$(($2 + FIRST_BAY_NUMBER)) 612 613 if [ -z "$SLOT" ] ; then 614 return 615 fi 616 617 CHAN=$(map_channel "$PCI_ID" "$PORT") 618 SLOT=$(map_slot "$SLOT" "$CHAN") 619 620 if [ -z "$CHAN" ] ; then 621 return 622 fi 623 echo "${CHAN}${SLOT}${PART}" 624} 625 626# Figure out the name for the enclosure symlink 627enclosure_handler () { 628 # We get all the info we need from udev's DEVPATH variable: 629 # 630 # DEVPATH=/sys/devices/pci0000:00/0000:00:03.0/0000:05:00.0/host0/subsystem/devices/0:0:0:0/scsi_generic/sg0 631 632 # Get the enclosure ID ("0:0:0:0") 633 ENC="${DEVPATH%/*}" 634 ENC="${ENC%/*}" 635 ENC="${ENC##*/}" 636 if [ ! -d "/sys/class/enclosure/$ENC" ] ; then 637 # Not an enclosure, bail out 638 return 639 fi 640 641 # Get the long sysfs device path to our enclosure. Looks like: 642 # /devices/pci0000:00/0000:00:03.0/0000:05:00.0/host0/port-0:0/ ... /enclosure/0:0:0:0 643 644 ENC_DEVICE=$(readlink "/sys/class/enclosure/$ENC") 645 646 # Grab the full path to the hosts port dir: 647 # /devices/pci0000:00/0000:00:03.0/0000:05:00.0/host0/port-0:0 648 PORT_DIR=$(echo "$ENC_DEVICE" | grep -Eo '.+host[0-9]+/port-[0-9]+:[0-9]+') 649 650 # Get the port number 651 PORT_ID=$(echo "$PORT_DIR" | grep -Eo "[0-9]+$") 652 653 # The PCI directory is two directories up from the port directory 654 # /sys/devices/pci0000:00/0000:00:03.0/0000:05:00.0 655 PCI_ID_LONG="$(readlink -m "/sys/$PORT_DIR/../..")" 656 PCI_ID_LONG="${PCI_ID_LONG##*/}" 657 658 # Strip down the PCI address from 0000:05:00.0 to 05:00.0 659 PCI_ID="${PCI_ID_LONG#[0-9]*:}" 660 661 # Name our device according to vdev_id.conf (like "L0" or "U1"). 662 NAME=$(awk "/channel/{if (\$1 == \"channel\" && \$2 == \"$PCI_ID\" && \ 663 \$3 == \"$PORT_ID\") {print \$4\$3}}" $CONFIG) 664 665 echo "${NAME}" 666} 667 668alias_handler () { 669 # Special handling is needed to correctly append a -part suffix 670 # to partitions of device mapper devices. The DEVTYPE attribute 671 # is normally set to "disk" instead of "partition" in this case, 672 # so the udev rules won't handle that for us as they do for 673 # "plain" block devices. 674 # 675 # For example, we may have the following links for a device and its 676 # partitions, 677 # 678 # /dev/disk/by-id/dm-name-isw_dibgbfcije_ARRAY0 -> ../../dm-0 679 # /dev/disk/by-id/dm-name-isw_dibgbfcije_ARRAY0p1 -> ../../dm-1 680 # /dev/disk/by-id/dm-name-isw_dibgbfcije_ARRAY0p2 -> ../../dm-3 681 # 682 # and the following alias in vdev_id.conf. 683 # 684 # alias A0 dm-name-isw_dibgbfcije_ARRAY0 685 # 686 # The desired outcome is for the following links to be created 687 # without having explicitly defined aliases for the partitions. 688 # 689 # /dev/disk/by-vdev/A0 -> ../../dm-0 690 # /dev/disk/by-vdev/A0-part1 -> ../../dm-1 691 # /dev/disk/by-vdev/A0-part2 -> ../../dm-3 692 # 693 # Warning: The following grep pattern will misidentify whole-disk 694 # devices whose names end with 'p' followed by a string of 695 # digits as partitions, causing alias creation to fail. This 696 # ambiguity seems unavoidable, so devices using this facility 697 # must not use such names. 698 DM_PART= 699 if echo "$DM_NAME" | grep -q -E 'p[0-9][0-9]*$' ; then 700 if [ "$DEVTYPE" != "partition" ] ; then 701 # Match p[number], remove the 'p' and prepend "-part" 702 DM_PART=$(echo "$DM_NAME" | 703 awk 'match($0,/p[0-9]+$/) {print "-part"substr($0,RSTART+1,RLENGTH-1)}') 704 fi 705 fi 706 707 # DEVLINKS attribute must have been populated by already-run udev rules. 708 for link in $DEVLINKS ; do 709 # Remove partition information to match key of top-level device. 710 if [ -n "$DM_PART" ] ; then 711 link=$(echo "$link" | sed 's/p[0-9][0-9]*$//') 712 fi 713 # Check both the fully qualified and the base name of link. 714 for l in $link ${link##*/} ; do 715 if [ ! -z "$l" ]; then 716 alias=$(awk -v var="$l" '($1 == "alias") && \ 717 ($3 == var) \ 718 { print $2; exit }' $CONFIG) 719 if [ -n "$alias" ] ; then 720 echo "${alias}${DM_PART}" 721 return 722 fi 723 fi 724 done 725 done 726} 727 728# main 729while getopts 'c:d:eg:jmp:h' OPTION; do 730 case ${OPTION} in 731 c) 732 CONFIG=${OPTARG} 733 ;; 734 d) 735 DEV=${OPTARG} 736 ;; 737 e) 738 # When udev sees a scsi_generic device, it calls this script with -e to 739 # create the enclosure device symlinks only. We also need 740 # "enclosure_symlinks yes" set in vdev_id.config to actually create the 741 # symlink. 742 ENCLOSURE_MODE=$(awk '{if ($1 == "enclosure_symlinks") \ 743 print $2}' "$CONFIG") 744 745 if [ "$ENCLOSURE_MODE" != "yes" ] ; then 746 exit 0 747 fi 748 ;; 749 g) 750 TOPOLOGY=$OPTARG 751 ;; 752 p) 753 PHYS_PER_PORT=${OPTARG} 754 ;; 755 j) 756 MULTIJBOD_MODE=yes 757 ;; 758 m) 759 MULTIPATH_MODE=yes 760 ;; 761 h) 762 usage 763 ;; 764 esac 765done 766 767if [ ! -r "$CONFIG" ] ; then 768 echo "Error: Config file \"$CONFIG\" not found" 769 exit 1 770fi 771 772if [ -z "$DEV" ] && [ -z "$ENCLOSURE_MODE" ] ; then 773 echo "Error: missing required option -d" 774 exit 1 775fi 776 777if [ -z "$TOPOLOGY" ] ; then 778 TOPOLOGY=$(awk '($1 == "topology") {print $2; exit}' "$CONFIG") 779fi 780 781if [ -z "$BAY" ] ; then 782 BAY=$(awk '($1 == "slot") {print $2; exit}' "$CONFIG") 783fi 784 785ZPAD=$(awk '($1 == "zpad_slot") {print $2; exit}' "$CONFIG") 786 787TOPOLOGY=${TOPOLOGY:-sas_direct} 788 789# Should we create /dev/by-enclosure symlinks? 790if [ "$ENCLOSURE_MODE" = "yes" ] && [ "$TOPOLOGY" = "sas_direct" ] ; then 791 ID_ENCLOSURE=$(enclosure_handler) 792 if [ -z "$ID_ENCLOSURE" ] ; then 793 exit 0 794 fi 795 796 # Just create the symlinks to the enclosure devices and then exit. 797 ENCLOSURE_PREFIX=$(awk '/enclosure_symlinks_prefix/{print $2}' "$CONFIG") 798 if [ -z "$ENCLOSURE_PREFIX" ] ; then 799 ENCLOSURE_PREFIX="enc" 800 fi 801 echo "ID_ENCLOSURE=$ID_ENCLOSURE" 802 echo "ID_ENCLOSURE_PATH=by-enclosure/$ENCLOSURE_PREFIX-$ID_ENCLOSURE" 803 exit 0 804fi 805 806# First check if an alias was defined for this device. 807ID_VDEV=$(alias_handler) 808 809if [ -z "$ID_VDEV" ] ; then 810 BAY=${BAY:-bay} 811 case $TOPOLOGY in 812 sas_direct|sas_switch) 813 ID_VDEV=$(sas_handler) 814 ;; 815 scsi) 816 ID_VDEV=$(scsi_handler) 817 ;; 818 *) 819 echo "Error: unknown topology $TOPOLOGY" 820 exit 1 821 ;; 822 esac 823fi 824 825if [ -n "$ID_VDEV" ] ; then 826 echo "ID_VDEV=${ID_VDEV}" 827 echo "ID_VDEV_PATH=disk/by-vdev/${ID_VDEV}" 828fi 829