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