#!/bin/bash # # lszfcp - Tool to display information about zfcp devices (adapters/ports/units) # # Copyright IBM Corp. 2006, 2019 # # s390-tools is free software; you can redistribute it and/or modify # it under the terms of the MIT license. See LICENSE for details. # SCRIPTNAME='lszfcp' SYSFS=`cat /proc/mounts | grep -m1 sysfs | awk '{ print $2 }'` FC_CLASS=false # Command line parameters VERBOSITY=0 SHOW_MODPARMS=0 SHOW_HOSTS=0 SHOW_PORTS=0 SHOW_DEVICES=0 SHOW_ATTRIBUTES=false SHOW_MORE_ATTRS=0 SHOW_EXTENDED=false unset PAR_BUSID PAR_WWPN PAR_LUN ############################################################################## check_sysfs() { if [ -z $SYSFS -o ! -d $SYSFS -o ! -r $SYSFS ]; then echo "Error: sysfs not available." exit 1 fi } check_zfcp_support() { if [ ! -e $SYSFS/bus/ccw/drivers/zfcp ]; then echo "Error: No zfcp support available." echo "Load the zfcp module or compile" echo "the kernel with zfcp support." exit 1 fi } check_fcp_devs() { local found=0 if $FC_CLASS; then # theoretically this includes "defunct" (and thus online) devs ignore=`ls $SYSFS/class/fc_host/host* 2>&1` else ignore=`ls $SYSFS/devices/css0/[0-9]*/[0-9]*/host[0-9]* 2>&1` fi [ $? -eq 0 ] && found=1 if [ $found -eq 0 ] && $SHOW_EXTENDED; then # nothing found yet so search for "defunct" FCP devices ignore=$(ls -dX $SYSFS/devices/css[0-9]*/defunct/[0-9]*/host[0-9]* 2>&1) [ $? -eq 0 ] && found=1 fi if [ $found -eq 0 ] && $SHOW_EXTENDED; then local mypath mylist="" # nothing found yet so search for FCP devices never been online for mypath in $SYSFS/bus/ccw/drivers/zfcp/*.*.*; do if [ "$mypath" = "$SYSFS/bus/ccw/drivers/zfcp/*.*.*" ]; then break # glob did not match anything fi [ -d $mypath/host[0-9]* ] && continue # is/was online mylist="$mylist $(readlink -e $mypath)/-" done [ -n "$mylist" ] && found=1 fi if [ $found -eq 0 ]; then echo "Error: No fcp devices found." exit 1 fi } check_fc_class() { if [ -d "$SYSFS/class/fc_host" ]; then FC_CLASS=true fi } print_version() { cat < lszfcp -P -H -D 0.0.3d0c host0 0.0.3d0c/0x500507630300c562 rport-0:0-0 0.0.3d0c/0x500507630300c562/0x4010403300000000 0:0:0:0 The default is to list bus_ids of all zfcp adapters and corresponding SCSI host names (equals "lszfcp -H"). EOF } show_attributes() { if [ -z $1 -o ! -d $1 -o ! -r $1 ]; then return 1 fi for FILE in `ls $1`; do read 2>/dev/null CONTENT < $1/$FILE # Read fails for directories and # files with permissions 0200. if [ $? -ne 0 ]; then continue fi printf " %-19s = \"%s\"\n" "$FILE" "$CONTENT" done } show_modparms() { local modparmdir=$SYSFS/module/zfcp/parameters [ -d "$modparmdir" ] || return if [ $VERBOSITY -ne 0 ]; then echo "$modparmdir" fi echo 'Module = "zfcp"' show_attributes "$modparmdir" echo } show_hosts() { HOST_LIST=`ls -dX $SYSFS/devices/css0/[0-9]*/[0-9]*/host[0-9]* \ 2> /dev/null` if $SHOW_EXTENDED; then # add all "defunct" FCP devices HOST_LIST="$HOST_LIST $(ls -dX $SYSFS/devices/css[0-9]*/defunct/[0-9]*/host[0-9]* 2>/dev/null)" # add all FCP devices that have never been online for HOST_PATH in $SYSFS/bus/ccw/drivers/zfcp/*.*.*; do [ -d $HOST_PATH/host[0-9]* ] && continue # is/was online HOST_LIST="$HOST_LIST $(readlink -e $HOST_PATH)/-" done fi for HOST_PATH in $HOST_LIST; do SCSI_HOST=`basename $HOST_PATH` ADAPTER_PATH=`dirname $HOST_PATH` ADAPTER=`basename $ADAPTER_PATH` [ $ADAPTER != ${PAR_BUSID:-$ADAPTER} ] && continue read ONLINE < $ADAPTER_PATH/online read AVAILABILITY < $ADAPTER_PATH/availability [ -r $ADAPTER_PATH/failed ] && read FAILED < $ADAPTER_PATH/failed DEFUNCT=${ADAPTER_PATH%/*} # strip devbusid DEFUNCT=${DEFUNCT##*/} # basename HOSTMARKER="" if $SHOW_EXTENDED; then if [ "$DEFUNCT" = "defunct" ]; then HOSTMARKER=" defunct" elif [ "$ONLINE" = "0" ]; then HOSTMARKER=" offline" elif [ "$FAILED" = "1" ]; then HOSTMARKER=" failed" elif [ "$AVAILABILITY" != "good" ]; then HOSTMARKER=" NotAvailable" fi fi if [ $VERBOSITY -eq 0 ]; then echo $ADAPTER $SCSI_HOST$HOSTMARKER else echo $ADAPTER_PATH$HOSTMARKER if [ "$SCSI_HOST" != "-" ]; then $FC_CLASS && echo "$SYSFS/class/fc_host/$SCSI_HOST" echo "$SYSFS/class/scsi_host/$SCSI_HOST" else echo "-" echo "-" fi fi if $SHOW_ATTRIBUTES; then if [ $SHOW_MORE_ATTRS -ge 1 ]; then echo 'Bus = "css"' if [ "$DEFUNCT" = "defunct" ]; then printf " %-19s\n" "defunct" else show_attributes ${ADAPTER_PATH%/*} fi fi echo 'Bus = "ccw"' show_attributes $ADAPTER_PATH if [ "$SCSI_HOST" = "-" ]; then # skip output of non-existent fc_host & scsi_host echo continue fi if $FC_CLASS; then echo 'Class = "fc_host"' show_attributes \ "$SYSFS/class/fc_host/$SCSI_HOST" fi echo 'Class = "scsi_host"' show_attributes "$SYSFS/class/scsi_host/$SCSI_HOST" echo fi done } show_ports() { # Without fc_remote_class there is far less information to display. if ! $FC_CLASS; then PORT_LIST=`ls -d $SYSFS/devices/css0/*/*/0x*` for PORT_PATH in $PORT_LIST; do WWPN=`basename $PORT_PATH` ADAPTER=`basename \`dirname $PORT_PATH\`` [ $WWPN != ${PAR_WWPN:-$WWPN} ] && continue [ $ADAPTER != ${PAR_BUSID:-$ADAPTER} ] && continue if [ $VERBOSITY -eq 0 ]; then echo "$ADAPTER/$WWPN" else echo $PORT_PATH fi done return fi if [ -e $SYSFS/class/fc_remote_ports/ ]; then PORT_LIST=`ls -d $SYSFS/class/fc_remote_ports/* 2>/dev/null` fi; for FC_PORT_PATH in $PORT_LIST; do PORT=`basename $FC_PORT_PATH` read PORT_STATE < $FC_PORT_PATH/port_state if [ "$PORT_STATE" == "Online" ] || $SHOW_EXTENDED; then read WWPN < $FC_PORT_PATH/port_name else continue fi PORTSTATEMARKER="" $SHOW_EXTENDED && [ "$PORT_STATE" != "Online" ] \ && PORTSTATEMARKER=" NotOnline" [ $WWPN != ${PAR_WWPN:-$WWPN} ] && continue local sysreal=$(readlink -e "$FC_PORT_PATH") local ADAPTER="" while [ -n "$sysreal" ]; do # ascend to parent: strip last path part sysreal=${sysreal%/*} [ -h $sysreal/subsystem ] || continue local subsystem=$(readlink -e $sysreal/subsystem) if [ "${subsystem##*/}" = "ccw" ]; then ADAPTER=${sysreal##*/} break fi done [ -z "$ADAPTER" ] && continue # skip not zfcp-attached ones if [ -d $SYSFS/devices/css[0-9]*/[0-9d]*/[0-9]*/$WWPN/../host[0-9]*/$PORT ]; then ADAPTER_PORT_PATH=$SYSFS/devices/css[0-9]*/[0-9d]*/$ADAPTER/$WWPN else ADAPTER_PORT_PATH="-" fi [ $ADAPTER != ${PAR_BUSID:-$ADAPTER} ] && continue if [ $VERBOSITY -eq 0 ]; then echo "$ADAPTER/$WWPN $PORT$PORTSTATEMARKER" else if [ "$ADAPTER_PORT_PATH" != "-" ]; then echo $ADAPTER_PORT_PATH else echo "- (NoMoreZfcpPort:$ADAPTER/$WWPN)" fi echo $FC_PORT_PATH$PORTSTATEMARKER fi if $SHOW_ATTRIBUTES; then if [ $SHOW_MORE_ATTRS -ge 1 ] \ && [ "$ADAPTER_PORT_PATH" != "-" ] \ && [ -d $ADAPTER_PORT_PATH ]; then # fc_rport can exist without zfcp_port # (e.g. after port_remove) echo 'Class = "zfcp_port"' show_attributes "$ADAPTER_PORT_PATH" fi echo 'Class = "fc_remote_ports"' show_attributes "$FC_PORT_PATH" echo fi done } show_devices() { # Differentiate old and new sysfs layout if $FC_CLASS; then SCSI_DEVICE_LIST=`ls -d \ $SYSFS/bus/ccw/drivers/zfcp/*/host*/rport*/target*/*/ \ 2>/dev/null |grep -P '\d+:\d+:\d+:\d+'` else SCSI_DEVICE_LIST=`ls -d $SYSFS/devices/css0/*/*/host[0-9]*/*/` fi if $SHOW_EXTENDED; then ZFCP_UNIT_LIST=$(ls -d \ $SYSFS/devices/css[0-9]*/[0-9d]*/[0-9]*/0x*/0x* \ 2> /dev/null) else ZFCP_UNIT_LIST="" fi if [ -z "$SCSI_DEVICE_LIST" ] && [ -z "$ZFCP_UNIT_LIST" ]; then if $SHOW_EXTENDED; then echo "Error: No zfcp-attached SCSI devices found." else echo "Error: No fcp devices found." fi fi for SCSI_DEVICE_PATH in $SCSI_DEVICE_LIST; do read ADAPTER < $SCSI_DEVICE_PATH/hba_id read WWPN < $SCSI_DEVICE_PATH/wwpn read LUN < $SCSI_DEVICE_PATH/fcp_lun # remove from ZFCP_UNIT_LIST if SCSI device exists REDUCED_LIST="" for UNIT_PATH in $ZFCP_UNIT_LIST; do STRIPPED_PATH=$UNIT_PATH L=${UNIT_PATH##*/} STRIPPED_PATH=${STRIPPED_PATH%/*} W=${STRIPPED_PATH##*/} STRIPPED_PATH=${STRIPPED_PATH%/*} A=${STRIPPED_PATH##*/} [ "$A/$W/$L" = "$ADAPTER/$WWPN/$LUN" ] && continue REDUCED_LIST="$REDUCED_LIST $UNIT_PATH" done ZFCP_UNIT_LIST="$REDUCED_LIST" [ $LUN != ${PAR_LUN:-$LUN} ] && continue [ $WWPN != ${PAR_WWPN:-$WWPN} ] && continue [ $ADAPTER != ${PAR_BUSID:-$ADAPTER} ] && continue ZFCP_UNIT_PATH=$SYSFS/devices/css[0-9]*/[0-9d]*/$ADAPTER/$WWPN/$LUN SDEVMARKER="" if $SHOW_EXTENDED; then [ -d $ZFCP_UNIT_PATH ] || SDEVMARKER="$SDEVMARKER auto" [ -r $SCSI_DEVICE_PATH/state ] \ && read SDEVSTATE < $SCSI_DEVICE_PATH/state [ "$SDEVSTATE" != "running" ] \ && SDEVMARKER="$SDEVMARKER NotRunning" fi if [ $VERBOSITY -eq 0 ]; then echo "$ADAPTER/$WWPN/$LUN `basename $SCSI_DEVICE_PATH`$SDEVMARKER" else echo "`ls -d $SYSFS/devices/css0/[0-9d]*/$ADAPTER`/$WWPN/$LUN" echo ${SCSI_DEVICE_PATH%*/}$SDEVMARKER # without trailing slash # On live systems, there are links to the block and # generic devices. In a dbginfo archive, these links # are not present. Therefore, fall back to reading # the runtime.out log file. if [ `ls $SCSI_DEVICE_PATH | grep -c block:` -eq 1 ] then BLOCK_DEV=`ls $SCSI_DEVICE_PATH | grep block:` GEN_DEV=`ls $SCSI_DEVICE_PATH |\ grep scsi_generic:` echo -n "$SYSFS/block/${BLOCK_DEV#*:} " echo "$SYSFS/class/scsi_generic/${GEN_DEV#*:}" elif [ -d $SCSI_DEVICE_PATH/block ] && $SHOW_EXTENDED then # case without CONFIG_SYSFS_DEPRECATED BLOCK_DEV=$(echo $SCSI_DEVICE_PATH/block/*) BLOCK_DEV=${BLOCK_DEV##*/} echo -n "$SYSFS/block/$BLOCK_DEV " local SGPATH=$SCSI_DEVICE_PATH/scsi_generic if [ -d $SGPATH ]; then GEN_DEV=$(echo $SGPATH/*) GEN_DEV=${GEN_DEV##*/} echo "$SYSFS/class/scsi_generic/$GEN_DEV" else echo "-" fi # FIXME Find a way to assign the generic devices. elif [ -r $SYSFS/../runtime.out ]; then SCSI_DEV=`basename $SCSI_DEVICE_PATH` echo "$SYSFS/block/"`grep -r "\[$SCSI_DEV\]"\ $SYSFS/../runtime.out |\ awk -F "/dev/" '{print $2}'` fi fi if $SHOW_ATTRIBUTES && [ $SHOW_MORE_ATTRS -ge 1 ]; then # auto scan LUNs not necessarily have a zfcp_unit if [ -d $ZFCP_UNIT_PATH ]; then echo 'Class = "zfcp_unit"' show_attributes "$ZFCP_UNIT_PATH" fi fi if $SHOW_ATTRIBUTES; then echo 'Class = "scsi_device"' show_attributes "$SCSI_DEVICE_PATH" fi if $SHOW_ATTRIBUTES && [ $SHOW_MORE_ATTRS -ge 2 ]; then if [ -d $SCSI_DEVICE_PATH/scsi_disk ]; then echo 'Class = "scsi_disk"' show_attributes "$SCSI_DEVICE_PATH/scsi_disk/$(basename $SCSI_DEVICE_PATH)" fi if [ -d $SCSI_DEVICE_PATH/block ]; then echo 'Class = "block"' show_attributes "$SCSI_DEVICE_PATH/block/*/" fi if [ -d $SCSI_DEVICE_PATH/block/*/integrity ]; then echo 'Class = "block_integrity"' show_attributes "$SCSI_DEVICE_PATH/block/*/integrity" fi if [ -d $SCSI_DEVICE_PATH/block/*/queue ]; then echo 'Class = "block_queue"' show_attributes "$SCSI_DEVICE_PATH/block/*/queue" fi if [ -d $SCSI_DEVICE_PATH/block/*/queue/iosched ]; then echo 'Class = "block_queue_iosched"' show_attributes "$SCSI_DEVICE_PATH/block/*/queue/iosched" fi fi if $SHOW_ATTRIBUTES; then echo fi done # what's left in ZFCP_UNIT_LIST are now units without SCSI device for UNIT_PATH in $ZFCP_UNIT_LIST; do STRIPPED_PATH=$UNIT_PATH LUN=${UNIT_PATH##*/} STRIPPED_PATH=${STRIPPED_PATH%/*} WWPN=${STRIPPED_PATH##*/} STRIPPED_PATH=${STRIPPED_PATH%/*} ADAPTER=${STRIPPED_PATH##*/} [ $LUN != ${PAR_LUN:-$LUN} ] && continue [ $WWPN != ${PAR_WWPN:-$WWPN} ] && continue [ $ADAPTER != ${PAR_BUSID:-$ADAPTER} ] && continue if [ $VERBOSITY -eq 0 ]; then echo "$ADAPTER/$WWPN/$LUN - failed" else echo "$UNIT_PATH failed" echo "-" fi if $SHOW_ATTRIBUTES && [ $SHOW_MORE_ATTRS -ge 1 ]; then echo 'Class = "zfcp_unit"' show_attributes "$UNIT_PATH" fi if $SHOW_ATTRIBUTES; then echo fi done } ############################################################################## ARGS=`getopt --options ahvHPDVb:p:l:s:emZ --longoptions \ attributes,help,version,hosts,ports,devices,verbose,busid:,wwpn:,lun:,sysfs:,extended,moreattrs,modparms \ -n "$SCRIPTNAME" -- "$@"` if [ $? -ne 0 ]; then echo print_help exit fi eval set -- "$ARGS" for ARG; do case "$ARG" in -a|--attributes) SHOW_ATTRIBUTES=true; shift 1;; -m|--moreattrs) ((SHOW_MORE_ATTRS++)); shift 1;; -e|--extended) SHOW_EXTENDED=true; shift 1;; -b|--busid) PAR_BUSID=$2; shift 2;; -h|--help) print_help; exit 0;; -l|--lun) PAR_LUN=$2; shift 2;; -p|--wwpn) PAR_WWPN=$2; shift 2;; -v|--version) print_version; exit 0;; -H|--hosts) SHOW_HOSTS=1; shift 1;; -D|--devices) SHOW_DEVICES=1; shift 1;; -P|--ports) SHOW_PORTS=1; shift 1;; -V|--verbose) VERBOSITY=1; shift 1;; -Z|--modparms) SHOW_MODPARMS=1; shift 1;; -s|--sysfs) SYSFS=$2; shift 2;; --) shift; break;; esac done check_sysfs check_zfcp_support check_fc_class check_fcp_devs default=1 if [ $SHOW_MODPARMS -eq 1 ]; then default=0; show_modparms fi if [ $SHOW_HOSTS -eq 1 ]; then default=0; show_hosts elif [ $SHOW_PORTS -eq 0 -a $SHOW_DEVICES -eq 0 -a -n "$PAR_BUSID" ]; then default=0; show_hosts fi if [ $SHOW_PORTS -eq 1 ]; then default=0; show_ports elif [ $SHOW_HOSTS -eq 0 -a $SHOW_DEVICES -eq 0 -a -n "$PAR_WWPN" ]; then default=0; show_ports fi if [ $SHOW_DEVICES -eq 1 ]; then default=0; show_devices elif [ $SHOW_HOSTS -eq 0 -a $SHOW_PORTS -eq 0 -a -n "$PAR_LUN" ]; then default=0; show_devices fi if [ $default == 1 ]; then show_hosts fi