Blob Blame History Raw
#!/bin/sh
#
# Copyright 2009 Red Hat, Inc.
# License: GPLv2
# Author: Dan HorĂ¡k <dhorak@redhat.com>
#
# unblock devices listed in various config files and wait until they are ready
#
# it uses dasd and zfcp config file
# config file syntax:
# deviceno   options
# or
# deviceno   WWPN   FCPLUN
#
# also processes the system ccw config file and network interface configurations
#

DASDCONFIG=/etc/dasd.conf
ZFCPCONFIG=/etc/zfcp.conf
ZNETCONFIG=/etc/ccw.conf
BLACKLIST=/proc/cio_ignore
VERBOSE=
PATH=/bin:/usr/bin:/sbin:/usr/sbin
ALL_DEVICES=
WAITING_TIMEOUT=60	# maximum time to wait for all devices to appear
WAITING_TOTAL=0		# actual time spent waiting for devices

usage()
{
    echo "Usage: $CMD [-h|--help] [-V|--verbose]"
    exit 1
}

# accepts single device, comma-separated lists and dash separated ranges and their combinations
free_device()
{
    local DEV

    [ -z "$1" ] && return

    DEV=$(echo $1 | tr "A-Z" "a-z")
    
    [ $VERBOSE ] && echo "Freeing device(s) $DEV"
    if ! echo "free $DEV" > $BLACKLIST 2> /dev/null ; then
	echo "Error: can't free device(s) $DEV"
    else
	if [ -z $ALL_DEVICES ]; then
	    ALL_DEVICES=$DEV
	else
	    ALL_DEVICES="$ALL_DEVICES,$DEV"
	fi
    fi
}

# wait until a device appears on the ccw bus
wait_on_device()
{
    local DEVICE_ONLINE DEV
    
    [ -z "$1" ] && return
    
    DEV=$1
    DEVICE_ONLINE=/sys/bus/ccw/devices/$DEV/online

    [ $VERBOSE ] && echo "Waiting on device $DEV"
    [ -f "$DEVICE_ONLINE" ] && return

    for t in 1 2 3 4 5
    do
	if [ $WAITING_TOTAL -ge $WAITING_TIMEOUT ]; then
	    [ $VERBOSE ] && echo "Waiting timeout of $WAITING_TIMEOUT seconds reached"
	    break
	fi
	WAITING_TOTAL=$(($WAITING_TOTAL + $t))
        [ $VERBOSE ] && echo "Waiting additional $t second(s) and $WAITING_TOTAL second(s) in total"
	sleep $t
	[ -f "$DEVICE_ONLINE" ] && return
    done
    echo "Error: device $DEV still not ready"
}

# check how we were called
CMD=$(basename "$0")
case $CMD in
    "dasd_cio_free")
	CONFIG=$DASDCONFIG
	MODE=dasd
	;;
    "zfcp_cio_free")
	CONFIG=$ZFCPCONFIG
	MODE=zfcp
	;;
    "znet_cio_free")
	CONFIG=$ZNETCONFIG
	MODE=znet
	;;
    *)
	echo "Error: unknown alias '$CMD'."
	echo "Supported aliases are dasd_cio_free, zfcp_cio_free and znet_cio_free."
	exit 1
	;;
esac

# process command line options
if [ $# -gt 0 ]; then
    case $1 in
	-V|--verbose)
	    VERBOSE=yes
	    shift
	    ;;
	-h|--help)
	    usage
	    ;;
	*)
	    echo "Error: unknown option $1"
	    usage
	    ;;
    esac
fi

if [ ! -f $BLACKLIST ]; then
    echo "Error: $BLACKLIST kernel interface doesn't exist"
    exit 2
fi

if [ $MODE = "dasd" -o $MODE = "zfcp" ]; then
    # process the config file
    if [ -f "$CONFIG" ]; then
        while read line; do
	    case $line in
		\#*) ;;
		*)
		    [ -z "$line" ] && continue
		    set $line
		    free_device $1
		    ;;
	    esac
	done < $CONFIG
    fi
fi

if [ $MODE = "dasd" ]; then
    # process the device list defined as option for the dasd module
    DEVICES=$(modprobe --showconfig | grep "options[[:space:]]\+dasd_mod" | \
	sed -e 's/.*[[:space:]]dasd=\([^[:space:]]*\).*/\1/' -e 's/([^)]*)//g' \
	-e 's/nopav\|nofcx\|autodetect\|probeonly//g' -e 's/,,/,/g' -e 's/^,//' -e 's/,$//')

    free_device $DEVICES
fi

if [ $MODE = "znet" ]; then
    # process the config file
    if [ -f "$CONFIG" ]; then
        while read line; do
	    case $line in
		\#*) ;;
		*)
		    [ -z "$line" ] && continue
		    # grep 2 or 3 channels from beginning of each line
		    DEVICES=$(echo $line | grep -E -i -o "^([0-9]\.[0-9]\.[a-f0-9]+,){1,2}([0-9]\.[0-9]\.[a-f0-9]+)")
		    free_device $DEVICES
		    ;;
	    esac
	done < $CONFIG
    fi
    # process channels from network interface configurations
    for line in $(grep -E -i -h "^[[:space:]]*SUBCHANNELS=['\"]?([0-9]\.[0-9]\.[a-f0-9]+,){1,2}([0-9]\.[0-9]\.[a-f0-9]+)['\"]?([[:space:]]+#|[[:space:]]*$)" /etc/sysconfig/network-scripts/ifcfg-* 2> /dev/null)
    do
	eval "$line"
        free_device $SUBCHANNELS
    done
fi

# wait until recently unblocked devices are ready
# at this point we know the content of ALL_DEVICES is syntacticly correct
OLD_IFS=$IFS
IFS=","
set $ALL_DEVICES
for DEV in $*
do
    IFS="."

    # get the lower bound for range or get the single device
    LOWER=${DEV%%-*}
    set $LOWER
    if [ $# -eq 1 ]; then
        L0=0
        L1=0
        L2=$(printf "%d" "0x$1")
    else
        L0=$(printf "%d" "0x$1")
        L1=$(printf "%d" "0x$2")
        L2=$(printf "%d" "0x$3")
    fi

    # get the upper bound for range or get the single device
    UPPER=${DEV##*-}
    set $UPPER
    if [ $# -eq 1 ]; then
        U0=0
        U1=0
        U2=$(printf "%d" "0x$1")
    else
        U0=$(printf "%d" "0x$1")
        U1=$(printf "%d" "0x$2")
        U2=$(printf "%d" "0x$3")
    fi

    IFS=$OLD_IFS

    # iterate thru all devices
    for i in $(seq $L0 $U0); do
        [ $i -eq $L0 ] && LJ=$L1 || LJ=0
        [ $i -eq $U0 ] && UJ=$U1 || UJ=3

        for j in $(seq $LJ $UJ); do
            [ $i -eq $L0 -a $j -eq $L1 ] && LK=$L2 || LK=0
            [ $i -eq $U0 -a $j -eq $U1 ] && UK=$U2 || UK=65535

            for k in $(seq $LK $UK); do
                wait_on_device "$(printf %x.%x.%04x $i $j $k)"
            done
        done
    done
done