mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Initial s390-tools-2.0.0 import
This commit is based on the s390-tools-1.39.0 version. Changes on top of s390-tools-1.39.0: - Add MIT license to all source files - Add LICENSE file - Transform REAMDE to README.md (markdown) - Add AUTHORS.md file - Add CONTRIBUTING.md file - Move changelog from README to CHANGELOG.md file Reviewed-by: Stefan Haberland <sth@linux.vnet.ibm.com> Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
#==============================================================================
|
||||
# Makefile for zSeries configuration utilities.
|
||||
#==============================================================================
|
||||
include ../common.mak
|
||||
|
||||
SCRIPTS = lsdasd lstape chccwdev lszfcp cio_ignore znetconf dasdstat
|
||||
USRSBIN_SCRIPTS = lsmem chmem lsluns
|
||||
MANPAGES= lsdasd.8 lstape.8 chccwdev.8 lszfcp.8 lsluns.8 \
|
||||
cio_ignore.8 znetconf.8 chmem.8 lsmem.8 dasdstat.8
|
||||
|
||||
SUB_DIRS = zcrypt scm chp css qeth
|
||||
|
||||
all: $(SUB_DIRS)
|
||||
|
||||
clean: $(SUB_DIRS)
|
||||
|
||||
install: install-scripts install-manpages install-usrsbin-scripts $(SUB_DIRS)
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 lsznet.raw \
|
||||
$(DESTDIR)$(TOOLS_LIBDIR)
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 znetcontrolunits \
|
||||
$(DESTDIR)$(TOOLS_LIBDIR)
|
||||
|
||||
install-scripts: $(SCRIPTS)
|
||||
@for i in $^; do \
|
||||
cat $$i | \
|
||||
sed -e 's+%S390_TOOLS_VERSION%+$(S390_TOOLS_RELEASE)+' \
|
||||
>$(DESTDIR)$(BINDIR)/$$i; \
|
||||
chown $(OWNER).$(GROUP) $(DESTDIR)$(BINDIR)/$$i; \
|
||||
chmod 755 $(DESTDIR)$(BINDIR)/$$i; \
|
||||
done
|
||||
|
||||
install-usrsbin-scripts: $(USRSBIN_SCRIPTS)
|
||||
@for i in $^; do \
|
||||
cat $$i | \
|
||||
sed -e 's+%S390_TOOLS_VERSION%+$(S390_TOOLS_RELEASE)+' \
|
||||
>$(DESTDIR)$(USRSBINDIR)/$$i; \
|
||||
chown $(OWNER).$(GROUP) $(DESTDIR)$(USRSBINDIR)/$$i; \
|
||||
chmod 755 $(DESTDIR)$(USRSBINDIR)/$$i; \
|
||||
done
|
||||
|
||||
install-manpages: $(MANPAGES)
|
||||
@if [ ! -d $(DESTDIR)$(MANDIR) ]; then \
|
||||
mkdir -p $(DESTDIR)$(MANDIR)/man8; \
|
||||
chown $(OWNER).$(GROUP) $(DESTDIR)$(MANDIR); \
|
||||
chown $(OWNER).$(GROUP) $(DESTDIR)$(MANDIR)/man8; \
|
||||
chmod 755 $(DESTDIR)$(MANDIR); \
|
||||
chmod 755 $(DESTDIR)$(MANDIR)/man8; \
|
||||
fi; \
|
||||
for i in $^; do \
|
||||
install -o $(OWNER) -g $(GROUP) -m 644 $$i $(DESTDIR)$(MANDIR)/man8; \
|
||||
done
|
||||
|
||||
#
|
||||
# For simple "make" we explicitly set the MAKECMDGOALS to "all".
|
||||
#
|
||||
ifeq ($(MAKECMDGOALS),)
|
||||
MAKECMDGOALS = all
|
||||
endif
|
||||
|
||||
$(SUB_DIRS):
|
||||
$(foreach goal,$(MAKECMDGOALS), \
|
||||
$(MAKE) -C $@ TOPDIR=$(TOPDIR) ARCH=$(ARCH) $(goal) ;)
|
||||
.PHONY: $(SUB_DIRS)
|
||||
|
||||
.PHONY: all install clean install-scripts install-manpages install-usrsbin-scripts
|
||||
Executable
+469
@@ -0,0 +1,469 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# chccwdev - Tool to change attributes of a ccw device
|
||||
#
|
||||
# Copyright IBM Corp. 2003, 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.
|
||||
#
|
||||
|
||||
CMD=$(basename $0)
|
||||
MAX_RETRIES=5
|
||||
CIO_SETTLE="/proc/cio_settle"
|
||||
ONLINEATTR="online"
|
||||
SYSPATH=NULL
|
||||
|
||||
if [ "$(cat /proc/filesystems|grep sysfs)" = "" ]; then
|
||||
echo "ERROR: $CMD requires sysfs support!" >&2
|
||||
exit 1
|
||||
fi
|
||||
SYSFSDIR=$(cat /proc/mounts|awk '$3=="sysfs"{print $2; exit}')
|
||||
if [ "$SYSFSDIR" = "" ]; then
|
||||
echo "ERROR: $CMD requires sysfs filesystem mounted!" >&2
|
||||
fi
|
||||
|
||||
function PrintUsage() {
|
||||
cat <<-EOD
|
||||
Usage: $(basename $0) [<options>] <devices>
|
||||
|
||||
<options>
|
||||
-a|--attribute <name>=<value>
|
||||
-e|--online
|
||||
Tries to set the given device online.
|
||||
-f|--forceonline
|
||||
Tries to force a device online if the device
|
||||
driver supports this.
|
||||
-d|--offline
|
||||
Tries to set the given device offline.
|
||||
-s|--safeoffline
|
||||
Tries to set the given device offline waiting for all outstanding I/O. May block forever.
|
||||
-v|--version
|
||||
Show tools and command version.
|
||||
|
||||
<devices>
|
||||
<bus ID>[-<busid>][,<busid>[-<busid>]] ...
|
||||
EOD
|
||||
}
|
||||
|
||||
function PrintVersion()
|
||||
{
|
||||
cat <<-EOD
|
||||
$CMD: version %S390_TOOLS_VERSION%
|
||||
Copyright IBM Corp. 2003, 2017
|
||||
EOD
|
||||
}
|
||||
|
||||
function CheckOnlineArg()
|
||||
{
|
||||
if [ "$ONLINE" = $1 ] ;then
|
||||
echo "$CMD: Incompatible argument list." >&2
|
||||
echo "Try '$CMD --help' for more information." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
function SetAttribute()
|
||||
{
|
||||
local NAME="$1"
|
||||
local VAL="$2"
|
||||
local CNT=0
|
||||
|
||||
if [ "$VAL" = "" ]; then
|
||||
echo "WARNING: Attribute[$NAME] has no value and will" \
|
||||
"be ignored!" >&2
|
||||
return
|
||||
fi
|
||||
if [ "$NAME" = "online" ]; then
|
||||
if [ "$VAL" = "force" ]; then
|
||||
CheckOnlineArg 0
|
||||
ONLINE=1
|
||||
FORCE="force"
|
||||
elif [ "$VAL" = "1" ]; then
|
||||
CheckOnlineArg 0
|
||||
ONLINE=1
|
||||
else
|
||||
CheckOnlineArg 1
|
||||
ONLINE=0
|
||||
fi
|
||||
ACTIONSET=true
|
||||
return
|
||||
elif [ "$NAME" = "safe_offline" ]; then
|
||||
CheckOnlineArg 1
|
||||
ONLINE=0
|
||||
ACTIONSET=true
|
||||
ONLINEATTR="safe_offline"
|
||||
return
|
||||
fi
|
||||
|
||||
while [ $CNT -lt $NUMATTR ]; do
|
||||
if [ "${ATTRNAME[$CNT]}" = "$NAME" ]; then
|
||||
break
|
||||
fi
|
||||
let "CNT++"
|
||||
done
|
||||
|
||||
ATTRNAME[$CNT]="$NAME"
|
||||
ATTRVAL[$CNT]="$VAL"
|
||||
|
||||
if [ $CNT = $NUMATTR ]; then
|
||||
let "NUMATTR++"
|
||||
ACTIONSET=true
|
||||
fi
|
||||
}
|
||||
|
||||
FORCE=""
|
||||
ONLINE=""
|
||||
NUMATTR=0
|
||||
ACTIONSET=false
|
||||
while [ $# -gt 0 ]; do
|
||||
case $1 in
|
||||
-a|--attribute)
|
||||
SetAttribute "$(echo $2|cut -d= -f1)" \
|
||||
"$(echo $2|cut -d= -f2)"
|
||||
shift
|
||||
;;
|
||||
-a*)
|
||||
SetAttribute "$(echo $1|cut -c3-|cut -d= -f1)" \
|
||||
"$(echo $1|cut -c3-|cut -d= -f2)"
|
||||
;;
|
||||
-h|--help)
|
||||
PrintUsage
|
||||
exit 0
|
||||
;;
|
||||
-e|--online)
|
||||
SetAttribute "online" 1
|
||||
;;
|
||||
-f|--forceonline)
|
||||
SetAttribute "online" "force"
|
||||
;;
|
||||
-d|--offline)
|
||||
SetAttribute "online" 0
|
||||
;;
|
||||
-s|--safeoffline)
|
||||
SetAttribute "safe_offline" 0
|
||||
;;
|
||||
-v|--version)
|
||||
PrintVersion
|
||||
exit 0
|
||||
;;
|
||||
-*)
|
||||
echo "$CMD: Invalid option $1" >&2
|
||||
echo "Try '$CMD --help' for more" \
|
||||
"information." >&2
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
if [ "$BUSIDLIST" = "" ]; then
|
||||
BUSIDLIST="$1"
|
||||
else
|
||||
BUSIDLIST="$BUSIDLIST,$1"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [ -w $CIO_SETTLE ] ; then
|
||||
echo 1 > $CIO_SETTLE
|
||||
fi
|
||||
|
||||
#
|
||||
# Parse the BUSIDLIST and expand the ranges and short IDs.
|
||||
#
|
||||
BUSIDLIST=$(
|
||||
echo "$BUSIDLIST" | awk '
|
||||
function hex2dec(hex, d, h, i) {
|
||||
d = 0
|
||||
for (i = 1; i <= length(hex); i++) {
|
||||
h = index("0123456789abcdef", tolower(substr(hex,i,1)))
|
||||
d = (d * 16) + (h - 1)
|
||||
}
|
||||
return d
|
||||
}
|
||||
function dec2hex(dec, d, h) {
|
||||
h = ""
|
||||
for(d = dec; d > 0; d = int(d / 16)) {
|
||||
h = substr("0123456789abcdef", (d % 16) + 1, 1) h
|
||||
}
|
||||
while (length(h) < 4)
|
||||
h = "0" h
|
||||
|
||||
return h
|
||||
}
|
||||
function BusIDValid(id, css, dsn) {
|
||||
split(id, part, ".")
|
||||
css = int(part[1])
|
||||
if (css < 0 || css > 255)
|
||||
return 0
|
||||
dsn = int(part[2])
|
||||
if (dsn < 0 || dsn > 255)
|
||||
return 0
|
||||
if (length(part[3]) > 4)
|
||||
return 0
|
||||
if (match(part[3], /^[a-f0-9]+$/) == 0)
|
||||
return 0
|
||||
return 1
|
||||
}
|
||||
function ExpandBusID(id) {
|
||||
if (length(id) < 1) {
|
||||
print "\"\" is not a valid bus ID." >err
|
||||
return ""
|
||||
}
|
||||
split(id, part, ".")
|
||||
if (3 in part) {
|
||||
css = part[1]
|
||||
dsn = part[2]
|
||||
did = part[3]
|
||||
} else if (2 in part) {
|
||||
css = 0
|
||||
dsn = part[1]
|
||||
did = part[2]
|
||||
} else {
|
||||
css = 0
|
||||
dsn = 0
|
||||
did = part[1]
|
||||
}
|
||||
while (length(did) < 4)
|
||||
did = "0" did
|
||||
|
||||
busid = css "." dsn "." did
|
||||
if (! BusIDValid(busid)) {
|
||||
print busid " is not a valid bus ID." >err
|
||||
return ""
|
||||
}
|
||||
return busid
|
||||
}
|
||||
function ExpandRange(range, i) {
|
||||
split(range, id, "-")
|
||||
from = ExpandBusID(id[1])
|
||||
if (from == "")
|
||||
return -1
|
||||
|
||||
to = ExpandBusID(id[2])
|
||||
if (to == "")
|
||||
return -1
|
||||
|
||||
split(from, parts1, ".")
|
||||
split(to, parts2, ".")
|
||||
|
||||
if (parts1[1] != parts2[1] || parts1[2] != parts2[2]) {
|
||||
print "Invalid range (" from "-" to ")" >err
|
||||
return -1
|
||||
}
|
||||
from = hex2dec(parts1[3])
|
||||
to = hex2dec(parts2[3])
|
||||
if (from > to) {
|
||||
print "Invalid range order" >err
|
||||
return -1
|
||||
}
|
||||
found = 0
|
||||
for (i = from; i <= to; i++) {
|
||||
# Expand ranges only to valid entries.
|
||||
busid = parts1[1] "." parts1[2] "." dec2hex(i)
|
||||
filen = SYSFSBASE busid "/devtype"
|
||||
if ((getline x <filen) > 0) {
|
||||
found = 1
|
||||
print busid
|
||||
}
|
||||
close(filen)
|
||||
}
|
||||
if (!found) {
|
||||
print "No Device in range (" parts1[1] "." parts1[2] \
|
||||
"." parts1[3] "-" parts2[1] "." parts2[2] \
|
||||
"." parts2[3] ") found" >err
|
||||
return -1
|
||||
}
|
||||
}
|
||||
BEGIN{
|
||||
SYSFSBASE = "'$SYSFSDIR'/bus/ccw/devices/"
|
||||
err = "/dev/stderr"
|
||||
}
|
||||
{
|
||||
line = tolower($0)
|
||||
gsub(/[ \t]+/, "", line)
|
||||
|
||||
split(line, range, ",")
|
||||
for (i=1; i in range; i++) {
|
||||
if (match(range[i], /-/)) {
|
||||
if (ExpandRange(range[i]) < 0)
|
||||
exit 1
|
||||
} else {
|
||||
busid = ExpandBusID(range[i])
|
||||
if (busid == "")
|
||||
exit 1
|
||||
print busid
|
||||
}
|
||||
}
|
||||
}'
|
||||
)
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "ERROR: Evaluation of bus IDs failed!" >&2
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$BUSIDLIST" = "" ]; then
|
||||
PrintUsage
|
||||
echo ""
|
||||
echo "No bus ID given!" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! $ACTIONSET; then
|
||||
PrintUsage
|
||||
echo ""
|
||||
echo "No action specified. Please use a valid action switch." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
function IsOnline() {
|
||||
local ONLINE=$(cat $1/online 2>/dev/null)
|
||||
if [ -z $ONLINE ]; then
|
||||
if [ $2 -eq 0 ]; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
if [ $ONLINE -eq $2 ]; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
function StoreAttribute()
|
||||
{
|
||||
local SYSPATH="$1"
|
||||
local BUSID="$(basename "$1")"
|
||||
local NAME="${ATTRNAME[$2]}"
|
||||
local VALUE="${ATTRVAL[$2]}"
|
||||
local CHECK="$3"
|
||||
|
||||
if [ ! -f "$SYSPATH/$NAME" ]; then
|
||||
return 1
|
||||
fi
|
||||
if [ "$VALUE" = "" ]; then
|
||||
return 0
|
||||
fi
|
||||
if [ "$(cat $SYSPATH/$NAME)" = "$VALUE" ]; then
|
||||
echo "The $NAME attribute of $BUSID already is $VALUE"
|
||||
ATTRVAL[$2]=""
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "Setting $NAME attribute of $BUSID to $VALUE"
|
||||
echo "$VALUE" >"$SYSPATH/$NAME" 2>/dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
ATTRVAL[$2]=""
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ $CHECK ]; then
|
||||
echo "ERROR: Failed to set $NAME attribute of $BUSID!" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
function PrintError()
|
||||
{
|
||||
read ERROR
|
||||
if [ -n "$ERROR" ] ;then
|
||||
echo "Failed (${ERROR##*: })" >&2
|
||||
if [ ! -e $SYSPATH/driver ]; then
|
||||
read CUTYPE 2>/dev/null < $SYSPATH/cutype
|
||||
read DEVTYPE 2>/dev/null < $SYSPATH/devtype
|
||||
if [ $? -ne 0 ] ;then
|
||||
exit 1
|
||||
fi
|
||||
if [[ $DEVTYPE == "n/a" ]] ;then
|
||||
DEVTYPE="0000/00"
|
||||
fi
|
||||
echo "Note: No driver is attached to this device" \
|
||||
"(DevType:$DEVTYPE CU Type:$CUTYPE)." >&2
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
SAVEDATTRS=("${ATTRVAL[@]}")
|
||||
for BUSID in $BUSIDLIST; do
|
||||
SYSPATH=$SYSFSDIR/bus/ccw/devices/$BUSID
|
||||
if [ ! -r $SYSPATH ]; then
|
||||
echo "Device $BUSID not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$ONLINE" != "" ]; then
|
||||
|
||||
CNT=0
|
||||
while [ $CNT -lt $NUMATTR ]; do
|
||||
StoreAttribute "$SYSPATH" $CNT
|
||||
if [ $? -ne 0 ]; then
|
||||
if IsOnline $SYSPATH 1; then
|
||||
echo "ERROR: Device[$BUSID] has no attribute" \
|
||||
"${ATTRNAME[$CNT]}!" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
let "CNT++"
|
||||
done
|
||||
|
||||
if IsOnline $SYSPATH $ONLINE; then
|
||||
if [ "$ONLINE" -eq 1 ]; then
|
||||
echo "WARNING: Device[$BUSID] is already " \
|
||||
"online" >&2
|
||||
else
|
||||
echo "Device is already offline" >&2
|
||||
fi
|
||||
else
|
||||
if [ "$ONLINE" -eq 1 ]; then
|
||||
echo "Setting device $BUSID online"
|
||||
else
|
||||
echo "Setting device $BUSID offline"
|
||||
fi
|
||||
if [ ! -e $SYSPATH/$ONLINEATTR ]; then
|
||||
echo "$ONLINEATTR attribute not available for" \
|
||||
" device[$BUSID]" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ "$FORCE" != "" ]; then
|
||||
echo $FORCE 2>&1 >$SYSPATH/$ONLINEATTR | PrintError
|
||||
else
|
||||
echo $ONLINE 2>&1 > $SYSPATH/$ONLINEATTR | PrintError
|
||||
fi
|
||||
|
||||
#
|
||||
# Workaround for bad drivers which report success but
|
||||
# silently fail or have an asynchronous online processing.
|
||||
#
|
||||
RETRIES=0
|
||||
while ! IsOnline $SYSPATH $ONLINE; do
|
||||
if [ $RETRIES -eq $MAX_RETRIES ]; then
|
||||
echo "Failed" >&2
|
||||
exit 1
|
||||
fi
|
||||
sleep 0.5
|
||||
let "RETRIES++"
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
CNT=0
|
||||
while [ $CNT -lt $NUMATTR ]; do
|
||||
StoreAttribute "$SYSPATH" $CNT "CHECK"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "ERROR: Device[$BUSID] has no attribute" \
|
||||
"${ATTRNAME[$CNT]}!" >&2
|
||||
exit 1
|
||||
fi
|
||||
let "CNT++"
|
||||
done
|
||||
echo "Done"
|
||||
|
||||
unset ATTRVAL
|
||||
ATTRVAL=("${SAVEDATTRS[@]}")
|
||||
done
|
||||
|
||||
exit 0
|
||||
@@ -0,0 +1,135 @@
|
||||
.\" Copyright 2017 IBM Corp.
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
.TH CHCCWDEV 8 "Apr 2006" "s390-tools"
|
||||
|
||||
.SH NAME
|
||||
chccwdev \- modify generic attributes of channel attached devices.
|
||||
|
||||
.SH SYNOPSIS
|
||||
.TP 9
|
||||
.B chccwdev
|
||||
.B -h
|
||||
.TP
|
||||
.B chccwdev
|
||||
.RB "[ (" -a
|
||||
.IB <name> = <value>
|
||||
.RB "| " -e " | " -d " | " -s " | " -f ") [...]]"
|
||||
.br
|
||||
.I <range>
|
||||
.RI "[, " "<range>" " [...]]"
|
||||
.TP
|
||||
.B chccwdev
|
||||
.B -v
|
||||
|
||||
.SH DESCRIPTION
|
||||
The chccwdev command is used to set generic attributes for devices that
|
||||
are controlled by the common I/O subsystem. Attributes are set in the order
|
||||
they are specified on the commandline except the online attribute which is
|
||||
special.
|
||||
.P
|
||||
If the same attribute is given more than one time the value that was set
|
||||
last will be used. This is also true (while not that obvious) when mixing
|
||||
the generic
|
||||
.BR -a " and the " -e ", " -d ", "-s" and " -f " arguments."
|
||||
.P
|
||||
All attributes will be set in the following order:
|
||||
.RS
|
||||
.TP 4
|
||||
1.
|
||||
All attributes except online. If the device is offline there will be no
|
||||
error if the attribute doesn't exist.
|
||||
.TP
|
||||
2.
|
||||
Set the online attribute to the desired value ((forced) online or
|
||||
(safe) offline).
|
||||
.TP
|
||||
3.
|
||||
Set all the attributes that havn't been set, yet. At this point invalid
|
||||
attribute names will always cause an error.
|
||||
.RE
|
||||
.P
|
||||
If any error occurs the execution is terminated. So if using ranges only
|
||||
devices before the current one have been modified. There is no automatic
|
||||
rollback. All attributes that already have been changed will stay that way.
|
||||
|
||||
.SH OPTIONS
|
||||
.TP 8
|
||||
.BR -h | --help
|
||||
Print help text.
|
||||
|
||||
.TP 8
|
||||
.BR -v | --version
|
||||
Print the version of the s390-tools package and the command.
|
||||
|
||||
.TP
|
||||
.BR -a | --attribute " \fI<name>\fR=\fI<value>\fR"
|
||||
Try to set the attribute named \fIname\fR to the given value. After writing
|
||||
the attribute it will be read to check whether the setting was accepted.
|
||||
|
||||
.TP
|
||||
.BR -e | --online
|
||||
Try to set the specified devices online.
|
||||
|
||||
.TP
|
||||
.BR -f | --forceonline
|
||||
Same as online but for devices that support this (DASD devices), it can
|
||||
be used to bring it online regardless of any reserved states.
|
||||
|
||||
.TP
|
||||
.BR -d | --offline
|
||||
Try to set the specified devices offline. The --online, --forceonline,
|
||||
--offline, and --safeoffline options are mutually exclusive.
|
||||
|
||||
.TP
|
||||
.BR -s |--safeoffline
|
||||
DASD only: For each specified device, wait until all outstanding I/O
|
||||
requests have completed, and then try to set the device offline. The
|
||||
--online, --forceonline, --offline, and --safeoffline options are
|
||||
mutually exclusive.
|
||||
|
||||
.TP
|
||||
\fB<range>\fR = <bus ID>\fB[-\fR<bus ID>\fB]\fR
|
||||
.TP
|
||||
\fB<bus ID>\fR = ([0-9]+\\.[0-9]+\\.)?[0-9a-f]{1,4}
|
||||
Note that bus IDs specified in short form (i.e. without leading "0.<n>.")
|
||||
will be interpreted as "0.0.<bus ID>".
|
||||
|
||||
Example: "0192" becomes "0.0.0192".
|
||||
|
||||
.SH EXAMPLES
|
||||
\fBchccwdev --online 0.0.0192,0.0.0195-0.0.0198\fR
|
||||
.RS
|
||||
After completing all outstanding I/O requests for the devices with bus
|
||||
ID 0.0.0192, 0.0.0195, 0.0.0196, 0.0.0197 and 0.0.0198, tries to set
|
||||
the device offline.
|
||||
.RE
|
||||
.P
|
||||
.B chccwdev --attribute readonly=1 --attribute online=1 0.0.0192
|
||||
.RS
|
||||
This will try to set the device with bus ID 0.0.192 online with readonly
|
||||
attribute set to one (read-only mode).
|
||||
.RE
|
||||
.P
|
||||
.B chccwdev --attribute cmb_enable=1 0.0.0195-0.0.0198
|
||||
.RS
|
||||
Set the cmb_enable attribute of the devices 0.0.0195, 0.0.0196, 0.0.0197 and
|
||||
0.0.0198 to one. This would for example activate the usage of the channel
|
||||
measurement block facility.
|
||||
.RE
|
||||
.P
|
||||
.B chccwdev --safeoffline 0.0.0192,0.0.0195-0.0.0198
|
||||
.RS
|
||||
Will try to set the devices with bus ID 0.0.0192, 0.0.0195,
|
||||
0.0.0196, 0.0.0197 and 0.0.0198 offline and finish all outstanding I/O
|
||||
requests before.
|
||||
.RE
|
||||
|
||||
.SH AUTHOR
|
||||
.nf
|
||||
This man-page was written by Stefan Bader <shbader@de.ibm.com>.
|
||||
.SH "SEE ALSO"
|
||||
.BR lscss (8)
|
||||
.fi
|
||||
|
||||
Executable
+334
@@ -0,0 +1,334 @@
|
||||
#!/usr/bin/perl
|
||||
#
|
||||
# chmem - Tool to change memory hotplug status
|
||||
#
|
||||
# Copyright IBM Corp. 2010, 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.
|
||||
#
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Getopt::Long qw(:config no_ignore_case no_auto_abbrev);
|
||||
use File::Basename;
|
||||
|
||||
my $script_name = fileparse($0);
|
||||
my $online = 0;
|
||||
my $offline = 0;
|
||||
my $memdir = "/sys/devices/system/memory";
|
||||
my $block_size = 0;
|
||||
my $max_block_nr = 0;
|
||||
my $devices = {};
|
||||
my $dev_size;
|
||||
my $blocks_per_dev = 0;
|
||||
my $devs_per_block = 0;
|
||||
my $ret = 0;
|
||||
my @entries;
|
||||
|
||||
sub chmem_usage()
|
||||
{
|
||||
print <<HERE;
|
||||
Usage: $script_name [OPTIONS] SIZE|RANGE
|
||||
|
||||
The $script_name command sets a particular size or range of memory online
|
||||
or offline.
|
||||
|
||||
Specify SIZE as <size>[m|M|g|G]. With m or M, <size> specifies the memory
|
||||
size in MB (1024 x 1024 bytes). With g or G, <size> specifies the memory size
|
||||
in GB (1024 x 1024 x 1024 bytes). The default unit is MB.
|
||||
|
||||
Specify RANGE in the form 0x<start>-0x<end> as shown in the output of the
|
||||
lsmem command. <start> is the hexadecimal address of the first byte and <end>
|
||||
is the hexadecimal address of the last byte in the memory range.
|
||||
|
||||
SIZE and RANGE must be aligned to the Linux memory block size, as shown in
|
||||
the output of the lsmem command.
|
||||
|
||||
OPTIONS
|
||||
-e, --enable
|
||||
Set the given RANGE or SIZE of memory online.
|
||||
|
||||
-d, --disable
|
||||
Set the given RANGE or SIZE of memory offline.
|
||||
|
||||
-h, --help
|
||||
Print a short help text, then exit.
|
||||
|
||||
-v, --version
|
||||
Print the version number, then exit.
|
||||
HERE
|
||||
}
|
||||
|
||||
sub chmem_version()
|
||||
{
|
||||
print "$script_name: version %S390_TOOLS_VERSION%\n";
|
||||
print "Copyright IBM Corp. 2010, 2017\n";
|
||||
}
|
||||
|
||||
sub chmem_get_dev_size()
|
||||
{
|
||||
my ($device, $old_device, $block, $old_block) = (0, 0, 0, 0);
|
||||
|
||||
foreach (@entries) {
|
||||
$_ =~ /memory(\d+)/;
|
||||
$block = $1;
|
||||
$device = `cat $_/phys_device`;
|
||||
chomp($device);
|
||||
if ($device > $old_device) {
|
||||
$dev_size = int((($block - $old_block) * $block_size) /
|
||||
($device - $old_device));
|
||||
last;
|
||||
}
|
||||
$dev_size += $block_size;
|
||||
$old_block = $block;
|
||||
}
|
||||
}
|
||||
|
||||
sub chmem_online($)
|
||||
{
|
||||
my $block = shift;
|
||||
|
||||
qx(echo online_movable > $memdir/memory$block/state 2>/dev/null);
|
||||
if ($? >> 8 != 0) {
|
||||
qx(echo online > $memdir/memory$block/state 2>/dev/null);
|
||||
}
|
||||
return $? >> 8;
|
||||
}
|
||||
|
||||
sub chmem_offline($)
|
||||
{
|
||||
my $block = shift;
|
||||
|
||||
qx(echo offline > $memdir/memory$block/state 2>/dev/null);
|
||||
return $? >> 8;;
|
||||
}
|
||||
|
||||
sub chmem_read_attr($$$)
|
||||
# parameters: state, device, block
|
||||
{
|
||||
my @attributes = qw(state phys_device);
|
||||
foreach (0..1) {
|
||||
$_[$_] = `cat $memdir/memory$_[2]/$attributes[$_]`;
|
||||
chomp($_[$_]);
|
||||
}
|
||||
}
|
||||
|
||||
sub chmem_read_devices()
|
||||
{
|
||||
my $block = 0;
|
||||
my $device = 0;
|
||||
my $old_device = 0;
|
||||
my $blocks = 0;
|
||||
my $state;
|
||||
|
||||
foreach (@entries) {
|
||||
$_ =~ /memory(\d+)/;
|
||||
$block = $1;
|
||||
chmem_read_attr($state, $device, $block);
|
||||
if ($device != $old_device) {
|
||||
$devices->{$old_device}->{'id'} = $old_device;
|
||||
$devices->{$old_device}->{'blocks'} = $blocks;
|
||||
$old_device = $device;
|
||||
$blocks = 0;
|
||||
}
|
||||
if ($state eq "online") {
|
||||
$blocks++;
|
||||
}
|
||||
}
|
||||
$devices->{$old_device}->{'blocks'} = $blocks;
|
||||
$devices->{$old_device}->{'id'} = $old_device;
|
||||
}
|
||||
|
||||
sub chmem_dev_action($$)
|
||||
{
|
||||
my ($dev_id, $blocks) = @_;
|
||||
my ($start_block, $end_block, $tmp_block, $max_blocks);
|
||||
my $state;
|
||||
my $i = 0;
|
||||
my $count = 0;
|
||||
|
||||
if ($blocks_per_dev > 0) {
|
||||
$start_block = $dev_id * $blocks_per_dev;
|
||||
$end_block = $start_block + $blocks_per_dev - 1;
|
||||
$max_blocks = $blocks_per_dev;
|
||||
} else {
|
||||
$start_block = int($dev_id / $devs_per_block);
|
||||
$end_block = $start_block;
|
||||
$max_blocks = 1;
|
||||
}
|
||||
if ($blocks > $max_blocks) {
|
||||
$blocks = $max_blocks;
|
||||
}
|
||||
while ($count < $blocks && $i < $max_blocks) {
|
||||
$tmp_block = $online ? $start_block + $i : $end_block - $i;
|
||||
$state = `cat $memdir/memory$tmp_block/state`;
|
||||
chomp($state);
|
||||
if ($offline && $state eq "online") {
|
||||
$count++ unless chmem_offline($tmp_block);
|
||||
}
|
||||
if ($online && $state eq "offline") {
|
||||
$count++ unless chmem_online($tmp_block);
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
sub chmem_size($)
|
||||
{
|
||||
my $size = shift;
|
||||
my ($blocks, $dev_blocks, $dev_id);
|
||||
|
||||
$blocks = int($size / $block_size);
|
||||
if ($online) {
|
||||
foreach my $device (sort {$b->{'blocks'} <=> $a->{'blocks'} ||
|
||||
$a->{'id'} <=> $b->{'id'}}
|
||||
values %{$devices}) {
|
||||
$dev_blocks = $device->{'blocks'};
|
||||
$dev_id = $device->{'id'};
|
||||
if ($dev_blocks < $blocks_per_dev || $dev_blocks == 0) {
|
||||
$blocks -= chmem_dev_action($dev_id, $blocks);
|
||||
if ($blocks == 0) {
|
||||
last;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($blocks > 0) {
|
||||
printf(STDERR "chmem: Could only set %lu MB of memory ".
|
||||
"online.\n", $size - $blocks * $block_size);
|
||||
$ret = 1;
|
||||
}
|
||||
} else {
|
||||
foreach my $device (sort {$a->{'blocks'} <=> $b->{'blocks'} ||
|
||||
$b->{'id'} <=> $a->{'id'}}
|
||||
values %{$devices}) {
|
||||
$dev_blocks = $device->{'blocks'};
|
||||
$dev_id = $device->{'id'};
|
||||
if ($dev_blocks > 0) {
|
||||
$blocks -= chmem_dev_action($dev_id, $blocks);
|
||||
if ($blocks == 0) {
|
||||
last;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($blocks > 0) {
|
||||
printf(STDERR "chmem: Could only set %lu MB of memory ".
|
||||
"offline.\n", $size - $blocks * $block_size);
|
||||
$ret = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub chmem_range($$)
|
||||
{
|
||||
my ($start, $end) = @_;
|
||||
my $block = 0;
|
||||
my $state;
|
||||
|
||||
while ($start < $end && $block < $max_block_nr) {
|
||||
$block = int($start / ($block_size << 20));
|
||||
$state = `cat $memdir/memory$block/state`;
|
||||
chomp($state);
|
||||
if ($online && $state eq "offline") {
|
||||
if (chmem_online($block)) {
|
||||
printf(STDERR "chmem: Could not set ".
|
||||
"0x%016x-0x%016x online\n", $start,
|
||||
$start + ($block_size << 20) - 1);
|
||||
$ret = 1;
|
||||
}
|
||||
}
|
||||
if ($offline && $state eq "online") {
|
||||
if (chmem_offline($block)) {
|
||||
printf(STDERR "chmem: Could not set ".
|
||||
"0x%016x-0x%016x offline\n", $start,
|
||||
$start + ($block_size << 20) - 1);
|
||||
$ret = 1;
|
||||
}
|
||||
}
|
||||
$start += $block_size << 20;
|
||||
}
|
||||
}
|
||||
|
||||
sub chmem_check()
|
||||
{
|
||||
$block_size = `cat $memdir/block_size_bytes`;
|
||||
chomp($block_size);
|
||||
if ($block_size =~ /(?:0x)?([[:xdigit:]]+)/) {
|
||||
$block_size = unpack("Q", pack("H16",
|
||||
substr("0" x 16 . $1, -16)));
|
||||
$block_size = $block_size >> 20;
|
||||
} else {
|
||||
die "chmem: Unknown block size format in sysfs.\n";
|
||||
}
|
||||
if ($online == 0 && $offline == 0) {
|
||||
die "chmem: Please specify one of the options -e or -d.\n";
|
||||
}
|
||||
if ($online == 1 && $offline == 1) {
|
||||
die "chmem: You cannot specify both options -e and -d.\n";
|
||||
}
|
||||
|
||||
chmem_get_dev_size();
|
||||
if ($dev_size >= $block_size) {
|
||||
$blocks_per_dev = int($dev_size / $block_size);
|
||||
} else {
|
||||
$devs_per_block = int($block_size / $dev_size);
|
||||
}
|
||||
}
|
||||
|
||||
sub chmem_action()
|
||||
{
|
||||
my ($start, $end, $size, $unit);
|
||||
|
||||
if (!defined($ARGV[0])) {
|
||||
die "chmem: Missing size or range.\n";
|
||||
}
|
||||
if ($ARGV[0] =~ /^0x([[:xdigit:]]+)-0x([[:xdigit:]]+)$/) {
|
||||
$start = unpack("Q", pack("H16", substr("0" x 16 . $1, -16)));
|
||||
$end = unpack("Q", pack("H16", substr("0" x 16 . $2, -16)));
|
||||
if ($start % ($block_size << 20) ||
|
||||
($end + 1) % ($block_size << 20)) {
|
||||
die "chmem: Start address and (end address + 1) must ".
|
||||
"be aligned to memory block size ($block_size MB).\n";
|
||||
}
|
||||
chmem_range($start, $end);
|
||||
} else {
|
||||
if ($ARGV[0] =~ m/^(\d+)([mg]?)$/i) {
|
||||
$size = $1;
|
||||
$unit = $2 || "";
|
||||
if ($unit =~ /g/i) {
|
||||
$size = $size << 10;
|
||||
}
|
||||
if ($size % $block_size) {
|
||||
die "chmem: Size must be aligned to memory ".
|
||||
"block size ($block_size MB).\n";
|
||||
}
|
||||
chmem_size($size);
|
||||
} else {
|
||||
printf(STDERR "chmem: Invalid size or range: %s\n",
|
||||
$ARGV[0]);
|
||||
exit 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Main
|
||||
unless (GetOptions('v|version' => sub {chmem_version(); exit 0;},
|
||||
'h|help' => sub {chmem_usage(); exit 0;},
|
||||
'e|enable' => \$online,
|
||||
'd|disable' => \$offline)) {
|
||||
die "Try '$script_name --help' for more information.\n";
|
||||
};
|
||||
|
||||
@entries = (sort {length($a) <=> length($b) || $a cmp $b} <$memdir/memory*>);
|
||||
if (@entries == 0) {
|
||||
die "chmem: No memory hotplug interface in sysfs ($memdir).\n";
|
||||
}
|
||||
$entries[-1] =~ /memory(\d+)/;
|
||||
$max_block_nr = $1;
|
||||
|
||||
chmem_read_devices();
|
||||
chmem_check();
|
||||
chmem_action();
|
||||
exit $ret;
|
||||
@@ -0,0 +1,75 @@
|
||||
.\" Copyright 2017 IBM Corp.
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
.TH CHMEM 8 "Apr 2010" "s390-tools"
|
||||
.
|
||||
.
|
||||
.SH NAME
|
||||
chmem \- set memory online or offline.
|
||||
.
|
||||
.SH SYNOPSIS
|
||||
.B chmem
|
||||
.RB OPTIONS
|
||||
.RB [SIZE|RANGE]
|
||||
.
|
||||
.
|
||||
.SH DESCRIPTION
|
||||
The chmem command sets a particular size or range of memory online or offline.
|
||||
.
|
||||
.IP "\(hy" 2
|
||||
Specify SIZE as <size>[m|M|g|G]. With m or M, <size> specifies the memory
|
||||
size in MB (1024 x 1024 bytes). With g or G, <size> specifies the memory size
|
||||
in GB (1024 x 1024 x 1024 bytes). The default unit is MB.
|
||||
.
|
||||
.IP "\(hy" 2
|
||||
Specify RANGE in the form 0x<start>-0x<end> as shown in the output of the
|
||||
lsmem command. <start> is the hexadecimal address of the first byte and <end>
|
||||
is the hexadecimal address of the last byte in the memory range.
|
||||
.
|
||||
.PP
|
||||
SIZE and RANGE must be aligned to the Linux memory block size, as shown in
|
||||
the output of the lsmem command.
|
||||
|
||||
Setting memory online can fail if the hypervisor does not have enough memory
|
||||
left, for example because memory was overcommitted. Setting memory offline can
|
||||
fail if Linux cannot free the memory. If only part of the requested memory can
|
||||
be set online or offline, a message tells you how much memory was set online
|
||||
or offline instead of the requested amount.
|
||||
.
|
||||
.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.BR \-h ", " \-\-help
|
||||
Print a short help text, then exit.
|
||||
.
|
||||
.TP
|
||||
.BR \-v ", " \-\-version
|
||||
Print the version number, then exit.
|
||||
.
|
||||
.TP
|
||||
.BR \-e ", " \-\-enable
|
||||
Set the given RANGE or SIZE of memory online.
|
||||
.
|
||||
.TP
|
||||
.BR \-d ", " \-\-disable
|
||||
Set the given RANGE or SIZE of memory offline.
|
||||
.
|
||||
.
|
||||
.SH EXAMPLES
|
||||
.TP
|
||||
.B chmem --enable 1024
|
||||
This command requests 1024 MB of memory to be set online.
|
||||
.
|
||||
.TP
|
||||
.B chmem -e 2g
|
||||
This command requests 2 GB of memory to be set online.
|
||||
.
|
||||
.TP
|
||||
.B chmem --disable 0x00000000e4000000-0x00000000f3ffffff
|
||||
This command requests the memory range starting with 0x00000000e4000000
|
||||
and ending with 0x00000000f3ffffff to be set offline.
|
||||
.
|
||||
.
|
||||
.SH SEE ALSO
|
||||
.BR lsmem (8)
|
||||
@@ -0,0 +1,21 @@
|
||||
include ../../common.mak
|
||||
|
||||
all: chchp lschp
|
||||
|
||||
libs = $(rootdir)/libutil/libutil.a
|
||||
|
||||
chchp: chchp.o $(libs)
|
||||
lschp: lschp.o $(libs)
|
||||
|
||||
install: all
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 chchp $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 lschp $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man8
|
||||
$(INSTALL) -m 644 -c chchp.8 $(DESTDIR)$(MANDIR)/man8
|
||||
$(INSTALL) -m 644 -c lschp.8 $(DESTDIR)$(MANDIR)/man8
|
||||
|
||||
clean:
|
||||
rm -f *.o chchp lschp
|
||||
|
||||
.PHONY: all install clean
|
||||
@@ -0,0 +1,142 @@
|
||||
.\" Copyright 2017 IBM Corp.
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
.TH CHCHP 8 "Mar 2007" s390\-tools
|
||||
|
||||
.SH NAME
|
||||
chchp \- modify channel\-path state.
|
||||
|
||||
.SH SYNOPSIS
|
||||
.B chchp
|
||||
.RB [ \-v|\-\-vary
|
||||
.IR value ]
|
||||
.RB [ \-c|\-\-configure
|
||||
.IR value ]
|
||||
.RS 6
|
||||
.br
|
||||
.RB [ \-a|\-\-attribute
|
||||
.IR key = value ]
|
||||
.I chpid
|
||||
.br
|
||||
.RB [ \-h|\-\-help ]
|
||||
.RB [ \-\-version ]
|
||||
|
||||
.SH DESCRIPTION
|
||||
The chchp command modifies the state of one or more channel\-paths.
|
||||
Channel\-path identifiers are specified in hexadecimal notation either simply
|
||||
as the CHPID\-number (e.g. e0) or in the form
|
||||
|
||||
.RS
|
||||
<cssid>.<id>
|
||||
.RE
|
||||
|
||||
where <cssid> is the channel\-subsystem identifier and <id> is the CHPID\-number (e.g. 0.7e).
|
||||
|
||||
An operation can be performed on more than one channel\-path by specifying
|
||||
multiple identifiers as a blank or comma\-separated list or a range or a
|
||||
combination of both (see EXAMPLES section).
|
||||
|
||||
Note that modifying the state of channel\-paths can affect the availability
|
||||
of I/O devices as well as trigger associated functions (e.g. channel\-path
|
||||
verification or device scanning) which in turn can result in a temporary
|
||||
increase in processor, memory and I/O load.
|
||||
.SH OPTIONS
|
||||
.BI "\-v " value
|
||||
.br
|
||||
.BI "\-\-vary " value
|
||||
.RS
|
||||
Change the logical channel\-path state to
|
||||
.IR value .
|
||||
The logical channel\-path state determines whether Linux will be actively
|
||||
using a channel\-path for I/O.
|
||||
.br
|
||||
|
||||
.RI "A " value
|
||||
of "0" specifies the logical offline state. A value of "1" specifies the logical
|
||||
online state.
|
||||
.br
|
||||
|
||||
Note that setting the logical state to offline may cause a currently running
|
||||
I/O operation to be aborted.
|
||||
.RE
|
||||
|
||||
.BI "\-c " value
|
||||
.br
|
||||
.BI "\-\-configure " value
|
||||
.RS
|
||||
Change the channel\-path configuration state to
|
||||
.IR value .
|
||||
.br
|
||||
|
||||
.RI "A " value
|
||||
of "0" specifies standby state. A value of "1" specifies configured state.
|
||||
.br
|
||||
|
||||
Note that setting the configured state to standby may cause a currently running
|
||||
I/O operation to be aborted.
|
||||
.RE
|
||||
|
||||
.B "\-a "
|
||||
.IR key = value
|
||||
.br
|
||||
.B \-\-attribute
|
||||
.IR key = value
|
||||
.RS
|
||||
Change the channel\-path sysfs attribute
|
||||
.IR key " to " value .
|
||||
.br
|
||||
|
||||
.I key
|
||||
can be the name of any available channel-path sysfs attribute (e.g. "configure"
|
||||
or "status"), while
|
||||
.I value
|
||||
can take any valid value that may be written to the attribute (e.g. "0"
|
||||
or "offline").
|
||||
.br
|
||||
|
||||
This is a more generic way of modifying the state of a channel-path via
|
||||
the sysfs interface. It is intended for cases where sysfs attributes
|
||||
or attribute values are available in the kernel but not in chchp.
|
||||
.RE
|
||||
|
||||
|
||||
.B \-h
|
||||
.br
|
||||
.B \-\-help
|
||||
.RS
|
||||
Print a short help text, then exit.
|
||||
.RE
|
||||
|
||||
.B \-V
|
||||
.br
|
||||
.B \-\-version
|
||||
.RS
|
||||
Print version number, then exit.
|
||||
.RE
|
||||
|
||||
.SH EXAMPLES
|
||||
|
||||
.B chchp \-c 0 19
|
||||
.RS
|
||||
Put channel\-path 0.19 into standby state.
|
||||
.RE
|
||||
|
||||
.B chchp \-a configure=0 19
|
||||
.RS
|
||||
Write value "0" into sysfs attribute "configure" of channel-path 0.19. The
|
||||
result is the same as when using the command chchp \-c 0 19
|
||||
.RE
|
||||
|
||||
.B chchp \-c 1 0.65\-0.6f
|
||||
.RS
|
||||
Put channel\-paths 0.65 to 0.6f into configured state.
|
||||
.RE
|
||||
|
||||
.B chchp \-v 0 0.12,0.7f,0.17\-0.20
|
||||
.RS
|
||||
Put channel\-paths 0.12, 0.7f and 0.17 to 0.20 into logical offline state.
|
||||
.RE
|
||||
|
||||
.SH SEE ALSO
|
||||
.BR lschp (8)
|
||||
@@ -0,0 +1,442 @@
|
||||
/*
|
||||
* chchp - Tool to modify channel-path state
|
||||
*
|
||||
* Provide main function and command line parsing.
|
||||
*
|
||||
* Copyright IBM Corp. 2016, 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.
|
||||
*/
|
||||
|
||||
#include <err.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "lib/util_base.h"
|
||||
#include "lib/util_file.h"
|
||||
#include "lib/util_libc.h"
|
||||
#include "lib/util_opt.h"
|
||||
#include "lib/util_panic.h"
|
||||
#include "lib/util_path.h"
|
||||
#include "lib/util_prg.h"
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
#define CIO_SETTLE "/proc/cio_settle"
|
||||
#define MAX_CHPID_CSS 255
|
||||
#define MAX_CHPID_ID 255
|
||||
|
||||
/*
|
||||
* Private data
|
||||
*/
|
||||
struct chchp_l {
|
||||
struct {
|
||||
enum cmd_code {
|
||||
CMD_NONE,
|
||||
CMD_ATTRIBUTE,
|
||||
CMD_VARY,
|
||||
CMD_CONFIGURE,
|
||||
} code;
|
||||
const char *value;
|
||||
} cmd;
|
||||
} l;
|
||||
|
||||
struct chchp_l *chchp_l = &l;
|
||||
|
||||
/*
|
||||
* Program configuration
|
||||
*/
|
||||
const struct util_prg prg = {
|
||||
.desc =
|
||||
"Modify the state of channel-path CHPID. CHPID can be a single, hexadecimal\n"
|
||||
"channel-path identifier, a comma-separated list or a range of identifiers.",
|
||||
.copyright_vec = {
|
||||
{
|
||||
.owner = "IBM Corp.",
|
||||
.pub_first = 2016,
|
||||
.pub_last = 2017,
|
||||
},
|
||||
UTIL_PRG_COPYRIGHT_END
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Configuration of command line options
|
||||
*/
|
||||
static struct util_opt opt_vec[] = {
|
||||
{
|
||||
.option = { "vary", required_argument, NULL, 'v'},
|
||||
.argument = "VALUE",
|
||||
.desc = "Logically vary channel-path to VALUE (1=on, 0=off)",
|
||||
},
|
||||
{
|
||||
.option = { "configure", required_argument, NULL, 'c'},
|
||||
.argument = "VALUE",
|
||||
.desc = "Configure channel-path to VALUE (1=on, 0=standby)",
|
||||
},
|
||||
{
|
||||
.option = { "attribute", required_argument, NULL, 'a'},
|
||||
.argument = "KEY=VALUE",
|
||||
.desc = "Set channel-path attribute KEY to VALUE",
|
||||
},
|
||||
UTIL_OPT_HELP,
|
||||
{
|
||||
.option = { "version", 0, NULL, 'V'},
|
||||
.desc = "Print version information, then exit",
|
||||
},
|
||||
UTIL_OPT_END
|
||||
};
|
||||
|
||||
/*
|
||||
* Write and check attribute value
|
||||
*/
|
||||
static void write_value(const char *dir, const char *key, const char *val)
|
||||
{
|
||||
char val2[256];
|
||||
|
||||
if (!util_path_is_reg_file("%s/%s", dir, key)) {
|
||||
printf("failed - no such attribute\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (!util_path_is_writable("%s/%s", dir, key)) {
|
||||
printf("failed - attribute not writable\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (util_file_write_s(val, "%s/%s", dir, key)) {
|
||||
printf("failed - write failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (util_file_read_line(val2, sizeof(val2), "%s/%s", dir, key)) {
|
||||
printf("failed - could not determine new attribute value\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
/*
|
||||
* Skip value comparison for 'status' attribute because input
|
||||
* can be specified in different ways.
|
||||
*/
|
||||
if (strcmp(key, "status") != 0) {
|
||||
if (strcmp(val, val2) != 0) {
|
||||
printf("failed - attribute value not as expected\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
printf("done.\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure channel-path
|
||||
*/
|
||||
static void configure(int css, int num, const char *dir, const char *val)
|
||||
{
|
||||
const char *op;
|
||||
|
||||
if (strcmp(val, "0") == 0)
|
||||
op = "standby";
|
||||
else
|
||||
op = "online";
|
||||
|
||||
printf("Configure %s %x.%02x... ", op, css, num);
|
||||
write_value(dir, "configure", val);
|
||||
}
|
||||
|
||||
/*
|
||||
* Vary channel-path
|
||||
*/
|
||||
static void vary(int css, int num, const char *dir, const char *val)
|
||||
{
|
||||
const char *op;
|
||||
|
||||
if (strcmp(val, "0") == 0)
|
||||
op = "offline";
|
||||
else
|
||||
op = "online";
|
||||
|
||||
printf("Vary %s %x.%02x... ", op, css, num);
|
||||
write_value(dir, "status", val);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get key and value from "KEY=VALUE"
|
||||
*/
|
||||
static int get_key_value(char **key, char **val, const char *key_val)
|
||||
{
|
||||
char *ptr;
|
||||
|
||||
if (!strchr(key_val, '='))
|
||||
return -1;
|
||||
*key = util_strdup(key_val);
|
||||
ptr = strchr(*key, '=');
|
||||
*ptr = '\0';
|
||||
ptr += 1;
|
||||
*val = util_strdup(ptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Modify channel-path attribute
|
||||
*/
|
||||
static void attribute(int css, int num, const char *dir, const char *key_val)
|
||||
{
|
||||
char *key, *val;
|
||||
|
||||
if (get_key_value(&key, &val, key_val))
|
||||
return;
|
||||
printf("Attribute %s=%s %x.%02x... ", key, val, css, num);
|
||||
write_value(dir, key, val);
|
||||
free(key);
|
||||
free(val);
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure only one command is specified and argument was specified correctly
|
||||
*/
|
||||
static void check_and_set_command(enum cmd_code code, const char *value)
|
||||
{
|
||||
char *key, *val;
|
||||
int rc;
|
||||
|
||||
if (l.cmd.code != CMD_NONE) {
|
||||
errx(EXIT_FAILURE, "Only one of --vary, --configure or "
|
||||
"--attribute allowed");
|
||||
}
|
||||
switch (code) {
|
||||
case CMD_ATTRIBUTE:
|
||||
rc = get_key_value(&key, &val, value);
|
||||
if (rc || strlen(key) == 0 || strlen(val) == 0)
|
||||
errx(EXIT_FAILURE, "--attribute requires an argument");
|
||||
free(key);
|
||||
free(val);
|
||||
break;
|
||||
case CMD_VARY:
|
||||
if (strcmp(value, "0") == 0 || strcmp(value, "1") == 0)
|
||||
break;
|
||||
errx(EXIT_FAILURE, "Invalid value for --vary (only 0 or 1 "
|
||||
"allowed)");
|
||||
case CMD_CONFIGURE:
|
||||
if (strcmp(value, "0") == 0 || strcmp(value, "1") == 0)
|
||||
break;
|
||||
errx(EXIT_FAILURE, "Invalid value for --configure (only 0 or 1 "
|
||||
"allowed)");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
l.cmd.code = code;
|
||||
l.cmd.value = value;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get channel-path directory
|
||||
*/
|
||||
static char *get_chp_dir(int css, int id)
|
||||
{
|
||||
struct stat sb;
|
||||
char *path;
|
||||
|
||||
path = util_path_sysfs("devices/css%x/chp%x.%x", css, css, id);
|
||||
if ((stat(path, &sb) == 0) && (sb.st_mode == S_IFDIR))
|
||||
return path;
|
||||
free(path);
|
||||
return util_path_sysfs("devices/css%x/chp%x.%02x", css, css, id);
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract css id from channel-path id string
|
||||
*/
|
||||
static int get_chpid_css(const char *chpid)
|
||||
{
|
||||
int id, css_id;
|
||||
|
||||
if (strchr(chpid, '.') == NULL) {
|
||||
css_id = 0;
|
||||
} else {
|
||||
if (sscanf(chpid, "%x.%x", &css_id, &id) != 2) {
|
||||
errx(EXIT_FAILURE, "Invalid channel-path identifier "
|
||||
"'%s'", chpid);
|
||||
}
|
||||
if (css_id < 0 || css_id > MAX_CHPID_CSS) {
|
||||
errx(EXIT_FAILURE, "Invalid channel-path identifier "
|
||||
"'%s'", chpid);
|
||||
}
|
||||
}
|
||||
return css_id;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract id from channel-path id string
|
||||
*/
|
||||
static int get_chpid_id(const char *chpid)
|
||||
{
|
||||
int id, css_id;
|
||||
|
||||
if (strchr(chpid, '.') == NULL) {
|
||||
if (sscanf(chpid, "%x", &id) != 1) {
|
||||
errx(EXIT_FAILURE, "Invalid channel-path identifier "
|
||||
"'%s'", chpid);
|
||||
}
|
||||
} else if (sscanf(chpid, "%x.%x", &css_id, &id) != 2) {
|
||||
errx(EXIT_FAILURE, "Invalid channel-path identifier '%s'",
|
||||
chpid);
|
||||
}
|
||||
if (id < 0 || id > MAX_CHPID_ID) {
|
||||
errx(EXIT_FAILURE, "Invalid channel-path identifier '%s'",
|
||||
chpid);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform command specified by COMMAND and VALUE
|
||||
*/
|
||||
static void perform_command(int css, int id)
|
||||
{
|
||||
struct stat sb;
|
||||
char *path;
|
||||
|
||||
path = get_chp_dir(css, id);
|
||||
if ((stat(path, &sb) != 0) || ((sb.st_mode & S_IFMT) != S_IFDIR)) {
|
||||
printf("Skipping unknown channel-path %x.%02x\n", css, id);
|
||||
goto out_free_path;
|
||||
}
|
||||
switch (l.cmd.code) {
|
||||
case CMD_VARY:
|
||||
vary(css, id, path, l.cmd.value);
|
||||
break;
|
||||
case CMD_CONFIGURE:
|
||||
configure(css, id, path, l.cmd.value);
|
||||
break;
|
||||
case CMD_ATTRIBUTE:
|
||||
attribute(css, id, path, l.cmd.value);
|
||||
break;
|
||||
default:
|
||||
util_panic("Invalid cmd: %d\n", l.cmd.code);
|
||||
}
|
||||
out_free_path:
|
||||
free(path);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate iterator steps for chpid loop
|
||||
*/
|
||||
static int get_iterator_step(int css1, int id1, int css2, int id2)
|
||||
{
|
||||
if (css1 == css2) {
|
||||
if (id1 < id2)
|
||||
return 1;
|
||||
else
|
||||
return -1;
|
||||
} else if (css1 < css2) {
|
||||
return 1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute command on all chpids: from - to
|
||||
*/
|
||||
static void loop_chpids(int css1, int id1, int css2, int id2)
|
||||
{
|
||||
int step = get_iterator_step(css1, id1, css2, id2);
|
||||
|
||||
while (1) {
|
||||
/* Perform function */
|
||||
perform_command(css1, id1);
|
||||
/* Check for loop end */
|
||||
if ((css1 == css2) && (id1 == id2))
|
||||
break;
|
||||
/* Advance iterator */
|
||||
id1 = id1 + step;
|
||||
if (id1 < 0) {
|
||||
css1 -= 1;
|
||||
id1 = 255;
|
||||
} else if (id2 > 255) {
|
||||
css1 += 1;
|
||||
id1 = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse options and execute the command
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char *chpid_from, *chpid_to, *chpid_list = NULL;
|
||||
int from_css, from_id, to_css, to_id;
|
||||
struct stat sb;
|
||||
int c, i;
|
||||
|
||||
util_prg_init(&prg);
|
||||
util_opt_init(opt_vec, NULL);
|
||||
while (1) {
|
||||
c = util_opt_getopt_long(argc, argv);
|
||||
if (c == -1)
|
||||
break;
|
||||
switch (c) {
|
||||
case 'h':
|
||||
util_prg_print_help();
|
||||
util_opt_print_help();
|
||||
return EXIT_SUCCESS;
|
||||
case 'V':
|
||||
util_prg_print_version();
|
||||
return EXIT_SUCCESS;
|
||||
case 'v':
|
||||
check_and_set_command(CMD_VARY, optarg);
|
||||
break;
|
||||
case 'c':
|
||||
check_and_set_command(CMD_CONFIGURE, optarg);
|
||||
break;
|
||||
case 'a':
|
||||
check_and_set_command(CMD_ATTRIBUTE, optarg);
|
||||
break;
|
||||
default:
|
||||
util_opt_print_parse_error(c, argv);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
if (l.cmd.code == CMD_NONE) {
|
||||
errx(EXIT_FAILURE, "One of --vary, --configure or --attribute "
|
||||
"required");
|
||||
}
|
||||
if (optind == argc) {
|
||||
errx(EXIT_FAILURE, "Need to specify at least one channel-path "
|
||||
"ID");
|
||||
}
|
||||
|
||||
/* Append each argument with comma to distinguish blank-separators */
|
||||
for (i = optind; i < argc; i++) {
|
||||
if (i > optind)
|
||||
chpid_list = util_strcat_realloc(chpid_list, ",");
|
||||
chpid_list = util_strcat_realloc(chpid_list, argv[i]);
|
||||
}
|
||||
|
||||
/* Loop over comma-separated list */
|
||||
chpid_from = strtok(chpid_list, ",");
|
||||
|
||||
while (chpid_from != NULL) {
|
||||
chpid_to = strchr(chpid_from, '-');
|
||||
if (chpid_to == NULL)
|
||||
chpid_to = chpid_from;
|
||||
else
|
||||
*chpid_to++ = '\0';
|
||||
if (*chpid_to == '\0') {
|
||||
errx(EXIT_FAILURE, "Invalid channel-path identifier "
|
||||
"range %s", chpid_from);
|
||||
}
|
||||
from_css = get_chpid_css(chpid_from);
|
||||
from_id = get_chpid_id(chpid_from);
|
||||
to_css = get_chpid_css(chpid_to);
|
||||
to_id = get_chpid_id(chpid_to);
|
||||
loop_chpids(from_css, from_id, to_css, to_id);
|
||||
chpid_from = strtok(NULL, ",");
|
||||
}
|
||||
|
||||
/* Do CIO settle */
|
||||
if (stat(CIO_SETTLE, &sb) != 0)
|
||||
util_file_write_s("1", CIO_SETTLE);
|
||||
free(chpid_list);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
.\" Copyright 2017 IBM Corp.
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
.TH LSCHP 8 "Mar 2007" s390\-tools
|
||||
|
||||
.SH NAME
|
||||
lschp \- list information about available channel\-paths.
|
||||
|
||||
.SH SYNOPSIS
|
||||
.B lschp
|
||||
.RB [ \-h|\-\-help ]
|
||||
.RB [ \-v|\-\-version ]
|
||||
|
||||
.SH DESCRIPTION
|
||||
The lschp command lists status and type information about available
|
||||
channel\-paths.
|
||||
|
||||
.B Column description:
|
||||
|
||||
CHPID
|
||||
.RS
|
||||
Channel\-path identifier.
|
||||
.RE
|
||||
|
||||
Vary
|
||||
.RS
|
||||
Logical channel\-path state:
|
||||
.br
|
||||
0 = channel\-path is not used for I/O
|
||||
.br
|
||||
1 = channel\-path is used for I/O
|
||||
.RE
|
||||
|
||||
Cfg.
|
||||
.RS
|
||||
Channel\-path configure state:
|
||||
.br
|
||||
0 = stand\-by
|
||||
.br
|
||||
1 = configured
|
||||
.br
|
||||
2 = reserved
|
||||
.br
|
||||
3 = not recognized
|
||||
.RE
|
||||
|
||||
Type
|
||||
.RS
|
||||
Channel\-path type identifier.
|
||||
.RE
|
||||
|
||||
Cmg
|
||||
.RS
|
||||
Channel measurement group identifier.
|
||||
.RE
|
||||
|
||||
Shared
|
||||
.RS
|
||||
Indicates whether a channel\-path is shared between LPARs:
|
||||
.br
|
||||
0 = channel\-path is not shared
|
||||
.br
|
||||
1 = channel\-path is shared
|
||||
.RE
|
||||
|
||||
PCHID
|
||||
.RS
|
||||
Physical channel-ID unless the 4-digit hexadecimal value is enclosed in
|
||||
parenthesis.
|
||||
|
||||
If the value is enclosed in parenthesis, no physical channel-ID is
|
||||
associated with the CHPID, and the value is an internal channel-ID.
|
||||
|
||||
For example, 0501 specifies a PCHID whereas (0502) specifies an internal
|
||||
channel-ID.
|
||||
.RE
|
||||
|
||||
A column value of '\-' indicates that a facility associated with the respective
|
||||
channel\-path attribute is not available.
|
||||
|
||||
.SH OPTIONS
|
||||
.B \-h
|
||||
.br
|
||||
.B \-\-help
|
||||
.RS
|
||||
Print a short help text, then exit.
|
||||
.RE
|
||||
|
||||
.B \-v
|
||||
.br
|
||||
.B \-\-version
|
||||
.RS
|
||||
Print version number, then exit.
|
||||
.RE
|
||||
|
||||
.SH SEE ALSO
|
||||
.BR chchp (8)
|
||||
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* lschp - List information about available channel-paths
|
||||
*
|
||||
* Provide main function and command line parsing.
|
||||
*
|
||||
* Copyright IBM Corp. 2016, 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.
|
||||
*/
|
||||
|
||||
#include <err.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "lib/util_base.h"
|
||||
#include "lib/util_file.h"
|
||||
#include "lib/util_opt.h"
|
||||
#include "lib/util_path.h"
|
||||
#include "lib/util_prg.h"
|
||||
#include "lib/util_rec.h"
|
||||
#include "lib/util_scandir.h"
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
/*
|
||||
* Program configuration
|
||||
*/
|
||||
static const struct util_prg prg = {
|
||||
.desc = "List information about available channel-paths.",
|
||||
.copyright_vec = {
|
||||
{
|
||||
.owner = "IBM Corp.",
|
||||
.pub_first = 2016,
|
||||
.pub_last = 2017,
|
||||
},
|
||||
UTIL_PRG_COPYRIGHT_END
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Configuration of command line options
|
||||
*/
|
||||
static struct util_opt opt_vec[] = {
|
||||
UTIL_OPT_HELP,
|
||||
UTIL_OPT_VERSION,
|
||||
UTIL_OPT_END
|
||||
};
|
||||
|
||||
/**
|
||||
* Read all attributes of a desired directory
|
||||
*
|
||||
* Which attributes are to be investigated is defined in the
|
||||
* fallowing function body.
|
||||
*
|
||||
* @param[in] dir Path of the desired directory
|
||||
* @param[in] css_id ID for device identification
|
||||
* @param[in] rec The buffer structure, where results are written to
|
||||
*/
|
||||
static void print_chpid(const char *chp_dir, unsigned int css_id,
|
||||
struct util_rec *rec)
|
||||
{
|
||||
unsigned int css_id_tmp, chp_id;
|
||||
bool chid_external;
|
||||
char *path, buf[8];
|
||||
|
||||
/* Get CHPID ID */
|
||||
if (sscanf(chp_dir, "chp%x.%x", &css_id_tmp, &chp_id) != 2)
|
||||
err(EXIT_FAILURE, "Invalid directory: %s", chp_dir);
|
||||
if (css_id != css_id_tmp)
|
||||
errx(EXIT_FAILURE, "Inconsistent css ids");
|
||||
path = util_path_sysfs("devices/css%x/%s", css_id, chp_dir);
|
||||
|
||||
/* chpid */
|
||||
util_rec_set(rec, "chpid", "%x.%02x", css_id, chp_id);
|
||||
|
||||
/* vary */
|
||||
util_file_read_line(buf, sizeof(buf), "%s/status", path);
|
||||
if (strcmp(buf, "online") == 0)
|
||||
util_rec_set(rec, "vary", "1");
|
||||
else if (strcmp(buf, "offline") == 0)
|
||||
util_rec_set(rec, "vary", "0");
|
||||
else
|
||||
util_rec_set(rec, "vary", "-");
|
||||
|
||||
/* configure */
|
||||
if (util_file_read_line(buf, sizeof(buf), "%s/configure", path))
|
||||
util_rec_set(rec, "cfg", "-");
|
||||
else
|
||||
util_rec_set(rec, "cfg", buf);
|
||||
|
||||
/* type */
|
||||
if (util_file_read_line(buf, sizeof(buf), "%s/type", path))
|
||||
util_rec_set(rec, "type", "%s", "-");
|
||||
else
|
||||
util_rec_set(rec, "type", "%02lx", strtoul(buf, NULL, 16));
|
||||
|
||||
/* cmg */
|
||||
util_file_read_line(buf, sizeof(buf), "%s/cmg", path);
|
||||
if ((strcmp(buf, "unknown") == 0) || (strlen(buf) == 0))
|
||||
util_rec_set(rec, "cmg", "%-3s", "-");
|
||||
else
|
||||
util_rec_set(rec, "cmg", "%-3lx", strtoul(buf, NULL, 0));
|
||||
|
||||
/* shared */
|
||||
util_file_read_line(buf, sizeof(buf), "%s/shared", path);
|
||||
if ((strcmp(buf, "unknown") == 0) || (strlen(buf) == 0))
|
||||
util_rec_set(rec, "shared", "%s", "-");
|
||||
else
|
||||
util_rec_set(rec, "shared", "%-6lx", strtoul(buf, NULL, 0));
|
||||
|
||||
/* chid */
|
||||
util_file_read_line(buf, sizeof(buf), "%s/chid_external", path);
|
||||
if (strcmp(buf, "1") == 0)
|
||||
chid_external = true;
|
||||
else
|
||||
chid_external = false;
|
||||
util_file_read_line(buf, sizeof(buf), "%s/chid", path);
|
||||
if (strlen(buf) != 0) {
|
||||
if (chid_external)
|
||||
util_rec_set(rec, "pchid", " %4s ", buf);
|
||||
else
|
||||
util_rec_set(rec, "pchid", "(%4s)", buf);
|
||||
} else {
|
||||
util_rec_set(rec, "pchid", "%s", "-");
|
||||
}
|
||||
util_rec_print(rec);
|
||||
free(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two dirents numerically (chp0.ff <-> chp0.fe)
|
||||
*
|
||||
* @param[in] de1 First directory entry
|
||||
* @param[in] de2 Second directory entry
|
||||
* @retval -1 de1 is lower
|
||||
* 0 de1 equals de2
|
||||
* 1 de2 is lower
|
||||
*/
|
||||
static int chpsort(const struct dirent **de1, const struct dirent **de2)
|
||||
{
|
||||
unsigned long val1 = strtoul(&(*de1)->d_name[5], NULL, 16);
|
||||
unsigned long val2 = strtoul(&(*de2)->d_name[5], NULL, 16);
|
||||
|
||||
if (val1 < val2)
|
||||
return -1;
|
||||
if (val1 == val2)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show all channel paths for a given directory in sysfs
|
||||
*
|
||||
* @param[in] css_dir The desired directory
|
||||
* @param[in] rec The buffer structure, where results are written to
|
||||
*/
|
||||
static void print_css(const char *css_dir, struct util_rec *rec)
|
||||
{
|
||||
struct dirent **de_vec;
|
||||
unsigned int css_id;
|
||||
int i, count;
|
||||
char *path;
|
||||
|
||||
if (sscanf(css_dir, "css%x", &css_id) != 1)
|
||||
err(EXIT_FAILURE, "Invalid directory: %s", css_dir);
|
||||
|
||||
path = util_path_sysfs("devices/css%d", css_id);
|
||||
count = util_scandir(&de_vec, chpsort, path, "chp%x.*", css_id);
|
||||
for (i = 0; i < count; i++)
|
||||
print_chpid(de_vec[i]->d_name, css_id, rec);
|
||||
util_scandir_free(de_vec, count);
|
||||
free(path);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print chpid table
|
||||
*/
|
||||
static void cmd_lschp(void)
|
||||
{
|
||||
struct dirent **de_vec;
|
||||
struct util_rec *rec;
|
||||
int i, count;
|
||||
char *path;
|
||||
|
||||
rec = util_rec_new_wide("=");
|
||||
util_rec_def(rec, "chpid", UTIL_REC_ALIGN_LEFT, 6, "CHPID");
|
||||
util_rec_def(rec, "vary", UTIL_REC_ALIGN_LEFT, 5, "Vary");
|
||||
util_rec_def(rec, "cfg", UTIL_REC_ALIGN_LEFT, 5, "Cfg.");
|
||||
util_rec_def(rec, "type", UTIL_REC_ALIGN_LEFT, 5, "Type");
|
||||
util_rec_def(rec, "cmg", UTIL_REC_ALIGN_LEFT, 4, "Cmg");
|
||||
util_rec_def(rec, "shared", UTIL_REC_ALIGN_LEFT, 6, "Shared");
|
||||
util_rec_def(rec, "pchid", UTIL_REC_ALIGN_LEFT, 6, " PCHID");
|
||||
|
||||
util_rec_print_hdr(rec);
|
||||
/*
|
||||
* Iterate over each "/sys/devices/css.*"
|
||||
*/
|
||||
path = util_path_sysfs("devices");
|
||||
count = util_scandir(&de_vec, alphasort, path, "^css[[:xdigit:]]{1,2}$");
|
||||
for (i = 0; i < count; i++)
|
||||
print_css(de_vec[i]->d_name, rec);
|
||||
util_ptr_vec_free((void **) de_vec, count);
|
||||
free(path);
|
||||
util_rec_free(rec);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse options and execute the command
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int c;
|
||||
|
||||
util_prg_init(&prg);
|
||||
util_opt_init(opt_vec, NULL);
|
||||
|
||||
while (1) {
|
||||
c = util_opt_getopt_long(argc, argv);
|
||||
if (c == -1)
|
||||
break;
|
||||
switch (c) {
|
||||
case 'h':
|
||||
util_prg_print_help();
|
||||
util_opt_print_help();
|
||||
return EXIT_SUCCESS;
|
||||
case 'v':
|
||||
util_prg_print_version();
|
||||
return EXIT_SUCCESS;
|
||||
default:
|
||||
util_opt_print_parse_error(c, argv);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
if (argc > optind) {
|
||||
util_prg_print_arg_error(argv[optind]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
cmd_lschp();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
Executable
+793
@@ -0,0 +1,793 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# cio_ignore - Tool to query and modify the cio blacklist
|
||||
#
|
||||
# Copyright IBM Corp. 2009, 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.
|
||||
#
|
||||
|
||||
VERSION="%S390_TOOLS_VERSION%"
|
||||
BLACKLIST="/proc/cio_ignore"
|
||||
CIO_SETTLE="/proc/cio_settle"
|
||||
WAIT_FOR_CIO=0
|
||||
SYSINFO="/proc/sysinfo"
|
||||
CONSDRV="/sys/bus/ccw/drivers/3215"
|
||||
MAXCSSID=0
|
||||
MAXSSID=3
|
||||
MAXDEVNO=65535
|
||||
LSCSS="lscss"
|
||||
TOOLNAME="${0##*/}"
|
||||
|
||||
# print_help()
|
||||
# Print help text.
|
||||
function print_help()
|
||||
{
|
||||
cat << EOF
|
||||
Usage: ${TOOLNAME} COMMANDS
|
||||
|
||||
Query or modify the CIO device driver blacklist. This blacklist determines if
|
||||
the CIO device driver ignores a newly discovered device. Ignored devices are
|
||||
not accessible and do not use resources.
|
||||
|
||||
COMMANDS:
|
||||
-h, --help Print this help text
|
||||
-v, --version Print version information
|
||||
-a, --add DEVID Add a device ID to the blacklist
|
||||
-A, --add-all Add all device IDs to the blacklist
|
||||
-r, --remove DEVID Remove a device ID from the blacklist
|
||||
-R, --remove-all Remove all device IDs from the blacklist
|
||||
-l, --list List device IDs on the blacklist
|
||||
-L, --list-not-blacklisted List device IDs not on the blacklist
|
||||
-i, --is-ignored DEVID Check if specified device ID is on the blacklist
|
||||
-k, --kernel-param List blacklist in cio_ignore kernel param format
|
||||
-u, --unused Create blacklist including all unused devices
|
||||
-p, --purge Unregister all unused devices on the blacklist
|
||||
EOF
|
||||
}
|
||||
|
||||
# print_version()
|
||||
# Print version information.
|
||||
function print_version()
|
||||
{
|
||||
echo "${TOOLNAME}: version ${VERSION}"
|
||||
echo "Copyright IBM Corp. 2009, 2017"
|
||||
}
|
||||
|
||||
# print_usage_tip()
|
||||
# Print usage tip text.
|
||||
function print_usage_tip()
|
||||
{
|
||||
echo "Use '${TOOLNAME} --help' to get usage information." >&2
|
||||
}
|
||||
|
||||
# warn(msg)
|
||||
# Print msg to stderr in warning text format.
|
||||
function warn()
|
||||
{
|
||||
local MSG=$1
|
||||
|
||||
echo "${TOOLNAME}: $MSG" >&2
|
||||
}
|
||||
|
||||
# error(msg)
|
||||
# Print msg to stderr in error text format and exit with non-zero exit code.
|
||||
function error()
|
||||
{
|
||||
local MSG=$1
|
||||
|
||||
warn "$MSG"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# blacklist_write(msg)
|
||||
# Check for write access and write msg to blacklist
|
||||
function blacklist_write()
|
||||
{
|
||||
local MSG="$*"
|
||||
|
||||
if [ ! -w "$BLACKLIST" ] ; then
|
||||
error "Error: missing write permission for $BLACKLIST"
|
||||
fi
|
||||
|
||||
echo "$MSG" > "$BLACKLIST" 2>/dev/null || return 1
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# split_range(range, &bus_id1, &bus_id2)
|
||||
# Set bus_id1 and bus_id2 to the bus IDs as specified in range.
|
||||
function split_range()
|
||||
{
|
||||
local RANGE=$1
|
||||
local LOCAL_BUSID1="${RANGE%%-*}"
|
||||
local LOCAL_BUSID2="${RANGE##*-}"
|
||||
|
||||
eval "$2=$LOCAL_BUSID1"
|
||||
eval "$3=$LOCAL_BUSID2"
|
||||
}
|
||||
|
||||
# split_bus_id(bus_id, &cssid, &ssid, &devno)
|
||||
# Set cssid, ssid and devno to the respective values as specified in bus_id.
|
||||
function split_bus_id()
|
||||
{
|
||||
local BUSID=$1
|
||||
local DEVNO="${BUSID##*.}"
|
||||
local BUS="${BUSID%.*}"
|
||||
local CSSID="${BUS%%.*}"
|
||||
local SSID="${BUS##*.}"
|
||||
|
||||
# Set cssid and ssid to zero if not specified
|
||||
if [ "${BUSID#*.}" == "$BUSID" ] ; then
|
||||
CSSID=0
|
||||
SSID=0
|
||||
fi
|
||||
eval "let $2=0x$CSSID"
|
||||
eval "let $3=0x$SSID"
|
||||
eval "let $4=0x$DEVNO"
|
||||
}
|
||||
|
||||
# count_char(string, char, &count)
|
||||
# Count the number of times that char occurs in string.
|
||||
function count_char()
|
||||
{
|
||||
local STRING=$1
|
||||
local CHAR=$2
|
||||
local COUNT=$3
|
||||
local I
|
||||
|
||||
I=0
|
||||
while [ "${STRING#*$CHAR}" != "$STRING" ] ; do
|
||||
let I=$I+1
|
||||
STRING="${STRING#*$CHAR}"
|
||||
done
|
||||
eval "$COUNT=$I"
|
||||
}
|
||||
|
||||
# check_hex_number(number, mindigits, maxdigits, max, &errmsg)
|
||||
# Check hex number for validity. Return 0 when valid, 1 otherwise.
|
||||
function check_hex_number()
|
||||
{
|
||||
local NUMBER=$1
|
||||
local MINDIGITS=$2
|
||||
local MAXDIGITS=$3
|
||||
local MAX=$4
|
||||
local ERRHEX=$5
|
||||
local VAL
|
||||
|
||||
if [ -z "$NUMBER" ] ; then
|
||||
eval "$ERRHEX='is empty'"
|
||||
return 1
|
||||
fi
|
||||
let VAL="0x$NUMBER" 2>/dev/null
|
||||
if [ -z "$VAL" ] ; then
|
||||
eval "$ERRHEX='is not valid'"
|
||||
return 1
|
||||
fi
|
||||
if [ $VAL -gt $MAX ] ; then
|
||||
eval "$ERRHEX='is too large'"
|
||||
return 1
|
||||
fi
|
||||
if [ ${#NUMBER} -lt $MINDIGITS ] ; then
|
||||
eval "$ERRHEX='is too short'"
|
||||
return 1
|
||||
fi
|
||||
if [ ${#NUMBER} -gt $MAXDIGITS ] ; then
|
||||
eval "$ERRHEX='is too long'"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# check_dev_id(devid, &errmsg)
|
||||
# Check device ID for validity. Return 0 when ID is valid, 1 otherwise.
|
||||
function check_dev_id()
|
||||
{
|
||||
local DEVID=$1
|
||||
local ERRDEV=$2
|
||||
local ERRTXT
|
||||
local CSSID
|
||||
local SSID
|
||||
local DEVNO
|
||||
local DOTCOUNT
|
||||
local IFS
|
||||
|
||||
# Check for empty device ID
|
||||
if [ -z "$DEVID" ] ; then
|
||||
eval "$ERRDEV='device ID is empty'"
|
||||
return 1
|
||||
fi
|
||||
# Check for spaces in device id
|
||||
IFS=' '
|
||||
set - $DEVID
|
||||
if [ $# -ne 1 -o "$DEVID" != "$1" ] ; then
|
||||
eval "$ERRDEV='device ID contains spaces'"
|
||||
return 1
|
||||
fi
|
||||
# Convert bus id to positional parameters
|
||||
count_char "$DEVID" '.' DOTCOUNT
|
||||
IFS='.'
|
||||
set - $DEVID
|
||||
unset IFS
|
||||
# Check number of components
|
||||
if [ $DOTCOUNT -eq 0 ] ; then
|
||||
# Old style device number
|
||||
DEVNO="${1#0x}"
|
||||
if [ -z "$DEVNO" ] ; then
|
||||
eval "$ERRDEV='device number is incomplete'"
|
||||
return 1
|
||||
fi
|
||||
# Check for valid device number
|
||||
if ! check_hex_number "$DEVNO" 1 65535 $MAXDEVNO ERRTXT; then
|
||||
eval "$ERRDEV='device number $ERRTXT'"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
# Check for bus id format
|
||||
if [ $DOTCOUNT -ne 2 ] ; then
|
||||
eval "$ERRDEV='unrecognized format'"
|
||||
return 1
|
||||
fi
|
||||
CSSID=$1
|
||||
SSID=$2
|
||||
DEVNO=$3
|
||||
# Check cssid
|
||||
if ! check_hex_number "$CSSID" 1 2 $MAXCSSID ERRTXT; then
|
||||
eval "$ERRDEV='CSSID $ERRTXT'"
|
||||
return 1
|
||||
fi
|
||||
# Check ssid
|
||||
if ! check_hex_number "$SSID" 1 1 $MAXSSID ERRTXT; then
|
||||
eval "$ERRDEV='SSID $ERRTXT'"
|
||||
return 1
|
||||
fi
|
||||
# Check devno
|
||||
if ! check_hex_number "$DEVNO" 4 4 $MAXDEVNO ERRTXT; then
|
||||
eval "$ERRDEV='device number $ERRTXT'"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# check_range(range, &errmsg)
|
||||
# Check a device ID range for validity. Return 0 if valid, 1 otherwise.
|
||||
function check_range()
|
||||
{
|
||||
local RANGE=$1
|
||||
local ERRRNG=$2
|
||||
local ERRTXTRNG
|
||||
local MINUSCOUNT
|
||||
local BUSID1
|
||||
local BUSID2
|
||||
local CSSID1
|
||||
local CSSID2
|
||||
local SSID1
|
||||
local SSID2
|
||||
local DEVNO1
|
||||
local DEVNO2
|
||||
local IFS
|
||||
|
||||
count_char "$RANGE" '-' MINUSCOUNT
|
||||
IFS='-'
|
||||
set - $RANGE
|
||||
unset IFS
|
||||
# Check number of device IDs
|
||||
if [ $MINUSCOUNT -gt 1 ] ; then
|
||||
eval "$ERRRNG='unrecognized format'"
|
||||
return 1
|
||||
fi
|
||||
# Check first device ID
|
||||
if ! check_dev_id "$1" ERRTXTRNG ; then
|
||||
eval "$ERRRNG='$ERRTXTRNG'"
|
||||
return 1
|
||||
fi
|
||||
if [ $MINUSCOUNT -eq 0 ] ; then
|
||||
return 0
|
||||
fi
|
||||
# Check second device ID
|
||||
if ! check_dev_id "$2" ERRTXTRNG ; then
|
||||
eval "$ERRRNG='$ERRTXTRNG'"
|
||||
return 1
|
||||
fi
|
||||
# Check actual ID
|
||||
split_range "$RANGE" BUSID1 BUSID2
|
||||
split_bus_id "$BUSID1" CSSID1 SSID1 DEVNO1
|
||||
split_bus_id "$BUSID2" CSSID2 SSID2 DEVNO2
|
||||
if [ "$CSSID1" -ne "$CSSID2" ] ; then
|
||||
eval "$ERRRNG='CSSIDs do not match'"
|
||||
return 1
|
||||
fi
|
||||
if [ "$SSID1" -ne "$SSID2" ] ; then
|
||||
eval "$ERRRNG='SSIDs do not match'"
|
||||
return 1
|
||||
fi
|
||||
if [ "$DEVNO1" -gt "$DEVNO2" ] ; then
|
||||
eval "$ERRRNG='reversed device ID order'"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# add_device(list)
|
||||
# Add a list of devices to blacklist
|
||||
function add_device()
|
||||
{
|
||||
local DEVID_LIST=$1
|
||||
local RANGE
|
||||
local ERRMSG
|
||||
local MINUSCOUNT
|
||||
local IFS=','
|
||||
|
||||
if [ -z "$DEVID_LIST" ] ; then
|
||||
error "--add requires an argument"
|
||||
fi
|
||||
for RANGE in $DEVID_LIST ; do
|
||||
if blacklist_write "add $RANGE" ; then
|
||||
continue
|
||||
fi
|
||||
# Try to determine why blacklist operation failed
|
||||
if ! check_range "$RANGE" ERRMSG ; then
|
||||
count_char "$RANGE" '-' MINUSCOUNT
|
||||
if [ "$MINUSCOUNT" -eq 0 ] ; then
|
||||
error "Error: device ID '$RANGE': $ERRMSG"
|
||||
else
|
||||
error "Error: device ID range '$RANGE': $ERRMSG"
|
||||
fi
|
||||
else
|
||||
error "Error: could not add '$RANGE' to blacklist"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# add_all_devices()
|
||||
# Add all devices to the blacklist.
|
||||
function add_all_devices()
|
||||
{
|
||||
blacklist_write 'add all' || \
|
||||
error "Error: add-all function not accepted by kernel"
|
||||
}
|
||||
|
||||
# remove_device(list)
|
||||
# Remove a list of devices from blacklist.
|
||||
function remove_device()
|
||||
{
|
||||
local DEVID_LIST=$1
|
||||
local RANGE
|
||||
local IFS=','
|
||||
|
||||
if [ -z "$DEVID_LIST" ] ; then
|
||||
error "--remove requires an argument"
|
||||
fi
|
||||
for RANGE in $DEVID_LIST ; do
|
||||
if blacklist_write "free $RANGE" ; then
|
||||
continue
|
||||
fi
|
||||
# Try to determine why blacklist operation failed
|
||||
if ! check_range "$RANGE" ERRMSG ; then
|
||||
count_char "$RANGE" '-' MINUSCOUNT
|
||||
if [ "$MINUSCOUNT" -eq 0 ] ; then
|
||||
error "Error: device ID '$RANGE': $ERRMSG"
|
||||
else
|
||||
error "Error: device ID range '$RANGE': $ERRMSG"
|
||||
fi
|
||||
else
|
||||
error "Error: could not remove '$RANGE' from blacklist"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# remove_all_devices()
|
||||
# Remove all devices from blacklist.
|
||||
function remove_all_devices()
|
||||
{
|
||||
blacklist_write 'free all' || \
|
||||
error "Error: remove-all function not accepted by kernel"
|
||||
}
|
||||
|
||||
# list_blacklisted(showheader)
|
||||
# Print list of devices on blacklist. Precede output by header text
|
||||
# if showheader is 1
|
||||
function list_blacklisted()
|
||||
{
|
||||
local SHOWHEADER=$1
|
||||
local LIST
|
||||
local ENTRY
|
||||
|
||||
if [ $SHOWHEADER -eq 1 ] ; then
|
||||
echo 'Ignored devices:'
|
||||
echo '================='
|
||||
fi
|
||||
LIST=$(cat "$BLACKLIST" 2>/dev/null) || \
|
||||
error "Error: could not read $BLACKLIST"
|
||||
# Parse each blacklist entry
|
||||
for ENTRY in $LIST ; do
|
||||
echo "$ENTRY"
|
||||
done
|
||||
}
|
||||
|
||||
# advance_ssid(cssid, ssid, &newcssid, &newssid)
|
||||
# Set newcssid and newssid to the next ssid after cssid and ssid. Return
|
||||
# 0 if there was another ssid, 1 if all ssids have been processed.
|
||||
function advance_ssid()
|
||||
{
|
||||
local LOCAL_CSSID=$1
|
||||
local LOCAL_SSID=$2
|
||||
let LOCAL_SSID=$LOCAL_SSID+1
|
||||
|
||||
if [ $LOCAL_SSID -gt $MAXSSID ] ; then
|
||||
LOCAL_SSID=0
|
||||
let LOCAL_CSSID=$LOCAL_CSSID+1
|
||||
if [ $LOCAL_CSSID -gt $MAXCSSID ] ; then
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
eval "$3=$LOCAL_CSSID"
|
||||
eval "$4=$LOCAL_SSID"
|
||||
return 0
|
||||
}
|
||||
|
||||
# print_range(cssid1, ssid1, devno1, cssid2, ssid2, devno2)
|
||||
# Print range for given device ID.
|
||||
function print_range()
|
||||
{
|
||||
local CSSID1
|
||||
local SSID1
|
||||
local DEVNO1
|
||||
local CSSID2
|
||||
local SSID2
|
||||
local DEVNO2
|
||||
|
||||
let CSSID1=$1
|
||||
let SSID1=$2
|
||||
let DEVNO1=$3
|
||||
let CSSID2=$4
|
||||
let SSID2=$5
|
||||
let DEVNO2=$6
|
||||
|
||||
if [ $CSSID1 -eq $CSSID2 -a $SSID1 -eq $SSID2 -a \
|
||||
$DEVNO1 -eq $DEVNO2 ] ; then
|
||||
printf '%x.%x.%04x\n' $CSSID1 $SSID1 $DEVNO1
|
||||
else
|
||||
printf '%x.%x.%04x-%x.%x.%04x\n' $CSSID1 $SSID1 $DEVNO1 \
|
||||
$CSSID2 $SSID2 $DEVNO2
|
||||
|
||||
fi
|
||||
}
|
||||
|
||||
# list_not_blacklisted(showheader)
|
||||
# Print list of devices not on blacklist. Precede output by header text
|
||||
# if showheader is 1.
|
||||
function list_not_blacklisted()
|
||||
{
|
||||
local SHOWHEADER=$1
|
||||
local CSSID=0
|
||||
local SSID=0
|
||||
local DEVNO=0
|
||||
local ENTRY
|
||||
local LIST
|
||||
|
||||
if [ $SHOWHEADER -eq 1 ] ; then
|
||||
echo 'Devices that are not ignored:'
|
||||
echo '============================='
|
||||
fi
|
||||
LIST=$(cat "$BLACKLIST" 2>/dev/null) || \
|
||||
error "Error: could not read $BLACKLIST"
|
||||
# Parse each blacklist entry
|
||||
for ENTRY in $LIST ; do
|
||||
local BLBUSID1
|
||||
local BLBUSID2
|
||||
local BLCSSID
|
||||
local BLSSID
|
||||
local BLDEVNO1
|
||||
local BLDEVNO2
|
||||
|
||||
# Prepare variables containing bus id information
|
||||
split_range $ENTRY BLBUSID1 BLBUSID2
|
||||
split_bus_id $BLBUSID1 BLCSSID BLSSID BLDEVNO1
|
||||
split_bus_id $BLBUSID2 BLCSSID BLSSID BLDEVNO2
|
||||
|
||||
# Print ranges in ssids before this entry
|
||||
while [ $CSSID -ne $BLCSSID -o $SSID -ne $BLSSID ] ; do
|
||||
print_range $CSSID $SSID $DEVNO $CSSID $SSID $MAXDEVNO
|
||||
DEVNO=0
|
||||
if ! advance_ssid $CSSID $SSID CSSID SSID ; then
|
||||
return
|
||||
fi
|
||||
done
|
||||
# Print range before this entry in the same ssid
|
||||
if [ $BLDEVNO1 -gt 0 ] ; then
|
||||
print_range $CSSID $SSID $DEVNO \
|
||||
$CSSID $SSID $BLDEVNO1-1
|
||||
fi
|
||||
# Advance current id pointer to after the end of this entry
|
||||
let DEVNO=$BLDEVNO2+1
|
||||
if [ $DEVNO -gt $MAXDEVNO ] ; then
|
||||
DEVNO=0
|
||||
if ! advance_ssid $CSSID $SSID CSSID SSID ; then
|
||||
return
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Print ranges in ssids after the final entry
|
||||
while [ $CSSID -le $MAXCSSID -o $SSID -le $MAXSSID ] ; do
|
||||
print_range $CSSID $SSID $DEVNO $CSSID $SSID $MAXDEVNO
|
||||
DEVNO=0
|
||||
if ! advance_ssid $CSSID $SSID CSSID SSID ; then
|
||||
return
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# simplify_range(range, &dest_range)
|
||||
# Remove 0.0. from bus ids in range.
|
||||
function simplify_range()
|
||||
{
|
||||
local LOCAL_RANGE=$1
|
||||
local BUSID1
|
||||
local BUSID2
|
||||
|
||||
split_range $LOCAL_RANGE BUSID1 BUSID2
|
||||
BUSID1=${BUSID1##0.0.}
|
||||
BUSID2=${BUSID2##0.0.}
|
||||
if [ $BUSID1 == $BUSID2 ] ; then
|
||||
eval "$2=$BUSID1"
|
||||
else
|
||||
eval "$2=$BUSID1-$BUSID2"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# to_param(invert, list)
|
||||
# Print list in comma-separated format, preceding each range with ! if
|
||||
# invert is 1.
|
||||
function to_param()
|
||||
{
|
||||
local INVERT=$1
|
||||
local LIST=$2
|
||||
local RANGE
|
||||
local SEP
|
||||
local PREFIX
|
||||
|
||||
if [ $INVERT -eq 1 ] ; then
|
||||
echo -n 'all'
|
||||
PREFIX='!'
|
||||
SEP=','
|
||||
fi
|
||||
|
||||
for RANGE in $LIST ; do
|
||||
simplify_range $RANGE RANGE
|
||||
echo -n "$SEP$PREFIX$RANGE"
|
||||
SEP=','
|
||||
done
|
||||
}
|
||||
|
||||
# is_blacklisted(bus_id)
|
||||
# Check if device is on the blacklist. Return 0 when on blacklist, 1 otherwise.
|
||||
function is_blacklisted()
|
||||
{
|
||||
local ISBUSID=$1
|
||||
local ISCSSID
|
||||
local ISSSID
|
||||
local ISDEVNO
|
||||
local BUSID1
|
||||
local BUSID2
|
||||
local CSSID1
|
||||
local CSSID2
|
||||
local SSID1
|
||||
local SSID2
|
||||
local DEVNO1
|
||||
local DEVNO2
|
||||
local LIST
|
||||
local RANGE
|
||||
|
||||
split_bus_id $ISBUSID ISCSSID ISSSID ISDEVNO
|
||||
LIST=$(cat "$BLACKLIST" 2>/dev/null) || \
|
||||
error "Error: could not read $BLACKLIST"
|
||||
# Parse each blacklist entry
|
||||
for RANGE in $LIST ; do
|
||||
split_range "$RANGE" BUSID1 BUSID2
|
||||
split_bus_id "$BUSID1" CSSID1 SSID1 DEVNO1
|
||||
split_bus_id "$BUSID2" CSSID2 SSID2 DEVNO2
|
||||
|
||||
if [ $ISCSSID -ne $CSSID1 -o $ISSSID -ne $SSID1 ] ; then
|
||||
continue
|
||||
fi
|
||||
if [ $ISDEVNO -ge $DEVNO1 -a $ISDEVNO -le $DEVNO2 ] ; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# is_blacklisted_opt(bus_id)
|
||||
# Check if specified busid is on blacklist. Print result and exit with code 0
|
||||
# if on blacklist, 2 otherwise.
|
||||
function is_blacklisted_opt()
|
||||
{
|
||||
local BUSID=$1
|
||||
local ERRBLO
|
||||
local COMMACOUNT
|
||||
local MINUSCOUNT
|
||||
|
||||
if [ -z "$BUSID" ] ; then
|
||||
error "--is-ignored requires an argument"
|
||||
fi
|
||||
count_char "$BUSID" ',' COMMACOUNT
|
||||
count_char "$BUSID" '-' MINUSCOUNT
|
||||
if [ $COMMACOUNT -gt 0 -o $MINUSCOUNT -gt 0 ] ; then
|
||||
error "Error: --is-ignored accepts only a single device ID"
|
||||
fi
|
||||
if ! check_dev_id "$BUSID" ERRBLO ; then
|
||||
error "Error: device ID '$BUSID': $ERRBLO"
|
||||
fi
|
||||
if is_blacklisted $BUSID ; then
|
||||
echo "Device $BUSID is ignored"
|
||||
exit 0
|
||||
else
|
||||
echo "Device $BUSID is not ignored"
|
||||
exit 2
|
||||
fi
|
||||
}
|
||||
|
||||
# is_vm()
|
||||
# Check if Linux is running on z/VM. Return 0 when on VM, 1 otherwise.
|
||||
function is_vm()
|
||||
{
|
||||
if grep 'z/VM' < "$SYSINFO" -q ; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# get_console_busid(&busid)
|
||||
# Return busid for VM console.
|
||||
function get_console_busid()
|
||||
{
|
||||
local DEVID
|
||||
|
||||
if [ ! -d "$CONSDRV" ] ; then
|
||||
return
|
||||
fi
|
||||
DEVID=$(echo $CONSDRV/*.*.*)
|
||||
DEVID="${DEVID##*/}"
|
||||
if [ "$DEVID" != "*.*.*" ] ; then
|
||||
eval "$1=$DEVID"
|
||||
fi
|
||||
}
|
||||
|
||||
# check_vm_console()
|
||||
# Print a warning if VM console is on blacklist.
|
||||
function check_vm_console()
|
||||
{
|
||||
local CONSBUSID
|
||||
|
||||
if ! is_vm ; then
|
||||
return
|
||||
fi
|
||||
get_console_busid CONSBUSID
|
||||
if [ -z "$CONSBUSID" ] ; then
|
||||
return
|
||||
fi
|
||||
if is_blacklisted $CONSBUSID ; then
|
||||
warn "Warning: reboot might fail due to blacklisted console $CONSBUSID"
|
||||
fi
|
||||
}
|
||||
|
||||
# kernel_param()
|
||||
# Print blacklist in kernel parameter format.
|
||||
function kernel_param()
|
||||
{
|
||||
local BLACKLISTED=$(list_blacklisted 0)
|
||||
local NOTBLACKLISTED=$(list_not_blacklisted 0)
|
||||
|
||||
echo -n 'cio_ignore='
|
||||
# Determine shorter list
|
||||
if [ ${#BLACKLISTED} -le ${#NOTBLACKLISTED} ] ; then
|
||||
# Use blacklist
|
||||
to_param 0 "$BLACKLISTED"
|
||||
else
|
||||
# Use negative blacklist
|
||||
to_param 1 "$NOTBLACKLISTED"
|
||||
fi
|
||||
echo
|
||||
check_vm_console
|
||||
}
|
||||
|
||||
# create_from_unused()
|
||||
# Create blacklist from unused devices as determined by lscss.
|
||||
function create_from_unused()
|
||||
{
|
||||
local DEVID
|
||||
local UNUSED
|
||||
|
||||
add_all_devices
|
||||
$LSCSS 2>/dev/null | grep yes | while read DEVID UNUSED ; do
|
||||
remove_device $DEVID
|
||||
done
|
||||
}
|
||||
|
||||
# purge()
|
||||
# Perform blacklist purge function.
|
||||
function purge()
|
||||
{
|
||||
blacklist_write 'purge' || \
|
||||
error "Error: purge function not accepted by kernel"
|
||||
}
|
||||
|
||||
# Check for blacklist
|
||||
if [ ! -f "$BLACKLIST" ] ; then
|
||||
error "Error: file $BLACKLIST not found"
|
||||
fi
|
||||
|
||||
# Check for zero options
|
||||
if [ $# -eq 0 ] ; then
|
||||
warn 'Need one of options -a, -A, -r, -R, -l, -L, -i, -k, -u or -p'
|
||||
print_usage_tip
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Parse command line options
|
||||
while [ $# -gt 0 ] ; do
|
||||
case $1 in
|
||||
-h|--help)
|
||||
print_help
|
||||
exit 0
|
||||
;;
|
||||
-v|--version)
|
||||
print_version
|
||||
exit 0
|
||||
;;
|
||||
-a|--add)
|
||||
shift
|
||||
add_device $1
|
||||
;;
|
||||
-A|--add-all)
|
||||
add_all_devices
|
||||
;;
|
||||
-r|--remove)
|
||||
shift
|
||||
remove_device $1
|
||||
WAIT_FOR_CIO=1
|
||||
;;
|
||||
-R|--remove-all)
|
||||
remove_all_devices
|
||||
WAIT_FOR_CIO=1
|
||||
;;
|
||||
-l|--list)
|
||||
list_blacklisted 1
|
||||
;;
|
||||
-L|--list-not-blacklisted)
|
||||
list_not_blacklisted 1
|
||||
;;
|
||||
-i|--is-ignored)
|
||||
shift
|
||||
is_blacklisted_opt $1
|
||||
;;
|
||||
-k|--kernel-param)
|
||||
kernel_param
|
||||
;;
|
||||
-u|--unused)
|
||||
create_from_unused
|
||||
;;
|
||||
-p|--purge)
|
||||
purge
|
||||
WAIT_FOR_CIO=1
|
||||
;;
|
||||
*)
|
||||
warn "invalid option '$1'"
|
||||
print_usage_tip
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [ \( -w $CIO_SETTLE \) -a $WAIT_FOR_CIO = 1 ] ; then
|
||||
echo 1 > $CIO_SETTLE
|
||||
fi
|
||||
|
||||
exit 0
|
||||
@@ -0,0 +1,256 @@
|
||||
.\" Copyright 2017 IBM Corp.
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
.TH CIO_IGNORE 8 "Apr 2009" s390\-tools
|
||||
|
||||
.SH NAME
|
||||
cio_ignore \- query or modify the CIO device driver blacklist
|
||||
|
||||
.SH SYNOPSIS
|
||||
.B cio_ignore
|
||||
.RB [ \-h | \-\-help ]
|
||||
.RB [ \-v | \-\-version ]
|
||||
.RS 11
|
||||
.br
|
||||
.RB [ \-a | \-\-add
|
||||
.IR DEVID ]
|
||||
.RB [ \-A | \-\-add\-all ]
|
||||
.br
|
||||
.RB [ \-r | \-\-remove
|
||||
.IR DEVID ]
|
||||
.RB [ \-R | \-\-remove\-all ]
|
||||
.br
|
||||
.RB [ \-l | \-\-list ]
|
||||
.RB [ \-L | \-\-list\-not\-blacklisted ]
|
||||
.br
|
||||
.RB [ \-i | \-\-is\-ignored
|
||||
.IR DEVID ]
|
||||
.br
|
||||
.RB [ \-k | \-\-kernel\-param ]
|
||||
.RB [ \-u | \-\-unused ]
|
||||
.RB [ \-p | \-\-purge ]
|
||||
|
||||
|
||||
.SH DESCRIPTION
|
||||
The cio_ignore command provides functions to query and modify the contents of
|
||||
the CIO device driver blacklist. This blacklist determines if Linux tries to
|
||||
make a device which is connected through the Channel-subsystem (CSS) available
|
||||
for use by Linux.
|
||||
|
||||
Adding a device to the blacklist does not immediately result in a change
|
||||
of device availability. For devices which are already available in Linux,
|
||||
the blacklist only has an effect when attaching or re-attaching the device, or
|
||||
when using the
|
||||
.I purge
|
||||
function. Removing a device from the blacklist on the other hand
|
||||
will directly result in an attempt to make that device available.
|
||||
|
||||
The
|
||||
blacklist is not persistent, that is it is cleared during initial program load.
|
||||
To create a persistent blacklist, add the output of the
|
||||
.I kernel-param
|
||||
option to the Linux kernel parameters.
|
||||
|
||||
.B Advantages of using a blacklist
|
||||
|
||||
The CIO device driver will not perform device discovery operations or allocate
|
||||
memory for devices which are on the blacklist. Therefore it is possible to
|
||||
significantly reduce time and memory consumption during the boot phase of Linux
|
||||
by specifying a blacklist as kernel parameter which contains all devices that
|
||||
are not required for normal operations.
|
||||
|
||||
Using the
|
||||
.I purge
|
||||
function, it is also possible to make devices only temporarily available (for
|
||||
example DASD disks which are only used for backup) and remove them afterwards
|
||||
when they are no longer required.
|
||||
|
||||
|
||||
.B Device ID format
|
||||
|
||||
To identify a device, specify its device ID in the format:
|
||||
"<CSSID>.<SSID>.<DEVNO>". For example: "0.0.0190".
|
||||
.br
|
||||
|
||||
The meaning of each field is:
|
||||
|
||||
CSSID
|
||||
.RS
|
||||
Channel-subsystem ID in hexadecimal notation with at most two digits.
|
||||
.RE
|
||||
|
||||
SSID
|
||||
.RS
|
||||
Subchannel-set ID in hexadecimal notation with exactly one digit.
|
||||
.RE
|
||||
|
||||
DEVNO
|
||||
.RS
|
||||
Device number in hexadecimal notation with exactly four digits.
|
||||
.RE
|
||||
|
||||
Devices for which CSSID and SSID are 0 can alternatively be specified by
|
||||
using only the device number, either with or without leading "0x" and zeros.
|
||||
For example: "190", "0x190" or "0190".
|
||||
|
||||
|
||||
|
||||
|
||||
.SH OPTIONS
|
||||
.B \-h
|
||||
.br
|
||||
.B \-\-help
|
||||
.RS
|
||||
Print a short help text, then exit.
|
||||
.RE
|
||||
|
||||
.B \-v
|
||||
.br
|
||||
.B \-\-version
|
||||
.RS
|
||||
Print version information, then exit.
|
||||
.RE
|
||||
|
||||
.B \-a
|
||||
.br
|
||||
.B \-\-add
|
||||
.I DEVID
|
||||
.RS
|
||||
Add one or more device IDs to the blacklist.
|
||||
.br
|
||||
|
||||
.I DEVID
|
||||
can be a single device ID as specified in section DESCRIPTION, or it can be a
|
||||
range of device IDs, or a comma-separated list of device IDs or ranges. Ranges
|
||||
may not cross SSID boundaries. See also section EXAMPLES.
|
||||
.br
|
||||
|
||||
Note that adding an existing device to the blacklist will not immediately
|
||||
change its availability in Linux.
|
||||
|
||||
.RE
|
||||
|
||||
.B \-A
|
||||
.br
|
||||
.B \-\-add\-all
|
||||
.RS
|
||||
Add the complete range of possible device IDs to the blacklist.
|
||||
.RE
|
||||
|
||||
.B \-r
|
||||
.br
|
||||
.B \-\-remove
|
||||
.I DEVID
|
||||
.RS
|
||||
Remove one or more device IDs from the blacklist
|
||||
|
||||
.I DEVID
|
||||
can be a single device ID as specified in section DESCRIPTION, or it can be a
|
||||
range of device IDs, or a comma-separated list of device IDs or ranges. Ranges
|
||||
may not cross SSID boundaries. See also section EXAMPLES.
|
||||
.br
|
||||
|
||||
When a device is removed from the blacklist, Linux will immediately attempt
|
||||
to make that device available.
|
||||
.RE
|
||||
|
||||
.B \-R
|
||||
.br
|
||||
.B \-\-remove\-all
|
||||
.RS
|
||||
Remove all device IDs from the blacklist.
|
||||
.RE
|
||||
|
||||
.B \-l
|
||||
.br
|
||||
.BI \-\-list
|
||||
.RS
|
||||
List device IDs on the blacklist.
|
||||
.RE
|
||||
|
||||
.B \-L
|
||||
.br
|
||||
.B \-\-list\-not\-blacklisted
|
||||
.RS
|
||||
List device IDs not on the blacklist.
|
||||
.RE
|
||||
|
||||
.B \-i
|
||||
.br
|
||||
.B \-\-is-ignored
|
||||
.I DEVID
|
||||
.RS
|
||||
Check if the device with the specified ID is on the blacklist. If it is
|
||||
on the blacklist, the exit code is 0. If it is not on the blacklist, the
|
||||
exit code is 2.
|
||||
.RE
|
||||
|
||||
.B \-k
|
||||
.br
|
||||
.B \-\-kernel\-param
|
||||
.RS
|
||||
List the current blacklist in cio_ignore kernel param format.
|
||||
|
||||
To make a blacklist persistent across IPL, use the output of this command
|
||||
and add it to the Linux kernel parameter.
|
||||
.RE
|
||||
|
||||
.B \-u
|
||||
.br
|
||||
.B \-\-unused
|
||||
.RS
|
||||
Create a blacklist which includes all unused (i.e. offline) devices.
|
||||
|
||||
Note: The new blacklist replaces any previous one. Also in this
|
||||
context, an unused device is a device which is currently not online (see
|
||||
.BR chccwdev(8) ).
|
||||
.RE
|
||||
|
||||
.B \-p
|
||||
.br
|
||||
.B \-\-purge
|
||||
.RS
|
||||
Remove all blacklisted unused (i.e. offline) devices from Linux.
|
||||
|
||||
To make a device available again, use the
|
||||
.I remove
|
||||
or
|
||||
.I remove\-all
|
||||
function.
|
||||
.RE
|
||||
|
||||
.SH EXAMPLES
|
||||
|
||||
.B cio_ignore -a 0x190,0.0.1000-0.0.1002
|
||||
.RS
|
||||
Add devices 0.0.0190, 0.0.1000, 0.0.1001 and 0.0.1002 to the blacklist. If these
|
||||
devices are currently available in Linux, their availability will not
|
||||
immediately change.
|
||||
.RE
|
||||
|
||||
.B cio_ignore -A -r 0x190
|
||||
.RS
|
||||
Add all devices except device 0.0.0190 to the blacklist.
|
||||
.RE
|
||||
|
||||
.B cio_ignore -r 0x190
|
||||
.RS
|
||||
Remove device 0.0.0190 from the blacklist. If this device is currently attached
|
||||
to the Linux system but not available, it will immediately become available.
|
||||
.RE
|
||||
|
||||
.B cio_ignore -u -p
|
||||
.RS
|
||||
Remove all devices from Linux which are currently not online.
|
||||
.RE
|
||||
|
||||
.B cio_ignore -u -k
|
||||
.RS
|
||||
Set the blacklist to contain all offline devices and print the corresponding
|
||||
kernel parameter.
|
||||
.RE
|
||||
|
||||
.SH SEE ALSO
|
||||
.BR lscss (8),
|
||||
.BR chccwdev (8)
|
||||
@@ -0,0 +1,25 @@
|
||||
# Common definitions
|
||||
include ../../common.mak
|
||||
|
||||
libs = $(rootdir)/libccw/libccw.a \
|
||||
$(rootdir)/libutil/libutil.a
|
||||
|
||||
objects = lscss.o misc.o
|
||||
|
||||
lscss.o: lscss.c
|
||||
|
||||
all: lscss
|
||||
|
||||
lscss: $(objects) $(libs)
|
||||
|
||||
install: all
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 lscss $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man8
|
||||
$(INSTALL) -m 644 -c lscss.8 $(DESTDIR)$(MANDIR)/man8
|
||||
|
||||
clean:
|
||||
rm -f $(objects) lscss
|
||||
|
||||
.PHONY: all install clean
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
.\" Copyright 2017 IBM Corp.
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
.TH LSCSS 8 "Mar 2009" "s390-tools"
|
||||
|
||||
.SH NAME
|
||||
lscss \- list channel subsystem devices.
|
||||
|
||||
.SH SYNOPSIS
|
||||
.TP 16
|
||||
.B lscss \fI<options>\fR \fI[RANGE]\fR
|
||||
|
||||
.SH DESCRIPTION
|
||||
The lscss command is used to list all or a subset of devices that are managed
|
||||
by the common I/O subsystem.
|
||||
|
||||
.SH [RANGE]
|
||||
Limit output to a range of subchannels by specifying
|
||||
multiple identifiers as a comma-separated list or a
|
||||
range or a combination of both, e.g.
|
||||
|
||||
0.0.1234-0.0.1235,4711
|
||||
|
||||
|
||||
Note that ranges may also be separated by spaces.
|
||||
|
||||
.SH OPTIONS
|
||||
.TP 8
|
||||
.BR -s | --short
|
||||
Shorten IDs by removing leading "0.0.". Note: only IDs beginning with "0.0."
|
||||
will be displayed in this case.
|
||||
|
||||
.TP 8
|
||||
.BR -t | --devtype " " \fI<devtype>[/<model>][,...]\fR
|
||||
For IO subchannels, limit output to devices of the given device type
|
||||
(e.g. 3390).
|
||||
|
||||
.TP 8
|
||||
.BR -d | --devrange
|
||||
Indicate that RANGE refers to device identifiers.
|
||||
|
||||
.TP 8
|
||||
.BR --avail
|
||||
Show availability attribute of IO devices.
|
||||
|
||||
.TP 8
|
||||
.BR --vpm
|
||||
Show verified path mask.
|
||||
VPM is an internal path mask used by Linux. A channel path can be used by Linux device drivers
|
||||
to do IO if the corresponding bit is set in the VPM. Events that can lead to a channel path
|
||||
not being available include:
|
||||
.RS
|
||||
.IP \[bu] 4
|
||||
The corresponding bit is not set in at least one of the PIM, PAM, or POM masks.
|
||||
.IP \[bu]
|
||||
The channel path is varied offline.
|
||||
.IP \[bu]
|
||||
Linux received no interrupt to IO using this channel path.
|
||||
.RE
|
||||
|
||||
.TP 8
|
||||
.BR -v | --version
|
||||
Print the version of the s390-tools package and the command.
|
||||
|
||||
.TP 8
|
||||
.BR -h | --help
|
||||
Print help text.
|
||||
|
||||
.TP 8
|
||||
.BR -u | --uppercase
|
||||
Print values using uppercase.
|
||||
|
||||
.TP 8
|
||||
.BR --io
|
||||
Show IO subchannels. (default)
|
||||
|
||||
.TP 8
|
||||
.BR --chsc
|
||||
Show CHSC subchannels.
|
||||
|
||||
.TP 8
|
||||
.BR --eadm
|
||||
Show EADM subchannels.
|
||||
|
||||
.TP 8
|
||||
.BR --vfio
|
||||
Show additional information for I/O subchannels used for VFIO.
|
||||
An MDEV is a mediated device that is required to be created by the VFIO channel
|
||||
I/O device driver for the VFIO driver framework as the pass-through target
|
||||
device when doing channel I/O pass-through.
|
||||
|
||||
.TP 8
|
||||
.BR -a | --all
|
||||
Show subchannels of all types.
|
||||
|
||||
|
||||
.SH EXAMPLES
|
||||
\fBlscss\fR
|
||||
.RS
|
||||
List all devices that are managed by the common I/O subsystem.
|
||||
.RE
|
||||
|
||||
\fBlscss -t 3390\fR
|
||||
.RS
|
||||
Same as above but shows only 3390 devices.
|
||||
.RE
|
||||
|
||||
.SH NOTES
|
||||
In rare situations a device might temporarily not be accessible to
|
||||
the subchannel. Then "none" is displayed as the device identifier and the
|
||||
other device attributes are empty.
|
||||
|
||||
.SH AUTHOR
|
||||
.nf
|
||||
This man-page was written by Stefan Bader <shbader@de.ibm.com>.
|
||||
New options added by Sebastian Ott <sebott@linux.vnet.ibm.com>.
|
||||
.SH "SEE ALSO"
|
||||
.BR chccwdev (8)
|
||||
.fi
|
||||
@@ -0,0 +1,937 @@
|
||||
/*
|
||||
* lscss - Tool to list information about subchannels
|
||||
*
|
||||
* Copyright IBM Corp. 2003, 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.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "lib/ccw.h"
|
||||
#include "lib/util_base.h"
|
||||
#include "lib/util_file.h"
|
||||
#include "lib/util_libc.h"
|
||||
#include "lib/util_list.h"
|
||||
#include "lib/util_opt.h"
|
||||
#include "lib/util_path.h"
|
||||
#include "lib/util_prg.h"
|
||||
#include "lib/util_rec.h"
|
||||
#include "lib/util_scandir.h"
|
||||
#include "lib/util_panic.h"
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
#include "misc.h"
|
||||
|
||||
/*
|
||||
* Numbers for lscss command options that do not have a short form
|
||||
*/
|
||||
#define OPT_AVAIL 256 /* --avail */
|
||||
#define OPT_VPM 257 /* --vpm */
|
||||
#define OPT_IO 258 /* --io */
|
||||
#define OPT_CHSC 259 /* --chsc */
|
||||
#define OPT_EADM 260 /* --eadm */
|
||||
#define OPT_VFIO 261 /* --vfio */
|
||||
|
||||
/* Bus_id format for subchannel or device id */
|
||||
#define ID_FORMAT "^[[:xdigit:]]{1,2}[.][[:xdigit:]][.][[:xdigit:]]{4}$"
|
||||
/* UUID format */
|
||||
#define UUID_FORMAT "^[[:xdigit:]]{8}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{12}$"
|
||||
|
||||
/* Misc constants */
|
||||
#define MAX_BUFFER_SIZE_FOR_SUBCHANNEL_ATTRIBUTES 37
|
||||
#define MAX_BUFFER_SIZE_FOR_DEVICE_ATTRIBUTES 16
|
||||
#define PREFIX_ID_LENGTH 4
|
||||
#define SHORT_ID_LENGTH 4
|
||||
#define CHPIDS_SEGMENT_LENGTH 8
|
||||
|
||||
/* Range of subchannel or device identifiers */
|
||||
struct range {
|
||||
struct util_list_node node; /* Pointers to previous and next range */
|
||||
struct ccw_devid lower; /* Lower sch_id or bus_id */
|
||||
struct ccw_devid upper; /* Upper sch_id or bus_id */
|
||||
};
|
||||
|
||||
/* Device type and model */
|
||||
struct devtype {
|
||||
struct util_list_node node; /* Pointers to previous and next devtype */
|
||||
bool no_model; /* <type> format input */
|
||||
unsigned int type; /* Device type */
|
||||
unsigned int model; /* Specific model of the specified device type */
|
||||
};
|
||||
|
||||
/* Subchannel types */
|
||||
enum sch_type {
|
||||
SUBCHANNEL_TYPE_IO = 0, /* I/O subchannels */
|
||||
SUBCHANNEL_TYPE_CHSC = 1, /* CHSC subchannels */
|
||||
SUBCHANNEL_TYPE_EADM = 3, /* EADM subchannels */
|
||||
};
|
||||
|
||||
/*
|
||||
* Private data
|
||||
*/
|
||||
static struct lscss_cmd_opts {
|
||||
/* Boolean flags to indicate wich command options are in effect */
|
||||
bool opt_short; /* -s or --short */
|
||||
bool opt_devtype; /* -t or --devtype */
|
||||
bool opt_devrange; /* -d or --devrange */
|
||||
bool opt_avail; /* --avail */
|
||||
bool opt_vpm; /* --vpm */
|
||||
bool opt_uppercase; /* -u or --uppercase */
|
||||
bool opt_io; /* --io */
|
||||
bool opt_chsc; /* --chsc */
|
||||
bool opt_eadm; /* --eadm */
|
||||
bool opt_vfio; /* --vfio */
|
||||
/* List of device types for the output limitation */
|
||||
int dev_count;
|
||||
struct util_list *devtypes;
|
||||
/* List of sch_id or bus_id ranges for the output limitation */
|
||||
int rng_count;
|
||||
struct util_list *ranges;
|
||||
} cmd;
|
||||
|
||||
/*
|
||||
* Program configuration
|
||||
*/
|
||||
static const struct util_prg prg = {
|
||||
.desc = "List information about available subchannels.\n"
|
||||
"\nRANGE\n"
|
||||
" ID Select single subchannel by ID, e.g. 0.0.004f or 4f\n"
|
||||
" FROM-TO Select range of subchannels between FROM and TO\n"
|
||||
" ID1,ID2-ID3,... Select list of subchannels or subchannel ranges",
|
||||
.args = "[RANGE]",
|
||||
.copyright_vec = {
|
||||
{
|
||||
.owner = "IBM Corp.",
|
||||
.pub_first = 2017,
|
||||
.pub_last = 2017,
|
||||
},
|
||||
UTIL_PRG_COPYRIGHT_END
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Command line options
|
||||
*/
|
||||
static struct util_opt opt_vec[] = {
|
||||
UTIL_OPT_SECTION("OPTIONS"),
|
||||
{
|
||||
.option = { "short", no_argument, NULL, 's'},
|
||||
.desc = "Shorten IDs by removing leading \"0.0.\" "
|
||||
"Note: only IDs beginning with \"0.0.\" "
|
||||
"will be displayed in this case.",
|
||||
},
|
||||
{
|
||||
.option = { "devtype", required_argument, NULL, 't'},
|
||||
.argument = "TYPE,..",
|
||||
.desc = "For IO subchannels, limit output to devices of "
|
||||
"the given TYPE (DEVTYPE[/MODEL])",
|
||||
},
|
||||
{
|
||||
.option = { "devrange", no_argument, NULL, 'd'},
|
||||
.desc = "Indicate that RANGE refers to device identifiers",
|
||||
},
|
||||
{
|
||||
.option = { "avail", no_argument, NULL, OPT_AVAIL},
|
||||
.desc = "Show availability attribute of IO devices",
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
{
|
||||
.option = { "vpm", no_argument, NULL, OPT_VPM},
|
||||
.desc = "Show verified path mask",
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
{
|
||||
.option = { "uppercase", no_argument, NULL, 'u'},
|
||||
.desc = "Print values using uppercase",
|
||||
},
|
||||
{
|
||||
.option = { "io", no_argument, NULL, OPT_IO},
|
||||
.desc = "Show IO subchannels (default)",
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
{
|
||||
.option = { "chsc", no_argument, NULL, OPT_CHSC},
|
||||
.desc = "Show CHSC subchannels",
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
{
|
||||
.option = { "eadm", no_argument, NULL, OPT_EADM},
|
||||
.desc = "Show EADM subchannels",
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
{
|
||||
.option = { "vfio", no_argument, NULL, OPT_VFIO},
|
||||
.desc = "Show VFIO subchannel information",
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
{
|
||||
.option = { "all", no_argument, NULL, 'a'},
|
||||
.desc = "Show subchannels of all types",
|
||||
},
|
||||
UTIL_OPT_HELP,
|
||||
UTIL_OPT_VERSION,
|
||||
UTIL_OPT_END
|
||||
};
|
||||
|
||||
/*
|
||||
* Add new subchannel or device range to the ranges list for further output
|
||||
* limitation
|
||||
*/
|
||||
static void add_new_range(const char *lower_id, const char *upper_id)
|
||||
{
|
||||
struct range *rng;
|
||||
|
||||
rng = util_malloc(sizeof(*rng));
|
||||
if (!ccw_parse_str(&rng->lower, lower_id))
|
||||
errx(EXIT_FAILURE, "Invalid ID specified: %s", lower_id);
|
||||
if (!ccw_parse_str(&rng->upper, upper_id))
|
||||
errx(EXIT_FAILURE, "Invalid ID specified: %s", upper_id);
|
||||
util_list_add_tail(cmd.ranges, rng);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if subchannel or device bus_id is within the provided range
|
||||
*/
|
||||
static bool id_in_range(struct range *rng, struct ccw_devid *id)
|
||||
{
|
||||
/* First compare cssid, then ssid and finally devno */
|
||||
if (id->cssid == rng->lower.cssid) {
|
||||
if (id->ssid == rng->lower.ssid) {
|
||||
if (id->devno < rng->lower.devno)
|
||||
return false;
|
||||
} else if (id->ssid < rng->lower.ssid) {
|
||||
return false;
|
||||
}
|
||||
} else if (id->cssid < rng->lower.cssid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (id->cssid == rng->upper.cssid) {
|
||||
if (id->ssid == rng->upper.ssid) {
|
||||
if (id->devno > rng->upper.devno)
|
||||
return false;
|
||||
} else if (id->ssid > rng->upper.ssid) {
|
||||
return false;
|
||||
}
|
||||
} else if (id->cssid > rng->upper.cssid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if subchannel or device id matches any range in the ranges list
|
||||
*/
|
||||
static bool id_in_ranges_list(const char *id)
|
||||
{
|
||||
struct ccw_devid ccw_id;
|
||||
struct range *rng;
|
||||
|
||||
if (!ccw_parse_str(&ccw_id, id))
|
||||
errx(EXIT_FAILURE, "Invalid subchannel directory '%s'", id);
|
||||
util_list_iterate(cmd.ranges, rng) {
|
||||
if (id_in_range(rng, &ccw_id))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse a string into a devtype structure
|
||||
*
|
||||
* @param[in,out] dt Pointer to devtype structure to be initialized
|
||||
* @parm[in] id String to parse
|
||||
*
|
||||
* @returns true if the input string has been parsed successfuly;
|
||||
* otherwise false
|
||||
*/
|
||||
static bool parse_devtype_str(struct devtype *dt, const char *id)
|
||||
{
|
||||
char d;
|
||||
|
||||
if (strncasecmp(id, "0x", 2) == 0)
|
||||
return false;
|
||||
dt->no_model = false;
|
||||
if (sscanf(id, "%4x %c", &dt->type, &d) == 1) {
|
||||
/* Process <type> input format (maximum length of 4 characters) */
|
||||
dt->no_model = true;
|
||||
} else if (sscanf(id, "%4x/%2x %c", &dt->type, &dt->model,
|
||||
&d) != 2) {
|
||||
/*
|
||||
* Process <type>/<model> input format (maximum lengths of 4 and 2
|
||||
* characters respectively)
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add new device type to the devtypes list for further output limitation
|
||||
*/
|
||||
static void add_new_devtype(const char *dtype)
|
||||
{
|
||||
struct devtype *dt;
|
||||
|
||||
dt = util_malloc(sizeof(struct devtype));
|
||||
if (!parse_devtype_str(dt, dtype))
|
||||
errx(EXIT_FAILURE, "Invalid device type specified: %s", dtype);
|
||||
util_list_add_tail(cmd.devtypes, dt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if provided device type matches any from the device types list
|
||||
*/
|
||||
static bool in_devtypes_list(char *dtype)
|
||||
{
|
||||
struct devtype arg_dt;
|
||||
struct devtype *dt;
|
||||
|
||||
if (!parse_devtype_str(&arg_dt, dtype))
|
||||
errx(EXIT_FAILURE, "Invalid device type detected: %s", dtype);
|
||||
util_list_iterate(cmd.devtypes, dt) {
|
||||
if (arg_dt.type == dt->type) {
|
||||
if (dt->no_model || arg_dt.model == dt->model)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill in the device related entry fields (devtyp, cutype, use, avail)
|
||||
*
|
||||
* @returns 0 - devtype matches devtypes list, device information filled in
|
||||
* 1 - devtype does not match devtypes list, skip entry
|
||||
*/
|
||||
static int fill_device_info(struct util_rec *rec, char *path, char *device)
|
||||
{
|
||||
char buf[MAX_BUFFER_SIZE_FOR_DEVICE_ATTRIBUTES];
|
||||
unsigned long int val_ul;
|
||||
|
||||
if (!path || !device) {
|
||||
if (cmd.opt_devtype && cmd.dev_count > 0)
|
||||
return 1;
|
||||
util_rec_set(rec, "devtyp", "");
|
||||
util_rec_set(rec, "cutype", "");
|
||||
util_rec_set(rec, "use", "");
|
||||
if (cmd.opt_avail)
|
||||
util_rec_set(rec, "avail", "");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (util_file_read_line(buf, sizeof(buf), "%s/%s/devtype",
|
||||
path, device) == 0) {
|
||||
if (strcmp(buf, "n/a") == 0)
|
||||
/* Special case for 'n/a' devtype */
|
||||
strncpy(buf, "0000/00", sizeof(buf));
|
||||
if (cmd.opt_devtype && cmd.dev_count > 0 &&
|
||||
!in_devtypes_list(buf))
|
||||
return 1;
|
||||
if (cmd.opt_uppercase)
|
||||
util_str_toupper(buf);
|
||||
util_rec_set(rec, "devtyp", "%s", buf);
|
||||
} else {
|
||||
if (cmd.opt_devtype && cmd.dev_count > 0)
|
||||
return 1;
|
||||
util_rec_set(rec, "devtyp", "");
|
||||
}
|
||||
|
||||
if (util_file_read_line(buf, sizeof(buf), "%s/%s/cutype",
|
||||
path, device) == 0) {
|
||||
if (cmd.opt_uppercase)
|
||||
util_str_toupper(buf);
|
||||
util_rec_set(rec, "cutype", "%s", buf);
|
||||
} else {
|
||||
util_rec_set(rec, "cutype", "");
|
||||
}
|
||||
|
||||
if (util_file_read_ul(&val_ul, 10, "%s/%s/online", path, device) == 0) {
|
||||
if (val_ul == 1) {
|
||||
snprintf(buf, sizeof(buf), "yes");
|
||||
if (cmd.opt_uppercase)
|
||||
util_str_toupper(buf);
|
||||
util_rec_set(rec, "use", "%s", buf);
|
||||
} else {
|
||||
util_rec_set(rec, "use", "");
|
||||
}
|
||||
} else {
|
||||
util_rec_set(rec, "use", "");
|
||||
}
|
||||
|
||||
if (cmd.opt_avail) {
|
||||
if (util_file_read_line(buf, sizeof(buf), "%s/%s/availability",
|
||||
path, device) == 0) {
|
||||
if (cmd.opt_uppercase)
|
||||
util_str_toupper(buf);
|
||||
util_rec_set(rec, "avail", "%s", buf);
|
||||
} else {
|
||||
util_rec_set(rec, "avail", "");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool is_sch_vfio(char *path)
|
||||
{
|
||||
char lnk[PATH_MAX], driver_path[PATH_MAX];
|
||||
ssize_t rc;
|
||||
|
||||
snprintf(lnk, PATH_MAX, "%s/driver", path);
|
||||
rc = readlink(lnk, driver_path, PATH_MAX);
|
||||
if (rc < 0)
|
||||
return false;
|
||||
|
||||
util_assert(rc < (PATH_MAX - 1),
|
||||
"Internal error: Symlink name too long");
|
||||
driver_path[rc] = '\0';
|
||||
|
||||
if (strcmp(basename(driver_path), "vfio_ccw") == 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill in the MDEV id entry field
|
||||
*
|
||||
* @returns 0 - MDEV id information filled in
|
||||
* 1 - skip entry
|
||||
*/
|
||||
static int fill_vfio_devid(struct util_rec *rec, char *path)
|
||||
{
|
||||
char *device, buf[MAX_BUFFER_SIZE_FOR_SUBCHANNEL_ATTRIBUTES];
|
||||
struct dirent **de_vec;
|
||||
int count;
|
||||
|
||||
if (!is_sch_vfio(path))
|
||||
return 1;
|
||||
|
||||
/* Find and process mdev device directory */
|
||||
count = util_scandir(&de_vec, alphasort, path, "%s", UUID_FORMAT);
|
||||
if (count > 0) {
|
||||
device = de_vec[0]->d_name;
|
||||
snprintf(buf, sizeof(buf), "%s", device);
|
||||
} else {
|
||||
strncpy(buf, "none", sizeof(buf));
|
||||
}
|
||||
if (cmd.opt_uppercase)
|
||||
util_str_toupper(buf);
|
||||
util_rec_set(rec, "mdev", "%s", buf);
|
||||
util_scandir_free(de_vec, count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill in the CCW device id entry field
|
||||
*
|
||||
* @returns 0 - CCW device id information filled in
|
||||
* 1 - skip entry
|
||||
*/
|
||||
static int fill_io_devid(struct util_rec *rec, char *path)
|
||||
{
|
||||
char *device, buf[MAX_BUFFER_SIZE_FOR_SUBCHANNEL_ATTRIBUTES];
|
||||
struct dirent **de_vec;
|
||||
int count;
|
||||
|
||||
/* Find and process device directory */
|
||||
count = util_scandir(&de_vec, alphasort, path, "%s", ID_FORMAT);
|
||||
if (count > 0) {
|
||||
device = de_vec[0]->d_name;
|
||||
if (cmd.opt_short) {
|
||||
/* Display only 0.0.xxxx devices for --short */
|
||||
if (strncmp(device, "0.0.", PREFIX_ID_LENGTH) != 0)
|
||||
return 1;
|
||||
snprintf(buf, sizeof(buf), "%s", device +
|
||||
strlen(device) - SHORT_ID_LENGTH);
|
||||
} else {
|
||||
snprintf(buf, sizeof(buf), "%s", device);
|
||||
}
|
||||
if (cmd.opt_devrange && cmd.rng_count > 0 &&
|
||||
!id_in_ranges_list(device)) {
|
||||
util_scandir_free(de_vec, count);
|
||||
return 1;
|
||||
}
|
||||
if (fill_device_info(rec, path, device) != 0) {
|
||||
util_scandir_free(de_vec, count);
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
if (cmd.opt_devrange && cmd.rng_count > 0) {
|
||||
util_scandir_free(de_vec, count);
|
||||
return 1;
|
||||
}
|
||||
strncpy(buf, "none", sizeof(buf));
|
||||
fill_device_info(rec, NULL, NULL);
|
||||
}
|
||||
if (cmd.opt_uppercase)
|
||||
util_str_toupper(buf);
|
||||
util_rec_set(rec, "device", "%s", buf);
|
||||
util_scandir_free(de_vec, count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Print IO subchannel entry
|
||||
*/
|
||||
static void print_sch_io(struct util_rec *rec, char *path, char *sch_dir)
|
||||
{
|
||||
char buf[MAX_BUFFER_SIZE_FOR_SUBCHANNEL_ATTRIBUTES];
|
||||
unsigned int pim, pam, pom;
|
||||
|
||||
/* Fill in subchannel ID */
|
||||
if (cmd.opt_short) {
|
||||
/* Display only 0.0.xxxx subchannels for --short */
|
||||
if (strncmp(sch_dir, "0.0.", PREFIX_ID_LENGTH) != 0)
|
||||
return;
|
||||
snprintf(buf, sizeof(buf), "%s", sch_dir +
|
||||
strlen(sch_dir) - SHORT_ID_LENGTH);
|
||||
} else {
|
||||
snprintf(buf, sizeof(buf), "%s", sch_dir);
|
||||
}
|
||||
if (!cmd.opt_devrange && cmd.rng_count > 0 &&
|
||||
!id_in_ranges_list(sch_dir))
|
||||
return;
|
||||
if (cmd.opt_uppercase)
|
||||
util_str_toupper(buf);
|
||||
util_rec_set(rec, "subch", "%s", buf);
|
||||
if (cmd.opt_vfio) {
|
||||
if (fill_vfio_devid(rec, path) != 0)
|
||||
return;
|
||||
} else if (fill_io_devid(rec, path) != 0)
|
||||
return;
|
||||
/* Fill in PIM-PAM-POM data */
|
||||
if (util_file_read_line(buf, sizeof(buf), "%s/pimpampom", path) == 0) {
|
||||
if (sscanf(buf, "%x %x %x", &pim, &pam, &pom) == 3) {
|
||||
if (cmd.opt_uppercase) {
|
||||
util_rec_set(rec, "pim", "%02X", pim);
|
||||
util_rec_set(rec, "pam", "%02X", pam);
|
||||
util_rec_set(rec, "pom", "%02X", pom);
|
||||
} else {
|
||||
util_rec_set(rec, "pim", "%02x", pim);
|
||||
util_rec_set(rec, "pam", "%02x", pam);
|
||||
util_rec_set(rec, "pom", "%02x", pom);
|
||||
}
|
||||
} else {
|
||||
util_rec_set(rec, "pim", "");
|
||||
util_rec_set(rec, "pam", "");
|
||||
util_rec_set(rec, "pom", "");
|
||||
}
|
||||
} else {
|
||||
util_rec_set(rec, "pim", "");
|
||||
util_rec_set(rec, "pam", "");
|
||||
util_rec_set(rec, "pom", "");
|
||||
}
|
||||
/* Fill in VPM data */
|
||||
if (cmd.opt_vpm) {
|
||||
if (util_file_read_line(buf, sizeof(buf),
|
||||
"%s/vpm", path) == 0) {
|
||||
if (cmd.opt_uppercase)
|
||||
util_str_toupper(buf);
|
||||
util_rec_set(rec, "vpm", "%s", buf);
|
||||
} else {
|
||||
util_rec_set(rec, "vpm", "");
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Fill in CHPIDs data.
|
||||
* Since chpids are stored as a list of two digit hexadecimal numbers,
|
||||
* we first read it as a single string, then eliminate blanks from it
|
||||
* and then break in two 8-char segments.
|
||||
*/
|
||||
if (util_file_read_line(buf, sizeof(buf), "%s/chpids", path) == 0) {
|
||||
misc_str_remove_symbol(buf, ' ');
|
||||
if (cmd.opt_uppercase)
|
||||
util_str_toupper(buf);
|
||||
util_rec_set(rec, "chpids", "%.8s %.8s", buf,
|
||||
buf + CHPIDS_SEGMENT_LENGTH);
|
||||
} else {
|
||||
util_rec_set(rec, "chpids", "");
|
||||
}
|
||||
|
||||
util_rec_print(rec);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print CHSC subchannel entry
|
||||
*/
|
||||
static void print_sch_chsc(struct util_rec *rec, char *sch_dir)
|
||||
{
|
||||
char buf[MAX_BUFFER_SIZE_FOR_DEVICE_ATTRIBUTES];
|
||||
|
||||
/* Skip entry if devrange or devtype option is active */
|
||||
if (cmd.opt_devrange && cmd.rng_count > 0)
|
||||
return;
|
||||
if (cmd.opt_devtype && cmd.dev_count > 0)
|
||||
return;
|
||||
/* Fill in subchannel ID */
|
||||
if (cmd.opt_short) {
|
||||
/* Display only 0.0.xxxx subchannels for --short */
|
||||
if (strncmp(sch_dir, "0.0.", PREFIX_ID_LENGTH) != 0)
|
||||
return;
|
||||
snprintf(buf, sizeof(buf), "%s", sch_dir +
|
||||
strlen(sch_dir) - SHORT_ID_LENGTH);
|
||||
} else {
|
||||
snprintf(buf, sizeof(buf), "%s", sch_dir);
|
||||
}
|
||||
if (cmd.rng_count > 0 && !id_in_ranges_list(sch_dir))
|
||||
return;
|
||||
if (cmd.opt_uppercase)
|
||||
util_str_toupper(buf);
|
||||
util_rec_set(rec, "subch", "%s", buf);
|
||||
/* Device field is always 'n/a' for CHSC */
|
||||
strncpy(buf, "n/a", sizeof(buf));
|
||||
if (cmd.opt_uppercase)
|
||||
util_str_toupper(buf);
|
||||
util_rec_set(rec, "device", "%s", buf);
|
||||
|
||||
util_rec_print(rec);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print EADM subchannel entry
|
||||
*/
|
||||
static void print_sch_eadm(struct util_rec *rec, char *sch_dir)
|
||||
{
|
||||
char buf[MAX_BUFFER_SIZE_FOR_DEVICE_ATTRIBUTES];
|
||||
|
||||
/* Skip entry if devrange or devtype option is active */
|
||||
if (cmd.opt_devrange && cmd.rng_count > 0)
|
||||
return;
|
||||
if (cmd.opt_devtype && cmd.dev_count > 0)
|
||||
return;
|
||||
/* Fill in subchannel ID */
|
||||
if (cmd.opt_short) {
|
||||
/* Display only 0.0.xxxx subchannels for --short */
|
||||
if (strncmp(sch_dir, "0.0.", PREFIX_ID_LENGTH) != 0)
|
||||
return;
|
||||
snprintf(buf, sizeof(buf), "%s", sch_dir +
|
||||
strlen(sch_dir) - SHORT_ID_LENGTH);
|
||||
} else {
|
||||
snprintf(buf, sizeof(buf), "%s", sch_dir);
|
||||
}
|
||||
if (cmd.rng_count > 0 && !id_in_ranges_list(sch_dir))
|
||||
return;
|
||||
if (cmd.opt_uppercase)
|
||||
util_str_toupper(buf);
|
||||
util_rec_set(rec, "subch", "%s", buf);
|
||||
/* Device field is always 'n/a' for EADM */
|
||||
strncpy(buf, "n/a", sizeof(buf));
|
||||
if (cmd.opt_uppercase)
|
||||
util_str_toupper(buf);
|
||||
util_rec_set(rec, "device", "%s", buf);
|
||||
|
||||
util_rec_print(rec);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print information for all defunct devices in one css
|
||||
*/
|
||||
static void print_defunct_devices(struct util_rec *rec, char *path)
|
||||
{
|
||||
char *device, buf[MAX_BUFFER_SIZE_FOR_DEVICE_ATTRIBUTES];
|
||||
struct dirent **de_vec;
|
||||
int i, count;
|
||||
|
||||
/* Process all the devices within defunct directory */
|
||||
count = util_scandir(&de_vec, alphasort, path, "%s", ID_FORMAT);
|
||||
for (i = 0; i < count; i++) {
|
||||
device = de_vec[i]->d_name;
|
||||
if (cmd.opt_short) {
|
||||
/* Display only 0.0.xxxx devices for --short */
|
||||
if (strncmp(device, "0.0.", PREFIX_ID_LENGTH) != 0)
|
||||
return;
|
||||
snprintf(buf, sizeof(buf), "%s", device +
|
||||
strlen(device) - SHORT_ID_LENGTH);
|
||||
} else {
|
||||
snprintf(buf, sizeof(buf), "%s", device);
|
||||
}
|
||||
if (cmd.opt_devrange && cmd.rng_count > 0 &&
|
||||
!id_in_ranges_list(device)) {
|
||||
util_scandir_free(de_vec, count);
|
||||
continue;
|
||||
}
|
||||
if (fill_device_info(rec, path, device) != 0) {
|
||||
util_scandir_free(de_vec, count);
|
||||
continue;
|
||||
}
|
||||
if (cmd.opt_uppercase)
|
||||
util_str_toupper(buf);
|
||||
util_rec_set(rec, "device", "%s", buf);
|
||||
/* Subchannel field is always 'n/a' for defunct devices */
|
||||
strncpy(buf, "n/a", sizeof(buf));
|
||||
if (cmd.opt_uppercase)
|
||||
util_str_toupper(buf);
|
||||
util_rec_set(rec, "subch", "%s", buf);
|
||||
/* Other fields are blank */
|
||||
util_rec_set(rec, "pim", "");
|
||||
util_rec_set(rec, "pam", "");
|
||||
util_rec_set(rec, "pom", "");
|
||||
if (cmd.opt_vpm)
|
||||
util_rec_set(rec, "vpm", "");
|
||||
util_rec_set(rec, "chpids", "");
|
||||
|
||||
util_rec_print(rec);
|
||||
}
|
||||
util_scandir_free(de_vec, count);
|
||||
}
|
||||
|
||||
/*
|
||||
* Loop through subchannel directories and print entries of specified type
|
||||
*/
|
||||
static void print_subchannels_of_type(enum sch_type type_requested,
|
||||
struct util_rec *rec)
|
||||
{
|
||||
unsigned long int type_ul;
|
||||
struct dirent **de_vec;
|
||||
char *path, *sch_dir;
|
||||
int i, count;
|
||||
|
||||
path = util_path_sysfs("bus/css/devices");
|
||||
count = util_scandir(&de_vec, alphasort, path, "%s", ID_FORMAT);
|
||||
free(path);
|
||||
for (i = 0; i < count; i++) {
|
||||
sch_dir = de_vec[i]->d_name;
|
||||
path = util_path_sysfs("bus/css/devices/%s", sch_dir);
|
||||
if (util_file_read_ul(&type_ul, 10, "%s/type", path) == 0) {
|
||||
if (type_ul != type_requested)
|
||||
continue;
|
||||
if (type_ul == SUBCHANNEL_TYPE_IO)
|
||||
print_sch_io(rec, path, sch_dir);
|
||||
else if (type_ul == SUBCHANNEL_TYPE_CHSC)
|
||||
print_sch_chsc(rec, sch_dir);
|
||||
else if (type_ul == SUBCHANNEL_TYPE_EADM)
|
||||
print_sch_eadm(rec, sch_dir);
|
||||
} else {
|
||||
/*
|
||||
* Subchannels with no type identifier treated as
|
||||
* IO subchannels
|
||||
*/
|
||||
if (type_requested == SUBCHANNEL_TYPE_IO)
|
||||
print_sch_io(rec, path, sch_dir);
|
||||
}
|
||||
free(path);
|
||||
}
|
||||
util_scandir_free(de_vec, count);
|
||||
/* Process defunct devices (if no subchannel range is specified) */
|
||||
if (!cmd.opt_devrange && cmd.rng_count > 0)
|
||||
return;
|
||||
path = util_path_sysfs("devices");
|
||||
count = util_scandir(&de_vec, alphasort, path, "css.*/");
|
||||
free(path);
|
||||
for (i = 0; i < count; i++) {
|
||||
path = util_path_sysfs("devices/%s/defunct", de_vec[i]->d_name);
|
||||
print_defunct_devices(rec, path);
|
||||
free(path);
|
||||
}
|
||||
util_scandir_free(de_vec, count);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print subchannels table
|
||||
*/
|
||||
static void cmd_lscss(void)
|
||||
{
|
||||
struct util_rec *rec;
|
||||
|
||||
if (cmd.opt_io) {
|
||||
rec = util_rec_new_wide("-");
|
||||
util_rec_def(rec, "device", UTIL_REC_ALIGN_LEFT, 8, "Device");
|
||||
util_rec_def(rec, "subch", UTIL_REC_ALIGN_LEFT, 9, "Subchan.");
|
||||
util_rec_def(rec, "devtyp", UTIL_REC_ALIGN_LEFT, 7, "DevType");
|
||||
util_rec_def(rec, "cutype", UTIL_REC_ALIGN_LEFT, 7, "CU Type");
|
||||
util_rec_def(rec, "use", UTIL_REC_ALIGN_LEFT, 4, "Use");
|
||||
util_rec_def(rec, "pim", UTIL_REC_ALIGN_LEFT, 3, "PIM");
|
||||
util_rec_def(rec, "pam", UTIL_REC_ALIGN_LEFT, 3, "PAM");
|
||||
if (cmd.opt_vpm) {
|
||||
util_rec_def(rec, "pom", UTIL_REC_ALIGN_LEFT, 3, "POM");
|
||||
util_rec_def(rec, "vpm", UTIL_REC_ALIGN_LEFT, 3, "VPM");
|
||||
} else
|
||||
util_rec_def(rec, "pom", UTIL_REC_ALIGN_LEFT, 4, "POM");
|
||||
util_rec_def(rec, "chpids", UTIL_REC_ALIGN_LEFT, 17, "CHPIDs");
|
||||
if (cmd.opt_avail)
|
||||
util_rec_def(rec, "avail", UTIL_REC_ALIGN_LEFT, 6,
|
||||
"Avail.");
|
||||
/* Print header only if other subchannel types are requested */
|
||||
if (cmd.opt_chsc || cmd.opt_eadm || cmd.opt_vfio)
|
||||
printf("IO Subchannels and Devices:\n");
|
||||
util_rec_print_hdr(rec);
|
||||
print_subchannels_of_type(SUBCHANNEL_TYPE_IO, rec);
|
||||
util_rec_free(rec);
|
||||
}
|
||||
|
||||
if (cmd.opt_chsc) {
|
||||
rec = util_rec_new_wide("-");
|
||||
util_rec_def(rec, "device", UTIL_REC_ALIGN_LEFT, 8, "Device");
|
||||
util_rec_def(rec, "subch", UTIL_REC_ALIGN_LEFT, 9, "Subchan.");
|
||||
/* Print header only if other subchannel types also requested */
|
||||
if (cmd.opt_io || cmd.opt_eadm || cmd.opt_vfio)
|
||||
printf("\nCHSC Subchannels:\n");
|
||||
util_rec_print_hdr(rec);
|
||||
print_subchannels_of_type(SUBCHANNEL_TYPE_CHSC, rec);
|
||||
util_rec_free(rec);
|
||||
}
|
||||
|
||||
if (cmd.opt_eadm) {
|
||||
rec = util_rec_new_wide("-");
|
||||
util_rec_def(rec, "device", UTIL_REC_ALIGN_LEFT, 8, "Device");
|
||||
util_rec_def(rec, "subch", UTIL_REC_ALIGN_LEFT, 9, "Subchan.");
|
||||
/* Print header only if other subchannel types also requested */
|
||||
if (cmd.opt_chsc || cmd.opt_io || cmd.opt_vfio)
|
||||
printf("\nEADM Subchannels:\n");
|
||||
util_rec_print_hdr(rec);
|
||||
print_subchannels_of_type(SUBCHANNEL_TYPE_EADM, rec);
|
||||
util_rec_free(rec);
|
||||
}
|
||||
|
||||
if (cmd.opt_vfio) {
|
||||
rec = util_rec_new_wide("-");
|
||||
util_rec_def(rec, "mdev", UTIL_REC_ALIGN_LEFT, 37, "MDEV");
|
||||
util_rec_def(rec, "subch", UTIL_REC_ALIGN_LEFT, 9, "Subchan.");
|
||||
util_rec_def(rec, "pim", UTIL_REC_ALIGN_LEFT, 3, "PIM");
|
||||
util_rec_def(rec, "pam", UTIL_REC_ALIGN_LEFT, 3, "PAM");
|
||||
if (cmd.opt_vpm) {
|
||||
util_rec_def(rec, "pom", UTIL_REC_ALIGN_LEFT, 3, "POM");
|
||||
util_rec_def(rec, "vpm", UTIL_REC_ALIGN_LEFT, 3, "VPM");
|
||||
} else
|
||||
util_rec_def(rec, "pom", UTIL_REC_ALIGN_LEFT, 4, "POM");
|
||||
util_rec_def(rec, "chpids", UTIL_REC_ALIGN_LEFT, 17, "CHPIDs");
|
||||
/* Print header only if other subchannel types are requested */
|
||||
if (cmd.opt_io || cmd.opt_chsc || cmd.opt_eadm)
|
||||
printf("\nI/O Subchannels used for VFIO:\n");
|
||||
util_rec_print_hdr(rec);
|
||||
print_subchannels_of_type(SUBCHANNEL_TYPE_IO, rec);
|
||||
util_rec_free(rec);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse options and execute the command
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char *id_from, *id_to, *id_list = NULL;
|
||||
char *dtype, *dtype_list = NULL;
|
||||
int c, i;
|
||||
|
||||
util_prg_init(&prg);
|
||||
util_opt_init(opt_vec, NULL);
|
||||
|
||||
while (1) {
|
||||
c = util_opt_getopt_long(argc, argv);
|
||||
if (c == -1)
|
||||
break;
|
||||
switch (c) {
|
||||
case 'h':
|
||||
util_prg_print_help();
|
||||
util_opt_print_help();
|
||||
return EXIT_SUCCESS;
|
||||
case 'v':
|
||||
util_prg_print_version();
|
||||
return EXIT_SUCCESS;
|
||||
case 's':
|
||||
cmd.opt_short = true;
|
||||
break;
|
||||
case 't':
|
||||
cmd.opt_devtype = true;
|
||||
dtype_list = optarg;
|
||||
break;
|
||||
case 'd':
|
||||
cmd.opt_devrange = true;
|
||||
break;
|
||||
case OPT_AVAIL:
|
||||
cmd.opt_avail = true;
|
||||
break;
|
||||
case OPT_VPM:
|
||||
cmd.opt_vpm = true;
|
||||
break;
|
||||
case 'u':
|
||||
cmd.opt_uppercase = true;
|
||||
break;
|
||||
case OPT_IO:
|
||||
cmd.opt_io = true;
|
||||
break;
|
||||
case OPT_CHSC:
|
||||
cmd.opt_chsc = true;
|
||||
break;
|
||||
case OPT_EADM:
|
||||
cmd.opt_eadm = true;
|
||||
break;
|
||||
case OPT_VFIO:
|
||||
cmd.opt_vfio = true;
|
||||
break;
|
||||
case 'a':
|
||||
cmd.opt_io = true;
|
||||
cmd.opt_chsc = true;
|
||||
cmd.opt_eadm = true;
|
||||
break;
|
||||
default:
|
||||
util_opt_print_parse_error(c, argv);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* VFIO subchannel view and IO subchannel view are mutual exclusion.
|
||||
* And it does not make sense to use the -d, -t, and --avail options
|
||||
* together with --vfio.
|
||||
*/
|
||||
if (cmd.opt_vfio &&
|
||||
(cmd.opt_io || cmd.opt_devrange ||
|
||||
cmd.opt_devtype || cmd.opt_avail)) {
|
||||
errx(EXIT_FAILURE, "Invalid option combination: "
|
||||
"--vfio can not be used with --io, -d, -t or --avail");
|
||||
}
|
||||
/* Display IO subchannels by default */
|
||||
if (!cmd.opt_chsc && !cmd.opt_eadm && !cmd.opt_vfio)
|
||||
cmd.opt_io = true;
|
||||
|
||||
/* Process the list of specified device types */
|
||||
if (dtype_list != NULL) {
|
||||
cmd.devtypes = util_list_new(struct devtype, node);
|
||||
cmd.dev_count = 0;
|
||||
/* Loop over comma-separated list */
|
||||
dtype = strtok(dtype_list, ",");
|
||||
while (dtype != NULL) {
|
||||
add_new_devtype(dtype);
|
||||
cmd.dev_count++;
|
||||
dtype = strtok(NULL, ",");
|
||||
}
|
||||
}
|
||||
|
||||
/* Scan RANGE parameters appending each argument with comma */
|
||||
for (i = optind; i < argc; i++) {
|
||||
if (i > optind)
|
||||
id_list = util_strcat_realloc(id_list, ",");
|
||||
id_list = util_strcat_realloc(id_list, argv[i]);
|
||||
}
|
||||
|
||||
/* Process the list of specified ranges */
|
||||
if (id_list != NULL) {
|
||||
cmd.ranges = util_list_new(struct range, node);
|
||||
cmd.rng_count = 0;
|
||||
/* Loop over comma-separated list */
|
||||
id_from = strtok(id_list, ",");
|
||||
while (id_from != NULL) {
|
||||
id_to = strchr(id_from, '-');
|
||||
if (id_to == NULL)
|
||||
id_to = id_from;
|
||||
else
|
||||
*id_to++ = '\0';
|
||||
if (*id_to == '\0')
|
||||
errx(EXIT_FAILURE, "Invalid ID specified: %s", id_from);
|
||||
add_new_range(id_from, id_to);
|
||||
cmd.rng_count++;
|
||||
id_from = strtok(NULL, ",");
|
||||
}
|
||||
free(id_list);
|
||||
}
|
||||
|
||||
/* Process lscss command with provided options and attributes */
|
||||
cmd_lscss();
|
||||
util_list_free(cmd.devtypes);
|
||||
util_list_free(cmd.ranges);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Misc - Local helper functions
|
||||
*
|
||||
* Copyright 2017 IBM Corp.
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Eliminate all the occurrences of the specified character in the string
|
||||
*
|
||||
* @param[in,out] str String to process
|
||||
* @param[in] symbol Character to be scanned for removal
|
||||
*
|
||||
*/
|
||||
void misc_str_remove_symbol(char *str, char symbol)
|
||||
{
|
||||
char *source = str;
|
||||
int i, j = 0;
|
||||
|
||||
for (i = 0; source[i] != '\0'; i++) {
|
||||
if (source[i] != symbol) {
|
||||
str[j] = source[i];
|
||||
j++;
|
||||
}
|
||||
}
|
||||
str[j] = '\0';
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Misc - Local helper functions
|
||||
*
|
||||
* Copyright 2017 IBM Corp.
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef MISC_H
|
||||
#define MISC_H
|
||||
|
||||
void misc_str_remove_symbol(char *str, char symbol);
|
||||
|
||||
#endif /* MISC_H */
|
||||
Executable
+466
@@ -0,0 +1,466 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# dasdstat - Tool to print DASD statistics data as formatted table
|
||||
#
|
||||
# Copyright IBM Corp. 2011, 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.
|
||||
#
|
||||
|
||||
function print_usage() {
|
||||
cat <<-EOD
|
||||
Usage: $CMD <options> [<statistic>]
|
||||
|
||||
<options> ::=
|
||||
-e|--enable
|
||||
Enable the statistics.
|
||||
-d|--disable
|
||||
Disable the statistics.
|
||||
-r|--reset
|
||||
Reset the statistics.
|
||||
-i|--directory <directory>
|
||||
Specify the directory in which the statistics can be found.
|
||||
-h|--help
|
||||
Print this text and exit.
|
||||
-l|--long
|
||||
Print more detailed information, e.g differentiate between
|
||||
read and write requests.
|
||||
-c|--columns <number>
|
||||
Format the output in a table with the given number of columns.
|
||||
-w|--column-width <width>
|
||||
Set the minimum width of the columns in the output table.
|
||||
-V|--verbose
|
||||
Print more verbose information.
|
||||
-v|--version
|
||||
Show tools and command version.
|
||||
|
||||
<statistic>
|
||||
Limit operation to one or more statistic.
|
||||
EOD
|
||||
}
|
||||
|
||||
function print_version()
|
||||
{
|
||||
echo "$CMD: version %S390_TOOLS_VERSION%"
|
||||
echo "Copyright IBM Corp. 2011, 2017"
|
||||
}
|
||||
|
||||
function unset_known_variables() {
|
||||
unset start_time
|
||||
unset total_requests
|
||||
unset total_sectors
|
||||
unset total_pav
|
||||
unset total_hpf
|
||||
unset histogram_sectors
|
||||
unset histogram_io_times
|
||||
unset histogram_io_times_weighted
|
||||
unset histogram_time_build_to_ssch
|
||||
unset histogram_time_ssch_to_irq
|
||||
unset histogram_time_ssch_to_irq_weighted
|
||||
unset histogram_time_irq_to_end
|
||||
unset histogram_ccw_queue_length
|
||||
unset total_read_requests
|
||||
unset total_read_sectors
|
||||
unset total_read_pav
|
||||
unset total_read_hpf
|
||||
unset histogram_read_sectors
|
||||
unset histogram_read_times
|
||||
unset histogram_read_time_build_to_ssch
|
||||
unset histogram_read_time_ssch_to_irq
|
||||
unset histogram_read_time_irq_to_end
|
||||
unset histogram_read_ccw_queue_length
|
||||
}
|
||||
|
||||
function print_array()
|
||||
{
|
||||
local width=$1
|
||||
shift
|
||||
local linebreak=$1
|
||||
shift
|
||||
local i=1
|
||||
for element in $*
|
||||
do
|
||||
printf "%${width}s" $element
|
||||
(( 0 == i++ % linebreak )) && printf "\n"
|
||||
done
|
||||
# add an extra line break if we do not already have one
|
||||
(( 0 != (i - 1) % linebreak )) && printf "\n"
|
||||
}
|
||||
|
||||
function subtract_array()
|
||||
{
|
||||
local -a a=( $1 )
|
||||
local -a b=( $2 )
|
||||
local -a c
|
||||
local i
|
||||
local cnt=${#a[@]}
|
||||
for (( i = 0 ; i < cnt ; i++ ))
|
||||
do
|
||||
(( c[i] = a[i] - b[i] ))
|
||||
done
|
||||
echo -n ${c[*]}
|
||||
}
|
||||
|
||||
function print_line()
|
||||
{
|
||||
local i
|
||||
for (( i = 0 ; i < $1 ; i++ ))
|
||||
do
|
||||
echo -n '-'
|
||||
done
|
||||
echo
|
||||
}
|
||||
|
||||
HEADERSEXP=" __<4 ___8 __16 __32 __64 _128 _256 _512 \
|
||||
__1k __2k __4k __8k _16k _32k _64k 128k \
|
||||
_256 _512 __1M __2M __4M __8M _16M _32M \
|
||||
_64M 128M 256M 512M __1G __2G __4G _>4G"
|
||||
|
||||
HEADERSLIN=" ___0 ___1 ___2 ___3 ___4 ___5 ___6 ___7 \
|
||||
___8 ___9 __10 __11 __12 __13 __14 __15 \
|
||||
__16 __17 __18 __19 __20 __21 __22 __23 \
|
||||
__24 __25 __26 __27 __28 __29 __30 __31"
|
||||
|
||||
|
||||
function print_format_standard()
|
||||
{
|
||||
local converted_time=$(date -d @$start_time)
|
||||
# all numbers in the histograms below are smaller or equal to the
|
||||
# total_requests. So we use that number to determine the column width.
|
||||
local width=${#total_requests}
|
||||
(( width++ ))
|
||||
(( width < 5 )) && width=5
|
||||
|
||||
if [[ -n $COLUMN_WIDTH ]] && [[ $width -lt $COLUMN_WIDTH ]]
|
||||
then
|
||||
width=$COLUMN_WIDTH
|
||||
fi
|
||||
|
||||
local linebreak=$NUMBER_COLUMNS
|
||||
local statname="$1"
|
||||
local tablewidth
|
||||
(( tablewidth = width * linebreak ))
|
||||
|
||||
# Note: This function does not print the histogram_io_times_weighted
|
||||
# and histogram_time_ssch_to_irq_weighted because the interpretation
|
||||
# of these histograms is not intuitive and may lead to confusion.
|
||||
print_line $tablewidth
|
||||
printf "statistics data for statistic: %s\n" "$statname"
|
||||
printf "start time of data collection: %s\n\n" "$converted_time"
|
||||
|
||||
printf "%d dasd I/O requests\n" ${total_requests[*]}
|
||||
printf "with %u sectors(512B each)\n" ${total_sectors[*]}
|
||||
printf "%d requests used a PAV alias device\n" ${total_pav[*]}
|
||||
printf "%d requests used HPF\n" ${total_hpf[*]}
|
||||
print_array $width $linebreak $HEADERSEXP
|
||||
printf "Histogram of sizes (512B secs)\n"
|
||||
print_array $width $linebreak ${histogram_sectors[*]}
|
||||
printf "Histogram of I/O times (microseconds)\n"
|
||||
print_array $width $linebreak ${histogram_io_times[*]}
|
||||
printf "Histogram of I/O time till ssch\n"
|
||||
print_array $width $linebreak ${histogram_time_build_to_ssch[*]}
|
||||
printf "Histogram of I/O time between ssch and irq\n"
|
||||
print_array $width $linebreak ${histogram_time_ssch_to_irq[*]}
|
||||
printf "Histogram of I/O time between irq and end\n"
|
||||
print_array $width $linebreak ${histogram_time_irq_to_end[*]}
|
||||
printf "# of req in chanq at enqueuing (0..31) \n"
|
||||
print_array $width $linebreak $HEADERSLIN
|
||||
print_array $width $linebreak ${histogram_ccw_queue_length[*]}
|
||||
|
||||
if [[ $OUTPUT == "short" ]]
|
||||
then
|
||||
print_line $tablewidth
|
||||
return
|
||||
fi
|
||||
|
||||
printf "\n%d dasd I/O read requests\n" ${total_read_requests[*]}
|
||||
printf "with %u sectors(512B each)\n" ${total_read_sectors[*]}
|
||||
printf "%d requests used a PAV alias device\n" ${total_read_pav[*]}
|
||||
printf "%d requests used HPF\n" ${total_read_hpf[*]}
|
||||
print_array $width $linebreak $HEADERSEXP
|
||||
printf "Histogram of sizes (512B secs)\n"
|
||||
print_array $width $linebreak ${histogram_read_sectors[*]}
|
||||
printf "Histogram of I/O times (microseconds)\n"
|
||||
print_array $width $linebreak ${histogram_read_times[*]}
|
||||
printf "Histogram of I/O time till ssch\n"
|
||||
print_array $width $linebreak ${histogram_read_time_build_to_ssch[*]}
|
||||
printf "Histogram of I/O time between ssch and irq\n"
|
||||
print_array $width $linebreak ${histogram_read_time_ssch_to_irq[*]}
|
||||
printf "Histogram of I/O time between irq and end\n"
|
||||
print_array $width $linebreak ${histogram_read_time_irq_to_end[*]}
|
||||
printf "# of req in chanq at enqueuing (0..31) \n"
|
||||
print_array $width $linebreak $HEADERSLIN
|
||||
print_array $width $linebreak ${histogram_read_ccw_queue_length[*]}
|
||||
|
||||
printf "\n%d dasd I/O write requests\n" $(( total_requests[0] - total_read_requests[0] ))
|
||||
printf "with %u sectors(512B each)\n" $(( total_sectors[0] - total_read_sectors[0] ))
|
||||
printf "%d requests used a PAV alias device\n" $(( total_pav[0] - total_read_pav[0] ))
|
||||
printf "%d requests used HPF\n" $(( total_hpf[0] - total_read_hpf[0] ))
|
||||
print_array $width $linebreak $HEADERSEXP
|
||||
printf "Histogram of sizes (512B secs)\n"
|
||||
print_array $width $linebreak $(subtract_array "${histogram_sectors[*]}" "${histogram_read_sectors[*]}")
|
||||
printf "Histogram of I/O times (microseconds)\n"
|
||||
print_array $width $linebreak $(subtract_array "${histogram_io_times[*]}" "${histogram_read_times[*]}")
|
||||
printf "Histogram of I/O time till ssch\n"
|
||||
print_array $width $linebreak $(subtract_array "${histogram_time_build_to_ssch[*]}" "${histogram_read_time_build_to_ssch[*]}")
|
||||
printf "Histogram of I/O time between ssch and irq\n"
|
||||
print_array $width $linebreak $(subtract_array "${histogram_time_ssch_to_irq[*]}" "${histogram_read_time_ssch_to_irq[*]}")
|
||||
printf "Histogram of I/O time between irq and end\n"
|
||||
print_array $width $linebreak $(subtract_array "${histogram_time_irq_to_end[*]}" "${histogram_read_time_irq_to_end[*]}")
|
||||
printf "# of req in chanq at enqueuing (0..31) \n"
|
||||
print_array $width $linebreak $HEADERSLIN
|
||||
print_array $width $linebreak $(subtract_array "${histogram_ccw_queue_length[*]}" "${histogram_read_ccw_queue_length[*]}")
|
||||
|
||||
print_line $tablewidth
|
||||
}
|
||||
|
||||
function read_stat_data() {
|
||||
local file="$1"
|
||||
|
||||
while read -d ' ' token
|
||||
do
|
||||
read -a $token
|
||||
done < <(cat $file) #avoid inconsistent data due to multiple reads/seeks
|
||||
|
||||
# differentiate I/O error from disabled statistic
|
||||
if [[ -n $total_requests ]] || [[ "$token" == "disabled" ]]
|
||||
then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
function print_stat() {
|
||||
myfile="$1"
|
||||
if [[ ! -f "$myfile" ]]
|
||||
then
|
||||
print_line 80
|
||||
echo "$CMD: Statistic \"$myfile\" does not exist" >&2
|
||||
print_line 80
|
||||
echo
|
||||
return
|
||||
fi
|
||||
unset_known_variables
|
||||
if read_stat_data "$myfile"
|
||||
then
|
||||
if [[ -z "$total_requests" ]]
|
||||
then
|
||||
if [[ "$VERBOSE" == "true" ]]
|
||||
then
|
||||
print_line 80
|
||||
echo "statistics \"$f\" are disabled"
|
||||
print_line 80
|
||||
echo
|
||||
fi
|
||||
else
|
||||
print_format_standard $f
|
||||
echo
|
||||
fi
|
||||
else
|
||||
print_line 80
|
||||
echo "$CMD: Could not read statistic \"$myfile\" " >&2
|
||||
print_line 80
|
||||
echo
|
||||
fi
|
||||
}
|
||||
|
||||
function enable_stat() {
|
||||
myfile="$1"
|
||||
if [[ ! -f "$myfile" ]]
|
||||
then
|
||||
echo "$CMD: Statistic \"$myfile\" does not exist" >&2
|
||||
return
|
||||
fi
|
||||
if echo on > "$myfile"
|
||||
then
|
||||
echo "enable statistic \"$myfile\""
|
||||
else
|
||||
echo "$CMD: Failed to enable statistic \"$myfile\"" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
function disable_stat() {
|
||||
myfile="$1"
|
||||
if [[ ! -f "$myfile" ]]
|
||||
then
|
||||
echo "$CMD: Statistic \"$myfile\" does not exist" >&2
|
||||
return
|
||||
fi
|
||||
if echo off > "$myfile"
|
||||
then
|
||||
echo "disable statistic \"$myfile\""
|
||||
else
|
||||
echo "$CMD: Failed to disable statistic \"$myfile\""
|
||||
fi
|
||||
}
|
||||
|
||||
function reset_stat() {
|
||||
myfile="$1"
|
||||
if [[ ! -f "$myfile" ]]
|
||||
then
|
||||
echo "$CMD: Statistic \"$myfile\" does not exist" >&2
|
||||
return
|
||||
fi
|
||||
if echo reset > "$myfile"
|
||||
then
|
||||
echo "reset statistic \"$myfile\""
|
||||
else
|
||||
echo "$CMD: Failed to reset statistic \"$myfile\"" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
function verbose_msg() {
|
||||
if [[ "$VERBOSE" == "true" ]]
|
||||
then
|
||||
echo "$*"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# Evaluating command line options
|
||||
CMD=$(basename $0)
|
||||
DASD_STATISTICS_DIR=""
|
||||
ALLFILES=""
|
||||
VERBOSE=false
|
||||
OUTPUT="short"
|
||||
NUMBER_COLUMNS=16
|
||||
COLUMN_WIDTH=
|
||||
ACTION="print"
|
||||
while [ $# -gt 0 ]; do
|
||||
case $1 in
|
||||
--help|-h)
|
||||
print_usage
|
||||
exit 0
|
||||
;;
|
||||
--enable|-e)
|
||||
ACTION="enable"
|
||||
;;
|
||||
--disable|-d)
|
||||
ACTION="disable"
|
||||
;;
|
||||
--reset|-r)
|
||||
ACTION="reset"
|
||||
;;
|
||||
--directory|-i)
|
||||
DASD_STATISTICS_DIR="$2"
|
||||
shift
|
||||
if [[ ! -d $DASD_STATISTICS_DIR ]]
|
||||
then
|
||||
echo "$CMD: $DASD_STATISTICS_DIR is not a directory" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
--long|-l)
|
||||
OUTPUT="extended"
|
||||
;;
|
||||
--columns|-c)
|
||||
NUMBER_COLUMNS="$2"
|
||||
if [[ ! "$NUMBER_COLUMNS" -gt 0 ]]
|
||||
then
|
||||
echo "$CMD: $NUMBER_COLUMNS is not a positive integer number" >&2
|
||||
exit 1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
--column-width|-w)
|
||||
COLUMN_WIDTH="$2"
|
||||
if [[ ! "$COLUMN_WIDTH" -gt 0 ]]
|
||||
then
|
||||
echo "$CMD: $COLUMN_WIDTH is not a positive integer number" >&2
|
||||
exit 1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
--verbose|-V)
|
||||
VERBOSE=true
|
||||
;;
|
||||
--version|-v)
|
||||
print_version
|
||||
exit 0
|
||||
;;
|
||||
-*)
|
||||
echo "$CMD: Invalid option $1" >&2
|
||||
echo "Try '$CMD --help' for more information." >&2
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
ALLFILES="$ALLFILES $1"
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# if no directory is given on command line, find dasd directory in debugfs
|
||||
if [[ "$DASD_STATISTICS_DIR" == "" ]]
|
||||
then
|
||||
while read -a mntentries
|
||||
do
|
||||
if [[ "${mntentries[2]}" == "debugfs" ]]
|
||||
then
|
||||
DASD_STATISTICS_DIR="${mntentries[1]}"
|
||||
verbose_msg "found debugfs mount point $DASD_STATISTICS_DIR"
|
||||
break;
|
||||
fi
|
||||
done < /etc/mtab
|
||||
if [[ "$DASD_STATISTICS_DIR" == "" ]]
|
||||
then
|
||||
echo "$CMD: No debugfs mount point found" >&2
|
||||
exit 1
|
||||
fi
|
||||
DASD_STATISTICS_DIR="$DASD_STATISTICS_DIR/dasd"
|
||||
if [[ ! -d "$DASD_STATISTICS_DIR" ]]
|
||||
then
|
||||
echo "$CMD: Default DASD debugfs directory $DASD_STATISTICS_DIR does not exist" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# look for directories that contain statistics
|
||||
if [[ "$ALLFILES" == "" ]]
|
||||
then
|
||||
ALLFILES=$(ls -x $DASD_STATISTICS_DIR)
|
||||
explicitstats="false"
|
||||
else
|
||||
explicitstats="true"
|
||||
fi
|
||||
ALLSTATS=""
|
||||
for f in $ALLFILES
|
||||
do
|
||||
if [[ -f "$DASD_STATISTICS_DIR/$f/statistics" ]]
|
||||
then
|
||||
ALLSTATS="$ALLSTATS $f"
|
||||
elif [[ $explicitstats == "true" ]]
|
||||
then
|
||||
echo "$CMD: No statistics found for $f" >&2
|
||||
fi
|
||||
done
|
||||
|
||||
verbose_msg "found the following statistics in directory $DASD_STATISTICS_DIR:"
|
||||
verbose_msg "$ALLSTATS"
|
||||
|
||||
# execute the required operation
|
||||
for f in $ALLSTATS
|
||||
do
|
||||
case $ACTION in
|
||||
"enable")
|
||||
enable_stat "$DASD_STATISTICS_DIR/$f/statistics"
|
||||
;;
|
||||
"disable")
|
||||
disable_stat "$DASD_STATISTICS_DIR/$f/statistics"
|
||||
;;
|
||||
"reset")
|
||||
reset_stat "$DASD_STATISTICS_DIR/$f/statistics"
|
||||
;;
|
||||
"print")
|
||||
print_stat "$DASD_STATISTICS_DIR/$f/statistics"
|
||||
;;
|
||||
*)
|
||||
echo "error"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
.\" Copyright 2017 IBM Corp.
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
.TH LSDASD 8 "Feb 2011" "s390-tools"
|
||||
|
||||
.SH NAME
|
||||
dasdstat \- read or modify the statistics of the DASD device driver.
|
||||
|
||||
.SH SYNOPSIS
|
||||
.TP 8
|
||||
.B dasdstat
|
||||
.RB [ -h ]
|
||||
.TP 8
|
||||
.B dasdstat
|
||||
.RB [ -e ]
|
||||
.RB [ -d ]
|
||||
.RB [ -r ]
|
||||
.RB [ -i ]
|
||||
.RB [ -l ]
|
||||
.RB [ -c ]
|
||||
.RB [ -w ]
|
||||
.RB [ -V ]
|
||||
.RB [ -v ]
|
||||
.RI [ <statistic> " [" <statistic> "] ...]]"
|
||||
|
||||
.SH DESCRIPTION
|
||||
The dasdstat command provides easy access to the debugfs based
|
||||
statistics of the DASD device driver.
|
||||
|
||||
The DASD statistics feature allows to gather statistical data for the
|
||||
I/O requests processed by the DASD device driver. This data can be
|
||||
collected for individual DASD CCW devices (including PAV base and
|
||||
alias devices), DASD block devices, or globally for all requests
|
||||
handled by the DASD device driver.
|
||||
|
||||
When no other options are specified, then the default operation is to
|
||||
print the statistics data in a formatted table. When no specific list
|
||||
of statistics is given, then the operation will be performed on all
|
||||
available statistics.
|
||||
|
||||
.SH OPTIONS
|
||||
.TP 8
|
||||
.BR -h | --help
|
||||
Print help text.
|
||||
.TP
|
||||
.BR -e | --enable
|
||||
Enable the statistics.
|
||||
.TP
|
||||
.BR -d | --disable
|
||||
Disable the statistics.
|
||||
.TP
|
||||
.BR -r | --reset
|
||||
Reset the statistics.
|
||||
.TP
|
||||
.BR -i | --directory
|
||||
Specify the directory in which the statistics can be found.
|
||||
.TP
|
||||
.BR -l | --long
|
||||
Print more detailed information, e.g differentiate between read and
|
||||
write requests.
|
||||
.TP
|
||||
.BR -c | --columns " \fI<number>\fR"
|
||||
Format the output in a table with the given number of columns.
|
||||
.TP
|
||||
.BR -w | --column-width " \fI<width>\fR"
|
||||
Set the minimum width of the columns in the output table.
|
||||
.TP
|
||||
.BR -V | --verbose
|
||||
Print more verbose information.
|
||||
.TP
|
||||
.BR -v | --version
|
||||
Print the version of the s390-tools package and the command.
|
||||
.TP
|
||||
\fB<statistic>\fR =
|
||||
Name of a statistic that the command should work on.
|
||||
|
||||
.SH EXAMPLES
|
||||
\fBdasdstat\fR
|
||||
.RS
|
||||
Print a statistics table for each enabled statistic.
|
||||
.RE
|
||||
|
||||
\fBdasdstat -e\fR
|
||||
.RS
|
||||
Enable all DASD statistics.
|
||||
.RE
|
||||
|
||||
\fBdasdstat -l dasda 0.0.1800 0.0.18fe 0.0.18ff\fR
|
||||
.RS
|
||||
Print a detailed statistics table for DASD block device dasda and CCW
|
||||
devices 0.0.1800, 0.0.18fe and 0.0.18ff. A typical scenario for this
|
||||
example would be that dasda is the block device that belongs to
|
||||
PAV base device 0.0.1800, and CCW devices 0.0.18fe and 0.0.18ff are the
|
||||
associated alias devices.
|
||||
.RE
|
||||
|
||||
.SH AUTHOR
|
||||
.nf
|
||||
This man-page was written by Stefan Weinhuber <wein@de.ibm.com>.
|
||||
.fi
|
||||
Executable
+801
@@ -0,0 +1,801 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# lsdasd - Tool to list information about DASDs
|
||||
#
|
||||
# Copyright IBM Corp. 2003, 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.
|
||||
#
|
||||
|
||||
CMD=$(basename $0)
|
||||
SYSFSDIR="/sys"
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# Print usage
|
||||
#------------------------------------------------------------------------------
|
||||
function PrintUsage() {
|
||||
cat <<-EOD
|
||||
Usage: $(basename $0) <options> [<device>]
|
||||
|
||||
<options> ::=
|
||||
-a|--offline
|
||||
Include devices that are currently offline.
|
||||
-b|--base
|
||||
Include only base devices.
|
||||
-h|--help
|
||||
Print this text and exit.
|
||||
-s|--short
|
||||
Strip leading 0.0. from bus IDs.
|
||||
-u|--uid
|
||||
Print and sort by uid.
|
||||
-c|--compat
|
||||
Print old version of lsdasd output.
|
||||
-l|--long
|
||||
Print extended information about DASDs.
|
||||
-H|--host-access-list
|
||||
Print information about hosts accessing DASDs.
|
||||
-v|--verbose
|
||||
For compatibility/future use. Currently ignored.
|
||||
--version
|
||||
Show tools and command version.
|
||||
|
||||
<device> ::= <bus ID>
|
||||
Limit output to one or more devices which are given as a bus ID.
|
||||
EOD
|
||||
}
|
||||
|
||||
function PrintVersion()
|
||||
{
|
||||
cat <<-EOD
|
||||
$CMD: version %S390_TOOLS_VERSION%
|
||||
Copyright IBM Corp. 2003, 2017
|
||||
EOD
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# Helper function to check a device string.
|
||||
#------------------------------------------------------------------------------
|
||||
function CheckDeviceString() {
|
||||
local X
|
||||
|
||||
X=$(
|
||||
echo "$1" |
|
||||
awk --posix -F. '
|
||||
function PrintBusID(css, grp, devno) {
|
||||
while(length(devno) < 4)
|
||||
devno = "0" devno
|
||||
print css "\\." grp "\\." devno "$"
|
||||
}
|
||||
NF == 1 && $1 ~ /^[0-9a-fA-F]{1,4}$/ {
|
||||
PrintBusID("0","0", $1)
|
||||
next
|
||||
}
|
||||
NF != 3 || $1 !~ /^[0-9a-fA-F]{1,2}$/ {
|
||||
next
|
||||
}
|
||||
$2 !~ /^[0-9a-fA-F]{1,2}$/ {
|
||||
next
|
||||
}
|
||||
$3 !~ /^[0-9a-fA-F]{1,4}$/ {
|
||||
next
|
||||
}
|
||||
{
|
||||
PrintBusID($1, $2, $3)
|
||||
}
|
||||
'
|
||||
)
|
||||
|
||||
if [ "$X" != "" ]; then
|
||||
echo $X
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# Generate list of DASDs
|
||||
#------------------------------------------------------------------------------
|
||||
function listDASDDeviceDirectories() {
|
||||
DRIVERECKD="$SYSFSDIR/bus/ccw/drivers/dasd-eckd/"
|
||||
DRIVERFBA="$SYSFSDIR/bus/ccw/drivers/dasd-fba/"
|
||||
SEARCHDIRS=
|
||||
|
||||
if [[ -d "$DRIVERECKD" ]]; then
|
||||
SEARCHDIRS="$DRIVERECKD"
|
||||
fi
|
||||
if [[ -d "$DRIVERFBA" ]]; then
|
||||
SEARCHDIRS="$SEARCHDIRS $DRIVERFBA"
|
||||
fi
|
||||
if [[ -n "$SEARCHDIRS" ]]; then
|
||||
find $SEARCHDIRS -type l -printf "%h/%l\n" 2> /dev/null
|
||||
else
|
||||
# The above paths may become invalid in the future, so we keep the
|
||||
# following query as backup:
|
||||
find "$SYSFSDIR/devices" -type l -name "driver" -lname "*/dasd*" \
|
||||
-printf "%h\n" 2> /dev/null
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# find dasd directory in debugfs
|
||||
#------------------------------------------------------------------------------
|
||||
function findDASDDebugfsDirectorie() {
|
||||
local mntentries
|
||||
|
||||
while read -a mntentries
|
||||
do
|
||||
if [[ "${mntentries[2]}" == "debugfs" ]]
|
||||
then
|
||||
DASD_DBF_DIR="${mntentries[1]}"
|
||||
break;
|
||||
fi
|
||||
done < /etc/mtab
|
||||
if [[ "$DASD_DBF_DIR" == "" ]]
|
||||
then
|
||||
echo "$CMD: No debugfs mount point found" >&2
|
||||
exit 1
|
||||
fi
|
||||
DASD_DBF_DIR="$DASD_DBF_DIR/dasd"
|
||||
if [[ ! -d "$DASD_DBF_DIR" ]]
|
||||
then
|
||||
echo "$CMD: Default DASD debugfs directory $DASD_DBF_DIR does not exist" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# gather device data and call appropriate output function
|
||||
#------------------------------------------------------------------------------
|
||||
function gatherDeviceData() {
|
||||
while read DEVPATH
|
||||
do
|
||||
#-------------------------------------------#
|
||||
# gather information from device attributes #
|
||||
#-------------------------------------------#
|
||||
read ONLINE 2> /dev/null < $DEVPATH/online || continue
|
||||
if [[ "$ONLINE" == 0 ]] &&
|
||||
[[ "$PRINTOFFLINE" == "false" ]]; then
|
||||
continue
|
||||
fi
|
||||
read ALIAS 2> /dev/null < $DEVPATH/alias || continue
|
||||
read DEV_UID 2> /dev/null < $DEVPATH/uid || continue
|
||||
read READONLY 2> /dev/null < $DEVPATH/readonly || continue
|
||||
read DISCIPLINE 2> /dev/null < $DEVPATH/discipline || continue
|
||||
|
||||
# Block device specific information is only available for
|
||||
# devices that are online and not a PAV alias
|
||||
if [[ ! "$ONLINE" == 0 ]] && [[ ! "$ALIAS" == 1 ]]; then
|
||||
#find device Path to the block device
|
||||
if [[ -d "$DEVPATH/block" ]]; then
|
||||
set - "$DEVPATH"/block/dasd*
|
||||
else
|
||||
set - "$DEVPATH"/block:dasd*
|
||||
fi
|
||||
MAJMIN=
|
||||
MAJOR=
|
||||
MINOR=
|
||||
SIZE=
|
||||
SSIZE=
|
||||
if [[ -d "$1" ]]; then
|
||||
cd -P "$1"
|
||||
BLOCKPATH=$PWD
|
||||
BLOCKNAME=${BLOCKPATH##*/}
|
||||
read MAJMIN 2> /dev/null < $BLOCKPATH/dev || continue
|
||||
MAJOR=${MAJMIN%%:*}
|
||||
MINOR=${MAJMIN##*:}
|
||||
read SIZE 2> /dev/null < $BLOCKPATH/size || continue
|
||||
read SSIZE 2> /dev/null < $BLOCKPATH/queue/hw_sector_size
|
||||
if [[ -z $SSIZE ]] && [[ -b "/dev/$BLOCKNAME" ]]; then
|
||||
SSIZE=$(blockdev --getss /dev/$BLOCKNAME 2>/dev/null)
|
||||
fi
|
||||
fi
|
||||
else
|
||||
# BLOCKNAME for offline and alias devices will not be
|
||||
# printed, it's just a key for sorting
|
||||
if [[ "$ONLINE" == 0 ]]; then
|
||||
BLOCKNAME=""
|
||||
else
|
||||
BLOCKNAME="a"
|
||||
fi
|
||||
MAJMIN=
|
||||
MAJOR=
|
||||
MINOR=
|
||||
SIZE=
|
||||
fi
|
||||
|
||||
# busid is the base name of the device path
|
||||
if [[ "$SHORTID" == "true" ]]; then
|
||||
BUSID=${DEVPATH##*.}
|
||||
else
|
||||
BUSID=${DEVPATH##*/}
|
||||
fi
|
||||
|
||||
if [[ "$PRINTUID" == "true" ]]; then
|
||||
SORTKEYLEN=${#DEV_UID}
|
||||
SORTKEY=$DEV_UID
|
||||
FORMATTED_UID="$DEV_UID"
|
||||
else
|
||||
SORTKEYLEN=${#BLOCKNAME}
|
||||
SORTKEY=$BLOCKNAME
|
||||
FORMATTED_UID=""
|
||||
fi
|
||||
|
||||
if [[ "$OUTPUT" == "old" ]]; then
|
||||
oldoutput
|
||||
elif [[ "$OUTPUT" == "extended" ]]; then
|
||||
extended
|
||||
elif [[ "$PRINTUID" == "true" ]]; then
|
||||
uid
|
||||
elif [[ "$OUTPUT" == "host" ]]; then
|
||||
host
|
||||
else
|
||||
newoutput
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
function newoutput()
|
||||
{
|
||||
#-------------------------------------------#
|
||||
# format data for output #
|
||||
#-------------------------------------------#
|
||||
|
||||
if [[ "$ONLINE" == 0 ]]; then
|
||||
printf "%s:%s:%-8s offline\n" \
|
||||
"$SORTKEYLEN" "$SORTKEY" \
|
||||
"$BUSID" ;
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ "$ALIAS" == 1 ]]; then
|
||||
if [[ "$BASEONLY" == "false" ]]; then
|
||||
printf "%s:%s:%-8s alias %28s\n" \
|
||||
"$SORTKEYLEN" "$SORTKEY" \
|
||||
"$BUSID" \
|
||||
"$DISCIPLINE"
|
||||
fi
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ "$READONLY" == 0 ]]; then
|
||||
ROSTRING=""
|
||||
else
|
||||
ROSTRING="(ro)"
|
||||
fi
|
||||
|
||||
if [[ -z "$BLOCKNAME" ]] || [[ -z "$SIZE" ]]; then
|
||||
ACTIVE="active"
|
||||
BLOCKCOUNT=""
|
||||
MBSIZE=""
|
||||
elif [[ "$SIZE" == 0 ]]; then
|
||||
ACTIVE="n/f"
|
||||
BLOCKCOUNT=""
|
||||
MBSIZE=""
|
||||
SSIZE=""
|
||||
else
|
||||
if [[ -n "$SSIZE" ]] && [[ "$SSIZE" > 0 ]]; then
|
||||
BLOCKCOUNT=$(( SIZE / (SSIZE / 512) ))
|
||||
else
|
||||
SSIZE="???"
|
||||
BLOCKCOUNT="???"
|
||||
fi
|
||||
MBSIZE=$(( SIZE / 2048 ))MB
|
||||
ACTIVE="active"
|
||||
fi
|
||||
|
||||
printf "%s:%s:%-8s %-6s%-4s %-8s %-2s:%-2s %-4s %-4s %-8s %s\n" \
|
||||
"$SORTKEYLEN" "$SORTKEY" \
|
||||
"$BUSID" \
|
||||
"$ACTIVE" \
|
||||
"$ROSTRING" \
|
||||
"$BLOCKNAME" \
|
||||
"$MAJOR" \
|
||||
"$MINOR" \
|
||||
"$DISCIPLINE" \
|
||||
"$SSIZE" \
|
||||
"$MBSIZE" \
|
||||
"$BLOCKCOUNT" ;
|
||||
}
|
||||
|
||||
function oldoutput()
|
||||
{
|
||||
#-------------------------------------------#
|
||||
# format data for output #
|
||||
#-------------------------------------------#
|
||||
|
||||
if [[ "$ONLINE" == 0 ]]; then
|
||||
printf "%s:%s:%s(%s)%s : offline\n" \
|
||||
"$SORTKEYLEN" "$SORTKEY" \
|
||||
"$BUSID" \
|
||||
"$DISCIPLINE" \
|
||||
"$FORMATTED_UID"
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ "$ALIAS" == 1 ]]; then
|
||||
if [[ "$BASEONLY" == "false" ]]; then
|
||||
printf "%s:%s:%s(%s)%s : alias\n" \
|
||||
"$SORTKEYLEN" "$SORTKEY" \
|
||||
"$BUSID" \
|
||||
"$DISCIPLINE" \
|
||||
"$FORMATTED_UID"
|
||||
fi
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ "$READONLY" == 0 ]]; then
|
||||
ROSTRING=""
|
||||
else
|
||||
ROSTRING="(ro)"
|
||||
fi
|
||||
|
||||
printf "%s:%s:%s(%-4s)%s at (%3i:%3i) is %-7s%4s: " \
|
||||
"$SORTKEYLEN" "$SORTKEY" \
|
||||
"$BUSID" \
|
||||
"$DISCIPLINE" \
|
||||
"$FORMATTED_UID" \
|
||||
"$MAJOR" \
|
||||
"$MINOR" \
|
||||
"$BLOCKNAME" \
|
||||
"$ROSTRING" ;
|
||||
|
||||
if [[ -z "$BLOCKNAME" ]] || [[ -z "$SIZE" ]]; then
|
||||
printf "active\n"
|
||||
elif [[ "$SIZE" == 0 ]]; then
|
||||
printf "n/f\n"
|
||||
else
|
||||
if [[ -n "$SSIZE" ]] && [[ "$SSIZE" > 0 ]]; then
|
||||
BLOCKCOUNT=$(( SIZE / (SSIZE / 512) ))
|
||||
else
|
||||
SSIZE="???"
|
||||
BLOCKCOUNT="???"
|
||||
fi
|
||||
MBSIZE=$(( SIZE / 2048 ))
|
||||
printf "active at blocksize %s, %s blocks, %i MB\n" \
|
||||
"$SSIZE" "$BLOCKCOUNT" "$MBSIZE"
|
||||
fi
|
||||
}
|
||||
|
||||
function extended()
|
||||
{
|
||||
PIM=0
|
||||
OPM=0
|
||||
NPPM=0
|
||||
CABLEPM=0
|
||||
CUIRPM=0
|
||||
HPFPM=0
|
||||
IFCCPM=0
|
||||
|
||||
# additional information
|
||||
read DIAG 2> /dev/null < $DEVPATH/use_diag
|
||||
read EER 2> /dev/null < $DEVPATH/eer_enabled
|
||||
read ERP 2> /dev/null < $DEVPATH/erplog
|
||||
read HPF 2> /dev/null < $DEVPATH/hpf
|
||||
# in case the path_masks do not exist simply ignore it
|
||||
read OPM NPPM CABLEPM CUIRPM HPFPM IFCCPM 2> /dev/null < $DEVPATH/path_masks
|
||||
read -a C 2> /dev/null < $DEVPATH/../chpids
|
||||
read PIM PAM POM 2> /dev/null < $DEVPATH/../pimpampom
|
||||
|
||||
# convert to hexadecimal values
|
||||
PIM=0x$PIM
|
||||
OPM=0x$OPM
|
||||
NPPM=0x$NPPM
|
||||
CABLEPM=0x$CABLEPM
|
||||
CUIRPM=0x$CUIRPM
|
||||
HPFPM=0x$HPFPM
|
||||
IFCCPM=0x$IFCCPM
|
||||
|
||||
#-----------------------------------------------------------#
|
||||
# aggregate chpids and path mask to useful information #
|
||||
#-----------------------------------------------------------#
|
||||
|
||||
# initialise chpid lists
|
||||
INSTALLED_PATHS=(" " " " " " " " " " " " " " " ")
|
||||
USED_PATHS=(" " " " " " " " " " " " " " " ")
|
||||
NP_PATHS=(" " " " " " " " " " " " " " " ")
|
||||
CUIR_PATHS=(" " " " " " " " " " " " " " " ")
|
||||
CABLE_PATHS=(" " " " " " " " " " " " " " " ")
|
||||
HPF_PATHS=(" " " " " " " " " " " " " " " ")
|
||||
IFCC_PATHS=(" " " " " " " " " " " " " " " ")
|
||||
|
||||
# installed paths
|
||||
j=0
|
||||
mask=0x80
|
||||
for (( i=0; i<8; i++ )) ;do
|
||||
PM=$(($PIM&$mask))
|
||||
if [ $PM -gt 0 ] ;then
|
||||
INSTALLED_PATHS[$j]=${C[$i]} ;
|
||||
((j++)) ;
|
||||
fi
|
||||
(( mask>>=1 ))
|
||||
done
|
||||
|
||||
# used paths
|
||||
j=0
|
||||
mask=0x80
|
||||
for (( i=0; i<8; i++ )) ;do
|
||||
PM=$(($OPM&$mask))
|
||||
if [ $PM -gt 0 ] ;then
|
||||
USED_PATHS[$j]=${C[$i]} ;
|
||||
((j++)) ;
|
||||
fi
|
||||
(( mask>>=1 ))
|
||||
done
|
||||
|
||||
# non preffered paths
|
||||
j=0
|
||||
mask=0x80
|
||||
for (( i=0; i<8; i++ )) ;do
|
||||
PM=$(($NPPM&$mask))
|
||||
if [ $PM -gt 0 ] ;then
|
||||
NP_PATHS[j]=${C[$i]} ;
|
||||
((j++)) ;
|
||||
fi
|
||||
(( mask>>=1 ))
|
||||
done
|
||||
|
||||
# cuir quiesced paths
|
||||
j=0
|
||||
mask=0x80
|
||||
for (( i=0; i<8; i++ )) ;do
|
||||
PM=$(($CUIRPM&$mask))
|
||||
if [ $PM -gt 0 ] ;then
|
||||
CUIR_PATHS[j]=${C[$i]} ;
|
||||
((j++)) ;
|
||||
fi
|
||||
(( mask>>=1 ))
|
||||
done
|
||||
|
||||
# mis cabled paths
|
||||
j=0
|
||||
mask=0x80
|
||||
for (( i=0; i<8; i++ )) ;do
|
||||
PM=$(($CABLEPM&$mask))
|
||||
if [ $PM -gt 0 ] ;then
|
||||
CABLE_PATHS[j]=${C[$i]} ;
|
||||
((j++)) ;
|
||||
fi
|
||||
(( mask>>=1 ))
|
||||
done
|
||||
|
||||
# HPF unusable paths
|
||||
j=0
|
||||
mask=0x80
|
||||
for (( i=0; i<8; i++ )) ;do
|
||||
PM=$(($HPFPM&$mask))
|
||||
if [ $PM -gt 0 ] ;then
|
||||
HPF_PATHS[j]=${C[$i]} ;
|
||||
((j++)) ;
|
||||
fi
|
||||
(( mask>>=1 ))
|
||||
done
|
||||
|
||||
# IFCC unusable paths
|
||||
j=0
|
||||
mask=0x80
|
||||
for (( i=0; i<8; i++ )) ;do
|
||||
PM=$(($IFCCPM&$mask))
|
||||
if [ $PM -gt 0 ] ;then
|
||||
IFCC_PATHS[j]=${C[$i]} ;
|
||||
((j++)) ;
|
||||
fi
|
||||
(( mask>>=1 ))
|
||||
done
|
||||
|
||||
#-------------------------------------------#
|
||||
# format data for output #
|
||||
#-------------------------------------------#
|
||||
|
||||
if [[ "$ONLINE" == 0 ]]; then
|
||||
ACTIVE="offline"
|
||||
printf "%s:%s:%s# status:\t\t\t\t%s# use_diag:\t\t\t\t%s# readonly:\t\t\t\t%s# eer_enabled:\t\t\t\t%s# erplog:\t\t\t\t%s# hpf:\t\t\t\t\t%s# uid: \t\t\t\t%s# paths_installed: \t\t\t%s %s %s %s %s %s %s %s# paths_in_use: \t\t\t%s %s %s %s %s %s %s %s# paths_non_preferred: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_cabling: \t\t%s %s %s %s %s %s %s %s# paths_cuir_quiesced: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_hpf_characteristics: \t%s %s %s %s %s %s %s %s# paths_error_threshold_exceeded: \t%s %s %s %s %s %s %s %s#\n" \
|
||||
"$SORTKEYLEN" "$SORTKEY" \
|
||||
"$BUSID" \
|
||||
"$ACTIVE" \
|
||||
"$DIAG" \
|
||||
"$READONLY" \
|
||||
"$EER" \
|
||||
"$ERP" \
|
||||
"$HPF" \
|
||||
"$DEV_UID" \
|
||||
"${INSTALLED_PATHS[@]}" \
|
||||
"${USED_PATHS[@]}" \
|
||||
"${NP_PATHS[@]}" \
|
||||
"${CABLE_PATHS[@]}" \
|
||||
"${CUIR_PATHS[@]}" \
|
||||
"${HPF_PATHS[@]}" \
|
||||
"${IFCC_PATHS[@]}" ;
|
||||
return
|
||||
elif [[ "$ALIAS" == 1 ]]; then
|
||||
if [[ "$BASEONLY" == "false" ]]; then
|
||||
ACTIVE="alias"
|
||||
printf "%s:%s:%s# status:\t\t\t\t%s# type: \t\t\t\t%s# use_diag:\t\t\t\t%s# readonly:\t\t\t\t%s# eer_enabled:\t\t\t\t%s# erplog:\t\t\t\t%s# hpf:\t\t\t\t\t%s # uid: \t\t\t\t%s# paths_installed: \t\t\t%s %s %s %s %s %s %s %s# paths_in_use: \t\t\t%s %s %s %s %s %s %s %s# paths_non_preferred: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_cabling: \t\t%s %s %s %s %s %s %s %s# paths_cuir_quiesced: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_hpf_characteristics: \t%s %s %s %s %s %s %s %s# paths_error_threshold_exceeded: \t%s %s %s %s %s %s %s %s#\n" \
|
||||
"$SORTKEYLEN" "$SORTKEY" \
|
||||
"$BUSID" \
|
||||
"$ACTIVE" \
|
||||
"$DISCIPLINE" \
|
||||
"$DIAG" \
|
||||
"$READONLY" \
|
||||
"$EER" \
|
||||
"$ERP" \
|
||||
"$HPF" \
|
||||
"$DEV_UID" \
|
||||
"${INSTALLED_PATHS[@]}" \
|
||||
"${USED_PATHS[@]}" \
|
||||
"${NP_PATHS[@]}" \
|
||||
"${CABLE_PATHS[@]}" \
|
||||
"${CUIR_PATHS[@]}" \
|
||||
"${HPF_PATHS[@]}" \
|
||||
"${IFCC_PATHS[@]}" ;
|
||||
fi
|
||||
return
|
||||
elif [[ -z "$BLOCKNAME" ]] || [[ -z "$SIZE" ]]; then
|
||||
ACTIVE="active"
|
||||
COLON=""
|
||||
elif [[ "$SIZE" == 0 ]]; then
|
||||
ACTIVE="n/f"
|
||||
COLON=""
|
||||
else
|
||||
if [[ -n "$SSIZE" ]] && [[ "$SSIZE" > 0 ]]; then
|
||||
BLOCKCOUNT=$(( SIZE / (SSIZE / 512) ))
|
||||
else
|
||||
SSIZE="???"
|
||||
BLOCKCOUNT="???"
|
||||
fi
|
||||
MBSIZE=$(( SIZE / 2048 ))MB
|
||||
ACTIVE="active"
|
||||
COLON=":"
|
||||
fi
|
||||
|
||||
printf "%s:%s:%s/%s/%s%s%s# status:\t\t\t\t%s# type: \t\t\t\t%s# blksz:\t\t\t\t%s# size: \t\t\t\t%s# blocks:\t\t\t\t%s# use_diag:\t\t\t\t%s# readonly:\t\t\t\t%s# eer_enabled:\t\t\t\t%s# erplog:\t\t\t\t%s# hpf:\t\t\t\t\t%s# uid: \t\t\t\t%s# paths_installed: \t\t\t%s %s %s %s %s %s %s %s# paths_in_use: \t\t\t%s %s %s %s %s %s %s %s# paths_non_preferred: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_cabling: \t\t%s %s %s %s %s %s %s %s# paths_cuir_quiesced: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_hpf_characteristics: \t%s %s %s %s %s %s %s %s# paths_error_threshold_exceeded: \t%s %s %s %s %s %s %s %s#\n" \
|
||||
"$SORTKEYLEN" "$SORTKEY" \
|
||||
"$BUSID" \
|
||||
"$BLOCKNAME" \
|
||||
"$MAJOR" \
|
||||
"$COLON" \
|
||||
"$MINOR" \
|
||||
"$ACTIVE" \
|
||||
"$DISCIPLINE" \
|
||||
"$SSIZE" \
|
||||
"$MBSIZE" \
|
||||
"$BLOCKCOUNT" \
|
||||
"$DIAG" \
|
||||
"$READONLY" \
|
||||
"$EER" \
|
||||
"$ERP" \
|
||||
"$HPF" \
|
||||
"$DEV_UID" \
|
||||
"${INSTALLED_PATHS[@]}" \
|
||||
"${USED_PATHS[@]}" \
|
||||
"${NP_PATHS[@]}" \
|
||||
"${CABLE_PATHS[@]}" \
|
||||
"${CUIR_PATHS[@]}" \
|
||||
"${HPF_PATHS[@]}" \
|
||||
"${IFCC_PATHS[@]}" ;
|
||||
}
|
||||
|
||||
function host()
|
||||
{
|
||||
findDASDDebugfsDirectorie
|
||||
|
||||
if [[ ! -f "$DASD_DBF_DIR/$BUSID/host_access_list" ]]
|
||||
then
|
||||
printf "\n%s: hosts access information not available\n" "$BUSID"
|
||||
return
|
||||
fi
|
||||
|
||||
local temp=`mktemp /tmp/lsdasd.XXXXXX`
|
||||
if test -w $temp ; then :; else
|
||||
printf "\nCreating temporary file failed\n"
|
||||
return
|
||||
fi
|
||||
|
||||
cat $DASD_DBF_DIR/$BUSID/host_access_list > $temp 2> /dev/null
|
||||
ret=$?
|
||||
if [[ $ret -ne 0 ]]
|
||||
then
|
||||
printf "%s: hosts access information not available\n" "$BUSID"
|
||||
rm -f $temp
|
||||
return $ret
|
||||
fi
|
||||
|
||||
unset index
|
||||
unset array
|
||||
declare -a array
|
||||
|
||||
index=(pgid status_flags sysplex_name supported_cylinder timestamp)
|
||||
|
||||
for element in ${index[@]}
|
||||
do
|
||||
count=0
|
||||
|
||||
declare -a $element
|
||||
OLDIFS=$IFS
|
||||
IFS=$'\n'
|
||||
for value in `grep $element $temp`
|
||||
do
|
||||
(( ++count ))
|
||||
value=$(echo -e $value | cut -d ' ' -f2)
|
||||
eval $element[$count]=$value
|
||||
done
|
||||
IFS=$OLDIFS
|
||||
done
|
||||
|
||||
printf "Host information for %s\n" "$BUSID";
|
||||
printf "Path-Group-ID LPAR CPU FL Status Sysplex Max_Cyls Time\n";
|
||||
printf "================================================================================\n";
|
||||
|
||||
# mask bits for online and reserved state
|
||||
online_reserved_mask=0xE0
|
||||
|
||||
# print name value lists
|
||||
for i in `seq 1 $count`;
|
||||
do
|
||||
# get flags field
|
||||
value=${status_flags[$i]}
|
||||
# mark as hex value
|
||||
value=0x$value
|
||||
# mask online and reserved bits
|
||||
value=$(($value & $online_reserved_mask))
|
||||
|
||||
case $value in
|
||||
0 ) # 0x00
|
||||
STATE="OFF"
|
||||
;;
|
||||
32 ) # 0x20
|
||||
STATE="OFF-RSV"
|
||||
;;
|
||||
64 ) # 0x40
|
||||
STATE="ON"
|
||||
;;
|
||||
96 ) # 0x60
|
||||
STATE="ON-RSV"
|
||||
;;
|
||||
* )
|
||||
STATE="-"
|
||||
;;
|
||||
esac
|
||||
|
||||
printf "%22s %02s %07s %02s %-6s %-8s %11u %10lu\n" \
|
||||
"${pgid[$i]}" \
|
||||
"${pgid[$i]:4:2}" \
|
||||
"${pgid[$i]:6:4}" \
|
||||
"${status_flags[$i]}" \
|
||||
"$STATE" \
|
||||
"${sysplex_name[$i]}" \
|
||||
"${supported_cylinder[$i]}" \
|
||||
"${timestamp[$i]}" \
|
||||
;
|
||||
done
|
||||
printf "\n";
|
||||
|
||||
rm -f $temp
|
||||
}
|
||||
|
||||
function uid()
|
||||
{
|
||||
#-------------------------------------------#
|
||||
# format data for output #
|
||||
#-------------------------------------------#
|
||||
|
||||
if [[ "$ONLINE" == 0 ]]; then
|
||||
BLOCKNAME="offline"
|
||||
elif [[ "$ALIAS" == 1 ]]; then
|
||||
if [[ "$BASEONLY" == "true" ]]; then
|
||||
return
|
||||
else
|
||||
BLOCKNAME="alias"
|
||||
fi
|
||||
fi
|
||||
|
||||
printf "%s:%s:%-8s %-8s %s\n" \
|
||||
"$SORTKEYLEN" "$SORTKEY" \
|
||||
"$BUSID" \
|
||||
"$BLOCKNAME" \
|
||||
"$FORMATTED_UID" ;
|
||||
}
|
||||
|
||||
SHORTID=false
|
||||
PRINTOFFLINE=false
|
||||
VERBOSE=false
|
||||
PRINTUID=false
|
||||
BASEONLY=false
|
||||
OUTPUT="new"
|
||||
#------------------------------------------------------------------------------
|
||||
# Evaluating command line options
|
||||
#------------------------------------------------------------------------------
|
||||
while [ $# -gt 0 ]; do
|
||||
case $1 in
|
||||
--help|-h)
|
||||
PrintUsage
|
||||
exit 0
|
||||
;;
|
||||
--verbose|-v)
|
||||
VERBOSE=true
|
||||
;;
|
||||
--offline|-a)
|
||||
PRINTOFFLINE=true
|
||||
;;
|
||||
--short|-s)
|
||||
SHORTID=true
|
||||
;;
|
||||
--uid|-u)
|
||||
PRINTUID=true
|
||||
;;
|
||||
--base|-b)
|
||||
BASEONLY=true
|
||||
;;
|
||||
--compat|-c)
|
||||
OUTPUT="old"
|
||||
;;
|
||||
--long|-l)
|
||||
OUTPUT="extended"
|
||||
;;
|
||||
--host-access-list|-H)
|
||||
OUTPUT="host"
|
||||
;;
|
||||
--version)
|
||||
PrintVersion
|
||||
exit 0
|
||||
;;
|
||||
-*)
|
||||
echo "$CMD: Invalid option $1"
|
||||
echo "Try 'lsdasd --help' for more information."
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
DEV="$(CheckDeviceString $1)"
|
||||
if [ "$DEV" = "" ]; then
|
||||
echo "$CMD: ERROR: $1 no device format"
|
||||
exit 1
|
||||
fi
|
||||
if [ "$DEVLIST" == "" ]; then
|
||||
DEVLIST="$DEV"
|
||||
else
|
||||
DEVLIST="$DEVLIST\|$DEV"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
if [[ $OUTPUT == "extended" ]]; then
|
||||
if [[ $PRINTUID == true ]]; then
|
||||
echo "$CMD: ERROR: invalid options specified"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
PROCESSING="listDASDDeviceDirectories "
|
||||
# if there is a DEVLIST remove all elements not in the DEVLIST
|
||||
if [ "$DEVLIST" != "" ]; then
|
||||
PROCESSING=" $PROCESSING | grep \"$DEVLIST\" "
|
||||
fi
|
||||
|
||||
# gather information on devices in list
|
||||
PROCESSING=" $PROCESSING | gatherDeviceData "
|
||||
|
||||
# sort resulting list
|
||||
if [[ "$OUTPUT" == "host" ]]; then
|
||||
PROCESSING=" $PROCESSING"
|
||||
else
|
||||
PROCESSING=" $PROCESSING | sort -t: -k1n -k2 | cut -d: -f3- "
|
||||
fi
|
||||
|
||||
if [[ "$PRINTUID" == "true" ]] && [[ "$OUTPUT" != "old" ]]; then
|
||||
printf "Bus-ID Name UID\n"
|
||||
printf "==============================================================================\n"
|
||||
elif [[ "$OUTPUT" == "new" ]]; then
|
||||
printf "Bus-ID Status Name Device Type BlkSz Size Blocks\n"
|
||||
printf "==============================================================================\n"
|
||||
elif [[ "$OUTPUT" == "extended" ]]; then
|
||||
PROCESSING=" $PROCESSING | sed 's/#/\n/g' "
|
||||
fi
|
||||
|
||||
#execute all steps
|
||||
eval "$PROCESSING"
|
||||
|
||||
exit 0
|
||||
@@ -0,0 +1,81 @@
|
||||
.\" Copyright 2017 IBM Corp.
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
.TH LSDASD 8 "Apr 2006" "s390-tools"
|
||||
|
||||
.SH NAME
|
||||
lsdasd \- list channel attached direct access storage devices (DASD).
|
||||
|
||||
.SH SYNOPSIS
|
||||
.TP 8
|
||||
.B lsdasd
|
||||
.RB [ -h ]
|
||||
.TP 8
|
||||
.B lsdasd
|
||||
.RB [ -a ]
|
||||
.RB [ -b ]
|
||||
.RB [ -s ]
|
||||
.RB [ -v ]
|
||||
.RB [ -l ]
|
||||
.RB [ -c ]
|
||||
.RB [ -u ]
|
||||
.RB [ --version ]
|
||||
.RI [ <bus-ID> " [" <bus-ID> "] ...]]"
|
||||
|
||||
.SH DESCRIPTION
|
||||
The lsdasd command provides an overview of available DASD devices.
|
||||
|
||||
.SH OPTIONS
|
||||
.TP 8
|
||||
.BR -h | --help
|
||||
Print help text.
|
||||
.TP
|
||||
.BR -s | --short
|
||||
Suppresses leading "0.0." for bus IDs.
|
||||
.TP
|
||||
.BR -a | --offline
|
||||
Include all (offline) devices.
|
||||
.TP
|
||||
.BR -b | --base
|
||||
Include only base devices.
|
||||
.TP
|
||||
.BR -c | --compat
|
||||
Old output of lsdasd for compatibility.
|
||||
.TP
|
||||
.BR -l | --long
|
||||
Extended output of lsdasd including UID and attributes.
|
||||
.TP
|
||||
.BR -u | --uid
|
||||
Output includes and is sorted by UID.
|
||||
.TP
|
||||
.BR -H | --host-acces
|
||||
Show information about all hosts using this device.
|
||||
.TP
|
||||
.BR -v | --verbose
|
||||
Only for compatibility (and maybe future) use. This option currently does
|
||||
nothing.
|
||||
.TP
|
||||
\fB--version\fR
|
||||
Print the version of the s390-tools package and the command.
|
||||
.TP
|
||||
\fB<bus-ID>\fR =
|
||||
Bus ID of the device(s) that should be displayed.
|
||||
|
||||
.SH EXAMPLES
|
||||
\fBlsdasd\fR
|
||||
.RS
|
||||
List all devices that are online.
|
||||
.RE
|
||||
|
||||
\fBlsdasd\fR 0.0.0193 0.0.0195
|
||||
.RS
|
||||
Same as above but will only show data for the given devices (if online).
|
||||
.RE
|
||||
|
||||
.SH AUTHOR
|
||||
.nf
|
||||
This man-page was written by Stefan Bader <shbader@de.ibm.com>.
|
||||
.fi
|
||||
.SH "SEE ALSO"
|
||||
.BR lscss (8)
|
||||
Executable
+338
@@ -0,0 +1,338 @@
|
||||
#!/usr/bin/perl
|
||||
#
|
||||
# lsluns - Tool to list all available LUNs
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use English;
|
||||
use Getopt::Long;
|
||||
use File::Basename;
|
||||
use File::Glob;
|
||||
|
||||
my %res_hash = ();
|
||||
my @adapter = ();
|
||||
my @port = ();
|
||||
my $active = "";
|
||||
|
||||
my $wlun = "0xc101000000000000";
|
||||
my $lun0 = "0x0000000000000000";
|
||||
my $sg_dir = "/sys/class/scsi_generic";
|
||||
my $udevsettle_call;
|
||||
my $udevadm = "/sbin/udevadm";
|
||||
|
||||
|
||||
if (! -e $udevadm) {
|
||||
$udevsettle_call = "/sbin/udevsettle";
|
||||
} else {
|
||||
$udevsettle_call = "$udevadm settle";
|
||||
}
|
||||
|
||||
# read the first line of a sysfs-entry and compare it to a given string
|
||||
# parameters:
|
||||
# 1 - $sysfs_attr_path: path to the sysfs-entry
|
||||
# 2 - $compare_value: compare-value (used as string)
|
||||
# return:
|
||||
# true/false: depending on the comparison (true for equal)
|
||||
# undef: if I/O on the given path isn't possible
|
||||
sub sysfs_read_and_compare($$)
|
||||
{
|
||||
my ($sysfs_attr_path, $compare_value) = @_;
|
||||
|
||||
open(my $sysfs_attr, "<", $sysfs_attr_path) or return undef;
|
||||
my $sysfs_value = <$sysfs_attr>;
|
||||
close($sysfs_attr);
|
||||
|
||||
# EOF?
|
||||
if (!defined($sysfs_value)) {
|
||||
return undef;
|
||||
}
|
||||
|
||||
chomp($sysfs_value);
|
||||
return $sysfs_value eq $compare_value;
|
||||
}
|
||||
|
||||
sub list_luns
|
||||
{
|
||||
my %lun_hash = get_lun_hash();
|
||||
my $drv_dir = "/sys/bus/ccw/drivers/zfcp";
|
||||
my $man_att;
|
||||
my $cnt;
|
||||
|
||||
foreach my $a (sort keys %res_hash) {
|
||||
# first check whether the adapter is online and good to use
|
||||
if (!sysfs_read_and_compare($drv_dir.'/'.$a.'/online', "1") ||
|
||||
!sysfs_read_and_compare($drv_dir.'/'.$a.'/availability', "good") ||
|
||||
!sysfs_read_and_compare($drv_dir.'/'.$a.'/failed', "0") ||
|
||||
!sysfs_read_and_compare($drv_dir.'/'.$a.'/in_recovery', "0"))
|
||||
{
|
||||
print "Adapter $a is not in a good state; skipping LUN scan.\n";
|
||||
next;
|
||||
}
|
||||
|
||||
print "Scanning for LUNs on adapter $a\n";
|
||||
foreach my $p (@{$res_hash{$a}}) {
|
||||
next if (! -e $drv_dir."/$a/$p");
|
||||
my $status = `cat $drv_dir/$a/$p/access_denied`;
|
||||
$status .= `cat $drv_dir/$a/$p/failed`;
|
||||
$status .= `cat $drv_dir/$a/$p/in_recovery`;
|
||||
if ($status =~ /1/) {
|
||||
print "\tat port $p:\n";
|
||||
print "\t\tPort not online. Cannot scan for LUNs.\n";
|
||||
next;
|
||||
}
|
||||
if (!defined($lun_hash{$a}{$p})) {
|
||||
`echo $lun0 >> $drv_dir/$a/$p/unit_add 2>/dev/null`;
|
||||
for ($cnt = 0; $cnt < 4; $cnt++) {
|
||||
`$udevsettle_call`;
|
||||
%lun_hash = get_lun_hash();
|
||||
last if (defined($lun_hash{$a}{$p}));
|
||||
select(undef, undef, undef, 0.1);
|
||||
}
|
||||
if (!defined($lun_hash{$a}{$p})) {
|
||||
`echo $lun0 >> $drv_dir/$a/$p/unit_remove 2>/dev/null`;
|
||||
`echo $wlun >> $drv_dir/$a/$p/unit_add 2>/dev/null`;
|
||||
for ($cnt = 0; $cnt < 4; $cnt++) {
|
||||
`$udevsettle_call`;
|
||||
%lun_hash = get_lun_hash();
|
||||
last if (defined($lun_hash{$a}{$p}));
|
||||
select(undef, undef, undef, 0.1);
|
||||
}
|
||||
if (!defined($lun_hash{$a}{$p})) {
|
||||
`echo $wlun >> $drv_dir/$a/$p/unit_remove 2>/dev/null`;
|
||||
print"\tat port $p:\n";
|
||||
print "\t\tCannot attach WLUN / LUN0 for scanning.\n";
|
||||
next;
|
||||
}
|
||||
}
|
||||
$man_att = 1;
|
||||
}
|
||||
|
||||
my $retries = 0;
|
||||
foreach my $lun (@{[keys %{$lun_hash{$a}{$p}}]}) {
|
||||
my $sg_dev = $lun_hash{$a}{$p}{$lun};
|
||||
select(undef, undef, undef, 0.1) while (! -e "/dev/$sg_dev");
|
||||
my @output = `sg_luns /dev/$sg_dev 2>/dev/null`;
|
||||
my $error = $?;
|
||||
if ($man_att) {
|
||||
`echo 1 >> $sg_dir/$sg_dev/device/delete 2>/dev/null`;
|
||||
select(undef, undef, undef, 0.1);
|
||||
`echo $lun >> $drv_dir/$a/$p/unit_remove 2>/dev/null`;
|
||||
$man_att = 0;
|
||||
}
|
||||
print "\tat port $p:\n" if (!$retries);
|
||||
if (!$error && @output) {
|
||||
splice(@output, 0, 2);
|
||||
map { s/\s*(\w{16})\s*/\t\t0x$1\n/ } @output;
|
||||
print @output;
|
||||
last;
|
||||
}
|
||||
if ($error) {
|
||||
print "\t\tUnable to send the REPORT_LUNS command to LUN.\n";
|
||||
}
|
||||
$retries++;
|
||||
last if ($retries > 3);
|
||||
}
|
||||
}
|
||||
if (! -d $sg_dir) {
|
||||
print "$PROGRAM_NAME: Error: Please load/configure SCSI Generic (sg) to use $PROGRAM_NAME.\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Look only for LUN0 and the REPORT LUNs WLUN. SAM specifies that the storage
|
||||
# only has to response on one of those to the REPORT LUNs command.
|
||||
sub get_lun_hash
|
||||
{
|
||||
my %lun_hash;
|
||||
|
||||
foreach my $device (</$sg_dir/sg*>) {
|
||||
my $l = `cat $device/device/fcp_lun`;
|
||||
my $p = `cat $device/device/wwpn`;
|
||||
my $a = `cat $device/device/hba_id`;
|
||||
|
||||
$l =~ s/(0x\w{16})*\n/$1/;
|
||||
$p =~ s/(0x\w{16})*\n/$1/;
|
||||
chomp($a);
|
||||
|
||||
if ($active or ($l eq $lun0 or $l eq $wlun)) {
|
||||
$lun_hash{$a}{$p}{$l} = ${[split('/', $device)]}[-1];
|
||||
}
|
||||
}
|
||||
return %lun_hash;
|
||||
}
|
||||
|
||||
sub lsluns_usage {
|
||||
print <<EOD;
|
||||
Usage: $PROGRAM_NAME [<options>]
|
||||
|
||||
lsluns provides information for LUNs.
|
||||
|
||||
The default is to list all LUNs that are available via the attached ports.
|
||||
The display can be limited by specifying an adapter or a port.
|
||||
|
||||
Options:
|
||||
-a, --active
|
||||
Shows all activated LUNs.
|
||||
In addition LUN encryption information is provided.
|
||||
e.g. "lsluns -a"
|
||||
|
||||
-c, --ccw
|
||||
Shows LUNs for a specific ccw device.
|
||||
e.g. "lsluns -c 0.0.3922"
|
||||
|
||||
-p, --port
|
||||
Shows LUNs for a specific port.
|
||||
e.g. "lsluns -p 0x5005123456789000"
|
||||
|
||||
-h, --help
|
||||
Print help message and exit.
|
||||
|
||||
-v, --version
|
||||
Display version info and exit.
|
||||
EOD
|
||||
exit 0;
|
||||
}
|
||||
|
||||
sub lsluns_version {
|
||||
print "$PROGRAM_NAME: version %S390_TOOLS_VERSION%\n";
|
||||
print "Copyright IBM Corp. 2008, 2017\n";
|
||||
}
|
||||
|
||||
sub lsluns_invalid_usage {
|
||||
print "$PROGRAM_NAME: invalid option\n";
|
||||
print "Try '$PROGRAM_NAME --help' for more information.\n";
|
||||
}
|
||||
|
||||
sub get_env_list
|
||||
{
|
||||
my $a_ref_list = shift();
|
||||
my $p_ref_list = shift();
|
||||
my @res ;
|
||||
my %res_hash;
|
||||
my @t_arr;
|
||||
|
||||
@res = </sys/bus/ccw/drivers/zfcp/*.*.*/0x*>;
|
||||
return () if (!@res);
|
||||
reload:
|
||||
foreach my $entry (@res) {
|
||||
my $a = ${[split('/', $entry)]}[-2];
|
||||
my $p = ${[split('/', $entry)]}[-1];
|
||||
next if (@$a_ref_list && "@$a_ref_list" !~ /$a/);
|
||||
next if (@$p_ref_list && "@$p_ref_list" !~ /$p/);
|
||||
push @{ $res_hash{$a} }, $p;
|
||||
}
|
||||
foreach my $a (sort @$a_ref_list) {
|
||||
if ("@{[keys %res_hash]}" !~ /$a/) {
|
||||
print "\tNo valid combination found for adapter '$a'. ",
|
||||
"Removing from resource list.\n";
|
||||
}
|
||||
}
|
||||
|
||||
push @t_arr, map { @{$res_hash{$_}} } keys %res_hash;
|
||||
foreach my $p (@$p_ref_list) {
|
||||
if ("@t_arr" !~ /$p/) {
|
||||
print "\tNo valid combination found for port '$p'. ",
|
||||
"Removing from resource list.\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (!%res_hash) {
|
||||
print "\nNo valid parameters left, ",
|
||||
"using all available resources in system.\n\n";
|
||||
@$a_ref_list = ();
|
||||
@$p_ref_list = ();
|
||||
@t_arr = ();
|
||||
goto reload;
|
||||
}
|
||||
return %res_hash;
|
||||
}
|
||||
|
||||
sub show_attached_lun_info
|
||||
{
|
||||
my %lun_hash = get_lun_hash();
|
||||
my @txt = ("Disk", "Tape", "Printer", "Proc", "WRO",
|
||||
"CD/DVD", "Scanner", "OMD", "Changer","Comm","n/a",
|
||||
"n/a","RAID", "Encl");
|
||||
|
||||
if (glob("/sys/class/scsi_device/*") && (! -d $sg_dir)) {
|
||||
print "$PROGRAM_NAME: Error: Please load/configure SCSI Generic (sg) to use $PROGRAM_NAME.\n";
|
||||
}
|
||||
foreach my $a (sort keys %lun_hash) {
|
||||
next if ("@adapter" !~ /$a/);
|
||||
print "adapter = $a\n";
|
||||
foreach my $p (sort keys %{$lun_hash{$a}}) {
|
||||
next if ("@port" !~ /$p/);
|
||||
print "\tport = $p\n";
|
||||
foreach my $l (sort keys %{$lun_hash{$a}{$p}}) {
|
||||
my $sg_dev = "/dev/".$lun_hash{$a}{$p}{$l};
|
||||
my $inq = `sg_inq -r $sg_dev 2>/dev/null`;
|
||||
if (!$inq) {
|
||||
print("\t\tlun = $l [offline]\n");
|
||||
next;
|
||||
}
|
||||
(my $vend = substr($inq, 0x8, 0x8)) =~ s/\s*//g;
|
||||
(my $mod = substr($inq, 0x10, 0x10)) =~ s/\s*//g;
|
||||
my $type = ord(substr($inq, 0x0, 0x1));
|
||||
my $enc = ($mod =~ /2107/) ?
|
||||
ord(substr($inq, 0xa2, 0x1)) : 0;
|
||||
$l .= "(X)" if ($enc & 0x80);
|
||||
$txt[$type] = $type if (!defined($txt[$type]));
|
||||
print("\t\tlun = $l\t$sg_dev\t$txt[$type]",
|
||||
"\t$vend:$mod\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
########### main ################
|
||||
|
||||
$PROGRAM_NAME = basename($PROGRAM_NAME);
|
||||
|
||||
Getopt::Long::Configure(qw(bundling));
|
||||
GetOptions('c|ccw=s' => \@adapter,
|
||||
'p|port=s' => \@port,
|
||||
'a|active' => \$active,
|
||||
'v|version' => sub { lsluns_version(); exit 0; },
|
||||
'h|help' => sub { lsluns_usage(); exit 0; },
|
||||
) or do {
|
||||
lsluns_invalid_usage();
|
||||
exit 1;
|
||||
};
|
||||
|
||||
@adapter = split(',', join(',', @adapter));
|
||||
foreach (@adapter) {
|
||||
$_ =~ tr/A-Z/a-z/;
|
||||
}
|
||||
|
||||
@port = split(',', join(',', @port));
|
||||
foreach (@port) {
|
||||
$_ =~ tr/A-Z/a-z/;
|
||||
}
|
||||
|
||||
%res_hash = get_env_list(\@adapter, \@port);
|
||||
|
||||
@adapter = keys %res_hash;
|
||||
push @port, map { @{$res_hash{$_}} } keys %res_hash;
|
||||
|
||||
# checking for helper progs
|
||||
|
||||
die "$PROGRAM_NAME: Unable to execute due to missing sg3_utils package. ".
|
||||
"Processing stopped.\n" if system("sg_luns -V > /dev/null 2>&1");
|
||||
|
||||
|
||||
|
||||
if ($active) {
|
||||
show_attached_lun_info();
|
||||
} else {
|
||||
list_luns();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
.\" Copyright IBM Corp. 2006, 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.
|
||||
.\"
|
||||
.TH LSLUNS 8 "June 2008" "s390-tools"
|
||||
.SH NAME
|
||||
lsluns \- list available LUNs
|
||||
|
||||
.SH SYNOPSIS
|
||||
.B lsluns
|
||||
.RB [ \-h]
|
||||
.RB [ \-v]
|
||||
.RB [ \-c
|
||||
.IR adapter id:0.0.XXXX ]
|
||||
.RB [ \-p
|
||||
.IR port number:0xXXXXXXXXXXXXXXXX ]
|
||||
|
||||
.SH DESCRIPTION
|
||||
.PP
|
||||
.B lsluns
|
||||
does a listing of all available LUNs.
|
||||
|
||||
The default is to list all LUNs that are found. The listing can be
|
||||
limited by specifying an adapter or a port.
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.B -h, --help
|
||||
Print help message and exit.
|
||||
.TP
|
||||
.B -v, --version
|
||||
Display version info and exit.
|
||||
.TP
|
||||
.B -c, --ccw
|
||||
Shows LUNs for a specific adapter.
|
||||
.TP
|
||||
.B -p, --port
|
||||
Shows LUNs for a specific port.
|
||||
.TP
|
||||
.B -a, --active
|
||||
Shows all activated LUNs. In addition information is provided
|
||||
whether the LUN is encrypted or not. This is indicated with a bracketed X
|
||||
right after the LUN number.
|
||||
|
||||
.SH EXAMPLES
|
||||
.PP
|
||||
.IP "lsluns"
|
||||
.RS
|
||||
Shows all available LUNs.
|
||||
.RE
|
||||
.IP "lsluns -c 0.0.3922"
|
||||
Shows all LUNs found on adapter 0.0.3922.
|
||||
.IP "lsluns -p 0x5005123456789000"
|
||||
Shows all LUNs for port 0x5005123456789000.
|
||||
.IP "lsluns -c 0.0.3922 -p 0x5005123456789000"
|
||||
Shows all LUNs for adapter 0.0.3922 and port 0x5005123456789000.
|
||||
.IP "lsluns -a "
|
||||
adapter = 0.0.3c02
|
||||
port = 0x500507630300c562
|
||||
lun = 0x401040a200000000(X) /dev/sg0 Disk IBM:2107900
|
||||
lun = 0x401040a300000000 /dev/sg1 Disk IBM:2107900
|
||||
|
||||
Shows all active LUNs including the information whether the device is encrypted or not.
|
||||
Executable
+172
@@ -0,0 +1,172 @@
|
||||
#!/usr/bin/perl
|
||||
#
|
||||
# lsmem - Tool to show memory hotplug status
|
||||
#
|
||||
# Copyright IBM Corp. 2010, 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.
|
||||
#
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Getopt::Long qw(:config no_ignore_case no_auto_abbrev);
|
||||
use File::Basename;
|
||||
|
||||
my $script_name = fileparse($0);
|
||||
my $memdir = "/sys/devices/system/memory";
|
||||
my $block_size = 0;
|
||||
my $list_all = 0;
|
||||
my $dev_size = 0;
|
||||
my @entries;
|
||||
|
||||
|
||||
sub lsmem_read_attr($$$$)
|
||||
# parameters: state, rem, device, block_nr
|
||||
{
|
||||
my @attributes = qw(state removable phys_device);
|
||||
foreach (0..2) {
|
||||
$_[$_] = `cat $memdir/memory$_[3]/$attributes[$_]`;
|
||||
chomp($_[$_]);
|
||||
}
|
||||
}
|
||||
|
||||
sub lsmem_get_dev_size()
|
||||
{
|
||||
my ($device, $old_device, $block, $old_block) = (0, 0, 0, 0);
|
||||
|
||||
foreach (@entries) {
|
||||
$_ =~ /memory(\d+)/;
|
||||
$block = $1;
|
||||
$device = `cat $_/phys_device`;
|
||||
chomp($device);
|
||||
if ($device > $old_device) {
|
||||
$dev_size = int((($block - $old_block) * $block_size) /
|
||||
($device - $old_device));
|
||||
last;
|
||||
}
|
||||
$dev_size += $block_size;
|
||||
$old_block = $block;
|
||||
}
|
||||
}
|
||||
|
||||
sub lsmem_list()
|
||||
{
|
||||
my $block = 0;
|
||||
my ($start, $end, $size) = (0, 0, 0);
|
||||
my ($state, $next_state) = (0, 0);
|
||||
my ($rem, $next_rem) = (0, 0);
|
||||
my ($device, $next_device, $end_dev) = (0, 0, 0);
|
||||
my ($mem_online, $mem_offline) = (0, 0);
|
||||
my $mem_hole;
|
||||
|
||||
$block_size = `cat $memdir/block_size_bytes`;
|
||||
chomp($block_size);
|
||||
if ($block_size =~ /(?:0x)?([[:xdigit:]]+)/) {
|
||||
$block_size = unpack("Q", pack("H16",
|
||||
substr("0" x 16 . $1, -16)));
|
||||
$block_size = $block_size >> 20;
|
||||
} else {
|
||||
die "lsmem: Unknown block size format in sysfs.\n";
|
||||
}
|
||||
lsmem_get_dev_size();
|
||||
# Start with $mem_hole = 1 to initialize $start, $state, $rem, $device
|
||||
$mem_hole = 1;
|
||||
|
||||
print <<HERE;
|
||||
Address Range Size (MB) State Removable Device
|
||||
===============================================================================
|
||||
HERE
|
||||
foreach (@entries) {
|
||||
$_ =~ /memory(\d+)/;
|
||||
$block = $1;
|
||||
if ($mem_hole) {
|
||||
lsmem_read_attr($state, $rem, $device, $block);
|
||||
$start = ($block) * ($block_size << 20);
|
||||
$mem_hole = 0;
|
||||
}
|
||||
# check next block or memory hole
|
||||
$block++;
|
||||
if (-d "$memdir/memory".$block) {
|
||||
lsmem_read_attr($next_state, $next_rem, $next_device,
|
||||
$block);
|
||||
} else {
|
||||
$mem_hole = 1
|
||||
}
|
||||
if ($state ne $next_state || $rem != $next_rem || $list_all ||
|
||||
$mem_hole) {
|
||||
$end = ($block) * ($block_size << 20) - 1;
|
||||
$size = ($end - $start + 1) >> 20;
|
||||
if ($state eq "going-offline") {
|
||||
$state = "on->off";
|
||||
}
|
||||
printf("0x%016x-0x%016x %10lu %-7s ", $start, $end,
|
||||
$size, $state);
|
||||
if ($state eq "online") {
|
||||
printf(" %-9s ", $rem ? "yes" : "no");
|
||||
$mem_online += $size;
|
||||
} else {
|
||||
printf(" %-9s ", "-");
|
||||
$mem_offline += $size;
|
||||
}
|
||||
$end_dev = ($end / $dev_size) >> 20;
|
||||
if ($device == $end_dev) {
|
||||
printf("%d\n", $device);
|
||||
} else {
|
||||
printf("%d-%d\n", $device, $end_dev);
|
||||
}
|
||||
$state = $next_state;
|
||||
$rem = $next_rem;
|
||||
$device = $end_dev + 1;
|
||||
$start = $end + 1;
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
printf("Memory device size : %lu MB\n", $dev_size);
|
||||
printf("Memory block size : %lu MB\n", $block_size);
|
||||
printf("Total online memory : %lu MB\n", $mem_online);
|
||||
printf("Total offline memory: %lu MB\n", $mem_offline);
|
||||
}
|
||||
|
||||
sub lsmem_usage()
|
||||
{
|
||||
print <<HERE;
|
||||
Usage: $script_name [OPTIONS]
|
||||
|
||||
The $script_name command lists the ranges of available memory with their online
|
||||
status. The listed memory blocks correspond to the memory block representation
|
||||
in sysfs. The command also shows the memory block size, the device size, and
|
||||
the amount of memory in online and offline state.
|
||||
|
||||
OPTIONS
|
||||
-a, --all
|
||||
List each individual memory block, instead of combining memory blocks
|
||||
with similar attributes.
|
||||
|
||||
-h, --help
|
||||
Print a short help text, then exit.
|
||||
|
||||
-v, --version
|
||||
Print the version number, then exit.
|
||||
HERE
|
||||
}
|
||||
|
||||
sub lsmem_version()
|
||||
{
|
||||
print "$script_name: version %S390_TOOLS_VERSION%\n";
|
||||
print "Copyright IBM Corp. 2010, 2017\n";
|
||||
}
|
||||
|
||||
|
||||
# Main
|
||||
unless (GetOptions('v|version' => sub {lsmem_version(); exit 0;},
|
||||
'h|help' => sub {lsmem_usage(); exit 0;},
|
||||
'a|all' => \$list_all)) {
|
||||
die "Try '$script_name --help' for more information.\n";
|
||||
};
|
||||
|
||||
@entries = (sort {length($a) <=> length($b) || $a cmp $b} <$memdir/memory*>);
|
||||
if (@entries == 0) {
|
||||
die "lsmem: No memory hotplug interface in sysfs ($memdir).\n";
|
||||
}
|
||||
lsmem_list();
|
||||
@@ -0,0 +1,73 @@
|
||||
.\" Copyright 2017 IBM Corp.
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
.TH LSMEM 8 "Apr 2010" s390\-tools
|
||||
.
|
||||
.
|
||||
.SH NAME
|
||||
lsmem \- list the ranges of available memory with their online status.
|
||||
.
|
||||
.
|
||||
.SH SYNOPSIS
|
||||
.B lsmem
|
||||
.RB [OPTIONS]
|
||||
.
|
||||
.
|
||||
.SH DESCRIPTION
|
||||
The lsmem command lists the ranges of available memory with their online
|
||||
status. The listed memory blocks correspond to the memory block representation
|
||||
in sysfs. The command also shows the memory block size, the device size, and
|
||||
the amount of memory in online and offline state.
|
||||
.
|
||||
.SS "Column description"
|
||||
.
|
||||
.TP 4
|
||||
Address Range
|
||||
Start and end address of the memory range.
|
||||
.
|
||||
.TP 4
|
||||
Size
|
||||
Size of the memory range in MB (1024 x 1024 bytes).
|
||||
.
|
||||
.TP 4
|
||||
State
|
||||
Indication of the online status of the memory range. State on->off means
|
||||
that the address range is in transition from online to offline.
|
||||
.
|
||||
.TP 4
|
||||
Removable
|
||||
"yes" if the memory range can be set offline, "no" if it cannot be set offline.
|
||||
A dash ("\-") means that the range is already offline.
|
||||
.
|
||||
.TP 4
|
||||
Device
|
||||
Device number or numbers that correspond to the memory range.
|
||||
|
||||
Each device represents a memory unit for the hypervisor in control of the
|
||||
memory. The hypervisor cannot reuse a memory unit unless the corresponding
|
||||
memory range is completely offline. For best memory utilization, each device
|
||||
should either be completely online or completely offline.
|
||||
|
||||
The chmem command with the size parameter automatically chooses the best suited
|
||||
device or devices when setting memory online or offline. The device size depends
|
||||
on the hypervisor and on the amount of total online and offline memory.
|
||||
.
|
||||
.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.BR \-a ", " \-\-all
|
||||
List each individual memory block, instead of combining memory blocks with
|
||||
similar attributes.
|
||||
.
|
||||
.TP
|
||||
.BR \-h ", " \-\-help
|
||||
Print a short help text, then exit.
|
||||
.
|
||||
.TP
|
||||
.BR \-v ", " \-\-version
|
||||
Print the version number, then exit.
|
||||
.
|
||||
.
|
||||
.SH SEE ALSO
|
||||
.BR chmem (8)
|
||||
Executable
+429
@@ -0,0 +1,429 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# lstape - Tool to show information about tape devices
|
||||
#
|
||||
# Copyright IBM Corp. 2003, 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.
|
||||
#
|
||||
|
||||
CMD=$(basename $0)
|
||||
SG_INQ=$(type -p sg_inq)
|
||||
|
||||
function RequireArgument() {
|
||||
if [ $2 -eq 1 ]; then
|
||||
echo "The $1 option requires an argument" >&2
|
||||
else
|
||||
echo "The $1 option requires $2 arguments" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
function PrintUsage() {
|
||||
cat <<-EOD | cut -c2-
|
||||
:Usage: $(basename $0) [<options>]
|
||||
:
|
||||
: <options>
|
||||
: -h|--help
|
||||
: Print this help text.
|
||||
: --ccw-only|--scsi-only
|
||||
: Limit output to CCW or SCSI devices only.
|
||||
: -t|--type <list of device types>
|
||||
: Limit output of CCW tape devices to the given
|
||||
: devices. The list consists of device types
|
||||
: separated by ','. (e.g. -t 3480,3490)
|
||||
: --online|--offline
|
||||
: Show only devices that are either online or
|
||||
: offline (only one option is allowed). This
|
||||
: option only affects CCW devices.
|
||||
: -s|--shortid
|
||||
: Show only devices on channel subsytem 0 with
|
||||
: subchannel set 0 and remove the leading '0.0.'
|
||||
: from the displayed bus id (only affects the
|
||||
: output of CCW devices).
|
||||
: -V|--verbose
|
||||
: Print additional information that does not fit
|
||||
: into a single line (only affects the output for
|
||||
: SCSI devices).
|
||||
: -v|--version
|
||||
: Display the version of the tools package and
|
||||
: the lstape command.
|
||||
EOD
|
||||
}
|
||||
|
||||
function PrintVersion()
|
||||
{
|
||||
cat <<-EOD
|
||||
$CMD: version %S390_TOOLS_VERSION%
|
||||
Copyright IBM Corp. 2003, 2017
|
||||
EOD
|
||||
}
|
||||
|
||||
FLSEP=""
|
||||
DEVLIST="3480,3490,3590"
|
||||
SHORTID=false
|
||||
SHOWCCW=true
|
||||
VERBOSE=false
|
||||
SHOWSCSI=true
|
||||
FILTERONLINE=false
|
||||
FILTEROFFLINE=false
|
||||
|
||||
while [ $# -ne 0 ]; do
|
||||
case $1 in
|
||||
-h|--help)
|
||||
PrintUsage
|
||||
exit 0
|
||||
;;
|
||||
-t|--type)
|
||||
if [ $# -lt 2 ]; then
|
||||
RequireArgument $1 1
|
||||
exit 1
|
||||
fi
|
||||
shift
|
||||
DEVLIST="$1"
|
||||
;;
|
||||
--online)
|
||||
if $FILTEROFFLINE; then
|
||||
echo -n "Option --online and --offline " >&2
|
||||
echo "are exclusive" >&2
|
||||
exit 1
|
||||
fi
|
||||
FILTERONLINE=true
|
||||
;;
|
||||
--offline)
|
||||
if $FILTERONLINE; then
|
||||
echo -n "Option --online and --offline " >&2
|
||||
echo "are exclusive" >&2
|
||||
exit 1
|
||||
fi
|
||||
FILTEROFFLINE=true
|
||||
;;
|
||||
-s|--shortid)
|
||||
SHORTID=true
|
||||
;;
|
||||
-v|--version)
|
||||
PrintVersion
|
||||
exit 0
|
||||
;;
|
||||
-V|--verbose)
|
||||
VERBOSE=true;
|
||||
;;
|
||||
--ccw-only)
|
||||
if ! $SHOWCCW; then
|
||||
echo "ERROR: --ccw-only after --scsi-only!" >&2
|
||||
exit 1
|
||||
fi
|
||||
SHOWSCSI=false;
|
||||
;;
|
||||
--scsi-only)
|
||||
if ! $SHOWSCSI; then
|
||||
echo "ERROR: --scsi-only after --ccw-only!" >&2
|
||||
exit 1
|
||||
fi
|
||||
SHOWCCW=false
|
||||
;;
|
||||
-*|--*)
|
||||
echo "$CMD: Invalid option $1" >&2
|
||||
echo "Try 'lstape --help' for more information." >&2
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
FILTERLIST="$FILTERLIST$FLSEP$1"
|
||||
FLSEP=","
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
function SysfsCreateListCCW() {
|
||||
(
|
||||
find $1/bus/ccw/drivers/tape_34xx -type l \
|
||||
-printf '%f\n' 2>/dev/null
|
||||
find $1/bus/ccw/drivers/tape_3590 -type l \
|
||||
-printf '%f\n' 2>/dev/null
|
||||
) | awk -v format="$CCWFORMAT" '
|
||||
BEGIN{
|
||||
medium_state_str[0] = "UNKNOWN "
|
||||
medium_state_str[1] = "LOADED "
|
||||
medium_state_str[2] = "UNLOADED"
|
||||
|
||||
split("'$DEVLIST'", A, ",")
|
||||
for(i = 1; i in A; i++) {
|
||||
devlist[A[i]] = 1
|
||||
}
|
||||
shortid = "'$SHORTID'"=="true" ? 1 : 0
|
||||
filteronline = "'$FILTERONLINE'"=="true" ? 1 : 0
|
||||
filteroffline = "'$FILTEROFFLINE'"=="true" ? 1 : 0
|
||||
}
|
||||
function Read(file) {
|
||||
value = ""
|
||||
getline value <file
|
||||
close(file)
|
||||
return value
|
||||
}
|
||||
{
|
||||
bus_id = tolower($1)
|
||||
devdir = "'$1'/bus/ccw/devices/" bus_id
|
||||
|
||||
devtype = Read(devdir "/devtype")
|
||||
split(devtype, A, "/")
|
||||
if (!(A[1] in devlist))
|
||||
next
|
||||
|
||||
online = Read(devdir "/online")
|
||||
if (filteronline && !online)
|
||||
next
|
||||
if (filteroffline && online)
|
||||
next
|
||||
|
||||
cutype = Read(devdir "/cutype")
|
||||
majmin = Read(devdir "/non-rewinding/dev")
|
||||
if (majmin != "") {
|
||||
split(majmin, A, ":")
|
||||
first_minor = A[2]
|
||||
} else {
|
||||
first_minor = Read(devdir "/first_minor")
|
||||
}
|
||||
if(first_minor == "")
|
||||
next
|
||||
|
||||
if (online) {
|
||||
medium_state = Read(devdir "/medium_state")
|
||||
state = Read(devdir "/state")
|
||||
operation = Read(devdir "/operation")
|
||||
blocksize = Read(devdir "/blocksize")
|
||||
if (blocksize == 0)
|
||||
blocksize = "auto"
|
||||
} else {
|
||||
state = "OFFLINE"
|
||||
operation = "---"
|
||||
blocksize = "N/A"
|
||||
}
|
||||
|
||||
if (shortid) {
|
||||
if (substr(bus_id, 1, 4) != "0.0.")
|
||||
next
|
||||
bus_id = substr(bus_id, 5)
|
||||
}
|
||||
|
||||
printf(format,
|
||||
(online==0) ? "N/A" : first_minor / 2,
|
||||
bus_id,
|
||||
tolower(cutype),
|
||||
tolower(devtype),
|
||||
blocksize,
|
||||
state,
|
||||
operation,
|
||||
(online==0) ? "N/A" : \
|
||||
medium_state_str[medium_state])
|
||||
}
|
||||
' | sort
|
||||
}
|
||||
|
||||
function SysfsCreateListSCSI()
|
||||
{
|
||||
for SCSI_DEV in $1/bus/scsi/devices/*:*:*:*; do
|
||||
if [ ! -e $SCSI_DEV ]; then
|
||||
continue
|
||||
fi
|
||||
TYPE=$(cat $SCSI_DEV/type)
|
||||
case $TYPE in
|
||||
1)
|
||||
DEV_TYPE=tapedrv
|
||||
DEV_NAME=IBMtape
|
||||
;;
|
||||
8)
|
||||
DEV_TYPE=changer
|
||||
DEV_NAME=IBMchanger
|
||||
;;
|
||||
*)
|
||||
continue
|
||||
esac
|
||||
SCSI_LIST=$(ls -1d $SCSI_DEV/*)
|
||||
SCSI_ID=$(basename $SCSI_DEV)
|
||||
VENDOR=$(cat $SCSI_DEV/vendor)
|
||||
MODEL=$(cat $SCSI_DEV/model)
|
||||
STATE=$(cat $SCSI_DEV/state)
|
||||
SG_DEV=$(echo $SCSI_DEV/scsi_generic*)
|
||||
|
||||
if [ -h $SG_DEV ]; then
|
||||
# deprecated sysfs layout
|
||||
SG_DEV=$(echo $SG_DEV | awk -F: '{print $NF}')
|
||||
else
|
||||
SG_DEV=$(basename $SG_DEV/*)
|
||||
fi
|
||||
|
||||
if [ "$SG_INQ" != "" ]; then
|
||||
TAPE_SERIAL=$(
|
||||
sg_inq /dev/$SG_DEV |
|
||||
awk '/serial/{print $NF}'
|
||||
)
|
||||
else
|
||||
TAPE_SERIAL="NO/INQ"
|
||||
fi
|
||||
|
||||
TAPE_DEV="N/A"
|
||||
if [ "$(echo "$SCSI_LIST"|grep scsi_tape)" != "" ]; then
|
||||
if [ -d $SCSI_DEV/scsi_tape ]; then
|
||||
TAPE_IDX=$(echo $SCSI_DEV/scsi_tape/st*[0-9] |
|
||||
sed -e 's/.*scsi_tape\///')
|
||||
else
|
||||
# deprecated sysfs layout
|
||||
TAPE_IDX=$(
|
||||
echo "$SCSI_LIST" |
|
||||
awk -F: '/scsi_tape\:st[0-9]+$/{print $NF}'
|
||||
)
|
||||
fi
|
||||
if [ "$TAPE_IDX" != "" ]; then
|
||||
TAPE_DEV=$TAPE_IDX
|
||||
fi
|
||||
elif [ "$(echo "$SCSI_LIST"|grep scsi_changer)" != "" ]; then
|
||||
if [ -d $SCSI_DEV/scsi_changer ]; then
|
||||
CHG_IDX=$(echo $SCSI_DEV/scsi_changer/sch*[0-9] |
|
||||
sed -e 's/.*scsi_changer\///')
|
||||
else
|
||||
# deprecated sysfs layout
|
||||
CHG_IDX=$(
|
||||
echo "$SCSI_LIST" |
|
||||
awk -F: '/scsi_changer\:sch[0-9]+$/{print $NF}'
|
||||
)
|
||||
fi
|
||||
if [ "$CHG_IDX" != "" ]; then
|
||||
TAPE_DEV=$CHG_IDX
|
||||
fi
|
||||
elif [ -r /proc/scsi/$DEV_NAME ]; then
|
||||
if [ "$TAPE_SERIAL" != "NO/INQ" ]; then
|
||||
IBM_IDX=$(
|
||||
awk '$3 == "'$TAPE_SERIAL'"{
|
||||
print $1
|
||||
}' /proc/scsi/$DEV_NAME
|
||||
)
|
||||
if [ "$IBM_IDX" != "" ]; then
|
||||
TAPE_DEV=$DEV_NAME$IBM_IDX
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
printf "$SCSIFORMAT" \
|
||||
$SG_DEV \
|
||||
$TAPE_DEV \
|
||||
$SCSI_ID \
|
||||
$VENDOR $MODEL \
|
||||
$DEV_TYPE \
|
||||
$STATE
|
||||
|
||||
if $VERBOSE; then
|
||||
printf "$SCSIVFORMAT" \
|
||||
$(cat $SCSI_DEV/hba_id) \
|
||||
$(cat $SCSI_DEV/wwpn) \
|
||||
$TAPE_SERIAL
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
case $(uname -r|cut -d. -f1-2) in
|
||||
1.*|2.[012])
|
||||
echo "Not supported!" >&2
|
||||
exit 1
|
||||
;;
|
||||
2.3|2.4)
|
||||
SYSFS=false
|
||||
;;
|
||||
*)
|
||||
SYSFS=true
|
||||
if [ "$(cat /proc/filesystems|grep sysfs)" = "" ]; then
|
||||
echo "WARNING: no sysfs support." >&2
|
||||
SYSFS=false
|
||||
fi
|
||||
SYSFSDIR=$(cat /proc/mounts|awk '$3=="sysfs"{print $2; exit}')
|
||||
if [ "SYSFS" = "true" -a "$SYSFSDIR" = "" ]; then
|
||||
echo "WARNING: sysfs not mounted." >&2
|
||||
SYSFS=false
|
||||
fi
|
||||
if [ "$SYSFS" = "false" -a "$SHOWCCW" = "true" ]; then
|
||||
echo -n "WARNING: proc interface will not find offline"
|
||||
echo " devices on kernel $(uname -r|cut -d. -f1-2)."
|
||||
echo
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
function ShowTapesCCW()
|
||||
{
|
||||
if $SYSFS; then
|
||||
SysfsCreateListCCW $SYSFSDIR
|
||||
else
|
||||
if [ ! -r /proc/tapedevices ]; then
|
||||
echo "ERROR: neither proc nor sysfs found!" >&2
|
||||
return 1
|
||||
fi
|
||||
cat /proc/tapedevices
|
||||
fi | awk -v LIST="$FILTERLIST" '
|
||||
BEGIN{
|
||||
if(LIST != "")
|
||||
split(LIST, A, ",")
|
||||
}
|
||||
LIST != ""{
|
||||
for(i in A) {
|
||||
if(index($2, A[i]) > 0) {
|
||||
print $0
|
||||
next
|
||||
}
|
||||
}
|
||||
next
|
||||
}
|
||||
{
|
||||
print $0
|
||||
}
|
||||
'
|
||||
}
|
||||
|
||||
if $SHOWCCW; then
|
||||
CCWFORMAT="%-7s %-10s %-12s %-15s %-7s %-7s %-7s %-8s\n"
|
||||
LIST=$(ShowTapesCCW)
|
||||
|
||||
if [ "$LIST" = "" ]; then
|
||||
NUMCCW=0
|
||||
else
|
||||
NUMCCW=$(echo "$LIST"|wc -l)
|
||||
fi
|
||||
|
||||
if $SHOWSCSI; then
|
||||
echo "FICON/ESCON tapes (found $NUMCCW):"
|
||||
fi
|
||||
printf "$CCWFORMAT" "TapeNo" "BusID" "CuType/Model" "DevType/Model" \
|
||||
"BlkSize" "State" "Op" "MedState"
|
||||
if [ $NUMCCW -gt 0 ]; then
|
||||
echo "$LIST"
|
||||
fi
|
||||
fi
|
||||
if $SHOWSCSI; then
|
||||
SCSIFORMAT="%-7s %-13s %-12s %-8s %-16s %-8s %s\n"
|
||||
SCSIVFORMAT=" %-8s %-18s %s\n"
|
||||
LIST=$(SysfsCreateListSCSI $SYSFSDIR)
|
||||
|
||||
if [ "$LIST" = "" ]; then
|
||||
NUMSCSI=0
|
||||
else
|
||||
NUMSCSI=$(echo "$LIST"|wc -l)
|
||||
if $VERBOSE; then
|
||||
let "NUMSCSI/=2"
|
||||
fi
|
||||
fi
|
||||
|
||||
if $SHOWCCW; then
|
||||
echo
|
||||
echo "SCSI tape devices (found $NUMSCSI):"
|
||||
fi
|
||||
printf "$SCSIFORMAT" "Generic" "Device" "Target" "Vendor" "Model" \
|
||||
"Type" "State"
|
||||
if $VERBOSE; then
|
||||
printf "$SCSIVFORMAT" "HBA" "WWPN" "Serial"
|
||||
fi
|
||||
if [ $NUMSCSI -gt 0 ]; then
|
||||
echo "$LIST"
|
||||
fi
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
.\" Copyright 2017 IBM Corp.
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
.TH LSTAPE 8 "Jul 2007" "s390-tools"
|
||||
|
||||
.SH NAME
|
||||
lstape \- list tape devices.
|
||||
|
||||
.SH SYNOPSIS
|
||||
.TP 16
|
||||
.B lstape
|
||||
.RB [ -h | --help]
|
||||
.RB [ --scsi-only | --ccw-only ]
|
||||
.RB [ -v | --version ]
|
||||
.RB [ -V | --verbose ]
|
||||
.br
|
||||
.RB [ --online | --offline ]
|
||||
.RB [ -s ]
|
||||
.br
|
||||
.RB [ -t
|
||||
.IR <device-type> [, <device-type> ] "" ...]
|
||||
|
||||
.SH DESCRIPTION
|
||||
The lstape command lists all available tape devices on the current host. For
|
||||
channel attached tape devices this output is the same as the contents of
|
||||
/proc/tapedevices (which is obsolete) but also includes offline devices. By
|
||||
default all tape devices are displayed.
|
||||
|
||||
Since SCSI tape devices are accessed differently to channel attached tape
|
||||
devices they are only visible if they are known to the SCSI layer. There
|
||||
are at least two possible drivers that can claim a SCSI tape device and the
|
||||
lstape command tries to find out which one this is. For the generic tape
|
||||
and changer driver the device names start with "st" or "sch", while for the
|
||||
IBM tape driver this would be "IBMtape" or "IBMchanger". If "N/A" is shown,
|
||||
the correct driver could not be obtained.
|
||||
This happens for example if there is no sg_inq command installed which is
|
||||
required to read the drive's serial number which in turn is used to find out
|
||||
the device number of the IBM tape driver.
|
||||
|
||||
The serial number of a SCSI tape can be displayed with the --verbose option. If
|
||||
there is no sg_inq command available "NO/INQ" is shown as the tape's serial.
|
||||
|
||||
.SH OPTIONS
|
||||
.TP 8
|
||||
\fB-h\fR or \fB--help\fR
|
||||
Print help text.
|
||||
|
||||
.TP 8
|
||||
\fB-v\fR or \fB--version\fR
|
||||
Print the version of the s390-tools package and the command.
|
||||
|
||||
.TP
|
||||
\fB-V\fB or \fB--verbose\fR
|
||||
Adds additional information that does not fit into a single line of output.
|
||||
This is currently only used for SCSI devices.
|
||||
|
||||
.TP
|
||||
.BR --scsi-only | --ccw-only
|
||||
Limit output to either SCSI or channel attached tape devices. The output without
|
||||
SCSI devices is the same as it was with previous versions of this command.
|
||||
|
||||
.TP
|
||||
.BR -s | --shortid
|
||||
Using this option will list only tape devices that are in channel subsystem 0,
|
||||
with subchannel set 0. All other devices will be suppressed and the leading
|
||||
"0.0." for bus IDs of the remaining devices will be removed.
|
||||
Since this is specific to CCW devices this option has no effect on the output
|
||||
of SCSI tape devices.
|
||||
|
||||
.TP
|
||||
.BR --online | --offline
|
||||
Limit output to either online or offline devices. This filter has no effect
|
||||
on the output of SCSI devices.
|
||||
|
||||
.TP
|
||||
.BR -t | --type " \fI<device-type>\fR"
|
||||
Limit output to given device types (currently only applies to channel attached
|
||||
tape devices).
|
||||
|
||||
.TP
|
||||
\fB<device-type>\fR =
|
||||
Device type of devices that should be displayed (e.g. 3490).
|
||||
|
||||
.SH EXAMPLES
|
||||
\fBlstape\fR
|
||||
.RS
|
||||
List all tape devices that are available
|
||||
.RE
|
||||
|
||||
\fBlstape --ccw-only -t 3490 --online\fR
|
||||
.RS
|
||||
Show all 3490 CCW devices that are online.
|
||||
.RE
|
||||
Executable
+348
@@ -0,0 +1,348 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# lszfcp - Tool to display information about zfcp devices (adapters/ports/units)
|
||||
#
|
||||
# Copyright IBM Corp. 2006, 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.
|
||||
#
|
||||
|
||||
SCRIPTNAME='lszfcp'
|
||||
SYSFS=`cat /proc/mounts | grep -m1 sysfs | awk '{ print $2 }'`
|
||||
FC_CLASS=false
|
||||
|
||||
# Command line parameters
|
||||
VERBOSITY=0
|
||||
SHOW_HOSTS=0
|
||||
SHOW_PORTS=0
|
||||
SHOW_DEVICES=0
|
||||
SHOW_ATTRIBUTES=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()
|
||||
{
|
||||
if $FC_CLASS; then
|
||||
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
|
||||
|
||||
if [ $? -ne 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 <<EOF
|
||||
$SCRIPTNAME: version %S390_TOOLS_VERSION%
|
||||
Copyright IBM Corp. 2006, 2017
|
||||
EOF
|
||||
}
|
||||
|
||||
print_help()
|
||||
{
|
||||
cat <<EOF
|
||||
Usage: $SCRIPTNAME [OPTIONS]
|
||||
|
||||
Provide information contained in sysfs about zfcp adapters, ports and
|
||||
units that are online.
|
||||
|
||||
Mandatory arguments to long options are mandatory for short options too.
|
||||
|
||||
OPTIONS:
|
||||
-H, --hosts show host information (default)
|
||||
-P, --ports show remote port information
|
||||
-D, --devices show SCSI device information
|
||||
-b, --busid=BUSID select specific busid
|
||||
-p, --wwpn=WWPN select specific port name
|
||||
-l, --lun=LUN select specific LUN
|
||||
-a, --attributes show all attributes
|
||||
-V, --verbose show sysfs paths of associated class
|
||||
and bus devices
|
||||
-s, --sysfs=PATH use path as sysfs (for dbginfo archives)
|
||||
-h, --help print this help
|
||||
-v, --version print version information
|
||||
|
||||
EXAMPLE:
|
||||
List for all zfcp adapters, ports and units the names of their
|
||||
associated SCSI hosts, FC remote ports and SCSI devices.
|
||||
|
||||
#> 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_hosts()
|
||||
{
|
||||
HOST_LIST=`ls -dX $SYSFS/devices/css0/[0-9]*/[0-9]*/host[0-9]*`
|
||||
|
||||
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
|
||||
|
||||
if [ $VERBOSITY -eq 0 ]; then
|
||||
echo $ADAPTER $SCSI_HOST
|
||||
else
|
||||
echo $ADAPTER_PATH
|
||||
$FC_CLASS && echo "$SYSFS/class/fc_host/$SCSI_HOST"
|
||||
echo "$SYSFS/class/scsi_host/$SCSI_HOST"
|
||||
fi
|
||||
|
||||
if $SHOW_ATTRIBUTES; then
|
||||
echo 'Bus = "ccw"'
|
||||
show_attributes $ADAPTER_PATH
|
||||
|
||||
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" ];
|
||||
then
|
||||
read WWPN < $FC_PORT_PATH/port_name
|
||||
else
|
||||
continue
|
||||
fi
|
||||
|
||||
[ $WWPN != ${PAR_WWPN:-$WWPN} ] && continue
|
||||
|
||||
ADAPTER_PORT_PATH=`ls -d \
|
||||
$SYSFS/devices/css0/*/*/$WWPN/../host[0-9]*/$PORT |\
|
||||
awk -F "/../host" '{ print $1 }'`
|
||||
ADAPTER=`basename \`dirname $ADAPTER_PORT_PATH\``
|
||||
|
||||
[ $ADAPTER != ${PAR_BUSID:-$ADAPTER} ] && continue
|
||||
|
||||
if [ $VERBOSITY -eq 0 ]; then
|
||||
echo "$ADAPTER/$WWPN $PORT"
|
||||
else
|
||||
echo $ADAPTER_PORT_PATH
|
||||
echo $FC_PORT_PATH
|
||||
fi
|
||||
|
||||
if $SHOW_ATTRIBUTES; then
|
||||
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 [ -z "$SCSI_DEVICE_LIST" ]; then
|
||||
echo "Error: No fcp devices found."
|
||||
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
|
||||
|
||||
[ $LUN != ${PAR_LUN:-$LUN} ] && continue
|
||||
[ $WWPN != ${PAR_WWPN:-$WWPN} ] && continue
|
||||
[ $ADAPTER != ${PAR_BUSID:-$ADAPTER} ] && continue
|
||||
|
||||
if [ $VERBOSITY -eq 0 ]; then
|
||||
echo "$ADAPTER/$WWPN/$LUN `basename $SCSI_DEVICE_PATH`"
|
||||
else
|
||||
echo "`ls -d $SYSFS/devices/css0/*/$ADAPTER`/$WWPN/$LUN"
|
||||
echo ${SCSI_DEVICE_PATH%*/} # 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#*:}"
|
||||
|
||||
# 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; then
|
||||
echo 'Class = "scsi_device"'
|
||||
show_attributes "$SCSI_DEVICE_PATH"
|
||||
echo
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
##############################################################################
|
||||
|
||||
ARGS=`getopt --options ahvHPDVb:p:l:s: --longoptions \
|
||||
attributes,help,version,hosts,ports,devices,verbose,busid:,wwpn:,lun:,sysfs: \
|
||||
-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;;
|
||||
-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;;
|
||||
-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_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
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
.\" Copyright IBM Corp. 2006, 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.
|
||||
.\"
|
||||
.TH LSZFCP 8 "Mar 2008" "s390-tools"
|
||||
.SH NAME
|
||||
lszfcp \- list information about zfcp adapters, ports, and units
|
||||
|
||||
.SH SYNOPSIS
|
||||
.B lszfcp
|
||||
.RB [ \-hvVaHDP ]
|
||||
.RB [ \-b
|
||||
.IR busid ]
|
||||
.RB [ \-l
|
||||
.IR lun ]
|
||||
.RB [ \-p
|
||||
.IR wwpn ]
|
||||
.RB [ \-s
|
||||
.IR /path/to/sys ]
|
||||
|
||||
.SH DESCRIPTION
|
||||
.PP
|
||||
.B lszfcp
|
||||
provides information contained in sysfs about zfcp adapters, ports and
|
||||
units and its associated scsi_hosts, fc_hosts, fc_remote_ports and
|
||||
scsi_devices.
|
||||
|
||||
The default is to list busids of all zfcp adapters and their corresponding
|
||||
SCSI host names.
|
||||
|
||||
There are three output variants. Default (without options "-a" or
|
||||
"-V") is one line for each object. For adapters the busid and their
|
||||
corresponding SCSI host names are listed. For ports the pair
|
||||
"busid"/"wwpn" and their corresponding FC-remote-port names are listed.
|
||||
For units the triple "busid"/"wwpn"/"lun" and their corresponding SCSI
|
||||
device names are listed.
|
||||
|
||||
Option "-V" additionally shows the sysfs paths of interest for the
|
||||
listed object.
|
||||
|
||||
Option "-a" additionally shows all attributes of interest found in
|
||||
sysfs for the listed object.
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.B -a, --attributes
|
||||
Show all attributes of the specified objects.
|
||||
.TP
|
||||
.B -D, --devices
|
||||
List zfcp units and SCSI devices.
|
||||
.TP
|
||||
.B -H, --hosts
|
||||
List zfcp adapters and fc-hosts (default). Information is given only
|
||||
for adapters that are online (registered at the SCSI stack).
|
||||
.TP
|
||||
.B -P, --ports
|
||||
List zfcp ports and FC remote ports.
|
||||
.TP
|
||||
.B -b busid, --busid busid
|
||||
Show zfcp adapter, fc-host selected by busid.
|
||||
.TP
|
||||
.B -l lun, --lun lun
|
||||
List zfcp unit(s) and SCSI device(s) selected by lun. (Information for
|
||||
several units might be shown if devices with equivalent LUNs are
|
||||
configured for different adapters or ports.)
|
||||
.TP
|
||||
.B -p wwpn, --wwpn wwpn
|
||||
List zfcp port(s) and FC remote port(s) selected by wwpn. (Information
|
||||
for several ports might be shown if a remote port is configured for
|
||||
different adapters.)
|
||||
.TP
|
||||
.B -V, --verbose
|
||||
Generate verbose output. Display sysfs path names of class and bus
|
||||
devices that are of interest for this object.
|
||||
.TP
|
||||
.B -s, --sysfs /path/to/sys
|
||||
Use path as sysfs (for dbginfo archives).
|
||||
.TP
|
||||
.B -h, --help
|
||||
Print help message and exit.
|
||||
.TP
|
||||
.B -v, --version
|
||||
Display version info and exit.
|
||||
|
||||
.SH NOTE
|
||||
.PP
|
||||
Options "-b", "-p" or "-l" are of restricting nature. They limit the output
|
||||
to those adapters, ports or units that match the specified busid, wwpn and lun.
|
||||
If none of the options "-H", "-P" and "-D" are specified, "-b" implies "-H",
|
||||
"-p" implies "-P" and "-l" implies "-D".
|
||||
|
||||
.SH EXAMPLES
|
||||
.PP
|
||||
.IP "lszfcp -P -H -D -V"
|
||||
Show all device paths of all zfcp adapters, ports, units and its
|
||||
associated SCSI devices, SCSI hosts, FC hosts and FC remote ports
|
||||
.PP
|
||||
.IP "lszfcp -b 0.0.0815 -a"
|
||||
Show all attributes of ccw_device, scsi_host and fc_host which belong
|
||||
to the adapter with busid "0.0.0815".
|
||||
.IP "lszfcp -D -b 0.0.0815 -p 0x5005123456789000 -l 0x0000000000000000"
|
||||
Show the device which matches the given busid, wwpn and lun.
|
||||
.IP "lszfcp -b 0.0.0815 -p 0x5005123456789000 -l 0x0000000000000000"
|
||||
Show all adapters that match the given busid, all ports that match the given
|
||||
busid and wwpnn and show all units that match the given busid, wwpn and lun.
|
||||
.IP "lszfcp -b 0.0.0815 -p 0x5005123456789000 -l 0x0000000000000000 -H -P -D"
|
||||
Generates same output as previous example.
|
||||
.SH "SEE ALSO"
|
||||
.BR lscss (8)
|
||||
|
||||
Executable
+341
@@ -0,0 +1,341 @@
|
||||
#!/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;
|
||||
}
|
||||
|
||||
# currently requires bash version 3.0 or later
|
||||
|
||||
. /lib/s390-tools/znetcontrolunits
|
||||
|
||||
# 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"
|
||||
"LCS OSA"
|
||||
"OSX"
|
||||
"OSM"
|
||||
)
|
||||
|
||||
readonly -a CU_DEVNAME=(
|
||||
eth
|
||||
hsi
|
||||
ctc
|
||||
ctc
|
||||
ctc
|
||||
eth
|
||||
eth
|
||||
eth
|
||||
)
|
||||
|
||||
readonly -a CU_GROUPCHANNELS=(
|
||||
3
|
||||
3
|
||||
2
|
||||
2
|
||||
2
|
||||
2
|
||||
3
|
||||
3
|
||||
)
|
||||
|
||||
readonly -a CHPIDTYPES=(
|
||||
[0x10]=OSE
|
||||
[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
|
||||
3088/60
|
||||
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
|
||||
|
||||
function debug() {
|
||||
level=$1
|
||||
shift
|
||||
[ $DEBUG -ge $level ] && echo "$*" 1>&2
|
||||
}
|
||||
|
||||
# Searches for a match of argument 1 on the array $CU_TCPIP.
|
||||
# Returns 0 on success, 1 on failure.
|
||||
function search_cu_tcpip() {
|
||||
local scu=$1
|
||||
local i
|
||||
if [ "$scu" == "${CU_TCPIP[i]}" ]; then
|
||||
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 $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
|
||||
@@ -0,0 +1,19 @@
|
||||
include ../../common.mak
|
||||
|
||||
all: lsqeth
|
||||
|
||||
libs = $(rootdir)/libutil/libutil.a
|
||||
|
||||
lsqeth: lsqeth.o misc.o $(libs)
|
||||
|
||||
install: all
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 lsqeth $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man8
|
||||
$(INSTALL) -m 644 -c lsqeth.8 $(DESTDIR)$(MANDIR)/man8
|
||||
|
||||
clean:
|
||||
rm -f *.o lsqeth
|
||||
|
||||
.PHONY: all install clean
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
.\" Copyright 2017 IBM Corp.
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
.TH LSQETH 8 "Sep 2013" "s390-tools"
|
||||
|
||||
.SH NAME
|
||||
lsqeth \- list all qeth-based network devices with their corresponding
|
||||
settings.
|
||||
|
||||
.SH SYNOPSIS
|
||||
.TP 16
|
||||
.B lsqeth \f [ -p ]\fB \f [ -h ]\fB \f [ -v ]\fB \f <device>\f
|
||||
|
||||
.SH DESCRIPTION
|
||||
The lsqeth command lists all available qeth-based network devices with
|
||||
their attributes and the current status of these attributes. By default all devices
|
||||
are listed. It is also possible to list the attributes for a given device by
|
||||
specifying it as the last parameter.
|
||||
There are 3 different output schemas available. By default the output is printed in
|
||||
a 2 column table where the attributes are displayed on the left side with
|
||||
their current settings on the right side. The two other options are described
|
||||
below.
|
||||
|
||||
|
||||
|
||||
.SH OPTIONS
|
||||
|
||||
|
||||
.TP
|
||||
.BR -p | --proc
|
||||
List the qeth based network devices in the former /proc/qeth format.
|
||||
|
||||
.TP 8
|
||||
\fB-h\fR or \fB--help\fR
|
||||
Print help text.
|
||||
|
||||
.TP 8
|
||||
\fB-v\fR or \fB--version\fR
|
||||
Print the version of the s390-tools package and the command.
|
||||
|
||||
.TP
|
||||
\fB<device>\fR
|
||||
By specifying a device only the attributes for this device are displayed.
|
||||
|
||||
.SH EXAMPLES
|
||||
\fBlsqeth\fR
|
||||
.RS
|
||||
List all qeth based network devices in a 2 column table layout.
|
||||
.RE
|
||||
|
||||
\fBlsqeth -p\fR
|
||||
.RS
|
||||
List all qeth based network devices in the former /proc/qeth format.
|
||||
.RE
|
||||
|
||||
\fBlsqeth -p hsi2\fR
|
||||
.RS
|
||||
List all device attributes with their current settings for HiperSockets
|
||||
device hsi2 in the former /proc/qeth format.
|
||||
.RE
|
||||
|
||||
.SH AUTHOR
|
||||
.nf
|
||||
This man-page was written by Steffen Thoss <thoss@de.ibm.com>.
|
||||
.fi
|
||||
@@ -0,0 +1,647 @@
|
||||
/**
|
||||
* lsqeth - command of s390-tools
|
||||
*
|
||||
* List qeth-based network devices with their attributes
|
||||
*
|
||||
* Copyright IBM Corp. 2004, 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.
|
||||
*/
|
||||
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/sockios.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <argz.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgen.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "lib/util_base.h"
|
||||
#include "lib/util_file.h"
|
||||
#include "lib/util_opt.h"
|
||||
#include "lib/util_libc.h"
|
||||
#include "lib/util_panic.h"
|
||||
#include "lib/util_path.h"
|
||||
#include "lib/util_prg.h"
|
||||
#include "lib/util_rec.h"
|
||||
#include "lib/util_scandir.h"
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
#include "misc.h"
|
||||
|
||||
#define ID_FORMAT "^[[:xdigit:]]{1,2}[.][[:xdigit:]][.][[:xdigit:]]{4}$"
|
||||
#define MAX_ID_LENGTH 10
|
||||
#define PAGE_SIZE 4096
|
||||
|
||||
/*
|
||||
* Constants for CP call (taken from vmcp.h)
|
||||
*/
|
||||
#define VMCP_DEVICE_NODE "/dev/vmcp"
|
||||
#define VMCP_GETCODE _IOR(0x10, 1, int)
|
||||
#define VMCP_SETBUF _IOW(0x10, 2, int)
|
||||
#define VMCP_GETSIZE _IOR(0x10, 3, int)
|
||||
#define CP_BUF_SIZE 8192
|
||||
|
||||
/*
|
||||
* Private data
|
||||
*/
|
||||
static struct lsqeth_cmd_flags {
|
||||
bool proc_format;
|
||||
} cmd;
|
||||
|
||||
/*
|
||||
* Program configuration
|
||||
*/
|
||||
const struct util_prg prg = {
|
||||
.desc = "List all qeth-based network devices with their corresponding settings.\n"
|
||||
"\nINTERFACE"
|
||||
"\n List only attributes of specified interface",
|
||||
.args = "[INTERFACE]",
|
||||
.copyright_vec = {
|
||||
{
|
||||
.owner = "IBM Corp.",
|
||||
.pub_first = 2017,
|
||||
.pub_last = 2017,
|
||||
},
|
||||
UTIL_PRG_COPYRIGHT_END
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Command line options
|
||||
*/
|
||||
static struct util_opt opt_vec[] = {
|
||||
UTIL_OPT_SECTION("OPTIONS"),
|
||||
{
|
||||
.option = { "proc", no_argument, NULL, 'p'},
|
||||
.desc = "List all devices in the former /proc/qeth format"
|
||||
},
|
||||
UTIL_OPT_HELP,
|
||||
UTIL_OPT_VERSION,
|
||||
UTIL_OPT_END
|
||||
};
|
||||
|
||||
/*
|
||||
* Exchange content of field value via simple translation map
|
||||
*
|
||||
* If the attribute is required for translation (attibute name is present in
|
||||
* tr_attr_list[]) then the value of the attribute (if matches the dictionary
|
||||
* translation key) is exchanged by the target value of the translation
|
||||
* dictionary (sys_to_proc_dict[][2]).
|
||||
*
|
||||
*/
|
||||
static void tr_to_proc_format(char *val, const char *attr_name)
|
||||
{
|
||||
static const char *tr_attr_list[] = {
|
||||
"checksumming",
|
||||
"priority_queueing",
|
||||
"route4",
|
||||
"route6"
|
||||
};
|
||||
static const char *sys_to_proc_dict[][2] = {
|
||||
{"sw checksumming", "sw"},
|
||||
{"hw checksumming", "hw"},
|
||||
{"no checksumming", "no"},
|
||||
{"always queue 0", "always_q_0"},
|
||||
{"always queue 1", "always_q_1"},
|
||||
{"always queue 2", "always_q_2"},
|
||||
{"always queue 3", "always_q_3"},
|
||||
{"by precedence", "by_prec."},
|
||||
{"by type of service", "by_ToS"},
|
||||
{"by skb-priority", "by_skb"},
|
||||
{"by VLAN headers", "by_vlan"},
|
||||
{"primary router", "pri"},
|
||||
{"secondary router", "sec"},
|
||||
{"primary connector+", "p+c"},
|
||||
{"primary connector", "p.c"},
|
||||
{"secondary connector+", "s+c"},
|
||||
{"secondary connector", "s.c"},
|
||||
{"multicast router+", "mc+"},
|
||||
{"multicast router", "mc"},
|
||||
{NULL, NULL}
|
||||
};
|
||||
unsigned int i = 0;
|
||||
|
||||
if (misc_str_in_list(attr_name, tr_attr_list,
|
||||
ARRAY_SIZE(tr_attr_list))) {
|
||||
while (sys_to_proc_dict[i][0]) {
|
||||
if (strcmp(val, sys_to_proc_dict[i][0]) == 0) {
|
||||
strcpy(val, sys_to_proc_dict[i][1]);
|
||||
return;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Read IPv4 and IPv6 addresses from related sysfs entries for ipa/parp/vipa and
|
||||
* store each value at the end of *argz array. Return the number of entries stored.
|
||||
*/
|
||||
static int get_qethconf(const char *name, const char *if_name, char **argz,
|
||||
size_t *argz_len)
|
||||
{
|
||||
char *path;
|
||||
int count;
|
||||
|
||||
path = util_path_sysfs("class/net/%s/device/%s/add4", if_name, name);
|
||||
count = misc_argz_add_from_file(argz, argz_len, path);
|
||||
free(path);
|
||||
path = util_path_sysfs("class/net/%s/device/%s/add6", if_name, name);
|
||||
count += misc_argz_add_from_file(argz, argz_len, path);
|
||||
free(path);
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get checksumming information for specified interface
|
||||
*/
|
||||
static void ethtool_checksumming(char *buf, const char *if_name)
|
||||
{
|
||||
struct ethtool_value val;
|
||||
struct ifreq ifr;
|
||||
int fd, rc;
|
||||
|
||||
fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (fd < 0)
|
||||
errx(EXIT_FAILURE, "Internal error: cannot get SOCK_DGRAM socket");
|
||||
strncpy(ifr.ifr_name, if_name, IFNAMSIZ);
|
||||
val.cmd = ETHTOOL_GRXCSUM;
|
||||
ifr.ifr_data = (void *)&val;
|
||||
rc = ioctl(fd, SIOCETHTOOL, &ifr);
|
||||
close(fd);
|
||||
if (!rc && val.data)
|
||||
strcpy(buf, "hw");
|
||||
else
|
||||
strcpy(buf, "sw");
|
||||
}
|
||||
|
||||
/*
|
||||
* Call cp command and return the resulting lines as argz vector
|
||||
*
|
||||
* @param[in,out] argz argz vector allocated by argz_create
|
||||
* so the caller should free the allocated memory
|
||||
* @param[in,out] argz_len argz length
|
||||
* @param[in] fmt Format string specifying the cp command
|
||||
*/
|
||||
static void exec_cp(char **argz, size_t *argz_len, const char *fmt, ...)
|
||||
{
|
||||
int fd, response_code, response_size, rc;
|
||||
static const int buf_size = CP_BUF_SIZE;
|
||||
char *buf, *command;
|
||||
va_list ap;
|
||||
|
||||
fd = open(VMCP_DEVICE_NODE, O_RDWR);
|
||||
if (fd == -1)
|
||||
errx(EXIT_FAILURE, "Could not open device %s", VMCP_DEVICE_NODE);
|
||||
if (ioctl(fd, VMCP_SETBUF, &buf_size) == -1)
|
||||
errx(EXIT_FAILURE, "Could not set buffer size");
|
||||
UTIL_VASPRINTF(&command, fmt, ap);
|
||||
if (write(fd, command, strlen(command)) == -1)
|
||||
errx(EXIT_FAILURE, "Could not issue CP command");
|
||||
free(command);
|
||||
if (ioctl(fd, VMCP_GETCODE, &response_code) == -1)
|
||||
errx(EXIT_FAILURE, "Could not query return code");
|
||||
if (ioctl(fd, VMCP_GETSIZE, &response_size) == -1)
|
||||
errx(EXIT_FAILURE, "Could not query response size");
|
||||
buf = util_malloc(buf_size);
|
||||
rc = misc_read_buf(fd, buf, buf_size);
|
||||
if (rc == -1)
|
||||
errx(EXIT_FAILURE, "Could not read CP response");
|
||||
rc = argz_create_sep(buf, '\n', argz, argz_len);
|
||||
if (rc)
|
||||
errx(EXIT_FAILURE, "Memory allocation error for CP response processing");
|
||||
free(buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return card_type attribute value based on 'vmcp q v nic' output
|
||||
*/
|
||||
static char *print_LAN(const char *cdev0)
|
||||
{
|
||||
char *tok_vec[5], *tk, *result, *retstr, *devno, *entry;
|
||||
char *tail = "not coupled";
|
||||
size_t argz_len = 0;
|
||||
char *argz = NULL;
|
||||
unsigned int i;
|
||||
|
||||
devno = strrchr(cdev0, '.') + 1;
|
||||
exec_cp(&argz, &argz_len, "QUERY VIRTUAL NIC %s", devno);
|
||||
/* Process CP command output in argz format */
|
||||
if (argz_count(argz, argz_len) < 2)
|
||||
errx(EXIT_FAILURE, "Internal error: Unexpected result of 'vmcp q v nic' command");
|
||||
/* Tokenize second line */
|
||||
entry = argz_next(argz, argz_len, argz);
|
||||
memset(tok_vec, 0, sizeof(tok_vec));
|
||||
tk = strtok(entry, " \t");
|
||||
for (i = 0; i < ARRAY_SIZE(tok_vec) && tk; i++) {
|
||||
tok_vec[i] = tk;
|
||||
tk = strtok(NULL, " \t");
|
||||
}
|
||||
util_asprintf(&result, "%s %s %s", tok_vec[2], tok_vec[3], tok_vec[4]);
|
||||
/* Tokenize first line */
|
||||
entry = argz;
|
||||
memset(tok_vec, 0, sizeof(tok_vec));
|
||||
tk = strtok(entry, " \t");
|
||||
for (i = 0; i < ARRAY_SIZE(tok_vec) && tk; i++) {
|
||||
tok_vec[i] = tk;
|
||||
tk = strtok(NULL, " \t");
|
||||
}
|
||||
if (strncmp(result, "LAN", 3) == 0) {
|
||||
if (strncmp(result + 5, "* Internal", 11) != 0)
|
||||
tail = result + 5;
|
||||
util_asprintf(&retstr, "GuestLAN: %s (%s %s)",
|
||||
tail, tok_vec[2], tok_vec[3]);
|
||||
} else {
|
||||
util_asprintf(&retstr, "%s (%s %s)", result, tok_vec[2],
|
||||
tok_vec[3]);
|
||||
}
|
||||
free(result);
|
||||
free(argz);
|
||||
return retstr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update card_type field for Virtual NIC devices
|
||||
*/
|
||||
static void update_card_type(struct util_rec *rec)
|
||||
{
|
||||
const char *card_type, *cdev0;
|
||||
char *upd_card_type;
|
||||
|
||||
if (!util_path_is_readable("/dev/vmcp"))
|
||||
return;
|
||||
card_type = util_rec_get(rec, "card_type");
|
||||
if (!card_type)
|
||||
return;
|
||||
if (strncmp(card_type, "GuestLAN", 8) == 0 ||
|
||||
strncmp(card_type, "Virt.NIC", 8) == 0) {
|
||||
cdev0 = util_rec_get(rec, "cdev0");
|
||||
if (!cdev0)
|
||||
return;
|
||||
upd_card_type = print_LAN(cdev0);
|
||||
util_rec_set(rec, "card_type", upd_card_type);
|
||||
free(upd_card_type);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Process regular sysfs attribute and save it to the record.
|
||||
*/
|
||||
static void process_sysfs_attribute(struct util_rec *rec, const char *path,
|
||||
const char *attr_name, const char *if_name)
|
||||
{
|
||||
char buf[PAGE_SIZE];
|
||||
char *link = NULL;
|
||||
|
||||
/* Process cdev attributes (for normal output format only) */
|
||||
if (strncmp(attr_name, "cdev", 4) == 0 && !cmd.proc_format) {
|
||||
link = misc_link_target("%s/%s", path, attr_name);
|
||||
/* If no simlink found, cdev* record field will not be set */
|
||||
if (link) {
|
||||
util_rec_set(rec, attr_name, basename(link));
|
||||
free(link);
|
||||
}
|
||||
}
|
||||
/* Process other sysfs attributes */
|
||||
if (util_file_read_line(buf, sizeof(buf), "%s/%s",
|
||||
path, attr_name) == 0 &&
|
||||
strlen(buf) != 0 &&
|
||||
strcmp(buf, "n/a") != 0) {
|
||||
/* Translate attribute values to proc-format if required */
|
||||
if (cmd.proc_format)
|
||||
tr_to_proc_format(buf, attr_name);
|
||||
/* Hex notation for 'chpid' attribute in proc format */
|
||||
if (strcmp(attr_name, "chpid") == 0 &&
|
||||
cmd.proc_format)
|
||||
util_rec_set(rec, attr_name, "x%s", buf);
|
||||
else
|
||||
util_rec_set(rec, attr_name, buf);
|
||||
} else {
|
||||
/* Special case for 'checksumming' and 'route6' */
|
||||
if (strcmp(attr_name, "checksumming") == 0) {
|
||||
if (cmd.proc_format) {
|
||||
ethtool_checksumming(buf, if_name);
|
||||
util_rec_set(rec, attr_name, buf);
|
||||
}
|
||||
} else if (strcmp(attr_name, "route6") == 0 &&
|
||||
strcmp(buf, "n/a") == 0) {
|
||||
util_rec_set(rec, attr_name, "no");
|
||||
} else {
|
||||
/* Set 'n/a' default value for proc format*/
|
||||
if (cmd.proc_format)
|
||||
util_rec_set(rec, attr_name, "n/a");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Set ipa/parp/vipa attributes for normal format output.
|
||||
*/
|
||||
static void set_ipa_vipa_parp(struct util_rec *rec, const char *attr_name,
|
||||
const char *path, const char *if_name)
|
||||
{
|
||||
size_t argz_len = 0;
|
||||
char *argz = NULL;
|
||||
|
||||
if (strcmp(attr_name, "ipa") == 0) {
|
||||
if (!util_path_is_dir("%s/ipa_takeover", path))
|
||||
return;
|
||||
if (get_qethconf("ipa_takeover", if_name, &argz, &argz_len))
|
||||
util_rec_set_argz(rec, attr_name, argz, argz_len);
|
||||
} else if (strcmp(attr_name, "parp") == 0) {
|
||||
if (!util_path_is_dir("%s/rxip", path))
|
||||
return;
|
||||
if (get_qethconf("rxip", if_name, &argz, &argz_len))
|
||||
util_rec_set_argz(rec, attr_name, argz, argz_len);
|
||||
} else {
|
||||
if (!util_path_is_dir("%s/vipa", path))
|
||||
return;
|
||||
if (get_qethconf("vipa", if_name, &argz, &argz_len))
|
||||
util_rec_set_argz(rec, attr_name, argz, argz_len);
|
||||
}
|
||||
free(argz);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set devices attribute for proc format output: '<cdev0>/<cdev1>/<cdev2>'
|
||||
*/
|
||||
static void set_devices_fld(struct util_rec *rec, const char *path)
|
||||
{
|
||||
char buf[3*MAX_ID_LENGTH + 3] = "";
|
||||
char *link;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
link = misc_link_target("%s/cdev%d", path, i);
|
||||
/* File name is a link */
|
||||
if (i == 2 && link) {
|
||||
strcat(buf, basename(link));
|
||||
} else {
|
||||
if (link)
|
||||
strcat(strcat(buf, basename(link)), "/");
|
||||
else
|
||||
strcat(buf, "/");
|
||||
}
|
||||
free(link);
|
||||
}
|
||||
util_rec_set(rec, "devices", buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the attribute should be skipped for layer2 device
|
||||
*/
|
||||
static bool not_layer2_attr(const char *attr_name)
|
||||
{
|
||||
/* Layer3 specific attributes */
|
||||
static const char *layer3_vec[] = {
|
||||
"route4",
|
||||
"route6",
|
||||
"large_send",
|
||||
"fake_ll",
|
||||
"fake_broadcast",
|
||||
"checksumming",
|
||||
"hsuid",
|
||||
"sniffer"
|
||||
};
|
||||
|
||||
return misc_str_in_list(attr_name, layer3_vec, ARRAY_SIZE(layer3_vec));
|
||||
}
|
||||
|
||||
/*
|
||||
* Collect and print attributes for qeth-based device in sepcified format
|
||||
*/
|
||||
static void print_device(struct util_rec *rec, const char *device_id)
|
||||
{
|
||||
char *path, *path_net, *if_name;
|
||||
unsigned long int layer2 = 0;
|
||||
struct util_rec_fld *fld;
|
||||
const char *attr_name;
|
||||
char buf[PAGE_SIZE];
|
||||
|
||||
path = util_path_sysfs("bus/ccwgroup/drivers/qeth/%s", device_id);
|
||||
/* Process if_name attribute */
|
||||
if (util_file_read_line(buf, sizeof(buf), "%s/if_name", path) == 0)
|
||||
if_name = util_strdup(buf);
|
||||
else
|
||||
if_name = util_strdup("");
|
||||
util_rec_set(rec, "if_name", if_name);
|
||||
|
||||
/* Read layer2 attribute */
|
||||
path_net = util_path_sysfs("class/net");
|
||||
util_file_read_ul(&layer2, 10, "%s/%s/device/layer2",
|
||||
path_net, if_name);
|
||||
free(path_net);
|
||||
/* Iterate over each rec field */
|
||||
util_rec_iterate(rec, fld) {
|
||||
attr_name = util_rec_fld_get_key(fld);
|
||||
/* Skip layer3 attributes for layer2 device in normal format output */
|
||||
if (layer2 == 1 &&
|
||||
!cmd.proc_format &&
|
||||
not_layer2_attr(attr_name))
|
||||
continue;
|
||||
/* Skip if_name attribute(already processed) */
|
||||
if (strcmp(attr_name, "if_name") == 0)
|
||||
continue;
|
||||
/* Process 'devices' field for proc format output */
|
||||
if (strcmp(attr_name, "devices") == 0 && cmd.proc_format) {
|
||||
set_devices_fld(rec, path);
|
||||
continue;
|
||||
}
|
||||
/* Process ipa/parp/vipa attributes */
|
||||
if (strcmp(attr_name, "ipa") == 0 ||
|
||||
strcmp(attr_name, "vipa") == 0 ||
|
||||
strcmp(attr_name, "parp") == 0) {
|
||||
set_ipa_vipa_parp(rec, attr_name, path, if_name);
|
||||
continue;
|
||||
}
|
||||
/* Process other sysfs attributes */
|
||||
process_sysfs_attribute(rec, path, attr_name, if_name);
|
||||
}
|
||||
free(if_name);
|
||||
free(path);
|
||||
/* Print the record */
|
||||
if (!cmd.proc_format) {
|
||||
/* Check if card_type attribute needs to be modified */
|
||||
update_card_type(rec);
|
||||
/* Print record header for each device in normal format output */
|
||||
util_rec_print_hdr(rec);
|
||||
}
|
||||
util_rec_print(rec);
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup record with the fields needed for output in wide form
|
||||
*/
|
||||
static void setup_rec_wide(struct util_rec *rec)
|
||||
{
|
||||
util_rec_def(rec, "devices", UTIL_REC_ALIGN_LEFT, 26, "devices");
|
||||
util_rec_def(rec, "chpid", UTIL_REC_ALIGN_LEFT, 5, "CHPID");
|
||||
util_rec_def(rec, "if_name", UTIL_REC_ALIGN_LEFT, 16, "interface");
|
||||
util_rec_def(rec, "card_type", UTIL_REC_ALIGN_LEFT, 14, "cardtype");
|
||||
util_rec_def(rec, "portno", UTIL_REC_ALIGN_LEFT, 4, "port");
|
||||
util_rec_def(rec, "checksumming", UTIL_REC_ALIGN_LEFT, 6, "chksum");
|
||||
util_rec_def(rec, "priority_queueing", UTIL_REC_ALIGN_LEFT, 10,
|
||||
"prio-q'ing");
|
||||
util_rec_def(rec, "route4", UTIL_REC_ALIGN_LEFT, 4, "rtr4");
|
||||
util_rec_def(rec, "route6", UTIL_REC_ALIGN_LEFT, 4, "rtr6");
|
||||
util_rec_def(rec, "layer2", UTIL_REC_ALIGN_LEFT, 5, "lay'2");
|
||||
util_rec_def(rec, "buffer_count", UTIL_REC_ALIGN_LEFT, 5, "cnt");
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup record for output in long form
|
||||
*/
|
||||
static void setup_rec_long(struct util_rec *rec)
|
||||
{
|
||||
util_rec_def(rec, "if_name", UTIL_REC_ALIGN_LEFT, 0, "Device name");
|
||||
util_rec_def(rec, "card_type", UTIL_REC_ALIGN_LEFT, 0, "card_type");
|
||||
util_rec_def(rec, "cdev0", UTIL_REC_ALIGN_LEFT, 0, "cdev0");
|
||||
util_rec_def(rec, "cdev1", UTIL_REC_ALIGN_LEFT, 0, "cdev1");
|
||||
util_rec_def(rec, "cdev2", UTIL_REC_ALIGN_LEFT, 0, "cdev2");
|
||||
util_rec_def(rec, "chpid", UTIL_REC_ALIGN_LEFT, 0, "chpid");
|
||||
util_rec_def(rec, "online", UTIL_REC_ALIGN_LEFT, 0, "online");
|
||||
util_rec_def(rec, "portname", UTIL_REC_ALIGN_LEFT, 0, "portname");
|
||||
util_rec_def(rec, "portno", UTIL_REC_ALIGN_LEFT, 0, "portno");
|
||||
util_rec_def(rec, "route4", UTIL_REC_ALIGN_LEFT, 0, "route4");
|
||||
util_rec_def(rec, "route6", UTIL_REC_ALIGN_LEFT, 0, "route6");
|
||||
util_rec_def(rec, "checksumming", UTIL_REC_ALIGN_LEFT, 0,
|
||||
"checksumming");
|
||||
util_rec_def(rec, "state", UTIL_REC_ALIGN_LEFT, 0, "state");
|
||||
util_rec_def(rec, "priority_queueing", UTIL_REC_ALIGN_LEFT, 0,
|
||||
"priority_queueing");
|
||||
util_rec_def(rec, "detach_state", UTIL_REC_ALIGN_LEFT, 0,
|
||||
"detach_state");
|
||||
util_rec_def(rec, "fake_ll", UTIL_REC_ALIGN_LEFT, 0, "fake_ll");
|
||||
util_rec_def(rec, "fake_broadcast", UTIL_REC_ALIGN_LEFT, 0,
|
||||
"fake_broadcast");
|
||||
util_rec_def(rec, "buffer_count", UTIL_REC_ALIGN_LEFT, 0,
|
||||
"buffer_count");
|
||||
util_rec_def(rec, "add_hhlen", UTIL_REC_ALIGN_LEFT, 0, "add_hhlen");
|
||||
util_rec_def(rec, "layer2", UTIL_REC_ALIGN_LEFT, 0, "layer2");
|
||||
util_rec_def(rec, "large_send", UTIL_REC_ALIGN_LEFT, 0, "large_send");
|
||||
util_rec_def(rec, "isolation", UTIL_REC_ALIGN_LEFT, 0, "isolation");
|
||||
util_rec_def(rec, "hsuid", UTIL_REC_ALIGN_LEFT, 0, "hsuid");
|
||||
util_rec_def(rec, "sniffer", UTIL_REC_ALIGN_LEFT, 0, "sniffer");
|
||||
util_rec_def(rec, "bridge_role", UTIL_REC_ALIGN_LEFT, 0,
|
||||
"bridge_role");
|
||||
util_rec_def(rec, "bridge_state", UTIL_REC_ALIGN_LEFT, 0,
|
||||
"bridge_state");
|
||||
util_rec_def(rec, "bridge_hostnotify", UTIL_REC_ALIGN_LEFT, 0,
|
||||
"bridge_hostnotify");
|
||||
util_rec_def(rec, "bridge_reflect_promisc", UTIL_REC_ALIGN_LEFT, 0,
|
||||
"bridge_reflect_promisc");
|
||||
util_rec_def(rec, "switch_attrs", UTIL_REC_ALIGN_LEFT, 0,
|
||||
"switch_attrs");
|
||||
util_rec_def(rec, "ipa", UTIL_REC_ALIGN_LEFT, 0, "ipa");
|
||||
util_rec_def(rec, "vipa", UTIL_REC_ALIGN_LEFT, 0, "vipa");
|
||||
util_rec_def(rec, "parp", UTIL_REC_ALIGN_LEFT, 0, "parp");
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup the record according to the desired output format
|
||||
*/
|
||||
static struct util_rec *setup_rec()
|
||||
{
|
||||
struct util_rec *rec;
|
||||
|
||||
if (cmd.proc_format) {
|
||||
rec = util_rec_new_wide("-");
|
||||
setup_rec_wide(rec);
|
||||
} else {
|
||||
rec = util_rec_new_long("-", ":", "if_name", 30, 42);
|
||||
setup_rec_long(rec);
|
||||
}
|
||||
return rec;
|
||||
}
|
||||
|
||||
/*
|
||||
* Entry point
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char device[MAX_ID_LENGTH];
|
||||
struct dirent **de_vec;
|
||||
struct util_rec *rec;
|
||||
int i, c = 0, count;
|
||||
char *path, *link;
|
||||
|
||||
util_prg_init(&prg);
|
||||
util_opt_init(opt_vec, NULL);
|
||||
|
||||
while (c != -1) {
|
||||
c = util_opt_getopt_long(argc, argv);
|
||||
if (c == -1)
|
||||
break;
|
||||
switch (c) {
|
||||
case 'h':
|
||||
util_prg_print_help();
|
||||
util_opt_print_help();
|
||||
return EXIT_SUCCESS;
|
||||
case 'v':
|
||||
util_prg_print_version();
|
||||
return EXIT_SUCCESS;
|
||||
case 'p':
|
||||
cmd.proc_format = true;
|
||||
continue;
|
||||
default:
|
||||
util_opt_print_parse_error(c, argv);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
/* Error if more than 1 argument specified */
|
||||
if (argc > optind + 1) {
|
||||
warnx("Too many arguments");
|
||||
util_prg_print_parse_error();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
/* Create a proper record structure */
|
||||
rec = setup_rec();
|
||||
/* For proc output format print record header only once */
|
||||
if (cmd.proc_format)
|
||||
util_rec_print_hdr(rec);
|
||||
if (argc > optind) {
|
||||
/* Single argument specified (interface name), get related device_id */
|
||||
path = util_path_sysfs("class/net/");
|
||||
link = misc_link_target("%s/%s/device/cdev0",
|
||||
path, argv[optind]);
|
||||
free(path);
|
||||
if (link) {
|
||||
/* Interface present */
|
||||
snprintf(device, sizeof(device), "%s", basename(link));
|
||||
free(link);
|
||||
print_device(rec, device);
|
||||
free(rec);
|
||||
} else {
|
||||
errx(EXIT_FAILURE, "No such device: %s", argv[optind]);
|
||||
}
|
||||
} else {
|
||||
/* No optional arguments specified, process all available devices */
|
||||
path = util_path_sysfs("bus/ccwgroup/drivers/qeth/");
|
||||
count = util_scandir(&de_vec, alphasort, path, "%s",
|
||||
ID_FORMAT);
|
||||
free(path);
|
||||
for (i = 0; i < count; i++) {
|
||||
/* Check if a symbolic link */
|
||||
if (de_vec[i]->d_type != DT_LNK)
|
||||
continue;
|
||||
if (i > 0)
|
||||
rec = setup_rec();
|
||||
print_device(rec, de_vec[i]->d_name);
|
||||
free(rec);
|
||||
}
|
||||
util_scandir_free(de_vec, count);
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Misc - Local helper functions
|
||||
*
|
||||
* Copyright 2017 IBM Corp.
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <argz.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "lib/util_base.h"
|
||||
#include "lib/util_libc.h"
|
||||
#include "lib/util_panic.h"
|
||||
#include "lib/util_prg.h"
|
||||
|
||||
#include "misc.h"
|
||||
|
||||
#define STR_LEN 256
|
||||
|
||||
/*
|
||||
* Check if the specified string presents in the predefined string array.
|
||||
*
|
||||
* @param[in].--str String to search for.
|
||||
* @param[in].--strings[] Array of strings to look through.
|
||||
*
|
||||
* @retval.-----true Equal string presents in the array.
|
||||
* @retval.-----false String does not found in the array.
|
||||
*/
|
||||
bool misc_str_in_list(const char *str, const char *strings[], int array_size)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < array_size; i++) {
|
||||
if (strcmp(str, strings[i]) == 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the path where a symbolic link points to
|
||||
*
|
||||
* @param[in] fmt Format string for path
|
||||
* @param[in] ... Variable arguments for format string
|
||||
*
|
||||
* @retval !=0 Pointer to a string with the path name
|
||||
* @retval NULL Error case
|
||||
*/
|
||||
char *misc_link_target(const char *fmt, ...)
|
||||
{
|
||||
char *lnk, *path;
|
||||
va_list ap;
|
||||
ssize_t rc;
|
||||
|
||||
path = util_malloc(PATH_MAX);
|
||||
/* Construct the file name */
|
||||
UTIL_VASPRINTF(&lnk, fmt, ap);
|
||||
rc = readlink(lnk, path, PATH_MAX);
|
||||
free(lnk);
|
||||
if (rc < 0) {
|
||||
free(path);
|
||||
return NULL;
|
||||
}
|
||||
util_assert(rc < (PATH_MAX - 1),
|
||||
"Internal error: Symlink name too long");
|
||||
path[rc] = '\0';
|
||||
return path;
|
||||
}
|
||||
|
||||
/*
|
||||
* Adds the strings read from the file to the end of the
|
||||
* array **argz and updates **argz and *argz_len
|
||||
*
|
||||
* @param[in,out] argz argz vector
|
||||
* @param[in,out] argz_len argz length
|
||||
* @param[in] fmt Format string for path
|
||||
* @param[in] ... Variable arguments for format string
|
||||
*
|
||||
* @retval !0 Number of string elemnts added to argz array
|
||||
* @retval 0 File is empty or cannot be processed
|
||||
*/
|
||||
int misc_argz_add_from_file(char **argz, size_t *argz_len, const char *fmt, ...)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
char str[STR_LEN];
|
||||
int count = 0;
|
||||
va_list ap;
|
||||
FILE *fp;
|
||||
|
||||
/* Construct the file name */
|
||||
UTIL_VSPRINTF(path, fmt, ap);
|
||||
|
||||
/* Open the file for reading */
|
||||
fp = fopen(path, "r");
|
||||
if (!fp)
|
||||
return 0;
|
||||
errno = 0;
|
||||
/* Read the strings */
|
||||
while (fscanf(fp, "%s", str) == 1 && errno == 0) {
|
||||
argz_add(argz, argz_len, str);
|
||||
count++;
|
||||
}
|
||||
fclose(fp);
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read at most COUNT bytes from FD into memory at location BUF
|
||||
*
|
||||
* @param[in] fd File descriptor of the opened file
|
||||
* @param[in,out] buf Buffer for writing the result
|
||||
* @param[in] count Size of buffer
|
||||
*
|
||||
* @retval >=0 Number of bytes read on success
|
||||
* @retval -1 Read error
|
||||
*/
|
||||
ssize_t misc_read_buf(int fd, char *buf, size_t count)
|
||||
{
|
||||
ssize_t rc, done;
|
||||
|
||||
for (done = 0; done < (ssize_t)count; done += rc) {
|
||||
rc = read(fd, &buf[done], count - done);
|
||||
if (rc == -1 && errno == EINTR)
|
||||
continue;
|
||||
if (rc == -1)
|
||||
return -1;
|
||||
if (rc == 0)
|
||||
break;
|
||||
}
|
||||
return done;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Misc - Local helper functions
|
||||
*
|
||||
* Copyright 2017 IBM Corp.
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef MISC_H
|
||||
#define MISC_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
|
||||
|
||||
char *misc_link_target(const char *fmt, ...);
|
||||
bool misc_str_in_list(const char *str, const char *strings[], int array_size);
|
||||
int misc_argz_add_from_file(char **argz, size_t *argz_len,
|
||||
const char *fmt, ...);
|
||||
ssize_t misc_read_buf(int fd, char *buf, size_t count);
|
||||
|
||||
#endif /* MISC_H */
|
||||
@@ -0,0 +1,18 @@
|
||||
include ../../common.mak
|
||||
|
||||
all: lsscm
|
||||
|
||||
libs = $(rootdir)/libutil/libutil.a
|
||||
|
||||
lsscm: lsscm.o $(libs)
|
||||
|
||||
install: all
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 lsscm $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man8
|
||||
$(INSTALL) -m 644 -c lsscm.8 $(DESTDIR)$(MANDIR)/man8
|
||||
|
||||
clean:
|
||||
rm -f *.o lsscm
|
||||
|
||||
.PHONY: all install clean
|
||||
@@ -0,0 +1,71 @@
|
||||
.\" Copyright 2017 IBM Corp.
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
.TH LSCHP 8 "Jul 2012" s390\-tools
|
||||
|
||||
.SH NAME
|
||||
lsscm \- list information about available Storage Class Memory Increments.
|
||||
|
||||
.SH SYNOPSIS
|
||||
.B lsscm
|
||||
.RB [ \-h|\-\-help ]
|
||||
.RB [ \-v|\-\-version ]
|
||||
|
||||
.SH DESCRIPTION
|
||||
The lsscm command lists status and information about available
|
||||
Storage Class Memory Increments.
|
||||
|
||||
.B Column description:
|
||||
|
||||
SCM Increment
|
||||
.RS
|
||||
Starting address of the SCM increment.
|
||||
.RE
|
||||
|
||||
Size
|
||||
.RS
|
||||
Size of the block device representing the SCM increment.
|
||||
.RE
|
||||
|
||||
Name
|
||||
.RS
|
||||
Name of the block device representing the SCM increment.
|
||||
.RE
|
||||
|
||||
Rank
|
||||
.RS
|
||||
Rank (conceptual quality) of the SCM increment.
|
||||
.RE
|
||||
|
||||
D_state
|
||||
.RS
|
||||
Data state of the SCM increment.
|
||||
.RE
|
||||
|
||||
O_state
|
||||
.RS
|
||||
Operation state of the SCM increment.
|
||||
.RE
|
||||
|
||||
Pers
|
||||
.RS
|
||||
Persistence attribute.
|
||||
.RE
|
||||
|
||||
ResID
|
||||
.RS
|
||||
Resource identifier.
|
||||
.RE
|
||||
|
||||
|
||||
.SH OPTIONS
|
||||
.B \-h | \-\-help
|
||||
.RS
|
||||
Print a short help text, then exit.
|
||||
.RE
|
||||
|
||||
.B \-v | \-\-version
|
||||
.RS
|
||||
Print version number, then exit.
|
||||
.RE
|
||||
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* lsscm - Show information about Storage Class Memory Increments
|
||||
*
|
||||
* Copyright IBM Corp. 2016, 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.
|
||||
*/
|
||||
|
||||
#include <err.h>
|
||||
|
||||
#include "lib/util_base.h"
|
||||
#include "lib/util_file.h"
|
||||
#include "lib/util_opt.h"
|
||||
#include "lib/util_panic.h"
|
||||
#include "lib/util_path.h"
|
||||
#include "lib/util_prg.h"
|
||||
#include "lib/util_rec.h"
|
||||
#include "lib/util_scandir.h"
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
/*
|
||||
* Program configuration
|
||||
*/
|
||||
const struct util_prg prg = {
|
||||
.desc = "List information about available Storage Class Memory Increments.",
|
||||
.copyright_vec = {
|
||||
{
|
||||
.owner = "IBM Corp.",
|
||||
.pub_first = 2016,
|
||||
.pub_last = 2017,
|
||||
},
|
||||
UTIL_PRG_COPYRIGHT_END
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Configuration of command line options
|
||||
*/
|
||||
static struct util_opt opt_vec[] = {
|
||||
UTIL_OPT_HELP,
|
||||
UTIL_OPT_VERSION,
|
||||
UTIL_OPT_END
|
||||
};
|
||||
|
||||
/**
|
||||
* Print all attributes of one device
|
||||
*
|
||||
* @param[in] name Block device name, or NULL if scm_block.ko is not loaed
|
||||
* @param[in] data Address of scm device
|
||||
* @param[in] rec Container for tabular output
|
||||
*/
|
||||
static void print_scm_attrs(const char *name, const char *addr,
|
||||
struct util_rec *rec)
|
||||
{
|
||||
long value;
|
||||
char *path;
|
||||
|
||||
path = util_path_sysfs("bus/scm/devices/%s", addr);
|
||||
|
||||
util_rec_set(rec, "addr", addr);
|
||||
if (name) {
|
||||
util_rec_set(rec, "name", name);
|
||||
if (util_file_read_l(&value, 10, "%s/block/%s/size", path, name))
|
||||
goto out_free_path;
|
||||
util_rec_set(rec, "size", "%ldMB", value * 512 / (1024 * 1024));
|
||||
} else {
|
||||
util_rec_set(rec, "name", "N/A");
|
||||
util_rec_set(rec, "size", "%ldMB", 0);
|
||||
}
|
||||
|
||||
if (util_file_read_l(&value, 10, "%s/rank", path))
|
||||
goto out_free_path;
|
||||
util_rec_set(rec, "rank", "%ld", value);
|
||||
|
||||
if (util_file_read_l(&value, 10, "%s/data_state", path))
|
||||
goto out_free_path;
|
||||
util_rec_set(rec, "dstate", "%ld", value);
|
||||
|
||||
if (util_file_read_l(&value, 10, "%s/oper_state", path))
|
||||
goto out_free_path;
|
||||
util_rec_set(rec, "ostate", "%ld", value);
|
||||
|
||||
if (util_file_read_l(&value, 10, "%s/persistence", path))
|
||||
goto out_free_path;
|
||||
util_rec_set(rec, "pers", "%ld", value);
|
||||
|
||||
if (util_file_read_l(&value, 10, "%s/res_id", path))
|
||||
goto out_free_path;
|
||||
util_rec_set(rec, "resid", "%ld", value);
|
||||
|
||||
util_rec_print(rec);
|
||||
out_free_path:
|
||||
free(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print one scm block device
|
||||
*
|
||||
* @param[in] addr Address
|
||||
* @param[in] rec Container for tabular output
|
||||
*/
|
||||
void print_scm(const char *addr, struct util_rec *rec)
|
||||
{
|
||||
struct dirent **de_vec;
|
||||
char *path;
|
||||
int count;
|
||||
|
||||
path = util_path_sysfs("bus/scm/devices/%s/block", addr);
|
||||
/* Match scma..scmzz */
|
||||
count = util_scandir(&de_vec, NULL, path, "^scm[[:lower:]]{1,2}$");
|
||||
if (count < 0) {
|
||||
/* If scm_block not loaded */
|
||||
print_scm_attrs(NULL, addr, rec);
|
||||
} else {
|
||||
util_assert(count == 1, "We expect only one block device\n");
|
||||
print_scm_attrs(de_vec[0]->d_name, addr, rec);
|
||||
util_scandir_free(de_vec, count);
|
||||
}
|
||||
free(path);
|
||||
}
|
||||
|
||||
/*
|
||||
* Look for used scm increments
|
||||
*/
|
||||
static void cmd_lsscm(void)
|
||||
{
|
||||
struct dirent **de_vec;
|
||||
struct util_rec *rec;
|
||||
int i, count;
|
||||
char *path;
|
||||
|
||||
rec = util_rec_new_wide("-");
|
||||
util_rec_def(rec, "addr", UTIL_REC_ALIGN_LEFT, 16, "SCM Increment");
|
||||
util_rec_def(rec, "size", UTIL_REC_ALIGN_LEFT, 7, "Size");
|
||||
util_rec_def(rec, "name", UTIL_REC_ALIGN_LEFT, 5, "Name");
|
||||
util_rec_def(rec, "rank", UTIL_REC_ALIGN_RIGHT, 4, "Rank");
|
||||
util_rec_def(rec, "dstate", UTIL_REC_ALIGN_RIGHT, 7, "D_state");
|
||||
util_rec_def(rec, "ostate", UTIL_REC_ALIGN_RIGHT, 7, "O_state");
|
||||
util_rec_def(rec, "pers", UTIL_REC_ALIGN_RIGHT, 4, "Pers");
|
||||
util_rec_def(rec, "resid", UTIL_REC_ALIGN_RIGHT, 5, "ResID");
|
||||
|
||||
util_rec_print_hdr(rec);
|
||||
/* Call print_scm() for each "/sys/bus/scm/devices/[%16x]" softlink */
|
||||
path = util_path_sysfs("bus/scm/devices");
|
||||
count = util_scandir(&de_vec, util_scandir_hexsort,
|
||||
path, "^[[:xdigit:]]{16}$");
|
||||
if (count < 0)
|
||||
errx(EXIT_FAILURE, "Could not read directory: %s", path);
|
||||
for (i = 0; i < count; i++) {
|
||||
if (de_vec[i]->d_type != DT_LNK)
|
||||
continue;
|
||||
print_scm(de_vec[i]->d_name, rec);
|
||||
}
|
||||
util_ptr_vec_free((void **) de_vec, count);
|
||||
util_rec_free(rec);
|
||||
free(path);
|
||||
}
|
||||
|
||||
/*
|
||||
* Entry point
|
||||
*/
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int c;
|
||||
|
||||
util_prg_init(&prg);
|
||||
util_opt_init(opt_vec, NULL);
|
||||
while (1) {
|
||||
c = util_opt_getopt_long(argc, argv);
|
||||
if (c == -1)
|
||||
break;
|
||||
switch (c) {
|
||||
case 'h':
|
||||
util_prg_print_help();
|
||||
util_opt_print_help();
|
||||
return EXIT_SUCCESS;
|
||||
case 'v':
|
||||
util_prg_print_version();
|
||||
return EXIT_SUCCESS;
|
||||
default:
|
||||
util_opt_print_parse_error(c, argv);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
if (argc > optind) {
|
||||
util_prg_print_arg_error(argv[optind]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
cmd_lsscm();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
include ../../common.mak
|
||||
|
||||
all: chzcrypt lszcrypt
|
||||
|
||||
libs = $(rootdir)/libutil/libutil.a
|
||||
|
||||
chzcrypt: chzcrypt.o misc.o $(libs)
|
||||
lszcrypt: lszcrypt.o misc.o $(libs)
|
||||
|
||||
install: all
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 chzcrypt $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 lszcrypt $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man8
|
||||
$(INSTALL) -m 644 -c chzcrypt.8 $(DESTDIR)$(MANDIR)/man8
|
||||
$(INSTALL) -m 644 -c lszcrypt.8 $(DESTDIR)$(MANDIR)/man8
|
||||
|
||||
clean:
|
||||
rm -f *.o chzcrypt lszcrypt
|
||||
|
||||
.PHONY: all install clean
|
||||
@@ -0,0 +1,112 @@
|
||||
.\" Copyright 2017 IBM Corp.
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
.TH CHZCRYPT 8 "AUG 2008" "s390-tools"
|
||||
.SH NAME
|
||||
chzcrypt \- modify zcrypt configuration
|
||||
.SH SYNOPSIS
|
||||
.TP 9
|
||||
.B chzcrypt
|
||||
.B -e
|
||||
.RB "|"
|
||||
.B -d
|
||||
.RB "( " -a " | "
|
||||
.I <device id>
|
||||
[...] )
|
||||
.TP
|
||||
.B chzcrypt
|
||||
.RB "[ " -p " | " -n " ] [ " -t
|
||||
.I <timeout>
|
||||
]
|
||||
.TP
|
||||
.B chzcrypt
|
||||
.RB "[ " -c
|
||||
.I <timeout>
|
||||
]
|
||||
.TP
|
||||
.B chzcrypt
|
||||
.RB "[ " -q
|
||||
.I <domain>
|
||||
]
|
||||
.TP
|
||||
.B chzcrypt -h
|
||||
.TP
|
||||
.B chzcrypt -v
|
||||
.SH DESCRIPTION
|
||||
The
|
||||
.B chzcrypt
|
||||
command is used to configure cryptographic devices managed by zcrypt and
|
||||
modify zcrypt's AP bus attributes.
|
||||
|
||||
Attributes may vary depending on the kernel
|
||||
version.
|
||||
.B chzcrypt
|
||||
requires that the sysfs filesystem is mounted.
|
||||
.SH OPTIONS
|
||||
.TP 8
|
||||
.B -e, --enable
|
||||
Set the given cryptographic device(s) online.
|
||||
.TP 8
|
||||
.B -d, --disable
|
||||
Set the given cryptographic device(s) offline.
|
||||
.TP 8
|
||||
.B -a, --all
|
||||
Set all available cryptographic device(s) online or offline.
|
||||
.TP 8
|
||||
.B <device id>
|
||||
Specifies a cryptographic device which will be set either online or offline.
|
||||
The device can either be a card device or a queue device.
|
||||
|
||||
Please note that the card device and queue device representation are both
|
||||
in hexadecimal notation.
|
||||
.TP 8
|
||||
.B -p, --poll-thread-enable
|
||||
Enable zcrypt's poll thread.
|
||||
.TP 8
|
||||
.B -n, --poll-thread-disable
|
||||
Disable zcrypt's poll thread.
|
||||
.TP 8
|
||||
.BI "-c, --config-time" " <timeout>"
|
||||
Set configuration timer for re-scanning the AP bus to
|
||||
.I <timeout>
|
||||
seconds.
|
||||
.TP 8
|
||||
.BI "-t, --poll-timeout" " <poll_timeout>"
|
||||
Set poll timer to run poll tasklet all
|
||||
.I <poll_timeout>
|
||||
nanoseconds.
|
||||
.TP 8
|
||||
.BI "-q, --default-domain" " <domain>"
|
||||
Set the new default domain of the AP bus to <domain>.
|
||||
The number of available domains can be retrieved with the lszcrypt
|
||||
command ('-d' option).
|
||||
.TP 8
|
||||
.B -V, --verbose
|
||||
Print verbose messages.
|
||||
.TP 8
|
||||
.B -h, --help
|
||||
Print help text and exit.
|
||||
.TP 8
|
||||
.B -v, --version
|
||||
Print version information and exit.
|
||||
.SH EXAMPLES
|
||||
.TP
|
||||
.B chzcrypt -e 0 1 12
|
||||
Will set the cryptographic card devices 0, 1 and 12 online.
|
||||
.TP
|
||||
.B chzcrypt -e 01.0038
|
||||
Will set the cryptographic device '10.0038' respectively card id 16
|
||||
(0x10) with domain 56 (0x38) online.
|
||||
.TP
|
||||
.B chzcrypt -d -a
|
||||
Will set all available cryptographic devices offline.
|
||||
.TP
|
||||
.B chzcrypt -c 60 -n
|
||||
Will set configuration timer for re-scanning the AP bus to 60 seconds and
|
||||
disable zcrypt's poll thread.
|
||||
.TP
|
||||
.B chzcrypt -q 67
|
||||
Will set the default domain to 67.
|
||||
.SH SEE ALSO
|
||||
\fBlszcrypt\fR(8)
|
||||
@@ -0,0 +1,419 @@
|
||||
/*
|
||||
* chzcrypt - Tool to modify zcrypt configuration
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <argz.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "lib/util_base.h"
|
||||
#include "lib/util_file.h"
|
||||
#include "lib/util_libc.h"
|
||||
#include "lib/util_opt.h"
|
||||
#include "lib/util_panic.h"
|
||||
#include "lib/util_path.h"
|
||||
#include "lib/util_prg.h"
|
||||
#include "lib/util_scandir.h"
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
#include "misc.h"
|
||||
|
||||
/*
|
||||
* Private data
|
||||
*/
|
||||
struct chzcrypt_l {
|
||||
int verbose;
|
||||
} l;
|
||||
|
||||
struct chzcrypt_l *chzcrypt_l = &l;
|
||||
|
||||
/*
|
||||
* Program configuration
|
||||
*/
|
||||
const struct util_prg prg = {
|
||||
.desc = "Modify zcrypt configuration.",
|
||||
.args = "[DEVICE_IDS]",
|
||||
.copyright_vec = {
|
||||
{
|
||||
.owner = "IBM Corp.",
|
||||
.pub_first = 2008,
|
||||
.pub_last = 2017,
|
||||
},
|
||||
UTIL_PRG_COPYRIGHT_END
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Configuration of command line options
|
||||
*/
|
||||
static struct util_opt opt_vec[] = {
|
||||
{
|
||||
.option = { "enable", no_argument, NULL, 'e'},
|
||||
.argument = "DEVICE_IDS",
|
||||
.desc = "Set the given cryptographic device(s) online"
|
||||
},
|
||||
{
|
||||
.option = { "disable", no_argument, NULL, 'd'},
|
||||
.argument = "DEVICE_IDS",
|
||||
.desc = "Set the given cryptographic device(s) offline",
|
||||
},
|
||||
{
|
||||
.option = { "all", no_argument, NULL, 'a'},
|
||||
.desc = "Set all available cryptographic device(s) "
|
||||
"online/offline, must be used in conjunction "
|
||||
"with the enable or disable option",
|
||||
},
|
||||
{
|
||||
.option = { "poll-thread-enable", no_argument, NULL, 'p'},
|
||||
.desc = "Enable zcrypt's poll thread",
|
||||
},
|
||||
{
|
||||
.option = { "poll-thread-disable", no_argument, NULL, 'n'},
|
||||
.desc = "Disable zcrypt's poll thread",
|
||||
},
|
||||
{
|
||||
.option = { "config-time", required_argument, NULL, 'c'},
|
||||
.argument = "TIMEOUT",
|
||||
.desc = "Set configuration timer for re-scanning the AP bus "
|
||||
"to TIMEOUT seconds",
|
||||
},
|
||||
{
|
||||
.option = { "poll-timeout", required_argument, NULL, 't'},
|
||||
.argument = "TIMEOUT",
|
||||
.desc = "Set poll timer to run poll tasklet all TIMEOUT "
|
||||
"nanoseconds after a request has been queued",
|
||||
},
|
||||
{
|
||||
.option = { "default-domain", required_argument, NULL, 'q'},
|
||||
.argument = "DOMAIN",
|
||||
.desc = "Set new default domain to DOMAIN",
|
||||
},
|
||||
{
|
||||
.option = { "verbose", no_argument, NULL, 'V'},
|
||||
.desc = "Print verbose messages",
|
||||
},
|
||||
UTIL_OPT_HELP,
|
||||
UTIL_OPT_VERSION,
|
||||
UTIL_OPT_END
|
||||
};
|
||||
|
||||
/*
|
||||
* Print if verbose is set
|
||||
*/
|
||||
#define verbose(x...) \
|
||||
do { \
|
||||
if (!l.verbose) \
|
||||
break; \
|
||||
printf(x); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Set poll settings
|
||||
*/
|
||||
static void poll_thread_set(const char *mode_str)
|
||||
{
|
||||
long mode, mode_read = -1;
|
||||
char *attr;
|
||||
|
||||
sscanf(mode_str, "%ld", &mode);
|
||||
if (mode == 1)
|
||||
verbose("Enabling poll thread.\n");
|
||||
else
|
||||
verbose("Disabling poll thread.\n");
|
||||
attr = util_path_sysfs("bus/ap/poll_thread");
|
||||
if (!util_path_is_writable(attr))
|
||||
errx(EXIT_FAILURE, "Error - can't write to %s.\n Wrong permissions"
|
||||
" or wrong tools version.", attr);
|
||||
util_file_write_l(mode, 10, attr);
|
||||
util_file_read_l(&mode_read, 10, attr);
|
||||
if (mode != mode_read)
|
||||
errx(EXIT_FAILURE, "Error - unable to change poll thread setting.");
|
||||
free(attr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set timer
|
||||
*/
|
||||
static void config_time_set(const char *timeout_str)
|
||||
{
|
||||
long timeout, timeout_read;
|
||||
char *attr;
|
||||
|
||||
if (sscanf(timeout_str, "%ld", &timeout) != 1) {
|
||||
errx(EXIT_FAILURE, "Error - invalid configuration timeout '%s'.", timeout_str);
|
||||
}
|
||||
attr = util_path_sysfs("bus/ap/config_time");
|
||||
verbose("Setting configuration timer to %ld seconds.\n", timeout);
|
||||
if (!util_path_is_writable(attr))
|
||||
errx(EXIT_FAILURE, "Error - can't write to %s.\n Wrong permissions"
|
||||
" or wrong tools version.", attr);
|
||||
util_file_write_l(timeout, 10, attr);
|
||||
util_file_read_l(&timeout_read, 10, attr);
|
||||
if (timeout != timeout_read)
|
||||
errx(EXIT_FAILURE, "Error - unable to change configuration timer setting.");
|
||||
free(attr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set poll timeout
|
||||
*/
|
||||
static void poll_timeout_set(const char *poll_timeout_str)
|
||||
{
|
||||
long poll_timeout, poll_timeout_read;
|
||||
char *attr;
|
||||
|
||||
if (sscanf(poll_timeout_str, "%ld", &poll_timeout) != 1)
|
||||
errx(EXIT_FAILURE, "Error - invalid poll timeout '%s'.", poll_timeout_str);
|
||||
attr = util_path_sysfs("bus/ap/poll_timeout");
|
||||
verbose("Setting poll timeout to %ld seconds.\n", poll_timeout);
|
||||
if (!util_path_is_writable(attr))
|
||||
errx(EXIT_FAILURE, "Error - can't write to %s.\n Wrong permissions"
|
||||
" or wrong tools version.", attr);
|
||||
util_file_write_l(poll_timeout, 10, attr);
|
||||
util_file_read_l(&poll_timeout_read, 10, attr);
|
||||
if (poll_timeout != poll_timeout_read)
|
||||
errx(EXIT_FAILURE, "Error - unable to change poll timeout setting.");
|
||||
free(attr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set default domain
|
||||
*/
|
||||
static void default_domain_set(const char *default_domain_str)
|
||||
{
|
||||
long max_dom, default_domain, default_domain_read;
|
||||
char *attr, *ap_max_domain_id;
|
||||
|
||||
sscanf(default_domain_str, "%li", &default_domain);
|
||||
ap_max_domain_id = util_path_sysfs("bus/ap/ap_max_domain_id");
|
||||
util_file_read_l(&max_dom, 10, ap_max_domain_id);
|
||||
if (default_domain < 0 || default_domain > max_dom)
|
||||
errx(EXIT_FAILURE, "Error - invalid default domain '%s'.", default_domain_str);
|
||||
attr = util_path_sysfs("bus/ap/ap_domain");
|
||||
if (!util_path_is_writable(attr))
|
||||
errx(EXIT_FAILURE, "Error - can't write to %s.\n Wrong permissions"
|
||||
" or wrong tools version.", attr);
|
||||
verbose("Setting default domain to %ld.\n", default_domain);
|
||||
util_file_write_l(default_domain, 10, attr);
|
||||
util_file_read_l(&default_domain_read, 10, attr);
|
||||
if (default_domain != default_domain_read)
|
||||
errx(EXIT_FAILURE, "Error - unable to change default domain.");
|
||||
free(ap_max_domain_id);
|
||||
free(attr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print invalid commandline error message and then exit with error code
|
||||
*/
|
||||
#define invalid_cmdline_exit(x...) \
|
||||
do { \
|
||||
fprintf(stderr, "%s: ", program_invocation_short_name); \
|
||||
fprintf(stderr, x); \
|
||||
util_prg_print_parse_error(); \
|
||||
exit(EXIT_FAILURE); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Get device list from sysfs
|
||||
*/
|
||||
static void dev_list_all(char **argz, size_t *len)
|
||||
{
|
||||
struct dirent **de_vec;
|
||||
int count, i;
|
||||
char *path;
|
||||
|
||||
path = util_path_sysfs("bus/ap/devices/");
|
||||
count = util_scandir(&de_vec, NULL, path, "card.*");
|
||||
if (count < 0)
|
||||
errx(EXIT_FAILURE, "Error - Could not read directory %s.", path);
|
||||
*argz = NULL;
|
||||
*len = 0;
|
||||
for (i = 0; i < count; i++)
|
||||
util_assert(argz_add(argz, len, de_vec[i]->d_name) == 0,
|
||||
"Out of memory\n");
|
||||
util_scandir_free(de_vec, count);
|
||||
free(path);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get device list from commandline
|
||||
*/
|
||||
static void dev_list_argv(char **argz, size_t *len, char * const argv[])
|
||||
{
|
||||
if (argv[0] == NULL)
|
||||
errx(EXIT_FAILURE, "Need to specify at least one device ID.");
|
||||
|
||||
util_assert(argz_create(argv, argz, len) == 0, "Out of memory\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Describe adapter ids
|
||||
*/
|
||||
void print_adapter_id_help(void)
|
||||
{
|
||||
printf("\n");
|
||||
printf("DEVICE_IDS\n");
|
||||
printf(" List of cryptographic device ids separated by blanks which will be set\n");
|
||||
printf(" online/offline. Must be used in conjunction with the enable or disable option.\n");
|
||||
|
||||
printf(" DEVICE_ID could either be card device id ('<card-id>') or queue device id\n");
|
||||
printf(" '<card-id>.<domain-id>').\n");
|
||||
printf(" \n");
|
||||
printf("EXAMPLE:\n");
|
||||
printf(" Disable the cryptographic device with card id '02' (inclusive all queues).\n");
|
||||
printf(" #>chzcrypt -d 02\n");
|
||||
printf(" \n");
|
||||
printf(" Enable the cryptographic devices with card id '03' and domain id '0005'.\n");
|
||||
printf(" #>chzcrypt -e 03.0005\n");
|
||||
printf(" \n");
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse options and execute the command
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
const char *online, *online_text = NULL, *poll_thread, *config_time;
|
||||
const char *poll_timeout, *default_domain;
|
||||
char *path, *dev_path, *dev, *dev_list, device[256], online_read[2];
|
||||
bool all = false, actionset = false;
|
||||
size_t len;
|
||||
int id, dom, c, i, j;
|
||||
|
||||
for (i=0; i < argc; i++)
|
||||
for (j=2; j < (int) strlen(argv[i]); j++)
|
||||
if (argv[i][j] == '_')
|
||||
argv[i][j] = '-';
|
||||
|
||||
online = poll_thread = config_time = poll_timeout = default_domain = NULL;
|
||||
util_prg_init(&prg);
|
||||
util_opt_init(opt_vec, NULL);
|
||||
while (1) {
|
||||
c = util_opt_getopt_long(argc, argv);
|
||||
if (c == -1)
|
||||
break;
|
||||
switch (c) {
|
||||
case 'e':
|
||||
actionset = true;
|
||||
online = "1";
|
||||
online_text = "online";
|
||||
break;
|
||||
case 'd':
|
||||
actionset = true;
|
||||
online = "0";
|
||||
online_text = "offline";
|
||||
break;
|
||||
case 'a':
|
||||
all = true;
|
||||
break;
|
||||
case 'p':
|
||||
actionset = true;
|
||||
poll_thread = "1";
|
||||
break;
|
||||
case 'n':
|
||||
actionset = true;
|
||||
poll_thread = "0";
|
||||
break;
|
||||
case 'c':
|
||||
actionset = true;
|
||||
config_time = optarg;
|
||||
break;
|
||||
case 't':
|
||||
actionset = true;
|
||||
poll_timeout = optarg;
|
||||
break;
|
||||
case 'q':
|
||||
actionset = true;
|
||||
default_domain = optarg;
|
||||
break;
|
||||
case 'V':
|
||||
l.verbose = true;
|
||||
break;
|
||||
case 'h':
|
||||
util_prg_print_help();
|
||||
util_opt_print_help();
|
||||
print_adapter_id_help();
|
||||
return EXIT_SUCCESS;
|
||||
case 'v':
|
||||
util_prg_print_version();
|
||||
return EXIT_SUCCESS;
|
||||
default:
|
||||
util_opt_print_parse_error(c, argv);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
if (!actionset)
|
||||
invalid_cmdline_exit("Error - missing argument.\n");
|
||||
path = util_path_sysfs("bus/ap");
|
||||
if (!util_path_is_dir(path))
|
||||
errx(EXIT_FAILURE, "Crypto device driver not available.");
|
||||
free(path);
|
||||
if (poll_thread) {
|
||||
poll_thread_set(poll_thread);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
if (config_time) {
|
||||
config_time_set(config_time);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
if (poll_timeout) {
|
||||
poll_timeout_set(poll_timeout);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
if (default_domain) {
|
||||
default_domain_set(default_domain);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
if (all)
|
||||
dev_list_all(&dev_list, &len);
|
||||
else
|
||||
dev_list_argv(&dev_list, &len, &argv[optind]);
|
||||
|
||||
if (online && len == 0)
|
||||
errx(EXIT_FAILURE, "Error - missing cryptographic device id(s).");
|
||||
|
||||
for (dev = dev_list; dev != NULL; dev = argz_next(dev_list, len, dev)) {
|
||||
if (strncmp(dev, "card", 4) == 0) {
|
||||
/* dev == "card2" */
|
||||
sscanf(dev, "card%02x", &id);
|
||||
sprintf(device, "card%02x", id);
|
||||
} else if (strncmp(dev, "0x", 2) == 0) {
|
||||
/* dev == "0x.." */
|
||||
sscanf(dev, "0x%02x", &id);
|
||||
sprintf(device, "card%02x", id);
|
||||
} else if (misc_regex_match(dev, "^[0-9a-fA-F]+$")) {
|
||||
/* dev == "2" */
|
||||
sscanf(dev, "%02x", &id);
|
||||
sprintf(device, "card%02x", id);
|
||||
} else {
|
||||
/* Form: 01.0003 ? */
|
||||
if (sscanf(dev, "%02x.%04x", &id, &dom) != 2)
|
||||
errx(EXIT_FAILURE, "Error - cryptographic device %s malformed.", dev);
|
||||
sprintf(device, "card%02x/%02x.%04x", id, id, dom);
|
||||
}
|
||||
dev_path = util_path_sysfs("bus/ap/devices/%s", device);
|
||||
if (!util_path_is_dir(dev_path))
|
||||
errx(EXIT_FAILURE, "Error - cryptographic device %s does not exist.", device);
|
||||
if (!util_path_is_writable("%s/online", dev_path))
|
||||
continue;
|
||||
verbose("Setting cryptographic device %s %s\n", device, online_text);
|
||||
util_file_write_s(online, "%s/online", dev_path);
|
||||
util_file_read_line(online_read, sizeof(online_read), "%s/online", dev_path);
|
||||
if (strcmp(online, online_read) != 0)
|
||||
errx(EXIT_FAILURE, "Error - unable to set cryptographic device %s %s.", device, online_text);
|
||||
free(dev_path);
|
||||
}
|
||||
free(dev_list);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
.\" lszcrypt.8
|
||||
.\"
|
||||
.\" Copyright 2017 IBM Corp.
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
.\" use
|
||||
.\" groff -man -Tutf8 lszcrypt.8
|
||||
.\" or
|
||||
.\" nroff -man lszcrypt.8
|
||||
.\" to process this source
|
||||
.\"
|
||||
.TH LSZCRYPT 8 "AUG 2008" "s390-tools"
|
||||
.SH NAME
|
||||
lszcrypt \- display zcrypt device and configuration information
|
||||
.SH SYNOPSIS
|
||||
.TP 9
|
||||
.B lszcrypt
|
||||
.RB "[ " -V " ] "
|
||||
[
|
||||
.I <device id>
|
||||
[...]]
|
||||
.TP
|
||||
.B lszcrypt
|
||||
.B -c
|
||||
<device id>
|
||||
.TP
|
||||
.B lszcrypt -b
|
||||
.TP
|
||||
.B lszcrypt -d
|
||||
.TP
|
||||
.B lszcrypt -h
|
||||
.TP
|
||||
.B lszcrypt -v
|
||||
.SH DESCRIPTION
|
||||
The
|
||||
.B lszcrypt
|
||||
command is used to display information about cryptographic devices managed by
|
||||
zcrypt and the AP bus attributes of zcrypt. Displayed information depends on the
|
||||
kernel version.
|
||||
.B lszcrypt
|
||||
requires that sysfs is mounted.
|
||||
.P
|
||||
The following information can be displayed for each cryptographic
|
||||
device: card ID, domain ID, card type (symbolic), mode, online status,
|
||||
hardware card type (numeric), installed function facilities, card capability,
|
||||
hardware queue depth, request count, number of requests in hardware queue, and
|
||||
the number of outstanding requests.
|
||||
The following AP bus attributes can be displayed: AP domain, Max AP domain,
|
||||
configuration timer, poll thread status, poll timeout, and AP interrupt
|
||||
status.
|
||||
.SH OPTIONS
|
||||
.TP 8
|
||||
.B -V, --verbose
|
||||
The verbose level for cryptographic device information.
|
||||
With this verbose level additional information like hardware card type,
|
||||
hardware queue depth, pending request queue count, outstanding
|
||||
request queue count, and installed function facilities are displayed.
|
||||
.TP 8
|
||||
.B <device id>
|
||||
Specifies a cryptographic device to display. A cryptographic device can be
|
||||
either a card device or a queue device. If no devices are specified information
|
||||
about all available devices is displayed.
|
||||
|
||||
Please note that the card device representation and the queue device are both
|
||||
in hexadecimal notation.
|
||||
.TP 8
|
||||
.B -b, --bus
|
||||
Displays the AP bus attributes and exits.
|
||||
.TP 8
|
||||
.B -c, --capability <card device id>
|
||||
Shows the capabilities of a cryptographic card device of hardware type 6 or
|
||||
higher. The card device id value may be given as decimal or hex value (with
|
||||
a leading 0x). The capabilities of a cryptographic card device depend on
|
||||
the card type and the installed function facilities. A cryptographic card
|
||||
device can provide one or more of the following capabilities:
|
||||
.RS
|
||||
.IP "o" 3
|
||||
RSA 2K Clear Key
|
||||
.IP "o"
|
||||
RSA 4K Clear Key
|
||||
.IP "o"
|
||||
CCA Secure Key
|
||||
.IP "o"
|
||||
EP11 Secure Key
|
||||
.IP "o"
|
||||
Long RNG
|
||||
.RE
|
||||
.TP 8
|
||||
.B -d, --domains
|
||||
Shows the usage and control domains of the cryptographic devices.
|
||||
The displayed domains of the cryptographic device depends on the initial
|
||||
cryptographic configuration.
|
||||
.RS
|
||||
.IP "o" 2
|
||||
'C' indicate a control domain
|
||||
.IP "o"
|
||||
'U' indicate a usage domain
|
||||
.IP "o"
|
||||
'B' indicate both (control and usage domain)
|
||||
.RE
|
||||
.TP 8
|
||||
.B -h, --help
|
||||
Displays help text and exits.
|
||||
.TP 8
|
||||
.B -v, --version
|
||||
Displays version information and exits.
|
||||
.SH EXAMPLES
|
||||
.TP
|
||||
.B lszcrypt
|
||||
Displays the card/domain ID, card type (short name), mode (long name), online
|
||||
status and request count of all available cryptographic devices.
|
||||
.TP
|
||||
.B lszcrypt 1 3 5
|
||||
Displays the card/domain ID, card type, mode, online status and request count
|
||||
for cryptographic devices 1, 3, and 5.
|
||||
.TP
|
||||
.B lszcrypt -V 3 7 11
|
||||
Displays the card/domain ID, card type, mode, online status, request count,
|
||||
number of requests in the hardware queue, number of outstanding requests and
|
||||
installed function facilities for cryptographic devices 3, 7 and 17 (0x11).
|
||||
.TP
|
||||
.B lszcrypt 10.0038
|
||||
Displays information of the cryptographic device '10.0038' respectively card
|
||||
id 16 (0x10) with domain 56 (0x38).
|
||||
.TP
|
||||
.B lszcrypt .0038
|
||||
Displays information of all available queue devices (potentially multiple
|
||||
adapters) with domain 56 (0x38).
|
||||
.TP
|
||||
.B lszcrypt -b
|
||||
Displays AP bus information.
|
||||
.TP
|
||||
.B lszcrypt -c 7
|
||||
.RS
|
||||
.br
|
||||
Coprocessor card07 provides capability for:
|
||||
.br
|
||||
CCA Secure Key
|
||||
.br
|
||||
RSA 4K Clear Key
|
||||
.br
|
||||
Long RNG
|
||||
.RE
|
||||
.SH SEE ALSO
|
||||
\fBchzcrypt\fR(8)
|
||||
@@ -0,0 +1,668 @@
|
||||
/**
|
||||
* lszcrypt - Display zcrypt devices and configuration settings
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <err.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "lib/util_base.h"
|
||||
#include "lib/util_file.h"
|
||||
#include "lib/util_opt.h"
|
||||
#include "lib/util_panic.h"
|
||||
#include "lib/util_path.h"
|
||||
#include "lib/util_prg.h"
|
||||
#include "lib/util_proc.h"
|
||||
#include "lib/util_rec.h"
|
||||
#include "lib/util_scandir.h"
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
/*
|
||||
* Private data
|
||||
*/
|
||||
struct lszcrypt_l {
|
||||
int verbose;
|
||||
} l;
|
||||
|
||||
struct lszcrypt_l *lszcrypt_l = &l;
|
||||
|
||||
/*
|
||||
* Capabilities
|
||||
*/
|
||||
#define CAP_RSA2K "RSA 2K Clear Key"
|
||||
#define CAP_RSA4K "RSA 4K Clear Key"
|
||||
#define CAP_CCA "CCA Secure Key"
|
||||
#define CAP_RNG "Long RNG"
|
||||
#define CAP_EP11 "EP11 Secure Key"
|
||||
|
||||
/*
|
||||
* Card types
|
||||
*/
|
||||
#define MASK_APSC 0x80000000
|
||||
#define MASK_RSA4K 0x60000000
|
||||
#define MASK_COPRO 0x10000000
|
||||
#define MASK_ACCEL 0x08000000
|
||||
#define MASK_EP11 0x04000000
|
||||
|
||||
/*
|
||||
* Program configuration
|
||||
*/
|
||||
const struct util_prg prg = {
|
||||
.desc = "Display zcrypt device and configuration information.",
|
||||
.args = "[DEVICE_IDS]",
|
||||
.copyright_vec = {
|
||||
{
|
||||
.owner = "IBM Corp.",
|
||||
.pub_first = 2008,
|
||||
.pub_last = 2017,
|
||||
},
|
||||
UTIL_PRG_COPYRIGHT_END
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Configuration of command line options
|
||||
*/
|
||||
static struct util_opt opt_vec[] = {
|
||||
{
|
||||
.option = {"bus", 0, NULL, 'b'},
|
||||
.desc = "Show AP bus attributes then exit",
|
||||
},
|
||||
{
|
||||
.option = { "capability", required_argument, NULL, 'c'},
|
||||
.argument = "DEVICE_ID",
|
||||
.desc = "Show the capabilities of a cryptographic device",
|
||||
},
|
||||
{
|
||||
.option = {"domains", 0, NULL, 'd'},
|
||||
.desc = "Show the configured AP usage and control domains",
|
||||
},
|
||||
{
|
||||
.option = {"verbose", 0, NULL, 'V'},
|
||||
.desc = "Print verbose messages",
|
||||
},
|
||||
UTIL_OPT_HELP,
|
||||
UTIL_OPT_VERSION,
|
||||
UTIL_OPT_END
|
||||
};
|
||||
|
||||
/*
|
||||
* Show bus
|
||||
*/
|
||||
static void show_bus(void)
|
||||
{
|
||||
long domain, max_domain, config_time, value;
|
||||
unsigned long long poll_timeout;
|
||||
const char *poll_thread, *ap_interrupts;
|
||||
char *ap;
|
||||
|
||||
/* check if ap driver is available */
|
||||
ap = util_path_sysfs("bus/ap");
|
||||
if (!util_path_is_dir(ap))
|
||||
errx(EXIT_FAILURE, "Crypto device driver not available.");
|
||||
|
||||
util_file_read_l(&domain, 10, "%s/ap_domain", ap);
|
||||
util_file_read_l(&max_domain, 10, "%s/ap_max_domain_id", ap);
|
||||
util_file_read_l(&config_time, 10, "%s/config_time", ap);
|
||||
util_file_read_ull(&poll_timeout, 10, "%s/poll_timeout", ap);
|
||||
util_file_read_l(&value, 10, "%s/poll_thread", ap);
|
||||
if (value == 1)
|
||||
poll_thread = "enabled";
|
||||
else
|
||||
poll_thread = "disabled";
|
||||
util_file_read_l(&value, 10, "%s/ap_interrupts", ap);
|
||||
if (value == 1)
|
||||
ap_interrupts = "enabled";
|
||||
else
|
||||
ap_interrupts = "disabled";
|
||||
printf("ap_domain=0x%lx\n", domain);
|
||||
printf("ap_max_domain_id=0x%lx\n", max_domain);
|
||||
if (util_path_is_reg_file("%s/ap_interrupts", ap))
|
||||
printf("ap_interrupts are %s\n", ap_interrupts);
|
||||
printf("config_time=%ld (seconds)\n", config_time);
|
||||
printf("poll_thread is %s\n", poll_thread);
|
||||
if (util_path_is_reg_file("%s/poll_timeout", ap))
|
||||
printf("poll_timeout=%llu (nanoseconds)\n", poll_timeout);
|
||||
free(ap);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print domain array using util_rec
|
||||
*/
|
||||
static void show_domains_util_rec(char *domain_array[])
|
||||
{
|
||||
struct util_rec *rec = util_rec_new_wide("-");
|
||||
char buf[256];
|
||||
int i, x, n;
|
||||
|
||||
util_rec_def(rec, "domain", UTIL_REC_ALIGN_RIGHT, 6, "DOMAIN");
|
||||
for (i = 0; i < 16; i++) {
|
||||
sprintf(buf, "%02x", i);
|
||||
util_rec_def(rec, buf, UTIL_REC_ALIGN_RIGHT, 2, buf);
|
||||
}
|
||||
|
||||
util_rec_print_hdr(rec);
|
||||
n = 0;
|
||||
for (i = 0; i < 16; i++) {
|
||||
sprintf(buf, "%02x", i * 16);
|
||||
util_rec_set(rec, "domain", buf);
|
||||
for (x = 0; x < 16; x++) {
|
||||
sprintf(buf, "%02x", x);
|
||||
util_rec_set(rec, buf, domain_array[n++]);
|
||||
}
|
||||
util_rec_print(rec);
|
||||
}
|
||||
util_rec_free(rec);
|
||||
printf("------------------------------------------------------\n");
|
||||
printf("C: Control domain\n");
|
||||
printf("U: Usage domain\n");
|
||||
printf("B: Both (Control + Usage domain)\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Show domains
|
||||
*/
|
||||
static void show_domains(void)
|
||||
{
|
||||
char ctrl_domain_mask[67], usag_domain_mask[67], byte_str[3] = {};
|
||||
int ctrl_chunk, usag_chunk;
|
||||
char *ap, *domain_array[32 * 8 + 4];
|
||||
int i, x, n;
|
||||
uint8_t dom_mask_bit;
|
||||
|
||||
/* check if ap driver is available */
|
||||
ap = util_path_sysfs("bus/ap");
|
||||
if (!util_path_is_dir(ap))
|
||||
errx(EXIT_FAILURE, "Crypto device driver not available.");
|
||||
|
||||
util_file_read_line(ctrl_domain_mask, sizeof(ctrl_domain_mask),
|
||||
"%s/ap_control_domain_mask", ap);
|
||||
util_file_read_line(usag_domain_mask, sizeof(usag_domain_mask),
|
||||
"%s/ap_usage_domain_mask", ap);
|
||||
/* remove leading '0x' from domain mask string */
|
||||
memmove(&ctrl_domain_mask[0], &ctrl_domain_mask[2],
|
||||
sizeof(ctrl_domain_mask) - 2);
|
||||
memmove(&usag_domain_mask[0], &usag_domain_mask[2],
|
||||
sizeof(usag_domain_mask) - 2);
|
||||
n = 0;
|
||||
for (i = 0; i < 32; i++) {
|
||||
dom_mask_bit = 0x80;
|
||||
memcpy(byte_str, &ctrl_domain_mask[i * 2], 2);
|
||||
sscanf(byte_str, "%02x", &ctrl_chunk);
|
||||
memcpy(byte_str, &usag_domain_mask[i * 2], 2);
|
||||
sscanf(byte_str, "%02x", &usag_chunk);
|
||||
for (x = 1; x <= 8; x++) {
|
||||
if (ctrl_chunk & dom_mask_bit &&
|
||||
usag_chunk & dom_mask_bit)
|
||||
domain_array[n] = "B"; /* c/u */
|
||||
else if (ctrl_chunk & dom_mask_bit)
|
||||
domain_array[n] = "C";
|
||||
else if (usag_chunk & dom_mask_bit)
|
||||
domain_array[n] = "U";
|
||||
else
|
||||
domain_array[n] = ".";
|
||||
dom_mask_bit = dom_mask_bit >> 1;
|
||||
n += 1;
|
||||
}
|
||||
}
|
||||
for (i = n; i < 260; i++)
|
||||
domain_array[n++] = "";
|
||||
|
||||
show_domains_util_rec(domain_array);
|
||||
}
|
||||
|
||||
/*
|
||||
* Show capability
|
||||
*/
|
||||
static void show_capability(const char *id_str)
|
||||
{
|
||||
unsigned long func_val;
|
||||
long hwtype, id;
|
||||
char *p, *ap, *dev, card[7];
|
||||
|
||||
/* check if ap driver is available */
|
||||
ap = util_path_sysfs("bus/ap");
|
||||
if (!util_path_is_dir(ap))
|
||||
errx(EXIT_FAILURE, "Crypto device driver not available.");
|
||||
|
||||
id = strtol(id_str, &p, 0);
|
||||
if (id < 0 || id > 255 || p == id_str || *p != '\0')
|
||||
errx(EXIT_FAILURE, "Error - '%s' is an invalid cryptographic device id.", id_str);
|
||||
snprintf(card, sizeof(card), "card%02lx", id);
|
||||
dev = util_path_sysfs("devices/ap/%s", card);
|
||||
if (!util_path_is_dir(dev))
|
||||
errx(EXIT_FAILURE, "Error - cryptographic device %s does not exist.", card);
|
||||
util_file_read_l(&hwtype, 10, "%s/hwtype", dev);
|
||||
/* If sysfs attribute is missing, set functions to 0 */
|
||||
if (util_file_read_ul(&func_val, 16, "%s/ap_functions", dev))
|
||||
func_val = 0x00000000;
|
||||
/* Skip devices, which are not supported by zcrypt layer */
|
||||
if (!util_path_is_readable("%s/type", dev) ||
|
||||
!util_path_is_readable("%s/online", dev)) {
|
||||
printf("Detailed capability information for %s (hardware type %ld) is not available.\n", card, hwtype);
|
||||
return;
|
||||
}
|
||||
printf("%s provides capability for:\n", card);
|
||||
switch (hwtype) {
|
||||
case 6:
|
||||
case 8:
|
||||
if (func_val & MASK_RSA4K)
|
||||
printf("%s", CAP_RSA4K);
|
||||
else
|
||||
printf("%s", CAP_RSA2K);
|
||||
break;
|
||||
case 7:
|
||||
case 9:
|
||||
printf("%s\n", CAP_RSA4K);
|
||||
printf("%s\n", CAP_CCA);
|
||||
printf("%s", CAP_RNG);
|
||||
break;
|
||||
case 10:
|
||||
case 11:
|
||||
if (func_val & MASK_ACCEL) {
|
||||
if (func_val & MASK_RSA4K)
|
||||
printf("%s", CAP_RSA4K);
|
||||
else
|
||||
printf("%s", CAP_RSA2K);
|
||||
} else if (func_val & MASK_COPRO) {
|
||||
printf("%s\n", CAP_RSA4K);
|
||||
printf("%s\n", CAP_CCA);
|
||||
printf("%s", CAP_RNG);
|
||||
} else if (func_val & MASK_EP11) {
|
||||
printf("%s", CAP_EP11);
|
||||
} else {
|
||||
|
||||
printf("Detailed capability information for %s (hardware type %ld) is not available.", card, hwtype);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
printf("Detailed capability information for %s (hardware type %ld) is not available.", card, hwtype);
|
||||
break;
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Read subdevice default attributes
|
||||
*/
|
||||
static void read_subdev_rec_default(struct util_rec *rec, const char *grp_dev,
|
||||
const char *sub_dev)
|
||||
{
|
||||
unsigned long facility;
|
||||
char buf[256];
|
||||
|
||||
util_file_read_line(buf, sizeof(buf), "%s/type", grp_dev);
|
||||
util_rec_set(rec, "type", buf);
|
||||
|
||||
util_file_read_line(buf, sizeof(buf), "%s/%s/online", grp_dev, sub_dev);
|
||||
if (strcmp(buf, "0") == 0)
|
||||
util_rec_set(rec, "online", "offline");
|
||||
else
|
||||
util_rec_set(rec, "online", "online");
|
||||
|
||||
util_file_read_ul(&facility, 16, "%s/ap_functions", grp_dev);
|
||||
if (facility & MASK_COPRO)
|
||||
util_rec_set(rec, "mode", "CCA-Coproc");
|
||||
else if (facility & MASK_ACCEL)
|
||||
util_rec_set(rec, "mode", "Accelerator");
|
||||
else if (facility & MASK_EP11)
|
||||
util_rec_set(rec, "mode", "EP11-Coproc");
|
||||
else
|
||||
util_rec_set(rec, "mode", "Unknown");
|
||||
|
||||
util_file_read_line(buf, sizeof(buf), "%s/%s/request_count",
|
||||
grp_dev, sub_dev);
|
||||
util_rec_set(rec, "request_count", buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read subdevice verbose attributes
|
||||
*/
|
||||
static void read_subdev_rec_verbose(struct util_rec *rec, const char *grp_dev,
|
||||
const char *sub_dev)
|
||||
{
|
||||
unsigned long facility;
|
||||
char buf[256];
|
||||
long depth;
|
||||
|
||||
if (l.verbose == 0)
|
||||
return;
|
||||
|
||||
util_file_read_line(buf, sizeof(buf), "%s/%s/pendingq_count",
|
||||
grp_dev, sub_dev);
|
||||
util_rec_set(rec, "pendingq_count", buf);
|
||||
|
||||
util_file_read_line(buf, sizeof(buf), "%s/%s/requestq_count",
|
||||
grp_dev, sub_dev);
|
||||
util_rec_set(rec, "requestq_count", buf);
|
||||
|
||||
util_file_read_line(buf, sizeof(buf), "%s/hwtype", grp_dev);
|
||||
util_rec_set(rec, "hwtype", buf);
|
||||
|
||||
util_file_read_l(&depth, 10, "%s/depth", grp_dev);
|
||||
util_rec_set(rec, "depth", "%02d", depth + 1);
|
||||
|
||||
util_file_read_ul(&facility, 16, "%s/ap_functions", grp_dev);
|
||||
util_rec_set(rec, "facility", "0x%08x", facility);
|
||||
}
|
||||
|
||||
/*
|
||||
* Show one subdevice
|
||||
*/
|
||||
static void show_subdevice(struct util_rec *rec, const char *grp_dev,
|
||||
const char *sub_dev)
|
||||
{
|
||||
if (!util_path_is_dir("%s/%s", grp_dev, sub_dev))
|
||||
errx(EXIT_FAILURE, "Error - cryptographic device %s/%s does not exist.", grp_dev, sub_dev);
|
||||
|
||||
/* Skip devices, which are not supported by zcrypt layer */
|
||||
if (!util_path_is_readable("%s/type", grp_dev) ||
|
||||
!util_path_is_readable("%s/%s/online", grp_dev, sub_dev))
|
||||
return;
|
||||
|
||||
util_rec_set(rec, "card", sub_dev);
|
||||
read_subdev_rec_default(rec, grp_dev, sub_dev);
|
||||
read_subdev_rec_verbose(rec, grp_dev, sub_dev);
|
||||
|
||||
util_rec_print(rec);
|
||||
}
|
||||
|
||||
/*
|
||||
* Show subdevices
|
||||
*/
|
||||
static void show_subdevices(struct util_rec *rec, const char *grp_dev)
|
||||
{
|
||||
struct dirent **dev_vec;
|
||||
int i, count;
|
||||
|
||||
count = util_scandir(&dev_vec, alphasort, grp_dev, "..\\....");
|
||||
if (count < 1)
|
||||
errx(EXIT_FAILURE, "Error - no subdevices found for %s.\n", grp_dev);
|
||||
for (i = 0; i < count; i++)
|
||||
show_subdevice(rec, grp_dev, dev_vec[i]->d_name);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read default attributes
|
||||
*/
|
||||
static void read_rec_default(struct util_rec *rec, const char *grp_dev)
|
||||
{
|
||||
unsigned long facility;
|
||||
char buf[256];
|
||||
|
||||
util_file_read_line(buf, sizeof(buf), "%s/type", grp_dev);
|
||||
util_rec_set(rec, "type", buf);
|
||||
|
||||
util_file_read_ul(&facility, 16, "%s/ap_functions", grp_dev);
|
||||
if (facility & MASK_COPRO)
|
||||
util_rec_set(rec, "mode", "CCA-Coproc");
|
||||
else if (facility & MASK_ACCEL)
|
||||
util_rec_set(rec, "mode", "Accelerator");
|
||||
else if (facility & MASK_EP11)
|
||||
util_rec_set(rec, "mode", "EP11-Coproc");
|
||||
else
|
||||
util_rec_set(rec, "mode", "Unknown");
|
||||
|
||||
util_file_read_line(buf, sizeof(buf), "%s/online", grp_dev);
|
||||
if (strcmp(buf, "0") == 0)
|
||||
util_rec_set(rec, "online", "offline");
|
||||
else
|
||||
util_rec_set(rec, "online", "online");
|
||||
|
||||
util_file_read_line(buf, sizeof(buf), "%s/request_count", grp_dev);
|
||||
util_rec_set(rec, "request_count", buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read verbose attributes
|
||||
*/
|
||||
static void read_rec_verbose(struct util_rec *rec, const char *grp_dev)
|
||||
{
|
||||
unsigned long facility;
|
||||
char buf[256];
|
||||
long depth;
|
||||
|
||||
if (l.verbose == 0)
|
||||
return;
|
||||
|
||||
util_file_read_line(buf, sizeof(buf), "%s/pendingq_count", grp_dev);
|
||||
util_rec_set(rec, "pendingq_count", buf);
|
||||
|
||||
util_file_read_line(buf, sizeof(buf), "%s/requestq_count", grp_dev);
|
||||
util_rec_set(rec, "requestq_count", buf);
|
||||
|
||||
util_file_read_line(buf, sizeof(buf), "%s/hwtype", grp_dev);
|
||||
util_rec_set(rec, "hwtype", buf);
|
||||
|
||||
util_file_read_l(&depth, 10, "%s/depth", grp_dev);
|
||||
util_rec_set(rec, "depth", "%02d", depth + 1);
|
||||
|
||||
util_file_read_ul(&facility, 16, "%s/ap_functions", grp_dev);
|
||||
util_rec_set(rec, "facility", "0x%08x", facility);
|
||||
}
|
||||
|
||||
/*
|
||||
* Show device: device is in the form "card00", "card01", ...
|
||||
*/
|
||||
static void show_device(struct util_rec *rec, const char *device)
|
||||
{
|
||||
char *grp_dev, card[3];
|
||||
|
||||
util_rec_set(rec, "card", card);
|
||||
|
||||
strcpy(card, &device[4]);
|
||||
grp_dev = util_path_sysfs("devices/ap/%s", device);
|
||||
if (!util_path_is_dir(grp_dev))
|
||||
errx(EXIT_FAILURE, "Error - cryptographic device %s does not exist.", device);
|
||||
/* Skip devices, which are not supported by zcrypt layer */
|
||||
if (!util_path_is_readable("%s/type", grp_dev) ||
|
||||
!util_path_is_readable("%s/online", grp_dev)) {
|
||||
goto out_free;
|
||||
}
|
||||
util_rec_set(rec, "card", card);
|
||||
|
||||
read_rec_default(rec, grp_dev);
|
||||
read_rec_verbose(rec, grp_dev);
|
||||
|
||||
util_rec_print(rec);
|
||||
show_subdevices(rec, grp_dev);
|
||||
out_free:
|
||||
free(grp_dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* Define the *default* attributes
|
||||
*/
|
||||
static void define_rec_default(struct util_rec *rec)
|
||||
{
|
||||
util_rec_def(rec, "card", UTIL_REC_ALIGN_LEFT, 11, "CARD.DOMAIN");
|
||||
util_rec_def(rec, "type", UTIL_REC_ALIGN_LEFT, 5, "TYPE");
|
||||
util_rec_def(rec, "mode", UTIL_REC_ALIGN_LEFT, 11, "MODE");
|
||||
util_rec_def(rec, "online", UTIL_REC_ALIGN_LEFT, 7, "STATUS");
|
||||
util_rec_def(rec, "request_count", UTIL_REC_ALIGN_RIGHT, 11,
|
||||
"REQUEST_CNT");
|
||||
}
|
||||
|
||||
/*
|
||||
* Define the *verbose* attributes
|
||||
*/
|
||||
static void define_rec_verbose(struct util_rec *rec)
|
||||
{
|
||||
if (l.verbose == 0)
|
||||
return;
|
||||
util_rec_def(rec, "pendingq_count", UTIL_REC_ALIGN_RIGHT, 12,
|
||||
"PENDINGQ_CNT");
|
||||
util_rec_def(rec, "requestq_count", UTIL_REC_ALIGN_RIGHT, 12,
|
||||
"REQUESTQ_CNT");
|
||||
util_rec_def(rec, "hwtype", UTIL_REC_ALIGN_RIGHT, 7, "HW_TYPE");
|
||||
util_rec_def(rec, "depth", UTIL_REC_ALIGN_RIGHT, 7, "Q_DEPTH");
|
||||
util_rec_def(rec, "facility", UTIL_REC_ALIGN_LEFT, 10, "FUNCTIONS");
|
||||
}
|
||||
|
||||
/*
|
||||
* Show all devices
|
||||
*/
|
||||
static void show_devices_all(void)
|
||||
{
|
||||
struct util_rec *rec = util_rec_new_wide("-");
|
||||
struct dirent **dev_vec;
|
||||
int i, count;
|
||||
char *ap, *path;
|
||||
|
||||
/* check if ap driver is available */
|
||||
ap = util_path_sysfs("bus/ap");
|
||||
if (!util_path_is_dir(ap))
|
||||
errx(EXIT_FAILURE, "Crypto device driver not available.");
|
||||
|
||||
/* Define the record */
|
||||
define_rec_default(rec);
|
||||
define_rec_verbose(rec);
|
||||
|
||||
/* Scan the devices */
|
||||
path = util_path_sysfs("devices/ap/");
|
||||
count = util_scandir(&dev_vec, alphasort, path, "card[0-9a-fA-F]+");
|
||||
if (count < 1)
|
||||
errx(EXIT_FAILURE, "No crypto card devices found.");
|
||||
util_rec_print_hdr(rec);
|
||||
for (i = 0; i < count; i++)
|
||||
show_device(rec, dev_vec[i]->d_name);
|
||||
free(path);
|
||||
}
|
||||
|
||||
/*
|
||||
* Show devices specified on commandline
|
||||
*/
|
||||
static void show_devices_argv(char *argv[])
|
||||
{
|
||||
struct util_rec *rec = util_rec_new_wide("-");
|
||||
struct dirent **dev_vec, **subdev_vec;
|
||||
char *ap, *grp_dev, *path, card[7], sub_dev[7];
|
||||
int id, dom, i, n, dev_cnt, sub_cnt;
|
||||
|
||||
/* check if ap driver is available */
|
||||
ap = util_path_sysfs("bus/ap");
|
||||
if (!util_path_is_dir(ap))
|
||||
errx(EXIT_FAILURE, "Crypto device driver not available.");
|
||||
|
||||
/* Define the record */
|
||||
define_rec_default(rec);
|
||||
define_rec_verbose(rec);
|
||||
|
||||
util_rec_print_hdr(rec);
|
||||
for (i = 0; argv[i] != NULL; i++) {
|
||||
id = -1;
|
||||
dom = -1;
|
||||
if (sscanf(argv[i], "%x.%x", &id, &dom) >= 1) {
|
||||
/* at least the id field was valid */
|
||||
if (id >= 0 && dom >= 0) { /* single subdevice */
|
||||
sprintf(sub_dev, "%02x.%04x", id, dom);
|
||||
grp_dev = util_path_sysfs("devices/ap/card%02x",
|
||||
id);
|
||||
show_subdevice(rec, grp_dev, sub_dev);
|
||||
free(grp_dev);
|
||||
} else { /* group device */
|
||||
sprintf(card, "card%02x", id);
|
||||
show_device(rec, card);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (sscanf(argv[i]+1, "%x", &dom) == 1) {
|
||||
/* list specific domains of all adapters */
|
||||
path = util_path_sysfs("devices/ap/");
|
||||
dev_cnt = util_scandir(&dev_vec, alphasort, path,
|
||||
"card[0-9a-fA-F]+");
|
||||
if (dev_cnt < 1)
|
||||
errx(EXIT_FAILURE, "No crypto card devices found.");
|
||||
free(path);
|
||||
for (i = 0; i < dev_cnt; i++) {
|
||||
path = util_path_sysfs("devices/ap/%s",
|
||||
dev_vec[i]->d_name);
|
||||
sub_cnt = util_scandir(&subdev_vec, alphasort,
|
||||
path,
|
||||
"[0-9a-fA-F]+.%04x",
|
||||
dom);
|
||||
if (sub_cnt < 1)
|
||||
errx(EXIT_FAILURE, "No queue devices with given domain value found.");
|
||||
for (n = 0; n < sub_cnt; n++) {
|
||||
show_subdevice(rec, path,
|
||||
subdev_vec[n]->d_name);
|
||||
}
|
||||
free(path);
|
||||
}
|
||||
return;
|
||||
}
|
||||
printf("Invalid adpater id!\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Describe adapter ids
|
||||
*/
|
||||
void print_adapter_id_help(void)
|
||||
{
|
||||
printf("\n");
|
||||
printf("DEVICE_IDS\n");
|
||||
printf(" List of cryptographic device ids separated by blanks which will be displayed.\n");
|
||||
printf(" DEVICE_ID could either be card device id ('<card-id>') or queue device id\n");
|
||||
printf(" '<card-id>.<domain-id>'). To filter all devices according to a dedicated\n");
|
||||
printf(" domain just provide '.<domain-id>'.\n");
|
||||
printf(" If no ids are given, all available devices are displayed.\n");
|
||||
printf("\n");
|
||||
printf("EXAMPLE:\n");
|
||||
printf(" List all cryptographic devices with card id '02'.\n");
|
||||
printf(" #>lszcrypt 02\n");
|
||||
printf("\n");
|
||||
printf(" List cryptographic devices with card id '02' and domain id '0005'.\n");
|
||||
printf(" #>lszcrypt 02.0005\n");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Entry point
|
||||
*/
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int c;
|
||||
|
||||
util_prg_init(&prg);
|
||||
util_opt_init(opt_vec, NULL);
|
||||
while (1) {
|
||||
c = util_opt_getopt_long(argc, argv);
|
||||
if (c == -1)
|
||||
break;
|
||||
switch (c) {
|
||||
case 'b':
|
||||
show_bus();
|
||||
return EXIT_SUCCESS;
|
||||
case 'c':
|
||||
show_capability(optarg);
|
||||
return EXIT_SUCCESS;
|
||||
case 'd':
|
||||
show_domains();
|
||||
return EXIT_SUCCESS;
|
||||
case 'V':
|
||||
l.verbose++;
|
||||
break;
|
||||
case 'h':
|
||||
util_prg_print_help();
|
||||
util_opt_print_help();
|
||||
print_adapter_id_help();
|
||||
return EXIT_SUCCESS;
|
||||
case 'v':
|
||||
util_prg_print_version();
|
||||
return EXIT_SUCCESS;
|
||||
default:
|
||||
util_opt_print_parse_error(c, argv);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
if (optind == argc)
|
||||
show_devices_all();
|
||||
else
|
||||
show_devices_argv(&argv[optind]);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Misc - Local helper functions
|
||||
*
|
||||
* Copyright IBM Corp. 2016, 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.
|
||||
*/
|
||||
|
||||
#include <regex.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "lib/util_panic.h"
|
||||
#include "misc.h"
|
||||
|
||||
/**
|
||||
* Test string against regular expression
|
||||
*
|
||||
* @param[in] str String to investigate
|
||||
* @param[in] regex Regular expression
|
||||
*
|
||||
* @returns true String matches with regular expression
|
||||
* false No match
|
||||
*/
|
||||
bool misc_regex_match(const char *str, const char *regex)
|
||||
{
|
||||
regmatch_t pmatch[1];
|
||||
regex_t preg;
|
||||
int rc;
|
||||
|
||||
rc = regcomp(&preg, regex, REG_EXTENDED);
|
||||
util_assert(rc == 0, "The regcomp() function failed: rc = %d\n", rc);
|
||||
|
||||
rc = regexec(&preg, str, (size_t) 1, pmatch, 0);
|
||||
regfree(&preg);
|
||||
return rc == 0 ? true : false;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* misc - Local helper functions
|
||||
*
|
||||
* Copyright IBM Corp. 2016, 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.
|
||||
*/
|
||||
|
||||
#ifndef MISC_H
|
||||
#define MISC_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
bool misc_regex_match(const char *str, const char *regex);
|
||||
|
||||
#endif /* MISC_H */
|
||||
Executable
+1465
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,475 @@
|
||||
.\" Copyright 2017 IBM Corp.
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
.TH ZNETCONF 8 "Mar 2009" "s390-tools"
|
||||
|
||||
.SH NAME
|
||||
znetconf \- list and configure network devices for System z network adapters
|
||||
|
||||
.SH SYNOPSIS
|
||||
.B znetconf
|
||||
.B [-h|--help] [-v|--version]
|
||||
.br
|
||||
|
||||
.br
|
||||
.B znetconf -u | -c
|
||||
.br
|
||||
.B znetconf -a <device_bus_id>[,...]{2} [-o <ATTR>=<VALUE>]+ [-d <DRIVER>]
|
||||
.br
|
||||
.B znetconf -A [-o <ATTR>=<VALUE>]+ [-d <DRIVER>] [-e <device_bus_id>]+
|
||||
.br
|
||||
.B znetconf -r <device_bus_id> [-n] | -R [-n] [-e <device_bus_id>]+
|
||||
|
||||
|
||||
.SH DESCRIPTION
|
||||
The znetconf command lists and automatically configures network devices for
|
||||
System z network adapters. It senses and lists potential
|
||||
network devices that are not yet configured or already configured.
|
||||
Based on these lists, it automatically adds or removes network devices.
|
||||
.P
|
||||
For automatic configuration, znetconf builds a channel command word
|
||||
(CCW) group device from sensed CCW devices, configures any specified
|
||||
option through the sensed network device driver and sets the new
|
||||
network device online.
|
||||
.P
|
||||
During automatic removal, znetconf sets the device offline and removes it.
|
||||
Be aware that removing all network devices leads to the
|
||||
complete loss of network connectivity. So a terminal session (e.g. 3270)
|
||||
might be required to restore.
|
||||
|
||||
.SH OPTIONS
|
||||
.TP 8
|
||||
.BR -h | --help
|
||||
Print help text.
|
||||
|
||||
.TP 8
|
||||
.BR -v | --version
|
||||
Print the version of the s390-tools package and the znetconf command.
|
||||
|
||||
.TP
|
||||
.BR -u | --unconfigured
|
||||
List potential network devices that are not yet configured.
|
||||
For each device, the following data is provided:
|
||||
.RS
|
||||
.TP 4
|
||||
*
|
||||
Device IDs (device bus-IDs) of the CCW devices constituting the network
|
||||
device
|
||||
.TP
|
||||
*
|
||||
Type of control unit (e.g. 1731/01)
|
||||
.TP
|
||||
*
|
||||
Network card type (e.g. OSA (QDIO))
|
||||
.TP
|
||||
*
|
||||
Channel path identifier (CHPID)
|
||||
.TP
|
||||
*
|
||||
Device driver (qeth, lcs, ctc, ctcm)
|
||||
.RE
|
||||
.TP
|
||||
.BR -c | --configured
|
||||
List configured network devices. For each device, the following data is
|
||||
provided:
|
||||
.RS
|
||||
.TP 4
|
||||
*
|
||||
Device IDs (device bus-IDs) of the CCW devices constituting the network device
|
||||
.TP
|
||||
*
|
||||
Control unit type (e.g. 1731/01)
|
||||
.TP
|
||||
*
|
||||
Card type (e.g. GuestLAN QDIO)
|
||||
.TP
|
||||
*
|
||||
Channel path identifier (CHPID)
|
||||
.TP
|
||||
*
|
||||
Driver (qeth, lcs, ctc, ctcm)
|
||||
.TP
|
||||
*
|
||||
Network interface name (if available)
|
||||
.TP
|
||||
*
|
||||
State (online vs. offline)
|
||||
.RE
|
||||
|
||||
.TP
|
||||
.BR -a | --add " <device_bus_id>[,<device_bus_id>)][,<device_bus_id>]
|
||||
[-o | --option <ATTR>=<VALUE>]+ [-d | --driver <DRIVER>]
|
||||
.br
|
||||
|
||||
.br
|
||||
Add the potential network device identified by device_bus_id.
|
||||
device_bus_id can be any of the device
|
||||
IDs listed as part of the potential network device list (argument
|
||||
.BR -u ")."
|
||||
For example, if znetconf
|
||||
.BR -u
|
||||
lists 0.0.f503,0.0.f504,0.0.f505 for a potential network device, device_bus_id
|
||||
may be 0.0.f503 or 0.0.f504 or 0.0.f505.
|
||||
If a device bus-ID begins with 0.0., you can abbreviate it to the final
|
||||
hexadecimal digits. For example, you can abbreviate 0.0.f503 to f503.
|
||||
.br
|
||||
.br
|
||||
If attribute value pairs are given with
|
||||
.BR -o ", "
|
||||
these pairs are configured for the created network device. The
|
||||
device is then set online regardless of whether the given attribute value pairs
|
||||
were applied successfully.
|
||||
.br
|
||||
Finally, the corresponding network interface name (e.g. eth1) is displayed.
|
||||
.br
|
||||
If more then one device_bus_id is given, the given set of devices is configured as a network device. znetconf tries to sense the required device driver
|
||||
automatically. If the device driver cannot be sensed, you must specify it with
|
||||
-d.
|
||||
.BR -d "."
|
||||
With
|
||||
.BR -d
|
||||
znetconf does NOT check the validity of the combination of device bus-IDs.
|
||||
|
||||
.TP
|
||||
.BR -A | --addall " [-o | --option <ATTR>=<VALUE>]+ [-d | --driver DRIVER]"
|
||||
[-e | --except <device_bus_id>]+
|
||||
.br
|
||||
|
||||
.br
|
||||
Add all potential network devices. If one or more device_bus_id are specified
|
||||
with
|
||||
.BR -e ", "
|
||||
the corresponding network devices are not added.
|
||||
Attribute value pairs given with
|
||||
.BR -o
|
||||
are configured for the network devices before they are set
|
||||
online. If the configuration of one potential network device fails,
|
||||
znetconf continues with the next remaining potential network device.
|
||||
|
||||
.TP
|
||||
.BR -r | --remove " <device_bus_id> [-n | --non-interactive]"
|
||||
Remove the network device identified by device_bus_id. device_bus_id is one of
|
||||
the device IDs of the network device. They are listed as part of znetconf
|
||||
.BR -c "."
|
||||
znetconf sets the device offline and removes it. If
|
||||
.BR -n
|
||||
is given, all confirmation questions are answered with 'yes'.
|
||||
|
||||
.TP
|
||||
.BR -R|--removall " [-n | --non-interactive] [-e | --except <device_bus_id>]+"
|
||||
Remove all network devices. If
|
||||
.BR -n
|
||||
is given, all confirmation questions are answered with 'yes'. To exclude
|
||||
certain devices from the removal, their device bus-IDs have to be given
|
||||
with
|
||||
.BR -e ". "
|
||||
|
||||
.TP
|
||||
\fB<ATTR>\fR
|
||||
Specify a device option. The option must match a sysfs attribute for the device
|
||||
to be configured. For a detailed description of the semantics of sysfs
|
||||
attributes please refer to the Device Drivers, Features, and Commands book for
|
||||
Linux on System z. The attributes are:
|
||||
|
||||
.RS
|
||||
.B qeth
|
||||
.br
|
||||
broadcast_mode
|
||||
.br
|
||||
buffer_count
|
||||
.br
|
||||
canonical_macaddr
|
||||
.br
|
||||
checksumming
|
||||
.br
|
||||
fake_broadcast
|
||||
.br
|
||||
ipa_takeover/add4
|
||||
.br
|
||||
ipa_takeover/add6
|
||||
.br
|
||||
ipa_takeover/del4
|
||||
.br
|
||||
ipa_takeover/del6
|
||||
.br
|
||||
ipa_takeover/enable
|
||||
.br
|
||||
ipa_takeover/invert4
|
||||
.br
|
||||
ipa_takeover/invert6
|
||||
.br
|
||||
isolation
|
||||
.br
|
||||
large_send
|
||||
.br
|
||||
layer2
|
||||
.br
|
||||
performance_stats
|
||||
.br
|
||||
portname
|
||||
.br
|
||||
portno
|
||||
.br
|
||||
priority_queueing
|
||||
.br
|
||||
route4
|
||||
.br
|
||||
route6
|
||||
.br
|
||||
rxip/add4
|
||||
.br
|
||||
rxip/add6
|
||||
.br
|
||||
rxip/del4
|
||||
.br
|
||||
rxip/del6
|
||||
.br
|
||||
vipa/add4
|
||||
.br
|
||||
vipa/add6
|
||||
.br
|
||||
vipa/del4
|
||||
.br
|
||||
vipa/del
|
||||
.br
|
||||
sniffer
|
||||
.RE
|
||||
|
||||
.RS
|
||||
.B ctc(m)
|
||||
.br
|
||||
buffer
|
||||
.br
|
||||
loglevel
|
||||
.br
|
||||
protocol
|
||||
.br
|
||||
stats
|
||||
.RE
|
||||
|
||||
.RS
|
||||
.B lcs
|
||||
.br
|
||||
portno
|
||||
.br
|
||||
lancmd_timeout
|
||||
.RE
|
||||
|
||||
.RS
|
||||
.B NOTE:
|
||||
for
|
||||
.BR qeth ", some attributes are specific to layer 2 or layer 3 mode
|
||||
of operation. The option "layer2=[0|1]" must be specified before
|
||||
such attributes in the command line, thus it is recommended to always
|
||||
specify it first.
|
||||
.RE
|
||||
|
||||
.TP
|
||||
\fB<device_bus_id>\fR
|
||||
Specify the device bus-ID of a CCW device. Device bus-IDs have the form
|
||||
([A-Fa-f0-9].[A-Fa-f0-9].)[A-Fa-f0-9]{4}.
|
||||
|
||||
If a device bus-ID begins with 0.0., you can abbreviate it to the final
|
||||
hexadecimal digits.
|
||||
|
||||
For example, you can abbreviate 0.0.f503 to f503.
|
||||
|
||||
.TP
|
||||
\fB<DRIVER>\fR
|
||||
Specify the device driver for the device. Valid values are qeth, lcs, ctc, or
|
||||
ctcm.
|
||||
|
||||
.SH EXAMPLES
|
||||
\fBznetconf -A\fR
|
||||
.RS
|
||||
Configures all potential network devices. To display a list of all potential
|
||||
network devices enter znetconf
|
||||
.BR -u "."
|
||||
After running znetconf
|
||||
.BR -A
|
||||
enter znetconf
|
||||
.BR -c
|
||||
to see which devices have been configured successfully.
|
||||
You can also enter znetconf
|
||||
.BR -u
|
||||
to display devices that have not been configured successfully.
|
||||
Successfully configured devices are no longer listed with znetconf
|
||||
.BR -u "."
|
||||
.RE
|
||||
.P
|
||||
\fBznetconf -A -e f500\fR
|
||||
.RS
|
||||
Configures all potential network devices except the one with the device bus-ID
|
||||
0.0.f500. To display a list of all potential network devices enter znetconf
|
||||
.BR -u "."
|
||||
After running \fBznetconf -A -e f500\fR
|
||||
enter znetconf
|
||||
.BR -c
|
||||
to see which devices have been configured successfully.
|
||||
You can also enter znetconf
|
||||
.BR -u
|
||||
to display devices that have not been configured successfully.
|
||||
Successfully configured devices are no longer listed with znetconf
|
||||
.BR -u "."
|
||||
.RE
|
||||
.P
|
||||
\fBznetconf -R\fR
|
||||
.RS
|
||||
Removes all configured network devices.
|
||||
After successfully running this command, all devices listed by znetconf -c
|
||||
become potential devices listed by
|
||||
.BR -u "."
|
||||
.RE
|
||||
.P
|
||||
\fBznetconf -R -e f501\fR
|
||||
.RS
|
||||
Removes all configured network devices except the one having the device bus-ID
|
||||
0.0.f501.
|
||||
After successfully running this command, all devices listed by znetconf -c
|
||||
except the one having the device bus-ID 0.0.f501 become potential devices
|
||||
listed by
|
||||
.BR -u "."
|
||||
.RE
|
||||
.P
|
||||
\fBznetconf -u\fR
|
||||
.RS
|
||||
Shows the list of potential network devices. Example output:
|
||||
.br
|
||||
|
||||
.br
|
||||
Device IDs Type Card Type CHPID Drv.
|
||||
.br
|
||||
--------------------------------------------------------
|
||||
.br
|
||||
0.0.f500,0.0.f501,0.0.f502 1731/01 OSA (QDIO) 00 qeth
|
||||
.br
|
||||
0.0.f503,0.0.f504,0.0.f505 1731/01 OSA (QDIO) 01 qeth
|
||||
.RE
|
||||
.P
|
||||
\fBznetconf -a 0.0.f503\fR
|
||||
.RS
|
||||
Adds the potential network device
|
||||
with 0.0.f503 as one of its device bus-IDs.
|
||||
After successfully running this command, znetconf
|
||||
.BR -c
|
||||
lists the new network device.
|
||||
.RE
|
||||
.P
|
||||
\fBznetconf -a f503\fR
|
||||
.RS
|
||||
This command is equivalent to \fBznetconf -a 0.0.f503\fR.
|
||||
.RE
|
||||
.P
|
||||
\fBznetconf -a f503 -o layer2=0 -o portname=myname\fR
|
||||
.RS
|
||||
Adds the potential network device
|
||||
with 0.0.f503 as one of its device bus-IDs
|
||||
and configures the options layer2 with value 0 and
|
||||
portname with myname.
|
||||
.RE
|
||||
.P
|
||||
\fBznetconf -c\fR
|
||||
.RS
|
||||
Shows a list of configured network devices. Example output:
|
||||
.br
|
||||
|
||||
.br
|
||||
Device IDs Type Card Type CHPID Drv. Name State
|
||||
.br
|
||||
-----------------------------------------------------------------------
|
||||
.br
|
||||
0.0.f503,0.0.f504,0.0.f505 1731/01 GuestLAN QDIO 01 qeth eth1 online
|
||||
.br
|
||||
0.0.f5f0,0.0.f5f1,0.0.f5f2 1731/01 OSD_1000 76 qeth eth0 online
|
||||
.RE
|
||||
.P
|
||||
\fBznetconf -r 0.0.f503\fR
|
||||
.RS
|
||||
Removes the network device with 0.0.f503 as one of its device bus-IDs.
|
||||
You can only remove configured devices as listed by znetconf
|
||||
.BR -c "."
|
||||
After successfully running this command the corresponding device appears in the
|
||||
list of potential network devices as listed by znetconf
|
||||
.BR -u "."
|
||||
.RE
|
||||
.P
|
||||
\fBznetconf -r f503\fR
|
||||
.RS
|
||||
This command is equivalent to \fBznetconf -r 0.0.f503\fR.
|
||||
.RE
|
||||
.P
|
||||
|
||||
.SH DIAGNOSTICS
|
||||
If znetconf runs successfully, the exit status is 0. In case of errors, the following codes are returned:
|
||||
.TP
|
||||
.BR 0
|
||||
success
|
||||
.TP
|
||||
.BR 9
|
||||
could not group devices
|
||||
.TP
|
||||
.BR 10
|
||||
could not set device online
|
||||
.TP
|
||||
.BR 11
|
||||
could not set device offline
|
||||
.TP
|
||||
.BR 12
|
||||
invalid attribute value pair
|
||||
.TP
|
||||
.BR 13
|
||||
missing component (broken installation)
|
||||
.TP
|
||||
.BR 15
|
||||
invalid device ID format
|
||||
.TP
|
||||
.BR 17
|
||||
unknown driver
|
||||
.TP
|
||||
.BR 19
|
||||
invalid argument
|
||||
.TP
|
||||
.BR 20
|
||||
too much arguments
|
||||
.TP
|
||||
.BR 21
|
||||
no configuration found for device ID
|
||||
.TP
|
||||
.BR 22
|
||||
device is not configured
|
||||
.TP
|
||||
.BR 23
|
||||
could not ungroup device
|
||||
.TP
|
||||
.BR 24
|
||||
at least one option could not be configured
|
||||
.TP
|
||||
.BR 25
|
||||
missing value for attribute
|
||||
.TP
|
||||
.BR 26
|
||||
device does not exist
|
||||
.TP
|
||||
.BR 27
|
||||
device already in use
|
||||
.TP
|
||||
.BR 28
|
||||
net device did not come online
|
||||
.TP
|
||||
.BR 29
|
||||
some devices could not be added or failed
|
||||
.TP
|
||||
.BR 30
|
||||
syntax error on command line
|
||||
.TP
|
||||
.BR 31
|
||||
ccwgroup devices do not exist
|
||||
.TP
|
||||
.BR 99
|
||||
internal znetconf bug
|
||||
.SH AUTHOR
|
||||
.nf
|
||||
This man-page was written by Einar Lueck <elelueck@de.ibm.com>.
|
||||
.fi
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# znetcontrolunits - define some common control unit mappings
|
||||
#
|
||||
# This definitions are not intended to be used as standalone tool, but should be
|
||||
# used from other tools like a library. E.g. znetconf is one exploiter
|
||||
# of them.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
# The arrays (among other things) should be adapted, if any of those device
|
||||
# drivers start supporting different CU types/models.
|
||||
|
||||
readonly -a CU=(
|
||||
1731/01
|
||||
1731/05
|
||||
3088/08
|
||||
3088/1f
|
||||
3088/1e
|
||||
3088/60
|
||||
1731/02
|
||||
1731/02
|
||||
)
|
||||
# 1731/06 (OSN) is no longer supported
|
||||
|
||||
readonly -a CU_DEVDRV=(
|
||||
qeth
|
||||
qeth
|
||||
ctcm
|
||||
ctcm
|
||||
ctcm
|
||||
lcs
|
||||
qeth
|
||||
qeth
|
||||
)
|
||||
|
||||
# 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
|
||||
}
|
||||
Reference in New Issue
Block a user