blob: f990c946711b8f37a302f4b283b61050ca5545ee [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001#!/bin/sh
2#
3# iscsi_offload
4#
5# Configure iSCSI offload engines for use with open-iscsi
6# Usage:
7# iscsi_offload [-d | -f | -i <ipaddr> | -t ] <nic>
8#
9# Copyright (c) 2011 Hannes Reinecke, SUSE Labs
10# This script is licensed under the GPL.
11#
12# The script creates an open-iscsi interface definition
13# in the style <nic>-<module>, where <nic> matches the
14# network interface passed on the commandline.
15# If '-t' (test mode) is passed as an option, the script
16# will not create nor modify any setting but just print
17# the currently active ones.
18#
19# Currently the script works with Broadcom (bnx2i) and
20# Chelsio T3 (cxgbi) iSCSI offload engines.
21# Should work with Chelsio T4, but has not been tested.
22# ServerEngines (be2iscsi) and QLogic (qla4xxx) can only
23# be configured via BIOS, open-iscsi support is still in
24# development.
25#
26
27#
28# Return codes:
29# 0: Success
30# 1: Invalid command line parameter
31# 2: iSCSI offloading not supported
32# 3: Error during module loading
33# 4: Cannot configure interface via iscsiadm, use BIOS setup
34# 5: internal error running iscsiadm
35#
36# Output:
37# <mac> [none|dhcp|ip <ipaddr>|ibft]
38# where
39# <mac>: MAC Address of the iSCSI offload engine
40# none: No IP configuration set for the iSCSI offload engine
41# dhcp: iSCSI offload engine configured for DHCP
42# ip: iSCSI offload engine configured with static IP address <ipaddr>
43# ibft: iSCSI offload engine configured from iBFT values
44#
45
46#
47# Figure out the MAC address of the iSCSI offload engine
48# corresponding to a NIC from a given PCI device.
49# bnx2 is using one PCI device per port for both network and iSCSI offloading
50# cxgb3 is using one PCI device for everything.
51#
52iscsi_macaddress_from_pcidevice()
53{
54 local path=$1
55 local if=$2
56 local h
57 local host
58
59 for h in $path/host* ; do
60 if [ -d "$h" ] ; then
61 host=${h##*/}
62 read netdev < /sys/class/iscsi_host/$host/netdev
63 if [ "$netdev" = "$IFNAME" ] ; then
64 read mac < /sys/class/iscsi_host/$host/hwaddress
65 if [ "$mac" != "00:00:00:00:00:00" ] ; then
66 echo "$mac"
67 fi
68 break;
69 fi
70 fi
71 done
72}
73
74#
75# Figure out the MAC address of the iSCSI offload engine
76# corresponding to a NIC from a given PCI function.
77# It is assumed that the MAC address of the iSCSI offload
78# engine is equal of the MAC address of the NIC plus one.
79# Suitable for be2iscsi and qla4xxx
80#
81iscsi_macaddress_from_pcifn()
82{
83 local path=$1
84 local if=$2
85 local h
86 local host
87 local ifmac
88
89 ifmac=$(ip addr show dev $if | sed -n 's/ *link\/ether \(.*\) brd.*/\1/p')
90 m5=$(( 0x${ifmac##*:} ))
91 m5=$(( $m5 + 1 ))
92 ifmac=$(printf "%s:%02x" ${ifmac%:*} $m5)
93 for host in /sys/class/iscsi_host/host* ; do
94 if [ -L "$host" ] ; then
95 read mac < $host/hwaddress
96 if [ "$mac" = "$ifmac" ] ; then
97 echo "$mac"
98 break;
99 fi
100 fi
101 done
102}
103
104update_iface_setting() {
105 local iface="$1"
106 local name="$2"
107 local value="$3"
108
109 iface_value=$(iscsiadm -m iface -I $iface | sed -n "s/$name = \(.*\)/\1/p")
110 if [ "$iface_value" = "<empty>" ] ; then
111 iface_value=
112 fi
113 if [ "$iface_value" != "$value" ] ; then
114 if ! iscsiadm -m iface -I $iface -o update -n "$name" -v "$value" ; then
115 return 1
116 fi
117 fi
118 return 0
119}
120
121while getopts di:t options ; do
122 case $options in
123 d ) mode=dhcp;;
124 i ) mode=static
125 optaddr=$OPTARG
126 ;;
127 f ) mode=firmware;;
128 t ) dry_run=1;;
129 ?) printf "Usage: %s [-d|-t|-i ipaddr|-f] ifname\n" $0
130 exit 1;;
131 esac
132done
133shift $(($OPTIND - 1))
134
135IFNAME=$1
136ibft_mode="none"
137
138if [ -z "$IFNAME" ] ; then
139 echo "No interface specified"
140 exit 1
141fi
142
143if [ "$dry_run" ] ; then
144 if [ "$mode" = "dhcp" ] ; then
145 echo "'-t' specified, ignoring '-d'"
146 mode=
147 elif [ "$mode" = "static" ] ; then
148 echo "'-t' specified, ignoring '-s'"
149 mode=
150 fi
151fi
152
153if [ ! -L /sys/class/net/$IFNAME ] ; then
154 echo "Interface $IFNAME not found"
155 exit 1
156fi
157
158if [ "$optaddr" ] && ! ip route get $optaddr ; then
159 echo "Invalid IP address $optaddr"
160 exit 1
161fi
162if [ "$dry_run" ] ; then
163 mode=
164fi
165
166
167ifpath=$(cd -P /sys/class/net/$IFNAME; echo $PWD)
168pcipath=$(cd -P $ifpath/device; echo $PWD)
169
170if [ -d $pcipath ] ; then
171 drvlink=$(readlink $pcipath/driver)
172 driver=${drvlink##*/}
173fi
174
175if [ -z "$driver" ] ; then
176 echo "No driver found for interface $IFNAME"
177 exit 1
178fi
179
180case "$driver" in
181 bnx2*)
182 mod=bnx2i
183 ;;
184 cxgb*)
185 mod=cxgb3i
186 ;;
187 be2*)
188 mod=be2iscsi
189 ;;
190 qla*)
191 mod=qla4xxx
192 ;;
193esac
194
195if [ -z "$mod" ] ; then
196 echo "iSCSI offloading not supported on interface $IFNAME"
197 exit 2
198fi
199
200# Check if the required modules are already loaded
201loaded=$(sed -n "/^$mod/p" /proc/modules)
202if [ -z "$loaded" ] ; then
203 modprobe $mod
204fi
205
206loaded=$(sed -n "/^$mod/p" /proc/modules)
207if [ -z "$loaded" ] ; then
208 echo "Loading of $mod.ko failed, please check dmesg"
209 exit 3
210fi
211
212# Get the correct MAC address for the various devices
213if [ "$mod" = "bnx2i" ] ; then
214 mac=$(iscsi_macaddress_from_pcidevice $pcipath $IFNAME)
215elif [ "$mod" = "cxgb3i" ] ; then
216 mac=$(iscsi_macaddress_from_pcidevice $pcipath $IFNAME)
217elif [ "$mod" = "be2iscsi" ] ; then
218 mac=$(iscsi_macaddress_from_pcifn $pcipath $IFNAME)
219elif [ "$mod" = "qla4xxx" ] ; then
220 mac=$(iscsi_macaddress_from_pcifn $pcipath $IFNAME)
221fi
222
223if [ -z "$mac" ] ; then
224 echo "iSCSI offloading not supported on interface $IFNAME"
225 exit 2
226fi
227
228gen_iface="$mod.$mac"
229ioe_iface="${IFNAME}-${mod}"
230
231# Get existing settings
232if iscsiadm -m iface -I $ioe_iface > /dev/null 2>&1 ; then
233 ioe_mac=$(iscsiadm -m iface -I $ioe_iface 2> /dev/null| sed -n "s/iface\.hwaddress = \(.*\)/\1/p")
234 ioe_mod=$(iscsiadm -m iface -I $ioe_iface 2> /dev/null| sed -n "s/iface\.transport_name = \(.*\)/\1/p")
235 ipaddr=$(iscsiadm -m iface -I $ioe_iface 2> /dev/null| sed -n "s/iface\.ipaddress = \(.*\)/\1/p")
236 if [ "$ipaddr" == "<empty>" ] ; then
237 ipaddr=
238 fi
239elif [ "$mod" = "be2iscsi" ] ; then
240 ioe_mac=$mac
241 ioe_mod=$mod
242else
243 # Create new interface
244 iscsiadm -m iface -I $ioe_iface --op=new 2> /dev/null
245 ioe_mac=
246 ioe_mod=
247 ipaddr=
248fi
249
250if [ -z "$dry_run" ] ; then
251 if [ "$ioe_mac" != "$mac" ] ; then
252 if [ -n "$ioe_mac" ] ; then
253 echo "Warning: Updating MAC address on iface $ioe_iface"
254 fi
255 update_iface_setting $ioe_iface iface.hwaddress "$mac"
256 fi
257
258 if [ "$ioe_mod" != "$mod" ] ; then
259 if [ -n "$ioe_mod" ] ; then
260 echo "Warning: Update transport on iface $ioe_iface"
261 fi
262 update_iface_setting $ioe_iface iface.transport_name "$mod"
263 fi
264elif [ -z "$ipaddr" ] ; then
265 ipaddr=$(iscsiadm -m iface -I $gen_iface 2> /dev/null| sed -n "s/iface\.ipaddress = \(.*\)/\1/p")
266 if [ "$ipaddr" = "<empty>" ] ; then
267 ipaddr=
268 fi
269elif [ "$ioe_mod" != "$mod" ] ; then
270 echo "Warning: Transport mismatch on iface $ioe_iface: $ioe_mod should be $mod"
271fi
272
273# Check iBFT setting
274for d in /sys/firmware/* ; do
275 [ -d $d ] || continue
276 [ -d $d/ethernet0 ] || continue
277 iboot_dir=$d
278done
279if [ -n "$iboot_dir" ] && [ -d "$iboot_dir" ] ; then
280 for if in ${iboot_dir}/ethernet* ; do
281 read ibft_mac < $if/mac
282 [ "$ibft_mac" = "$mac" ] || continue
283 ibft_origin=0
284 [ -f ${if}/origin ] && read ibft_origin < $if/origin
285 if [ "$ibft_origin" -eq 1 ] ; then
286 ibft_mode="static"
287 elif [ "$ibft_origin" -eq 3 ] ; then
288 ibft_mode="dhcp"
289 fi
290 [ -f $if/dhcp ] && read ibft_dhcp < $if/dhcp
291 if [ -n "$ibft_dhcp" -a "$ibft_mode" != "dhcp" ] ; then
292 ibft_mode=dhcp
293 fi
294 if [ "$ibft_mode" = "dhcp" ] ; then
295 ibft_ipaddr="0.0.0.0"
296 ibft_gateway=
297 ibft_mask=
298 break
299 fi
300 [ -f $if/ip-addr ] && read ibft_ipaddr < $if/ip-addr
301 [ -f $if/gateway ] && read ibft_gateway < $if/gateway
302 [ -f $if/subnet-mask ] && read ibft_mask < $if/subnet-mask
303 break
304 done
305fi
306
307if [ -z "$optaddr" ] && [ "$ibft_ipaddr" ] ; then
308 optaddr=$ibft_ipaddr
309fi
310
311# Check if the interface needs to be configured
312if [ -z "$mode" ] ; then
313 if [ "$ibft_mode" != "none" ] ; then
314 echo "$mac ibft"
315 mode="ibft"
316 elif [ -z "$ipaddr" ] ; then
317 echo "$mac none"
318 mode="none"
319 elif [ "$ipaddr" = "0.0.0.0" ] ; then
320 echo "$mac dhcp"
321 ipaddr=
322 mode="dhcp"
323 else
324 echo "$mac ip $ipaddr"
325 mode="static"
326 fi
327 [ "$dry_run" ] && exit 0
328elif [ "$mode" = "dhcp" ] ; then
329 if [ "$ipaddr" = "0.0.0.0" ] ; then
330 echo "$mac dhcp"
331 exit 0
332 fi
333 optaddr="0.0.0.0"
334elif [ "$mode" = "static" ] && [ "$ipaddr" = "$optaddr" ] ; then
335 echo "$mac ip $ipaddr"
336 exit 0
337fi
338
339if [ "$mod" = "be2iscsi" ] ; then
340 exit 4
341fi
342
343if ! update_iface_setting $ioe_iface iface.ipaddress "$optaddr" ; then
344 echo "Failed to set IP address: $?"
345 exit 1
346fi
347if ! update_iface_setting $gen_iface iface.ipaddress "$optaddr" ; then
348 echo "Failed to set IP address for generic interface: $?"
349 exit 1
350fi
351
352if ! update_iface_setting $ioe_iface iface.gateway "$ibft_gateway" ; then
353 echo "Failed to set gateway address: $?"
354 exit 1
355fi
356
357if ! update_iface_setting $gen_iface iface.gateway "$ibft_gateway" ; then
358 echo "Failed to set gateway address for generic interface: $?"
359 exit 1
360fi
361
362if ! update_iface_setting $ioe_iface iface.subnet_mask "$ibft_mask" ; then
363 echo "Failed to set subnet mask: $?"
364 exit 1
365fi
366
367if ! update_iface_setting $gen_iface iface.subnet_mask "$ibft_mask" ; then
368 echo "Failed to set subnet mask for generic interface: $?"
369 exit 1
370fi
371
372if [ "$mod" = "qla4xxx" ] ; then
373 iscsiadm -m iface -H $mac -o applyall
374fi
375ip link set dev $IFNAME up
376
377exit 0
378