]>
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 | # | |
14 | # The only currently supported topologies are sas_direct and | |
15 | # sas_switch. A multipath mode is supported in which dm-mpath | |
16 | # devices are handled by examining the first-listed running | |
17 | # component disk. In multipath mode the configuration file | |
18 | # should contain a channel definition with the same name for | |
19 | # each path to a given enclosure. | |
20 | ||
21 | # | |
22 | # Some example configuration files are given below. | |
23 | ||
24 | # # | |
25 | # # Example vdev_id.conf - sas_direct. | |
26 | # # | |
27 | # | |
28 | # multipath no | |
29 | # topology sas_direct | |
30 | # phys_per_port 4 | |
31 | # | |
32 | # # PCI_ID HBA PORT CHANNEL NAME | |
33 | # channel 85:00.0 1 A | |
34 | # channel 85:00.0 0 B | |
35 | # channel 86:00.0 1 C | |
36 | # channel 86:00.0 0 D | |
37 | # | |
38 | # # Linux Mapped | |
39 | # # Slot Slot | |
40 | # slot 1 7 | |
41 | # slot 2 10 | |
42 | # slot 3 3 | |
43 | # slot 4 6 | |
44 | # slot 5 2 | |
45 | # slot 6 8 | |
46 | # slot 7 1 | |
47 | # slot 8 4 | |
48 | # slot 9 9 | |
49 | # slot 10 5 | |
50 | ||
51 | # # | |
52 | # # Example vdev_id.conf - sas_switch | |
53 | # # | |
54 | # | |
55 | # topology sas_switch | |
56 | # | |
57 | # # SWITCH PORT CHANNEL NAME | |
58 | # channel 1 A | |
59 | # channel 2 B | |
60 | # channel 3 C | |
61 | # channel 4 D | |
62 | ||
63 | # # | |
64 | # # Example vdev_id.conf - multipath | |
65 | # # | |
66 | # | |
67 | # multipath yes | |
68 | # | |
69 | # # PCI_ID HBA PORT CHANNEL NAME | |
70 | # channel 85:00.0 1 A | |
71 | # channel 85:00.0 0 B | |
72 | # channel 86:00.0 1 A | |
73 | # channel 86:00.0 0 B | |
74 | ||
75 | PATH=/bin:/sbin:/usr/bin:/usr/sbin | |
76 | CONFIG=/etc/zfs/vdev_id.conf | |
77 | PHYS_PER_PORT= | |
78 | DEV= | |
79 | SLOT_MAP= | |
80 | CHANNEL_MAP= | |
81 | MULTIPATH= | |
82 | TOPOLOGY= | |
821b6834 NB |
83 | |
84 | usage() { | |
85 | cat << EOF | |
86 | Usage: vdev_id [-h] | |
87 | vdev_id <-d device> [-c config_file] [-p phys_per_port] | |
88 | [-g sas_direct|sas_switch] [-m] | |
89 | ||
90 | -c specify name of alernate config file [default=$CONFIG] | |
91 | -d specify basename of device (i.e. sda) | |
92 | -g Storage network topology [default="$TOPOLOGY"] | |
93 | -m Run in multipath mode | |
94 | -p number of phy's per switch port [default=$PHYS_PER_PORT] | |
95 | -h show this summary | |
96 | EOF | |
97 | exit 0 | |
98 | } | |
99 | ||
100 | map_slot() { | |
101 | local LINUX_SLOT=$1 | |
102 | local MAPPED_SLOT= | |
103 | ||
104 | MAPPED_SLOT=`awk "/^slot / && \\$2 == ${LINUX_SLOT} \ | |
105 | { print \\$3; exit }" $CONFIG` | |
106 | if [ -z "$MAPPED_SLOT" ] ; then | |
107 | MAPPED_SLOT=$LINUX_SLOT | |
108 | fi | |
109 | printf "%d" ${MAPPED_SLOT} | |
110 | } | |
111 | ||
112 | map_channel() { | |
113 | local MAPPED_CHAN= | |
114 | local PCI_ID=$1 | |
115 | local PORT=$2 | |
116 | ||
117 | case $TOPOLOGY in | |
118 | "sas_switch") | |
119 | MAPPED_CHAN=`awk "/^channel / && \\$2 == ${PORT} \ | |
120 | { print \\$3; exit }" $CONFIG` | |
121 | ;; | |
122 | "sas_direct") | |
123 | MAPPED_CHAN=`awk "/^channel / && \\$2 == \"${PCI_ID}\" && \ | |
124 | \\$3 == ${PORT} { print \\$4; exit }" \ | |
125 | $CONFIG` | |
126 | ;; | |
127 | esac | |
128 | printf "%s" ${MAPPED_CHAN} | |
129 | } | |
130 | ||
131 | while getopts 'c:s:d:g:mp:h' OPTION; do | |
132 | case ${OPTION} in | |
133 | c) | |
134 | CONFIG=`readlink -e ${OPTARG}` | |
135 | ;; | |
136 | d) | |
137 | DEV=${OPTARG} | |
138 | ;; | |
139 | g) | |
140 | TOPOLOGY=$OPTARG | |
141 | ;; | |
142 | p) | |
143 | PHYS_PER_PORT=${OPTARG} | |
144 | ;; | |
145 | m) | |
146 | MULTIPATH_MODE=yes | |
147 | ;; | |
148 | s) | |
149 | SLOT_MAP=`readlink -e ${OPTARG}` | |
150 | if [ ! -r $SLOT_MAP ] ; then | |
151 | echo "Error: $SLOT_MAP is nonexistant or unreadable" | |
152 | exit 1 | |
153 | fi | |
154 | ;; | |
155 | h) | |
156 | usage | |
157 | ;; | |
158 | esac | |
159 | done | |
160 | ||
161 | if [ ! -r $CONFIG ] ; then | |
162 | exit 0 | |
163 | fi | |
164 | ||
165 | if [ -z "$DEV" ] ; then | |
166 | echo "Error: missing required option -d" | |
167 | exit 1 | |
168 | fi | |
169 | ||
170 | if [ -z "$TOPOLOGY" ] ; then | |
171 | TOPOLOGY=`awk "/^topology /{print \\$2; exit}" $CONFIG` | |
172 | fi | |
173 | TOPOLOGY=${TOPOLOGY:-sas_direct} | |
174 | case $TOPOLOGY in | |
175 | sas_direct|sas_switch) | |
176 | ;; | |
177 | *) | |
178 | echo "Error: unknown topology $TOPOLOGY" | |
179 | exit 1 | |
180 | ;; | |
181 | esac | |
182 | ||
183 | if [ -z "$PHYS_PER_PORT" ] ; then | |
184 | PHYS_PER_PORT=`awk "/^phys_per_port /{print \\$2; exit}" $CONFIG` | |
185 | fi | |
186 | PHYS_PER_PORT=${PHYS_PER_PORT:-4} | |
a6ef9522 | 187 | if ! echo $PHYS_PER_PORT | grep -q -E '^[0-9]+$' ; then |
821b6834 NB |
188 | echo "Error: phys_per_port value $PHYS_PER_PORT is non-numeric" |
189 | exit 1 | |
190 | fi | |
191 | ||
192 | if [ -z "$MULTIPATH_MODE" ] ; then | |
193 | MULTIPATH_MODE=`awk "/^multipath /{print \\$2; exit}" $CONFIG` | |
194 | fi | |
195 | ||
196 | # Use first running component device if we're handling a dm-mpath device. | |
197 | if [ "$MULTIPATH_MODE" = "yes" ] ; then | |
198 | # If udev didn't tell us the UUID via DM_NAME, find it in /dev/mapper | |
199 | if [ -z "$DM_NAME" ] ; then | |
200 | DM_NAME=`ls -l --full-time /dev/mapper | | |
201 | awk "/\/$DEV$/{print \\$9}"` | |
202 | fi | |
203 | ||
204 | # For raw disks udev exports DEVTYPE=partition when handling partitions, | |
205 | # and the rules can be written to take advantage of this to append a | |
206 | # -part suffix. For dm devices we get DEVTYPE=disk even for partitions | |
207 | # so we have to append the -part suffix directly in the helper. | |
208 | if [ "$DEVTYPE" != "partition" ] ; then | |
209 | PART=`echo $DM_NAME | awk -Fp '/p/{print "-part"$2}'` | |
210 | fi | |
211 | ||
212 | # Strip off partition information. | |
213 | DM_NAME=`echo $DM_NAME | sed 's/p[0-9][0-9]*$//'` | |
214 | if [ -z "$DM_NAME" ] ; then | |
215 | exit 0 | |
216 | fi | |
217 | ||
218 | # Get the raw scsi device name from multipath -l. | |
219 | DEV=`multipath -l $DM_NAME |awk '/running/{print $3 ; exit}'` | |
220 | if [ -z "$DEV" ] ; then | |
221 | exit 0 | |
222 | fi | |
223 | fi | |
224 | ||
225 | if echo $DEV | grep -q ^/devices/ ; then | |
226 | sys_path=$DEV | |
227 | else | |
228 | sys_path=`udevadm info -q path -p /sys/block/$DEV 2>/dev/null` | |
229 | fi | |
230 | ||
a6ef9522 NB |
231 | # Use positional parameters as an ad-hoc array |
232 | set -- $(echo "$sys_path" | tr / ' ') | |
233 | num_dirs=$# | |
821b6834 NB |
234 | scsi_host_dir="/sys" |
235 | ||
236 | # Get path up to /sys/.../hostX | |
a6ef9522 NB |
237 | i=1 |
238 | while [ $i -le $num_dirs ] ; do | |
38b344d2 | 239 | d=$(eval echo \${$i}) |
821b6834 | 240 | scsi_host_dir="$scsi_host_dir/$d" |
a6ef9522 NB |
241 | echo $d | grep -q -E '^host[0-9]+$' && break |
242 | i=$(($i + 1)) | |
821b6834 NB |
243 | done |
244 | ||
a6ef9522 | 245 | if [ $i = $num_dirs ] ; then |
821b6834 NB |
246 | exit 0 |
247 | fi | |
248 | ||
38b344d2 | 249 | PCI_ID=$(eval echo \${$(($i -1))} | awk -F: '{print $2":"$3}') |
821b6834 | 250 | |
a6ef9522 | 251 | # In sas_switch mode, the directory four levels beneath /sys/.../hostX |
821b6834 NB |
252 | # contains symlinks to phy devices that reveal the switch port number. In |
253 | # sas_direct mode, the phy links one directory down reveal the HBA port. | |
254 | port_dir=$scsi_host_dir | |
255 | case $TOPOLOGY in | |
a6ef9522 | 256 | "sas_switch") j=$(($i + 4)) ;; |
821b6834 NB |
257 | "sas_direct") j=$(($i + 1)) ;; |
258 | esac | |
a6ef9522 NB |
259 | |
260 | i=$(($i + 1)) | |
261 | while [ $i -le $j ] ; do | |
38b344d2 | 262 | port_dir="$port_dir/$(eval echo \${$i})" |
a6ef9522 | 263 | i=$(($i + 1)) |
821b6834 NB |
264 | done |
265 | ||
266 | PHY=`ls -d $port_dir/phy* 2>/dev/null | head -1 | awk -F: '{print $NF}'` | |
267 | if [ -z "$PHY" ] ; then | |
268 | exit 0 | |
269 | fi | |
270 | PORT=$(( $PHY / $PHYS_PER_PORT )) | |
271 | ||
272 | # Look in /sys/.../sas_device/end_device-X for the bay_identifier | |
273 | # attribute. | |
274 | end_device_dir=$port_dir | |
a6ef9522 | 275 | while [ $i -lt $num_dirs ] ; do |
38b344d2 | 276 | d=$(eval echo \${$i}) |
821b6834 | 277 | end_device_dir="$end_device_dir/$d" |
a6ef9522 | 278 | if echo $d | grep -q '^end_device' ; then |
821b6834 NB |
279 | end_device_dir="$end_device_dir/sas_device/$d" |
280 | break | |
281 | fi | |
a6ef9522 | 282 | i=$(($i + 1)) |
821b6834 NB |
283 | done |
284 | ||
285 | SLOT=`cat $end_device_dir/bay_identifier 2>/dev/null` | |
286 | if [ -z "$SLOT" ] ; then | |
287 | exit 0 | |
288 | fi | |
289 | ||
290 | SLOT=`map_slot $SLOT` | |
291 | CHAN=`map_channel $PCI_ID $PORT` | |
292 | if [ -z "$CHAN" ] ; then | |
293 | exit 0 | |
294 | fi | |
295 | ID_VDEV=${CHAN}${SLOT}${PART} | |
296 | ||
297 | echo "ID_VDEV=${ID_VDEV}" | |
298 | echo "ID_VDEV_PATH=disk/by-vdev/${ID_VDEV}" |