#! /bin/bash # lsznet.raw: list sensible network device hardware setups for Linux on s390(x) # Copyright (C) IBM Corp. 2008,2009 # Author: Steffen Maier # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 of the License only. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA readonly SYSFS=/sys # DEBUG=0 turns off debugging. >=1 means increasing debugging. readonly DEBUG=0 # nothing to be changed below here export TEXTDOMAIN=lsznet.raw readonly CMD=${0##*/} function error() { echo $"$CMD: ERROR: $*" 1>&2 exit 1; } # currently requires bash version 3.0 or later # (this seems reasonable since bash-3.0 has been shipped with # RHEL 4.0, 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7; # bash-3.1 with RHEL 5.0, 5.1; and bash-3.2 with RHEL 5.2) if [ ${BASH_VERSINFO[0]} -lt 3 -o \ ${BASH_VERSINFO[1]} -lt 0 ]; then error $"only works with BASH version 3.0 or later (current version used is ${BASH_VERSION})" fi # the script was designed for Linux kernel 2.6 and might work with newer ones kernel_version=$(uname -r) IFS=. read krn_ver krn_patch krn_foo <<< "$kernel_version" unset IFS if [ $krn_ver -lt 2 -o $krn_patch -lt 6 ]; then error $"only works for kernel versions 2.6 or probably later" fi # The following combinations of control unit type and model were taken from the # Linux network device drivers for s390 on 2008-06-09 from Linux 2.6.25.4. # The list (among other things) should be adapted, if any of those device # drivers start supporting different CU types/models. # (Alternatively, the list could be generated by reading the modaliases # directly out of the device driver modules: # modinfo qeth/cu3088 | fgrep 'alias:' # However, this would still require a list of device driver modules.) readonly -a CU=( 1731/01 1731/05 3088/08 3088/1f 3088/1e 3088/01 3088/60 3088/61 ) # $CU_CARDTYPE array is the only one which may contain entries with spaces readonly -a CU_CARDTYPE=( "OSA (QDIO)" "HiperSocket" "CTC adapter" "escon channel" "ficon channel" "LCS p390" "LCS OSA" "LCS CLAW" ) readonly -a CU_DEVDRV=( qeth qeth ctc ctc ctc lcs lcs lcs ) readonly -a CU_DEVNAME=( eth hsi ctc ctc ctc eth eth eth ) readonly -a CU_GROUPCHANNELS=( 3 3 2 2 2 2 2 2 ) readonly -a CHPIDTYPES=( [10]=OSE [11]=OSD [24]=IQD ) readonly PREFIXFORMAT=[[:xdigit:]]* readonly SSIDFORMAT=[0-3] readonly BUSIDFORMAT=[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]] readonly IDFORMAT=$PREFIXFORMAT.$SSIDFORMAT.$BUSIDFORMAT readonly SUBCHANNEL_TYPE_IO=0 function debug() { level=$1 shift [ $DEBUG -ge $level ] && echo "$*" 1>&2 } # Searches for a match of argument 1 on the array $CU and sets $cu_idx # to the matched array index on success. # Returns 0 on success, 1 on failure. function search_cu() { local scu=$1 local i for ((i=0; i < ${#CU[@]}; i++)); do if [ $scu == ${CU[i]} ]; then cu_idx=$i return 0 fi done return 1 } # Returns symbolic name of CHPID type in $chpidtype_symbolic, # if an entry in the array $CHPIDTYPES has been found at index of argument 1. # Returns "?" otherwise. # Always succeeds and returns 0. function search_chpt() { local chpidtype_number=$1 chpidtype_symbolic=${CHPIDTYPES[$chpidtype_number]} if [ "$chpidtype_symbolic" == "" ]; then chpidtype_symbolic="?" fi return 0 } # build_list: # # Prints list on standard output consisting of all subchannels and # ccwdevices whose control unit type/model match supported network # device types on s390. Each matching entry is accompanied with # (almost all) corresponding attributes. # function build_list() { # use /sys/devices/css*/ for startpath readonly STARTPATH=$SYSFS/devices # change to base directory so path globbing length with find is minimal cd $STARTPATH # fail out gracefully, if there is not expected sysfs environment # (could even fail out near the top, if $(uname -m) != s390x) csses=css$PREFIXFORMAT for d in $csses; do [ -d $d ] || exit done find $csses -name "$IDFORMAT" | while read dir; do debug 6 " examining sysfs directory $dir" # must not use $...FORMAT (file globs) here since this is a regex: [[ "$dir" =~ ^css\([[:xdigit:]]+\)/\([[:xdigit:]]+\\.[0-3]\\.[[:xdigit:]]{4}\)/\([[:xdigit:]]+\\.[0-3]\\.[[:xdigit:]]{4}\)$ ]] case $? in 0) # string matched the pattern debug 6 " ${BASH_REMATCH[@]}" prefix=${BASH_REMATCH[1]} subch=${BASH_REMATCH[2]} devbusid=${BASH_REMATCH[3]} subch_p=css$prefix/$subch dev_p=$subch_p/$devbusid debug 6 " $subch_p $dev_p" ;; 1) # string did not match the pattern continue ;; 2) error $"syntax error in regex of match operator =~, code needs to be fixed" ;; *) error $"unexpected return code of regex match operator =~, code needs to be fixed" ;; esac debug 5 " sysfs directory matched regex $dir" # skip non-I/O-subchannels, i.e. chsc and message subchannels if [ -f $subch_p/type ]; then read type < $subch_p/type if [ $type != $SUBCHANNEL_TYPE_IO ]; then debug 3 " skip non-I/O subchannel" continue fi fi # get subchannel information... # ATTENTION: hex values from sysfs are WITHOUT leading 0x prefix! read chpid_list < $subch_p/chpids read -a chpids <<< "$chpid_list" if [ ${#chpids[@]} -ne 8 ]; then error $"sysfs reported ${#chpids[@]} CHPIDs instead of expected 8" fi read pim pam pom foo < $subch_p/pimpampom pimchpidZ="" local chp for ((chp=0; chp < 8; chp++)); do mask=$((0x80 >> chp)) if (( 0x$pim & $mask )); then pimchpidZ=${pimchpidZ}${chpids[chp]} else pimchpidZ=${pimchpidZ}"ZZ" fi done # get device information... read cutype < $dev_p/cutype read active < $dev_p/online # skip already active subchannels and those that are already in a # ccwgroup and thus not available any more: [ $active == "1" ] && continue [ -h $dev_p/group_device ] && continue # get chpid information... pimchpids=${pimchpidZ//ZZ/} [ $pimchpids == "" ] && continue # Taking the first 2 hex digits as CHPID relies somewhat on the fact # that network adaptors don't use multipathing and only have one CHP. # Anyway it's OK since we're only interested in CHPID type and I guess # this should be equal for all possible multipaths to the same device. chpid=${pimchpids:0:2} chpid_p=css$prefix/chp$prefix.$chpid read chptype < $chpid_p/type # filter and output... if search_cu $cutype; then if [ "${CU_DEVDRV[$cu_idx]}" == "ctc" ]; then # assume CTC are mostly virtual and ignore chpid from sysfs chpidtype_symbolic="-" else search_chpt $chptype fi echo $pimchpids $devbusid $STARTPATH/$dev_p $cutype $chpidtype_symbolic $chptype ${CU_DEVDRV[$cu_idx]} ${CU_DEVNAME[$cu_idx]} ${CU_GROUPCHANNELS[$cu_idx]} $cu_idx ${CU_CARDTYPE[$cu_idx]} else debug 5 " skip non-network device $devbusid CU $cutype" fi done } # search_groups: # # Prints enumeration list on standard output consisting of possible # hardware configurations (ccwgroups) for network devices on s390. # Each configuration suggestion includes corresponding attributes # that are of potential interest for the user and fit in a fixed column # table on an 80 column screen. # # PRECONDITION: Standard input has to be stably sorted by device bus IDs and # then by CHPIDs, i.e. grouped by CHPIDs. # function search_groups() { local w_prefix w_ssid w_devno local d_prefix d_ssid d_devno local prefix ssid devno x local chp devbusid dev_p cutype chpidtypename chptype devdrv devname groupchs cu_idx cardtype # remembered last state variables for possible ccwgroup: local r_prefix="Z" local r_ssid="Z" local r_devno="ZZZZ" local r_chp="ZZ" local r_cutype="ZZZZ/ZZ" local count=0 local item=1 local skipped=0 # currently unused are: dev_p,chptype, cu_idx. while read chp devbusid dev_p cutype chpidtypename chptype devdrv devname groupchs cu_idx cardtype; do debug 1 " # $chp $devbusid $dev_p $cutype $chpidtypename $chptype $devdrv $devname $groupchs $cu_idx $cardtype" IFS=. read prefix ssid devno x <<< "$devbusid" unset IFS if [ $r_chp != $chp \ -o $r_prefix != $prefix \ -o $r_ssid != $ssid \ -o $r_cutype != $cutype ]; then # restart with new read channel info and remember it r_prefix=$prefix r_ssid=$ssid r_devno=$devno r_chp=$chp r_cutype=$cutype count=1 debug 2 " INFO: restart on different CHPID or prefix or CUtype/model" continue fi count=$((count + 1)) if [ $count -eq 2 ]; then # about to check if write channel is one above read channel if [ $((0x$devno)) -ne $((0x$r_devno + 1)) ]; then # start with new read channel info r_prefix=$prefix r_ssid=$ssid r_devno=$devno r_chp=$chp r_cutype=$cutype count=1 skipped=$((skipped + 1)) # unimplemented possible packed channel usage option: # remember unused channels for later use as data channel debug 2 " INFO: restart on unmatching read channel" continue fi w_prefix=$prefix w_ssid=$ssid w_devno=$devno elif [ $count -eq 3 ]; then # remember data channel info d_prefix=$prefix d_ssid=$ssid d_devno=$devno fi debug 2 " INFO: groupchs=$groupchs count=$count" if [ $count -ne $groupchs ]; then debug 2 " INFO: skip" continue fi # found possible ccwgroup case $count in 2) chlist=$r_prefix.$r_ssid.$r_devno,$w_prefix.$w_ssid.$w_devno ;; 3) chlist=$r_prefix.$r_ssid.$r_devno,$w_prefix.$w_ssid.$w_devno,$d_prefix.$d_ssid.$d_devno ;; *) error $"unknown number of channels for group, code needs to be fixed" ;; esac echo $item $cutype $chp $chpidtypename $devdrv $devname $chlist "$cardtype" item=$((item + 1)) # restart after successful detection r_prefix="Z" count=0 done debug 1 " STATISTIC: skipped $skipped devnos because of unmatching read channel" } build_list | # stable sort by device bus IDs and then by CHPIDs => grouped by CHPIDs # (sorting only works since keys are fixed no. of digits with leading zeros!) sort -s -k 1,1 -k 2,2 | #cat ; exit # move at desired line and uncomment to see intermediate output search_groups