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