From c44bdb5af6ac232adde2390990e96220d642e43d Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Sun, 26 Jun 2011 17:52:13 -0500 Subject: Update SUSE init script From Hannes Reinecke Rework the iSCSI shutdown logic to tear down the block device stack correctly. Signed-off-by: Hannes Reinecke [fix merge issues] Mike Christie --- etc/initd/initd.suse | 454 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 355 insertions(+), 99 deletions(-) (limited to 'etc') diff --git a/etc/initd/initd.suse b/etc/initd/initd.suse index cc0baa9..a1c515a 100644 --- a/etc/initd/initd.suse +++ b/etc/initd/initd.suse @@ -5,20 +5,22 @@ ### BEGIN INIT INFO # Provides: iscsi # Required-Start: $network -# Should-Start: -# Required-Stop: -# Should-Stop: +# Should-Start: iscsitarget multipathd +# Required-Stop: $network +# Should-Stop: multipathd # Default-Start: 3 5 # Default-Stop: -# Short-Description: Starts and stops the iSCSI client initiator -# +# Short-Description: iSCSI initiator daemon +# Description: The iSCSI initator is used to create and +# manage iSCSI connections to a iSCSI Target. +# ### END INIT INFO -PID_FILE=/var/run/iscsi.pid CONFIG_FILE=/etc/iscsi/iscsid.conf DAEMON=/sbin/iscsid ISCSIADM=/sbin/iscsiadm -ARGS="-c $CONFIG_FILE -p $PID_FILE" +BRCM_ISCSIUIO=/sbin/brcm_iscsiuio +ARGS="-c $CONFIG_FILE -n" # Source LSB init functions . /etc/rc.status @@ -26,6 +28,8 @@ ARGS="-c $CONFIG_FILE -p $PID_FILE" # Reset status of this service rc_reset +DM_MAJOR=$(sed -n 's/\(.*\) device-mapper/\1/p' /proc/devices) + iscsi_login_all_nodes() { echo -n "Setting up iSCSI targets: " @@ -36,58 +40,349 @@ iscsi_login_all_nodes() rc_status -v } -iscsi_logout_all_nodes() +# +# Try to load all required modules prior to startup +# +iscsi_load_transport_modules() { - echo -n "Closing all iSCSI connections: " - # Logout from all sessions marked automatic - if ! $ISCSIADM -m node --logoutall=automatic 2> /dev/null; then - if [ $? == 21 ] ; then - RETVAL=6 - else - RETVAL=1 + loaded=$(sed -n "/^iscsi_tcp/p" /proc/modules) + if [ -z "$loaded" ] ; then + modprobe iscsi_tcp + if [ $? = 0 ] ; then + echo -n " tcp" + fi + fi + + for iface in /etc/iscsi/ifaces/*; do + [ -f "$iface" ] || continue + [ "$iface" = "iface.example" ] && continue + # Check if the iface has been configured + result=$(sed '/#.*/D;/iface.iscsi_ifacename/D;/iface.hwaddress/D;/iface.transport_name/D' $iface) + if [ "$result" ] ; then + mod=$(sed -n 's/iface.transport_name *= *\(.*\)/\1/p' $iface) + loaded=$(sed -n "/^$mod/p" /proc/modules) + if [ -z "$loaded" ] ; then + modprobe $mod + if [ $? = 0 ] ; then + echo -n " $mod" fi - rc_failed $RETVAL + fi fi - rc_status -v + done +} - # Not sure whether this is still needed - sleep 1 - return ${RETVAL:-0} +# +# Set a temporary startmode for ifdown +# +iscsi_modify_if_startmode() +{ + local ifname=$1 + local tmp_ifcfg=/dev/.sysconfig/network/if-$ifname + + if [ -e "$tmp_ifcfg" ] ; then + . $tmp_ifcfg + if [ "$startmode" ] ; then + return + fi + fi + : disabling shutdown on $ifname + echo "startmode=nfsroot" >> $tmp_ifcfg } -iscsi_umount_all_luns() +iscsi_get_ifacename_from_session() { - local d m dev p s + local session=$1 + local ifacename - cat /proc/mounts | sed -ne '/^\/dev\/.*/p' | while read d m t o x; do - if [ "$m" = "/" ] ; then - continue; + ifacename=$(iscsiadm -m session -r ${session##.*/session} 2> /dev/null | \ + sed -n 's/iface.iscsi_ifacename = \(.*\)/\1/p') + if [ -z "$ifacename" ] ; then + # Check for iBFT + ifacename=$(iscsiadm -m fw 2> /dev/null) + if [ -n "$ifacename" ] ; then + ifacename="fw" + fi + fi + echo $ifacename +} + +iscsi_get_hwaddress_from_iface() +{ + local iface=$1 + local hwaddress + + hwaddress=$(iscsiadm -m iface -I "$iface" 2> /dev/null | sed -n 's/iface.hwaddress = \(.*\)/\1/p') + [ "$hwaddress" = "" ] && hwaddress= + + echo $hwaddress +} + +iscsi_get_ifname_from_iface() +{ + local iface=$1 + local ifname + + ifname=$(iscsiadm -m iface -I "$iface" 2> /dev/null | sed -n 's/iface.net_ifacename = \(.*\)/\1/p') + [ "$ifname" = "" ] && ifname= + + echo $ifname +} + +iscsi_get_ipaddr_from_iface() +{ + local iface=$1 + local ipaddr + + ipaddr=$(iscsiadm -m iface -I "$iface" 2> /dev/null | sed -n 's/iface.ipaddress = \(.*\)/\1/p') + [ "$ipaddr" = "" ] && ipaddr= + + echo $ipaddr +} + +iscsi_get_ifname_from_firmware() +{ + local hwaddress + + hwaddress=$(iscsiadm -m fw 2> /dev/null | sed -n 's/iface.net_ifacename = \(.*\)/\1/p') + + echo $hwaddress +} + +# +# cxgb3i is using the HWAddress to select +# the correct interface +# +iscsi_get_ifname_from_hwaddress() +{ + local hwaddress=$1 + + for if in /sys/class/net/*; do + [ -e "$if" ] || continue + read mac < $if/address + [ "$mac" = "$hwaddress" ] || continue + echo ${if##*/} + break + done +} + +iscsi_get_ifname_from_ipaddr() +{ + local ipaddr=$1 + local ifname + + ifname=$(ip addr show to $ipaddr | sed -n 's/[0-9]*: \([^ :]*\): .*/\1/p') + return $ifname +} + +# +# Handle 'default' interface: +# It is basically impossible to determine via which +# interface the iSCSI traffic will flow, so we take +# the easy option and ignore _all_ active interfaces +# during shutdown +# +iscsi_modify_all_interfaces() +{ + ip link show up | sed -n '/.*LOOPBACK.*/d;s/[0-9]*: \(.*\): .*/\1/p' | while read ifname; do + iscsi_modify_if_startmode $ifname + done +} + +# +# Check iface setting and disable +# affected network interfaces +# +iscsi_check_interface() +{ + local session=$1 + local i h n + + i=$(iscsi_get_ifacename_from_session $session) + [ -z "$i" ] && continue + if [ "$i" = "default" ] ; then + iscsi_modify_all_interfaces + elif [ "$i" = "fw" ] ; then + n=$(iscsi_get_ifname_from_firmware) + else + n=$(iscsi_get_ifname_from_iface $i) + if [ -z "$n" ] ; then + h=$(iscsi_get_hwaddress_from_iface $i) + if [ -n "$h" ] ; then + n=$(iscsi_get_ifname_from_hwaddress $h) + fi fi + if [ -z "$n" ] ; then + h=$(iscsi_get_ipaddr_from_iface $i) + if [ -n "$h" ] ; then + n=$(iscsi_get_ifname_from_ipaddr $h) + fi + fi + fi + if [ "$n" ] ; then + iscsi_modify_if_startmode $n + fi +} + +# +# Check if device 'dev' is mounted +# Returns the mount point on success +# +iscsi_check_if_mounted() +{ + local dev=$1 + local d m t o x p + + cat /proc/mounts | sed -ne '/^\/dev\/.*/p' | while read d m t o x; do if [ -L "$d" ] ; then d=$(readlink -f $d) fi - dev=${d##/dev} + [ -b "$d" ] || continue + + b=$(ls -l $d | sed -n 's/.* \([0-9]*\), \([0-9]*\) .*/\1:\2/p') + p=$(cd -P /sys/dev/block/$b ; echo $PWD) + + if [ -z "$p" ] ; then + d=${d##/dev} + p="/sys/block${d%%[0-9]*}" + fi + + [ ! -d ${p} ] && continue + + if [ -e $p/partition ] ; then + p=$(cd -P $p/../; echo $PWD) + fi + if [ "$dev" = "${p##*/}" ] ; then + echo $m + fi + done +} + +# +# Unwind block device stack +# +# Stops unwinding if either no more 'holders' +# are found or if a device is mounted +# +# Unmounts top-level device and deconfigures +# all devices down the stack +# +# Root fs is not unmounted +# +iscsi_unwind_stack() +{ + local p=$1 + local d=${p##*/} + local u + local m + + if [ ! -d ${p} ] ; then + return; + fi + + m=$(iscsi_check_if_mounted $d) + if [ -z "$m" ] ; then + for s in $p/holders/* ; do + [ -e $s ] || continue + p=$(cd -P $s; echo $PWD) + u=$(iscsi_unwind_stack $p) + if [ "$u" ] ; then + echo -n "$u " + fi + done + else + if [ "$m" = "/" ] ; then + echo -n "$d " + return 1 + fi + if ! umount $m ; then + echo -n "$d " + return 1 + fi + fi - if [ "${dev##/sd}" = "$dev" ] ; then - continue; + if [ "${d#dm-}" != "$d" ] ; then + if ! dmsetup remove -j $DM_MAJOR -m ${d#dm-} 2> /dev/null ; then + echo -n "$d " + return 1 fi - p="/sys/block${dev%%[0-9]*}" + fi - if [ ! -d ${p} ] && [ ! -d ${p}/device ] ; then - continue; + if [ "${d#md}" != "$d" ] ; then + if ! mdadm --manage /dev/$d --stop 2> /dev/null ; then + echo -n "$d " + return 1 fi + fi + return 0 +} - s=$(cd -P ${p}/device && echo $PWD) +# +# Return all targets for a given session +# +iscsi_get_target() +{ + local session=$1 + local d - case "$s" in - */session[0-9]*/*) - # This is an iSCSI device - umount "$m" - ;; - esac + for d in $session/device/target* ; do + [ -e "$d" ] || continue + echo "$d" done } +# +# Checks all devices presented by a target +# and tries to umount them. +# Skip unmounting for the root fs. +# Stops on the first device which could not be unmounted +# and returns the mount device of that device. +# +iscsi_check_target() +{ + local t=$1 + local d b m + + for d in $t/* ; do + [ -d $d/block ] || continue + for b in $d/block/sd* ; do + [ -d "$b" ] || continue + m=$(iscsi_unwind_stack $b) + if [ -n "$m" ] ; then + echo $m + return 1 + fi + done + done +} + +# +# Check all sessions for mounted devices +# and shutdown the session if the affected +# devices could be umounted cleanly. +# If umount fails disable shutdown on all +# affected network interfaces +# +iscsi_stop_sessions() +{ + local t m s i + + i=0 + for session in /sys/class/iscsi_session/session* ; do + [ -e "$session" ] || continue; + [ -e $session/device ] || continue + t=$(iscsi_get_target $session) + m=$(iscsi_check_target $t) + s=${session##*/session} + if [ -z "$m" ] ; then + iscsiadm -m session -r ${s} -u + i=$(( $i + 1 )) + else + iscsi_check_interface $s + fi + done + echo $i +} + iscsi_list_all_nodes() { # Check for active sessions @@ -101,84 +396,45 @@ iscsi_list_all_nodes() done } -iscsi_discover_all_targets() -{ - # Strip off any existing ID information - RAW_NODE_LIST=`iscsiadm -m node | sed -nre 's/^(\[[0-9a-f]*\] )?(.*)$/\2/p'` - # Obtain IPv4 list - IPV4_NODE_LIST=`echo "$RAW_NODE_LIST" | sed -nre 's/^([0-9]{1,3}(\.[0-9]{1,3}){3}):[^: ]* (.*)$/\1 \3/p'` - # Now obtain IPv6 list - IPV6_NODE_LIST=`echo "$RAW_NODE_LIST" | sed -nre 's/^([0-9a-f]{1,4}(:[0-9a-f]{0,4}){6}:[0-9a-f]{1,4}):[^: ]* (.*)$/\1 \3/p'` - - DISC_TARGETS="" - while read NODE_ADDR NODE_NAME; do - [ -z "$NODE_ADDR" -a -z "$NODE_NAME" ] && continue - NODE_ATTRS=`iscsiadm -m node -p "$NODE_ADDR" -T "$NODE_NAME"` - NODE_STATUS=`echo "$NODE_ATTRS" | sed -nre 's/^.*node\.conn\[0\]\.startup = ([a-z]*).*$/\1/p'` - - if [ "$NODE_STATUS" == 'automatic' ]; then - DISC_TARGETS=`echo "$DISC_TARGETS" | sed -re '/'"$NODE_ADDR"'/!{s/(.*)/\1 '"$NODE_ADDR"'/}'` - fi - done < <(echo "$IPV4_NODE_LIST"; echo "$IPV6_NODE_LIST") - - for TARGET_ADDR in $DISC_TARGETS; do - echo -n "Attempting discovery on target at ${TARGET_ADDR}: " - iscsiadm -m discovery -t st -p "$TARGET_ADDR" > /dev/null 2>&1 - if [ "$?" -ne 0 ]; then - rc_failed 1 - rc_status -v - return 1 - fi - rc_status -v - done -} - case "$1" in start) - [ ! -d /var/lib/iscsi ] && mkdir -p /var/lib/iscsi if checkproc $DAEMON ; then RETVAL=0 else echo -n "Starting iSCSI initiator service: " - modprobe iscsi_tcp - modprobe -q ib_iser + iscsi_load_transport_modules + if grep -q bnx2i /proc/modules && [ -x $BRCM_ISCSIUIO ] ; then + startproc $BRCM_ISCSIUIO + fi startproc $DAEMON $ARGS RETVAL=$? rc_status -v fi - if [ "$RETVAL" == "0" ]; then - iscsi_discover_all_targets - RETVAL=$? - fi if [ "$RETVAL" == "0" ]; then iscsi_login_all_nodes fi ;; stop) - iscsi_umount_all_luns - if iscsi_logout_all_nodes ; then - iscsiadm -k 0 - RETVAL=$? - else - RETVAL=1 - fi + n=$(iscsi_stop_sessions) echo -n "Stopping iSCSI initiator service: " - if [ "$RETVAL" == "0" ]; then - rm -f $PID_FILE - status=0 - modprobe -r iscsi_tcp - if [ "$?" -ne "0" -a "$?" -ne "1" ]; then - status=1 + if [ "$n" ] && [ "$n" != "0" ] ; then + m=$(iscsiadm -m session 2> /dev/null) + if [ -z "$m" ] ; then + killproc -KILL $DAEMON + RETVAL=$? + if grep -q bnx2i /proc/modules && [ -x $BRCM_ISCSIUIO ]; then + killproc -KILL $BRCM_ISCSIUIO + fi + RETVAL=$? + else + RETVAL=1 fi - modprobe -q -r ib_iser - if [ "$?" -ne "0" -a "$?" -ne "1" ]; then - status=1 - fi - rc_failed $status - else rc_failed $RETVAL + rc_status -v + else + # umounting failed, leave initiator running + rc_status -s fi - rc_status -v ;; status) echo -n "Checking for iSCSI initiator service: " @@ -190,7 +446,7 @@ case "$1" in rc_status -v fi ;; - restart) + restart|reload) $0 stop RETVAL=$? if [ "$RETVAL" != "0" ]; then @@ -201,7 +457,7 @@ case "$1" in $0 start ;; *) - echo "Usage: $0 {start|stop|status|restart}" + echo "Usage: $0 {start|stop|status|restart|reload}" exit 1 ;; esac -- cgit v1.2.1