]>
Commit | Line | Data |
---|---|---|
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 |
119 | PATH=/bin:/sbin:/usr/bin:/usr/sbin |
120 | CONFIG=/etc/zfs/vdev_id.conf | |
121 | PHYS_PER_PORT= | |
122 | DEV= | |
821b6834 | 123 | TOPOLOGY= |
bba365cf | 124 | BAY= |
677cdf78 AH |
125 | ENCL_ID="" |
126 | UNIQ_ENCL_ID="" | |
821b6834 NB |
127 | |
128 | usage() { | |
129 | cat << EOF | |
130 | Usage: 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 | |
142 | EOF | |
b0f3f393 AG |
143 | exit 1 |
144 | # exit with error to avoid processing usage message by a udev rule | |
821b6834 NB |
145 | } |
146 | ||
147 | map_slot() { | |
64025fa3 BB |
148 | LINUX_SLOT=$1 |
149 | CHANNEL=$2 | |
821b6834 | 150 | |
bc9c7265 AH |
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) | |
821b6834 NB |
154 | if [ -z "$MAPPED_SLOT" ] ; then |
155 | MAPPED_SLOT=$LINUX_SLOT | |
156 | fi | |
677cdf78 | 157 | printf "%d" "${MAPPED_SLOT}" |
821b6834 NB |
158 | } |
159 | ||
160 | map_channel() { | |
64025fa3 BB |
161 | MAPPED_CHAN= |
162 | PCI_ID=$1 | |
163 | PORT=$2 | |
821b6834 NB |
164 | |
165 | case $TOPOLOGY in | |
166 | "sas_switch") | |
677cdf78 | 167 | MAPPED_CHAN=$(awk -v port="$PORT" \ |
bc9c7265 | 168 | '$1 == "channel" && $2 == port \ |
677cdf78 | 169 | { print $3; exit }' $CONFIG) |
821b6834 | 170 | ;; |
269db7a4 | 171 | "sas_direct"|"scsi") |
677cdf78 AH |
172 | MAPPED_CHAN=$(awk -v pciID="$PCI_ID" -v port="$PORT" \ |
173 | '$1 == "channel" && $2 == pciID && $3 == port \ | |
174 | {print $4}' $CONFIG) | |
821b6834 NB |
175 | ;; |
176 | esac | |
677cdf78 AH |
177 | printf "%s" "${MAPPED_CHAN}" |
178 | } | |
179 | ||
180 | get_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 | ||
193 | get_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. | |
218 | map_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}" | |
821b6834 NB |
251 | } |
252 | ||
2957f38d NB |
253 | sas_handler() { |
254 | if [ -z "$PHYS_PER_PORT" ] ; then | |
677cdf78 AH |
255 | PHYS_PER_PORT=$(awk '$1 == "phys_per_port" \ |
256 | {print $2; exit}' $CONFIG) | |
2957f38d NB |
257 | fi |
258 | PHYS_PER_PORT=${PHYS_PER_PORT:-4} | |
677cdf78 AH |
259 | |
260 | if ! echo "$PHYS_PER_PORT" | grep -q -E '^[0-9]+$' ; then | |
2957f38d NB |
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 | |
677cdf78 AH |
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) | |
2957f38d NB |
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 | |
677cdf78 AH |
279 | DM_NAME=$(ls -l --full-time /dev/mapper | |
280 | grep "$DEV"$ | awk '{print $9}') | |
2957f38d NB |
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 | |
3ee4e6d8 TH |
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)}') | |
2957f38d NB |
293 | fi |
294 | ||
295 | # Strip off partition information. | |
677cdf78 | 296 | DM_NAME=$(echo "$DM_NAME" | sed 's/p[0-9][0-9]*$//') |
2957f38d NB |
297 | if [ -z "$DM_NAME" ] ; then |
298 | return | |
299 | fi | |
300 | ||
677cdf78 AH |
301 | # Utilize DM device name to gather subordinate block devices |
302 | # using sysfs to avoid userspace utilities | |
4fdbd434 TH |
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 | | |
677cdf78 | 310 | awk '{gsub("../", " "); print $NF}') |
4fdbd434 | 311 | fi |
677cdf78 AH |
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 | ||
2957f38d NB |
325 | if [ -z "$DEV" ] ; then |
326 | return | |
327 | fi | |
328 | fi | |
329 | ||
677cdf78 | 330 | if echo "$DEV" | grep -q ^/devices/ ; then |
2957f38d NB |
331 | sys_path=$DEV |
332 | else | |
677cdf78 | 333 | sys_path=$(udevadm info -q path -p "/sys/block/$DEV" 2>/dev/null) |
2957f38d NB |
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 | |
677cdf78 AH |
343 | |
344 | while [ $i -le "$num_dirs" ] ; do | |
345 | d=$(eval echo '$'{$i}) | |
2957f38d | 346 | scsi_host_dir="$scsi_host_dir/$d" |
677cdf78 AH |
347 | echo "$d" | grep -q -E '^host[0-9]+$' && break |
348 | i=$((i + 1)) | |
2957f38d NB |
349 | done |
350 | ||
677cdf78 AH |
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 | |
2957f38d NB |
355 | return |
356 | fi | |
357 | ||
677cdf78 | 358 | PCI_ID=$(eval echo '$'{$((i -1))} | awk -F: '{print $2":"$3}') |
2957f38d NB |
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 | |
677cdf78 | 365 | |
2957f38d | 366 | case $TOPOLOGY in |
677cdf78 AH |
367 | "sas_switch") j=$((i + 4)) ;; |
368 | "sas_direct") j=$((i + 1)) ;; | |
2957f38d NB |
369 | esac |
370 | ||
677cdf78 AH |
371 | i=$((i + 1)) |
372 | ||
2957f38d | 373 | while [ $i -le $j ] ; do |
677cdf78 AH |
374 | port_dir="$port_dir/$(eval echo '$'{$i})" |
375 | i=$((i + 1)) | |
2957f38d NB |
376 | done |
377 | ||
4d4998ea | 378 | PHY=$(ls -vd "$port_dir"/phy* 2>/dev/null | head -1 | awk -F: '{print $NF}') |
2957f38d | 379 | if [ -z "$PHY" ] ; then |
d49d9c2b | 380 | PHY=0 |
2957f38d | 381 | fi |
677cdf78 | 382 | PORT=$((PHY / PHYS_PER_PORT)) |
2957f38d NB |
383 | |
384 | # Look in /sys/.../sas_device/end_device-X for the bay_identifier | |
385 | # attribute. | |
386 | end_device_dir=$port_dir | |
677cdf78 AH |
387 | |
388 | while [ $i -lt "$num_dirs" ] ; do | |
389 | d=$(eval echo '$'{$i}) | |
2957f38d | 390 | end_device_dir="$end_device_dir/$d" |
677cdf78 | 391 | if echo "$d" | grep -q '^end_device' ; then |
2957f38d NB |
392 | end_device_dir="$end_device_dir/sas_device/$d" |
393 | break | |
394 | fi | |
677cdf78 | 395 | i=$((i + 1)) |
2957f38d NB |
396 | done |
397 | ||
677cdf78 AH |
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. | |
bba365cf | 403 | SLOT= |
677cdf78 | 404 | |
bba365cf AB |
405 | case $BAY in |
406 | "bay") | |
677cdf78 AH |
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 | |
bba365cf AB |
415 | ;; |
416 | "phy") | |
677cdf78 | 417 | SLOT=$(cat "$end_device_dir/phy_identifier" 2>/dev/null) |
bba365cf | 418 | ;; |
d49d9c2b | 419 | "port") |
677cdf78 AH |
420 | d=$(eval echo '$'{$i}) |
421 | SLOT=$(echo "$d" | sed -e 's/^.*://') | |
d49d9c2b | 422 | ;; |
bba365cf | 423 | "id") |
677cdf78 AH |
424 | i=$((i + 1)) |
425 | d=$(eval echo '$'{$i}) | |
426 | SLOT=$(echo "$d" | sed -e 's/^.*://') | |
bba365cf AB |
427 | ;; |
428 | "lun") | |
677cdf78 AH |
429 | i=$((i + 2)) |
430 | d=$(eval echo '$'{$i}) | |
431 | SLOT=$(echo "$d" | sed -e 's/^.*://') | |
bba365cf | 432 | ;; |
993669a7 SG |
433 | "ses") |
434 | # look for this SAS path in all SCSI Enclosure Services | |
435 | # (SES) enclosures | |
677cdf78 AH |
436 | sas_address=$(cat "$end_device_dir/sas_address" 2>/dev/null) |
437 | enclosures=$(lsscsi -g | \ | |
438 | sed -n -e '/enclosu/s/^.* \([^ ][^ ]*\) *$/\1/p') | |
993669a7 | 439 | for enclosure in $enclosures; do |
677cdf78 | 440 | set -- $(sg_ses -p aes "$enclosure" | \ |
993669a7 SG |
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 | ;; | |
bba365cf | 450 | esac |
2957f38d NB |
451 | if [ -z "$SLOT" ] ; then |
452 | return | |
453 | fi | |
454 | ||
677cdf78 AH |
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}" | |
2957f38d | 472 | fi |
2957f38d NB |
473 | } |
474 | ||
269db7a4 SG |
475 | scsi_handler() { |
476 | if [ -z "$FIRST_BAY_NUMBER" ] ; then | |
677cdf78 AH |
477 | FIRST_BAY_NUMBER=$(awk '$1 == "first_bay_number" \ |
478 | {print $2; exit}' $CONFIG) | |
269db7a4 SG |
479 | fi |
480 | FIRST_BAY_NUMBER=${FIRST_BAY_NUMBER:-0} | |
481 | ||
482 | if [ -z "$PHYS_PER_PORT" ] ; then | |
677cdf78 AH |
483 | PHYS_PER_PORT=$(awk '$1 == "phys_per_port" \ |
484 | {print $2; exit}' $CONFIG) | |
269db7a4 SG |
485 | fi |
486 | PHYS_PER_PORT=${PHYS_PER_PORT:-4} | |
677cdf78 AH |
487 | |
488 | if ! echo "$PHYS_PER_PORT" | grep -q -E '^[0-9]+$' ; then | |
269db7a4 SG |
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 | |
677cdf78 AH |
494 | MULTIPATH_MODE=$(awk '$1 == "multipath" \ |
495 | {print $2; exit}' $CONFIG) | |
269db7a4 SG |
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 | |
677cdf78 AH |
502 | DM_NAME=$(ls -l --full-time /dev/mapper | |
503 | grep "$DEV"$ | awk '{print $9}') | |
269db7a4 SG |
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 | |
3ee4e6d8 TH |
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)}') | |
269db7a4 SG |
516 | fi |
517 | ||
518 | # Strip off partition information. | |
677cdf78 | 519 | DM_NAME=$(echo "$DM_NAME" | sed 's/p[0-9][0-9]*$//') |
269db7a4 SG |
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. | |
677cdf78 AH |
526 | DEV=$(multipath -ll "$DM_NAME" | |
527 | awk '/running/{gsub("^[|]"," "); print $3 ; exit}') | |
269db7a4 SG |
528 | if [ -z "$DEV" ] ; then |
529 | return | |
530 | fi | |
531 | fi | |
532 | ||
677cdf78 | 533 | if echo "$DEV" | grep -q ^/devices/ ; then |
269db7a4 SG |
534 | sys_path=$DEV |
535 | else | |
677cdf78 | 536 | sys_path=$(udevadm info -q path -p "/sys/block/$DEV" 2>/dev/null) |
269db7a4 SG |
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 | |
677cdf78 AH |
549 | |
550 | while [ $i -le "$num_dirs" ] ; do | |
551 | d=$(eval echo '$'{$i}) | |
269db7a4 | 552 | scsi_host_dir="$scsi_host_dir/$d" |
677cdf78 AH |
553 | |
554 | echo "$d" | grep -q -E '^host[0-9]+$' && break | |
555 | i=$((i + 1)) | |
269db7a4 SG |
556 | done |
557 | ||
677cdf78 | 558 | if [ $i = "$num_dirs" ] ; then |
269db7a4 SG |
559 | return |
560 | fi | |
561 | ||
677cdf78 | 562 | PCI_ID=$(eval echo '$'{$((i -1))} | awk -F: '{print $2":"$3}') |
269db7a4 SG |
563 | |
564 | # In scsi mode, the directory two levels beneath | |
565 | # /sys/.../hostX reveals the port and slot. | |
566 | port_dir=$scsi_host_dir | |
677cdf78 | 567 | j=$((i + 2)) |
269db7a4 | 568 | |
677cdf78 | 569 | i=$((i + 1)) |
269db7a4 | 570 | while [ $i -le $j ] ; do |
677cdf78 AH |
571 | port_dir="$port_dir/$(eval echo '$'{$i})" |
572 | i=$((i + 1)) | |
269db7a4 SG |
573 | done |
574 | ||
677cdf78 | 575 | set -- $(echo "$port_dir" | sed -e 's/^.*:\([^:]*\):\([^:]*\)$/\1 \2/') |
269db7a4 | 576 | PORT=$1 |
677cdf78 | 577 | SLOT=$(($2 + FIRST_BAY_NUMBER)) |
269db7a4 SG |
578 | |
579 | if [ -z "$SLOT" ] ; then | |
580 | return | |
581 | fi | |
582 | ||
677cdf78 AH |
583 | CHAN=$(map_channel "$PCI_ID" "$PORT") |
584 | SLOT=$(map_slot "$SLOT" "$CHAN") | |
585 | ||
269db7a4 SG |
586 | if [ -z "$CHAN" ] ; then |
587 | return | |
588 | fi | |
677cdf78 | 589 | echo "${CHAN}${SLOT}${PART}" |
269db7a4 SG |
590 | } |
591 | ||
c66401fa TH |
592 | # Figure out the name for the enclosure symlink |
593 | enclosure_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") | |
420b4448 AZ |
599 | ENC="${DEVPATH%/*}" |
600 | ENC="${ENC%/*}" | |
601 | ENC="${ENC##*/}" | |
677cdf78 | 602 | if [ ! -d "/sys/class/enclosure/$ENC" ] ; then |
c66401fa TH |
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 | ||
677cdf78 | 610 | ENC_DEVICE=$(readlink "/sys/class/enclosure/$ENC") |
c66401fa TH |
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 | |
677cdf78 | 614 | PORT_DIR=$(echo "$ENC_DEVICE" | grep -Eo '.+host[0-9]+/port-[0-9]+:[0-9]+') |
c66401fa TH |
615 | |
616 | # Get the port number | |
677cdf78 | 617 | PORT_ID=$(echo "$PORT_DIR" | grep -Eo "[0-9]+$") |
c66401fa TH |
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 | |
420b4448 AZ |
621 | PCI_ID_LONG="$(readlink -m "/sys/$PORT_DIR/../..")" |
622 | PCI_ID_LONG="${PCI_ID_LONG##*/}" | |
c66401fa TH |
623 | |
624 | # Strip down the PCI address from 0000:05:00.0 to 05:00.0 | |
420b4448 | 625 | PCI_ID="${PCI_ID_LONG#[0-9]*:}" |
c66401fa TH |
626 | |
627 | # Name our device according to vdev_id.conf (like "L0" or "U1"). | |
1886cdfc TH |
628 | NAME=$(awk "/channel/{if (\$1 == \"channel\" && \$2 == \"$PCI_ID\" && \ |
629 | \$3 == \"$PORT_ID\") {print \$4\$3}}" $CONFIG) | |
c66401fa TH |
630 | |
631 | echo "${NAME}" | |
632 | } | |
633 | ||
2957f38d NB |
634 | alias_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. | |
64025fa3 | 664 | DM_PART= |
677cdf78 | 665 | if echo "$DM_NAME" | grep -q -E 'p[0-9][0-9]*$' ; then |
2957f38d | 666 | if [ "$DEVTYPE" != "partition" ] ; then |
3ee4e6d8 TH |
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)}') | |
2957f38d NB |
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 | |
677cdf78 | 677 | link=$(echo "$link" | sed 's/p[0-9][0-9]*$//') |
2957f38d NB |
678 | fi |
679 | # Check both the fully qualified and the base name of link. | |
420b4448 | 680 | for l in $link ${link##*/} ; do |
677cdf78 AH |
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 | |
2957f38d NB |
689 | fi |
690 | done | |
691 | done | |
692 | } | |
693 | ||
677cdf78 AH |
694 | # main |
695 | while getopts 'c:d:eg:jmp:h' OPTION; do | |
821b6834 NB |
696 | case ${OPTION} in |
697 | c) | |
2957f38d NB |
698 | CONFIG=${OPTARG} |
699 | ;; | |
821b6834 | 700 | d) |
2957f38d NB |
701 | DEV=${OPTARG} |
702 | ;; | |
c66401fa TH |
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. | |
677cdf78 AH |
708 | ENCLOSURE_MODE=$(awk '{if ($1 == "enclosure_symlinks") \ |
709 | print $2}' "$CONFIG") | |
710 | ||
c66401fa TH |
711 | if [ "$ENCLOSURE_MODE" != "yes" ] ; then |
712 | exit 0 | |
713 | fi | |
714 | ;; | |
821b6834 NB |
715 | g) |
716 | TOPOLOGY=$OPTARG | |
717 | ;; | |
718 | p) | |
719 | PHYS_PER_PORT=${OPTARG} | |
720 | ;; | |
677cdf78 AH |
721 | j) |
722 | MULTIJBOD_MODE=yes | |
723 | ;; | |
821b6834 NB |
724 | m) |
725 | MULTIPATH_MODE=yes | |
726 | ;; | |
821b6834 NB |
727 | h) |
728 | usage | |
729 | ;; | |
730 | esac | |
731 | done | |
732 | ||
677cdf78 | 733 | if [ ! -r "$CONFIG" ] ; then |
e2870fb2 | 734 | echo "Error: Config file \"$CONFIG\" not found" |
b0f3f393 | 735 | exit 1 |
821b6834 NB |
736 | fi |
737 | ||
64025fa3 | 738 | if [ -z "$DEV" ] && [ -z "$ENCLOSURE_MODE" ] ; then |
821b6834 NB |
739 | echo "Error: missing required option -d" |
740 | exit 1 | |
741 | fi | |
742 | ||
743 | if [ -z "$TOPOLOGY" ] ; then | |
677cdf78 | 744 | TOPOLOGY=$(awk '($1 == "topology") {print $2; exit}' "$CONFIG") |
821b6834 | 745 | fi |
a6ef9522 | 746 | |
bba365cf | 747 | if [ -z "$BAY" ] ; then |
677cdf78 | 748 | BAY=$(awk '($1 == "slot") {print $2; exit}' "$CONFIG") |
bba365cf AB |
749 | fi |
750 | ||
c66401fa TH |
751 | TOPOLOGY=${TOPOLOGY:-sas_direct} |
752 | ||
753 | # Should we create /dev/by-enclosure symlinks? | |
64025fa3 | 754 | if [ "$ENCLOSURE_MODE" = "yes" ] && [ "$TOPOLOGY" = "sas_direct" ] ; then |
c66401fa TH |
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. | |
677cdf78 | 761 | ENCLOSURE_PREFIX=$(awk '/enclosure_symlinks_prefix/{print $2}' "$CONFIG") |
c66401fa TH |
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 | |
768 | fi | |
769 | ||
2957f38d | 770 | # First check if an alias was defined for this device. |
677cdf78 | 771 | ID_VDEV=$(alias_handler) |
821b6834 | 772 | |
2957f38d | 773 | if [ -z "$ID_VDEV" ] ; then |
bba365cf | 774 | BAY=${BAY:-bay} |
2957f38d NB |
775 | case $TOPOLOGY in |
776 | sas_direct|sas_switch) | |
677cdf78 | 777 | ID_VDEV=$(sas_handler) |
2957f38d | 778 | ;; |
269db7a4 | 779 | scsi) |
677cdf78 | 780 | ID_VDEV=$(scsi_handler) |
269db7a4 | 781 | ;; |
2957f38d NB |
782 | *) |
783 | echo "Error: unknown topology $TOPOLOGY" | |
784 | exit 1 | |
785 | ;; | |
786 | esac | |
821b6834 NB |
787 | fi |
788 | ||
2957f38d NB |
789 | if [ -n "$ID_VDEV" ] ; then |
790 | echo "ID_VDEV=${ID_VDEV}" | |
791 | echo "ID_VDEV_PATH=disk/by-vdev/${ID_VDEV}" | |
821b6834 | 792 | fi |