You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

378 lines
9.2 KiB

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