#!/bin/bash # # iscsi_offload # # Configure iSCSI offload engines for use with open-iscsi # Usage: # iscsi_offload [-d | -f | -i | -t ] # # Copyright (c) 2011 Hannes Reinecke, SUSE Labs # This script is licensed under the GPL. # # The script creates an open-iscsi interface definition # in the style -, where matches the # network interface passed on the commandline. # If '-t' (test mode) is passed as an option, the script # will not create nor modify any setting but just print # the currently active ones. # # Currently the script works with Broadcom (bnx2i) and # Chelsio T3 (cxgbi) iSCSI offload engines. # Should work with Chelsio T4, but has not been tested. # ServerEngines (be2iscsi) and QLogic (qla4xxx) can only # be configured via BIOS, open-iscsi support is still in # development. # # # Return codes: # 0: Success # 1: Invalid command line parameter # 2: iSCSI offloading not supported # 3: Error during module loading # 4: Cannot configure interface via iscsiadm, use BIOS setup # 5: internal error running iscsiadm # # Output: # [none|dhcp|ip |ibft] # where # : MAC Address of the iSCSI offload engine # none: No IP configuration set for the iSCSI offload engine # dhcp: iSCSI offload engine configured for DHCP # ip: iSCSI offload engine configured with static IP address # ibft: iSCSI offload engine configured from iBFT values # # # Figure out the MAC address of the iSCSI offload engine # corresponding to a NIC from a given PCI device. # bnx2 is using one PCI device per port for both network and iSCSI offloading # cxgb3 is using one PCI device for everything. # iscsi_macaddress_from_pcidevice() { local path=$1 local if=$2 local h local host for h in $path/host* ; do if [ -d "$h" ] ; then host=${h##*/} read netdev < /sys/class/iscsi_host/$host/netdev if [ "$netdev" = "$IFNAME" ] ; then read mac < /sys/class/iscsi_host/$host/hwaddress if [ "$mac" != "00:00:00:00:00:00" ] ; then echo "$mac" fi break; fi fi done } # # Figure out the MAC address of the iSCSI offload engine # corresponding to a NIC from a given PCI function. # It is assumed that the MAC address of the iSCSI offload # engine is equal of the MAC address of the NIC plus one. # Suitable for be2iscsi and qla4xxx # iscsi_macaddress_from_pcifn() { local path=$1 local if=$2 local h local host local ifmac local olemacoffset=$3 ifmac=$(ip addr show dev $if | sed -n 's/ *link\/ether \(.*\) brd.*/\1/p') m5=$(( 0x${ifmac##*:} )) m5=$(( $m5 + $olemacoffset )) ifmac=$(printf "%s:%02x" ${ifmac%:*} $m5) for host in /sys/class/iscsi_host/host* ; do if [ -L "$host" ] ; then read mac < $host/hwaddress if [ "$mac" = "$ifmac" ] ; then echo "$mac" break; fi fi done } update_iface_setting() { local iface="$1" local name="$2" local value="$3" iface_value=$(iscsiadm -m iface -I $iface | sed -n "s/$name = \(.*\)/\1/p") if [ "$iface_value" = "" ] ; then iface_value= fi if [ "$iface_value" != "$value" ] ; then if ! iscsiadm -m iface -I $iface -o update -n "$name" -v "$value" ; then return 1 fi fi return 0 } while getopts di:t options ; do case $options in d ) mode=dhcp;; i ) mode=static optaddr=$OPTARG ;; f ) mode=firmware;; t ) dry_run=1;; ?) printf "Usage: %s [-d|-t|-i ipaddr|-f] ifname\n" $0 exit 1;; esac done shift $(($OPTIND - 1)) IFNAME=$1 ibft_mode="none" if [ -z "$IFNAME" ] ; then echo "No interface specified" exit 1 fi if [ "$dry_run" ] ; then if [ "$mode" = "dhcp" ] ; then echo "'-t' specified, ignoring '-d'" mode= elif [ "$mode" = "static" ] ; then echo "'-t' specified, ignoring '-s'" mode= fi fi if [ ! -L /sys/class/net/$IFNAME ] ; then echo "Interface $IFNAME not found" exit 1 fi if [ "$optaddr" ] && ! ip route get $optaddr ; then echo "Invalid IP address $optaddr" exit 1 fi if [ "$dry_run" ] ; then mode= fi ifpath=$(cd -P /sys/class/net/$IFNAME; echo $PWD) pcipath=$(cd -P $ifpath/device; echo $PWD) if [ -d $pcipath ] ; then drvlink=$(readlink $pcipath/driver) driver=${drvlink##*/} fi if [ -z "$driver" ] ; then echo "No driver found for interface $IFNAME" exit 1 fi case "$driver" in bnx2*) mod=bnx2i ;; cxgb*) mod=cxgb3i ;; be2*) mod=be2iscsi ;; qla*) mod=qla4xxx ;; qed*) mod=qedi ;; esac if [ -z "$mod" ] ; then echo "iSCSI offloading not supported on interface $IFNAME" exit 2 fi # Check if the required modules are already loaded loaded=$(sed -n "/^$mod/p" /proc/modules) if [ -z "$loaded" ] ; then modprobe $mod fi loaded=$(sed -n "/^$mod/p" /proc/modules) if [ -z "$loaded" ] ; then echo "Loading of $mod.ko failed, please check dmesg" exit 3 fi # Get the correct MAC address for the various devices if [ "$mod" = "bnx2i" ] ; then mac=$(iscsi_macaddress_from_pcidevice $pcipath $IFNAME) elif [ "$mod" = "cxgb3i" ] ; then mac=$(iscsi_macaddress_from_pcidevice $pcipath $IFNAME) elif [ "$mod" = "be2iscsi" ] ; then mac=$(iscsi_macaddress_from_pcifn $pcipath $IFNAME 1) elif [ "$mod" = "qla4xxx" ] ; then mac=$(iscsi_macaddress_from_pcifn $pcipath $IFNAME 1) elif [ "$mod" = "qede" -o "$mod" = "qedi" ] ; then mac=$(iscsi_macaddress_from_pcifn $pcipath $IFNAME 4) fi if [ -z "$mac" ] ; then echo "iSCSI offloading not supported on interface $IFNAME" exit 2 fi gen_iface="$mod.$mac" ioe_iface="${IFNAME}-${mod}" # Get existing settings if iscsiadm -m iface -I $ioe_iface > /dev/null 2>&1 ; then ioe_mac=$(iscsiadm -m iface -I $ioe_iface 2> /dev/null| sed -n "s/iface\.hwaddress = \(.*\)/\1/p") ioe_mod=$(iscsiadm -m iface -I $ioe_iface 2> /dev/null| sed -n "s/iface\.transport_name = \(.*\)/\1/p") ipaddr=$(iscsiadm -m iface -I $ioe_iface 2> /dev/null| sed -n "s/iface\.ipaddress = \(.*\)/\1/p") if [ "$ipaddr" == "" ] ; then ipaddr= fi elif [ "$mod" = "be2iscsi" ] ; then ioe_mac=$mac ioe_mod=$mod else # Create new interface iscsiadm -m iface -I $ioe_iface --op=new 2> /dev/null ioe_mac= ioe_mod= ipaddr= fi if [ -z "$dry_run" ] ; then if [ "$ioe_mac" != "$mac" ] ; then if [ -n "$ioe_mac" ] ; then echo "Warning: Updating MAC address on iface $ioe_iface" fi update_iface_setting $ioe_iface iface.hwaddress "$mac" fi if [ "$ioe_mod" != "$mod" ] ; then if [ -n "$ioe_mod" ] ; then echo "Warning: Update transport on iface $ioe_iface" fi update_iface_setting $ioe_iface iface.transport_name "$mod" fi elif [ -z "$ipaddr" ] ; then ipaddr=$(iscsiadm -m iface -I $gen_iface 2> /dev/null| sed -n "s/iface\.ipaddress = \(.*\)/\1/p") if [ "$ipaddr" = "" ] ; then ipaddr= fi elif [ "$ioe_mod" != "$mod" ] ; then echo "Warning: Transport mismatch on iface $ioe_iface: $ioe_mod should be $mod" fi # Check iBFT setting for d in /sys/firmware/* ; do [ -d $d ] || continue [ -d $d/ethernet0 ] || continue iboot_dir=$d done if [ -n "$iboot_dir" ] && [ -d "$iboot_dir" ] ; then for if in ${iboot_dir}/ethernet* ; do read ibft_mac < $if/mac [ "$ibft_mac" = "$mac" ] || continue ibft_origin=0 [ -f ${if}/origin ] && read ibft_origin < $if/origin if [ "$ibft_origin" -eq 1 ] ; then ibft_mode="static" elif [ "$ibft_origin" -eq 3 ] ; then ibft_mode="dhcp" fi [ -f $if/dhcp ] && read ibft_dhcp < $if/dhcp if [ -n "$ibft_dhcp" -a "$ibft_mode" != "dhcp" ] ; then ibft_mode=dhcp fi if [ "$ibft_mode" = "dhcp" ] ; then ibft_ipaddr="0.0.0.0" ibft_gateway= ibft_mask= break fi [ -f $if/ip-addr ] && read ibft_ipaddr < $if/ip-addr [ -f $if/gateway ] && read ibft_gateway < $if/gateway [ -f $if/subnet-mask ] && read ibft_mask < $if/subnet-mask break done fi if [ -z "$optaddr" ] && [ "$ibft_ipaddr" ] ; then optaddr=$ibft_ipaddr fi # Check if the interface needs to be configured if [ -z "$mode" ] ; then if [ "$ibft_mode" != "none" ] ; then echo "$mac ibft" mode="ibft" elif [ -z "$ipaddr" ] ; then echo "$mac none" mode="none" elif [ "$ipaddr" = "0.0.0.0" ] ; then echo "$mac dhcp" ipaddr= mode="dhcp" else echo "$mac ip $ipaddr" mode="static" fi [ "$dry_run" ] && exit 0 elif [ "$mode" = "dhcp" ] ; then if [ "$ipaddr" = "0.0.0.0" ] ; then echo "$mac dhcp" exit 0 fi optaddr="0.0.0.0" elif [ "$mode" = "static" ] && [ "$ipaddr" = "$optaddr" ] ; then echo "$mac ip $ipaddr" exit 0 fi if [ "$mod" = "be2iscsi" ] ; then exit 4 fi if ! update_iface_setting $ioe_iface iface.ipaddress "$optaddr" ; then echo "Failed to set IP address: $?" exit 1 fi if ! update_iface_setting $gen_iface iface.ipaddress "$optaddr" ; then echo "Failed to set IP address for generic interface: $?" exit 1 fi if ! update_iface_setting $ioe_iface iface.gateway "$ibft_gateway" ; then echo "Failed to set gateway address: $?" exit 1 fi if ! update_iface_setting $gen_iface iface.gateway "$ibft_gateway" ; then echo "Failed to set gateway address for generic interface: $?" exit 1 fi if ! update_iface_setting $ioe_iface iface.subnet_mask "$ibft_mask" ; then echo "Failed to set subnet mask: $?" exit 1 fi if ! update_iface_setting $gen_iface iface.subnet_mask "$ibft_mask" ; then echo "Failed to set subnet mask for generic interface: $?" exit 1 fi if [ "$mod" = "qla4xxx" ] ; then iscsiadm -m iface -H $mac -o applyall fi ip link set dev $IFNAME up exit 0