Blame utils/iscsi_offload

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