5 # Set BUSES and HOST_PORTS to match the topology of your system. As
6 # each port is enumerated it will be assigned the next channel name.
7 # The current script enumerates each port on a bus before moving on
8 # to enumerate the next bus.
10 # Every distribution, version of udev, and type of attached storage
11 # seems to result in slightly different formatting of the by-path
12 # name. For this reason you may need to adjust the parsing below
13 # to suit your needs. This is one of the reasons to use a custom
14 # /etc/zfs/zdev.conf file, it allows the by-path naming convention
15 # to change and still keep the simple <channel><rank> naming.
18 # -------------------------
19 # When the host accesses disk via SAS switches the combination of
20 # bus and port number does not necessarily uniquely identify a
21 # channel or disk drawer. In this case we must resort to other
22 # means to infer the physical topology. For a single-level network
23 # (i.e. no switch cascading) we can assign alphabetic channel labels
24 # based on the switch port number that the drawer is connected to.
25 # If support for more complex topologies is needed this script will
26 # need to be customized or replaced.
28 # In SAS-Switch mode (enabled with "-g switch" ) we require that
29 # udev has been configured to create per-disk symbolic links in
30 # /dev/disk/by-id of the form
31 # <label>-<UUID>-switch-port:<X>-slot:<Y>. <label> is a string that
32 # depends on the subsystem that created the link and defaults to
33 # "dm-uuid-mpath" (this prefix is used by multipathd). <UUID> is a
34 # unique identifier for the disk typically obtained from the scsi_id
35 # program. <X> and <Y> denote the switch port and disk slot
36 # numbers, respectively, and are typically obtained from sysfs.
38 AWK=${AWK:-/usr/bin/awk}
39 CONFIG=${CONFIG:-/etc/zfs/zdev.conf}
42 SWITCH_PORTS=( 0 1 2 3 4 5 6 7 8 9 )
43 CHANNELS=( A B C D E F G H I J K L M N O P Q R S T U V W X Y Z )
47 LABEL=${LABEL:-"dm-uuid-mpath"}
48 DEV_DISK_DIR="/dev/disk/by-path"
54 Usage: zpool_layout [-th] [-c file] [-b buses] [-o switch_ports]
55 [-p host_ports] [-n channels] [-m map] [-l label]
57 -c Alternate config file [default=${CONFIG}]
58 -b Enumerate buses [default="${BUSES[*]}"]
59 -o Enumerate switch ports [default="${SWITCH_PORTS[*]}"]
60 -p Enumerate host ports [default="${HOST_PORTS[*]}"]
61 -n Channel names [default="A..Z"]
62 -g Storage network topology [default="${TOPOLOGY}"]
63 -t Trigger and wait for udev to settle [default=${TRIGGER}]
64 -l Prefix of SAS-switch-mode device links [default=${LABEL}]
65 -m Slot mapping [default=${MAPPING}]
71 while getopts 'c:b:o:p:n:l:m:g:th' OPTION; do
80 SWITCH_PORTS=(${OPTARG})
83 HOST_PORTS=(${OPTARG})
92 MAPPING=`readlink -e ${OPTARG}`
106 # Verify mapping file exists if specified.
107 # Linux-Slot Custom-Slot
108 if [ ${MAPPING} != "linux" ] && [ ! -e ${MAPPING} ]; then
109 echo "Error: Mapping file '${MAPPING}' does not exist"
113 # Save stdout as fd #8, then redirect stdout to the config file.
121 if [ ${MAPPING} = "linux" ]; then
122 MAPPED_SLOT=${LINUX_SLOT}
124 MAPPED_SLOT=`${AWK} "\\$1 == ${LINUX_SLOT} && !/^#/ \
125 { print \\$2; exit }" $MAPPING`
127 printf "%d" ${MAPPED_SLOT}
130 # Generate host port layout table for comment header.
131 print_host_port_layout() {
132 echo "# ------------------ Host Port Layout ---------------------"
134 for (( i=0; i<${#BUSES[*]}; i++ )); do
135 printf "%-8d" ${BUSES[$i]}
139 for (( i=0, k=0; i<${#HOST_PORTS[*]}; i++ )); do
140 printf "# Port %-2d " ${HOST_PORTS[$i]}
142 for (( j=0; j<${#BUSES[*]}; j++, k++ )); do
143 let k=$j*${#HOST_PORTS[*]}+$i
144 printf "%-8s" ${CHANNELS[$k]}
151 # Generate SAS switch port layout table for comment header.
152 print_switch_port_layout() {
153 echo "# --------------- SAS Switch Port Layout ------------------"
154 echo -n "# Switch Port "
155 for (( i=0; i<${#SWITCH_PORTS[*]}; i++ )); do
156 printf "%3d" ${SWITCH_PORTS[$i]}
160 for (( i=0; i<${#SWITCH_PORTS[*]}; i++ )); do
161 printf "%3s" ${CHANNELS[$i]}
167 # Generate channel/disk layout table for comment header.
168 print_channel_layout() {
169 pushd ${DEV_DISK_DIR} >/dev/null
170 echo "# ----------------- Channel/Disk Layout -------------------"
171 echo "# Channel Disks"
172 if [ ${TOPOLOGY} = "switch" ] ; then
173 for (( i=0; i<${#SWITCH_PORTS[*]}; i++ )); do
174 printf "# %-9s" ${CHANNELS[$i]}
175 p=${SWITCH_PORTS[$i]}
176 ls ${LABEL}-+([0-9a-f])-switch-port:${p}-slot:+([0-9]) \
177 2>/dev/null | cut -f3 -d':' | sort -u -n | \
181 for (( i=0, k=0; i<${#BUSES[*]}; i++ )); do
182 for (( j=0; j<${#HOST_PORTS[*]}; j++, k++ )); do
183 printf "# %-9s" ${CHANNELS[$k]}
184 ls *:${BUSES[$i]}:*:${HOST_PORTS[$j]}* \
185 2>/dev/null | cut -f7 -d'-' | \
186 sort -u -n | xargs | tr ' ' ','
194 # Generate mapping from <channel><rank> to by-path name.
195 map_shortname_to_by_path() {
196 pushd ${DEV_DISK_DIR} >/dev/null
197 for (( i=0, k=0; i<${#BUSES[*]}; i++ )); do
198 for (( j=0; j<${#HOST_PORTS[*]}; j++, k++ )); do
199 BYPATH=(`ls *:${BUSES[$i]}:*:${HOST_PORTS[$j]}* \
200 2>/dev/null | grep -v part | \
201 sort -n -k7 -t'-' | cut -f1-6 -d'-'`)
202 SLOTS=(`ls *:${BUSES[$i]}:*:${HOST_PORTS[$j]}* \
203 2>/dev/null | grep -v part | \
204 sort -n -k7 -t'-' | cut -f7 -d'-'`)
207 for (( l=0; l<${#SLOTS[*]}; l++ )); do
208 MAPPED_SLOT=`map_slot ${SLOTS[$l]}`
209 printf "%s%d\t%s-%d\n" \
210 ${CHANNELS[$k]} ${MAPPED_SLOT} \
211 ${BYPATH[$l]} ${SLOTS[$l]} >>${TMP_FILE}
215 echo -n "# Channel ${CHANNELS[$k]}, "
216 echo "Bus ${BUSES[$i]}, Port ${HOST_PORTS[$j]}"
217 cat ${TMP_FILE} | sort -n -k2 -t${CHANNELS[$k]}
224 # Generate mapping from <channel><rank> to by-id name.
225 map_shortname_to_by_id() {
226 pushd ${DEV_DISK_DIR} >/dev/null
227 for (( i=0; i<${#SWITCH_PORTS[*]}; i++ )); do
228 p=${SWITCH_PORTS[$i]}
229 BYID=(`ls ${LABEL}-+([0-9a-f])-switch-port:${p}-slot:+([0-9]) \
230 2>/dev/null | grep -v part | sort -k3n -t':' | \
232 SLOTS=(`ls ${LABEL}-+([0-9a-f])-switch-port:${p}-slot:+([0-9]) \
233 2>/dev/null | grep -v part | sort -k3n -t':' | \
237 for (( l=0; l<${#SLOTS[*]}; l++ )); do
238 MAPPED_SLOT=`map_slot ${SLOTS[$l]}`
239 printf "%s%d\t%s:%d\n" \
240 ${CHANNELS[$i]} ${MAPPED_SLOT} ${BYID[$l]} \
241 ${SLOTS[$l]} >>${TMP_FILE}
245 echo -n "# Channel ${CHANNELS[$i]}, "
246 echo "SAS Switch Port ${SWITCH_PORTS[$i]}"
247 cat ${TMP_FILE} | sort -n -k2 -t${CHANNELS[$i]}
253 # Generate comment header.
255 echo "# Custom ${DEV_DISK_DIR} to /dev/disk/zpool mapping, "
256 echo "# based of the following physical cable layout."
261 print_host_port_layout
263 map_shortname_to_by_path
266 DEV_DISK_DIR="/dev/disk/by-id"
267 print_switch_port_layout
269 map_shortname_to_by_id
273 # Restore stdout from fd #8 and close fd #8.
276 if [ ${TRIGGER} = "yes" ]; then
277 udevadm trigger --action=change --subsystem-match=block