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:
Michael Holzheu
2017-08-07 16:13:17 +02:00
commit b627b8d8e1
647 changed files with 168974 additions and 0 deletions
+65
View File
@@ -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
View File
@@ -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
+135
View File
@@ -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
View File
@@ -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;
+75
View File
@@ -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)
+21
View File
@@ -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
+142
View File
@@ -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)
+442
View File
@@ -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;
}
+98
View File
@@ -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)
+240
View File
@@ -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;
}
+793
View File
@@ -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
+256
View File
@@ -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)
+25
View File
@@ -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
+120
View File
@@ -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
+937
View File
@@ -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;
}
+30
View File
@@ -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';
}
+15
View File
@@ -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
View File
@@ -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
+101
View File
@@ -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
View File
@@ -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
+81
View File
@@ -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
View File
@@ -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();
}
+63
View File
@@ -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
View File
@@ -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();
+73
View File
@@ -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
View File
@@ -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
+94
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+341
View File
@@ -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
+19
View File
@@ -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
+66
View File
@@ -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
+647
View File
@@ -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;
}
+142
View File
@@ -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;
}
+23
View File
@@ -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 */
+18
View File
@@ -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
+71
View File
@@ -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
+192
View File
@@ -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;
}
+21
View File
@@ -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
+112
View File
@@ -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)
+419
View File
@@ -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;
}
+146
View File
@@ -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)
+668
View File
@@ -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;
}
+37
View File
@@ -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;
}
+17
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+475
View File
@@ -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
+54
View File
@@ -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
}