#!/bin/bash # # lsznet.raw - Tool to list sensible network device hardware setups # # This script is not intended to be used as standalone tool, but should be # used from other tools like a library function. E.g. znetconf is one exploiter # of this script. # # Copyright IBM Corp. 2008, 2017 # # s390-tools is free software; you can redistribute it and/or modify # it under the terms of the MIT license. See LICENSE for details. # readonly SYSFS=/sys # DEBUG=0 turns off debugging. >=1 means increasing debugging. readonly DEBUG=0 # nothing to be changed below here readonly CMD=${0##*/} function error() { echo "$CMD: ERROR: $*" 1>&2 exit 1 } # The arrays (among other things) should be adapted, if any of those device # drivers start supporting different CU types/models. # $CU_CARDTYPE array is the only one which may contain entries with spaces readonly -a CU_CARDTYPE=( "OSA (QDIO)" "HiperSockets" "CTC adapter" "escon channel" "ficon channel" "OSX" "OSM" ) readonly -a CU_DEVNAME=( eth hsi ctc ctc ctc eth eth ) readonly -a CU_DEVDRV=( qeth qeth ctcm ctcm ctcm qeth qeth ) readonly -a CU_GROUPCHANNELS=( 3 3 2 2 2 3 3 ) readonly -a CHPIDTYPES=( [0x11]=OSD [0x24]=IQD [0x30]=OSX [0x31]=OSM ) # [0x15]=OSN is no longer supported # whitelist of network devices for TCP/IP stack, e.g. for Linux installers readonly -a CU_TCPIP=( 1731/01 1731/05 3088/08 3088/1f 3088/1e 1731/02 1731/02 ) # 1731/06 (OSN) is no longer supported readonly PREFIXFORMAT=[[:xdigit:]]* readonly SSIDFORMAT=[0-3] readonly BUSIDFORMAT=[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]] readonly IDFORMAT=$PREFIXFORMAT.$SSIDFORMAT.$BUSIDFORMAT readonly SUBCHANNEL_TYPE_IO=0 declare CU_IDX function debug() { level=$1 shift [ $DEBUG -ge $level ] && echo "$*" 1>&2 } # Searches for a match of argument 1 on the array $CU_TCPIP and sets $CU_IDX to # the matched array index on success. # Returns 0 on success, 1 on failure. function search_cu_tcpip() { local scu=$1 local i for ((i = 0; i < ${#CU_TCPIP[@]}; i++)); do if [ "$scu" == "${CU_TCPIP[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[$((0x$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: EXPR="^css([[:xdigit:]]+)/([[:xdigit:]]+.[0-3].[[:xdigit:]]{4})/([[:xdigit:]]+.[0-3].[[:xdigit:]]{4})$" [[ "$dir" =~ $EXPR ]] 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 [ -z "$all" ] && ! search_cu_tcpip $cutype; then continue fi if search_cu_tcpip $cutype; then if [ "${CU_DEVDRV[$CU_IDX]}" == "ctcm" ]; then # assume CTC are mostly virtual and ignore chpid from sysfs chpidtype_symbolic="-" else search_chpt $chptype fi echo $pimchpids $devbusid $cutype $chpidtype_symbolic ${CU_DEVDRV[$CU_IDX]} ${CU_DEVNAME[$cu_idx]} ${CU_GROUPCHANNELS[$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 cutype chpidtypename devdrv devname groupchs 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 while read chp devbusid cutype chpidtypename devdrv devname groupchs cardtype; do debug 1 " # $chp $devbusid $cutype $chpidtypename $devdrv $devname $groupchs $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" } if [ $# == 1 -a "$1" == "-a" ]; then all=1 fi 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