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

89
.gitignore vendored Normal file
View File

@@ -0,0 +1,89 @@
#
# Ignore compiler generated files
#
*.o
*.a
*.d
#
# Ignore generated executables and other generated files
#
cmsfs-fuse/cmsfs-fuse
cpacfstats/cpacfstats
cpacfstats/cpacfstatsd
cpumf/bin/chcpumf
cpumf/bin/cpumf_helper
cpumf/bin/lscpumf
cpuplugd/cpuplugd
dasdfmt/dasdfmt
dasdinfo/dasdinfo
dasdview/dasdview
dump2tar/src/dump2tar
fdasd/fdasd
hmcdrvfs/hmcdrvfs
hyptop/hyptop
ip_watcher/xcec-bridge
ipl_tools/chreipl
ipl_tools/chshut
ipl_tools/lsreipl
ipl_tools/lsshut
iucvterm/bin/chiucvallow
iucvterm/bin/ts-shell
iucvterm/etc/ts-shell.conf
iucvterm/po/iucvterm.pot
iucvterm/src/iucvconn
iucvterm/src/iucvtty
iucvterm/src/ttyrun
iucvterm/test/test_afiucv
libutil/util_base_example
libutil/util_file_example
libutil/util_libc_example
libutil/util_opt_command_example
libutil/util_opt_example
libutil/util_panic_example
libutil/util_path_example
libutil/util_prg_example
libutil/util_rec_example
libutil/util_scandir_example
libzds/libzds.a
mon_tools/mon_fsstatd
mon_tools/mon_procd
osasnmpd/osasnmpd
qetharp/qetharp
qethqoat/qethqoat
systemd/cpacfstatsd.service
systemd/iucvtty-login@.service
systemd/ttyrun-getty@.service
tape390/tape390_crypt
tape390/tape390_display
tunedasd/src/tunedasd
vmconvert/vmconvert
vmcp/vmcp
vmur/vmur
zconf/chp/chchp
zconf/chp/lschp
zconf/css/lscss
zconf/qeth/lsqeth
zconf/scm/lsscm
zconf/zcrypt/chzcrypt
zconf/zcrypt/lszcrypt
zdev/src/chzdev
zdev/src/chzdev_usage.c
zdev/src/lszdev
zdev/src/lszdev_usage.c
zdsfs/zdsfs
zdump/zgetdump
zfcpdump/cpioinit
zfcpdump/zfcpdump_part
zfcpdump/zfcpdump_part.rd
ziomon/ziomon_mgr
ziomon/ziomon_util
ziomon/ziomon_zfcpdd
ziomon/ziorep_traffic
ziomon/ziorep_utilization
zipl/boot/*.bin
zipl/boot/*.exec
zipl/boot/data.h
zipl/src/chreipl_helper.device-mapper
zipl/src/zipl
zkey/zkey

93
AUTHORS.md Normal file
View File

@@ -0,0 +1,93 @@
List of all individuals having contributed content to s390-tools
----------------------------------------------------------------
- Alexey Ishchuk
- Andreas Herrmann
- Andre Wild
- Antoinette Kaschner
- Arnd Bergmann
- Axel Wirbser
- Benjamin Block
- Carsten Otte
- Christian Borntraeger
- Christof Schmitt
- Clemens von Mann
- Despina Papadopoulou
- Eberhard Pasch
- Einar Lueck
- Erwin Vicari
- Eugene Dvurechenski
- Eugene Crosser
- Farhan Ali
- Felix Beck
- Frank Blaschka
- Frank Munzert
- Frank Pavlic
- Fritz Elfert
- Gerald Schaefer
- Gerhard Tonn
- Hans Wippel
- Hans-Joachim Picht
- Harald Freudenberger
- Heiko Carstens
- Hendrik Brueckner
- Holger Dengler
- Holger Smolinski
- Hongjie Yang
- Horst Hummel
- Ingo Franzki
- Ingo Tuchscherer
- Jan Glauber
- Jan Hoeppner
- Jan Willeke
- Jean-Baptiste Joret
- Jens Remus
- Jochen Roehrig
- Julian Wiedmann
- Karsten Graul
- Kittipon Meesompop
- Klaus-Dieter Wacker
- Lakhvich Dmitriy
- Mark Dettinger
- Martin Kammerer
- Martin Peschke
- Martin Petermann
- Martin Schwidefsky
- Maxim Shchetynin
- Melissa Howland
- Michael Ernst
- Michael Holzheu
- Michael Mueller
- Mijo Safradin
- Mikhail Zaslonko
- Peter Oberparleiter
- Peter Tiedemann
- Raimund Schroeder
- Ralph Wuerthner
- Rene Trumpp
- Rolf Schaefer
- Sa Liu
- Sascha Silbe
- Sebastian Ott
- Seshagiri N. Ippili
- Simon Sturm
- Stefan Bader
- Stefan Haberland
- Stefan Raspl
- Stefan Reimbold
- Stefan Weinhuber
- Steffen Maier
- Steffen Thoss
- Susanne Wintenberger
- Sven Schuetz
- Swen Schillig
- Taraka R. Bodireddy
- Thomas Heidrich
- Thomas Richter
- Thomas Spatzier
- Thomas Weber
- Ursula Braun
- Utz Bacher
- Viktor Mihajlovski
- Volker Sameske
- Wolfgang Taphorn

11
CHANGELOG.md Normal file
View File

@@ -0,0 +1,11 @@
Release history for s390-tools (MIT version)
--------------------------------------------
* __v2.0.0 (2017-08-xx)__
- Publish package under the MIT license with the same contents as
the already available s390-tools-1.39.0
Previous releases of s390-tools can be found on the IBM Developer Works web pages:
- https://www.ibm.com/developerworks/linux/linux390/s390-tools.html

65
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,65 @@
Contributing to s390-tools
==========================
License
-------
All contributions have to be submitted under the MIT license. See also
the [LICENSE](LICENSE) file.
Developer's Certificate of Origin and Signed-off-by
---------------------------------------------------
The sign-off is a simple line at the end of the explanation for the patch,
which certifies that you wrote it or otherwise have the right to pass it on as
an open-source patch.
With the Signed-off-by line you certify the below:
```
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
```
If you can certify the above, just add a line saying:
```
Signed-off-by: Random Developer <random@developer.example.org>
```
Please use your real name (no pseudonyms or anonymous contributions).
Submitting code
---------------
The preferred way is to create a github pull request for your code.
Coding guidelines
-----------------
For s390-tools the preferred language is C. We provide libraries, e.g. libutil
that should be used by all tools if possible.
The coding style is based on the Linux kernel guidelines. Therefore, use
the checkpatch tool [1] for verification before you submit a patch.
[1] https://github.com/torvalds/linux/blob/master/scripts/checkpatch.pl

19
LICENSE Normal file
View File

@@ -0,0 +1,19 @@
Copyright IBM Corp. 2001, 2017
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

48
Makefile Normal file
View File

@@ -0,0 +1,48 @@
ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
# Include common definitions
include common.mak
LIB_DIRS = libvtoc libu2s libutil libzds libdasd libvmdump libccw
TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \
tape390 osasnmpd qetharp ip_watcher qethconf scripts zconf \
vmconvert vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \
ziomon iucvterm hyptop cmsfs-fuse qethqoat zfcpdump zdsfs cpumf \
systemd hmcdrvfs cpacfstats zdev dump2tar zkey
SUB_DIRS = $(LIB_DIRS) $(TOOL_DIRS)
all: $(TOOL_DIRS)
clean: $(TOOL_DIRS)
install: $(TOOL_DIRS)
#
# For simple "make" we explicitly set the MAKECMDGOALS to "all".
#
ifeq ($(MAKECMDGOALS),)
MAKECMDGOALS = all
endif
#
# We have to build the libraries before the tools are built. Otherwise
# the tools would trigger parallel "make -C" builds for libraries in
# case of "make -j".
#
# MAKECMDGOALS contains the list of goals, e.g. "clean all". We use
# "foreach" to generate a ";" separated list of "make -C <target>".
# For example the the expansion for "make clean all" is:
#
# $(MAKE) -C $@ [..] clean ; $(MAKE) -C $@ [...] all ;
#
# This ensures that the commandline targets are serialized and also "make -j"
# works as expected, e.g. "make clean all -j 20".
#
$(TOOL_DIRS): $(LIB_DIRS)
$(foreach goal,$(MAKECMDGOALS), \
$(MAKE) -C $@ TOPDIR=$(TOPDIR) ARCH=$(ARCH) $(goal) ;)
.PHONY: $(TOOL_DIRS)
$(LIB_DIRS):
$(foreach goal,$(MAKECMDGOALS), \
$(MAKE) -C $@ TOPDIR=$(TOPDIR) ARCH=$(ARCH) $(goal) ;)
.PHONY: $(LIB_DIRS)

361
README.md Normal file
View File

@@ -0,0 +1,361 @@
s390-tools
==========
The s390-tools package contains the source tree of a set of user space
utilities for use with the s390 Linux kernel and device drivers.
The package also contains the following files:
* [CONTRIBUTING](CONTRIBUTING.md): Contribution guidelines
* [LICENSE](LICENSE): The MIT license that applies to this package
* [CHANGELOG](CHANGELOG.md): The history of s390-tools versions
* [AUTHORS](AUTHORS.md): A list of all authors of the s390-tools package
Package contents
----------------
* dasdfmt:
Low-level format ECKD DASDs with the classical Linux disk layout or the new
z/OS compatible disk layout.
* fdasd:
Create or modify partitions on ECKD DASDs formatted with the z/OS
compatible disk layout.
* dasdview:
Display DASD and VTOC information or dump the contents of a DASD to the
console.
* dasdinfo:
Display unique DASD ID, either UID or volser.
* udev rules:
- 59-dasd.rules: rules for unique DASD device nodes created in /dev/disk/.
- 57-osasnmpd.rules: udev rules for osasnmpd.
- 60-readahead.rules: udev rules to set increased "default max readahead".
- 40-z90crypt.rules: udev rules for z90crypt driver
* systemd units:
- cpi.service: Unit to apply CPI settings
- dumpconf.service: Unit to configure dump on panic for s390
- mon_fsstatd.service: Unit for mon_fsstatd
- mon_procd.service: Unit for mon_procd
- iucvtty-login@.service: Instance unit to manage iucvtty instances
- ttyrun-getty@.service: Instance unit to manage ttyrun
* zipl:
Make DASDs or tapes bootable for system IPL or system dump.
* zgetdump:
Retrieve system dumps from either tapes or DASDs.
* qetharp:
Read and flush the ARP cache on OSA Express network cards.
* tape390_display:
Display information on the message display facility of a s390 tape
device.
* tape390_crypt:
Control and query crypto settings for 3592 tape devices.
* osasnmpd:
NET-SNMP subagent implementing MIBs provided by OSA-Express
features Fast Ethernet, Gigabit Ethernet, 10 Gigabit Ethernet.
* qethconf:
bash shell script simplifying the usage of qeth IPA (IP address
takeover), VIPA (Virtual IP address) and Proxy ARP.
* dbginfo.sh:
Shell script collecting useful information about the current system for
debugging purposes.
* zfcpdump:
Dump tool to create system dumps on fibre channel attached SCSI disk
partitions. It is installed using the "zipl -d" command.
* ip_watcher:
Provides HiperSockets Network Concentrator functionality.
It looks for addresses in the HiperSockets and sets them as Proxy ARP
on the OSA cards. It also adds routing entries for all IP addresses
configured on active HiperSockets devices.
Use start_hsnc.sh to start HiperSockets Network Concentrator.
* tunedasd:
Adjust tunable parameters on DASD devices.
* vmconvert:
Convert system dumps created by the z/VM VMDUMP command into dumps with
LKCD format. These LKCD dumps can then be analyzed with the dump analysis
tool lcrash.
* vmcp:
Send commands from Linux as a z/VM guest to the z/VM control program (CP).
Call vmcp with the CP command as an argument. The response of z/VM is
written to the standard output.
* vmur:
Work with z/VM spool file queues (reader, punch, printer).
* zfcpdbf:
Display debug data of zfcp. zfcp provides traces via the s390 debug
feature. Those traces are filtered with the zfcpdbf script, i.e. merge
several traces, make it more readable etc.
* scsi_logging_level:
Create, get or set the logging level for the SCSI logging facility.
* zconf:
Set of scripts to configure and list status information of Linux on s390
devices.
- chccwdev: Modify generic attributes of channel attached devices.
- lscss: List channel subsystem devices.
- lsdasd: List channel attached direct access storage devices (DASD).
- lsqeth: List all qeth-based network devices with their corresponding
settings.
- lstape: List tape devices, both channel and FCP attached.
- lszfcp: Show sysfs information about zfcp adapters, ports and units
that are online.
- lschp: List information about available channel-paths.
- lsscm: List information about available Storage Class Memory
Increments.
- chchp: Modify channel-path state.
- lsluns: List available SCSI LUNs depending on adapter or port.
- lszcrypt: Show Information about zcrypt devices and configuration.
- chzcrypt: Modify the zcrypt configuration.
- znetconf: List and configure network devices for s390 network adapters.
- cio_ignore: Query and modify the contents of the CIO device driver
blacklist.
- lsmem: Display the online status of the available memory.
- chmem: Set hotplug memory online or offline.
- dasdstat: Configure and format the debugfs based DASD statistics data.
* zkey:
Use the zkey tool to generate secure AES keys that are enciphered
with a master key of an IBM cryptographic adapter in CCA coprocessor mode.
You can also use the zkey tool to validate and re-encipher secure
AES keys.
* dumpconf:
Configure the dump device used for system dump in case a kernel
panic occurs. This tool can also be used by the "dumpconf" systemd unit
or as System V init script in /etc/init.d.
Prerequisite for dumpconf is a Linux kernel with the "dump on panic"
feature.
* mon_statd:
Linux - z/VM monitoring daemons.
- mon_fsstatd: Daemon that writes file system utilization data to the
z/VM monitor stream.
- mon_procd: Daemon that writes process information data to the z/VM
monitor stream.
* cpuplugd:
Manages CPU and memory resources based on a set of rules. Depending on
the workload, CPUs can be enabled or disabled. The amount of memory can
be increased or decreased exploiting the CMM1 feature.
* ipl_tools:
Tool set to configure and list re-IPL and shutdown actions.
- lsreipl: List information of re-IPL device.
- chreipl: Change re-IPL device settings.
- lsshut: List the actions that are configured as responses to of halt,
poff, reboot or panic.
- chshut: Change the actions that are to result from of halt, poff,
reboot or panic.
* ziomon tools:
Tool set to collect data for zfcp performance analysis and report.
* iucvterm:
z/VM IUCV terminal applications.
A set of applications to provide terminal access via the z/VM Inter-User
Communication Vehicle (IUCV). The terminal access does not require an
active TCP/IP connection between two Linux guest operating systems.
- iucvconn: Application to establish a terminal connection via z/VM IUCV.
- iucvtty: Application to provide terminal access via z/VM IUCV.
- ts-shell: Terminal server shell to authorize and control IUCV terminal
connections for individual Linux users.
* ttyrun:
Depending on your setup, Linux on s390 might or might not provide a
particular terminal or console. The ttyrun tool safely starts getty
programs and prevents respawns through the init program, if a terminal
is not available.
* cmsfs-fuse:
Read and write files stored on a z/VM CMS disk. The cmsfs-fuse file system
translates the record-based EDF file system on the CMS disk to UNIX
semantics. It is possible to mount a CMS disk and use common Linux tools
to access the files on the disk.
* hmcdrvfs:
Provide (read-only) access to files stored on a (assigned) DVD inserted
in a HMC drive. The command creates a FUSE.HMCDRVFS filesystem at the
specified mount point. The feature works with either the LPAR or the
z/VM hypervisor. But note especially for z/VM that the DVD must be
assigned to the associated system image (LPAR).
* hyptop:
Provide a dynamic real-time view of a s390 hypervisor environment.
The tool works with the z/VM and LPAR hypervisor. Depending on the available
data it shows e.g. CPU and memory consumption of active LPARs or z/VM
virtual guests. The tool provides a curses based user interface similar
to the popular Linux 'top' command.
* qethqoat:
Query the OSA address table and display physical and logical device
information.
* zdsfs:
Mount a z/OS DASD as Linux file system.
* CPU-measurement facilities (CPU-MF) tools:
Use the lscpumf tool to display information about the CPU-measurement
counter and sampling facilities. Use the chcpumf tool to control the
sampling facility support.
* cpacfstats:
The cpacfstats tools provide a client/server application set to monitor
and maintain CPACF activity counters.
* zdev:
Provides two tools to modify (chzdev) and display (lszdev) the persistent
configuration of devices and device drivers that are specific to the s390
platform.
* dump2tar:
dump2tar is a tool for creating a tar archive from the contents of
arbitrary files. It works even when the size of the actual file content
is not known beforehand (e.g. FIFOs, sysfs files).
For more information refer to the following publications:
* "Device Drivers, Features, and Commands" chapter "Useful Linux commands"
* "Using the dump tools"
Dependencies
------------
The s390-tools package has several build and runtime requirements. If your
build system does not have the required support, you can disable parts of
the s390-tools build with "`make HAVE_<LIBRARY>=0`", for example "`make
HAVE_FUSE=0`".
The following table provides an overview of the used libraries and
build options:
| __LIBRARY__|__BUILD OPTION__| __TOOLS__ |
|------------|:--------------:|:-------------------------------------:|
| fuse | `HAVE_FUSE` | cmsfs-fuse, zdsfs, hmcdrvfs, zgetdump |
| zlib | `HAVE_ZLIB` | zgetdump, dump2tar |
| ncurses | `HAVE_NCURSES` | hyptop |
| pfm | `HAVE_PFM` | cpacfstats |
| net-snmp | `HAVE_SNMP` | osasnmpd |
This table lists additional build or install options:
| __COMPONENT__ | __OPTION__ | __TOOLS__ |
|----------------|:----------------:|:-------------------------------:|
| dracut | `HAVE_DRACUT` | zdev |
The s390-tools build process uses "pkg-config" if available and hard-coded
compiler and linker options otherwise.
Build and runtime requirements for specific tools
-------------------------------------------------
In the following more details on the build an runtime requirements of
the different tools are provided:
* osasnmpd:
You need at least the NET-SNMP 5.1.x package (net-snmp-devel.rpm)
installed, before building the osasnmpd subagent.
For more information on NET-SNMP refer to:
http://net-snmp.sourceforge.net
* lsluns:
For executing the lsluns script the sg_luns command must be available.
The sg_luns executable is part of the SCSI generic device driver package
(sg3 utils/sg utils).
* ziomon tools:
For running the ziomon tools the following tools/packages are required:
- Packages: blktrace, multipath-tools, sg3-utils
- Tools: rsync, tar, lsscsi
* cmsfs-fuse/zdsfs/hmcdrvfs/zgetdump:
The tools cmsfs-fuse, zdsfs, hmcdrvfs, and zgetdump depend on FUSE.
FUSE is provided by installing the fuse and libfuse packages and by a
kernel compiled with `CONFIG_FUSE_FS`. For compiling the s390-tools package
the fuse-devel package is required.
The cmsfs-fuse tool requires FUSE version 2.8.1 or newer for full
functionality.
For further information about FUSE see: http://fuse.sourceforge.net
* hyptop:
The ncurses-devel package is required to build hyptop.
The libncurses package is required to run hyptop.
IMPORTANT: When running hyptop on a System z10 LPAR, the required minimum
microcode code level is the following:
Driver 79 MCL N24404.008 in the SE-LPAR stream
* ip_watcher/xcec-bridge:
As of s390-tools-1.10.0, the minimum required kernel level is 2.6.26.
For running ip_watcher these programs are required:
- qetharp (s390-tools)
- qethconf (s390-tools)
- route (net-tools)
* zfcpdbf:
As of s390-tools-1.13.0, the minimum required kernel level is 2.6.38.
* cpacfstats:
For building the cpacfstats tools you need libpfm version 4 or
newer installed (libpfm-devel.rpm). Tip: you may skip the cpacfstats
build by adding `HAVE_PFN=0` to the make invocation. To run the
cpacfstats daemon the kernel needs to have performance
events enabled (check for `CONFIG_PERF_EVENTS=y`) and you need libpfm
version 4 or newer installed. A new group 'cpacfstats' needs to be
created and all users intending to use the tool should be added to
this group.
* zdev:
Depending on the boot loader and initial RAM-disk mechanism used by a
Linux distribution, specific steps may be required to make changes to
the root device configuration persistent. zdev encapsulates this root
device logic in a helper script called 'zdev-root-update': This script
is invoked whenever the root device configuration is changed and it must
ensure that the persistent root device configuration is put into effect
during boot. zdev provides a sample implementation for zdev-root-update
which can be selected during the 'make install' step using the `HAVE_DRACUT`
variable:
- `HAVE_DRACUT=1` installs a zdev-root-update helper that works with zipl
as boot loader and dracut as initial RAM-disk provider.
Distributors with different boot or RAM-disk mechanisms should provide
a custom zdev-root-update helper script.
Some functions of zdev require that the following programs are available:
- modprobe (kmod)
- udevadm (systemd)
The following programs are not required but when available will improve
the functionality of the zdev tools:
- lsblk (util-linux)
- findmnt (util-linux)
- vmcp (s390-tools)
- ip (iproute2)
* znetconf:
For running znetconf these programs are required:
- modprobe (kmod)
- vmcp (s390-tools)

58
cmsfs-fuse/Makefile Normal file
View File

@@ -0,0 +1,58 @@
#!/usr/bin/make -f
include ../common.mak
ifeq (${HAVE_FUSE},0)
all:
$(SKIP) HAVE_FUSE=0
install:
$(SKIP) HAVE_FUSE=0
else
check_dep:
$(call check_dep, \
"cmsfs-fuse", \
"fuse.h", \
"fuse-devel or libfuse-dev", \
"HAVE_FUSE=0")
all: check_dep cmsfs-fuse
ifneq ($(shell sh -c 'command -v pkg-config'),)
FUSE_CFLAGS = $(shell pkg-config --silence-errors --cflags fuse)
FUSE_LDLIBS = $(shell pkg-config --silence-errors --libs fuse)
else
FUSE_CFLAGS = -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse
FUSE_LDLIBS = -lfuse
endif
ALL_CFLAGS += -DHAVE_SETXATTR $(FUSE_CFLAGS)
LDLIBS += $(FUSE_LDLIBS) -lm
OBJECTS = cmsfs-fuse.o dasd.o amap.o config.o
CMSFS_FUSE_DIR = $(SYSCONFDIR)/cmsfs-fuse
CONFIG_FILES = filetypes.conf
libs = $(rootdir)/libutil/libutil.a
cmsfs-fuse: $(OBJECTS) $(libs)
install: all
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 cmsfs-fuse \
$(DESTDIR)$(USRBINDIR)
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 cmsfs-fuse.1 \
$(DESTDIR)$(MANDIR)/man1
$(INSTALL) -g $(GROUP) -o $(OWNER) -d $(DESTDIR)$(CMSFS_FUSE_DIR)
for cnf in $(CONFIG_FILES); do \
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 etc/$$cnf $(DESTDIR)$(CMSFS_FUSE_DIR) ; \
done
endif
clean:
rm -f cmsfs-fuse *.o
.PHONY: all install clean check_dep

292
cmsfs-fuse/amap.c Normal file
View File

@@ -0,0 +1,292 @@
/*
* cmsfs-fuse - CMS EDF filesystem support for Linux
*
* Allocation map functions
*
* 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.
*/
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include "lib/zt_common.h"
#include "cmsfs-fuse.h"
#include "edf.h"
#include "helper.h"
/*
* Hint where to look for the next free block (level 0 only).
* Updated if a free block is found. If the level 0 amap bitmap
* block is exhausted we still scan all amap blocks.
*/
struct amap_alloction_hint {
/* addr of amap bitmap block to check */
off_t amap_addr;
/* disk addr of the last allocated or freed block */
off_t addr;
/* offset to start of the amap data block */
off_t offset;
};
static struct amap_alloction_hint amap_hint;
static void update_amap_hint(off_t amap_addr, off_t addr)
{
amap_hint.amap_addr = amap_addr;
amap_hint.addr = addr;
}
/*
* Get L1 block number from address.
*/
static int amap_blocknumber(off_t addr)
{
return addr / BYTES_PER_BLOCK;
}
/*
* Get the block number for a specific level.
*/
static int amap_blocknumber_level(int level, off_t addr)
{
int entry = amap_blocknumber(addr);
while (level-- > 1)
entry /= PTRS_PER_BLOCK;
return entry;
}
/*
* Return address of to the allocation map for a block number > 0.
*/
static off_t get_amap_addr(int level, off_t addr, off_t ptr)
{
int block = amap_blocknumber_level(level, addr) % PTRS_PER_BLOCK;
if (cmsfs.amap_levels == 0)
return cmsfs.amap;
if (level--) {
ptr = get_fixed_pointer(ptr + (off_t) block * PTR_SIZE);
if (!ptr)
DIE("amap invalid ptr at addr: %llx\n",
(unsigned long long) ptr +
(off_t) block * PTR_SIZE);
return get_amap_addr(level, addr, ptr);
}
return ptr;
}
/*
* Mark disk address as allocated in alloc map.
*/
static void amap_block_set(off_t amap, int bit)
{
u8 entry;
int rc;
rc = _read(&entry, sizeof(entry), amap);
BUG(rc < 0);
/* already used */
BUG(entry & (1 << (7 - bit)));
entry |= (1 << (7 - bit));
rc = _write(&entry, sizeof(entry), amap);
BUG(rc < 0);
}
/*
* Mark disk address as free in alloc map. Unaligned addr is tolerated.
*/
static void amap_block_clear(off_t addr)
{
off_t amap = get_amap_addr(cmsfs.amap_levels, addr, cmsfs.amap);
int rc, block = amap_blocknumber(addr);
off_t disk_addr = addr;
unsigned int byte, bit;
u8 entry;
if (block > 0)
addr -= (off_t) block * BYTES_PER_BLOCK;
addr >>= BITS_PER_DATA_BLOCK;
byte = addr / 8;
bit = addr % 8;
rc = _read(&entry, sizeof(entry), amap + byte);
BUG(rc < 0);
/* already cleared */
BUG(!(entry & (1 << (7 - bit))));
entry &= ~(1 << (7 - bit));
rc = _write(&entry, sizeof(entry), amap + byte);
BUG(rc < 0);
/*
* If the freed addr is lower set the hint to it to ensure
* the amap bitmap is packed from the start. That way we do not
* need an extra check if the bitmap entry is above disk end, the
* check if we overflow the total block limit is sufficient.
*/
if (disk_addr < amap_hint.addr)
update_amap_hint(amap + byte, disk_addr);
}
/*
* Return the first free bit in one byte.
*/
static inline int find_first_empty_bit(u8 entry)
{
u8 i;
for (i = 0; i < 8; i++)
if (!(entry & 1 << (7 - i)))
return i;
/* unreachable */
return -1;
}
/*
* Return the number of bytes addressed by one pointer entry for the
* specified level.
*/
static off_t bytes_per_level(int level)
{
off_t mult = BYTES_PER_BLOCK;
if (!level)
return 0;
level--;
while (level--)
mult *= PTRS_PER_BLOCK;
return mult;
}
static inline int get_amap_entry_bit(off_t amap)
{
u8 entry;
int rc;
rc = _read(&entry, sizeof(entry), amap);
BUG(rc < 0);
if (entry == 0xff)
return -1;
return find_first_empty_bit(entry);
}
static off_t __get_free_block_fast(void)
{
off_t addr, amap = amap_hint.amap_addr & ~DATA_BLOCK_MASK;
int bit, i = amap_hint.amap_addr & DATA_BLOCK_MASK;
for (; i < cmsfs.blksize; i++) {
bit = get_amap_entry_bit(amap + i);
if (bit == -1)
continue;
/* Calculate the addr for the free block we've found. */
addr = (off_t) amap_blocknumber(amap_hint.addr) * BYTES_PER_BLOCK;
addr += i * 8 * cmsfs.blksize;
addr += bit * cmsfs.blksize;
amap_block_set(amap + i, bit);
update_amap_hint(amap + i, addr);
return addr;
}
return 0;
}
/*
* Look for the first unallocated block and return addr of allocated block.
*/
static off_t __get_free_block(int level, off_t amap, off_t addr)
{
off_t ptr;
int bit, i;
if (level > 0) {
for (i = 0; i < PTRS_PER_BLOCK; i++) {
ptr = get_fixed_pointer(amap);
if (!ptr)
return 0;
ptr = __get_free_block(level - 1, ptr,
addr + i * bytes_per_level(level));
if (ptr)
return ptr;
amap += PTR_SIZE;
}
return 0;
}
for (i = 0; i < cmsfs.blksize; i++) {
bit = get_amap_entry_bit(amap + i);
if (bit == -1)
continue;
amap_block_set(amap + i, bit);
/* add byte offset */
addr += i * 8 * cmsfs.blksize;
/* add bit offset */
addr += bit * cmsfs.blksize;
update_amap_hint(amap + i, addr);
return addr;
}
return 0;
}
/*
* Allocate a free block and increment label block counter.
*/
off_t get_free_block(void)
{
off_t addr = 0;
if (cmsfs.used_blocks + cmsfs.reserved_blocks >= cmsfs.total_blocks)
return -ENOSPC;
if (amap_hint.amap_addr)
addr = __get_free_block_fast();
if (!addr)
addr = __get_free_block(cmsfs.amap_levels, cmsfs.amap, 0);
BUG(!addr);
cmsfs.used_blocks++;
return addr;
}
/*
* Allocate a zero-filled block and increment label block counter.
*/
off_t get_zero_block(void)
{
off_t addr = get_free_block();
int rc;
if (addr < 0)
return -ENOSPC;
rc = _zero(addr, cmsfs.blksize);
if (rc < 0)
return rc;
return addr;
}
/*
* Free a block and decrement label block counter.
*/
void free_block(off_t addr)
{
if (addr) {
amap_block_clear(addr);
cmsfs.used_blocks--;
}
}

208
cmsfs-fuse/cmsfs-fuse.1 Normal file
View File

@@ -0,0 +1,208 @@
.\" 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.
.\"
.TH CMSFS-FUSE 1 "February 2010" "s390-tools"
.SH NAME
cmsfs-fuse \- File system for z/VM CMS disks
.SH SYNOPSIS
.SS mounting:
.TP
\fBcmsfs-fuse\fP DEVICE MOUNTPOINT [OPTIONS]
.SS unmounting:
.TP
\fBfusermount\fP -u MOUNTPOINT
.SH DESCRIPTION
Use the \fBcmsfs-fuse\fP command to provide read and write access
to files stored on a z/VM CMS disk.
The cmsfs-fuse file system translates the record-based EDF file system on
the CMS disk to UNIX semantics.
After mounting the CMS disk, you can use common Linux tools to access
the files on the disk. You can enable automatic conversions of text files from
EBCDIC to ASCII.
Attention: You can inadvertently damage files and lose data when directly
writing to files within the cmsfs-fuse file system. To avoid problems when writing,
multiple restrictions must be observed, especially with regard to linefeeds (see
section RESTRICTIONS).
If you are unsure about how to safely write to a file on the cmsfs-fuse file
system, copy the file to a location outside the cmsfs-fuse file system, edit the file,
and then copy it back to its original location.
.SH OPTIONS
.SS "general options:"
.TP
\fB\-o\fR opt,[opt...]
Fuse or mount command options. For fuse options see below, for mount options
see \fBmount(8)\fP.
.TP
\fB\-h\fR or \fB\-\-help\fR
Print usage information, then exit.
.TP
\fB\-v\fR or \fB\-\-version\fR
Print version information, then exit.
.SS "cmsfs-fuse options:"
.TP
\fB\-a\fR or \fB\-\-ascii\fR
Interpret all files on the CMS disk as text files and convert them from
EBCDIC to ASCII.
.TP
\fB--from\fR
The codepage of the files on the CMS disk. If this option is not
specified the default codepage CP1047 is used. For a list of all available
codepages see iconv --list.
.TP
\fB--to\fR
The codepage to which CMS files should be converted to. If this option is not
specified the default codepage ISO-8859-1 is used. For a list of all available
codepages see iconv --list.
.TP
\fB\-t\fR or \fB\-\-filetype\fR
Interpret files on the CMS disk as text files based on the file type
and convert them from EBCDIC to ASCII. The file types that are treated
as text files are taken from a configuration file (see section CONFIGURATION FILES).
.SS "Applicable FUSE options (version 2.8):"
.TP
\fB\-d\fR or \fB\-o\fR debug
Enable debug output (implies \fB\-f\fR)
.TP
\fB\-f\fR
Foreground operation
.TP
\fB\-o\fR allow_other
Allow access by other users
.TP
\fB\-o\fR allow_root
Allow access by root
.TP
\fB\-o\fR nonempty
Allow mounts over non\-empty file/dir
.TP
\fB\-o\fR default_permissions
Enable permission checking by kernel
.TP
.TP
\fB\-o\fR max_read=N
Set maximum size of read requests
.TP
\fB\-o\fR kernel_cache
Cache files in kernel
.TP
\fB\-o\fR [no]auto_cache
Enable caching based on modification times
.TP
\fB\-o\fR umask=M
Set file permissions (octal)
.TP
\fB\-o\fR uid=N
Set file owner
.TP
\fB\-o\fR gid=N
Set file group
.TP
\fB\-o\fR max_write=N
Set maximum size of write requests
.TP
\fB\-o\fR max_readahead=N
Set maximum readahead
.TP
\fB\-o\fR async_read
Perform reads asynchronously (default)
.TP
\fB\-o\fR sync_read
Perform reads synchronously
.TP
\fB\-o big_writes\fR
Enable write operations with more than 4 KB
.SH EXTENDED ATTRIBUTES
Use the following extended attributes to handle the CMS characteristics of a file:
\fBuser.record_format\fR: The format of a file. Allowed values are F for fixed record length files
and V for variable record length files. This attribute can be set only if the file is empty.
\fBuser.record_lrecl\fR: The record length of a file. This attribute can be set only for a fixed
record length file and if the file is empty. A valid record length is an integer in the range 1-65535.
\fBuser.file_mode\fR: The file mode of a file which is interpreted by CMS. The file mode consists
of a mode letter from A-Z and mode number from 0-6.
New files are created by default as variable files with file mode A1.
.SH RESTRICTIONS
\fBrename\fR and \fBcreat\fR:
Uppercase file names are enforced.
\fBtruncate\fR:
Only shrinking of a file is supported. For fixed length record files, the new file size must
be a multiple of the record length.
\fBunlink\fR:
Creating a file with the name of a previously unlinked file which is still in use is not supported
and will fail with -ENOENT.
\fBwrite\fR:
Writes are supported only at the end of the file.
A write on a fixed length record file always writes a multiple
of the record length. If additional bytes are added, the
bytes are filled with zero in binary mode or with spaces in ASCII mode. Sparse files are not supported.
If the cp tool is used to write files to a CMS disk the option "--sparse=never" must be specified.
If ASCII translation is enabled for a file a linefeed character determines the end of a record.
The following restrictions must be observed for writing files in ASCII mode:
For fixed record length files a linefeed must occur exactly after a record of the length specified in the fixed record length.
For variable record length files a linefeed must occur after the maximum record length is reached or earlier.
If a record of a variable record length file consists only of a linefeed character cmsfs-fuse adds a space to this record since
empty records are not supported by the CMS file system.
.SH CONFIGURATION FILES
cmsfs-fuse uses a configuration file for automatic translation based on the file type.
Upon startup, cmsfs-fuse evaluates the file .cmsfs-fuse/filetypes.conf in the user's home directory. If the file does not
exist cmsfs-fuse evaluates the file /etc/cmsfs-fuse/filetypes.conf.
The filetypes.conf file contains the CMS file types that are automaticaly translated to ASCII if cmsfs-fuse is started
with the -t option. The syntax of the configuration file is one file type per line. Lines that start with a # followed by a space are treated as
comments and are ignored. The file type is 8 characters long and must consist of valid CMS file name characters only.
The default file types in the configuration file were taken from the z/VM TCPIP.DATA file
(z/VM version 5.4.0).
.SH EXAMPLES
To mount the CMS disk with the name dasde enter:
.br
# cmsfs-fuse /dev/dasde /mnt
.br
To mount the CMS disk with the name dasde and enable automatic translation
of known text files enter:
.br
# cmsfs-fuse -t /dev/dasde /mnt
To mount the CMS disk with the name dasde and enable automatic translation
of all files to UTF-8 enter:
.br
# cmsfs-fuse --to=UTF-8 -a /dev/dasde /mnt
To unmount the CMS disk mounted on /mnt enter:
.br
# fusermount -u /mnt
To show the record format of file PROFILE.EXEC assuming the CMS disk was mounted on /mnt:
# getfattr -n user.record_format /mnt/PROFILE.EXEC
The following example assumes that an empty, fixed record format file, PROFILE.EXEC, can be accessed on a CMS disk that has been mounted on /mnt. To set the record length of PROFILE.EXEC to 80 bytes:
# setfattr -n user.record_lrecl -v 80 /mnt/PROFILE.EXEC
.SH SEE ALSO
attr (5), getfattr (1), setfattr(1), iconv(1) and Linux on System z: Device Drivers, Features and Commands

4646
cmsfs-fuse/cmsfs-fuse.c Normal file

File diff suppressed because it is too large Load Diff

132
cmsfs-fuse/cmsfs-fuse.h Normal file
View File

@@ -0,0 +1,132 @@
/*
* cmsfs-fuse - CMS EDF filesystem support for Linux
*
* Data structures
*
* 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.
*/
#ifndef _CMSFS_H
#define _CMSFS_H
#include <iconv.h>
#include <search.h>
#include "lib/util_list.h"
#define COMP "cmsfs-fuse: "
extern struct cmsfs cmsfs;
/* conversion between absolute and relative addresses */
#define ABS(x) ((off_t) (x - 1) * cmsfs.blksize)
#define REL(x) ((x / cmsfs.blksize) + 1)
struct fcache_entry {
/* filename used as hash key */
char name[18];
/* location of fst entry */
off_t fst_addr;
/* filename string address */
char *str;
};
enum cmsfs_mode {
BINARY_MODE,
TEXT_MODE,
TYPE_MODE,
};
/* the per device global struture */
struct cmsfs {
/* name of the block device, e.g. /dev/dasde */
const char *device;
/* global file descriptor of the underlying block device */
int fd;
/* start of mmap of the whole block device */
char *map;
/* size of the disk */
off_t size;
/* formatted blocksize */
int blksize;
/* number of 512 byte blocks per block */
int nr_blocks_512;
/* device is read only */
int readonly;
/* access permission for other users */
int allow_other;
/* offset to label */
off_t label;
/* offset to file directory root FST */
off_t fdir;
/* offset to allocation map */
off_t amap;
/* depth of directories */
int dir_levels;
/* depth of allocation maps */
int amap_levels;
/* files count on the device */
int files;
/* conversion mode */
enum cmsfs_mode mode;
/* iconv codepage options */
const char *codepage_from;
const char *codepage_to;
iconv_t iconv_from;
iconv_t iconv_to;
/* disk stats */
int total_blocks;
int used_blocks;
/* blocks reserved for outstanding meta data */
int reserved_blocks;
/* constants */
int fixed_ptrs_per_block;
int var_ptrs_per_block;
int bits_per_data_block;
int bits_per_ptr_block;
int data_block_mask;
off_t amap_bytes_per_block;
/* file cache */
struct fcache_entry *fcache;
int fcache_used;
int fcache_max;
struct hsearch_data htab;
};
#define MAX_TYPE_LEN 9
struct filetype {
char name[MAX_TYPE_LEN];
struct util_list_node list;
};
#define NULL_BLOCK 0
#define VAR_FILE_END 1
#define PTRS_PER_BLOCK (cmsfs.fixed_ptrs_per_block)
#define VPTRS_PER_BLOCK (cmsfs.var_ptrs_per_block)
#define DATA_BLOCK_MASK (cmsfs.data_block_mask)
#define BITS_PER_DATA_BLOCK (cmsfs.bits_per_data_block)
#define BYTES_PER_BLOCK (cmsfs.amap_bytes_per_block)
extern int get_device_info(struct cmsfs *cmsfs);
extern int scan_conf_file(struct util_list *list);
extern int is_edf_char(int c);
#ifndef _CMSFS_FSCK
int _read(void *, size_t, off_t);
int _write(const void *, size_t, off_t);
int _zero(off_t, size_t);
off_t get_fixed_pointer(off_t);
off_t get_free_block(void);
off_t get_zero_block(void);
void free_block(off_t);
#endif
#endif

127
cmsfs-fuse/config.c Normal file
View File

@@ -0,0 +1,127 @@
/*
* cmsfs-fuse - CMS EDF filesystem support for Linux
*
* Config option parsing
*
* 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.
*/
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "lib/zt_common.h"
#include "cmsfs-fuse.h"
#include "helper.h"
#define MAX_LINE_LEN 80
static char *conf_file;
static int open_conf_file(FILE **fh)
{
const char *home_env;
conf_file = malloc(4096);
if (conf_file == NULL)
DIE_PERROR("malloc failed");
home_env = getenv("HOME");
if (home_env == NULL)
goto no_home;
sprintf(conf_file, "%s/.cmsfs-fuse/filetypes.conf", home_env);
*fh = fopen(conf_file, "r");
if (*fh != NULL)
goto out;
no_home:
sprintf(conf_file, "%s/%s", TOOLS_SYSCONFDIR,
"/cmsfs-fuse/filetypes.conf");
*fh = fopen(conf_file, "r");
if (*fh == NULL) {
free(conf_file);
return -ENOENT;
}
out:
DEBUG("using config file: %s\n", conf_file);
return 0;
}
static void add_filetype(char *name, struct util_list *list)
{
struct filetype *entry;
entry = malloc(sizeof(*entry));
if (entry == NULL)
DIE_PERROR("malloc failed");
strncpy(entry->name, name, MAX_TYPE_LEN);
util_list_add_head(list, entry);
}
static int filetype_valid(const char *type, int line)
{
unsigned int i;
if (strlen(type) > 8) {
WARN("entry too long in line: %d in config file: %s\n",
line, conf_file);
return 0;
}
for (i = 0; i < strlen(type); i++)
if (!is_edf_char(*(type + i))) {
WARN("invalid character in line: %d in config file: %s\n",
line, conf_file);
return 0;
}
return 1;
}
int scan_conf_file(struct util_list *list)
{
char buf[MAX_LINE_LEN], *tmp;
int line = 0;
FILE *fh;
if (open_conf_file(&fh) < 0)
return -ENOENT;
while (fgets(buf, MAX_LINE_LEN, fh) != NULL) {
line++;
tmp = buf;
while (isblank(*tmp))
tmp++;
if (*tmp == '\n')
continue;
/*
* Skip comments, comment must be "# " because # is a valid
* EDF character.
*/
if (strlen(tmp) > 1 && *tmp == '#' && *(tmp + 1) == ' ')
continue;
/* remove trailing \n */
if (strlen(tmp) && *(tmp + strlen(tmp) - 1) == '\n')
*(tmp + strlen(tmp) - 1) = '\0';
if (filetype_valid(tmp, line))
add_filetype(tmp, list);
}
fclose(fh);
free(conf_file);
return 0;
}

180
cmsfs-fuse/dasd.c Normal file
View File

@@ -0,0 +1,180 @@
/*
* cmsfs-fuse - CMS EDF filesystem support for Linux
*
* DASD specific functions
*
* 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.
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "cmsfs-fuse.h"
#include "edf.h"
#include "helper.h"
#define BLKSSZGET _IO(0x12, 104)
/* CMS disk label starts with ASCII string "CMS1" */
#define VOL_LABEL_EBCDIC 0xc3d4e2f1
static int disk_supported(int fd, struct cmsfs *cmsfs)
{
unsigned int cms_id = VOL_LABEL_EBCDIC;
struct cms_label label;
int rc;
rc = lseek(fd, cmsfs->label, SEEK_SET);
if (rc < 0) {
perror(COMP "lseek failed");
return 0;
}
rc = read(fd, &label, sizeof(label));
if (rc < 0) {
perror(COMP "read failed");
return 0;
}
/* check that the label contains the CMS1 string */
if (memcmp(label.id, &cms_id, sizeof(cms_id)) != 0)
return 0;
/* label sanity checks */
if (label.blocksize != 4096 &&
label.blocksize != 2048 &&
label.blocksize != 1024 &&
label.blocksize != 512) {
fprintf(stderr, COMP "Invalid disk block size!\n");
return 0;
}
if (label.dop != 4 && label.dop != 5) {
fprintf(stderr, COMP "Invalid disk origin pointer!\n");
return 0;
}
if (label.fst_entry_size != sizeof(struct fst_entry)) {
fprintf(stderr, COMP "Invalid FST entry size!\n");
return 0;
}
if (label.fst_per_block != label.blocksize / label.fst_entry_size) {
fprintf(stderr, COMP "Invalid FST per block value!\n");
return 0;
}
/* set the blocksize to the formatted one */
cmsfs->blksize = label.blocksize;
DEBUG(" DOP: %d", label.dop);
/* block number 5 means 0x4000... */
cmsfs->fdir = (label.dop - 1) * cmsfs->blksize;
DEBUG(" fdir: %lx", cmsfs->fdir);
/* get disk usage for statfs */
cmsfs->total_blocks = label.total_blocks;
cmsfs->used_blocks = label.used_blocks;
DEBUG(" Total blocks: %d Used blocks: %d",
cmsfs->total_blocks, cmsfs->used_blocks);
return 1;
}
static void get_device_info_ioctl(int fd, struct cmsfs *cmsfs)
{
if (ioctl(fd, BLKSSZGET, &cmsfs->blksize) != 0)
DIE("ioctl error get blocksize\n");
}
static int label_offsets[] = { 4096, 512, 2048, 1024, 8192 };
static void get_device_info_file(int fd, struct cmsfs *cmsfs)
{
unsigned int cms_id = VOL_LABEL_EBCDIC;
unsigned int i;
char label[4];
off_t offset;
int rc;
cmsfs->label = 0;
/*
* Read the blocksize from label. Unfortunately the blocksize
* position depends on the blocksize... time for some heuristics.
*/
for (i = 0; i < ARRAY_SIZE(label_offsets); i++) {
offset = label_offsets[i];
rc = lseek(fd, offset, SEEK_SET);
if (rc < 0)
DIE_PERROR("lseek failed");
rc = read(fd, &label, 4);
if (rc < 0)
DIE_PERROR("read failed");
/* check if the label contains the CMS1 string */
if (memcmp(label, &cms_id, sizeof(cms_id)) == 0) {
cmsfs->label = offset;
break;
}
}
if (!cmsfs->label)
DIE("Error CMS1 label not found!\n");
}
int get_device_info(struct cmsfs *cmsfs)
{
struct stat stat;
int fd;
/*
* Open writable, if write access is not granted fall back to
* read only.
*/
fd = open(cmsfs->device, O_RDWR);
if (fd < 0) {
if (errno == EROFS || errno == EACCES) {
cmsfs->readonly = 1;
fd = open(cmsfs->device, O_RDONLY);
if (fd < 0)
DIE_PERROR("open failed");
} else
DIE_PERROR("open failed");
}
if (fstat(fd, &stat) < 0)
DIE_PERROR("fstat failed");
if (S_ISBLK(stat.st_mode)) {
get_device_info_ioctl(fd, cmsfs);
cmsfs->label = 2 * cmsfs->blksize;
/* FBA disks have a different label location */
if (!disk_supported(fd, cmsfs)) {
cmsfs->label = cmsfs->blksize;
if (!disk_supported(fd, cmsfs))
goto error;
}
} else if (S_ISREG(stat.st_mode))
get_device_info_file(fd, cmsfs);
else
goto error;
if (!disk_supported(fd, cmsfs))
goto error;
return fd;
error:
DIE("Unsupported disk\n");
}

156
cmsfs-fuse/ebcdic.h Normal file
View File

@@ -0,0 +1,156 @@
/*
* cmsfs-fuse - CMS EDF filesystem support for Linux
*
* EBCDIC to ASCII conversion:
* EDF uses an EBCDIC codepage based on 037 with some modifications.
*
* 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.
*/
#ifndef _EBCDIC_H
#define _EBCDIC_H
#include <stdlib.h>
#include <sys/types.h>
/*
* EBCDIC 037 -> ISO8859-1
* changes:
* 0x5f: 0xaa -> 0x5e ^
* 0xad: 0x07 -> 0x5b [
* 0xbd: 0x07 -> 0x5d ]
*/
static char ebc2asc[256] = {
/* 0x00 */
0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F,
0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
/* 0x10 */
0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07,
0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
/* 0x20 */
0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B,
0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07,
/* 0x30 */
0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04,
0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A,
/* 0x40 */
0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86,
0x87, 0xA4, 0x9B, 0x2E, 0x3C, 0x28, 0x2B, 0x7C,
/* 0x50 */
0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07,
0x8D, 0xE1, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0x5E,
/* 0x60 */
0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F,
0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F,
/* 0x70 */
0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22,
/* 0x80 */
0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1,
/* 0x90 */
0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70,
0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07,
/* 0xa0 */
0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x5B, 0x07, 0x07,
/* 0xb0 */
0x5E, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC,
0xAB, 0x07, 0x5B, 0x5D, 0x07, 0x5D, 0x07, 0x07,
/* 0xc0 */
0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07,
/* 0xd0 */
0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98,
/* 0xe0 */
0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07,
/* 0xf0 */
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07
};
/* ISO8859-1 -> EBCDIC 037 */
static char asc2ebc[256] = {
/* 0x00 */
0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F,
0x16, 0x05, 0x15, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
/* 0x10 */
0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26,
0x18, 0x19, 0x3F, 0x27, 0x22, 0x1D, 0x1E, 0x1F,
/* 0x20 */
0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D,
0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61,
/* 0x30 */
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F,
/* 0x40 */
0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6,
/* 0x50 */
0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6,
0xE7, 0xE8, 0xE9, 0xBA, 0xE0, 0xBB, 0xB0, 0x6D,
/* 0x60 */
0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96,
/* 0x70 */
0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6,
0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07,
/* 0x80 */
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
/* 0x90 */
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
/* 0xa0 */
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
/* 0xb0 */
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
/* 0xc0 */
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
/* 0xd0 */
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
/* 0xe0 */
0x3F, 0x59, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
/* 0xf0 */
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x90, 0x3F, 0x3F, 0x3F, 0x3F, 0xEA, 0x3F, 0xFF
};
#define EBCDIC_ENCODE 0x0
#define EBCDIC_DECODE 0x1
static inline void a2e(char *dst, const char *src, int len, int to)
{
char *conv;
int i;
if (to == EBCDIC_ENCODE)
conv = asc2ebc;
else
conv = ebc2asc;
for (i = 0; i < len; i++)
dst[i] = conv[(unsigned int)src[i]];
}
static inline void ebcdic_enc(char *dst, const char *src, int len)
{
a2e(dst, src, len, EBCDIC_ENCODE);
}
static inline void ebcdic_dec(char *dst, const char *src, int len)
{
a2e(dst, src, len, EBCDIC_DECODE);
}
#endif

125
cmsfs-fuse/edf.h Normal file
View File

@@ -0,0 +1,125 @@
/*
* cmsfs-fuse - CMS EDF filesystem support for Linux
*
* EDF and label structures
*
* 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.
*/
#ifndef _EDF_H
#define _EDF_H
#include "helper.h"
/*
* File status table entry
*/
struct fst_entry {
char name[8];
char type[8];
char res1[8];
short int mode;
char res2[4];
char record_format;
char flag;
int record_len;
char res3[4];
unsigned int fop;
/* number of data blocks (not incl. pointer blocks) */
int nr_blocks;
int nr_records;
char levels;
char ptr_size;
char date[6];
char res4[4];
};
struct cms_label {
char id[6];
char user_id[6];
unsigned int blocksize;
unsigned int dop;
unsigned int f_cylinders;
unsigned int max_cylinders;
unsigned int total_blocks;
unsigned int used_blocks;
unsigned int fst_entry_size;
unsigned int fst_per_block;
char date[6];
unsigned int res1[3];
char res2[8];
};
#define RECORD_LEN_VARIABLE 0xe5
#define RECORD_LEN_FIXED 0xc6
/* TODO: correct for fixed? */
#define MAX_RECORD_LEN 0xffff
#define FST_ENTRY_SIZE sizeof(struct fst_entry)
#define FST_ENTRY_DIR_NAME 0x0000000100000000ULL
#define FST_ENTRY_DIR_TYPE 0xc4c9d9c5c3e3d6d9ULL /* 'DIRECTOR' */
#define FST_ENTRY_ALLOC_NAME 0x0000000200000000ULL
#define FST_ENTRY_ALLOC_TYPE 0xc1d3d3d6c3d4c1d7ULL /* 'ALLOCMAP' */
#define FST_FLAG_CENTURY 0x0008
#define FST_FOP_OFFSET 0x28
#define FST_LEVEL_OFFSET 0x34
#define VAR_RECORD_HEADER_SIZE 0x2
#define VAR_RECORD_SPANNED 0xffffffff
#define PTR_SIZE (sizeof(struct fixed_ptr))
#define VPTR_SIZE (sizeof(struct var_ptr))
struct fixed_ptr {
unsigned int next;
};
struct var_ptr {
unsigned int next;
int hi_record_nr;
unsigned int disp;
};
static inline int is_directory(const char *name,
const char *type)
{
if ((*(unsigned long long *) name) != FST_ENTRY_DIR_NAME)
return 0;
if ((*(unsigned long long *) type) != FST_ENTRY_DIR_TYPE)
return 0;
return 1;
}
static inline int is_allocmap(const char *name,
const char *type)
{
if ((*(unsigned long long *) name) != FST_ENTRY_ALLOC_NAME)
return 0;
if ((*(unsigned long long *) type) != FST_ENTRY_ALLOC_TYPE)
return 0;
return 1;
}
static inline int is_file(void *name, void *type)
{
if ((*(unsigned long long *) name) == 0ULL)
return 0;
/* Assumption: type = 0 is not legal */
if ((*(unsigned long long *) type) == 0ULL)
return 0;
return 1;
}
#endif

View File

@@ -0,0 +1,107 @@
#
# Filetypes that are interpreted as text files. If you want an EBCDIC
# file translated to ASCII, add the extension here.
#
# Comments must include a space after the #
# Add your extensions here:
PRM
CONF
# The following types were taken from the z/VM TCPIP.DATA file:
$EXEC
$REXX
$XEDIT
AMS
AMSERV
ANN
ANNOUNCE
APP
APPEND
ASC
ASCII
ASM
ASM3705
ASSEMBLE
AVL
AVAIL
A37
BASDATA
BASIC
BKS
BKSHELF
C
C++
CAT
CATALOG
CNTRL
COB
COBOL
COPY
CPP
DIRECT
DLCS
DOCUMENT
ESERV
EXC
EXEC
FFT
FOR
FORM
FORTRAN
FREEFORT
GCS
GROUP
H
HPP
HTM
HTML
H++
JOB
LISTING
LOG
LST
MAC
MACLIB
MACRO
MAK
MAKE
ME
MEMBER
MEMO
MODULE
NAM
NAMES
NETLOG
NONE
NOT
NOTE
NOTEBOOK
OFS
OPT
OPTIONS
PACKAGE
PASCAL
PKG
PLAS
PLI
PLIOPT
PLS
PVT
REXX
RPG
SCR
SCRIPT
STY
STYLE
TEXT
TEXTXXXX
TXT
TXTXXXX
UPDATE
UPDT
VMT
VSBASIC
VSBDATA
XED
XEDIT

54
cmsfs-fuse/helper.h Normal file
View File

@@ -0,0 +1,54 @@
/*
* cmsfs-fuse - CMS EDF filesystem support for Linux
*
* Common helper functions
*
* 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.
*/
#ifndef _HELPER_H
#define _HELPER_H
extern FILE *logfile;
#define DEBUG_LOGFILE "/tmp/cmsfs-fuse.log"
#ifdef DEBUG_ENABLED
#define DEBUG(...) \
do { \
fprintf(logfile, __VA_ARGS__); \
fflush(logfile); \
} while (0)
#else
#define DEBUG(...)
#endif
#define DIE(...) \
do { \
fprintf(stderr, COMP __VA_ARGS__); \
exit(1); \
} while (0)
#define DIE_PERROR(...) \
do { \
perror(COMP __VA_ARGS__); \
exit(1); \
} while (0)
#define BUG(x) \
if (x) { \
fprintf(stderr, COMP " assert failed at " \
__FILE__ ":%d in %s()\n", __LINE__, __func__); \
exit(1); \
}
#define WARN(...) \
do { \
fprintf(stderr, COMP "Warning, " __VA_ARGS__); \
} while (0)
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
#endif

353
common.mak Normal file
View File

@@ -0,0 +1,353 @@
ifndef $(COMMON_INCLUDED)
COMMON_INCLUDED = true
# Global definitions
# The variable "DISTRELEASE" should be overwritten in rpm spec files with:
# "make DISTRELEASE=%{release}" and "make install DISTRELEASE=%{release}"
VERSION = 1
RELEASE = 39
PATCHLEVEL = 0
DISTRELEASE = build-$(shell date +%Y%m%d)
S390_TOOLS_RELEASE = $(VERSION).$(RELEASE).$(PATCHLEVEL)-$(DISTRELEASE)
export S390_TOOLS_RELEASE
reldir = $(subst $(shell cd -P $(dir $(filter %common.mak,$(MAKEFILE_LIST))); \
pwd)/,,$(CURDIR))
rootdir= $(dir $(filter %common.mak,$(MAKEFILE_LIST)))
export S390_TEST_LIB_PATH=$(rootdir)/s390-tools-testsuite/lib
#
# For cross compiles specify CROSS_COMPILE= on the commandline:
#
# $ make CROSS_COMPILE="s390x-5.1.0-"
#
CROSS_COMPILE =
#
# Commands can be overwritten on the command line with "make <VAR>=<VALUE>":
#
# $ make CC=gcc-4.8
#
# The "cmd_define" macro wraps the command definition so that the commands
# can be user supplied and are still pretty-printed for the build process.
#
# The macro is called with the following parameters:
#
# $(1) - Used command variable in the Makefiles
# $(2) - Pretty Print output for the command
# $(3) - Default command if not user-specified
#
# The Example below...
#
# $(eval $(call cmd_define, CC," CC ",$(CROSS_COMPILE)gcc))
#
# ... produces the following code:
#
# CC = $(CROSS_COMPILE)gcc
# CC_SILENT := $(CC)
# override CC = $(call echocmd," CC ",/$@)$(CC_SILENT)
#
# The "strip" make function is used for the first parameter to allow blanks,
# which improves readability.
#
define cmd_define
$(strip $(1)) = $(3)
$(strip $(1))_SILENT := $$($(strip $(1)))
override $(strip $(1)) = $$(call echocmd,$(2),/$$@)$$($(strip $(1))_SILENT)
endef
$(eval $(call cmd_define, AS," AS ",$(CROSS_COMPILE)as))
$(eval $(call cmd_define, LINK," LINK ",$(CROSS_COMPILE)gcc))
$(eval $(call cmd_define, LD," LD ",$(CROSS_COMPILE)ld))
$(eval $(call cmd_define, CC," CC ",$(CROSS_COMPILE)gcc))
$(eval $(call cmd_define, HOSTCC," HOSTCC ",gcc))
$(eval $(call cmd_define, LINKXX," LINKXX ",$(CROSS_COMPILE)g++))
$(eval $(call cmd_define, CXX," CXX ",$(CROSS_COMPILE)g++))
$(eval $(call cmd_define, CPP," CPP ",$(CROSS_COMPILE)gcc -E))
$(eval $(call cmd_define, AR," AR ",$(CROSS_COMPILE)ar))
$(eval $(call cmd_define, NM," NM ",$(CROSS_COMPILE)nm))
$(eval $(call cmd_define, STRIP," STRIP ",$(CROSS_COMPILE)strip))
$(eval $(call cmd_define,OBJCOPY," OBJCOPY ",$(CROSS_COMPILE)objcopy))
$(eval $(call cmd_define,OBJDUMP," OBJDUMP ",$(CROSS_COMPILE)objdump))
$(eval $(call cmd_define,RUNTEST," RUNTEST ",$(S390_TEST_LIB_PATH)/s390_runtest))
$(eval $(call cmd_define, CAT," CAT ",cat))
$(eval $(call cmd_define, SED," SED ",sed))
$(eval $(call cmd_define, GZIP," GZIP ",gzip))
$(eval $(call cmd_define, MV," MV ",mv))
CHECK = sparse
CHECK_SILENT := $(CHECK)
CHECKTOOL = $(call echocmd," CHECK ",/$@)$(CHECK_SILENT)
SKIP = echo " SKIP $(call reldir) due to"
INSTALL = install
CP = cp
ifneq ("${V}","1")
MAKEFLAGS += --quiet
echocmd=echo $1$(call reldir)$2;
RUNTEST += > /dev/null 2>&1
else
echocmd=
endif
ifeq ("${W}","1")
DEFAULT_CFLAGS = -g -rdynamic -fstack-protector-all -W -Wall -Wformat-security -Wextra
else
DEFAULT_CFLAGS = -g -rdynamic -fstack-protector-all -W -Wall -Wformat-security
endif
ifeq ("${D}","1")
DEFAULT_CFLAGS += -Og
else
DEFAULT_CFLAGS += -O3
endif
DEFAULT_CPPFLAGS = -D_GNU_SOURCE
DEFAULT_LDFLAGS = -rdynamic
#
# Check for build dependency
#
# $1: Name of tool or feature that requires dependency
# $2: Name of include file to check
# $3: Name of required devel package
# $4: Option to skip build (e.g. HAVE_FUSE=0)
#
check_dep=\
printf "\#include <%s>" $2 | ( $(CC) $(filter-out --coverage, $(ALL_CFLAGS)) $(ALL_CPPFLAGS) -c -o /dev/null -xc - ) > /dev/null 2>&1; \
if [ $$? != 0 ]; \
then \
printf " REQCHK %s (%s)\n" $1 $2; \
printf "********************************************************************************\n" >&2; \
printf "* Missing build requirement for: %-45s *\n" $1 >&2; \
printf "* Install package..............: %-45s *\n" $3 >&2; \
printf "* You can skip build with......: make %-40s *\n" $4 >&2; \
printf "********************************************************************************\n" >&2; \
exit 1; \
fi
#
# Support alternate install root
#
# INSTALLDIR: Finally install s390-tools to INSTALLDIR. This can be used
# for testing locally installed tools.
# DESTDIR: Temporary install s390-tools to this directory. This can be
# used for building s390-tools e.g. with rpmbuild.
#
# The difference between INSTALLDIR and DESTDIR is that for INSTALLDIR
# internally used directories (e.g. for config files) are adjusted.
#
# Example:
#
# $ cd cpumf
# $ INSTALLDIR=/tmp make install
# $ cat /tmp/lib/s390-tools/cpumf_helper | grep DATA_DIR
# my $CPUMF_DATA_DIR = '/tmp/usr/share/s390-tools/cpumf';
#
# $ make clean
# $ DESTDIR=/tmp make install
# $ cat /tmp/lib/s390-tools/cpumf_helper | grep DATA_DIR
# my $CPUMF_DATA_DIR = '/usr/share/s390-tools/cpumf';
#
ifdef INSTROOT
$(error INSTROOT is no longer available, use DESTDIR instead)
endif
INSTALLDIR ?=
DESTDIR ?=
USRSBINDIR = $(INSTALLDIR)/usr/sbin
USRBINDIR = $(INSTALLDIR)/usr/bin
BINDIR = $(INSTALLDIR)/sbin
LIBDIR = $(INSTALLDIR)/lib
SYSCONFDIR = $(INSTALLDIR)/etc
MANDIR = $(INSTALLDIR)/usr/share/man
VARDIR = $(INSTALLDIR)/var
TOOLS_DATADIR = $(INSTALLDIR)/usr/share/s390-tools
TOOLS_LIBDIR = $(INSTALLDIR)/lib/s390-tools
ZFCPDUMP_DIR = $(INSTALLDIR)/lib/s390-tools/zfcpdump
# Systemd support files are installed only if a directory is specified
# for SYSTEMDSYSTEMUNITDIR (e.g. /lib/systemd/system)
SYSTEMDSYSTEMUNITDIR =
INSTDIRS = $(USRSBINDIR) $(USRBINDIR) $(BINDIR) $(LIBDIR) $(MANDIR) \
$(SYSCONFDIR) $(TOOLS_LIBDIR) $(TOOLS_DATADIR) \
$(ZFCPDUMP_DIR) $(SYSTEMDSYSTEMUNITDIR)
OWNER = $(shell id -un)
GROUP = $(shell id -gn)
export INSTALLDIR BINDIR LIBDIR MANDIR OWNER GROUP
# Special defines for zfcpdump
ZFCPDUMP_PART_IMAGE = zfcpdump_part.image
ZFCPDUMP_PART_RD = zfcpdump_part.rd
export ZFCPDUMP_DIR ZFCPDUMP_PART_IMAGE ZFCPDUMP_PART_RD
CFLAGS ?= $(DEFAULT_CFLAGS) $(OPT_FLAGS)
HOSTCFLAGS ?= $(DEFAULT_CFLAGS) $(OPT_FLAGS)
CPPFLAGS ?= $(DEFAULT_CPPFLAGS)
LDFLAGS ?= $(DEFAULT_LDFLAGS)
ALL_CFLAGS = -DS390_TOOLS_RELEASE=$(S390_TOOLS_RELEASE) \
-DS390_TOOLS_LIBDIR=$(TOOLS_LIBDIR) \
-DS390_TOOLS_DATADIR=$(TOOLS_DATADIR) \
-DS390_TOOLS_SYSCONFDIR=$(SYSCONFDIR) \
$(CFLAGS)
CXXFLAGS ?= $(DEFAULT_CFLAGS) $(OPT_FLAGS)
ALL_CXXFLAGS = -DS390_TOOLS_RELEASE=$(S390_TOOLS_RELEASE) \
-DS390_TOOLS_LIBDIR=$(TOOLS_LIBDIR) \
-DS390_TOOLS_DATADIR=$(TOOLS_DATADIR) \
-DS390_TOOLS_SYSCONFDIR=$(SYSCONFDIR) \
$(CXXFLAGS)
ALL_CPPFLAGS = -I $(rootdir)include $(CPPFLAGS)
ALL_LDFLAGS = $(LDFLAGS)
# make G=1
# Compile tools so that gcov can be used to collect code coverage data.
# See the gcov man page for details.
ifeq ("${G}","1")
ALL_CFLAGS := $(filter-out -O%,$(ALL_CFLAGS)) --coverage
ALL_CXXFLAGS := $(filter-out -O%,$(ALL_CXXFLAGS)) --coverage
ALL_LDFLAGS += --coverage
endif
export AS LD CC CPP AR NM STRIP OBJCOPY OBJDUMP INSTALL CFLAGS CXXFLAGS \
LDFLAGS CPPFLAGS ALL_CFLAGS ALL_CXXFLAGS ALL_LDFLAGS ALL_CPPFLAGS
# Overwrite implicite makefile rules for having nice compile output
%.o: %.c
$(CC) $(ALL_CPPFLAGS) $(ALL_CFLAGS) -c $< -o $@
ifeq ("${C}","1")
$(CHECKTOOL) $(ALL_CPPFLAGS) $(ALL_CFLAGS) -c $< -o $@
endif
%.o: %.cpp
$(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $< -o $@
%: %.o
$(LINK) $(ALL_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@
%.a:
$(AR) rcs $@ $^
all:
help:
@echo 'Usage: make [TARGETS] [OPTIONS]'
@echo ''
@echo 'TARGETS'
@echo ' all Build all tools (default target)'
@echo ' install Install tools'
@echo ' clean Delete all generated files'
@echo ''
@echo 'OPTIONS'
@echo ' D=1 Build with debugging option "-Og"'
@echo ' C=1 Build with check tool defined with "CHECK=" (default=sparse)'
@echo ' G=1 Build with gcov to collect code coverage data'
@echo ' V=1 Generate verbose build output'
@echo ' W=1 Build with higher warning level'
@echo ''
@echo 'EXAMPLES'
@echo ' # make clean all D=1 W=1 -j'
@echo ' # make C=1 CHECK=smatch'
.PHONY: help
# Automatic dependency generation
#
# Create ".o.d" dependency files with the -MM compile option for all ".c" and
# ".cpp" files in the directory of the Makefile that includes common.mak:
#
# $ gcc -MM vmcp.c
# vmcp.o: vmcp.c vmcp.h ../include/zt_common.h
#
# Use -MM instead of -M to *not* mention system header files. We expect
# "make clean all" in case of system header updates.
# We consider header files in three possible directories
sources_h = \
$(wildcard *.h) \
$(wildcard ../include/*.h) \
$(wildcard $(rootdir)/include/lib/*.h)
# Rules to create ".o.d" files out of ".c" or ".cpp" files:
.%.o.d: %.c $(sources_h)
$(CC_SILENT) -MM $(ALL_CPPFLAGS) $(ALL_CFLAGS) $< > $@
.%.o.d: %.cpp $(sources_h)
$(CXX_SILENT) -MM $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) $< > $@
# The sources_c/cpp variable contains a list of all ".c" or ".cpp" files in
# in the current directory.
sources_c = $(wildcard *.c)
sources_cpp = $(wildcard *.cpp)
# The dependencies_c/cpp variable contains a list of all ".o.d" files,
# one for each ".c" or ".cpp" file.
dependencies_c = $(sources_c:%.c=.%.o.d)
dependencies_cpp = $(sources_cpp:%.cpp=.%.o.d)
# Include all ".o.d" dependency files for all make targets except for "clean"
ifneq ($(MAKECMDGOALS),clean)
-include $(dependencies_c)
-include $(dependencies_cpp)
endif
# Rules for internal libraries needed to ensure that these files are build
# with their own build flags even if they are build from external directories.
#
# Because of the PHONY directory dependency all tools that use libraries
# check the library directory via "make -C" when the tools Makefile is
# processed.
$(rootdir)/libutil/libutil.a: $(rootdir)/libutil
$(MAKE) -C $(rootdir)/libutil/ libutil.a
.PHONY: $(rootdir)/libutil
$(rootdir)/libccw/libccw.a: $(rootdir)/libccw
$(MAKE) -C $(rootdir)/libccw/ libccw.a
.PHONY: $(rootdir)/libccw
$(rootdir)/libvtoc/libvtoc.a: $(rootdir)/libvtoc
$(MAKE) -C $(rootdir)/libvtoc/ libvtoc.a
.PHONY: $(rootdir)/libvtoc
$(rootdir)/libdasd/libdasd.a: $(rootdir)/libdasd
$(MAKE) -C $(rootdir)/libdasd/ libdasd.a
.PHONY: $(rootdir)/libdasd
$(rootdir)/libzds/libzds.a: $(rootdir)/libzds
$(MAKE) -C $(rootdir)/libzds/ libzds.a
.PHONY: $(rootdir)/libzds
$(rootdir)/libu2s/libu2s.a: $(rootdir)/libu2s
$(MAKE) -C $(rootdir)/libu2s/ libu2s.a
.PHONY: $(rootdir)/libu2s
$(rootdir)/libvmdump/libvmdump.a: $(rootdir)/libvmdump
$(MAKE) -C $(rootdir)/libvmdump/ libvmdump.a
.PHONY: $(rootdir)/libvmdump
$(rootdir)/zipl/boot/data.o:
$(MAKE) -C $(rootdir)/zipl/boot/ data.o
install_dirs:
for dir in $(INSTDIRS); do \
test -d $(DESTDIR)$$dir || $(INSTALL) -g $(GROUP) -o $(OWNER) -d $(DESTDIR)$$dir; \
done
for i in 1 2 3 4 5 6 7 8; do \
test -d $(DESTDIR)$(MANDIR)/man$$i || $(INSTALL) -g $(GROUP) -o $(OWNER) \
-d $(DESTDIR)$(MANDIR)/man$$i; \
done
install_echo:
$(call echocmd," INSTALL ")
install: install_echo install_dirs
clean_echo:
$(call echocmd," CLEAN ")
clean_gcov:
rm -f *.gcda *.gcno *.gcov
clean_dep:
rm -f .*.o.d
clean: clean_echo clean_gcov clean_dep
endif

41
cpacfstats/Makefile Normal file
View File

@@ -0,0 +1,41 @@
include ../common.mak
ALL_CPPFLAGS += -DVERSION=$(VERSION)
ifeq (${HAVE_PFM},0)
all:
$(SKIP) HAVE_PFM=0
install:
$(SKIP) HAVE_PFM=0
else
check_dep:
$(call check_dep, \
"cpacfstats", \
"perfmon/pfmlib.h", \
"libpfm-devel or libpfm4-dev", \
"HAVE_PFM=0")
all: check_dep cpacfstats cpacfstatsd
cpacfstatsd: cpacfstatsd.o stats_sock.o perf_crypto.o
$(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -lpfm -o $@
cpacfstats: cpacfstats.o stats_sock.o
$(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@
install: all
$(INSTALL) -m 755 cpacfstatsd $(DESTDIR)$(USRSBINDIR)
$(INSTALL) -m 755 cpacfstats $(DESTDIR)$(USRBINDIR)
$(INSTALL) -m 644 cpacfstatsd.8 $(DESTDIR)$(MANDIR)/man8
$(INSTALL) -m 644 cpacfstats.1 $(DESTDIR)$(MANDIR)/man1
endif
clean:
rm -f *.o *~ cpacfstatsd cpacfstats
.PHONY: all clean install check_dep

127
cpacfstats/cpacfstats.1 Normal file
View File

@@ -0,0 +1,127 @@
.\" cpacfstats.1
.\"
.\" Copyright IBM Corp. 2015, 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
.\" groff -man -Tutf8 cpacfstats.1
.\" or
.\" nroff -man cpacfstats.1
.\" to process this source
.\"
.TH cpacfstats "1" "January 2015" "s390-tools"
.
.ds c \fcpacfstats\fP
.
.SH NAME
cpacfstats \- enable, disable and display CPACF statistical data
.
.SH SYNOPSIS
.B cpacfstats
.RB [ \-h | \-\-help ]
.RB [ \-v | \-\-version ]
.RB [ \-e | \-\-enable
.I counter
.RB ]
.RB [ \-d | \-\-disable
.I counter
.RB ]
.RB [ \-r | \-\-reset
.I counter
.RB ]
.RB [ \-p | \-\-print
.I counter
.RB ]
.
.SH DESCRIPTION
The cpacfstats client application interacts with the cpacfstatsd daemon and
triggers actions. The application enables, disables, resets, and fetches
one or all of the mainframe CPACF performance counters with the help of the
daemon process.
All counters are initially disabled and must be switched on to measure
CPACF activities of the system. There is a slight performance penalty with
CPACF counters enabled.
Note: All cryptographic counters are bound to physical CPUs. If you use
dedicated CPUs for your LPAR, you obtain only your CPACF activities. If
your CPUs are shared with other LPARs, their cryptographic activities might
distort the results. CPACF performance counters are available on LPARs
only. For security reasons only members of the group \fIcpacfstats\fR are
allowed to run the cpacfstats client application.
Example usage scenario:
.P
1. Start the cpacfstatsd daemon with root privileges.
.P
2. Check for successful startup by using the ps and syslog commands.
.P
3. Enable the CPACF counters of interest. For example, enable all counters
by issuing cpacfstats -e.
.P
4. Run your applications.
.P
5. Display counter values by using the cpacfstats command. Reset the
cryptographic counters as required. To reset, use, for example, cpacfstats
-r.
.P
6. Disable all the CPACF measurements, for example, by using cpacfstats
-d.
.P
7. Shutdown the cpacfstatsd daemon by using killall cpacfstatsd.
.SH OPTIONS
.TP
\fB\-h\fR or \fB\-\-help\fR
Display help information for the command.
.TP
\fB\-v\fR or \fB\-\-version\fR
Display version and copyright information for the command.
.TP
\fB\-e\fR or \fB\-\-enable\fR [counter]
Enable one or all CPACF performance counters. The optional counter argument
can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR or \fBall\fR. If
the counter argument is omitted, all performance counters are
enabled. Enabling a counter does not reset it. New events are added to the
current counter value.
.TP
\fB\-d\fR or \fB\-\-disable\fR [counter]
Disable one or all CPACF performance counters. The optional counter
argument can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR or
\fBall\fR. If the counter argument is omitted, all performance counters
are disabled. Disabling a counter does not reset it. The counter value is
preserved when a counter is disabled, and counting will resume using the
preserved value when the counter is re-enabled.
.TP
\fB\-r\fR or \fB\-\-reset\fR [counter]
Reset one or all CPACF performance counters. The optional counter
argument can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR or
\fBall\fR. If the counter argument is omitted, all performance counters are
reset to 0.
.TP
\fB\-p\fR or \fB\-\-print\fR [counter]
Display the value of one or all CPACF performance counters. The optional
counter argument can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR
or \fBall\fR. If the counter argument is omitted or if there is no
argument, all performance counters are displayed.
.TP
The default command is --print all.
.
.SH FILES
.nf
/var/run/cpacfstatsd_socket
.fi
.
.SH RETURN VALUE
.IP 0
Successful program execution.
.IP 1
An error occurred, reasons include: invalid argument, cpacfstatsd could not
be reached (check that the daemon is running), insufficient access rights,
version mismatch between client and daemon, or the application is out of
memory. The application prints a message with the details of the error and
the errno value.
.
.SH SEE ALSO
cpacfstatsd (8)

237
cpacfstats/cpacfstats.c Normal file
View File

@@ -0,0 +1,237 @@
/*
* cpacfstats - display and maintain CPACF perf counters
*
* cpacfstats client implementation
*
* Copyright IBM Corp. 2015, 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 <errno.h>
#include <getopt.h>
#include <getopt.h>
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "lib/zt_common.h"
#include "cpacfstats.h"
static const char *const name = "cpacfstats";
static const char *const usage =
"Usage: %s [OPTIONS [COUNTER]]\n"
"\n"
"Enable, disable, reset and read CPACF Crypto Activity Counters\n"
"Use OPTIONS described below:\n"
"\n"
"\t-h, --help Print this help, then exit\n"
"\t-v, --version Print version information, then exit\n"
"\t-e, --enable [counter] Enable one or all counters\n"
"\t-d, --disable [counter] Disable one or all counters\n"
"\t-r, --reset [counter] Reset one or all counter values\n"
"\t-p, --print [counter] Print one or all counter values\n"
"\tcounter can be: 'aes' 'des' 'rng' 'sha' or 'all'\n";
static const char *const counter_str[] = {
[DES_FUNCTIONS] = "des",
[AES_FUNCTIONS] = "aes",
[SHA_FUNCTIONS] = "sha",
[PRNG_FUNCTIONS] = "rng",
[ALL_COUNTER] = "all"
};
static int send_query(int s, enum cmd_e cmd, enum ctr_e ctr)
{
struct msg m;
memset(&m, 0, sizeof(m));
m.head.m_ver = VERSION;
m.head.m_type = QUERY;
m.query.m_ctr = ctr;
m.query.m_cmd = cmd;
return send_msg(s, &m);
}
static int recv_answer(int s, int *ctr, int *state, uint64_t *value)
{
struct msg m;
int rc;
rc = recv_msg(s, &m);
if (rc == 0) {
if (m.head.m_ver != VERSION) {
eprint("Received msg with wrong version %d != %d\n",
m.head.m_ver, VERSION);
return -1;
}
if (m.head.m_type != ANSWER) {
eprint("Received msg with wrong type %d != %d\n",
m.head.m_type, ANSWER);
return -1;
}
*ctr = m.answer.m_ctr;
*state = m.answer.m_state;
*value = m.answer.m_value;
}
return rc;
}
static void print_answer(int ctr, int state, uint64_t value)
{
if (state < 0)
printf(" %s counter: error state %d\n",
counter_str[ctr], state);
else if (state == DISABLED)
printf(" %s counter: disabled\n", counter_str[ctr]);
else
printf(" %s counter: %"PRIu64"\n", counter_str[ctr], value);
}
int eprint(const char *format, ...)
{
char buf[1024];
va_list vargs;
int i, n;
i = snprintf(buf, sizeof(buf), "%s: ", name);
va_start(vargs, format);
n = vsnprintf(buf+i, sizeof(buf)-i, format, vargs);
va_end(vargs);
if (n > 0)
fputs(buf, stderr);
return n;
}
int main(int argc, char *argv[])
{
enum ctr_e ctr = ALL_COUNTER;
enum cmd_e cmd = PRINT;
int i, j, s, state;
uint64_t value;
if (argc > 1) {
int opt, idx = 0;
const struct option long_opts[] = {
{ "help", 0, NULL, 'h' },
{ "version", 0, NULL, 'v' },
{ "enable", 0, NULL, 'e' },
{ "disable", 0, NULL, 'd' },
{ "reset", 0, NULL, 'r' },
{ "print", 0, NULL, 'p' },
{ NULL, 0, NULL, 0 } };
while (1) {
opt = getopt_long(argc, argv,
"hvedrp", long_opts, &idx);
if (opt == -1)
break; /* no more arguments */
switch (opt) {
case 'h':
printf(usage, name);
exit(0);
break;
case 'v':
printf("%s: Linux on System z CPACF Crypto Activity Counters Client\n"
"Version %s\n%s\n",
name, RELEASE_STRING, COPYRIGHT);
exit(0);
break;
case 'e':
cmd = ENABLE;
break;
case 'd':
cmd = DISABLE;
break;
case 'r':
cmd = RESET;
break;
case 'p':
cmd = PRINT;
break;
default:
eprint("Invalid argument, try -h or --help for more information\n");
exit(1);
break;
}
}
/* there may be an optional counter argument */
if (optind > 0 && optind < argc) {
for (i = 0; i <= ALL_COUNTER; i++)
if (strcmp(argv[optind], counter_str[i]) == 0)
break;
if (i > ALL_COUNTER) {
eprint("Unknown counter '%s'\n", argv[optind]);
exit(1);
}
ctr = (enum ctr_e) i;
}
}
/* try to open and connect socket to the cpacfstatsd daemon */
s = open_socket(CLIENT);
if (s < 0) {
eprint("Can't connect to daemon\n");
exit(1);
}
/* send query */
if (send_query(s, cmd, ctr) != 0) {
eprint("Error on sending query message to daemon\n");
close(s);
exit(1);
}
if (ctr == ALL_COUNTER) {
for (i = 0; i < ALL_COUNTER; i++) {
/* receive answer */
if (recv_answer(s, &j, &state, &value) != 0) {
eprint("Error on receiving answer message from daemon\n");
exit(1);
}
if (state < 0) {
eprint("Received bad status code %d from daemon\n",
state);
close(s);
exit(1);
}
print_answer(j, state, value);
}
} else {
/* receive answer */
if (recv_answer(s, &j, &state, &value) != 0) {
eprint("Error on receiving answer message from daemon\n");
close(s);
exit(1);
}
if (state < 0) {
eprint("Received bad status code %d from daemon\n",
state);
close(s);
exit(1);
}
print_answer(j, state, value);
}
/* close connection */
close(s);
return 0;
}

112
cpacfstats/cpacfstats.h Normal file
View File

@@ -0,0 +1,112 @@
/*
* cpacfstats - display and maintain CPACF perf counters
*
* common function prototypes and definitions
*
* Copyright IBM Corp. 2015, 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 CPACFSTATS_H
#define CPACFSTATS_H
#include "lib/zt_common.h"
#define COPYRIGHT "Copyright IBM Corp. 2015, 2017"
int eprint(const char *format, ...);
/*
* Counter names
* ALL_COUNTER must always be the last member of the enum
*/
enum ctr_e {
DES_FUNCTIONS = 0,
AES_FUNCTIONS,
SHA_FUNCTIONS,
PRNG_FUNCTIONS,
ALL_COUNTER
};
enum type_e {
QUERY = 0,
ANSWER
};
enum cmd_e {
PRINT = 0,
ENABLE,
DISABLE,
RESET
};
enum state_e {
DISABLED = 0,
ENABLED
};
/*
* query send from clent to daemon
* Consist of:
* enum counter
* enum command
*/
struct msg_query {
uint32_t m_ctr;
uint32_t m_cmd;
} __packed;
/*
* answer send from daemon to client
* Consist of:
* enum counter
* status code: < 0 error, 0 disabled, > 0 enabled
* counter value
*/
struct msg_answer {
uint32_t m_ctr;
int32_t m_state;
uint64_t m_value;
} __packed;
/* stats_sock.c */
#define SERVER 1
#define CLIENT 2
#define BACKLOG 10
#define SOCKET_FILE "/var/run/cpacfstatsd_socket"
#define PID_FILE "/var/run/cpacfstatsd.pid"
#define CPACFSTATS_GROUP "cpacfstats"
struct msg_header {
uint32_t m_ver;
uint32_t m_type;
} __packed;
struct msg {
struct msg_header head;
union {
struct msg_query query;
struct msg_answer answer;
};
} __packed;
int open_socket(int mode);
int send_msg(int sfd, struct msg *m);
int recv_msg(int sfd, struct msg *m);
/* perf_crypto.c */
int perf_init(void);
void perf_close(void);
int perf_enable_ctr(enum ctr_e ctr);
int perf_disable_ctr(enum ctr_e ctr);
int perf_reset_ctr(enum ctr_e ctr);
int perf_read_ctr(enum ctr_e ctr, uint64_t *value);
#endif

97
cpacfstats/cpacfstatsd.8 Normal file
View File

@@ -0,0 +1,97 @@
.\" cpacfstatsd.8
.\"
.\" Copyright IBM Corp. 2015, 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
.\" groff -man -Tutf8 cpacfstatsd.8
.\" or
.\" nroff -man cpacfstatsd.8
.\" to process this source
.\"
.TH cpacfstatsd "8" "January 2015" "s390-tools"
.
.ds c \fcpacfstatsd\fP
.
.SH NAME
cpacfstatsd \- CPACF statistics collection daemon process
.
.SH SYNOPSIS
.B cpacfstatsd
.RB [ \-h | \-\-help ]
.RB [ \-v | \-\-version ]
.RB [ \-f | \-\-foreground ]
.
.SH DESCRIPTION
The cpacfstatsd controlling daemon enables, disables, resets, and fetches
the mainframe CPACF performance counter registers.
The daemon receives commands from the user application cpacfstats through
the UNIX Domain Socket, processes them and returns the requested
information. For all available commands, see the cpacfstats man page.
Prerequisites
.P
- The running Linux kernel must have the the CONFIG_PERF_EVENTS
config option enabled.
.P
- Libpfm version 4 or higher is needed to successfully run the daemon.
.P
- Your LPAR must be configured to enable the "Counter Facility Security Options".
.P
- The daemon requires root privileges to interact with the performance
ioctls of the kernel.
CPU hotplug is not recognized by the daemon. When adding or removing a CPU,
restart the daemon to ensure correct summing of the per-CPU performance
counters.
The starting daemon first checks for any stale pid file
/var/run/cpacfstatsd.pid. If this file exists, and the process ID in the
file belongs to an active process, an error message is printed to the
console and the program terminates.
The daemon and the client cpacfstats communicate through a Unix Domain
Socket. This socket is created by the daemon at startup with the associated
socket file /run/cpacfstatsd_socket. For security reasons only members of
the group \fIcpacfstats\fR are allowed to communicate with the daemon. A
system administrator should create this group and add all users which are
allowed to run the cpacfstats client to the group.
After startup, the daemon runs in the background and detaches from any
terminal. Errors and warnings are posted to the syslog subsystem. Check the
process list and the system syslog messages for confirmation of successful
startup.
On regular termination the pid file, the communication socket and the
associated file is removed gracefully.
.SH OPTIONS
.TP
\fB\-h\fR or \fB\-\-help\fR
Display help information for the command.
.TP
\fB\-v\fR or \fB\-\-version\fR
Display version and copyright information for the command.
.TP
\fB\-f\fR or \fB\-\-foreground\fR
Run the daemon in foreground mode, thus printing errors to stderr instead
of posting them through syslog. This option might be useful when debugging
daemon startup and initialization failures.
.SH FILES
.nf
/var/run/cpacfstatsd_socket
/var/run/cpacfstatsd.pid
.fi
.SH RETURN VALUE
.IP 0
The daemon was successfully set to run in the background. This does not
imply that the daemon startup was successful, as the main initialization is
done in the re-spawned process. Check the syslog for success or failure.
.IP 1
The daemon could not be set to run in the background.
.SH SEE ALSO
cpacfstats (1)

477
cpacfstats/cpacfstatsd.c Normal file
View File

@@ -0,0 +1,477 @@
/*
* cpacfstats - display and maintain CPACF perf counters
*
* cpacfstatsd daemon implementation
*
* Copyright IBM Corp. 2015, 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 <errno.h>
#include <fcntl.h>
#include <getopt.h>
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#include <signal.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <syslog.h>
#include <unistd.h>
#include "lib/zt_common.h"
#include "cpacfstats.h"
static const char *const name = "cpacfstatsd";
static const char *const usage =
"Usage: %s [OPTIONS]\n"
"\n"
"Daemon to provide access to CPACF perf counters\n"
"Use OPTIONS described below:\n"
"\n"
"\t-h, --help Print this help, then exit\n"
"\t-v, --version Print version information, then exit\n"
"\t-f, --foreground Run in foreground, do not detach\n";
static int daemonized;
static int ctr_state[ALL_COUNTER];
static int recv_query(int s, enum ctr_e *ctr, enum cmd_e *cmd)
{
struct msg m;
int rc;
rc = recv_msg(s, &m);
if (rc == 0) {
if (m.head.m_ver != VERSION) {
eprint("Received msg with wrong version %d != %d\n",
m.head.m_ver, VERSION);
return -1;
}
if (m.head.m_type != QUERY) {
eprint("Received msg with wrong type %d != %d\n",
m.head.m_type, QUERY);
return -1;
}
*ctr = m.query.m_ctr;
*cmd = m.query.m_cmd;
}
return rc;
}
static int send_answer(int s, int ctr, int state, uint64_t value)
{
struct msg m;
memset(&m, 0, sizeof(m));
m.head.m_ver = VERSION;
m.head.m_type = ANSWER;
m.answer.m_ctr = ctr;
m.answer.m_state = state;
m.answer.m_value = value;
return send_msg(s, &m);
}
static int do_enable(int s, enum ctr_e ctr)
{
uint64_t value;
int i, rc = 0;
for (i = 0; i < ALL_COUNTER; i++) {
if (i == (int) ctr || ctr == ALL_COUNTER) {
if (!ctr_state[i]) {
rc = perf_enable_ctr(i);
if (rc != 0) {
send_answer(s, i, rc, 0);
break;
}
ctr_state[i] = 1;
}
rc = perf_read_ctr(i, &value);
if (rc != 0) {
send_answer(s, i, rc, 0);
break;
}
send_answer(s, i, ENABLED, value);
}
}
return rc;
}
static int do_disable(int s, enum ctr_e ctr)
{
int i, rc = 0;
for (i = 0; i < ALL_COUNTER; i++) {
if (i == (int) ctr || ctr == ALL_COUNTER) {
if (ctr_state[i]) {
rc = perf_disable_ctr(i);
if (rc != 0) {
send_answer(s, i, rc, 0);
break;
}
ctr_state[i] = 0;
}
send_answer(s, i, DISABLED, 0);
}
}
return rc;
}
static int do_reset(int s, enum ctr_e ctr)
{
int i, rc = 0;
for (i = 0; i < ALL_COUNTER; i++) {
if (i == (int) ctr || ctr == ALL_COUNTER) {
if (ctr_state[i]) {
rc = perf_reset_ctr(i);
if (rc != 0) {
send_answer(s, i, rc, 0);
break;
}
send_answer(s, i, ENABLED, 0);
} else {
send_answer(s, i, DISABLED, 0);
}
}
}
return rc;
}
static int do_print(int s, enum ctr_e ctr)
{
int i, rc = 0;
uint64_t value;
for (i = 0; i < ALL_COUNTER; i++) {
if (i == (int) ctr || ctr == ALL_COUNTER) {
if (ctr_state[i]) {
rc = perf_read_ctr(i, &value);
if (rc != 0) {
send_answer(s, i, rc, 0);
break;
}
send_answer(s, i, ENABLED, value);
} else {
send_answer(s, i, DISABLED, 0);
}
}
}
return rc;
}
static int become_daemon(void)
{
FILE *f;
int fd;
/* syslog */
openlog("cpacfstatsd", 0, LOG_DAEMON);
/*
* fork and terminate parent
* Reasons:
* - opens new command line prompt
* - the child process is guaranteed not to be the process group leader
* nessecarry for setsid.
*/
switch (fork()) {
case -1: /* error */
eprint("Fork() failed, errno=%d [%s]\n",
errno, strerror(errno));
return -1;
case 0: /* child */
break;
default: /* parent */
_exit(0);
}
if (chdir("/") != 0) {
eprint("Chdir('/') failed, errno=%d [%s]\n",
errno, strerror(errno));
return -1;
}
/* start new session */
if (setsid() == -1) {
eprint("Setsid() failed, errno=%d [%s]\n",
errno, strerror(errno));
return -1;
}
/* clear umask so that socket has right default permission */
umask(0007);
/* make stdin, stdout and stderr use /dev/null */
fd = open("/dev/null", O_RDWR);
if (fd < 0) {
eprint("Could not open /dev/null, errno=%d [%s]\n",
errno, strerror(errno));
return -1;
}
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
close(fd);
daemonized = 1;
/* make pid file, fails if the file exists */
f = fopen(PID_FILE, "w+x");
if (!f) {
eprint("Couldn't create pid file '%s', errno=%d [%s]\n",
PID_FILE, errno, strerror(errno));
return -1;
}
fprintf(f, "%lu", (unsigned long)getpid());
fflush(f);
fclose(f);
chmod(PID_FILE, 0644);
return 0;
}
static void remove_sock(void)
{
remove(SOCKET_FILE);
}
static int check_pidfile(void)
{
unsigned long pid;
FILE *f;
f = fopen(PID_FILE, "r");
if (!f) {
if (errno == ENOENT) {
/* pid file does not exit, pid file check is ok */
return 0;
}
/* unknown errno, pid file check is not ok */
eprint("Unknown error on pid file check '%s', errno=%d [%s]\n",
PID_FILE, errno, strerror(errno));
return -1;
}
/* pid file could be opened, scan pid in there */
if (fscanf(f, "%lu", &pid) != 1) {
/*
* invalid, maybe a leftover from a previous run
* remove and return pid file check ok
*/
fclose(f);
remove(PID_FILE);
return 0;
}
fclose(f);
/* check if this process is still running */
if (kill(pid, 0) != 0) {
/*
* failure, assume this means there is no such pid running
* remove pid file and return pid file check ok
*/
remove(PID_FILE);
return 0;
}
/*
* looks like there is another cpacfstatsd running
* return with pid file check failure
*/
eprint("Looks like there is another cpacfstatsd (pid=%lu) running\n",
pid);
eprint("Please check and maybe remove stale pid file '%s'\n",
PID_FILE);
return -1;
}
static void remove_pidfile(void)
{
remove(PID_FILE);
}
void signalhandler(int sig)
{
if (sig == SIGTERM)
eprint("Caught signal SIGTERM, terminating...\n");
else if (sig == SIGINT)
eprint("Caught signal SIGINT, terminating...\n");
else
eprint("Caught signal %d, terminating...\n", sig);
remove_sock();
perf_close();
remove_pidfile();
exit(0);
}
int eprint(const char *format, ...)
{
char buf[512];
va_list vargs;
int i, n;
i = snprintf(buf, sizeof(buf), "%s: ", name);
va_start(vargs, format);
n = vsnprintf(buf+i, sizeof(buf)-i, format, vargs);
va_end(vargs);
if (n > 0) {
if (daemonized)
syslog(LOG_WARNING, "%s", buf);
else
fputs(buf, stderr);
}
return n;
}
int main(int argc, char *argv[])
{
int rc, sfd, foreground = 0;
struct sigaction act;
if (argc > 1) {
int opt, idx = 0;
const struct option long_opts[] = {
{ "help", 0, NULL, 'h' },
{ "foreground", 0, NULL, 'f' },
{ "version", 0, NULL, 'v' },
{ NULL, 0, NULL, 0 } };
while (1) {
opt = getopt_long(argc, argv,
"hfv", long_opts, &idx);
if (opt == -1)
break; /* no more arguments */
switch (opt) {
case 'h':
printf(usage, name);
exit(0);
case 'f':
foreground = 1;
break;
case 'v':
printf("%s: Linux on System z CPACF Crypto Activity Counters Daemon\n"
"Version %s\n%s\n",
name, RELEASE_STRING, COPYRIGHT);
exit(0);
default:
printf("%s: Invalid argument, try -h or --help for more information\n",
name);
exit(1);
}
}
}
if (check_pidfile() != 0) {
eprint("Stalled pid file or daemon allready running, terminating\n");
exit(1);
}
if (!foreground) {
if (become_daemon() != 0) {
eprint("Couldn't daemonize\n");
exit(1);
}
}
if (perf_init() != 0) {
eprint("Couldn't initialize perf lib\n");
exit(1);
}
atexit(perf_close);
sfd = open_socket(SERVER);
if (sfd < 0) {
eprint("Couldn't initialize server socket\n");
exit(1);
}
atexit(remove_sock);
memset(&act, 0, sizeof(act));
act.sa_handler = signalhandler;
act.sa_flags = 0;
if (sigaction(SIGINT, &act, 0) != 0) {
eprint("Couldn't establish signal handler for SIGINT, errno=%d [%s]\n",
errno, strerror(errno));
exit(1);
}
if (sigaction(SIGTERM, &act, 0) != 0) {
eprint("Couldn't establish signal handler for SIGTERM, errno=%d [%s]\n",
errno, strerror(errno));
exit(1);
}
eprint("Running\n");
while (1) {
enum ctr_e ctr;
enum cmd_e cmd;
int s;
s = accept(sfd, NULL, NULL);
if (s < 0) {
if (errno == EINTR)
continue;
eprint("Accept() failure, errno=%d [%s]\n",
errno, strerror(errno));
exit(1);
}
rc = recv_query(s, &ctr, &cmd);
if (rc != 0) {
eprint("Recv_query() failed, ignoring\n");
goto cleanup;
}
if (cmd == ENABLE)
rc = do_enable(s, ctr);
else if (cmd == DISABLE)
rc = do_disable(s, ctr);
else if (cmd == RESET)
rc = do_reset(s, ctr);
else if (cmd == PRINT)
rc = do_print(s, ctr);
else {
eprint("Received unknown command %d, ignoring\n",
(int) cmd);
goto cleanup;
}
cleanup:
close(s);
}
return 0;
}

258
cpacfstats/perf_crypto.c Normal file
View File

@@ -0,0 +1,258 @@
/*
* cpacfstats - display and maintain CPACF perf counters
*
* low level perf functions
*
* Copyright IBM Corp. 2015, 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 <errno.h>
#include <getopt.h>
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#include <perfmon/perf_event.h>
#include <perfmon/pfmlib.h>
#include <perfmon/pfmlib_perf_event.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "cpacfstats.h"
/* correlation between counter and perf counter string */
static const struct {
char pfm_name[80];
enum ctr_e ctr;
} pmf_counter_name[ALL_COUNTER] = {
{"cpum_cf::DEA_FUNCTIONS", DES_FUNCTIONS},
{"cpum_cf::AES_FUNCTIONS", AES_FUNCTIONS},
{"cpum_cf::SHA_FUNCTIONS", SHA_FUNCTIONS},
{"cpum_cf::PRNG_FUNCTIONS", PRNG_FUNCTIONS}
};
/*
* We need one filedescriptor per CPU per counter.
* So perf_init builds this:
*
* ctr_fds - is an array of pointers to file descriptor arrays.
* Each file descriptor array has space for number of logical CPUs + 1
* filedescriptors (int values). The last element of each file descriptor
* array is always 0, assuming there will never appear a filedescriptor
* with value 0:
*
* ctr_fds:
* ctr_fds[0] -> [file descriptor 0] [fd1] ... [fd cpus-1][0]
* ctr_fds[1] -> [file descriptor 0] [fd1] ... [fd cpus-1][0]
* ...
* ctr_fds[ALL_COUNTER-1] -> [file descriptor 0] [fd1] ... [fd cpus-1][0]
*/
static int *ctr_fds[ALL_COUNTER];
int perf_init(void)
{
int i, cpus, ctr, cpu, ec, *fds;
memset(ctr_fds, 0, sizeof(ctr_fds));
/* initialize performance monitoring library */
ec = pfm_initialize();
if (ec != PFM_SUCCESS) {
eprint("Pfm_initialize() returned with failure (%d:%s)\n",
ec, pfm_strerror(ec));
return -1;
}
/* get number of logical processors */
cpus = sysconf(_SC_NPROCESSORS_ONLN);
/* for each counter */
for (ctr = 0; ctr < ALL_COUNTER; ctr++) {
/*
* allocate an array of ints to store for each CPU
* one filedescriptor + a terminating 0
*/
fds = (int *) calloc(sizeof(int), cpus+1);
if (!fds) {
eprint("Malloc() of %d byte failed, errno=%d [%s]\n",
(int)(sizeof(int) * (cpus+1)),
errno, strerror(errno));
return -1;
}
ctr_fds[ctr] = fds;
for (cpu = 0; cpu < cpus; cpu++) {
pfm_perf_encode_arg_t pfm_arg;
struct perf_event_attr pfm_event;
int fd;
memset(&pfm_arg, 0, sizeof(pfm_arg));
memset(&pfm_event, 0, sizeof(pfm_event));
pfm_arg.attr = &pfm_event;
pfm_arg.size = sizeof(pfm_arg);
/* search for the counter's corresponding pfm name */
for (i = ALL_COUNTER-1; i >= 0; i--)
if ((int) pmf_counter_name[i].ctr == ctr)
break;
if (i < 0) {
eprint("Pfm ctr name not found for counter %d, please adjust pmf_counter_name[] in %s\n",
ctr, __FILE__);
return -1;
}
/* encode the counters perf event into pfm_arg.attr */
ec = pfm_get_os_event_encoding(
pmf_counter_name[i].pfm_name,
PFM_PLM0,
PFM_OS_PERF_EVENT,
&pfm_arg);
if (ec != PFM_SUCCESS) {
eprint("Pfm_initialize() for %s failed (%d:%s)\n",
pmf_counter_name[i].pfm_name,
ec, pfm_strerror(ec));
return -1;
}
/* fetch file descriptor for this perf event
* the counter event should start disabled
*/
pfm_event.disabled = 1;
fd = perf_event_open(
&pfm_event,
-1, /* pid -1 means all processes */
cpu,
-1, /* group filedescriptor */
0); /* flags */
if (fd < 0) {
eprint("Perf_event_open() failed with errno=%d [%s]\n",
errno, strerror(errno));
return -1;
}
fds[cpu] = fd;
}
}
return 0;
}
void perf_close(void)
{
int ctr, *fds;
for (ctr = 0; ctr < ALL_COUNTER; ctr++) {
for (fds = ctr_fds[ctr]; fds && *fds; fds++) {
close(*fds);
*fds = 0;
}
free(ctr_fds[ctr]);
ctr_fds[ctr] = NULL;
}
}
int perf_enable_ctr(enum ctr_e ctr)
{
int *fds, ec, rc = 0;
if (ctr == ALL_COUNTER) {
for (ctr = 0; ctr < ALL_COUNTER; ctr++) {
rc = perf_enable_ctr(ctr);
if (rc != 0)
return rc;
}
} else {
for (fds = ctr_fds[ctr]; fds && *fds; fds++) {
ec = ioctl(*fds, PERF_EVENT_IOC_ENABLE, 0);
if (ec < 0) {
eprint("Ioctl(PERF_EVENT_IOC_ENABLE) failed with errno=%d [%s]\n",
errno, strerror(errno));
rc = -1;
}
}
}
return rc;
}
int perf_disable_ctr(enum ctr_e ctr)
{
int *fds, ec, rc = 0;
if (ctr == ALL_COUNTER) {
for (ctr = 0; ctr < ALL_COUNTER; ctr++) {
rc = perf_disable_ctr(ctr);
if (rc != 0)
return rc;
}
} else {
for (fds = ctr_fds[ctr]; fds && *fds; fds++) {
ec = ioctl(*fds, PERF_EVENT_IOC_DISABLE, 0);
if (ec < 0) {
eprint("Ioctl(PERF_EVENT_IOC_DISABLE) failed with errno=%d [%s]\n",
errno, strerror(errno));
rc = -1;
}
}
}
return rc;
}
int perf_reset_ctr(enum ctr_e ctr)
{
int *fds, ec, rc = 0;
if (ctr == ALL_COUNTER) {
for (ctr = 0; ctr < ALL_COUNTER; ctr++) {
rc = perf_reset_ctr(ctr);
if (rc != 0)
return rc;
}
} else {
for (fds = ctr_fds[ctr]; fds && *fds; fds++) {
ec = ioctl(*fds, PERF_EVENT_IOC_RESET, 0);
if (ec < 0) {
eprint("Ioctl(PERF_EVENT_IOC_RESET) failed with errno=%d [%s]\n",
errno, strerror(errno));
rc = -1;
}
}
}
return rc;
}
int perf_read_ctr(enum ctr_e ctr, uint64_t *value)
{
int *fds, ec, rc = -1;
uint64_t val;
if (!value)
return -1;
*value = 0;
for (fds = ctr_fds[ctr]; fds && *fds; fds++) {
ec = read(*fds, &val, sizeof(val));
if (ec != sizeof(val)) {
eprint("Read() on perf file descriptor failed with errno=%d [%s]\n",
errno, strerror(errno));
} else {
*value += val;
rc = 0;
}
}
return rc;
}

211
cpacfstats/stats_sock.c Normal file
View File

@@ -0,0 +1,211 @@
/*
* cpacfstats - display and maintain CPACF perf counters
*
* basic socket and receive/send functions
*
* Copyright IBM Corp. 2015, 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 <arpa/inet.h>
#include <endian.h>
#include <errno.h>
#include <grp.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>
#include "cpacfstats.h"
int open_socket(int mode)
{
struct sockaddr_un sock_addr;
struct group *grp;
mode_t m;
int s;
/* group handling */
grp = getgrnam(CPACFSTATS_GROUP);
if (!grp) {
eprint("Getgrnam() failed, group '%s' may not exist on this system ?\n",
CPACFSTATS_GROUP);
return -1;
}
/* the client checks for the unix domain socket file */
if (mode != SERVER) {
if (access(SOCKET_FILE, F_OK) != 0) {
eprint("Can't access domain socket file '%s', errno=%d [%s]\n",
SOCKET_FILE, errno, strerror(errno));
if (errno == ENOENT)
eprint("Maybe cpacfstatsd daemon is not running ???\n");
return -1;
}
}
/* create socket */
s = socket(AF_UNIX, SOCK_STREAM, 0);
if (s == -1) {
eprint("Socket(AF_UNIX,SOCK_STREAM) failed, errno=%d [%s]\n",
errno, strerror(errno));
return -1;
}
if (mode == SERVER)
remove(SOCKET_FILE);
memset(&sock_addr, 0, sizeof(sock_addr));
sock_addr.sun_family = AF_UNIX;
strncpy(sock_addr.sun_path, SOCKET_FILE, sizeof(sock_addr.sun_path));
sock_addr.sun_path[sizeof(sock_addr.sun_path)-1] = '\0';
if (mode == SERVER) {
if (bind(s, (struct sockaddr *) &sock_addr,
sizeof(struct sockaddr_un)) < 0) {
eprint("Bind('%s') failed, errno=%d [%s]\n",
SOCKET_FILE, errno, strerror(errno));
return -1;
}
/* change group ownership of the socket file */
if (chown(SOCKET_FILE, 0, grp->gr_gid)) {
eprint("Chown('%s',...) failed, errno=%d [%s]\n",
SOCKET_FILE, errno, strerror(errno));
return -1;
}
/* adapt permissions */
m = S_IRUSR|S_IWUSR|S_IXUSR | S_IRGRP|S_IWGRP|S_IXGRP;
if (chmod(SOCKET_FILE, m)) {
eprint("Chmod('%s',...) failed, errno=%d [%s]\n",
SOCKET_FILE, errno, strerror(errno));
return -1;
}
/* now put the socket into listen state */
if (listen(s, BACKLOG) < 0) {
eprint("Listen() failed, errno=%d [%s]\n",
errno, strerror(errno));
return -1;
}
} else {
if (connect(s, (struct sockaddr *) &sock_addr,
sizeof(sock_addr)) < 0) {
eprint("Connect() failed, errno=%d [%s]\n",
errno, strerror(errno));
return -1;
}
}
return s;
}
static int __write(int fd, const void *buf, int buflen)
{
const unsigned char *p = buf;
int n, i = 0;
while (i < buflen) {
n = write(fd, p+i, buflen-i);
if (n < 0) {
if (errno == EINTR)
continue;
else
return n;
}
i += n;
}
return i;
}
static int __read(int fd, void *buf, int buflen)
{
unsigned char *p = buf;
int n, i = 0;
while (i < buflen) {
n = read(fd, p+i, buflen-i);
if (n < 0) {
if (errno == EINTR)
continue;
else
return n;
} else if (n == 0) {
return i;
} else {
i += n;
}
}
return i;
}
int send_msg(int sfd, struct msg *m)
{
int n, len;
len = sizeof(m->head);
switch (m->head.m_type) {
case QUERY:
len += sizeof(m->query);
break;
case ANSWER:
len += sizeof(m->answer);
break;
default:
eprint("Unknown type %d\n", m->head.m_type);
return -1;
}
n = __write(sfd, m, len);
if (n != len) {
eprint("Write() error: write()=%d expected %d, errno=%d [%s]\n",
n, len, errno, strerror(errno));
return -1;
}
return 0;
}
int recv_msg(int sfd, struct msg *m)
{
int n, len;
len = sizeof(m->head);
n = __read(sfd, m, len);
if (n != len) {
eprint("Recv() error: read()=%d expected %d, errno=%d [%s]\n",
n, len, errno, strerror(errno));
return -1;
}
switch (m->head.m_type) {
case QUERY:
len = sizeof(m->query);
break;
case ANSWER:
len = sizeof(m->answer);
break;
default:
eprint("Unknown type %d\n", m->head.m_type);
return -1;
}
n = __read(sfd, ((char *)m) + sizeof(m->head), len);
if (n != len) {
eprint("Recv() error: recv()=%d expected %d, errno=%d [%s]\n",
n, len, errno, strerror(errno));
return -1;
}
return 0;
}

64
cpumf/Makefile Normal file
View File

@@ -0,0 +1,64 @@
#!/usr/bin/make -f
include ../common.mak
CPUMF_DATADIR = $(TOOLS_DATADIR)/cpumf
DATA_FILES = cpum-cf-hw-counter.map cpum-cf-generic.ctr \
cpum-cf-extended-z10.ctr cpum-cf-extended-z196.ctr \
cpum-cf-extended-zEC12.ctr cpum-sf-modes.ctr \
cpum-cf-extended-z13.ctr
LIB_FILES = bin/cpumf_helper
USRBIN_SCRIPTS = bin/lscpumf
USRSBIN_SCRIPTS = bin/chcpumf
MAN_FILES = lscpumf.1 chcpumf.8
all:
scripts: $(USRBIN_SCRIPTS) $(USRSBIN_SCRIPTS) $(LIB_FILES)
chmod +x $(USRBIN_SCRIPTS) $(USRSBIN_SCRIPTS) $(LIB_FILES)
check:
install: scripts install-man
for prg in $(USRBIN_SCRIPTS); do \
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 $$prg $(DESTDIR)$(USRBINDIR) ; \
done
for prg in $(USRSBIN_SCRIPTS); do \
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 $$prg $(DESTDIR)$(USRSBINDIR) ; \
done
test -d $(DESTDIR)$(CPUMF_DATADIR) || mkdir -p $(DESTDIR)$(CPUMF_DATADIR)
for lib in $(LIB_FILES); do \
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 $$lib $(DESTDIR)$(TOOLS_LIBDIR) ; \
done
for data in $(DATA_FILES); do \
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 data/$$data $(DESTDIR)$(CPUMF_DATADIR) ; \
done
install-man:
for man in $(MAN_FILES); do \
msection=`echo $$man |sed 's/.*\.\([1-9]\)$$/man\1/'` ; \
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 -D man/$$man $(DESTDIR)$(MANDIR)/$$msection/$$man ; \
done
man2pdf:
for man in $(MAN_FILES); do \
man -t man/$$man |ps2pdf -sPAPERSIZE=a4 - man/$${man}.pdf ; \
done
man2text:
for man in $(MAN_FILES); do \
MANWIDTH=80 LANG=C man man/$$man |col -b |expand > man/$${man}.txt ; \
done
clean:
rm -f $(LIB_FILES) $(USRBIN_SCRIPTS) $(USRSBIN_SCRIPTS)
%: %.in
real_libdir=$(TOOLS_LIBDIR); \
real_cpumfdatadir=$(CPUMF_DATADIR); \
$(SED) -e "s#@lib_path@#$$real_libdir#g" \
-e "s#@cpumfdata_path@#$$real_cpumfdatadir#g" \
-e 's#@S390_TOOLS_RELEASE@#$(S390_TOOLS_RELEASE)#g' \
< $< > $@
.PHONY: all scripts install install-man man2pdf man2text clean

182
cpumf/bin/chcpumf.in Normal file
View File

@@ -0,0 +1,182 @@
#!/usr/bin/perl -W
#
# chcpumf - Control CPU-measurement facilities
#
# Copyright IBM Corp. 2014, 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 File::Basename qw/fileparse/;
use Data::Dumper;
use Getopt::Long qw/:config no_ignore_case/;
# Global constants
my $CPUMF_HELPER = '@lib_path@/cpumf_helper';
my $CPUM_SFB_SIZE = '/sys/module/kernel/parameters/cpum_sfb_size';
# Prototypes
sub main();
sub show_help();
sub show_version();
sub do_set_sfb_size($);
sub cpumf_set_sfb_size($);
sub invoke_cpumf_helper($);
sub main()
{
my $config = {
# Internal data
cpumf => {}, # CPU-MF information hash
};
unless (GetOptions(
# General options for help, version, verbose,...
"h|help" => \&show_help,
"v|version" => \&show_version,
"V|verbose+" => \$config->{verbose},
# Change specific options
"m|min=i" => \$config->{min},
"x|max=i" => \$config->{max},
)) {
print STDERR "One or more options are not valid\n";
print STDERR "Try '" . fileparse($0) .
" --help' for more information\n";
exit 1;
}
# Collect CPU-MF information
$config->{cpumf} = invoke_cpumf_helper("-i");
die "Failed to collect CPU-MF information: $!\n" unless $config->{cpumf};
# Process parameters
my $exitval = 5;
if (defined($config->{min}) || defined($config->{max})) {
$exitval = do_set_sfb_size($config);
} else {
print STDERR "You must specify a valid option\n";
exit 1;
}
exit($exitval);
}
sub show_help()
{
my $prog = fileparse($0);
print <<"EoHelp";
Usage: chcpumf -h|-v
chcpumf -m <num_sdb>
chcpumf -x <num_sdb>
Options:
-m <num_sdb> Specifies the initial size of the sampling buffer.
A sample-data-block (SDB) consumes about 4 kilobytes.
-x <num_sdb> Specifies the maximum size of the sampling buffer.
A sample-data-block (SDB) consumes about 4 kilobytes.
-h Displays help information, then exits.
-v Displays version information, then exits.
For more help information, issue 'man $prog'.
EoHelp
exit 0;
}
sub show_version()
{
print <<'EoVersion';
CPU-measurement facility utilities, version @S390_TOOLS_RELEASE@
Copyright IBM Corp. 2014, 2017
EoVersion
exit 0;
}
sub cpumf_set_sfb_size($)
{
my @size = @{shift()};
my $val = join ',', @size[0,1];
my ($SFBSIZE, $rc);
return undef unless open($SFBSIZE, '>', $CPUM_SFB_SIZE);
# Check the return code of print and close to detect error conditions
# reported by the device driver. Because perl might buffer data, print
# might be successful, but close might then report the error condition.
# So check the return code of both functions and always close the file
# handle.
$rc = print { $SFBSIZE } "$val\n";
unless ($rc) {
close($SFBSIZE);
return $rc;
}
return close($SFBSIZE);
}
sub do_set_sfb_size($)
{
my $c = shift();
my $size;
# Check if sampling facility is available
unless (exists $c->{cpumf}->{sf}) {
print STDERR "No CPU-measurement sampling facility detected\n";
return 2;
}
# Check if perf support is available
unless (exists $c->{cpumf}->{sf}->{perf}) {
print STDERR "No perf support for the CPU-measurement" .
" sampling facility availalble\n";
return 2;
}
# Optionally, change the sampling buffer sizes
if (defined($c->{min}) || defined($c->{max})) {
$size = invoke_cpumf_helper("--sfb-size");
# Use current size value for zero min/max specifications
$size->[0] = $c->{min} if defined($c->{min});
$size->[1] = $c->{max} if defined($c->{max});
# Validate new settings
if ($size->[0] < 1 || $size->[1] < 1) {
die "The specified number(s) are not valid\n";
}
if ($size->[0] >= $size->[1]) {
die "The specified maximum must be greater " .
"than the minimum\n";
}
# Set new sampling buffer sizes
unless (cpumf_set_sfb_size($size)) {
die "Failed to change sampling buffer size: $!\n";
}
}
# Finally, show sampling buffer sizes
if ($c->{verbose}) {
$size = invoke_cpumf_helper("--sfb-size");
print "Sampling buffer sizes:\n";
printf " Minimum: %6u sample-data-blocks\n", $size->[0];
printf " Maximum: %6u sample-data-blocks\n", $size->[1];
}
return 0;
}
sub invoke_cpumf_helper($)
{
my $parms = shift();
my $result;
# Call helper module
my $output = qx"$CPUMF_HELPER $parms";
die "Failed to run helper module for '$parms'\n" if $? >> 8;
$result = eval "$output";
die "Failed to parse helper module data\n" if $@;
return $result;
}
&main();
__DATA__
__END__

483
cpumf/bin/cpumf_helper.in Normal file
View File

@@ -0,0 +1,483 @@
#!/usr/bin/perl -W
#
# cpumf_helper - Helper module for managing CPU-measurement facilities (CPU-MF)
#
# Copyright IBM Corp. 2014, 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 Carp qw/croak/;
use Data::Dumper;
use Getopt::Long qw/:config no_ignore_case/;
use Pod::Usage;
# Global constants
my $SERVICE_LEVELS = '/proc/service_levels';
my $CPUMF_DATA_DIR = '@cpumfdata_path@';
my $CPUM_SFB_SIZE = '/sys/module/kernel/parameters/cpum_sfb_size';
my $CPUM_SF_DBF='/sys/kernel/debug/s390dbf/cpum_sf';
# Counter set bits (according to QUERY COUNTER INFORMATION)
my $BASIC_SET = 0x0002;
my $PROBLEM_STATE_SET = 0x0004;
my $CRYPTO_SET = 0x0008;
my $EXTENTED_SET = 0x0001;
my $MT_DIAG_SET = 0x0020;
my $COPROC_GRP_SET = 0x8000;
# Public prototypes
sub cpumf_collect_data();
sub cpumf_get_sfb_size();
sub cpumf_set_sfb_size($);
sub cpumf_parse_ctrdef($;$);
sub cpumf_load_ctrdef($;$);
sub cpumf_get_counter_set($);
sub cpumf_counter_set_names();
sub cpumf_counter_set_ids();
sub cpumf_hardware_counter_map();
# Internal prototypes
sub cpumf_parse_cf($$);
sub cpumf_parse_sf($$);
sub cpumf_get_sfb_size()
{
my $val = "0,0";
my $SFBSIZE;
return $val unless open($SFBSIZE, '<', $CPUM_SFB_SIZE);
$val = <$SFBSIZE>;
chomp($val);
close($SFBSIZE);
return [split /,/, $val];
}
sub cpumf_set_sfb_size($)
{
my @size = @{shift()};
my $val = join ',', @size[0,1];
my ($SFBSIZE, $rc);
return undef unless open($SFBSIZE, '>', $CPUM_SFB_SIZE);
# Check the return code of print and close to detect error conditions
# reported by the device driver. Because perl might buffer data, print
# might be successful, but close might then report the error condition.
# So check the return code of both functions and always close the file
# handle.
$rc = print { $SFBSIZE } "$val\n";
unless ($rc) {
close($SFBSIZE);
return $rc;
}
return close($SFBSIZE);
}
sub cpumf_parse_cf($$)
{
my ($ent, $data) = @_;
if ($ent =~ /version=([0-9.]+) authorization=([[:xdigit:]]+)/) {
$data->{cf} = { version => $1, auth => hex($2) };
}
}
sub cpumf_parse_sf($$)
{
my ($ent, $data) = @_;
# Parse common sampling facility entry
if ($ent =~ /min_rate=(\d+) max_rate=(\d+) cpu_speed=(\d+)/) {
$data->{sf} = {
min_sampl_interval => $1,
max_sampl_interval => $2,
cpu_speed => $3,
# This contains a list of authorized sampling modes, for
# example, basic
modes => {},
};
}
# Parse sampling facility mode entries
if ($ent =~ /mode=(\w+) sample_size=(\d+)/) {
$data->{sf}->{modes}->{$1}->{sample_size} = $2;
}
}
sub cpumf_collect_data()
{
my $SL;
# Collect CPU-MF information from /proc/service_levels
return undef unless open($SL, '<', $SERVICE_LEVELS);
my @sl = <$SL>;
chomp(@sl);
close($SL);
# Process CPU-MF information and build data hash
my $data = {};
foreach my $ent (@sl) {
$ent =~ s/^CPU-MF: // or next;
cpumf_parse_cf($ent, $data) if $ent =~ s/Counter facility: //;
cpumf_parse_sf($ent, $data) if $ent =~ s/Sampling facility: //;
}
# Collect perf support for available facilities
if (-e '/sys/bus/event_source/devices/cpum_cf') {
$data->{cf}->{perf} = "cpum_cf" if exists $data->{cf};
}
if (-e '/sys/bus/event_source/devices/cpum_sf') {
$data->{sf}->{perf} = "cpum_sf" if exists $data->{sf};
}
return $data;
}
# Parse the specified counter definition file and returns a hash containing
# the parsed counter definition. The optional argument specifies a hash
# reference to which the new definitions are added. This reference is returned.
sub cpumf_parse_ctrdef($;$)
{
my $ctrdef = shift();
my $h = @_ ? shift() : {};
my $CTRDEF;
return undef unless open($CTRDEF, '<', "$CPUMF_DATA_DIR/$ctrdef");
my ($ctr, $name);
while (my $line = <$CTRDEF>) {
next if $line =~ /^#/;
chomp($line);
# Parse start of counter definition entry
if ($line =~ m/^Counter:\s*(0x[[:xdigit:]]+|\d+)
\s+Name:\s*([[:alnum:]_]+)$/x) {
($ctr, $name) = ($1, $2);
$ctr = hex($ctr) if $ctr =~ /^0x/;
unless (length($ctr)) {
print STDERR "Found invalid entry in counter " .
"definition: line $.\n";
}
$h->{$ctr} = { name => $name || "" };
}
# At this point, a counter must be defined
next unless defined($ctr);
# Parse short description (optional)
if ($line =~ m/^Short-Description:\s*(\S.*)?$/) {
$h->{ctr}->{shortdesc} = $1 || "";
# Parse start of counter description
} elsif ($line =~ m/^Description:\s*(\S.*)?$/) {
$h->{$ctr}->{desc} = $1 || "";
# Parse end of counter description
} elsif ($line =~ m/^\.$/) {
# Complete the counter definition
$h->{$ctr}->{set} = cpumf_get_counter_set($ctr);
# Trim whitespaces
$h->{$ctr}->{shortdesc} =~ s/^\s+|\s+$//g if $h->{$ctr}->{shortdesc};
$h->{$ctr}->{desc} =~ s/^\s+|\s+$//g if $h->{$ctr}->{desc};
# Finally, reset counter for next entry
$ctr = undef;
# Line is part of counter description (if $ctr_num is set)
} else {
$h->{$ctr}->{desc} .= " $line";
}
}
close($CTRDEF);
return $h;
}
# IBM System z hardware with CPU-M counter facility support
my $system_z_hwtype_map = {
# Machine type Description
'' => 'Unknown hardware model',
2097 => 'IBM System z10 EC',
2098 => 'IBM System z10 BC',
2817 => 'IBM zEnterprise 196',
2818 => 'IBM zEnterprise 114',
2827 => 'IBM zEnterprise EC12',
2828 => 'IBM zEnterprise BC12',
2964 => 'IBM z13',
2965 => 'IBM z13s',
};
sub get_hardware_type()
{
my $type = "";
my $SYSINFO;
return undef unless open($SYSINFO, '<', '/proc/sysinfo');
while (my $line = <$SYSINFO>) {
if ($line =~ m/^Type:\s*(\d+)\s*$/) {
$type = $1;
last;
}
}
close($SYSINFO);
return $type;
}
sub cpumf_load_ctrdef($;$)
{
my $hw_type = shift();
my $authorized = @_ ? shift() : 0xffff; # Counter Set authorization
my $ctrmap = cpumf_hardware_counter_map();
return unless $ctrmap;
my $h = {};
# Load generic counter sets
cpumf_parse_ctrdef($ctrmap->{0}, $h) or
croak "Failed to read generic counter definition: $!\n";
# Load hardware model specific counter set(s)
if ($hw_type && $ctrmap->{$hw_type}) {
# Hardware-model specific counter sets are:
# - Extended Counter Set
# - MT-diagnostic Counter Set
cpumf_parse_ctrdef($ctrmap->{$hw_type}, $h) or
croak "Failed to read hardware-model counter definition: $!\n";
}
# Remove counter sets that miss authorizations
my @no_auth_list = ();
foreach my $ctr (sort keys %$h) {
push @no_auth_list, $ctr unless $h->{$ctr}->{set} & $authorized;
}
delete $h->{$_} foreach (@no_auth_list);
return $h;
}
sub cpumf_get_counter_set($)
{
my $ctr = shift();
return $BASIC_SET if $ctr < 32;
return $PROBLEM_STATE_SET if $ctr < 64;
return $CRYPTO_SET if $ctr < 128;
#
# The extended counter set ranges from
# 128 to
# 159 for csvn == 1
# 175 for csvn == 2
# 255 for csvn > 2
# Tolerate any future counters up to
# the MT-diagnostic counter set.
return $EXTENTED_SET if $ctr < 448;
#
# The MT-diagnostic counter set ranges from
# 448 to
# 495 for cvsn > 3
return $MT_DIAG_SET if $ctr <= 495;
return 0;
}
sub cpumf_counter_set_names()
{
return {
# Identifier Name
$BASIC_SET => 'Basic Counter Set',
$PROBLEM_STATE_SET => 'Problem-State Counter Set',
$CRYPTO_SET => 'Crypto-Activity Counter Set',
$EXTENTED_SET => 'Extended Counter Set',
$MT_DIAG_SET => 'MT-diagnostic Counter Set',
$COPROC_GRP_SET => 'Coprocessor Group Counter Set',
};
}
sub cpumf_counter_set_ids()
{
return [$BASIC_SET, $PROBLEM_STATE_SET, $CRYPTO_SET, $EXTENTED_SET,
$MT_DIAG_SET, $COPROC_GRP_SET];
}
sub cpumf_hardware_counter_map()
{
my $map = do "$CPUMF_DATA_DIR/cpum-cf-hw-counter.map";
croak "Failed to parse mapfile: $@" if $@;
croak "Failed to read mapfile: $!" unless defined $map;
return $map;
}
sub cpumf_helper_main()
{
# Configuration settings and options
my $conf = {
};
# Parse command line option
GetOptions(
"i|info" => \$conf->{opt_info},
"c|counter=i" => \$conf->{opt_ctr},
"ctr-def=i" => \$conf->{opt_ctrdef},
"hardware-type" => \$conf->{opt_hwtype},
"ctr-set-names" => \$conf->{opt_ctrset_names},
"ctr-set-ids" => \$conf->{opt_ctrset_ids},
"sfb-size" => \$conf->{opt_sfb_size},
"ctr-sf" => \$conf->{opt_sf_ctr},
) or pod2usage(-message =>"One or more options are not valid",
-exitval => 1);
# Setting up Data::Dumper to create parseable Perl output
local $Data::Dumper::Purity = 1;
local $Data::Dumper::Sortkeys = 1;
local $Data::Dumper::Terse = 1;
###print STDERR "CONF: " . Dumper($conf) . "\n";
# Process command line options
my $exitval = 0;
my $result;
if (defined($conf->{opt_info})) {
$result = cpumf_collect_data();
# Display System z hardware type
} elsif (defined($conf->{opt_hwtype})) {
my $type = get_hardware_type();
$result = [$type, $system_z_hwtype_map->{$type} || ""];
# Display counters for current System z hardware
} elsif (defined($conf->{opt_ctr})) {
my $type = get_hardware_type();
$type = 0 unless $type;
$result = cpumf_load_ctrdef($type, $conf->{opt_ctr});
# Display counters for a particular System z hardware type
} elsif (defined($conf->{opt_ctrdef})) {
my $m = cpumf_hardware_counter_map();
if (exists $m->{$conf->{opt_ctrdef}}) {
$result = cpumf_parse_ctrdef($m->{$conf->{opt_ctrdef}});
} else {
printf STDERR "Invalid counter definition\n";
$exitval = 2;
}
# Display the size of the sampling facility buffer (sfb)
} elsif (defined($conf->{opt_sfb_size})) {
$result = cpumf_get_sfb_size();
# Display counter definitions for the sampling facility support (perf)
} elsif (defined($conf->{opt_sf_ctr})) {
$result = cpumf_parse_ctrdef('cpum-sf-modes.ctr');
# Display mapping of counter set IDs to counter set names
} elsif (defined($conf->{opt_ctrset_names})) {
$result = cpumf_counter_set_names();
# Display counter set IDs
} elsif (defined($conf->{opt_ctrset_ids})) {
$result = cpumf_counter_set_ids();
} else {
pod2usage(-message => "No option specified",
-exitval => 1);
}
# Display result
print Dumper($result) if $result;
exit $exitval;
}
&cpumf_helper_main();
__DATA__
__END__
=head1 NAME
B<cpumf_helper> - Helper module for managing CPU-Measurement Facilities (CPU-MF)
=head1 SYNOPSIS
=head1 DESCRIPTION
The B<cpumf_helper> program is not intended for ordinary use.
=head1 OPTIONS
=over 8
=item B<-i>, B<--info>
Displays detailed information about installed and available CPU-measurement
facilities and the related Linux support.
=item B<-c>, B<--counter> I<authorization_value>
Displays counter information for the current System z hardware. The
authorization value provides information about the authorized counter sets. The
value must be specified in decimal format.
To display all supported counters, specify C<65535> (FFFF hex).
To display supported counters for a particular System z hardware, use the
B<--ctr-def> option and specify the System z hardware type.
=item B<--hardware-type>
Displays the System z hardware type.
=item B<--ctr-def> I<hardware_type>
Displays detailed information about a particular counter set for the specified
System z hardware type, I<hardware_type>. If you specify zero for
I<hardware_type>, type-independent counter sets are displayed.
=item B<--ctr-set-names>
Displays the mapping of counter set IDs to counter set names. The counter set
IDs are numbers that are used in counter definitions.
=item B<--ctr-set-ids>
Displays the counter set IDs. The counter set IDs are numbers that match the
authorization bits (see QUERY COUNTER INFORMATION). The output format is a
list. To get the ID for a particular counter set, use an index number as
follows:
=over 16
=item 0: Basic Counter Set
=item 1: Problem-State Counter Set
=item 2: Crypto-Activity Counter Set
=item 3: Extended Counter Set
=item 4: MT-diagnostic Counter Set
=item 5: Coprocessor Group Counter Set
=back
=item B<--ctr-sf>
Displays the counter definitions for the sampling facility support. These
counter definitions are specific to Linux perf infrastructure.
=item B<--sfb-size>
Displays the size of the sampling facility buffer (SFB). The minimum and
maximum numbers are measured in units of sample-data-blocks. A
sample-data-block uses about 4 kilobytes.
=back
=head1 FILES
=head1 SEE ALSO
L<lscpumf(1)>, L<chcpumf(1)>
=cut

387
cpumf/bin/lscpumf.in Normal file
View File

@@ -0,0 +1,387 @@
#!/usr/bin/perl -W
#
# lscpumf - Display information about CPU-measurement facilities
#
# Copyright IBM Corp. 2014, 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 Data::Dumper;
use File::Basename qw/fileparse/;
use Getopt::Long qw/:config no_ignore_case/;
# Global constants
my $CPUMF_HELPER = '@lib_path@/cpumf_helper';
# Prototypes
sub main();
sub show_help();
sub show_version();
sub do_show_info($);
sub do_show_cf($);
sub do_show_sf($);
sub do_show_ctr($;$);
sub do_show_sf_events($);
sub invoke_cpumf_helper($);
sub main()
{
my $config = {
# Internal data
cpumf => {}, # CPU-MF information hash
};
unless (GetOptions(
# General options for help, version,...
"h|help" => \&show_help,
"v|version" => \&show_version,
# Display options
"i|info" => \$config->{opt_info},
"c|list-counters" => \$config->{opt_ctr},
"C|list-all-counters" => \$config->{opt_ctr_all},
"s|list-sampling-events" => \$config->{opt_ctr_sf},
)) {
print STDERR "One or more options are not valid\n";
print STDERR "Try '" . fileparse($0) .
" --help' for more information\n";
exit 1;
}
# Collect CPU-MF information
$config->{cpumf} = invoke_cpumf_helper("-i");
die "Failed to collect CPU-MF information: $!\n" unless $config->{cpumf};
# Process parameters
my $exitval = 5;
if (defined($config->{opt_info})) {
do_show_cf($config);
do_show_sf($config);
$exitval = 0;
} elsif (defined($config->{opt_ctr})) {
$exitval = do_show_ctr($config);
} elsif (defined($config->{opt_ctr_all})) {
$exitval = do_show_ctr($config, "all")
} elsif (defined($config->{opt_ctr_sf})) {
$exitval = do_show_sf_events($config);
} else {
$exitval = do_show_info($config);
}
exit($exitval);
}
sub show_help()
{
my $prog = fileparse($0);
print <<"EoHelp";
Usage: lscpumf -h|-v
lscpumf [-i]
lscpumf -c|-C
Options:
-i Displays detailed information.
-c Lists counters for which the LPAR is authorized.
-C Lists counters regardless of LPAR authorization.
-s Lists perf raw events that activate the sampling facility.
-h Displays help information, then exits.
-v Displays version information, then exits.
For more help information, issue 'man $prog'.
EoHelp
exit 0;
}
sub show_version()
{
print <<'EoVersion';
CPU-measurement facility utilities, version @S390_TOOLS_RELEASE@
Copyright IBM Corp. 2014, 2017
EoVersion
exit 0;
}
sub do_show_info($)
{
my $c = shift();
my $cpumf = $c->{cpumf};
my @f = ();
push @f, "CPU-measurement Counter Facility" if exists $cpumf->{cf};
push @f, "CPU-measurement Sampling Facility" if exists $cpumf->{sf};
if (@f) {
print((join "\n", @f) . "\n");
} else {
print STDERR "No CPU-measurement facilities detected\n";
return 2;
}
return 0;
}
sub do_show_cf($)
{
my $c = shift();
# Check if counter facility is available
unless (exists $c->{cpumf}->{cf}) {
print STDERR "No CPU-measurement counter facility detected\n";
return 2;
}
# Retrieve counter facility information
my $cf = $c->{cpumf}->{cf};
# Create list of authorized counter sets
my @sets = ();
push @sets, "None" unless $cf->{auth};
push @sets, "Crypto-Activity counter set" if $cf->{auth} & 0x8;
push @sets, "Problem-State counter set" if $cf->{auth} & 0x4;
push @sets, "Basic counter set" if $cf->{auth} & 0x2;
push @sets, "Extented counter set" if $cf->{auth} & 0x1;
push @sets, "MT-diagnostic counter set" if $cf->{auth} & 0x20;
print "CPU-measurement counter facility\n";
print "-" x 74 . "\n";
# TODO Display additional information about available conters depending
# on the version information
print "Version: " . $cf->{version} . "\n";
print "\n";
print "Authorized counter sets:\n";
print " $_\n" foreach (sort @sets);
printf "\nLinux perf event support: %s\n\n",
exists $cf->{perf} ? "Yes (PMU: $cf->{perf})" : "No";
return 0;
}
sub div_ceil($$)
{
my ($a, $b) = @_;
return int(($a + $b - 1) / $b);
}
sub humanize_bytes($;$)
{
my $bytes = shift();
my @units = split //, " KMGTPEZY";
my $u = @_ ? shift() : 0;
while ($bytes >= 1024 && $u <= $#units) {
$bytes /= 1024;
$u++;
}
return sprintf "%.f%sB", $bytes, $units[$u];
}
sub get_sfb_details($)
{
my $n_sdb = shift();
# Calculate sampling buffer structure
my $n_sdbt = div_ceil($n_sdb, 511);
my $n_pages = $n_sdb + $n_sdbt;
return [
$n_sdb, # number of sample-data-blocks
$n_sdbt, # number of sample-data-block-tables
$n_pages, # number of 4K pages
$n_pages * 4096, # size in bytes
];
}
sub do_show_sf($)
{
my $c = shift();
# Check if sampling facility is available
unless (exists $c->{cpumf}->{sf}) {
print STDERR "No CPU-measurement sampling facility detected\n";
return 2;
}
my $sf = $c->{cpumf}->{sf};
my $size = invoke_cpumf_helper("--sfb-size");
# Sampling facility information
print "CPU-measurement sampling facility\n";
print "-" x 74 . "\n";
print "Sampling Interval:\n";
printf " Minimum: %10u cycles (approx. %8u Hz)\n",
$sf->{min_sampl_interval},
1000000 * $sf->{cpu_speed} / $sf->{min_sampl_interval};
printf " Maximum: %10u cycles (approx. %8u Hz)\n",
$sf->{max_sampl_interval},
1000000 * $sf->{cpu_speed} / $sf->{max_sampl_interval};
print "\n";
print "Authorized sampling modes:\n";
foreach my $m (sort keys %{$sf->{modes}}) {
printf " %-10s (sample size: %3u bytes)\n", $m,
$sf->{modes}->{$m}->{sample_size};
}
print "\n";
printf "\nLinux perf event support: %s\n\n",
exists $sf->{perf} ? "Yes (PMU: $sf->{perf})" : "No";
# Sampling buffer settings for cpum_sf
goto out unless exists $sf->{perf};
print "Current sampling buffer settings for $sf->{perf}:\n";
printf " Basic-sampling mode\n";
my $s = get_sfb_details($size->[0]);
printf " Minimum: %6u sample-data-blocks (%6s)\n",
$s->[0], humanize_bytes($s->[3]);
$s = get_sfb_details($size->[1]);
printf " Maximum: %6u sample-data-blocks (%6s)\n",
$s->[0], humanize_bytes($s->[3]);
unless (exists $sf->{modes}->{diagnostic}) {
goto out;
}
# Sampling buffer setting specific to diagnostic-sampling mode
my $f_diag = div_ceil($sf->{modes}->{diagnostic}->{sample_size},
$sf->{modes}->{basic}->{sample_size});
print "\n";
printf " Diagnostic-sampling mode (including basic-sampling)\n";
$s = get_sfb_details($size->[0] * $f_diag);
printf " Minimum: %6u sample-data-blocks (%6s)\n",
$s->[0], humanize_bytes($s->[3]);
$s = get_sfb_details($size->[1] * $f_diag);
printf " Maximum: %6u sample-data-blocks (%6s)\n",
$s->[0], humanize_bytes($s->[3]);
printf " Size factor: %2u\n", $f_diag;
out:
return 0;
}
sub print_counters($$)
{
my ($ctrdef, $header) = @_;
my $set_name_map = invoke_cpumf_helper('--ctr-set-names');
my $out = [];
my ($ctr_perf, $ctr_num, $set, $name, $desc);
format PERF_CTR_FORM =
r@<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$ctr_perf, $name
^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$desc
^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ~~
$desc
@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$set
.
print $header;
$~ = "PERF_CTR_FORM";
foreach my $ctr (sort { $a <=> $b } keys %$ctrdef) {
$ctr_perf = sprintf "%x", $ctr;
$ctr_num = $ctr < (1 << 16) ? $ctr : "";
$name = $ctrdef->{$ctr}->{name};
$desc = $ctrdef->{$ctr}->{shortdesc} ?
$ctrdef->{$ctr}->{shortdesc}
: $ctrdef->{$ctr}->{desc};
$desc .= ".";
$set = $set_name_map->{$ctrdef->{$ctr}->{set}};
if ($set) {
$set = "Counter $ctr_num / $set.";
} else {
$set = "This event is not associated with a counter set.";
}
write;
}
}
sub do_show_ctr($;$)
{
my $c = shift();
# Check if counter facility is available
unless (exists $c->{cpumf}->{cf}) {
print STDERR "No CPU-measurement counter facility detected\n";
return 2;
}
# Retrieve counter authorization ("all" or authorized counters only)
my $auth = @_ ? hex("0xFFFF") : $c->{cpumf}->{cf}->{auth};
# Retrieve counter information
my $ctrs = invoke_cpumf_helper("-c $auth");
unless ($ctrs) {
print STDERR "No counters are available or authorized\n";
return 3;
}
# Retrieve hardware type
my $hwtype = invoke_cpumf_helper("--hardware-type");
my $model = length $hwtype->[1] ? "for $hwtype->[1]" : "";
my $header = <<"EoHeader";
Perf event counter list $model
==============================================================================
Raw
event Name Description
------------------------------------------------------------------------------
EoHeader
print_counters($ctrs, $header);
return 0;
}
sub do_show_sf_events($)
{
my $c = shift();
# Check if sampling facility is available
unless (exists $c->{cpumf}->{sf}) {
print STDERR "No CPU-measurement sampling facility detected\n";
return 2;
}
my $sf = $c->{cpumf}->{sf};
my $events = invoke_cpumf_helper("--ctr-sf");
# Remove events with missing authorization
delete $events->{0xB0000} unless exists $sf->{modes}->{basic};
delete $events->{0xBD000} unless exists $sf->{modes}->{diagnostic};
unless ($events) {
print STDERR "Sampling facility is not authorized\n";
return 3;
}
# Display sampling facility events (aka. counters)
my $header = <<"EoHeader";
Perf events for activating the sampling facility
==============================================================================
Raw
event Name Description
------------------------------------------------------------------------------
EoHeader
print_counters($events, $header);
return 0;
}
sub invoke_cpumf_helper($)
{
my $parms = shift();
my $result;
# Call helper module
my $output = qx"$CPUMF_HELPER $parms";
die "Failed to run helper module for '$parms'\n" if $? >> 8;
$result = eval "$output";
die "Failed to parse helper module data\n" if $@;
return $result;
}
&main();
__DATA__
__END__

View File

@@ -0,0 +1,96 @@
Counter: 128 Name:L1I_L2_SOURCED_WRITES
Description:
A directory write to the Level-1 I-Cache directory where the returned
cache line was sourced from the Level-2 (L1.5) cache
.
Counter: 129 Name:L1D_L2_SOURCED_WRITES
Description:
A directory write to the Level-1 D-Cache directory where the installed
cache line was sourced from the Level-2 (L1.5) cache
.
Counter: 130 Name:L1I_L3_LOCAL_WRITES
Description:
A directory write to the Level-1 I-Cache directory where the installed
cache line was sourced from the Level-3 cache that is on the same book
as the Instruction cache (Local L2 cache)
.
Counter: 131 Name:L1D_L3_LOCAL_WRITES
Description:
A directory write to the Level-1 D-Cache directory where the installtion
cache line was source from the Level-3 cache that is on the same book
as the Data cache (Local L2 cache)
.
Counter: 132 Name:L1I_L3_REMOTE_WRITES
Description:
A directory write to the Level-1 I-Cache directory where the installed
cache line was sourced from a Level-3 cache that is not on the same
book as the Instruction cache (Remote L2 cache)
.
Counter: 133 Name:L1D_L3_REMOTE_WRITES
Description:
A directory write to the Level-1 D-Cache directory where the installed
cache line was sourced from a Level-3 cache that is not on the same
book as the Data cache (Remote L2 cache)
.
Counter: 134 Name:L1D_LMEM_SOURCED_WRITES
Description:
A directory write to the Level-1 D-Cache directory where the installed
cache line was sourced from memory that is attached to the same book
as the Data cache (Local Memory)
.
Counter: 135 Name:L1I_LMEM_SOURCED_WRITES
Description:
A directory write to the Level-1 I-Cache where the installed cache
line was sourced from memory that is attached to the s ame book as the
Instruction cahe (local Memory)
.
Counter: 136 Name:L1D_RO_EXCL_WRITES
Description:
A directory write to the Level-1 D-Cache where the line was originally
in a Read-Only state in the cache but has been updated to be in the
Exclusive state that allows stores to the cache line
.
Counter: 137 Name:L1I_CACHELINE_INVALIDATES
Description:
A cache line in the Level-1 I-Cache has been invalidated by a store on
the same CPU as the Level-1 I-Cache
.
Counter: 138 Name:ITLB1_WRITES
Description:
A translation entry has been written into the Level-1 Instruction
Translation Lookaside Buffer
.
Counter: 139 Name:DTLB1_WRITES
Description:
A translation entry has been written to the Level-1 Data Translation
Lookaside Buffer
.
Counter: 140 Name:TLB2_PTE_WRITES
Description:
A translation entry has been written to the Level-2 TLB Page Table
Entry arrays
.
Counter: 141 Name:TLB2_CRSTE_WRITES
Description:
A translation entry has been written to the Level-2 TLB Common Region
Segment Table Entry arrays
.
Counter: 142 Name:TLB2_CRSTE_HPAGE_WRITES
Description:
A translation entry has been written to the Level-2 TLB Common Region
Segment Table Entry arrays for a one-megabyte large page translation
.
Counter: 145 Name:ITLB1_MISSES
Description:
Level-1 Instruction TLB miss in progress. Incremented by one for every
cycle an ITLB1 miss is in progress
.
Counter: 146 Name:DTLB1_MISSES
Description:
Level-1 Data TLB miss in progress. Incremented by one for every cycle
an DTLB1 miss is in progress
.
Counter: 147 Name:L2C_STORES_SENT
Description:
Incremented by one for every store sent to Level-2 (L1.5) cache
.

View File

@@ -0,0 +1,316 @@
# Counter decriptions for the
# IBM z13 extended counter and MT-diagnostic counter set
#
# Notes for transactional-execution mode symbolic names:
# TX .. transactional-execution mode
# NC .. nonconstrained
# C .. constrained
#
# Undefined counters in the extended counter set:
# 142
# 180-217
# 221-225
# Undefined counters in the MT-diagnostic counter set:
# 450-495
#
#
# Extended Counter Set
# ---------------------------------------------------------------------
Counter:128 Name:L1D_WRITES_RO_EXCL
A directory write to the Level-1 Data cache where the line was
originally in a Read-Only state in the cache but has been updated
to be in the Exclusive state that allows stores to the cache line.
.
Counter:129 Name:DTLB1_WRITES
Description:
A translation entry has been written to the Level-1 Data Translation
Lookaside Buffer
.
Counter:130 Name:DTLB1_MISSES
Description:
Level-1 Data TLB miss in progress. Incremented by one for every cycle
a DTLB1 miss is in progress.
.
Counter:131 Name:DTLB1_HPAGE_WRITES
Description:
A translation entry has been written to the Level-1 Data Translation
Lookaside Buffer for a one-megabyte page
.
Counter:132 Name:DTLB1_GPAGE_WRITES
A translation entry has been written to the Level-1 Data Translation
Lookaside Buffer for a two-gigabyte page.
.
Counter:133 Name:L1D_L2D_SOURCED_WRITES
Description:
A directory write to the Level-1 Data cache directory where the returned
cache line was sourced from the Level-2 Data cache
.
Counter:134 Name:ITLB1_WRITES
Description:
A translation entry has been written to the Level-1 Instruction
Translation Lookaside Buffer
.
Counter:135 Name:ITLB1_MISSES
Description:
Level-1 Instruction TLB miss in progress. Incremented by one for every
cycle an ITLB1 miss is in progress
.
Counter:136 Name:L1I_L2I_SOURCED_WRITES
Description:
A directory write to the Level-1 Instruction cache directory where the
returned cache line was sourced from the Level-2 Instruction cache
.
Counter:137 Name:TLB2_PTE_WRITES
Description:
A translation entry has been written to the Level-2 TLB Page Table
Entry arrays
.
Counter:138 Name:TLB2_CRSTE_HPAGE_WRITES
Description:
A translation entry has been written to the Level-2 TLB Combined Region
Segment Table Entry arrays for a one-megabyte large page translation
.
Counter:139 Name:TLB2_CRSTE_WRITES
Description:
A translation entry has been written to the Level-2 TLB Combined Region
Segment Table Entry arrays
.
Counter:140 Name:TX_C_TEND
Description:
A TEND instruction has completed in a constrained transactional-execution
mode
.
Counter:141 Name:TX_NC_TEND
Description:
A TEND instruction has completed in a non-constrained
transactional-execution mode
.
Counter:143 Name:L1C_TLB1_MISSES
Description:
Increments by one for any cycle where a Level-1 cache or Level-1 TLB miss
is in progress.
.
Counter:144 Name:L1D_ONCHIP_L3_SOURCED_WRITES
Description:
A directory write to the Level-1 Data cache directory where the returned
cache line was sourced from an On-Chip Level-3 cache without intervention
.
Counter:145 Name:L1D_ONCHIP_L3_SOURCED_WRITES_IV
Description:
A directory write to the Level-1 Data cache directory where the returned
cache line was sourced from an On-Chip Level-3 cache with intervention
.
Counter:146 Name:L1D_ONNODE_L4_SOURCED_WRITES
Description:
A directory write to the Level-1 Data cache directory where the returned
cache line was sourced from an On-Node Level-4 cache
.
Counter:147 Name:L1D_ONNODE_L3_SOURCED_WRITES_IV
Description:
A directory write to the Level-1 Data cache directory where the returned
cache line was sourced from an On-Node Level-3 cache with intervention
.
Counter:148 Name:L1D_ONNODE_L3_SOURCED_WRITES
Description:
A directory write to the Level-1 Data cache directory where the returned
cache line was sourced from an On-Node Level-3 cache without intervention
.
Counter:149 Name:L1D_ONDRAWER_L4_SOURCED_WRITES
Description:
A directory write to the Level-1 Data cache directory where the returned
cache line was sourced from an On-Drawer Level-4 cache
.
Counter:150 Name:L1D_ONDRAWER_L3_SOURCED_WRITES_IV
Description:
A directory write to the Level-1 Data cache directory where the returned
cache line was sourced from an On-Drawer Level-3 cache with intervention
.
Counter:151 Name:L1D_ONDRAWER_L3_SOURCED_WRITES
Description:
A directory write to the Level-1 Data cache directory where the returned
cache line was sourced from an On-Drawer Level-3 cache without intervention
.
Counter:152 Name:L1D_OFFDRAWER_SCOL_L4_SOURCED_WRITES
Description:
A directory write to the Level-1 Data cache directory where the returned
cache line was sourced from an Off-Drawer Same-Column Level-4 cache
.
Counter:153 Name:L1D_OFFDRAWER_SCOL_L3_SOURCED_WRITES_IV
Description:
A directory write to the Level-1 Data cache directory where the returned
cache line was sourced from an Off-Drawer Same-Column Level-3 cache with
intervention
.
Counter:154 Name:L1D_OFFDRAWER_SCOL_L3_SOURCED_WRITES
Description:
A directory write to the Level-1 Data cache directory where the returned
cache line was sourced from an Off-Drawer Same-Column Level-3 cache
without intervention
.
Counter:155 Name:L1D_OFFDRAWER_FCOL_L4_SOURCED_WRITES
Description:
A directory write to the Level-1 Data cache directory where the returned
cache line was sourced from an Off-Drawer Far-Column Level-4 cache
.
Counter:156 Name:L1D_OFFDRAWER_FCOL_L3_SOURCED_WRITES_IV
Description:
A directory write to the Level-1 Data cache directory where the returned
cache line was sourced from an Off-Drawer Far-Column Level-3 cache with
intervention
.
Counter:157 Name:L1D_OFFDRAWER_FCOL_L3_SOURCED_WRITES
Description:
A directory write to the Level-1 Data cache directory where the returned
cache line was sourced from an Off-Drawer Far-Column Level-3 cache
without intervention
.
Counter:158 Name:L1D_ONNODE_MEM_SOURCED_WRITES
Description:
A directory write to the Level-1 Data cache directory where the returned
cache line was sourced from On-Node memory
.
Counter:159 Name:L1D_ONDRAWER_MEM_SOURCED_WRITES
Description:
A directory write to the Level-1 Data cache directory where the returned
cache line was sourced from On-Drawer memory
.
Counter:160 Name:L1D_OFFDRAWER_MEM_SOURCED_WRITES
Description:
A directory write to the Level-1 Data cache directory where the returned
cache line was sourced from On-Drawer memory
.
Counter:161 Name:L1D_ONCHIP_MEM_SOURCED_WRITES
Description:
A directory write to the Level-1 Data cache directory where the returned
cache line was sourced from On-Chip memory
.
Counter:162 Name:L1I_ONCHIP_L3_SOURCED_WRITES
Description:
A directory write to the Level-1 Instruction cache directory where the
returned cache line was sourced from an On-Chip Level-3 cache without
intervention
.
Counter:163 Name:L1I_ONCHIP_L3_SOURCED_WRITES_IV
Description:
A directory write to the Level-1 Instruction cache directory where the
returned cache line was sourced from an On Chip Level-3 cache with
intervention
.
Counter:164 Name:L1I_ONNODE_L4_SOURCED_WRITES
Description:
A directory write to the Level-1 Instruction cache directory where the
returned cache line was sourced from an On-Node Level-4 cache
.
Counter:165 Name:L1I_ONNODE_L3_SOURCED_WRITES_IV
Description:
A directory write to the Level-1 Instruction cache directory where
the returned cache line was sourced from an On-Node Level-3 cache
with intervention
.
Counter:166 Name:L1I_ONNODE_L3_SOURCED_WRITES
Description:
A directory write to the Level-1 Instruction cache directory where
the returned cache line was sourced from an On-Node Level-3 cache
without intervention
.
Counter:167 Name:L1I_ONDRAWER_L4_SOURCED_WRITES
Description:
A directory write to the Level-1 Instruction cache directory where
the returned cache line was sourced from an On-Drawer Level-4 cache
.
Counter:168 Name:L1I_ONDRAWER_L3_SOURCED_WRITES_IV
Description:
A directory write to the Level-1 Instruction cache directory where
the returned cache line was sourced from an On-Drawer Level-3 cache
with intervention
.
Counter:169 Name:L1I_ONDRAWER_L3_SOURCED_WRITES
Description:
A directory write to the Level-1 Instruction cache directory where
the returned cache line was sourced from an On-Drawer Level-3 cache
without intervention
.
Counter:170 Name:L1I_OFFDRAWER_SCOL_L4_SOURCED_WRITES
Description:
A directory write to the Level-1 Instruction cache directory where
the returned cache line was sourced from an Off-Drawer Same-Column
Level-4 cache
.
Counter:171 Name:L1I_OFFDRAWER_SCOL_L3_SOURCED_WRITES_IV
Description:
A directory write to the Level-1 Instruction cache directory where
the returned cache line was sourced from an Off-Drawer Same-Column
Level-3 cache with intervention
.
Counter:172 Name:L1I_OFFDRAWER_SCOL_L3_SOURCED_WRITES
Description:
A directory write to the Level-1 Instruction cache directory where
the returned cache line was sourced from an Off-Drawer Same-Column
Level-3 cache without intervention
.
Counter:173 Name:L1I_OFFDRAWER_FCOL_L4_SOURCED_WRITES
Description:
A directory write to the Level-1 Instruction cache directory where
the returned cache line was sourced from an Off-Drawer Far-Column
Level-4 cache
.
Counter:174 Name:L1I_OFFDRAWER_FCOL_L3_SOURCED_WRITES_IV
Description:
A directory write to the Level-1 Instruction cache directory where
the returned cache line was sourced from an Off-Drawer Far-Column
Level-3 cache with intervention
.
Counter:175 Name:L1I_OFFDRAWER_FCOL_L3_SOURCED_WRITES
Description:
A directory write to the Level-1 Instruction cache directory where
the returned cache line was sourced from an Off-Drawer Far-Column
Level-3 cache without intervention
.
Counter:176 Name:L1I_ONNODE_MEM_SOURCED_WRITES
Description:
A directory write to the Level-1 Instruction cache directory where
the returned cache line was sourced from On-Node memory
.
Counter:177 Name:L1I_ONDRAWER_MEM_SOURCED_WRITES
Description:
A directory write to the Level-1 Instruction cache directory where
the returned cache line was sourced from On-Drawer memory
.
Counter:178 Name:L1I_OFFDRAWER_MEM_SOURCED_WRITES
Description:
A directory write to the Level-1 Instruction cache directory where
the returned cache line was sourced from On-Drawer memory
.
Counter:179 Name:L1I_ONCHIP_MEM_SOURCED_WRITES
Description:
A directory write to the Level-1 Instruction cache directory where
the returned cache line was sourced from On-Chip memory
.
Counter:218 Name:TX_NC_TABORT
Description:
A transaction abort has occurred in a non-constrained
transactional-execution mode
.
Counter:219 Name:TX_C_TABORT_NO_SPECIAL
Description:
A transaction abort has occurred in a constrained transactional-execution
mode and the CPU is not using any special logic to allow the transaction
to complete
.
Counter:220 Name:TX_C_TABORT_SPECIAL
Description:
A transaction abort has occurred in a constrained transactional-execution
mode and the CPU is using special logic to allow the transaction to
complete
.
#
# MT-diagnostic counter set
# ---------------------------------------------------------------------
Counter:448 Name:MT_DIAG_CYCLES_ONE_THR_ACTIVE
Description:
Cycle count with one thread active
.
Counter:449 Name:MT_DIAG_CYCLES_TWO_THR_ACTIVE
Description:
Cycle count with two threads active
.

View File

@@ -0,0 +1,122 @@
Counter: 128 Name:L1D_L2_SOURCED_WRITES
Description:
A directory write to the Level-1 D-Cache directory where the returned
cache line was sourced from the Level-2 cache
.
Counter: 129 Name:L1I_L2_SOURCED_WRITES
Description:
A directory write to the Level-1 I-Cache directory where the returned
cache line was sourced from the Level-2 cache
.
Counter: 130 Name:DTLB1_MISSES
Description:
Level-1 Data TLB miss in progress. Incremented by one for every cycle
a DTLB1 miss is in progress.
.
Counter: 131 Name:ITLB1_MISSES
Description:
Level-1 Instruction TLB miss in progress. Incremented by one for every
cycle a ITLB1 miss is in progress.
.
Counter: 133 Name:L2C_STORES_SENT
Description:
Incremented by one for every store sent to Level-2 cache
.
Counter: 134 Name:L1D_OFFBOOK_L3_SOURCED_WRITES
Description:
A directory write to the Level-1 D-Cache directory where the returned
cache line was sourced from an Off Book Level-3 cache
.
Counter: 135 Name:L1D_ONBOOK_L4_SOURCED_WRITES
Description:
A directory write to the Level-1 D-Cache directory where the returned
cache line was sourced from an On Book Level-4 cache
.
Counter: 136 Name:L1I_ONBOOK_L4_SOURCED_WRITES
Description:
A directory write to the Level-1 I-Cache directory where the returned
cache line was sourced from an On Book Level-4 cache
.
Counter: 137 Name:L1D_RO_EXCL_WRITES
Description:
A directory write to the Level-1 D-Cache where the line was originally
in a Read-Only state in the cache but has been updated to be in the
Exclusive state that allows stores to the cache line
.
Counter: 138 Name:L1D_OFFBOOK_L4_SOURCED_WRITES
Description:
A directory write to the Level-1 D-Cache directory where the returned
cache line was sourced from an Off Book Level-4 cache
.
Counter: 139 Name:L1I_OFFBOOK_L4_SOURCED_WRITES
Description:
A directory write to the Level-1 I-Cache directory where the returned
cache line was sourced from an Off Book Level-4 cache
.
Counter: 140 Name:DTLB1_HPAGE_WRITES
Description:
A translation entry has been written to the Level-1 Data Translation
Lookaside Buffer for a one-megabyte page
.
Counter: 141 Name:L1D_LMEM_SOURCED_WRITES
Description:
A directory write to the Level-1 D-Cache where the installed cache line
was sourced from memory that is attached to the same book as the Data
cache (Local Memory)
.
Counter: 142 Name:L1I_LMEM_SOURCED_WRITES
Description:
A directory write to the Level-1 I-Cache where the installed cache
line was sourced from memory that is attached to the same book as the
Instruction cache (Local Memory)
.
Counter: 143 Name:L1I_OFFBOOK_L3_SOURCED_WRITES
Description:
A directory write to the Level-1 I-Cache directory where the returned
cache line was sourced from an Off Book Level-3 cache
.
Counter: 144 Name:DTLB1_WRITES
Description:
A translation entry has been written to the Level-1 Data Translation
Lookaside Buffer
.
Counter: 145 Name:ITLB1_WRITES
Description:
A translation entry has been written to the Level-1 Instruction
Translation Lookaside Buffer
.
Counter: 146 Name:TLB2_PTE_WRITES
Description:
A translation entry has been written to the Level-2 TLB Page Table
Entry arrays
.
Counter: 147 Name:TLB2_CRSTE_HPAGE_WRITES
Description:
A translation entry has been written to the Level-2 TLB Common Region
Segment Table Entry arrays for a one-megabyte large page translation
.
Counter: 148 Name:TLB2_CRSTE_WRITES
Description:
A translation entry has been written to the Level-2 TLB Common Region
Segment Table Entry arrays
.
Counter: 150 Name:L1D_ONCHIP_L3_SOURCED_WRITES
Description:
A directory write to the Level-1 D-Cache directory where the returned
cache line was sourced from an On Chip Level-3 cache
.
Counter: 152 Name:L1D_OFFCHIP_L3_SOURCED_WRITES
Description:
A directory write to the Level-1 D-Cache directory where the returned
cache line was sourced from an Off Chip/On Book Level-3 cache
.
Counter: 153 Name:L1I_ONCHIP_L3_SOURCED_WRITES
Description:
A directory write to the Level-1 I-Cache directory where the returned
cache line was sourced from an On Chip Level-3 cache
.
Counter: 155 Name:L1I_OFFCHIP_L3_SOURCED_WRITES
Description:
A directory write to the Level-1 I-Cache directory where the returned
cache line was sourced from an Off Chip/On Book Level-3 cache
.

View File

@@ -0,0 +1,195 @@
Counter: 128 Name:DTLB1_MISSES
Description:
Level-1 Data TLB miss in progress. Incremented by one for every cycle
a DTLB1 miss is in progress.
.
Counter:129 Name:ITLB1_MISSES
Description:
Level-1 Instruction TLB miss in progress. Incremented by one for every
cycle a ITLB1 miss is in progress.
.
Counter:130 Name:L1D_L2I_SOURCED_WRITES
Description:
A directory write to the Level-1 Data cache directory where the returned
cache line was sourced from the Level-2 Instruction cache
.
Counter:131 Name:L1I_L2I_SOURCED_WRITES
Description:
A directory write to the Level-1 Instruction cache directory where the
returned cache line was sourced from the Level-2 Instruction cache
.
Counter:132 Name:L1D_L2D_SOURCED_WRITES
Description:
A directory write to the Level-1 Data cache directory where the returned
cache line was sourced from the Level-2 Data cache
.
Counter:133 Name:DTLB1_WRITES
Description:
A translation entry has been written to the Level-1 Data Translation
Lookaside Buffer
.
Counter:135 Name:L1D_LMEM_SOURCED_WRITES
Description:
A directory write to the Level-1 Data cache where the installed cache line
was sourced from memory that is attached to the same book as the Data
cache (Local Memory)
.
Counter:137 Name:L1I_LMEM_SOURCED_WRITES
Description:
A directory write to the Level-1 Instruction cache where the installed cache
line was sourced from memory that is attached to the same book as the
Instruction cache (Local Memory)
.
Counter:138 Name:L1D_RO_EXCL_WRITES
Description:
A directory write to the Level-1 D-Cache where the line was originally
in a Read-Only state in the cache but has been updated to be in the
Exclusive state that allows stores to the cache line
.
Counter:139 Name:DTLB1_HPAGE_WRITES
Description:
A translation entry has been written to the Level-1 Data Translation
Lookaside Buffer for a one-megabyte page
.
Counter:140 Name:ITLB1_WRITES
Description:
A translation entry has been written to the Level-1 Instruction
Translation Lookaside Buffer
.
Counter:141 Name:TLB2_PTE_WRITES
Description:
A translation entry has been written to the Level-2 TLB Page Table
Entry arrays
.
Counter:142 Name:TLB2_CRSTE_HPAGE_WRITES
Description:
A translation entry has been written to the Level-2 TLB Common Region
Segment Table Entry arrays for a one-megabyte large page translation
.
Counter:143 Name:TLB2_CRSTE_WRITES
Description:
A translation entry has been written to the Level-2 TLB Common Region
Segment Table Entry arrays
.
Counter:144 Name:L1D_ONCHIP_L3_SOURCED_WRITES
Description:
A directory write to the Level-1 Data cache directory where the returned
cache line was sourced from an On Chip Level-3 cache without intervention
.
Counter:145 Name:L1D_OFFCHIP_L3_SOURCED_WRITES
Description:
A directory write to the Level-1 Data cache directory where the returned
cache line was sourced from an Off Chip/On Book Level-3 cache without
intervention
.
Counter:146 Name:L1D_OFFBOOK_L3_SOURCED_WRITES
Description:
A directory write to the Level-1 Data cache directory where the returned
cache line was sourced from an Off Book Level-3 cache without intervention
.
Counter:147 Name:L1D_ONBOOK_L4_SOURCED_WRITES
Description:
A directory write to the Level-1 Data cache directory where the returned
cache line was sourced from an On Book Level-4 cache
.
Counter:148 Name:L1D_OFFBOOK_L4_SOURCED_WRITES
Description:
A directory write to the Level-1 Data cache directory where the returned
cache line was sourced from an Off Book Level-4 cache
.
#
# Notes for the transactional-execution mode notations:
# TX .. transactional-execution mode
# NC .. nonconstrained
# C .. constrained
#
Counter:149 Name:TX_NC_TEND
Description:
A TEND instruction has completed in a nonconstrained
transactional-execution mode
.
Counter:150 Name:L1D_ONCHIP_L3_SOURCED_WRITES_IV
Description:
A directory write to the Level-1 Data cache directory where the returned
cache line was sourced from a On Chip Level-3 cache with intervention
.
Counter:151 Name:L1D_OFFCHIP_L3_SOURCED_WRITES_IV
Description:
A directory write to the Level-1 Data cache directory where the returned
cache line was sourced from an Off Chip/On Book Level-3 cache with
intervention
.
# XXX Remove SOURCED from L1D_OFFBOOK_L3_SOURCED_WRITES...
Counter:152 Name:L1D_OFFBOOK_L3_SOURCED_WRITES_IV
Description:
A directory write to the Level-1 Data cache directory where the returned
cache line was sourced from an Off Book Level-3 cache with intervention
.
Counter:153 Name:L1I_ONCHIP_L3_SOURCED_WRITES
Description:
A directory write to the Level-1 Instruction cache directory where the
returned cache line was sourced from an On Chip Level-3 cache without
intervention
.
Counter:154 Name:L1I_OFFCHIP_L3_SOURCED_WRITES
Description:
A directory write to the Level-1 Instruction cache directory where the
returned cache line was sourced from an Off Chip/On Book Level-3 cache
without intervention
.
Counter:155 Name:L1I_OFFBOOK_L3_SOURCED_WRITES
Description:
A directory write to the Level-1 Instruction cache directory where the
returned cache line was sourced from an Off Book Level-3 cache without
intervention
.
Counter:156 Name:L1I_ONBOOK_L4_SOURCED_WRITES
Description:
A directory write to the Level-1 Instruction cache directory where the
returned cache line was sourced from an On Book Level-4 cache
.
Counter:157 Name:L1I_OFFBOOK_L4_SOURCED_WRITES
Description:
A directory write to the Level-1 Instruction cache directory where the
returned cache line was sourced from an Off Book Level-4 cache
.
Counter:158 Name:TX_C_TEND
Description:
A TEND instruction has completed in a constrained transactional-execution
mode
.
Counter:159 Name:L1I_ONCHIP_L3_SOURCED_WRITES_IV
Description:
A directory write to the Level-1 Instruction cache directory where the
returned cache line was sourced from an On Chip Level-3 cache with
intervention
.
Counter:160 Name:L1I_OFFCHIP_L3_SOURCED_WRITES_IV
Description:
A directory write to the Level-1 Instruction cache directory where the
returned cache line was sourced from an Off Chip/On Book Level-3 cache
with intervention
.
Counter:161 Name:L1I_OFFBOOK_L3_SOURCED_WRITES_IV
Description:
A directory write to the Level-1 Instruction cache directory where the
returned cache line was sourced from an Off Book Level-3 cache with
intervention
.
Counter:177 Name:TX_NC_TABORT
Description:
A transaction abort has occurred in a nonconstrained
transactional-execution mode
.
Counter:178 Name:TX_C_TABORT_NO_SPECIAL
Description:
A transaction abort has occurred in a constrained transactional-execution
mode and the CPU is not using any special logic to allow the transaction
to complete
.
Counter:179 Name:TX_C_TABORT_SPECIAL
Description:
A transaction abort has occurred in a constrained transactional-execution
mode and the CPU is using special logic to allow the transaction to
complete
.

View File

@@ -0,0 +1,132 @@
Counter: 0 Name:CPU_CYCLES
Description:
Cycle Count
.
Counter: 1 Name:INSTRUCTIONS
Description:
Instruction Count
.
Counter: 2 Name:L1I_DIR_WRITES
Description:
Level-1 I-Cache Directory Write Count
.
Counter: 3 Name:L1I_PENALTY_CYCLES
Description:
Level-1 I-Cache Penalty Cycle Count
.
Counter: 4 Name:L1D_DIR_WRITES
Description:
Level-1 D-Cache Directory Write Count
.
Counter: 5 Name:L1D_PENALTY_CYCLES
Description:
Level-1 D-Cache Penalty Cycle Count
.
Counter: 32 Name:PROBLEM_STATE_CPU_CYCLES
Description:
Problem-State Cycle Count
.
Counter: 33 Name:PROBLEM_STATE_INSTRUCTIONS
Description:
Problem-State Instruction Count
.
Counter: 34 Name:PROBLEM_STATE_L1I_DIR_WRITES
Description:
Problem-State Level-1 I-Cache Directory Write Count
.
Counter: 35 Name:PROBLEM_STATE_L1I_PENALTY_CYCLES
Description:
Problem-State Level-1 I-Cache Penalty Cycle Count
.
Counter: 36 Name:PROBLEM_STATE_L1D_DIR_WRITES
Description:
Problem-State Level-1 D-Cache Directory Write Count
.
Counter: 37 Name:PROBLEM_STATE_L1D_PENALTY_CYCLES
Description:
Problem-State Level-1 D-Cache Penalty Cycle Count
.
Counter: 64 Name:PRNG_FUNCTIONS
Description:
Total number of the PRNG functions issued by the CPU
.
Counter: 65 Name:PRNG_CYCLES
Description:
Total number of CPU cycles when the DEA/AES coprocessor is busy
performing PRNG functions issued by the CPU
.
Counter: 66 Name:PRNG_BLOCKED_FUNCTIONS
Description:
Total number of the PRNG functions that are issued by the CPU and are
blocked because the DEA/AES coprocessor is busy performing a function
issued by another CPU
.
Counter: 67 Name:PRNG_BLOCKED_CYCLES
Description:
Total number of CPU cycles blocked for the PRNG functions issued by
the CPU because the DEA/AES coprocessor is busy performing a function
issued by another CPU
.
Counter: 68 Name:SHA_FUNCTIONS
Description:
Total number of SHA functions issued by the CPU
.
Counter: 69 Name:SHA_CYCLES
Description:
Total number of CPU cycles when the SHA coprocessor is busy performing
the SHA functions issued by the CPU
.
Counter: 70 Name:SHA_BLOCKED_FUNCTIONS
Description:
Total number of the SHA functions that are issued by the CPU and are
blocked because the SHA coprocessor is busy performing a function issued
by another CPU
.
Counter: 71 Name:SHA_BLOCKED_CYCLES
Description:
Total number of CPU cycles blocked for the SHA functions issued by the
CPU because the SHA coprocessor is busy performing a function issued
by another CPU
.
Counter: 72 Name:DEA_FUNCTIONS
Description:
Total number of the DEA functions issued by the CPU
.
Counter: 73 Name:DEA_CYCLES
Description:
Total number of CPU cycles when the DEA/AES coprocessor is busy
performing the DEA functions issued by the CPU
.
Counter: 74 Name:DEA_BLOCKED_FUNCTIONS
Description:
Total number of the DEA functions that are issued by the CPU and are
blocked because the DEA/AES coprocessor is busy performing a function
issued by another CPU
.
Counter: 75 Name:DEA_BLOCKED_CYCLES
Description:
Total number of CPU cycles blocked for the DEA functions issued by the
CPU because the DEA/AES coprocessor is busy performing a function issued
by another CPU
.
Counter: 76 Name:AES_FUNCTIONS
Description:
Total number of AES functions issued by the CPU
.
Counter: 77 Name:AES_CYCLES
Description:
Total number of CPU cycles when the DEA/AES coprocessor is busy
performing the AES functions issued by the CPU
.
Counter: 78 Name:AES_BLOCKED_FUNCTIONS
Description:
Total number of AES functions that are issued by the CPU and are blocked
because the DEA/AES coprocessor is busy performing a function issued
by another CPU
.
Counter: 79 Name:AES_BLOCKED_CYCLES
Description:
Total number of CPU cycles blocked for the AES functions issued by the
CPU because the DEA/AES coprocessor is busy performing a function issued
by another CPU
.

View File

@@ -0,0 +1,17 @@
# CPU-measurement facilities
#
# Mapping of IBM System z hardware types to extended counter set defintions
#
#
{
# Definition # File name
0 => 'cpum-cf-generic.ctr',
2097 => 'cpum-cf-extended-z10.ctr',
2098 => 'cpum-cf-extended-z10.ctr',
2817 => 'cpum-cf-extended-z196.ctr',
2818 => 'cpum-cf-extended-z196.ctr',
2827 => 'cpum-cf-extended-zEC12.ctr',
2828 => 'cpum-cf-extended-zEC12.ctr',
2964 => 'cpum-cf-extended-z13.ctr',
2965 => 'cpum-cf-extended-z13.ctr',
};

View File

@@ -0,0 +1,13 @@
# Perf raw event counter definitions for the
# CPU-measurment sampling facility
#
# Basic-sampling mode
Counter: 0xB0000 Name:SF_CYCLES_BASIC
Description:
Sample CPU cycles using basic-sampling mode
.
# Diagnostic-sampling mode (includes basic-sampling)
Counter: 0xBD000 Name:SF_CYCLES_BASIC_DIAG
Description:
Sample CPU cycle using diagnostic-sampling mode (not for ordinary use)
.

66
cpumf/man/chcpumf.8 Normal file
View File

@@ -0,0 +1,66 @@
.\" chcpumf.8
.\"
.\"
.\" Copyright IBM Corp. 2014, 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 chcpumf "8" "February 2014" "s390-tools" "CPU-MF management programs"
.
.ds c \fBchcpumf\fP
.
.
.SH NAME
chcpumf \- manage the CPU-measurement facilities support
.
.
.SH SYNOPSIS
.B chcpumf
.RB [ \-m | \-\-min
.IR num_sdb ]
.RB [ \-x | \-\-max
.IR num_sdb ]
.br
.B chcpumf
.BR \-h | \-\-help
.br
.B chcpumf
.BR \-v | \-\-version
.
.
.SH DESCRIPTION
The chcpumf program helps you to manage settings for the CPU-measurement
facilities for Linux on System z.
.
.
.SH OPTIONS
.TP
.BR \-m ", " \-\-min " \fInum_sdb\fP"
Specifies the minimum sampling facility buffer size in sample-data-blocks
(SDB). A sample-data-block consumes about 4 kilobytes.
This is the initial buffer size when you start the sampling facility.
.
.TP
.BR \-x ", " \-\-max " \fInum_sdb\fP"
Specifies the maximum sampling facility buffer size in sample-data-blocks
(SDB). A sample-data-block consumes about 4 kilobytes.
This is the maximum size to which the buffer is dynamically adjusted during
sampling.
.
.TP
.BR \-V ", " \-\-verbose
Displays verbose messages.
.
.TP
.BR \-h ", " \-\-help
Displays help information, then exits.
.
.TP
.BR \-v ", " \-\-version
Displays version information, then exits.
.
.
.SH "SEE ALSO"
.BR lscpumf (1)

70
cpumf/man/lscpumf.1 Normal file
View File

@@ -0,0 +1,70 @@
\" lscpumf.1
.\"
.\"
.\" Copyright IBM Corp. 2014, 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 lscpumf "1" "February 2014" "s390-tools" "CPU-MF management programs"
.
.ds c \fBlscpumf\fP
.
.
.SH NAME
lscpumf \- display information about CPU-measurement facilities
.
.
.SH SYNOPSIS
.B lscpumf
.RB [ \-i | \-\-info ]
.br
.B lscpumf
.RB [ \-c | \-\-list\-counters ]
.RB [ \-C | \-\-list\-all\-counters ]
.RB [ \-s | \-\-list\-sampling\-events ]
.br
.B lscpumf
.BR \-h | \-\-help
.br
.B lscpumf
.BR \-v | \-\-version
.
.
.SH DESCRIPTION
The lscpumf program lists information about CPU-measurement facilities that
are supported by Linux on System z.
.
.
.SH OPTIONS
.TP
.BR \-i ", " \-\-info
Displays detailed information about available and supported CPU-measurement
facilities.
.
.TP
.BR \-c ", " \-\-list\-counters
Lists counters that are provided by the CPU-measurement facility, omitting
counters for which the LPAR is not authorized. For counter measurements with
the perf program, the raw event identifier is displayed.
.
.TP
.BR \-C ", " \-\-list\-all\-counters
Lists all counters that are provided by the CPU-measurement counter facility,
regardless of LPAR authorization. To list only those counters for which the
LPAR is authorized, use the -c option.
.
.TP
.BR \-s ", " \-\-list\-sampling\-events
Lists perf raw events that activate the sampling facility.
.
.TP
.BR \-h ", " \-\-help
Displays help information, then exits.
.
.TP
.BR \-v ", " \-\-version
Displays version information, then exits.
.
.
.SH "SEE ALSO"
.BR chcpumf (8)

23
cpuplugd/Makefile Normal file
View File

@@ -0,0 +1,23 @@
include ../common.mak
all: cpuplugd
LDLIBS += -lm
OBJECTS = daemon.o cpu.o info.o terms.o config.o main.o getopt.o mem.o
cpuplugd: $(OBJECTS)
$(LINK) $(ALL_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@
clean:
rm -f cpuplugd $(OBJECTS)
install: all
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 cpuplugd \
$(DESTDIR)$(USRSBINDIR)
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 man/cpuplugd.8 \
$(DESTDIR)$(MANDIR)/man8
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 man/cpuplugd.conf.5 \
$(DESTDIR)$(MANDIR)/man5
.PHONY: all install clean

336
cpuplugd/config.c Normal file
View File

@@ -0,0 +1,336 @@
/*
* cpuplugd - Linux for System z Hotplug Daemon
*
* Config file parsing
*
* Copyright IBM Corp. 2007, 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 "cpuplugd.h"
/*
* Return the value of a variable which parse_config() found within the
* configuration file. Use only for values valid >= 0, because -1 is returned
* in error case.
*/
static long parse_positive_value(char *ptr)
{
long value = 0;
unsigned int i;
if (ptr == NULL)
return -1;
for (i = 0; i < strlen(ptr); i++) {
if (isdigit(ptr[i]) == 0)
return -1;
}
sscanf(ptr, "%ld", &value);
return value;
}
char *get_var_rvalue(char *var_name)
{
char tmp_name[MAX_VARNAME + 3]; /* +3 for '\0', '=' and '\n' */
unsigned int tmp_length;
char *rvalue;
tmp_name[0] = '\n';
strncpy(&tmp_name[1], var_name, MAX_VARNAME + 1); /* +1 for '\0' */
tmp_length = strlen(tmp_name);
tmp_name[tmp_length] = '=';
tmp_name[tmp_length + 1] = '\0';
rvalue = strstr(varinfo, tmp_name);
if (rvalue == NULL)
return NULL;
rvalue += strlen(tmp_name);
return rvalue;
}
static void add_var(char *name, char *rvalue)
{
size_t offset, size;
unsigned int i;
if (get_var_rvalue(name))
cpuplugd_exit("Variable defined twice: %s\n", name);
for (i = 0; i < sym_names_count; i++) {
if (strncmp(name, sym_names[i].name,
MAX(strlen(sym_names[i].name), strlen(name))) != 0)
continue;
cpuplugd_exit("Cannot use (pre-defined) variable name: %s\n",
name);
}
offset = strlen(varinfo);
/* +3 because of extra '=', '\n' and '\0' */
size = offset + strlen(name) + strlen(rvalue) + 3;
if (size > varinfo_size)
//TODO realloc?
cpuplugd_exit("buffer for variables too small: need %ld, "
"have %ld (bytes)\n", size, varinfo_size);
size -= offset;
snprintf(&varinfo[offset], size, "%s=%s\n", name, rvalue);
return;
}
static int check_term(char *symbol, char *name, char *rvalue, struct term **term)
{
if (!strncasecmp(name, symbol, strlen(symbol))) {
cpuplugd_debug("found the following rule: %s = %s\n",
name, rvalue);
*term = parse_term(&rvalue, OP_PRIO_NONE);
if (rvalue[0] == '\0')
return 1;
cpuplugd_exit("parsing error at %s, position: %s\n", symbol,
rvalue);
}
return 0;
}
static int check_value(char *symbol, char *name, char *rvalue, long *value)
{
if (!strncasecmp(name, symbol, strlen(symbol))) {
*value = parse_positive_value(rvalue);
cpuplugd_debug("found %s value: %ld\n", symbol, *value);
if (*value >= 0)
return 1;
cpuplugd_exit("parsing error at update\n");
}
return 0;
}
/*
* Parse a single line of the configuration file
*/
static void parse_configline(char *line)
{
char *match, *name, *rvalue, *start, *stop;
int i, j;
size_t len;
char temp[strlen(line) + 1];
if (line[0] == '#')
return;
for (i = j = 0; line[i] != 0; i++) /* Remove whitespace. */
if (!isblank(line[i]) && !isspace(line[i]))
temp[j++] = line[i];
temp[j] = '\0';
match = strchr(temp, '=');
if (match == NULL)
return;
*match = '\0'; /* Separate name and right hand value */
name = temp; /* left side of = */
rvalue = match + 1; /* right side of = */
/*
* remove the double quotes
* example: CPU_MIN="2"
*/
start = strchr(rvalue, '"'); /* points to first " */
stop = strrchr(rvalue, '"'); /* points to last " */
len = stop - start;
if (start != NULL && stop != NULL && len > 0) {
rvalue[len] = '\0';
rvalue = rvalue + 1;
} else
cpuplugd_exit("the configuration file has syntax "
"errors at %s, position: %s\n", name, rvalue);
if (check_term("hotplug", name, rvalue, &cfg.hotplug))
return;
if (check_term("hotunplug", name, rvalue, &cfg.hotunplug))
return;
if (check_term("memplug", name, rvalue, &cfg.memplug))
return;
if (check_term("memunplug", name, rvalue, &cfg.memunplug))
return;
if (check_term("cmm_inc", name, rvalue, &cfg.cmm_inc))
return;
if (check_term("cmm_dec", name, rvalue, &cfg.cmm_dec))
return;
if (check_value("update", name, rvalue, &cfg.update)) {
if (cfg.update > 0)
return;
cpuplugd_exit("update must be > 0\n");
}
if (check_value("cpu_min", name, rvalue, &cfg.cpu_min)) {
if (cfg.cpu_min > 0)
return;
cpuplugd_exit("cpu_min must be > 0\n");
}
if (check_value("cpu_max", name, rvalue, &cfg.cpu_max)) {
if (cfg.cpu_max == 0)
/* if cpu_max is 0, we use the overall number of cpus */
cfg.cpu_max = get_numcpus();
return;
}
if (check_value("cmm_min", name, rvalue, &cfg.cmm_min))
return;
if (check_value("cmm_max", name, rvalue, &cfg.cmm_max))
return;
cpuplugd_debug("found the following variable: %s = %s\n",
name, rvalue);
if (strlen(name) > MAX_VARNAME)
cpuplugd_exit("Variable name too long (max. length is "
"%i chars): %s\n", MAX_VARNAME, name);
add_var(name, rvalue);
}
/*
* Function used to parse the min and max values at the beginning of the
* configuration file as well as the hotplug and hotunplug rules.
*/
void parse_configfile(char *file)
{
char linebuffer[MAX_LINESIZE + 2]; /* current line incl. \n and \0 */
char *linep_offset;
FILE *filp;
filp = fopen(file, "r");
if (!filp)
cpuplugd_exit("Opening configuration file failed: %s\n",
strerror(errno));
while (fgets(linebuffer, sizeof(linebuffer), filp) != NULL) {
if (!(linep_offset = strchr(linebuffer, '\n')))
cpuplugd_exit("Line is too long (max. length is %i "
"characters): %s\n", MAX_LINESIZE,
linebuffer);
parse_configline(linebuffer);
}
}
/*
* Check if the required settings are found in the configuration file.
* "Autodetect" if cpu and/or memory hotplug configuration entries
* where specified
*/
void check_config()
{
int cpuid;
int lpar_status;
lpar_status = check_lpar();
if (cfg.update < 0)
cpuplugd_exit("No valid update interval specified.\n");
if (cfg.cpu_max < cfg.cpu_min && cfg.cpu_max != 0)
cpuplugd_exit("cpu_max below cpu_min, aborting.\n");
if (cfg.cpu_max < 0 || cfg.cpu_min < 0 || cfg.hotplug == NULL ||
cfg.hotunplug == NULL) {
cpuplugd_error("No valid CPU hotplug configuration "
"detected.\n");
cpu = 0;
} else {
cpu = 1;
cpuplugd_debug("Valid CPU hotplug configuration detected.\n");
}
if (cfg.cmm_max < 0 || cfg.cmm_min < 0 || cfg.memplug == NULL ||
cfg.memunplug == NULL || cfg.cmm_inc == NULL ||
cfg.cmm_max < cfg.cmm_min) {
cpuplugd_error("No valid memory hotplug configuration "
"detected.\n");
memory = 0;
} else {
memory = 1;
/*
* check if all the necessary files exit below /proc
*/
if (check_cmmfiles() != 0 && lpar_status == 0) {
cpuplugd_info("Can not open /proc/sys/vm/cmm_pages. "
"The memory hotplug function will be "
"disabled.\n");
memory = 0;
}
if (memory == 1 && lpar_status == 0)
cpuplugd_debug("Valid memory hotplug configuration "
"detected.\n");
if (memory == 1 && lpar_status == 1) {
cpuplugd_debug("Valid memory hotplug configuration "
"detected inside LPAR. "
"The memory hotplug function will be "
"disabled. \n");
memory = 0;
}
}
if (memory == 0 && cpu == 0)
cpuplugd_exit("Exiting, because neither a valid cpu nor a val"
"id memory hotplug configuration was found.\n");
/*
* Save the number of online cpus and the cmm_pagesize at startup,
* so that we can enable exactly the same amount when the daemon ends
*/
if (cpu) {
num_cpu_start = get_num_online_cpus();
cpuplugd_debug("Daemon started with %d active cpus.\n",
num_cpu_start);
/*
* Check that the initial number of cpus is not below the
* minimum
*/
if (num_cpu_start < cfg.cpu_min &&
get_numcpus() >= cfg.cpu_min) {
cpuplugd_debug("The number of online cpus is below "
"the minimum and will be increased.\n");
cpuid = 0;
while (get_num_online_cpus() < cfg.cpu_min &&
cpuid < get_numcpus()) {
if (is_online(cpuid) == 1) {
cpuid++;
continue;
}
cpuplugd_debug("cpu with id %d is currently offline "
"and will be enabled\n", cpuid);
hotplug(cpuid);
cpuid++;
}
}
if (get_num_online_cpus() > cfg.cpu_max) {
cpuplugd_debug("The number of online cpus is above the maximum"
" and will be decreased.\n");
cpuid = 0;
while (get_num_online_cpus() > cfg.cpu_max &&
cpuid < get_numcpus()) {
if (is_online(cpuid) != 1) {
cpuid++;
continue;
}
cpuplugd_debug("cpu with id %d is currently online "
"and will be disabled\n", cpuid);
hotunplug(cpuid);
cpuid++;
}
}
if (cfg.cpu_min > get_numcpus())
/*
* This check only works if nobody used the
* additional_cpus in the boot parameter section
*/
cpuplugd_exit("The minimum amount of cpus is above "
"the number of available cpus.\n"
"Detected %d available cpus\n",
get_numcpus());
if (get_num_online_cpus() < cfg.cpu_min)
cpuplugd_exit("Failed to set the number of online "
"cpus to the minimum. Aborting.\n");
}
if (memory == 1) {
/*
* Check that the initial value of cmm_pages is not below
* cmm_min or above cmm_max
*/
cmm_pagesize_start = get_cmmpages_size();
if (cmm_pagesize_start < cfg.cmm_min) {
cpuplugd_debug("cmm_pages is below minimum and will "
"be increased.\n");
set_cmm_pages(cfg.cmm_min);
}
if (cmm_pagesize_start > cfg.cmm_max) {
cpuplugd_debug("cmm_pages is above the maximum and will"
" be decreased.\n");
set_cmm_pages(cfg.cmm_max);
}
}
}

227
cpuplugd/cpu.c Normal file
View File

@@ -0,0 +1,227 @@
/*
* cpuplugd - Linux for System z Hotplug Daemon
*
* CPU hotplug functions
*
* Copyright IBM Corp. 2007, 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 <limits.h>
#include "cpuplugd.h"
/*
* Return overall number of available cpus. This does not necessarily
* mean that those are currently online
*/
int get_numcpus()
{
int i;
char path[PATH_MAX];
int number = 0;
for (i = 0; ; i++) {
/* check whether file exists and is readable */
sprintf(path, "/sys/devices/system/cpu/cpu%d/online", i);
if (access(path, R_OK) == 0)
number++;
else
break;
}
return number;
}
/*
* Return number of online cpus
*/
int get_num_online_cpus()
{
FILE *filp;
int i;
char path[PATH_MAX];
int status = 0;
int value_of_onlinefile, rc;
for (i = 0; i <= get_numcpus(); i++) {
/* check wether file exists and is readable */
sprintf(path, "/sys/devices/system/cpu/cpu%d/online", i);
if (access(path, R_OK) != 0)
continue;
filp = fopen(path, "r");
if (!filp)
cpuplugd_exit("Cannot open cpu online file: "
"%s\n", strerror(errno));
else {
rc = fscanf(filp, "%d", &value_of_onlinefile);
if (rc != 1)
cpuplugd_exit("Cannot read cpu online file: "
"%s\n", strerror(errno));
if (value_of_onlinefile == 1)
status++;
}
fclose(filp);
}
return status;
}
/*
* Enable a certain cpu
*/
int hotplug(int cpuid)
{
FILE *filp;
char path[PATH_MAX];
int status, rc;
sprintf(path, "/sys/devices/system/cpu/cpu%d/online", cpuid);
if (access(path, W_OK) == 0) {
filp = fopen(path, "w");
if (!filp)
cpuplugd_exit("Cannot open cpu online file: %s\n",
strerror(errno));
fprintf(filp, "1");
fclose(filp);
/*
* check if the attempt to enable the cpus really worked
*/
filp = fopen(path, "r");
rc = fscanf(filp, "%d", &status);
if (rc != 1)
cpuplugd_exit("Cannot open cpu online file: %s\n",
strerror(errno));
fclose(filp);
if (status == 1) {
cpuplugd_debug("cpu with id %d enabled\n", cpuid);
return 1;
} else {
cpuplugd_debug("failed to enable cpu with id %d\n",
cpuid);
return -1;
}
} else {
cpuplugd_error("hotplugging cpu with id %d failed\n", cpuid);
return -1;
}
return -1;
}
/*
* Disable a certain cpu
*/
int hotunplug(int cpuid)
{
FILE *filp;
int state, rc;
int retval = -1;
char path[PATH_MAX];
state = -1;
sprintf(path, "/sys/devices/system/cpu/cpu%d/online", cpuid);
if (access(path, W_OK) == 0) {
filp = fopen(path, "w");
fprintf(filp, "0");
fclose(filp);
/*
* Check if the attempt to enable the cpus really worked
*/
filp = fopen(path, "r");
rc = fscanf(filp, "%d", &state);
if (rc != 1)
cpuplugd_error("Failed to disable cpu with id %d\n",
cpuid);
fclose(filp);
if (state == 0)
return 1;
} else {
cpuplugd_error("unplugging cpu with id %d failed\n", cpuid);
}
return retval;
}
/*
* Check if a certain cpu is currently online
*/
int is_online(int cpuid)
{
FILE *filp;
int state;
int retval, rc;
char path[PATH_MAX];
retval = -1;
sprintf(path, "/sys/devices/system/cpu/cpu%d/online", cpuid);
if (access(path, R_OK) == 0) {
filp = fopen(path, "r");
rc = fscanf(filp, "%d", &state);
if (rc == 1) {
if (state == 1)
retval = 1;
if (state == 0)
retval = 0;
fclose(filp);
}
}
return retval;
}
/*
* Cleanup method. If the daemon is stopped, we (re)activate all cpus
*/
void reactivate_cpus()
{
/*
* Only enable the number of cpus which where
* available at daemon startup time
*/
int cpuid, nc;
cpuid = 0;
/* suppress verbose messages on exit */
debug = 0;
/*
* We check for num_cpu_start != 0 because we might want to
* clean up, before we queried for the number on cpus at
* startup
*/
if (num_cpu_start == 0)
return;
while (get_num_online_cpus() != num_cpu_start && cpuid < get_numcpus()) {
nc = get_num_online_cpus();
if (nc == num_cpu_start)
return;
if (nc > num_cpu_start && is_online(cpuid) == 1)
hotunplug(cpuid);
if (nc < num_cpu_start && is_online(cpuid) == 0)
hotplug(cpuid);
cpuid++;
}
}
/*
* In kernels > 2.6.24 cpus can be deconfigured. The following functions is used
* to check if a certain cpus is in a deconfigured state.
*/
int cpu_is_configured(int cpuid)
{
FILE *filp;
int retval, state, rc;
char path[4096];
retval = -1;
sprintf(path, "/sys/devices/system/cpu/cpu%d/configure", cpuid);
if (access(path, R_OK) == 0) {
filp = fopen(path, "r");
rc = fscanf(filp, "%d", &state);
if (rc == 1) {
if (state == 1)
retval = 1;
if (state == 0)
retval = 0;
fclose(filp);
}
}
return retval;
}

234
cpuplugd/cpuplugd.h Normal file
View File

@@ -0,0 +1,234 @@
/*
* cpuplugd - Linux for System z Hotplug Daemon
*
* Header file
*
* Copyright IBM Corp. 2007, 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 __USE_ISOC99
#define __USE_ISOC99
#endif
#include <ctype.h>
#include <errno.h>
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include "lib/util_base.h"
#include "lib/zt_common.h"
#define NAME "cpuplugd"
#define MAX_HISTORY 100
#define PIDFILE "/var/run/cpuplugd.pid"
#define LOCKFILE "/var/lock/cpuplugd.lock"
#define PROCINFO_LINE 512
#define CPUSTAT_SIZE 1024
#define VARINFO_SIZE 4096
#define MAX_VARNAME 128
#define MAX_LINESIZE 2048
#define CPUSTATS 10
/*
* Precedence of C operators
* full list:
* http://www.imada.sdu.dk/~svalle/
* courses/dm14-2005/mirror/c/_7193_tabular246.gif
*
* ()
* +-
* * /
* < >
* &
* |
*/
enum op_prio {
OP_PRIO_NONE,
OP_PRIO_OR,
OP_PRIO_AND,
/* greater and lower */
OP_PRIO_CMP,
OP_PRIO_ADD,
OP_PRIO_MULT
};
enum operation {
/* Leaf operators */
OP_SYMBOL_LOADAVG,
OP_SYMBOL_RUNABLE,
OP_SYMBOL_CPUS,
OP_SYMBOL_USER,
OP_SYMBOL_NICE,
OP_SYMBOL_SYSTEM,
OP_SYMBOL_IDLE,
OP_SYMBOL_IOWAIT,
OP_SYMBOL_IRQ,
OP_SYMBOL_SOFTIRQ,
OP_SYMBOL_STEAL,
OP_SYMBOL_GUEST,
OP_SYMBOL_GUEST_NICE,
OP_SYMBOL_APCR,
OP_SYMBOL_SWAPRATE,
OP_SYMBOL_FREEMEM,
OP_SYMBOL_MEMINFO,
OP_SYMBOL_VMSTAT,
OP_SYMBOL_CPUSTAT,
OP_SYMBOL_TIME,
OP_CONST,
/* Unary operators */
OP_NEG,
OP_NOT,
/* Binary operators */
OP_AND,
OP_OR,
OP_GREATER,
OP_LESSER,
OP_PLUS,
OP_MINUS,
OP_MULT,
OP_DIV,
/* ... */
/*Variables which are eligible within rules*/
VAR_LOAD, /* loadaverage */
VAR_RUN, /* number of runnable processes */
VAR_ONLINE /* number of online cpus */
};
struct symbols {
double loadavg;
double runnable_proc;
double onumcpus;
double idle;
double freemem;
double apcr;
double swaprate;
double user;
double nice;
double system;
double iowait;
double irq;
double softirq;
double steal;
double guest;
double guest_nice;
};
struct term {
enum operation op;
double value;
struct term *left, *right;
char *proc_name;
unsigned int index;
};
/*
* List of argurments taken fromt the configuration file
*
*/
struct config {
long cpu_max;
long cpu_min;
long update;
long cmm_max;
long cmm_min;
struct term *cmm_inc;
struct term *cmm_dec;
struct term *hotplug;
struct term *hotunplug;
struct term *memplug;
struct term *memunplug;
};
struct symbol_names {
char *name;
enum operation symop;
};
extern int foreground;
extern int debug;
extern char *configfile;
extern int debug; /* is verbose specified? */
extern int memory;
extern int cpu;
extern int num_cpu_start; /* # of online cpus at the time of the startup */
extern long cmm_pagesize_start; /* cmm_pageize at the time of daemon startup */
extern struct config cfg;
extern int reload_pending;
extern unsigned long meminfo_size;
extern unsigned long vmstat_size;
extern unsigned long cpustat_size;
extern unsigned long varinfo_size;
extern char *meminfo;
extern char *vmstat;
extern char *cpustat;
extern char *varinfo;
extern double *timestamps;
extern unsigned int history_max;
extern unsigned int history_current;
extern struct symbol_names sym_names[];
extern unsigned int sym_names_count;
int get_numcpus();
int get_num_online_cpus();
void get_loadavg_runnable(double *loadavg, double *runnable);
void clean_up();
void reactivate_cpus();
void parse_configfile(char *file);
void print_term(struct term *fn);
struct term *parse_term(char **p, enum op_prio prio);
int eval_term(struct term *fn, struct symbols *symbols);
double eval_double(struct term *fn, struct symbols *symbols);
double get_proc_value(char *procinfo, char *name, char separator);
void proc_read(char *procinfo, char *path, unsigned long size);
void proc_cpu_read(char *procinfo);
unsigned long proc_read_size(char *path);
char *get_var_rvalue(char *var_name);
void cleanup_cmm(void);
int hotplug(int cpuid);
int hotunplug(int cpuid);
int is_online(int cpuid);
long get_cmmpages_size();
void parse_options(int argc, char **argv);
void check_if_started_twice();
void store_pid(void);
void handle_signals(void);
void handle_sighup(void);
void reload_daemon(void);
int check_cmmfiles(void);
void check_config();
void set_cmm_pages(long size);
int check_lpar();
int cpu_is_configured(int cpuid);
void setup_history(void);
#define cpuplugd_info(fmt, ...) ({ \
if (foreground == 1) \
printf(fmt, ##__VA_ARGS__); \
if (foreground == 0) \
syslog(LOG_INFO, fmt, ##__VA_ARGS__); \
})
#define cpuplugd_error(fmt, ...) ({ \
if (foreground == 1) \
fprintf(stderr, fmt, ##__VA_ARGS__); \
if (foreground == 0) \
syslog(LOG_ERR, fmt, ##__VA_ARGS__); \
})
#define cpuplugd_debug(fmt, ...) ({ \
if (debug) \
cpuplugd_info(fmt, ##__VA_ARGS__); \
})
#define cpuplugd_exit(fmt, ...) ({ \
cpuplugd_error(fmt, ##__VA_ARGS__); \
clean_up(); \
})

232
cpuplugd/daemon.c Normal file
View File

@@ -0,0 +1,232 @@
/*
* cpuplugd - Linux for System z Hotplug Daemon
*
* Daemon functions
*
* Copyright IBM Corp. 2007, 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 "cpuplugd.h"
const char *name = NAME;
static const char *pid_file = PIDFILE;
const char *const usage =
"Usage: %s [OPTIONS]\n"
"\n"
"Daemon to dynamically hotplug cpus and memory based on a set of rules\n"
"Use OPTIONS described below.\n"
"\n"
"\t-c, --config CONFIGFILE Path to the configuration file\n"
"\t-f, --foreground Run in foreground, do not detach\n"
"\t-h, --help Print this help, then exit\n"
"\t-v, --version Print version information, then exit\n"
"\t-V, --verbose Provide more verbose output\n";
/*
* Print command usage
*/
void print_usage(int is_error, char program_name[])
{
fprintf(is_error ? stderr : stdout, usage, program_name);
exit(is_error ? 1 : 0);
}
/*
* Print command version
*/
void print_version()
{
printf("%s: Linux on System z CPU hotplug daemon version %s\n",
name, RELEASE_STRING);
printf("Copyright IBM Corp. 2007, 2017\n");
exit(0);
}
/*
* Store daemon's pid so it can be stopped
*/
void store_pid(void)
{
FILE *filp;
filp = fopen(pid_file, "w");
if (!filp) {
cpuplugd_error("cannot open pid file %s: %s\n", pid_file,
strerror(errno));
exit(1);
}
fprintf(filp, "%d\n", getpid());
fclose(filp);
}
/*
* Check that we don't try to start this daemon twice
*/
void check_if_started_twice()
{
FILE *filp;
int pid, rc;
filp = fopen(pid_file, "r");
if (filp) {
rc = fscanf(filp, "%d", &pid);
if (rc != 1) {
cpuplugd_error("Reading pid file failed. Aborting!\n");
exit(1);
}
cpuplugd_error("pid file %s still exists.\nThis might indicate "
"that an instance of this daemon is already "
"running.\n", pid_file);
exit(1);
}
}
/*
* Clean up method
*/
void clean_up()
{
cpuplugd_info("terminated\n");
remove(pid_file);
remove(LOCKFILE);
reactivate_cpus();
if (memory)
cleanup_cmm();
exit(1);
}
/*
* End the deamon
*/
void kill_daemon(int UNUSED(a))
{
cpuplugd_info("shutting down\n");
remove(pid_file);
remove(LOCKFILE);
reactivate_cpus();
if (memory)
cleanup_cmm();
exit(0);
}
/*
* Reload the daemon (for lsb compliance)
*/
void reload_handler(int UNUSED(a))
{
reload_pending = 1;
}
void reload_daemon()
{
unsigned int temp_history;
long temp_mem;
int temp_cpu;
cpuplugd_info("cpuplugd restarted\n");
/*
* Before we parse the configuration file again we have to save
* the original values prior to startup. If we don't do this cpuplugd
* will no longer know how many cpus the system had before the daemon
* was started and therefor can't restore theres in case it is stopped
*/
temp_cpu = num_cpu_start;
temp_mem = cmm_pagesize_start;
temp_history = history_max;
/* clear varinfo before re-reading variables from config file */
memset(varinfo, 0, varinfo_size);
history_max = 1;
parse_configfile(configfile);
if (history_max > MAX_HISTORY)
cpuplugd_exit("History depth %i exceeded maximum (%i)\n",
history_max, MAX_HISTORY);
if (history_max != temp_history) {
free(meminfo);
free(vmstat);
free(cpustat);
free(timestamps);
setup_history();
}
check_config();
num_cpu_start = temp_cpu;
cmm_pagesize_start = temp_mem;
}
/*
* Set up for handling SIGTERM or SIGINT
*/
void handle_signals(void)
{
struct sigaction act;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
act.sa_handler = kill_daemon;
if (sigaction(SIGTERM, &act, NULL) < 0) {
cpuplugd_error("sigaction( SIGTERM, ... ) failed - reason %s\n",
strerror(errno));
exit(1);
}
if (sigaction(SIGINT, &act, NULL) < 0) {
cpuplugd_error("sigaction( SIGINT, ... ) failed - reason %s\n",
strerror(errno));
exit(1);
}
}
/*
* Signal handler for sighup. This is used to force the deamon to reload its
* configuration file.
* This feature is also required by a lsb compliant init script
*/
void handle_sighup(void)
{
struct sigaction act;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
act.sa_handler = reload_handler;
if (sigaction(SIGHUP, &act, NULL) < 0) {
cpuplugd_error("sigaction( SIGHUP, ... ) failed - reason %s\n",
strerror(errno));
exit(1);
}
}
/* Check if we are running in an LPAR environment.
* This functions return 1 if we run inside an lpar and 0 otherwise
*/
int check_lpar()
{
int rc;
FILE *filp;
size_t bytes_read;
char buffer[2048];
char *contains_vm;
rc = 0;
filp = fopen("/proc/cpuinfo", "r");
if (!filp)
cpuplugd_exit("cannot open /proc/cpuinfo: %s\n",
strerror(errno));
bytes_read = fread(buffer, 1, sizeof(buffer) - 1, filp);
if (bytes_read == 0)
cpuplugd_exit("Reading /proc/cpuinfo failed: %s\n",
strerror(errno));
/* NUL-terminate the text */
buffer[bytes_read] = '\0';
contains_vm = strstr(buffer, "version = FF");
if (contains_vm == NULL) {
rc = 1;
cpuplugd_debug("Detected System running in LPAR mode\n");
} else
cpuplugd_debug("Detected System running in z/VM mode\n");
fclose(filp);
return rc;
}

100
cpuplugd/getopt.c Normal file
View File

@@ -0,0 +1,100 @@
/*
* cpuplugd - Linux for System z Hotplug Daemon
*
* Command line parsing
*
* Copyright IBM Corp. 2007, 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 <getopt.h>
#include "cpuplugd.h"
void print_usage(int is_error, char program_name[]);
void print_version();
int foreground;
int debug;
char *configfile;
int cpu_idle_limit;
void parse_options(int argc, char **argv)
{
int config_file_specified = -1;
const struct option long_options[] = {
{ "help", no_argument, NULL, 'h'},
{ "foreground", no_argument, NULL, 'f' },
{ "config", required_argument, NULL, 'c' },
{ "version", no_argument, NULL, 'v' },
{ "verbose", no_argument, NULL, 'V' },
{ NULL, 0, NULL, 0}
};
/* dont run without any argument */
if (argc == 0 || argc == 1)
print_usage(0, argv[0]);
while (optind < argc) {
int index = -1;
struct option *opt = 0;
int result = getopt_long(argc, argv, "hfc:vVm",
long_options, &index);
if (result == -1)
break; /* end of list */
switch (result) {
case 'h':
print_usage(0, argv[0]);
break;
case 'f':
foreground = 1;
break;
case 'c':
/*
* This prevents -cbla and enforces the
* user to specify -c bla
*/
if (strcmp(argv[optind-1], optarg) == 0) {
configfile = optarg;
config_file_specified = 1;
} else {
cpuplugd_error("Unrecognized option: %s\n",
optarg);
exit(1);
}
break;
case 'v':
print_version();
break;
case 'V':
debug = 1;
break;
case 0:
/* all parameter that do not appear in the optstring */
opt = (struct option *)&(long_options[index]);
printf("'%s' was specified.",
opt->name);
if (opt->has_arg == required_argument)
printf("Arg: <%s>", optarg);
printf("\n");
break;
case '?':
printf("Try '%s' --help' for more information.\n",
argv[0]);
exit(1);
break;
case -1:
/*
* We also run in this case if no argument was
* specified
*/
break;
default:
print_usage(0, argv[0]);
}
}
if (config_file_specified == -1) {
printf("You have to specify a configuration file!\n");
printf("Try '%s' --help' for more information.\n", argv[0]);
exit(1);
}
}

157
cpuplugd/info.c Normal file
View File

@@ -0,0 +1,157 @@
/*
* cpuplugd - Linux for System z Hotplug Daemon
*
* /proc info functions
*
* Copyright IBM Corp. 2007, 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 <sys/stat.h>
#include <sys/types.h>
#include "cpuplugd.h"
/*
* Return current load average and runnable processes based on /proc/loadavg
*
* Example: 0.20 0.18 0.12 1/80 11206
*
* The first three columns measure CPU utilization of the last 1, 5,
* and 15 minute periods.
* The fourth column shows the number of currently running processes
* and the total number of processes.
* The last column displays the last process ID used.
*/
void get_loadavg_runnable(double *loadavg, double *runnable)
{
FILE *filp;
double dummy;
int rc;
filp = fopen("/proc/loadavg", "r");
if (!filp)
cpuplugd_exit("cannot open kernel loadaverage "
"statistics: %s\n", strerror(errno));
rc = fscanf(filp, "%lf %lf %lf %lf/", loadavg, &dummy, &dummy,
runnable);
if (rc != 4)
cpuplugd_exit("cannot parse kernel loadaverage "
"statistics: %s\n", strerror(errno));
fclose(filp);
return;
}
void proc_cpu_read(char *procinfo)
{
FILE *filp;
unsigned int rc, onumcpus;
unsigned long user, nice, system, idle, iowait, irq, softirq, steal,
guest, guest_nice, total_ticks;
double loadavg, runnable;
guest = guest_nice = 0; /* set to 0 if not present in kernel */
filp = fopen("/proc/stat", "r");
if (!filp)
cpuplugd_exit("/proc/stat open failed: %s\n", strerror(errno));
rc = fscanf(filp, "cpu %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld", &user,
&nice, &system, &idle, &iowait, &irq, &softirq, &steal,
&guest, &guest_nice);
get_loadavg_runnable(&loadavg, &runnable);
onumcpus = get_num_online_cpus();
total_ticks = user + nice + system + idle + iowait + irq + softirq +
steal + guest + guest_nice;
rc = snprintf(procinfo, cpustat_size, "onumcpus %d\nloadavg %f\n"
"runnable_proc %f\nuser %ld\nnice %ld\nsystem %ld\n"
"idle %ld\niowait %ld\nirq %ld\nsoftirq %ld\nsteal %ld\n"
"guest %ld\nguest_nice %ld\ntotal_ticks %ld\n",
onumcpus, loadavg, runnable, user, nice, system, idle,
iowait, irq, softirq, steal, guest, guest_nice,
total_ticks);
if (rc >= cpustat_size)
cpuplugd_exit("cpustat buffer too small: need %d, have %ld "
"(bytes)\n", rc, cpustat_size);
fclose(filp);
return;
}
void proc_read(char *procinfo, char *path, unsigned long size)
{
size_t bytes_read;
FILE *filp;
filp = fopen(path, "r");
if (!filp)
cpuplugd_exit("%s open failed: %s\n", path, strerror(errno));
bytes_read = fread(procinfo, 1, size, filp);
if (bytes_read == 0)
cpuplugd_exit("%s read failed\n", path);
if (bytes_read == size)
cpuplugd_exit("procinfo buffer too small for %s\n", path);
procinfo[bytes_read] = '\0';
fclose(filp);
return;
}
unsigned long proc_read_size(char *path)
{
FILE *filp;
char buf[PROCINFO_LINE];
char *linep, *linep_offset;
unsigned long size;
filp = fopen(path, "r");
if (!filp)
cpuplugd_exit("%s open failed: %s\n", path, strerror(errno));
size = 0;
while ((linep = fgets(buf, sizeof(buf), filp))) {
if (!(linep_offset = strchr(linep, '\n')))
cpuplugd_exit("buf too small for line\n");
size = size + linep_offset - linep + 1;
}
fclose(filp);
return size;
}
double get_proc_value(char *procinfo, char *name, char separator)
{
char buf[PROCINFO_LINE];
char *proc_offset;
unsigned long proc_length, name_length;
double value;
int found;
value = -1;
found = 0;
name_length = strlen(name);
while ((proc_offset = strchr(procinfo, separator))) {
proc_length = proc_offset - procinfo;
/*
* proc_read_size() made sure that proc_length < PROCINFO_LINE
*/
memcpy(buf, procinfo, proc_length);
buf[proc_length] = '\0';
procinfo = proc_offset + 1;
if (strncmp(buf, name, MAX(proc_length, name_length)) == 0) {
errno = 0;
value = strtod(procinfo, NULL);
if (errno)
cpuplugd_exit("strtod failed\n");
found = 1;
break;
}
proc_offset = strchr(procinfo, '\n');
procinfo = proc_offset + 1;
}
if (!found)
cpuplugd_exit("Symbol %s not found, check your config file\n",
name);
return value;
}

485
cpuplugd/main.c Normal file
View File

@@ -0,0 +1,485 @@
/*
* cpuplugd - Linux for System z Hotplug Daemon
*
* Main functions
*
* Copyright IBM Corp. 2007, 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 <fcntl.h>
#include <fenv.h>
#include <sys/file.h>
#include <sys/time.h>
#include <time.h>
#include "cpuplugd.h"
struct symbol_names sym_names[] = {
{ "loadavg", OP_SYMBOL_LOADAVG },
{ "runnable_proc", OP_SYMBOL_RUNABLE },
{ "onumcpus", OP_SYMBOL_CPUS },
{ "user", OP_SYMBOL_USER },
{ "nice", OP_SYMBOL_NICE },
{ "system", OP_SYMBOL_SYSTEM },
{ "idle", OP_SYMBOL_IDLE },
{ "iowait", OP_SYMBOL_IOWAIT },
{ "irq", OP_SYMBOL_IRQ },
{ "softirq", OP_SYMBOL_SOFTIRQ },
{ "steal", OP_SYMBOL_STEAL },
{ "guest_nice", OP_SYMBOL_GUEST_NICE },
{ "guest", OP_SYMBOL_GUEST },
{ "swaprate", OP_SYMBOL_SWAPRATE },
{ "apcr", OP_SYMBOL_APCR },
{ "freemem", OP_SYMBOL_FREEMEM },
{ "meminfo.", OP_SYMBOL_MEMINFO },
{ "vmstat.", OP_SYMBOL_VMSTAT },
{ "cpustat.", OP_SYMBOL_CPUSTAT },
{ "time", OP_SYMBOL_TIME },
};
struct config cfg = {
.cpu_max = -1,
.cpu_min = -1,
.update = -1,
.cmm_min = -1,
.cmm_max = -1,
.cmm_inc = NULL,
.cmm_dec = NULL,
.memplug = NULL,
.memunplug = NULL,
.hotplug = NULL,
.hotunplug = NULL,
};
int num_cpu_start, memory, cpu, reload_pending;
long cmm_pagesize_start;
unsigned long meminfo_size, vmstat_size, cpustat_size, varinfo_size;
char *meminfo, *vmstat, *cpustat, *varinfo;
double *timestamps;
unsigned int history_max, history_current, history_prev, sym_names_count;
static struct symbols symbols;
static jmp_buf jmpenv;
static struct sigaction act;
/*
* Handle the sigfpe signal which we might catch during rule evaluating
*/
static void sigfpe_handler(int UNUSED(sig))
{
longjmp(jmpenv, 1);
}
static void eval_cpu_rules(void)
{
double diffs[CPUSTATS], diffs_total, percent_factor;
char *procinfo_current, *procinfo_prev;
int cpu, nr_cpus, on_off;
nr_cpus = get_numcpus();
procinfo_current = cpustat + history_current * cpustat_size;
procinfo_prev = cpustat + history_prev * cpustat_size;
diffs[0] = get_proc_value(procinfo_current, "user", ' ') -
get_proc_value(procinfo_prev, "user", ' ');
diffs[1] = get_proc_value(procinfo_current, "nice", ' ') -
get_proc_value(procinfo_prev, "nice", ' ');
diffs[2] = get_proc_value(procinfo_current, "system", ' ') -
get_proc_value(procinfo_prev, "system", ' ');
diffs[3] = get_proc_value(procinfo_current, "idle", ' ') -
get_proc_value(procinfo_prev, "idle", ' ');
diffs[4] = get_proc_value(procinfo_current, "iowait", ' ') -
get_proc_value(procinfo_prev, "iowait", ' ');
diffs[5] = get_proc_value(procinfo_current, "irq", ' ') -
get_proc_value(procinfo_prev, "irq", ' ');
diffs[6] = get_proc_value(procinfo_current, "softirq", ' ') -
get_proc_value(procinfo_prev, "softirq", ' ');
diffs[7] = get_proc_value(procinfo_current, "steal", ' ') -
get_proc_value(procinfo_prev, "steal", ' ');
diffs[8] = get_proc_value(procinfo_current, "guest", ' ') -
get_proc_value(procinfo_prev, "guest", ' ');
diffs[9] = get_proc_value(procinfo_current, "guest_nice", ' ') -
get_proc_value(procinfo_prev, "guest_nice", ' ');
diffs_total = get_proc_value(procinfo_current, "total_ticks", ' ') -
get_proc_value(procinfo_prev, "total_ticks", ' ');
if (diffs_total == 0)
diffs_total = 1;
symbols.loadavg = get_proc_value(procinfo_current, "loadavg", ' ');
symbols.runnable_proc = get_proc_value(procinfo_current,
"runnable_proc", ' ');
symbols.onumcpus = get_proc_value(procinfo_current, "onumcpus", ' ');
percent_factor = 100 * symbols.onumcpus;
symbols.user = (diffs[0] / diffs_total) * percent_factor;
symbols.nice = (diffs[1] / diffs_total) * percent_factor;
symbols.system = (diffs[2] / diffs_total) * percent_factor;
symbols.idle = (diffs[3] / diffs_total) * percent_factor;
symbols.iowait = (diffs[4] / diffs_total) * percent_factor;
symbols.irq = (diffs[5] / diffs_total) * percent_factor;
symbols.softirq = (diffs[6] / diffs_total) * percent_factor;
symbols.steal = (diffs[7] / diffs_total) * percent_factor;
symbols.guest = (diffs[8] / diffs_total) * percent_factor;
symbols.guest_nice = (diffs[9] / diffs_total) * percent_factor;
/* only use this for development and testing */
cpuplugd_debug("cpustat values:\n%s", cpustat + history_current *
cpustat_size);
if (debug && foreground == 1) {
printf("-------------------- CPU --------------------\n");
printf("cpu_min: %ld\n", cfg.cpu_min);
printf("cpu_max: %ld\n", cfg.cpu_max);
printf("loadavg: %f \n", symbols.loadavg);
printf("user percent = %f\n", symbols.user);
printf("nice percent = %f\n", symbols.nice);
printf("system percent = %f\n", symbols.system);
printf("idle percent = %f\n", symbols.idle);
printf("iowait percent = %f\n", symbols.iowait);
printf("irq percent = %f\n", symbols.irq);
printf("softirq percent = %f\n", symbols.softirq);
printf("steal percent = %f\n", symbols.steal);
printf("guest percent = %f\n", symbols.guest);
printf("guest_nice percent = %f\n", symbols.guest_nice);
printf("numcpus %d\n", nr_cpus);
printf("runnable_proc: %d\n", (int) symbols.runnable_proc);
printf("---------------------------------------------\n");
printf("onumcpus: %d\n", (int) symbols.onumcpus);
printf("---------------------------------------------\n");
printf("hotplug: ");
print_term(cfg.hotplug);
printf("\n");
printf("hotunplug: ");
print_term(cfg.hotunplug);
printf("\n");
printf("---------------------------------------------\n");
}
on_off = 0;
/* Evaluate the hotplug rule */
if (eval_term(cfg.hotplug, &symbols))
on_off++;
/* Evaluate the hotunplug rule only if hotplug did not match */
else if (eval_term(cfg.hotunplug, &symbols))
on_off--;
if (on_off > 0) {
/* check the cpu nr limit */
if (symbols.onumcpus + 1 > cfg.cpu_max) {
/* cpu limit reached */
cpuplugd_debug("maximum cpu limit is reached\n");
return;
}
/* try to find a offline cpu */
for (cpu = 0; cpu < nr_cpus; cpu++)
if (is_online(cpu) == 0 && cpu_is_configured(cpu) != 0)
break;
if (cpu < nr_cpus) {
cpuplugd_debug("cpu with id %d is currently offline "
"and will be enabled\n", cpu);
if (hotplug(cpu) == -1)
cpuplugd_debug("unable to find a cpu which "
"can be enabled\n");
} else {
/*
* In case we tried to enable a cpu but this failed.
* This is the case if a cpu is deconfigured
*/
cpuplugd_debug("unable to find a cpu which can "
"be enabled\n");
}
} else if (on_off < 0) {
/* check cpu nr limit */
if (symbols.onumcpus <= cfg.cpu_min) {
cpuplugd_debug("minimum cpu limit is reached\n");
return;
}
/* try to find a online cpu */
for (cpu = get_numcpus() - 1; cpu >= 0; cpu--) {
if (is_online(cpu) != 0)
break;
}
if (cpu > 0) {
cpuplugd_debug("cpu with id %d is currently online "
"and will be disabled\n", cpu);
hotunplug(cpu);
}
}
}
static void eval_mem_rules(double interval)
{
long cmmpages_size, cmm_inc, cmm_dec, cmm_new;
double free_memory, swaprate, apcr;
char *procinfo_current, *procinfo_prev;
procinfo_current = meminfo + history_current * meminfo_size;
free_memory = get_proc_value(procinfo_current, "MemFree", ':');
procinfo_current = vmstat + history_current * vmstat_size;
procinfo_prev = vmstat + history_prev * vmstat_size;
swaprate = (get_proc_value(procinfo_current, "pswpin", ' ') +
get_proc_value(procinfo_current, "pswpout", ' ') -
get_proc_value(procinfo_prev, "pswpin", ' ') -
get_proc_value(procinfo_prev, "pswpout", ' ')) /
interval;
apcr = (get_proc_value(procinfo_current, "pgpgin", ' ') +
get_proc_value(procinfo_current, "pgpgout", ' ') -
get_proc_value(procinfo_prev, "pgpgin", ' ') -
get_proc_value(procinfo_prev, "pgpgout", ' ')) /
interval;
cmmpages_size = get_cmmpages_size();
symbols.apcr = apcr; // apcr in 512 byte blocks / sec
symbols.swaprate = swaprate; // swaprate in 4K pages / sec
symbols.freemem = free_memory / 1024; // freemem in MB
cmm_inc = eval_double(cfg.cmm_inc, &symbols);
/* cmm_dec is optional */
if (cfg.cmm_dec)
cmm_dec = eval_double(cfg.cmm_dec, &symbols);
else
cmm_dec = cmm_inc;
/* only use this for development and testing */
if (debug && foreground == 1) {
printf("------------------- Memory ------------------\n");
printf("cmm_min: %ld\n", cfg.cmm_min);
printf("cmm_max: %ld\n", cfg.cmm_max);
printf("swaprate: %f\n", symbols.swaprate);
printf("apcr: %f\n", symbols.apcr);
printf("cmm_inc: %ld = ", cmm_inc);
print_term(cfg.cmm_inc);
printf("\n");
printf("cmm_dec: %ld = ", cmm_dec);
if (cfg.cmm_dec)
print_term(cfg.cmm_dec);
else
print_term(cfg.cmm_inc);
printf("\n");
printf("free memory: %f MB\n", symbols.freemem);
printf("---------------------------------------------\n");
printf("cmm_pages: %ld\n", cmmpages_size);
printf("---------------------------------------------\n");
printf("memplug: ");
print_term(cfg.memplug);
printf("\n");
printf("memunplug: ");
print_term(cfg.memunplug);
printf("\n");
printf("---------------------------------------------\n");
}
cmm_new = cmmpages_size;
/* Evaluate the memplug rule */
if (eval_term(cfg.memplug, &symbols)) {
if (cmm_dec < 0) {
cpuplugd_error("cmm_dec went negative (%ld), set it "
"to 0.\n", cmm_dec);
cmm_dec = 0;
}
cmm_new -= cmm_dec;
/* Evaluate the memunplug rule only if memplug did not match */
} else if (eval_term(cfg.memunplug, &symbols)) {
if (cmm_inc < 0) {
cpuplugd_error("cmm_inc went negative (%ld), set it "
"to 0.\n", cmm_inc);
cmm_inc = 0;
}
cmm_new += cmm_inc;
}
if (cmm_new < cfg.cmm_min) {
cpuplugd_debug("minimum memory limit is reached\n");
cmm_new = cfg.cmm_min;
}
if (cmm_new > cfg.cmm_max) {
cpuplugd_debug("maximum memory limit is reached\n");
cmm_new = cfg.cmm_max;
}
if (cmm_new != cmmpages_size)
set_cmm_pages(cmm_new);
}
static void time_read(double *timestamps)
{
struct timeval tv;
int rc;
cpuplugd_debug("\n==================== New interval "
"====================\n");
rc = gettimeofday(&tv, NULL);
if (!rc) {
*timestamps = tv.tv_sec + (double) tv.tv_usec / 1000000;
cpuplugd_debug("Timestamp: %s (%f seconds since "
"the Epoch)\n", ctime(&tv.tv_sec), *timestamps);
} else
cpuplugd_exit("gettimeofday failed: %s\n", strerror(errno));
return;
}
void setup_history()
{
/*
* The /proc file size will vary during intervals, use double of current
* size to have enough buffer for growing values.
*/
meminfo_size = proc_read_size("/proc/meminfo") * 2;
vmstat_size = proc_read_size("/proc/vmstat") * 2;
cpustat_size = CPUSTAT_SIZE;
meminfo = malloc(meminfo_size * (history_max + 1));
if (!meminfo)
cpuplugd_exit("Out of memory: meminfo\n");
vmstat = malloc(vmstat_size * (history_max + 1));
if (!vmstat)
cpuplugd_exit("Out of memory: vmstat\n");
cpustat = malloc(cpustat_size * (history_max + 1));
if (!cpustat)
cpuplugd_exit("Out of memory: cpustat\n");
timestamps = malloc(sizeof(double) * (history_max + 1));
if (!timestamps)
cpuplugd_exit("Out of memory: timestamps\n");
/*
* Read history data, at least 1 interval for swaprate, apcr, idle, etc.
*/
history_current = 0;
cpuplugd_info("Waiting %i intervals to accumulate history.\n",
history_max);
do {
time_read(&timestamps[history_current]);
proc_read(meminfo + history_current * meminfo_size,
"/proc/meminfo", meminfo_size);
proc_read(vmstat + history_current * vmstat_size,
"/proc/vmstat", vmstat_size);
proc_cpu_read(cpustat + history_current * cpustat_size);
sleep(cfg.update);
history_current++;
} while (history_current < history_max);
history_current--;
}
int main(int argc, char *argv[])
{
double interval;
int fd, rc;
reload_pending = 0;
sym_names_count = sizeof(sym_names) / sizeof(struct symbol_names);
varinfo_size = VARINFO_SIZE;
varinfo = calloc(varinfo_size, 1);
if (!varinfo) {
cpuplugd_error("Out of memory: varinfo\n");
exit(1);
}
/*
* varinfo must start with '\n' for correct string matching
* in get_var_rvalue().
*/
varinfo[0] = '\n';
/* Parse the command line options */
parse_options(argc, argv);
/* flock() lock file to prevent multiple instances of cpuplugd */
fd = open(LOCKFILE, O_CREAT | O_RDONLY, S_IRUSR);
if (fd == -1) {
cpuplugd_error("Cannot open lock file %s: %s\n", LOCKFILE,
strerror(errno));
exit(1);
}
rc = flock(fd, LOCK_EX | LOCK_NB);
if (rc) {
cpuplugd_error("flock() failed on lock file %s: %s\nThis might "
"indicate that an instance of this daemon is "
"already running.\n", LOCKFILE, strerror(errno));
exit(1);
}
/* Make sure that the daemon is not started multiple times */
check_if_started_twice();
/* Store daemon pid also in foreground mode */
handle_signals();
handle_sighup();
/* Need 1 history level minimum for internal symbols */
history_max = 1;
/*
* Parse arguments from the configuration file, also calculate
* history_max
*/
parse_configfile(configfile);
if (history_max > MAX_HISTORY)
cpuplugd_exit("History depth %i exceeded maximum (%i)\n",
history_max, MAX_HISTORY);
/* Check the settings in the configuration file */
check_config();
if (!foreground) {
rc = daemon(1, 0);
if (rc < 0)
cpuplugd_exit("Detach from terminal failed: %s\n",
strerror(errno));
}
/* Store daemon pid */
store_pid();
/* Unlock lock file */
flock(fd, LOCK_UN);
close(fd);
/* Install signal handler for floating point exceptions */
rc = feenableexcept(FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW |
FE_INVALID);
act.sa_flags = SA_NODEFER;
sigemptyset(&act.sa_mask);
act.sa_handler = sigfpe_handler;
if (sigaction(SIGFPE, &act, NULL) < 0)
cpuplugd_exit("sigaction( SIGFPE, ... ) failed - reason %s\n",
strerror(errno));
setup_history();
/* Main loop */
while (1) {
if (reload_pending) { // check for daemon reload
reload_daemon();
reload_pending = 0;
}
history_prev = history_current;
history_current = (history_current + 1) % (history_max + 1);
time_read(&timestamps[history_current]);
proc_read(meminfo + history_current * meminfo_size,
"/proc/meminfo", meminfo_size);
proc_read(vmstat + history_current * vmstat_size,
"/proc/vmstat", vmstat_size);
proc_cpu_read(cpustat + history_current * cpustat_size);
interval = timestamps[history_current] -
timestamps[history_prev];
cpuplugd_debug("config update interval: %ld seconds\n",
cfg.update);
cpuplugd_debug("real update interval: %f seconds\n", interval);
/* Run code that may signal failure via longjmp. */
if (cpu == 1) {
if (setjmp(jmpenv) == 0)
eval_cpu_rules();
else
cpuplugd_error("Floating point exception, "
"skipping cpu rule "
"evaluation.\n");
}
if (memory == 1) {
if (setjmp(jmpenv) == 0)
eval_mem_rules(interval);
else
cpuplugd_error("Floating point exception, "
"skipping memory rule "
"evaluation.\n");
}
sleep(cfg.update);
}
return 0;
}

64
cpuplugd/man/cpuplugd.8 Normal file
View File

@@ -0,0 +1,64 @@
.\" 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 CPUPLUGD 8 "May 2011" "s390-tools"
.
.SH NAME
cpuplugd \- Linux on System z CPU and memory hotplug daemon
.
.SH SYNOPSIS
.B cpuplugd
.RI [ OPTIONS ]
.
.SH DESCRIPTION
The cpuplugd daemon dynamically enables and disables CPUs and increases or
decreases the cooperative memory management (CMM) page pool based on a set of
rules.
When the daemon is stopped, the size of the CMM page pool and the number
of active CPUs are reset to the values they had before the cpuplugd was started.
This program can be used to control the number of CPUs for Linux on z/VM
and for Linux in LPAR mode. The memory hotplug feature (CMM page pool) applies
to Linux on z/VM only.
.
.SH OPTIONS
.TP
\fB\-c\fP or \fB\-\-config\fP \fI<configuration file>\fP
Specify the absolute path to the configuration file. This option is mandatory.
The default configuration file can be found in /etc/cpuplugd.conf.
.
.TP
\fB\-f\fP or \fB\-\-foreground\fP
Run in the foreground and not as daemon. If this option is
omitted, the program runs in the background.
.
.TP
\fB\-h\fP or \fB\-\-help\fP
Print usage message and exit.
.
.TP
\fB\-v\fP or \fB\-\-version\fP
Print Version information and exit.
.
.TP
\fB\-V\fP or \fB\-\-verbose\fP
Print verbose messages to stdout (when running in foreground)
or to syslog otherwise.
This options is mainly used for debugging purposes.
.
.SH EXAMPLES
To test a setup start cpuplugd in foreground mode using verbose output:
.br
.RS 4
cpuplugd \-V \-f \-c /etc/cpuplugd.conf
.RE
For daemon mode, start cpuplugd from an init script as follows:
.br
.RS 4
cpuplugd \-c /etc/cpuplugd.conf
.RE
.SH SEE ALSO
.BR cpuplugd.conf (5)

View File

@@ -0,0 +1,284 @@
.\" 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 CPUPLUGD.CONF 5 "May 2011" "s390-tools"
.
.SH NAME
cpuplugd.conf \- Configuration file for the Linux on System z CPU and memory
hotplug daemon
.
.SH DESCRIPTION
The cpuplugd.conf configuration file contains the configuration information for
the Linux for System z CPU and memory hotplug daemon.
Use this file to specify rules for enabling or disabling CPUs and for adding
or removing memory.
.
.SS "CPU hotplug"
CPUs can be enabled and disabled through a sysfs interface.
The status file for a CPU, here CPU number 16 (counting starts at 0),
is /sys/devices/system/cpu/cpu15/online.
Writing a 0 to this file disables the CPU. Writing a 1 enables the CPU.
.
.SS "Memory hotplug"
The rules that add or remove memory use the cooperative memory management
(CMM) feature.
CMM is a mechanism to reduce the memory available to Linux instances that run
as guests of z/VM.
CMM allocates pages to a dynamic page pool not available to Linux.
A diagnose code indicates to z/VM that the pages in the page pool are out of
use. z/VM can then immediately reuse these pages for other guests.
.
.SS "Layout of the configuration file"
The configuration file contains variables specifying static numbers or
expressions. They are of the format \fB<variable>="<value>"\fP and they
need to be specified within one line. Expressions can be specified
to calculate algebraic values or to define boolean rules, which determine
when a hotplug/hotunplug action should be taken. The maximum valid line
length is 2048 characters.
There are case-insensitive pre-defined and case-sensitive user-defined
variables. The configuration file must include specifications for all
pre-defined variables. If a variable is not set, the hotplug function it
applies to, CPU or memory, is disabled.
The only exception to this rule is CMM_DEC, which defaults to the setting
for CMM_INC if omitted. If a pre-defined variable is set more than once, only
the last occurrence is used. User-defined variables must not be set more than
once.
.
.SS "Hotplug rules"
Set these pre-defined variables to an expression that resolves to a boolean
value (true or false). These variables trigger hotplug actions. Setting a
variable to "0" disables the action.
.
.RS 2
.IP "-" 2
\fBHOTPLUG\fP - used to enable CPUs
.IP "-" 2
\fBHOTUNPLUG\fP - used to disable CPUs
.IP "-" 2
\fBMEMPLUG\fP - used to increase the available memory
.IP "-" 2
\fBMEMUNPLUG\fP - used to decrease the amount of memory
.RE
.PP
The following operators can be used in a hotplug rule expression:
.br
.RS 2
.B + * ( ) / - < >
.RE
.br
Furthermore, the boolean operators \fB & \fP (and) \fB|\fP (or) and \fB!\fP
(not) can be used.
If both HOTPLUG and HOTUNPLUG evaluate to true, only the HOTPLUG action is
triggered. If both MEMPLUG and MEMUNPLUG evaluate to true, only the MEMPLUG
action is triggered.
.
.SS "Pre-defined static variables"
The following pre-defined variables can be set only to a static, positive,
numeric value:
.
.RS 2
.IP "-" 2
\fBCPU_MIN\fP - the minimum number of CPUs to keep online (> 0)
.IP "-" 2
\fBCPU_MAX\fP - the maximum number of CPUs to enable (>= 0)
.IP "-" 2
\fBUPDATE\fP - the interval at which cpuplugd evaluates the rules (in seconds,
> 0)
.IP "-" 2
\fBCMM_MIN\fP - the minimum size of the CMM page pool (>= 0)
.IP "-" 2
\fBCMM_MAX\fP - the maximum size of the CMM page pool (>= 0)
.RE
.PP
If the value of CPU_MAX is 0, the overall number of CPUs found in this system
is used as the maximum.
.
.SS "Pre-defined dynamic variables"
The following pre-defined variables can either be set to a static value or to an
algebraic expression:
.
.RS 2
.IP "-" 2
\fBCMM_INC\fP - the amount of pages by which the CMM page pool is increased
if the MEMUNPLUG rule is matched (available system memory is decreased).
.IP "-" 2
\fBCMM_DEC\fP - the amount of pages by which the CMM page pool is decreased
if the MEMPLUG rule is matched (available system memory is increased).
.RE
.PP
The following operators can be used in a dynamic variable expression:
.br
.RS 2
.B + * ( ) / - < >
.RE
.br
.
.SS "User-defined variables"
You can specify complex calculations as user-defined variables, which can then
be used in expressions. User-defined variables are case-sensitive and must not
match a pre-defined variable or keyword. In the configuration file, definitions
for user-defined variables must precede their use in expressions.
Variable names consist of alphanumeric characters (a-z,A-Z,0-9) and
the "_" character, see section \fB"EXAMPLES"\fP for an example (pgscanrate). The
maximum name
length for a variable is 128 characters, and the maximum total size for all
user-defined variables (names + values) is 4096 characters.
.
.SS "Keywords for CPU hotplug rules"
The \fBHOTPLUG\fP and \fBHOTUNPLUG\fP rules can contain the following
pre-defined keywords:
.
.RS 2
.IP "-" 2
\fBloadavg\fP - the current load average
.IP "-" 2
\fBonumcpus\fP - the current number of CPUs which are online
.IP "-" 2
\fBrunnable_proc\fP - the current amount of runnable processes
.IP "-" 2
\fBuser\fP - the current CPU user percentage
.IP "-" 2
\fBnice\fP - the current CPU nice percentage
.IP "-" 2
\fBsystem\fP - the current CPU system percentage
.IP "-" 2
\fBidle\fP - the current CPU idle percentage
.IP "-" 2
\fBiowait\fP - the current CPU iowait percentage
.IP "-" 2
\fBirq\fP - the current CPU irq percentage
.IP "-" 2
\fBsoftirq\fP - the current CPU softirq percentage
.IP "-" 2
\fBsteal\fP - the current CPU steal percentage
.IP "-" 2
\fBguest\fP - the current CPU guest percentage (depends on kernel version: if not reported in /proc/stat, this is set to 0)
.IP "-" 2
\fBguest_nice\fP - the current CPU guest_nice percentage (depends on kernel version: if not reported in /proc/stat, this is set to 0)
.IP "-" 2
\fBcpustat.<name>\fP - data from /proc/stat and /proc/loadavg
.IP "-" 2
\fBtime\fP - floating point timestamp in "seconds.microseconds" since the Unix
Epoch (1970-01-01 00:00:00 +0000 (UTC))
.RE
.PP
The percentage values are accumulated over all online CPUs, so they can vary
between 0 and (100 * \fBonumcpus\fP).
CPU usage data from /proc/stat and /proc/loadavg is accessible by
specifying \fBcpustat.<name>\fP, where \fB<name>\fP can be any of the keywords
described above, plus \fBtotal_ticks\fP. In this case, \fBloadavg\fP,
\fBonumcpus\fP and \fBrunnable_proc\fP
provide the same values as the pre-defined keywords, while the others refer
to the raw timer ticks as reported by /proc/stat, not the percentage.
For example, \fBcpustat.idle\fP reports the timer ticks spent in idle since
system start, and \fBcpustat.total_ticks\fP indicates the sum of all reported
timer ticks, which can be useful for user-defined percentage calculations.
.
.SS "Keywords for memory hotplug rules"
The \fBMEMPLUG\fP and \fBMEMUNPLUG\fP rules can contain the following
pre-defined keywords:
.
.RS 2
.IP "-" 2
\fBapcr\fP - the amount of page cache operations, i.e. pgpin + pgpout from
/proc/vmstat (in 512 byte blocks / second)
.IP "-" 2
\fBfreemem\fP - the amount of free memory (in megabytes)
.IP "-" 2
\fBswaprate\fP - the number of swap operations, i.e. pswpin + pswpout from
/proc/vmstat (in pages / second)
.IP "-" 2
\fBmeminfo.<name>\fP - any value from /proc/meminfo
.IP "-" 2
\fBvmstat.<name>\fP - any value from /proc/vmstat
.IP "-" 2
\fBtime\fP - floating point timestamp in "seconds.microseconds" since the Unix
Epoch (1970-01-01 00:00:00 +0000 (UTC))
.RE
.PP
All values from /proc/meminfo and /proc/vmstat can be used in an expression
by specifying \fBmeminfo.<name>\fP or \fBvmstat.<name>\fP, where \fB<name>\fP
matches a symbol name reported by /proc/meminfo or /proc/vmstat (case
sensitive), e.g. \fBmeminfo.MemTotal\fP.
.
.SS "History function"
There is a history function for the following keywords:
.
.RS 2
.IP "-" 2
\fBcpustat.<name>\fP - data from /proc/stat and /proc/loadavg
.IP "-" 2
\fBmeminfo.<name>\fP - any value from /proc/meminfo
.IP "-" 2
\fBvmstat.<name>\fP - any value from /proc/vmstat
.IP "-" 2
\fBtime\fP - floating point timestamp in "seconds.microseconds" since the Unix
Epoch (1970-01-01 00:00:00 +0000 (UTC))
.RE
.PP
The history levels can be accessed by appending \fB[<history level>]\fP to the
name, where \fB<history level>\fP indicates the amount of past intervals where
the value was gathered. [0] means the current interval (the [0] can be omitted
in this case), [1] means the previous interval, [2] means two intervals ago,
and so on. The history limit is 100. For example, \fBcpustat.system[1]\fP would
indicate the system value from /proc/stat at the previous interval, while
\fBvmstat.pgpgin\fP and \fBvmstat.pgpgin[0]\fP would both mean the current
pgpgin value from /proc/vmstat.
The \fBtime\fP keyword and its history values can be used to calculate values
dependent on time intervals, see section \fB"EXAMPLES"\fP for an example
(pgscanrate).
.
.SH EXAMPLES
A complete configuration file could look like this:
.nf
------------------------------ config file start ------------------------------
UPDATE="5"
CPU_MIN="2"
CPU_MAX="5"
CMM_MIN="0"
CMM_MAX="131072" # 512 MB
pgscan_k="vmstat.pgscan_kswapd_dma + vmstat.pgscan_kswapd_normal + vmstat.pgscan_kswapd_movable"
pgscan_d="vmstat.pgscan_direct_dma + vmstat.pgscan_direct_normal + vmstat.pgscan_direct_movable"
pgscan_k1="vmstat.pgscan_kswapd_dma[1] + vmstat.pgscan_kswapd_normal[1] + vmstat.pgscan_kswapd_movable[1]"
pgscan_d1="vmstat.pgscan_direct_dma[1] + vmstat.pgscan_direct_normal[1] + vmstat.pgscan_direct_movable[1]"
pgscanrate="(pgscan_k + pgscan_d - pgscan_k1 - pgscan_d1) / (time - time[1])"
cache="meminfo.Cached + meminfo.Buffers"
# CMM_INC: 10% of free memory + cache, in 4K pages
CMM_INC="(meminfo.MemFree + cache) / 40"
# CMM_DEC: 10% of total memory in 4K pages
CMM_DEC="meminfo.MemTotal / 40"
HOTPLUG = "(loadavg > onumcpus + 0.75) & (idle < 10.0)"
HOTUNPLUG = "(loadavg < onumcpus - 0.25) | (idle > 50)"
# Plug memory if page scan rate is above 20 pages / sec
MEMPLUG = "pgscanrate > 20"
# Unplug memory while free memory is above 10% of total memory, or cache uses
# more than 50% of total memory
MEMUNPLUG = "(meminfo.MemFree > meminfo.MemTotal / 10) | (cache > meminfo.MemTotal / 2)"
------------------------------ config file end ------------------------------
.fi
The example includes multiple user-defined variables to calculate the page scan
rate with values from /proc/vmstat, as well as the cache size.
\fBAttention:\fP Do not use these example rules on production systems. The
rules have been designed to illustrate the configuration file syntax and are
not suitable for actually governing hotplug actions. Useful rules differ
considerably depending on the workload, resources, and requirements of the
system they are designed for.
.
.SH SEE ALSO
.BR cpuplugd (8)

82
cpuplugd/mem.c Normal file
View File

@@ -0,0 +1,82 @@
/*
* cpuplugd - Linux for System z Hotplug Daemon
*
* cmm functions
*
* Copyright IBM Corp. 2007, 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 "cpuplugd.h"
/*
* The cmm_pages value defines the size of the balloon of blocked memory.
* Increasing the value is removing memory from Linux, which is an memunplug.
* Decreasing the value is adding memory back to Linux, which is memplug.
*/
/*
* Set the value of cmm_pages
*/
void set_cmm_pages(long pages)
{
FILE *filp;
filp = fopen("/proc/sys/vm/cmm_pages", "w");
if (!filp)
cpuplugd_exit("Cannot open /proc/sys/vm/cmmpages: %s\n",
strerror(errno));
cpuplugd_debug("changing number of pages permanently reserved to %ld\n",
pages);
fprintf(filp, "%ld\n", pages);
fclose(filp);
return;
}
/*
* Read number of pages permanently reserved
*/
long get_cmmpages_size()
{
FILE *filp;
long size;
int rc;
filp = fopen("/proc/sys/vm/cmm_pages", "r");
if (!filp)
cpuplugd_exit("Cannot open /proc/sys/vm/cmm_pages: %s\n",
strerror(errno));
rc = fscanf(filp, "%ld", &size);
if (rc == 0)
cpuplugd_exit("Can not read /proc/sys/vm/cmm_pages: %s\n",
strerror(errno));
fclose(filp);
return size;
}
/*
* Reset cmm pagesize to value we found prior to daemon startup
*/
void cleanup_cmm()
{
set_cmm_pages(cmm_pagesize_start);
return;
}
/*
* Function to check if the cmm kernel module is loaded and the required
* files below /proc exit
*/
int check_cmmfiles(void)
{
FILE *filp;
filp = fopen("/proc/sys/vm/cmm_pages", "r");
if (!filp)
return -1;
fclose(filp);
return 0;
}

550
cpuplugd/terms.c Normal file
View File

@@ -0,0 +1,550 @@
/*
* cpuplugd - Linux for System z Hotplug Daemon
*
* Term parsing
*
* Copyright IBM Corp. 2007, 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 "cpuplugd.h"
static enum op_prio op_prio_table[] =
{
[OP_NEG] = OP_PRIO_ADD,
[OP_GREATER] = OP_PRIO_CMP,
[OP_LESSER] = OP_PRIO_CMP,
[OP_PLUS] = OP_PRIO_ADD,
[OP_MINUS] = OP_PRIO_ADD,
[OP_MULT] = OP_PRIO_MULT,
[OP_DIV] = OP_PRIO_MULT,
[OP_AND] = OP_PRIO_AND,
[OP_OR] = OP_PRIO_OR,
};
static void free_term(struct term *fn)
{
if (!fn)
return;
switch (fn->op) {
case OP_SYMBOL_LOADAVG:
case OP_SYMBOL_RUNABLE:
case OP_SYMBOL_CPUS:
case OP_SYMBOL_USER:
case OP_SYMBOL_NICE:
case OP_SYMBOL_SYSTEM:
case OP_SYMBOL_IDLE:
case OP_SYMBOL_IOWAIT:
case OP_SYMBOL_IRQ:
case OP_SYMBOL_SOFTIRQ:
case OP_SYMBOL_STEAL:
case OP_SYMBOL_GUEST:
case OP_SYMBOL_GUEST_NICE:
case OP_CONST:
free(fn);
break;
case OP_NEG:
case OP_NOT:
free_term(fn->left);
free(fn);
break;
case OP_GREATER:
case OP_LESSER:
case OP_PLUS:
case OP_MINUS:
case OP_MULT:
case OP_DIV:
case OP_AND:
free_term(fn->left);
free_term(fn->right);
free(fn);
break;
case OP_OR:
free_term(fn->left);
free_term(fn->right);
free(fn);
break;
default:
break;
}
}
void print_term(struct term *fn)
{
switch (fn->op) {
case OP_SYMBOL_LOADAVG:
printf("loadavg");
break;
case OP_SYMBOL_RUNABLE:
printf("runnable_proc");
break;
case OP_SYMBOL_CPUS:
printf("onumcpus");
break;
case OP_SYMBOL_USER:
printf("user");
break;
case OP_SYMBOL_NICE:
printf("nice");
break;
case OP_SYMBOL_SYSTEM:
printf("system");
break;
case OP_SYMBOL_IDLE:
printf("idle");
break;
case OP_SYMBOL_IOWAIT:
printf("iowait");
break;
case OP_SYMBOL_IRQ:
printf("irq");
break;
case OP_SYMBOL_SOFTIRQ:
printf("softirq");
break;
case OP_SYMBOL_STEAL:
printf("steal");
break;
case OP_SYMBOL_GUEST:
printf("guest");
break;
case OP_SYMBOL_GUEST_NICE:
printf("guest_nice");
break;
case OP_SYMBOL_SWAPRATE:
printf("swaprate");
break;
case OP_SYMBOL_FREEMEM:
printf("freemem");
break;
case OP_SYMBOL_APCR:
printf("apcr");
break;
case OP_SYMBOL_MEMINFO:
printf("meminfo.%s[%u]", fn->proc_name, fn->index);
break;
case OP_SYMBOL_VMSTAT:
printf("vmstat.%s[%u]", fn->proc_name, fn->index);
break;
case OP_SYMBOL_CPUSTAT:
printf("cpustat.%s[%u]", fn->proc_name, fn->index);
break;
case OP_SYMBOL_TIME:
printf("time[%u]", fn->index);
break;
case OP_CONST:
printf("%f", fn->value);
break;
case OP_NEG:
printf("-(");
print_term(fn->left);
printf(")");
break;
case OP_NOT:
printf("!(");
print_term(fn->left);
printf(")");
break;
case OP_PLUS:
case OP_MINUS:
case OP_MULT:
case OP_DIV:
case OP_AND:
case OP_OR:
case OP_GREATER:
case OP_LESSER:
printf("(");
print_term(fn->left);
switch (fn->op) {
case OP_AND:
printf(") & (");
break;
case OP_OR:
printf(") | (");
break;
case OP_GREATER:
printf(") > (");
break;
case OP_LESSER:
printf(") < (");
break;
case OP_PLUS:
printf(") + (");
break;
case OP_MINUS:
printf(") - (");
break;
case OP_MULT:
printf(") * (");
break;
case OP_DIV:
printf(") / (");
break;
// TODO OP_CONST, OP_SYMBOL_LOADAVG, ... possible here???
case OP_CONST:
printf("%f", fn->value);
break;
case OP_SYMBOL_LOADAVG:
case OP_SYMBOL_RUNABLE:
case OP_SYMBOL_CPUS:
case OP_SYMBOL_USER:
case OP_SYMBOL_NICE:
case OP_SYMBOL_SYSTEM:
case OP_SYMBOL_IDLE:
case OP_SYMBOL_IOWAIT:
case OP_SYMBOL_IRQ:
case OP_SYMBOL_SOFTIRQ:
case OP_SYMBOL_STEAL:
case OP_SYMBOL_GUEST:
case OP_SYMBOL_GUEST_NICE:
case OP_SYMBOL_APCR:
case OP_SYMBOL_SWAPRATE:
case OP_SYMBOL_FREEMEM:
case OP_SYMBOL_MEMINFO: // TODO use default: ???
case OP_SYMBOL_VMSTAT: // TODO use default: ???
case OP_SYMBOL_CPUSTAT: // TODO use default: ???
case OP_SYMBOL_TIME: // TODO use default: ???
case OP_NEG:
case OP_NOT:
case VAR_LOAD:
case VAR_RUN:
case VAR_ONLINE:
break;
}
print_term(fn->right);
printf(")");
break;
case VAR_LOAD:
case VAR_RUN:
case VAR_ONLINE:
break;
}
}
static struct term *parse_var_term(char **p)
{
char *s, *var_rvalue;
struct term *fn;
unsigned int length;
char var_name[MAX_VARNAME + 1];
s = *p;
length = 0;
fn = NULL;
while (isalnum(*s) || *s == '_') {
var_name[length] = *s;
length++;
s++;
if (length > MAX_VARNAME)
cpuplugd_exit("Variable name too long (max. length is "
"%i chars): %s\n", MAX_VARNAME, *p);
}
var_name[length] = '\0';
var_rvalue = get_var_rvalue(var_name);
if (var_rvalue) {
fn = parse_term(&var_rvalue, OP_PRIO_NONE);
if (var_rvalue[0] != '\n')
cpuplugd_exit("parsing error at %s, position: %s\n",
var_name, var_rvalue);
*p = s;
}
return fn;
}
struct term *parse_term(char **p, enum op_prio prio)
{
struct term *fn, *new;
enum operation op;
char *s, *endptr;
double value;
unsigned int length, i, index;
s = *p;
fn = NULL;
if (*s == '-') {
s++;
fn = malloc(sizeof(struct term));
if (fn == NULL)
goto out_error;
if (isdigit(*s)) {
value = 0;
length = 0;
sscanf(s, "%lf%n", &value, &length);
fn->op = OP_CONST;
fn->value = -value;
s += length;
} else {
fn->op = OP_NEG;
fn->left = parse_term(&s, prio);
if (fn->left == NULL)
goto out_error;
}
} else if (*s == '!') {
s++;
fn = malloc(sizeof(struct term));
if (fn == NULL)
goto out_error;
fn->op = OP_NOT;
fn->left = parse_term(&s, prio);
if (fn->left == NULL)
goto out_error;
} else if (isdigit(*s)) {
value = 0;
length = 0;
sscanf(s, "%lf%n", &value, &length);
for (i = 0; i < length; i++)
s++;
fn = malloc(sizeof(struct term));
if (fn == NULL)
goto out_error;
fn->op = OP_CONST;
fn->value = value;
} else if (*s == '(') {
s++;
fn = parse_term(&s, OP_PRIO_NONE);
if (fn == NULL || *s != ')')
goto out_error;
s++;
} else {
/* Check for variable name */
fn = parse_var_term(&s);
if (fn == NULL) {
for (i = 0; i < sym_names_count; i++)
if (strncmp(s, sym_names[i].name,
strlen(sym_names[i].name)) == 0)
break;
if (i >= sym_names_count)
/* Term doesn't make sense. */
goto out_error;
/*
* Parse meminfo/vmstat/cpustat with optional history
* index [x]
*/
fn = malloc(sizeof(struct term));
if (fn == NULL)
goto out_error;
fn->op = sym_names[i].symop;
s += strlen(sym_names[i].name);
length = 0;
if (fn->op == OP_SYMBOL_MEMINFO ||
fn->op == OP_SYMBOL_VMSTAT ||
fn->op == OP_SYMBOL_CPUSTAT) {
while (isalpha(s[length]) || s[length] == '_')
length++;
fn->proc_name = malloc(length + 1);
if (fn->proc_name == NULL)
goto out_error;
strncpy(fn->proc_name, s, length);
fn->proc_name[length] = '\0';
}
if (fn->op == OP_SYMBOL_MEMINFO ||
fn->op == OP_SYMBOL_VMSTAT ||
fn->op == OP_SYMBOL_CPUSTAT ||
fn->op == OP_SYMBOL_TIME) {
if (s[length] == '[') {
length++;
if (!isdigit(s[length]))
goto out_error;
index = strtol(s + length, &endptr, 10);
length = endptr - s;
if (s[length] != ']')
goto out_error;
fn->index = index;
if (history_max < index)
history_max = index;
length++;
}
s += length;
}
}
}
while (1) {
switch (*s) {
case '>':
op = OP_GREATER;
break;
case '<':
op = OP_LESSER;
break;
case '+':
op = OP_PLUS;
break;
case '-':
op = OP_MINUS;
break;
case '*':
op = OP_MULT;
break;
case '/':
op = OP_DIV;
break;
case '|':
op = OP_OR;
break;
case '&':
op = OP_AND;
break;
default:
goto out;
}
if (prio >= op_prio_table[op])
break;
s++;
new = malloc(sizeof(struct term));
new->op = op;
new->left = fn;
if (new == NULL)
goto out_error;
new->right = parse_term(&s, op_prio_table[op]);
if (new->right == NULL) {
free(new);
goto out_error;
}
fn = new;
}
out:
*p = s;
return fn;
out_error:
if (fn)
free_term(fn);
return NULL;
}
static double get_value(struct term *fn)
{
double value = 0;
char *procinfo;
unsigned int history_index;
if (fn->index <= history_current)
history_index = history_current - fn->index;
else
history_index = history_max + 1 - (fn->index - history_current);
switch (fn->op) {
case OP_SYMBOL_MEMINFO:
procinfo = meminfo + history_index * meminfo_size;
value = get_proc_value(procinfo, fn->proc_name, ':');
break;
case OP_SYMBOL_VMSTAT:
procinfo = vmstat + history_index * vmstat_size;
value = get_proc_value(procinfo, fn->proc_name, ' ');
break;
case OP_SYMBOL_CPUSTAT:
procinfo = cpustat + history_index * cpustat_size;
value = get_proc_value(procinfo, fn->proc_name, ' ');
break;
case OP_SYMBOL_TIME:
value = timestamps[history_index];
break;
default:
cpuplugd_exit("Invalid term specified: %i\n", fn->op);
}
return value;
}
double eval_double(struct term *fn, struct symbols *symbols)
{
double a, b, sum;
switch (fn->op) {
case OP_SYMBOL_LOADAVG:
return symbols->loadavg;
case OP_SYMBOL_RUNABLE:
return symbols->runnable_proc;
case OP_SYMBOL_CPUS:
return symbols->onumcpus;
case OP_SYMBOL_USER:
return symbols->user;
case OP_SYMBOL_NICE:
return symbols->nice;
case OP_SYMBOL_SYSTEM:
return symbols->system;
case OP_SYMBOL_IDLE:
return symbols->idle;
case OP_SYMBOL_IOWAIT:
return symbols->iowait;
case OP_SYMBOL_IRQ:
return symbols->irq;
case OP_SYMBOL_SOFTIRQ:
return symbols->softirq;
case OP_SYMBOL_STEAL:
return symbols->steal;
case OP_SYMBOL_GUEST:
return symbols->guest;
case OP_SYMBOL_GUEST_NICE:
return symbols->guest_nice;
case OP_SYMBOL_FREEMEM:
return symbols->freemem;
case OP_SYMBOL_APCR:
return symbols->apcr;
case OP_SYMBOL_SWAPRATE:
return symbols->swaprate;
case OP_SYMBOL_MEMINFO:
case OP_SYMBOL_VMSTAT:
case OP_SYMBOL_CPUSTAT:
case OP_SYMBOL_TIME:
return get_value(fn);
case OP_CONST:
return fn->value;
case OP_NEG:
return -eval_double(fn->left, symbols);
case OP_PLUS:
return eval_double(fn->left, symbols) +
eval_double(fn->right, symbols);
case OP_MINUS:
return eval_double(fn->left, symbols) -
eval_double(fn->right, symbols);
case OP_MULT:
a = eval_double(fn->left, symbols);
b = eval_double(fn->right, symbols);
sum = a*b;
return sum;
/*return eval_double(fn->left, symbols) *
eval_double(fn->right, symbols);*/
case OP_DIV:
a = eval_double(fn->left, symbols);
b = eval_double(fn->right, symbols);
sum = a/b;
return sum;
/*return eval_double(fn->left, symbols) /
eval_double(fn->right, symbols); */
case OP_NOT:
case OP_AND:
case OP_OR:
case OP_GREATER:
case OP_LESSER:
case VAR_LOAD:
case VAR_RUN:
case VAR_ONLINE:
cpuplugd_exit("Invalid term specified: %i\n", fn->op);
}
return 0;
}
int eval_term(struct term *fn, struct symbols *symbols)
{
if (fn == NULL || symbols == NULL)
return 0.0;
switch (fn->op) {
case OP_NOT:
return !eval_term(fn->left, symbols);
case OP_OR:
return eval_term(fn->left, symbols) == 1 ||
eval_term(fn->right, symbols) == 1;
case OP_AND:
return eval_term(fn->left, symbols) == 1 &&
eval_term(fn->right, symbols) == 1;
case OP_GREATER:
return eval_double(fn->left, symbols) >
eval_double(fn->right, symbols);
case OP_LESSER:
return eval_double(fn->left, symbols) <
eval_double(fn->right, symbols);
default:
return eval_double(fn, symbols) != 0.0;
}
}

21
dasdfmt/Makefile Normal file
View File

@@ -0,0 +1,21 @@
include ../common.mak
all: dasdfmt
libs = $(rootdir)/libdasd/libdasd.a \
$(rootdir)/libvtoc/libvtoc.a \
$(rootdir)/libu2s/libu2s.a \
$(rootdir)/libutil/libutil.a
dasdfmt: dasdfmt.o $(libs)
install: all
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man8
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 dasdfmt $(DESTDIR)$(BINDIR)
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 dasdfmt.8 \
$(DESTDIR)$(MANDIR)/man8
clean:
rm -f *.o *~ dasdfmt core
.PHONY: all install clean

195
dasdfmt/dasdfmt.8 Normal file
View File

@@ -0,0 +1,195 @@
.\" 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 DASDFMT 8 "Apr 2006" "s390-tools"
.SH NAME
dasdfmt \- formatting of DASD (ECKD) disk drives.
.SH SYNOPSIS
\fBdasdfmt\fR [-h] [-t] [-v] [-y] [-p] [-P] [-m \fIstep\fR]
.br
[-r \fIcylinder\fR] [-b \fIblksize\fR] [-l \fIvolser\fR] [-d \fIlayout\fR]
.br
[-L] [-V] [-F] [-k] [-C] [-M \fImode\fR] \fIdevice\fR
.SH DESCRIPTION
\fBdasdfmt\fR formats a DASD (ECKD) disk drive to prepare it
for usage with Linux for S/390.
The \fIdevice\fR is the node of the device (e.g. '/dev/dasda').
Any device node created by udev for kernel 2.6 can be used
(e.g. '/dev/dasd/0.0.b100/disc').
.br
\fBWARNING\fR: Careless usage of \fBdasdfmt\fR can result in
\fBLOSS OF DATA\fR.
.SH OPTIONS
.TP
\fB-h\fR or \fB--help\fR
Print usage and exit.
.TP
\fB-t\fR or \fB--test\fR
Disables any modification of the disk drive.
.br
\fBdasdfmt\fR just prints
out, what it \fBwould\fR do.
.TP
\fB-v\fR
Increases verbosity.
.TP
\fB-y\fR
Start formatting without further user-confirmation.
.TP
\fB--norecordzero\fR
Remove permission for subsystem to format write record zero.
.br
This is an expert option: Per default in recent dasd drivers, subsystems are
granted the permission to format write record zero. This option is used
to remove this permission.
.br
.TP
\fB-L\fR or \fB--no_label\fR
Omit the writing of a disk label after formatting.
.br
This makes only sense for the 'ldl' disk layout.
.br
The '-L' option has to be specified after the '-d ldl' option.
.br
e.g. dasdfmt -d ldl -L /dev/...
.TP
\fB-V\fR or \fB--version\fR
Print version number and exit.
.TP
\fB-F\fR or \fB--force\fR
Formats the device without performing sanity checking.
.TP
\fB-C\fR or \fB--check_host_count\fR
Force dasdfmt to check the host access open count to ensure the device
is not online on another operating system instance
.TP
\fB-d\fR \fIlayout\fR or \fB--disk_layout\fR=\fIlayout\fR
Formats the device with compatible disk layout or linux disk layout.
\fIlayout\fR is either \fIcdl\fR for the compatible disk layout
(default) or \fIldl\fR for the linux disk layout.
.br
Compatible disk layout means a special handling of the
first two tracks of the volume. This enables other S/390 or zSeries
operating systems to access this device (e.g. for backup purposes).
.TP
\fB-p\fR or \fB--progressbar\fR
Print a progress bar while formatting.
Do not use this option if you are using a 3270 console,
running in background or redirecting the output to a file.
.TP
\fB-P\fR or \fB--percentage\fR
Print one line for each formatted cylinder showing the number of the
cylinder and percentage of formatting process.
Intended to be used by higher level interfaces.
.TP
\fB-m\fR \fIstep\fR or \fB--hashmarks\fR=\fIstep\fR
Print a hashmark every \fIstep\fR cylinders. The value \fIstep\fR has to be within range [1,1000], otherwise it will be set to the default, which is 10.
.br
You can use this option to see the progress of formatting in case you
are not able to use the progress bar option -p, e.g. with a 3270
terminal.
.br
The value will be at least as big as the -r or --requestsize value.
.br
.TP
\fB-M\fR \fImode\fR or \fB--mode\fR=\fImode\fR
Specify the \fImode\fR to be used to format the device. Valid modes are:
.RS
.IP full
Format the entire disk with the specified blocksize. (default)
.IP quick
Format the first two tracks and write label and partition information. Only use
this option if you are sure that the target DASD already contains a regular
format with the specified blocksize. A blocksize can optionally be specified
using \fB-b\fR (\fB--blocksize\fR).
.IP expand
Format all unformatted tracks at the end of the target DASD. This mode assumes
that tracks at the beginning of the DASD volume have already been correctly
formatted, while a consecutive set of tracks at the end are unformatted. You can
use this mode to make added space available for Linux use after dynamically
increasing the size of a DASD volume. A blocksize can optionally be specified
using \fB-b\fR (\fB--blocksize\fR).
.RE
.TP
\fB--check\fR
Perform a complete format check on a DASD volume. A blocksize can be specified
with \fB-b\fR (\fB--blocksize\fR).
.TP
\fB-r\fR \fIcylindercount\fR or \fB--requestsize\fR=\fIcylindercount\fR
Number of cylinders to be processed in one formatting step.
The value must be an integer in the range 1 - 255.
.br
Use this parameter to exploit any available PAV devices.
The number of cylinders optimally matches the number of associated
devices, counting the base device and all alias devices.
.br
.TP
\fB-b\fR \fIblksize\fR or \fB--blocksize\fR=\fIblksize\fR
Specify blocksize to be used. \fIblksize\fR must be a positive integer
and always be a power of two. The recommended blocksize is 4096 bytes.
.TP
\fB-l\fR \fIvolser\fR or \fB--label\fR=\fIvolser\fR
Specify the volume serial number or volume identifier to be written
to disk after formatting. If no label is specified, a sensible default
is used. \fIvolser\fR is interpreted as ASCII string and is automatically
converted to uppercase and then to EBCDIC.
.br
e.g. -l LNX001 or --label=DASD01
.br
The \fIvolser\fR identifies by serial number the volume. A volume serial
number is 1 through 6 alphanumeric or one of the following special
characters: $, #, @, %. Enclose a serial number that contains special
characters in apostrophes. If the number is shorter than six
characters, it is padded with trailing blanks.
.br
Do not code a volume serial number as SCRTCH, PRIVAT, or Lnnnnn (L with
five numbers); these are used in OS/390 messages to ask the operator to
mount a volume. Do not code a volume serial number as MIGRAT, which is
used by the OS/390 Hierarchical Storage Manager DFSMShsm for migrated
data sets.
.br
NOTE: Try to avoid using special characters in the volume serial. This may cause problems accessing a disk by volser.
.br
In case you really have to use special characters, make sure you are using quotes. In addition there is a special handling for the '$' sign. Please specify it using '\\$' if necessary.
.br
e.g. -l 'a@b\\$c#' to get A@B$C#
.br
.TP
\fB-k\fR or \fB--keep_volser\fR
Keeps the Volume Serial Number, when writing the Volume Label. This is
useful, if the Serial Number has been written with a VM Tool and should not
be overwritten.
.br
.SH SEE ALSO
.BR fdasd (8)

1713
dasdfmt/dasdfmt.c Normal file

File diff suppressed because it is too large Load Diff

350
dasdfmt/dasdfmt.h Normal file
View File

@@ -0,0 +1,350 @@
/*
* dasdfmt - Format DASD ECKD devices for use by Linux
*
* Copyright IBM Corp. 2002, 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 DASDFMT_H
#define DASDFMT_H
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <limits.h>
#include <limits.h>
#include <mntent.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
/****************************************************************************
* SECTION: Definition needed for DASD-API (see dasd.h) *
****************************************************************************/
#define DASD_IOCTL_LETTER 'D'
/*
* struct dasd_information2_t
* represents any data about the device, which is visible to userspace.
* including format and features.
*/
typedef struct dasd_information2_t {
unsigned int devno; /* S/390 devno */
unsigned int real_devno; /* for aliases */
unsigned int schid; /* S/390 subchannel identifier */
unsigned int cu_type : 16; /* from SenseID */
unsigned int cu_model : 8; /* from SenseID */
unsigned int dev_type : 16; /* from SenseID */
unsigned int dev_model : 8; /* from SenseID */
unsigned int open_count;
unsigned int req_queue_len;
unsigned int chanq_len; /* length of chanq */
char type[4]; /* from discipline.name, */
/* 'none' for unknown */
unsigned int status; /* current device level */
unsigned int label_block; /* where to find the VOLSER */
unsigned int FBA_layout; /* fixed block size (like AIXVOL) */
unsigned int characteristics_size;
unsigned int confdata_size;
char characteristics[64]; /* from read_device_characteristics */
char configuration_data[256]; /* from read_configuration_data */
unsigned int format; /* format info like formatted/cdl/ldl/... */
unsigned int features; /* dasd features like 'ro',... */
unsigned int reserved0; /* reserved for further use ,... */
unsigned int reserved1; /* reserved for further use ,... */
unsigned int reserved2; /* reserved for further use ,... */
unsigned int reserved3; /* reserved for further use ,... */
unsigned int reserved4; /* reserved for further use ,... */
unsigned int reserved5; /* reserved for further use ,... */
unsigned int reserved6; /* reserved for further use ,... */
unsigned int reserved7; /* reserved for further use ,... */
} dasd_information2_t;
struct dasd_eckd_characteristics {
unsigned short cu_type;
struct {
unsigned char support:2;
unsigned char async:1;
unsigned char reserved:1;
unsigned char cache_info:1;
unsigned char model:3;
} __attribute__ ((packed)) cu_model;
unsigned short dev_type;
unsigned char dev_model;
struct {
unsigned char mult_burst:1;
unsigned char RT_in_LR:1;
unsigned char reserved1:1;
unsigned char RD_IN_LR:1;
unsigned char reserved2:4;
unsigned char reserved3:8;
unsigned char defect_wr:1;
unsigned char XRC_supported:1;
unsigned char reserved4:1;
unsigned char striping:1;
unsigned char reserved5:4;
unsigned char cfw:1;
unsigned char reserved6:2;
unsigned char cache:1;
unsigned char dual_copy:1;
unsigned char dfw:1;
unsigned char reset_alleg:1;
unsigned char sense_down:1;
} __attribute__ ((packed)) facilities;
unsigned char dev_class;
unsigned char unit_type;
unsigned short no_cyl;
unsigned short trk_per_cyl;
unsigned char sec_per_trk;
unsigned char byte_per_track[3];
unsigned short home_bytes;
unsigned char formula;
union {
struct {
unsigned char f1;
unsigned short f2;
unsigned short f3;
} __attribute__ ((packed)) f_0x01;
struct {
unsigned char f1;
unsigned char f2;
unsigned char f3;
unsigned char f4;
unsigned char f5;
} __attribute__ ((packed)) f_0x02;
} __attribute__ ((packed)) factors;
unsigned short first_alt_trk;
unsigned short no_alt_trk;
unsigned short first_dia_trk;
unsigned short no_dia_trk;
unsigned short first_sup_trk;
unsigned short no_sup_trk;
unsigned char MDR_ID;
unsigned char OBR_ID;
unsigned char director;
unsigned char rd_trk_set;
unsigned short max_rec_zero;
unsigned char reserved1;
unsigned char RWANY_in_LR;
unsigned char factor6;
unsigned char factor7;
unsigned char factor8;
unsigned char reserved2[3];
unsigned char reserved3[6];
unsigned int long_no_cyl;
} __attribute__ ((packed));
/*
* Represents possible format modes that can be specified when formatting
* a DASD.
*/
typedef enum format_mode_t {
FULL, /* default mode */
QUICK, /* format only the first 2 tracks */
EXPAND, /* search for unformatted area and format only that part*/
} format_mode_t;
static const char mode_str[3][10] = {
"Full", "Quick", "Expand"
};
/*
* struct format_data_t
* represents all data necessary to format a dasd
*/
typedef struct format_data_t {
unsigned int start_unit; /* from track */
unsigned int stop_unit; /* to track */
unsigned int blksize; /* sectorsize */
unsigned int intensity;
} format_data_t;
/*
* values to be used for format_data_t.intensity
* 0/8: normal format
* 1/9: also write record zero
* 3/11: also write home address
* 4/12: invalidate track
*/
#define DASD_FMT_INT_FMT_R0 1 /* write record zero */
#define DASD_FMT_INT_FMT_HA 2 /* write home address, also set FMT_R0 ! */
#define DASD_FMT_INT_INVAL 4 /* invalidate tracks */
#define DASD_FMT_INT_COMPAT 8 /* use OS/390 compatible disk layout */
#define DASD_FMT_INT_FMT_NOR0 16 /* remove permission to write record zero */
/*
* struct format_check_t
* represents all data necessary to evaluate the format of
* different tracks of a dasd
*/
typedef struct format_check_t {
/* Input */
struct format_data_t expect;
/* Output */
unsigned int result; /* Error indication (DASD_FMT_ERR_*) */
unsigned int unit; /* Track that is in error */
unsigned int rec; /* Record that is in error */
unsigned int num_records; /* Records in the track in error */
unsigned int blksize; /* Block-size of first record in error */
unsigned int key_length; /* Key length of first record in error */
} format_check_t;
/*
* values to be used in format_check_t for indicating
* possible format errors
*/
#define DASD_FMT_ERR_TOO_FEW_RECORDS 1
#define DASD_FMT_ERR_TOO_MANY_RECORDS 2
#define DASD_FMT_ERR_BLKSIZE 3
#define DASD_FMT_ERR_RECORD_ID 4
#define DASD_FMT_ERR_KEY_LENGTH 5
/*
* values to be used for dasd_information2_t.format
* 0x00: NOT formatted
* 0x01: Linux disc layout
* 0x02: Common disc layout
*/
#define DASD_FORMAT_NONE 0
#define DASD_FORMAT_LDL 1
#define DASD_FORMAT_CDL 2
/* Disable the volume (for Linux) */
#define BIODASDDISABLE _IO(DASD_IOCTL_LETTER,0)
/* Enable the volume (for Linux) */
#define BIODASDENABLE _IO(DASD_IOCTL_LETTER,1)
/* Get information on a dasd device (enhanced) */
#define BIODASDINFO2 _IOR(DASD_IOCTL_LETTER, 3, dasd_information2_t)
/* #define BIODASDFORMAT _IOW(IOCTL_LETTER,0,format_data_t) , deprecated */
#define BIODASDFMT _IOW(DASD_IOCTL_LETTER,1,format_data_t)
/* Check device format according to format_data_t */
#define BIODASDCHECKFMT _IOWR(DASD_IOCTL_LETTER, 2, format_check_t)
/****************************************************************************
* SECTION: Further IOCTL Definitions (see fs.h and hdreq.h ) *
****************************************************************************/
/* re-read partition table */
#define BLKRRPART _IO(0x12,95)
/* get block device sector size */
#define BLKSSZGET _IO(0x12,104)
/* get read-only status (0 = read_write) */
#define BLKROGET _IO(0x12,94)
/* get device geometry */
#define HDIO_GETGEO 0x0301
/****************************************************************************
* SECTION: DASDFMT internal types *
****************************************************************************/
#define DASD_PARTN_BITS 2
#define PARTN_MASK ((1 << DASD_PARTN_BITS) - 1)
#define EXIT_MISUSE 1
#define EXIT_BUSY 2
#define LABEL_LENGTH 14
#define VLABEL_CHARS 84
#define LINE_LENGTH 80
#define ERR_LENGTH 90
#define DEFAULT_BLOCKSIZE 4096
/* requestsize - number of cylinders in one format step */
#define DEFAULT_REQUESTSIZE 10
#define USABLE_PARTITIONS ((1 << DASD_PARTN_BITS) - 1)
#define ERRMSG(x...) {fflush(stdout);fprintf(stderr,x);}
#define ERRMSG_EXIT(ec,x...) {fflush(stdout);fprintf(stderr,x);exit(ec);}
#define CHECK_SPEC_MAX_ONCE(i,str) \
{if (i>1) ERRMSG_EXIT(EXIT_MISUSE,"%s: " str " " \
"can only be specified once\n",prog_name);}
#define PARSE_PARAM_INTO(x,param,base,str) \
{char *endptr=NULL; x=(int)strtol(param,&endptr,base); \
if (*endptr) ERRMSG_EXIT(EXIT_MISUSE,"%s: " str " " \
"is in invalid format\n",prog_name);}
typedef struct bootstrap1 {
u_int32_t key;
u_int32_t data[6];
} __attribute__ ((packed)) bootstrap1_t;
typedef struct bootstrap2 {
u_int32_t key;
u_int32_t data[36];
} __attribute__ ((packed)) bootstrap2_t;
typedef struct dasdfmt_info {
dasd_information2_t dasd_info;
char devname[PATH_MAX];
int verbosity;
int testmode;
int withoutprompt;
int print_progressbar;
int print_hashmarks, hashstep;
int print_percentage;
int force;
int writenolabel;
int labelspec;
int cdl_format;
int blksize_specified;
int reqsize_specified;
int device_id;
int keep_volser;
int force_host;
int layout_specified;
int check;
} dasdfmt_info_t;
/*
C9D7D3F1 000A0000 0000000F 03000000 00000001 00000000 00000000
*/
static bootstrap1_t ipl1 = {
0xC9D7D3F1, {
0x000A0000, 0x0000000F, 0x03000000,
0x00000001, 0x00000000, 0x00000000
}
};
/*
C9D7D3F2 07003AB8 40000006 31003ABE 40000005 08003AA0 00000000 06000000
20000000 00000000 00000000 00000400 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000
*/
static bootstrap2_t ipl2 = {
0xC9D7D3F2, {
0x07003AB8, 0x40000006, 0x31003ABE,
0x40000005, 0x08003AA0, 0x00000000,
0x06000000, 0x20000000, 0x00000000,
0x00000000, 0x00000400, 0x00000000,
0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000
}
};
#endif /* DASDFMT_H */

20
dasdinfo/Makefile Normal file
View File

@@ -0,0 +1,20 @@
include ../common.mak
libs = $(rootdir)/libu2s/libu2s.a \
$(rootdir)/libutil/libutil.a
all: dasdinfo
dasdinfo: dasdinfo.o $(libs)
install: all
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) \
$(DESTDIR)$(MANDIR)/man8
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 dasdinfo $(DESTDIR)$(BINDIR)
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 dasdinfo.8 \
$(DESTDIR)$(MANDIR)/man8
clean:
rm -f *.o *~ dasdinfo core
.PHONY: all install clean

97
dasdinfo/dasdinfo.8 Normal file
View File

@@ -0,0 +1,97 @@
.\" 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 DASDINFO 8 "Febr 2007" "s390-tools" "Linux Administrator's Manual"
.SH NAME
.B "dasdinfo "
\- tool to read unique id from s390 DASD device
.SH SYNOPSIS
.BI "dasdinfo [-a] [-l] [-u] [-x] [-e] {-i " <busid>
.BI "| -b " <blockdev>
.BI " | -d " <devnode>
.BI "}"
.sp
.BI "dasdinfo [-h] [-v]"
.SH DESCRIPTION
.B dasdinfo
displays specific information about a specified DASD device.
It is normally called from a udev rule, to provide udev with a unique id string and
additional information (type, serial) for an S390 DASD drive. Udev can use this
information to create symlinks in /dev/disk/by-id and /dev/disk/by-label
to the real device node.
.SH OPTIONS
.TP
.BI "-a|--all"
Same as -u -x -l
.TP
.BI "-x|--extended-uid"
Print DASD uid
This option prints the full uid of the DASD. When z/VM provides two
virtual devices that are actually located on the same real device, the
first four tokens of the uid will be identical for both devices. z/VM
may provide an additional token that allows to distinguish between
different minidisks. You need both support in the Linux kernel and
z/VM to receive such an additional token.
For z/VM: VM support for the hypervisor injected Special Node Element
Qualifier (SNEQ) (or hypervisor injected self-description data) is
available by applying the PTFs for VM APAR VM64273 on z/VM 5.2.0 and higher.
.TP
.BI "-u|--uid"
Print DASD uid without z/VM minidisk token
z/VM may provide an additional token that allows to distinguish
between different minidisks (see --extended-uid option). To remain
compatibile with systems that were installed on older Linux or z/VM
levels, the -u option will print the uid excluding any z/VM-provided
minidisk token.
For example, if the extended uid is
IBM.75000000092461.e900.10.00000000000037400000000000000000 then the
uid is IBM.75000000092461.e900.10. If the extended uid contains no
minidisk token, e.g. in an LPAR environment, then both uids are the
same.
.TP
.BI "-l|--label"
Print DASD volume label (volser).
.TP
.BI "-i|--busid " <busid>
Use the bus ID as input parameter, e.g. 0.0.e910.
.TP
.BI "-b|--block " <blockdev>
Use the block device name as input parameter, e.g. dasdb.
.TP
.BI "-d|--devnode " <devnode>
Use a device node as input parameter, e.g. /dev/dasdb.
.TP
.BI "-e|--export"
Print all values (ID_BUS, ID_TYPE, ID_SERIAL).
.TP
.BI "-h|--help"
Print usage text.
.TP
.BI "-v|--version"
Print version number.
.SH EXAMPLES
dasdinfo -u -i 0.0.e910
dasdinfo -u -b dasdb
dasdinfo -u -d /dev/dasdb
All three examples should return the same unique ID for
the same DASD device, e.g. IBM.75000000092461.e900.10.
In case this uid is not available, dasdinfo will return
the volume label instead, e.g. 0XE910.
.SH SEE ALSO
.BR udev (7)
.SH AUTHORS
Volker Sameske <sameske@de.ibm.com>

837
dasdinfo/dasdinfo.c Normal file
View File

@@ -0,0 +1,837 @@
/*
* dasdinfo - Display unique DASD ID, either UID or volser
*
* Copyright IBM Corp. 2007, 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 <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <ftw.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <unistd.h>
#include "lib/util_base.h"
#include "lib/util_opt.h"
#include "lib/util_prg.h"
#include "lib/zt_common.h"
#define READCHUNK 80
#define BLKSSZGET _IO(0x12,104)
#define DASD_IOCTL_LETTER 'D'
#define BIODASDINFO _IOR(DASD_IOCTL_LETTER,1,struct dasd_information)
#define BIODASDINFO2 _IOR(DASD_IOCTL_LETTER,3,struct dasd_information2)
#define TEMP_DEV_MAX_RETRIES 1000
static const struct util_prg prg = {
.desc = "Display DASD volume serial number and ID information",
.args = "-i BUSID | -b BLOCKDEV | -d DEVNODE",
.copyright_vec = {
{
.owner = "IBM Corp.",
.pub_first = 2007,
},
UTIL_PRG_COPYRIGHT_END
}
};
static struct util_opt opt_vec[] = {
UTIL_OPT_SECTION("DEVICE"),
{
.option = { "block", required_argument, NULL, 'b' },
.argument = "BLOCKDEV",
.desc = "Block device name, e.g. dasdb",
},
{
.option = { "devnode", required_argument, NULL, 'd' },
.argument = "DEVNODE",
.desc = "Device node, e.g. /dev/dasda",
},
{
.option = { "busid", required_argument, NULL, 'i' },
.argument = "BUSID",
.desc = "Bus ID, e.g. 0.0.e910",
},
UTIL_OPT_SECTION("OPTIONS"),
{
.option = { "label", no_argument, NULL, 'l' },
.desc = "Print DASD volume label (volser)",
},
{
.option = { "uid", no_argument, NULL, 'u' },
.desc = "Print DASD uid (without z/VM minidisk token)",
},
{
.option = { "extended-uid", no_argument, NULL, 'x' },
.desc = "Print DASD uid (including z/VM minidisk token)",
},
{
.option = { "all", no_argument, NULL, 'a' },
.desc = "Same as -u -x -l",
},
{
.option = { "export", no_argument, NULL, 'e' },
.desc = "Export ID_BUS, ID_TYPE, ID_SERIAL for use in udev",
},
UTIL_OPT_HELP,
UTIL_OPT_VERSION,
UTIL_OPT_END
};
/* needed because ftw can not pass arbitrary arguments */
static char *searchbusid;
static char *busiddir;
struct volume_label {
char volkey[4];
char vollbl[4];
char volid[6];
} __attribute__ ((packed));
struct dasd_information2 {
unsigned int devno;
unsigned int real_devno;
unsigned int schid;
unsigned int cu_type : 16;
unsigned int cu_model : 8;
unsigned int dev_type : 16;
unsigned int dev_model : 8;
unsigned int open_count;
unsigned int req_queue_len;
unsigned int chanq_len;
char type[4];
unsigned int status;
unsigned int label_block;
unsigned int FBA_layout;
unsigned int characteristics_size;
unsigned int confdata_size;
char characteristics[64];
char configuration_data[256];
unsigned int format;
unsigned int features;
unsigned int reserved0;
unsigned int reserved1;
unsigned int reserved2;
unsigned int reserved3;
unsigned int reserved4;
unsigned int reserved5;
unsigned int reserved6;
unsigned int reserved7;
};
struct dasd_information {
unsigned int devno;
unsigned int real_devno;
unsigned int schid;
unsigned int cu_type : 16;
unsigned int cu_model : 8;
unsigned int dev_type : 16;
unsigned int dev_model : 8;
unsigned int open_count;
unsigned int req_queue_len;
unsigned int chanq_len;
char type[4];
unsigned int status;
unsigned int label_block;
unsigned int FBA_layout;
unsigned int characteristics_size;
unsigned int confdata_size;
char characteristics[64];
char configuration_data[256];
};
struct dasd_data
{
struct dasd_information2 dasd_info;
int dasd_info_version;
int blksize;
};
static char EBCtoASC[256] =
{
/* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */
0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F,
/* 0x08 -GE -SPS -RPT VT FF CR SO SI */
0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
/* 0x10 DLE DC1 DC2 DC3 -RES -NL BS -POC */
0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07,
/* 0x18 CAN EM -UBS -CU1 -IFS -IGS -IRS -ITB */
0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
/* 0x20 -DS -SOS FS -WUS -BYP LF ETB ESC */
0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B,
/* 0x28 -SA -SFE -SM -CSP -MFA ENQ ACK BEL */
0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07,
/* 0x30 ---- ---- SYN -IR -PP -TRN -NBS EOT */
0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04,
/* 0x38 -SBS -IT -RFF -CU3 DC4 NAK ---- SUB */
0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A,
/* 0x40 SP RSP ? ---- */
0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86,
/* 0x48 . < ( + | */
0x87, 0xA4, 0x9B, 0x2E, 0x3C, 0x28, 0x2B, 0x7C,
/* 0x50 & ---- */
0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07,
/* 0x58 ? ! $ * ) ; */
0x8D, 0xE1, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0xAA,
/* 0x60 - / ---- ? ---- ---- ---- */
0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F,
/* 0x68 ---- , % _ > ? */
0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F,
/* 0x70 --- ---- ---- ---- ---- ---- ---- */
0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
/* 0x78 * ` : # @ ' = " */
0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22,
/* 0x80 * a b c d e f g */
0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
/* 0x88 h i ---- ---- ---- */
0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1,
/* 0x90 ? j k l m n o p */
0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70,
/* 0x98 q r ---- ---- */
0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07,
/* 0xA0 ~ s t u v w x */
0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
/* 0xA8 y z ---- ---- ---- ---- */
0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07,
/* 0xB0 ^ ---- ? ---- */
0x5E, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC,
/* 0xB8 ---- [ ] ---- ---- ---- ---- */
0xAB, 0x07, 0x5B, 0x5D, 0x07, 0x07, 0x07, 0x07,
/* 0xC0 { A B C D E F G */
0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
/* 0xC8 H I ---- ? ---- */
0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07,
/* 0xD0 } J K L M N O P */
0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
/* 0xD8 Q R ---- ? */
0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98,
/* 0xE0 \ S T U V W X */
0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
/* 0xE8 Y Z ---- ? ---- ---- ---- */
0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07,
/* 0xF0 0 1 2 3 4 5 6 7 */
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
/* 0xF8 8 9 ---- ---- ? ---- ---- ---- */
0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07
};
static char *dinfo_ebcdic_dec (char *source, char *target, int l)
{
int i;
for (i = 0; i < l; i++)
target[i]=EBCtoASC[(unsigned char)(source[i])];
return target;
}
static int dinfo_read_dasd_uid (char *uidfile, char *readbuf, int readbuflen)
{
FILE *dasduid;
int offset = 0;
if ((dasduid = fopen(uidfile,"r")) == NULL)
return -1;
while (fgets(readbuf + offset, READCHUNK, dasduid) &&
readbuf[strlen(readbuf)-1] != '\n' ) {
offset += READCHUNK-1;
if ( offset+READCHUNK-1 >= readbuflen ) {
readbuf = realloc(readbuf,
readbuflen + READCHUNK-1);
readbuflen += READCHUNK-1;
}
}
fclose(dasduid);
if (strlen(readbuf) <= 1)
return -1;
return 0;
}
static int dinfo_read_dasd_vlabel (char *device, struct volume_label *vlabel,
char *readbuf)
{
struct dasd_data data;
struct volume_label tmp;
int vlsize = sizeof(struct volume_label);
unsigned long vlabel_start;
char vollbl[5];
int f;
char *space;
if ((f = open(device, O_RDONLY)) < 0) {
printf("Could not open device node.\n");
goto error;
}
if (ioctl(f, BLKSSZGET, &data.blksize) != 0) {
printf("Unable to figure out block size.\n");
goto error;
}
if (ioctl(f, BIODASDINFO2, &data.dasd_info) == 0)
data.dasd_info_version = 2;
else {
if (ioctl(f, BIODASDINFO, &data.dasd_info) != 0) {
printf("Unable to figure out DASD informations.\n");
goto error;
}
}
vlabel_start = data.dasd_info.label_block * data.blksize;
if (lseek(f, vlabel_start, SEEK_SET) < 0)
goto error;
bzero(vlabel, vlsize);
if (read(f, vlabel, vlsize) != vlsize) {
printf("Could not read volume label.\n");
goto error;
}
if (data.dasd_info.FBA_layout) {
bzero(&tmp, vlsize);
memcpy(&tmp, vlabel, vlsize);
memcpy(vlabel->vollbl, &tmp, vlsize-4);
}
close(f);
bzero(readbuf, 7);
bzero(vollbl, 5);
strncpy(vollbl, vlabel->vollbl, 4);
dinfo_ebcdic_dec(vollbl, vollbl, 4);
if ((strncmp(vollbl, "VOL1", 4) == 0) ||
(strncmp(vollbl, "LNX1", 4) == 0) ||
(strncmp(vollbl, "CMS1", 4) == 0)) {
strncpy(readbuf, vlabel->volid, 6);
dinfo_ebcdic_dec(readbuf, readbuf, 6);
space = strchr(readbuf,' ');
if (space)
*space = 0;
} else
strcpy(readbuf, "");
return 0;
error:
close(f);
return -1;
}
static void *dinfo_malloc(size_t size)
{
void *result;
result = malloc(size);
if (result == NULL) {
printf("Could not allocate %lld bytes of memory",
(unsigned long long) size);
}
return result;
}
static char *dinfo_make_path(char *dirname, char *filename)
{
char *result;
size_t len;
len = strlen(dirname) + strlen(filename) + 2;
result = (char *) dinfo_malloc(len);
if (result == NULL)
return NULL;
sprintf(result, "%s/%s", dirname, filename);
return result;
}
static int dinfo_create_devnode(dev_t dev, char **devno)
{
char *result;
char * pathname[] = { "/dev", getenv("TMPDIR"), "/tmp",
getenv("HOME"), "." , "/"};
char filename[] = "dasdinfo0000";
mode_t mode;
unsigned int path;
int retry;
int rc;
int fd;
mode = S_IFBLK | S_IRWXU;
/* Try several locations for the temporary device node. */
for (path=0; path < sizeof(pathname) / sizeof(pathname[0]); path++) {
if (pathname[path] == NULL)
continue;
for (retry=0; retry < TEMP_DEV_MAX_RETRIES; retry++) {
sprintf(filename, "dasdinfo%04d", retry);
result = dinfo_make_path(pathname[path], filename);
if (result == NULL)
return -1;
rc = mknod(result, mode, dev);
if (rc == 0) {
/* Need this test to cover
* 'nodev'-mounted
* filesystems. */
fd = open(result, O_RDONLY);
if (fd != -1) {
close(fd);
*devno = result;
return 0;
}
remove(result);
retry = TEMP_DEV_MAX_RETRIES;
} else if (errno != EEXIST)
retry = TEMP_DEV_MAX_RETRIES;
free(result);
}
}
printf("Error: Unable to create temporary device node");
return -1;
}
static void dinfo_free_devnode(char *device)
{
if (remove(device)) {
printf("Warning: Could not remove "
"temporary file %s", device);
}
}
static int dinfo_extract_dev(dev_t *dev, char *str, int readbuflen)
{
char tmp[readbuflen];
char *p = NULL;
int ma, mi;
bzero(tmp, readbuflen);
strncpy(tmp, str, readbuflen);
if ((p = strchr(tmp, ':')) == NULL) {
printf("Error: unable to extract major/minor\n");
return -1;
}
*p = '\0';
ma = atoi(tmp);
mi = atoi(p + sizeof(char));
*dev = makedev(ma, mi);
return 0;
}
static int dinfo_get_dev_from_blockdev(char *blockdev, dev_t *dev)
{
FILE *dasddev;
int offset = 0;
char *devfile = NULL;
char *readbuf = NULL;
int readbuflen = READCHUNK;
if ((devfile = dinfo_malloc(readbuflen)) == NULL)
return -1;
sprintf(devfile,"/sys/block/%s/dev", blockdev);
if ((readbuf = dinfo_malloc(readbuflen)) == NULL) {
printf("Error: Not enough memory to allocate readbuffer\n");
return -1;
}
if ((dasddev = fopen(devfile,"r")) == NULL)
return -1;
while (fgets(readbuf + offset, READCHUNK, dasddev) &&
readbuf[strlen(readbuf)-1] != '\n' ) {
offset += READCHUNK-1;
if (offset+READCHUNK-1 >= readbuflen) {
readbuf = realloc(readbuf,
readbuflen + READCHUNK-1);
readbuflen += READCHUNK-1;
}
}
fclose(dasddev);
if (dinfo_extract_dev(dev, readbuf, readbuflen) != 0)
return -1;
return 0;
}
static int
dinfo_is_busiddir(const char *fpath, const struct stat *UNUSED(sb),
int tflag, struct FTW *ftwbuf)
{
char *tempdir;
char linkdir[128];
ssize_t i;
if (tflag != FTW_D || (strncmp((fpath + ftwbuf->base), searchbusid,
strlen(searchbusid)) != 0))
return FTW_CONTINUE;
/*
* ensure that the found entry is a busid and not a
* subchannel ID
* for large systems subchannel IDs may look like busids
*/
if (asprintf(&tempdir, "%s/driver", fpath) < 0)
return -1;
i = readlink(tempdir, linkdir, 128);
free(tempdir);
if ((i < 0) || (i >= 128))
return -1;
/* append '\0' because readlink returns non zero terminated string */
tempdir[i+1] = '\0';
if (strstr(linkdir, "dasd") == NULL)
return FTW_CONTINUE;
free(busiddir);
busiddir = strdup(fpath);
if (busiddir == NULL)
return -1;
return FTW_STOP;
}
static int
dinfo_find_entry(const char *dir, const char *searchstring,
char type, char **result)
{
DIR *directory = NULL;
struct dirent *dir_entry = NULL;
directory = opendir(dir);
if (directory == NULL)
return -1;
while ((dir_entry = readdir(directory)) != NULL) {
/* compare if the found entry has exactly the same name
and type as searched */
if ((strncmp(dir_entry->d_name, searchstring,
strlen(searchstring)) == 0)
&& (dir_entry->d_type & type)) {
*result = strdup(dir_entry->d_name);
if (*result == NULL)
goto out;
closedir(directory);
return 0; /* found */
}
}
out:
closedir(directory);
return -1; /* nothing found or error */
}
static int
dinfo_get_blockdev_from_busid(char *busid, char **blkdev)
{
int flags = FTW_PHYS; /* do not follow links */
int rc = -1;
char *tempdir = NULL;
char *result = NULL;
char *sysfsdir = "/sys/devices/";
/* dinfo_is_devnode needs to know the busid */
searchbusid = busid;
if (nftw(sysfsdir, dinfo_is_busiddir, 200, flags) != FTW_STOP)
goto out;
/*
* new sysfs: busid directory contains a directory 'block'
* which contains a directory 'dasdXXX'
*/
rc = dinfo_find_entry(busiddir, "block", DT_DIR, &result);
if (rc == 0) {
if (asprintf(&tempdir, "%s/%s/", busiddir, result) < 0) {
rc = -1;
goto out2;
}
rc = dinfo_find_entry(tempdir, "dasd", DT_DIR, blkdev);
} else {
/*
* old sysfs: entry for busiddir contain a link
* 'block:dasdXXX'
*/
rc = dinfo_find_entry(busiddir, "block:", DT_LNK, &result);
if (rc != 0)
goto out2;
*blkdev = strdup(strchr(result, ':') + 1);
if (*blkdev == NULL)
rc = -1;
}
out:
free(tempdir);
out2:
free(busiddir);
free(result);
return rc;
}
static int dinfo_get_uid_from_devnode(char **uidfile, char *devnode)
{
struct stat stat_buffer;
char stat_dev[READCHUNK];
char sys_dev_path[READCHUNK];
char *readbuf;
DIR *directory = NULL;
struct dirent *dir_entry = NULL;
FILE *block_dev;
int readbuflen = READCHUNK;
int offset;
if (stat(devnode, &stat_buffer) != 0) {
printf("Error: could not stat %s\n", devnode);
return -1;
}
sprintf(stat_dev, "%d:%d", major(stat_buffer.st_rdev),
minor(stat_buffer.st_rdev));
if ((directory = opendir("/sys/block/")) == NULL) {
printf("Error: could not open directory /sys/block\n");
return -1;
}
if ((readbuf = dinfo_malloc(readbuflen)) == NULL) {
printf("Error: Not enough memory to allocate readbuffer\n");
return -1;
}
while ((dir_entry = readdir(directory)) != NULL) {
sprintf(sys_dev_path, "/sys/block/%s/dev", dir_entry->d_name);
if ((block_dev = fopen(sys_dev_path,"r")) == NULL)
continue;
offset = 0;
while (fgets(readbuf + offset, READCHUNK, block_dev) &&
readbuf[strlen(readbuf)-1] != '\n' ) {
offset += READCHUNK-1;
if ( offset+READCHUNK-1 >= readbuflen ) {
readbuf = realloc(readbuf,
readbuflen + READCHUNK-1);
readbuflen += READCHUNK-1;
}
}
fclose(block_dev);
if (strncmp(stat_dev, readbuf,
MAX(strlen(stat_dev), strlen(readbuf)-1)) == 0) {
sprintf(*uidfile,"/sys/block/%s/device/uid",
dir_entry->d_name);
break;
}
}
closedir(directory);
return 0;
}
int main(int argc, char * argv[])
{
struct utsname uname_buf;
int version, release;
char *uidfile = NULL;
char *device = NULL;
char *readbuf = NULL;
int readbuflen = READCHUNK;
dev_t dev;
int export = 0;
int c;
int print_uid = 0;
int print_extended_uid = 0;
int print_vlabel = 0;
char *blockdev = NULL;
char *busid = NULL;
char *devnode = NULL;
struct volume_label vlabel;
char *srchuid;
int i, rc = 0;
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 'a':
print_uid = 1;
print_vlabel = 1;
print_extended_uid = 1;
break;
case 'u':
print_uid = 1;
break;
case 'x':
print_extended_uid = 1;
break;
case 'l':
print_vlabel = 1;
break;
case 'i':
busid=strdup(optarg);
break;
case 'b':
blockdev=strdup(optarg);
break;
case 'd':
devnode=strdup(optarg);
break;
case 'e':
export = 1;
break;
case 'h':
util_prg_print_help();
util_opt_print_help();
exit(EXIT_SUCCESS);
case 'v':
util_prg_print_version();
exit(EXIT_SUCCESS);
default:
fprintf(stderr, "Try 'dasdinfo --help' for more "
"information.\n");
exit(1);
}
}
uname(&uname_buf);
sscanf(uname_buf.release, "%d.%d", &version,&release);
if (strcmp(uname_buf.sysname,"Linux") ||
version < 2 || (version == 2 && release < 6)) {
printf("%s %d.%d is not supported\n", uname_buf.sysname,
version,release);
exit(1);
}
if (!busid && !blockdev && !devnode) {
printf("Error: please specify a device using either -b, -i "
"or -d\n");
exit(1);
}
if ((busid && blockdev) || (busid && devnode) || (blockdev && devnode)) {
printf("Error: please specify device only once, either -b, -i "
"or -d\n");
exit(1);
}
if (!print_uid && !print_extended_uid && !print_vlabel) {
printf("Error: no action specified (e.g. -u)\n");
exit(1);
}
if (((readbuf = dinfo_malloc(readbuflen)) == NULL) ||
((uidfile = dinfo_malloc(readbuflen)) == NULL))
exit(1);
/* try to read the uid attribute */
if (busid) {
sprintf(uidfile,"/sys/bus/ccw/devices/%s/uid", busid);
} else if (blockdev) {
sprintf(uidfile,"/sys/block/%s/device/uid", blockdev);
} else if (devnode) {
if (dinfo_get_uid_from_devnode(&uidfile, devnode) != 0)
goto error;
}
if (export) {
printf("ID_BUS=ccw\n");
printf("ID_TYPE=disk\n");
}
if (print_uid) {
if (dinfo_read_dasd_uid(uidfile, readbuf, readbuflen) == 0) {
/* look for the 4th '.' and cut there */
srchuid = readbuf - 1;
for (i = 0; i < 4; ++i) {
srchuid = index(srchuid + 1, '.');
if (!srchuid)
break;
}
if (srchuid) {
srchuid[0] = '\n';
srchuid[1] = 0;
}
if (export) {
printf("ID_UID=%s",readbuf);
} else
printf("%s",readbuf);
if (!print_vlabel && !print_extended_uid)
goto out;
}
}
if (print_extended_uid) {
if (dinfo_read_dasd_uid(uidfile, readbuf, readbuflen) == 0) {
if (export) {
printf("ID_XUID=%s",readbuf);
} else
printf("%s",readbuf);
if (!print_vlabel)
goto out;
}
}
/* there is no uid, try to read the volume serial */
if (busid) {
char *blockdev_name = NULL;
if (dinfo_get_blockdev_from_busid(busid, &blockdev_name) != 0)
goto error;
if (dinfo_get_dev_from_blockdev(blockdev_name, &dev) != 0)
goto error;
if (dinfo_create_devnode(dev, &device) != 0)
goto error;
free(blockdev_name);
} else if (blockdev) {
if (dinfo_get_dev_from_blockdev(blockdev, &dev) != 0)
goto error;
if (dinfo_create_devnode(dev, &device) != 0)
goto error;
} else if (devnode) {
if ((device = dinfo_malloc(readbuflen)) == NULL)
exit(1);
strcpy(device, devnode);
}
if (dinfo_read_dasd_vlabel(device, &vlabel, readbuf) == 0) {
if (export) {
printf("ID_SERIAL=%s\n",readbuf);
} else
printf("%s\n", readbuf);
goto out;
}
error:
printf("Error: could not read unique DASD ID\n");
rc = 1;
out:
if (device && (busid || blockdev))
dinfo_free_devnode(device);
free(uidfile);
free(device);
free(readbuf);
exit(rc);
}

24
dasdview/Makefile Normal file
View File

@@ -0,0 +1,24 @@
include ../common.mak
ALL_CPPFLAGS += -DSYSFS
all: dasdview
libs = $(rootdir)/libdasd/libdasd.a \
$(rootdir)/libzds/libzds.a \
$(rootdir)/libvtoc/libvtoc.a \
$(rootdir)/libu2s/libu2s.a \
$(rootdir)/libutil/libutil.a
dasdview: dasdview.o $(libs)
install: all
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man8
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 dasdview $(DESTDIR)$(BINDIR)
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 dasdview.8 \
$(DESTDIR)$(MANDIR)/man8
clean:
rm -f *.o *~ dasdview core
.PHONY: all install clean

218
dasdview/dasdview.8 Normal file
View File

@@ -0,0 +1,218 @@
.\" 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 DASDVIEW 8 "Apr 2006" "s390-tools"
.SH NAME
dasdview \- Display DASD and VTOC information and dump the content of a DASD
to the console.
.SH SYNOPSIS
\fBdasdview\fR [-h] [-v]
.br
[-b \fIbegin\fR] [-s \fIsize\fR] [-1|-2]
.br
[-i] [-x] [-j] [-c]
.br
[-l] [-t {\fIinfo\fR|\fIf1\fR|\fIf3\fR|\fIf4\fR|\fIf5\fR|\fIf7\fR|\fIf8\fR|\fIf9\fR}]
.br
\fIdevice\fR
.SH DESCRIPTION
\fBdasdview\fR prints you some useful information of your disks to the console.
You can display a disk dump by specifying start point and offset and you can
print the volume label and VTOC entries.
The \fIdevice\fR is the node of the device (e.g. '/dev/dasda').
Any device node created by udev for kernel 2.6 can be used
(e.g. '/dev/dasd/0.0.b100/disc').
DASD devices in raw_track_access mode are supported and detected
automatically. When in raw_track_access mode, the same basic
functions are available as in the regular mode, but the output may
have a slightly different layout:
.IP \(bu 2
The disk dump functions (\fB-b\fR and \fB-s\fR) print the count,
key and data information for the whole track, and not just the
contents of the data areas.
.IP \(bu 2
The VTOC listing (\fB-t\fR) print all specified DSCBs in the same
format as in the regular mode, but in the sequence as they appear in
the VTOC. The \fB-t info\fR overview contains more details for each
data set than in the regular mode, to support the larger variety of
data set layouts.
.SH OPTIONS
.TP
\fB-h\fR or \fB--help\fR
Print usage and exit.
.TP
\fB-v\fR or \fB--version\fR
Print version number and exit.
.TP
\fB-b\fR \fIbegin\fR or \fB--begin=\fR\fIbegin\fR
Print a disk dump to the console, starting with \fIbegin\fR. The content of
the disk will be displayed in hexadecimal numbers, ASCII text and EBCDIC text.
If no size is specified dasdview will take the default size. The variable
\fIbegin\fR can be specified in one of the following ways:
.br
begin[k|m|b|t|c]
.br
The default for \fIbegin\fR is \fI0\fR.
.br
\fBNote 1:\fR dasdview will show you the content of your disk using the DASD
driver. If this driver decides to hide or add some parts of the disk, you have
to live with it. This happens for example with the first two tracks of a
cdl-formatted disk. In this case the DASD driver fills up shorter blocks with
zeros to have a constant blocksize. And all applications, including dasdview,
believe it.
.br
\fBNote 2:\fR In raw_track_access mode \fIbegin\fR must be aligned to
track boundaries. A simple way to do that is to specify a track or
cylinder as starting point.
.br
examples:
.br
-b 32 --> start printing at Byte 32
.br
-b 32k --> start printing at kByte 32
.br
-b 32m --> start printing at MByte 32
.br
-b 32b --> start printing at block 32
.br
-b 32t --> start printing at track 32
.br
-b 32c --> start printing at cylinder 32
.TP
\fB-s\fR \fIsize\fR or \fB--size=\fR\fIsize\fR
Print a disk dump to the console, starting with \fIbegin\fR, specified with
the \fB-b\fR option and size \fIsize\fR. The content of the disk will be
displayed in hexadecimal numbers, ASCII text and EBCDIC text. If no start
value is specified dasdview will take the default start value. The variable
\fIsize\fR can be specified in one of the following ways:
.br
size[k|m|b|t|c]
.br
\fBNote:\fR In raw_track_access mode \fIsize\fR must be a multiple of
one track. A simple way to do that is to specify the size in tracks or
cylinders.
.br
The default for \fIsize\fR is \fI128\fR in regular mode and \fI1t\fR
in raw_track_access mode.
.br
examples:
.br
-s 16 --> use a 16 Byte size
.br
-s 16k --> use a 16 kByte size
.br
-s 16m --> use a 16 MByte size
.br
-s 16b --> use a 16 block size
.br
-s 16t --> use a 16 track size
.br
-s 16c --> use a 16 cylinder size
.TP
\fB-1\fR
This option tells dasdview to print the disk dump using format 1. This means
you will get 16 Bytes per line in hex, ascii and ebcdic. There is no line
number.
.br
The \fB-1\fR option makes only sense with the \fB-b\fR and/or the \fB-s\fR
options.
.br
This is the default.
.TP
\fB-2\fR
This option tells dasdview to print the disk dump using format 2. This means
you will get 8 Bytes per line in hex, ascii and ebcdic. And in addition a line
number and a decimal and hexadecimal byte count will be printed.
.br
The \fB-2\fR option makes only sense with the \fB-b\fR and/or the \fB-s\fR
options. In raw_track_access mode this format is not supported and the
option will be ignored.
.TP
\fB-i\fR or \fB--info\fR
Print some useful information (e.g. device node/number/type or geometry data).
When running dasdview on a kernel 2.6 based distribution the busid
is printed instead of the device number.
.TP
\fB-x\fR or \fB--extended\fR
Print some more DASD information (e.g. open count, subchannel identifier).
.TP
\fB-j\fR or \fB--volser\fR
Print volume serial number (volume identifier).
.TP
\fB-l\fR or \fB--label\fR
Print the volume label.
.TP
\fB-c\fR or \fB--characteristic\fR
Print some information about the device e.g. if it is encrypted.
.TP
\fB-t\fR \fIspec\fR or \fB--vtoc=\fR\fIspec\fR
Print the VTOC (table of content) or single VTOC entries to the console.
\fIspec\fR can be one of the following strings:
.br
\fIinfo\fR:
.br
Gives you a VTOC overview. You will see what other S/390 or zSeries operating
systems would see (e.g. data set names and sizes).
.br
\fIf1\fR:
.br
Print the content of all format 1 DSCBs.
.br
\fIf3\fR:
.br
Print the content of all format 3 DSCBs.
.br
\fIf4\fR:
.br
Print the content of the format 4 DSCB.
.br
\fIf5\fR:
.br
Print the content of the format 5 DSCB.
.br
\fIf7\fR:
.br
Print the content of the format 7 DSCB.
.br
\fIf8\fR:
.br
Print the content of all format 8 DSCBs.
.br
\fIf9\fR:
.br
Print the content of all format 9 DSCBs.
.br
\fIall\fR:
.br
Print the content of all DSCBs.

2470
dasdview/dasdview.c Normal file

File diff suppressed because it is too large Load Diff

269
dasdview/dasdview.h Normal file
View File

@@ -0,0 +1,269 @@
/*
* dasdview - Display DASD and VTOC information or dump the contents of a DASD
*
* Copyright IBM Corp. 2002, 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 DASDVIEW_H
#define DASDVIEW_H
#include <limits.h>
#include "lib/u2s.h"
/********************************************************************************
* SECTION: Definitions needed for DASD-API (see dasd.h)
*******************************************************************************/
#define DASD_IOCTL_LETTER 'D'
/*
* struct dasd_information_t
* represents any data about the device, which is visible to userspace.
* including foramt and features.
*/
typedef struct dasd_information_t {
unsigned int devno; /* S/390 devno */
unsigned int real_devno; /* for aliases */
unsigned int schid; /* S/390 subchannel identifier */
unsigned int cu_type : 16; /* from SenseID */
unsigned int cu_model : 8; /* from SenseID */
unsigned int dev_type : 16; /* from SenseID */
unsigned int dev_model : 8; /* from SenseID */
unsigned int open_count;
unsigned int req_queue_len;
unsigned int chanq_len; /* length of chanq */
char type[4]; /* from discipline.name, 'none' for unknown */
unsigned int status; /* current device level */
unsigned int label_block; /* where to find the VOLSER */
unsigned int FBA_layout; /* fixed block size (like AIXVOL) */
unsigned int characteristics_size;
unsigned int confdata_size;
char characteristics[64]; /* from read_device_characteristics */
char configuration_data[256]; /* from read_configuration_data */
} dasd_information_t;
typedef struct dasd_information2_t {
unsigned int devno; /* S/390 devno */
unsigned int real_devno; /* for aliases */
unsigned int schid; /* S/390 subchannel identifier */
unsigned int cu_type : 16; /* from SenseID */
unsigned int cu_model : 8; /* from SenseID */
unsigned int dev_type : 16; /* from SenseID */
unsigned int dev_model : 8; /* from SenseID */
unsigned int open_count;
unsigned int req_queue_len;
unsigned int chanq_len; /* length of chanq */
char type[4]; /* from discipline.name, 'none' for unknown */
unsigned int status; /* current device level */
unsigned int label_block; /* where to find the VOLSER */
unsigned int FBA_layout; /* fixed block size (like AIXVOL) */
unsigned int characteristics_size;
unsigned int confdata_size;
unsigned char characteristics[64];/*from read_device_characteristics */
unsigned char configuration_data[256];/*from read_configuration_data */
unsigned int format; /* format info like formatted/cdl/ldl/... */
unsigned int features; /* dasd features like 'ro',... */
unsigned int reserved0; /* reserved for further use ,... */
unsigned int reserved1; /* reserved for further use ,... */
unsigned int reserved2; /* reserved for further use ,... */
unsigned int reserved3; /* reserved for further use ,... */
unsigned int reserved4; /* reserved for further use ,... */
unsigned int reserved5; /* reserved for further use ,... */
unsigned int reserved6; /* reserved for further use ,... */
unsigned int reserved7; /* reserved for further use ,... */
} dasd_information2_t;
struct dasd_eckd_characteristics {
unsigned short cu_type;
struct {
unsigned char support:2;
unsigned char async:1;
unsigned char reserved:1;
unsigned char cache_info:1;
unsigned char model:3;
} __attribute__ ((packed)) cu_model;
unsigned short dev_type;
unsigned char dev_model;
struct {
unsigned char mult_burst:1;
unsigned char RT_in_LR:1;
unsigned char reserved1:1;
unsigned char RD_IN_LR:1;
unsigned char reserved2:4;
unsigned char reserved3:8;
unsigned char defect_wr:1;
unsigned char XRC_supported:1;
unsigned char reserved4:1;
unsigned char striping:1;
unsigned char reserved5:4;
unsigned char cfw:1;
unsigned char reserved6:2;
unsigned char cache:1;
unsigned char dual_copy:1;
unsigned char dfw:1;
unsigned char reset_alleg:1;
unsigned char sense_down:1;
} __attribute__ ((packed)) facilities;
unsigned char dev_class;
unsigned char unit_type;
unsigned short no_cyl;
unsigned short trk_per_cyl;
unsigned char sec_per_trk;
unsigned char byte_per_track[3];
unsigned short home_bytes;
unsigned char formula;
union {
struct {
unsigned char f1;
unsigned short f2;
unsigned short f3;
} __attribute__ ((packed)) f_0x01;
struct {
unsigned char f1;
unsigned char f2;
unsigned char f3;
unsigned char f4;
unsigned char f5;
} __attribute__ ((packed)) f_0x02;
} __attribute__ ((packed)) factors;
unsigned short first_alt_trk;
unsigned short no_alt_trk;
unsigned short first_dia_trk;
unsigned short no_dia_trk;
unsigned short first_sup_trk;
unsigned short no_sup_trk;
unsigned char MDR_ID;
unsigned char OBR_ID;
unsigned char director;
unsigned char rd_trk_set;
unsigned short max_rec_zero;
unsigned char reserved1;
unsigned char RWANY_in_LR;
unsigned char factor6;
unsigned char factor7;
unsigned char factor8;
unsigned char reserved2[3];
unsigned char reserved3[6];
unsigned int long_no_cyl;
} __attribute__ ((packed));
/*
* values to be used for dasd_information2_t.format
* 0x00: NOT formatted
* 0x01: Linux disc layout
* 0x02: Common disc layout
*/
#define DASD_FORMAT_NONE 0
#define DASD_FORMAT_LDL 1
#define DASD_FORMAT_CDL 2
/*
* values to be used for dasd_information2_t.features
* 0x00: default features
* 0x01: readonly (ro)
* 0x02: use diag discipline (diag)
*/
#define DASD_FEATURE_DEFAULT 0
#define DASD_FEATURE_READONLY 1
#define DASD_FEATURE_USEDIAG 2
/* Get information on a dasd device (enhanced) */
#define BIODASDINFO _IOR(DASD_IOCTL_LETTER,1,dasd_information_t)
#define BIODASDINFO2 _IOR(DASD_IOCTL_LETTER,3,dasd_information2_t)
/********************************************************************************
* SECTION: Further IOCTL Definitions (see fs.h and hdreq.h)
*******************************************************************************/
/* get block device sector size */
#define BLKSSZGET _IO(0x12,104)
/* return device size in bytes (u64 *arg) */
#define BLKGETSIZE64 _IOR(0x12,114,size_t)
/* get device geometry */
#define HDIO_GETGEO 0x0301
/********************************************************************************
* SECTION: DASDVIEW internal types
*******************************************************************************/
#define LINE_LENGTH 80
#define DASDVIEW_ERROR "dasdview:"
#define DEFAULT_BEGIN 0
#define DEFAULT_SIZE 128
#define NO_PART_LABELS 8 /* for partition related labels (f1,f8 and f9) */
#define SEEK_STEP 4194304LL
#define DUMP_STRING_SIZE 1024LL
#define ERROR_STRING_SIZE 1024
static char error_str[ERROR_STRING_SIZE];
enum dasdview_failure {
open_error,
seek_error,
read_error,
ioctl_error,
usage_error,
disk_layout,
vtoc_error
};
typedef struct dasdview_info
{
char device[PATH_MAX];
dasd_information2_t dasd_info;
int dasd_info_version;
int blksize;
struct hd_geometry geo;
u_int32_t hw_cylinders;
unsigned long long begin;
unsigned long long size;
int format1;
int format2;
int action_specified;
int begin_specified;
int size_specified;
int characteristic_specified;
int device_id;
int general_info;
int extended_info;
int volser;
int vtoc;
int vtoc_info;
int vtoc_f1;
int vtoc_f3;
int vtoc_f4;
int vtoc_f5;
int vtoc_f7;
int vtoc_f8;
int vtoc_f9;
int vtoc_all;
int vlabel_info;
format1_label_t f1[NO_PART_LABELS];
format4_label_t f4;
format5_label_t f5;
format7_label_t f7;
format1_label_t f8[NO_PART_LABELS];
format9_label_t f9[NO_PART_LABELS];
int f1c;
int f4c;
int f5c;
int f7c;
int f8c;
int f9c;
char busid[U2S_BUS_ID_SIZE];
int busid_valid;
int raw_track_access;
struct zdsroot *zdsroot;
struct raw_vtoc *rawvtoc;
struct dasd *dasd;
} dasdview_info_t;
#endif /* DASDVIEW_H */

12
dump2tar/Makefile Normal file
View File

@@ -0,0 +1,12 @@
# Common definitions
include ../common.mak
all:
$(MAKE) -C src
install: all
$(MAKE) -C src install
$(MAKE) -C man install
clean:
$(MAKE) -C src clean

50
dump2tar/include/buffer.h Normal file
View File

@@ -0,0 +1,50 @@
/*
* dump2tar - tool to dump files and command output into a tar archive
*
* Data buffering 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 BUFFER_H
#define BUFFER_H
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
/* Buffers for building tar file entries */
struct buffer {
size_t total; /* Total number of bytes in buffer */
size_t off; /* Current offset to next free byte in memory buffer */
size_t size; /* Memory buffer size */
char *addr; /* Memory buffer address */
bool fd_open; /* Has fd been openend yet? */
FILE *file; /* FILE * of file containing previous buffer data */
int fd; /* Handle of file containing previous buffer data */
};
void buffer_init(struct buffer *buffer, size_t size);
struct buffer *buffer_alloc(size_t size);
void buffer_reset(struct buffer *buffer);
void buffer_close(struct buffer *buffer);
void buffer_free(struct buffer *buffer, bool dyn);
int buffer_open(struct buffer *buffer);
int buffer_flush(struct buffer *buffer);
ssize_t buffer_make_room(struct buffer *buffer, size_t size, bool usefile,
size_t max_buffer_size);
int buffer_truncate(struct buffer *buffer, size_t len);
ssize_t buffer_read_fd(struct buffer *buffer, int fd, size_t chunk,
bool usefile, size_t max_buffer_size);
int buffer_add_data(struct buffer *buffer, char *addr, size_t len,
bool usefile, size_t max_buffer_size);
typedef int (*buffer_cb_t)(void *data, void *addr, size_t len);
int buffer_iterate(struct buffer *buffer, buffer_cb_t cb, void *data);
void buffer_print(struct buffer *buffer);
#endif /* BUFFER_H */

29
dump2tar/include/dref.h Normal file
View File

@@ -0,0 +1,29 @@
/*
* dump2tar - tool to dump files and command output into a tar archive
*
* Reference counting for directory handles
*
* 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 DREF_H
#define DREF_H
#include <dirent.h>
#include <stdbool.h>
/* Multiple jobs may refer to an open DIR * - need reference counting */
struct dref {
DIR *dd;
int dirfd;
unsigned int count;
};
struct dref *dref_create(const char *dirname);
struct dref *dref_get(struct dref *dref);
void dref_put(struct dref *dref);
#endif /* DREF_H */

63
dump2tar/include/dump.h Normal file
View File

@@ -0,0 +1,63 @@
/*
* dump2tar - tool to dump files and command output into a tar archive
*
* Main dump logic
*
* 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 DUMP_H
#define DUMP_H
#include <stdbool.h>
#include <stddef.h>
#include <sys/stat.h>
#include "strarray.h"
#define NUM_EXCLUDE_TYPES 7
struct dump_spec {
char *inname;
char *outname;
bool is_cmd;
};
struct dump_opts {
bool add_cmd_status;
bool append;
bool dereference;
bool exclude_type[NUM_EXCLUDE_TYPES];
bool gzip;
bool ignore_failed_read;
bool no_eof;
bool quiet;
bool recursive;
bool threaded;
bool verbose;
const char *output_file;
int file_timeout;
int timeout;
long jobs;
long jobs_per_cpu;
size_t file_max_size;
size_t max_buffer_size;
size_t max_size;
size_t read_chunk_size;
struct strarray exclude;
struct dump_spec *specs;
unsigned int num_specs;
};
struct dump_opts *dump_opts_new(void);
int dump_opts_set_type_excluded(struct dump_opts *opts, char c);
void dump_opts_add_spec(struct dump_opts *opts, char *inname, char *outname,
bool is_cmd);
void dump_opts_free(struct dump_opts *opts);
int dump_to_tar(struct dump_opts *opts);
#endif /* DUMP_H */

23
dump2tar/include/global.h Normal file
View File

@@ -0,0 +1,23 @@
/*
* dump2tar - tool to dump files and command output into a tar archive
*
* Global variables
*
* 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 GLOBAL_H
#define GLOBAL_H
#include <stdbool.h>
extern bool global_threaded;
extern bool global_debug;
extern bool global_verbose;
extern bool global_quiet;
extern bool global_timestamps;
#endif /* GLOBAL_H */

View File

@@ -0,0 +1,26 @@
/*
* dump2tar - tool to dump files and command output into a tar archive
*
* Caches for user and group ID lookups
*
* 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 IDCACHE_H
#define IDCACHE_H
#include <stdlib.h>
#include <sys/types.h>
/* Buffer sizes for getpwuid_r and getgid_r calls (bytes) */
#define PWD_BUFFER_SIZE 4096
#define GRP_BUFFER_SIZE 4096
void uid_to_name(uid_t uid, char *name, size_t len);
void gid_to_name(gid_t gid, char *name, size_t len);
void idcache_cleanup(void);
#endif /* IDCACHE_H */

101
dump2tar/include/misc.h Normal file
View File

@@ -0,0 +1,101 @@
/*
* dump2tar - tool to dump files and command output into a tar archive
*
* 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>
#include <stdlib.h>
#include <sys/stat.h>
#include <time.h>
#include "lib/util_libc.h"
#include "global.h"
#define MSG_LEN 256
#define DBG(...) \
do { \
if (global_debug) \
debug(__FILE__, __LINE__, ##__VA_ARGS__); \
} while (0)
#define mwarn(fmt, ...) _mwarn(true, (fmt), ##__VA_ARGS__)
#define mwarnx(fmt, ...) _mwarn(false, (fmt), ##__VA_ARGS__)
/* Helper macro for constructing messages in variables */
#define HANDLE_RC(rc, max, off, label) \
do { \
if ((rc) > 0) \
(off) += (rc); \
if ((off) > (max)) \
goto label; \
} while (0)
/* Program exit codes */
#define EXIT_OK 0
#define EXIT_RUNTIME 1
#define EXIT_USAGE 2
/* Number of nanoseconds in a second */
#define NSEC_PER_SEC 1000000000L
#define NSEC_PER_MSEC 1000000L
#define NSEC_PER_USEC 1000L
extern struct timespec main_start_ts;
struct dref;
int misc_write_data(int fd, char *addr, size_t len);
ssize_t misc_read_data(int fd, char *addr, size_t len);
void inc_timespec(struct timespec *ts, time_t sec, long nsec);
void set_timespec(struct timespec *ts, time_t sec, long nsec);
bool ts_before(struct timespec *a, struct timespec *b);
int snprintf_duration(char *buff, size_t len, struct timespec *start,
struct timespec *end);
char *get_threadname(void);
void debug(const char *file, unsigned long line, const char *format, ...);
void _mwarn(bool print_errno, const char *format, ...);
void verb(const char *format, ...);
void info(const char *format, ...);
#define mmalloc(len) util_zalloc(len)
#define mcalloc(n, len) util_zalloc((n) * (len))
#define mrealloc(ptr, len) util_realloc((ptr), (len))
#define mstrdup(str) util_strdup(str)
#define masprintf(fmt, ...) __masprintf(__func__, __FILE__, __LINE__, \
(fmt), ##__VA_ARGS__)
char *__masprintf(const char *func, const char *file, int line,
const char *fmt, ...);
#define set_threadname(fmt, ...) __set_threadname(__func__, __FILE__, \
__LINE__, (fmt), \
##__VA_ARGS__)
void __set_threadname(const char *func, const char *file, int line,
const char *fmt, ...);
void clear_threadname(void);
void chomp(char *str, char *c);
void lchomp(char *str, char *c);
void remove_double_slashes(char *str);
int stat_file(bool dereference, const char *abs, const char *rel,
struct dref *dref, struct stat *st);
void set_dummy_stat(struct stat *st);
bool starts_with(const char *str, const char *prefix);
bool ends_with(const char *str, const char *suffix);
int cmd_child(int fd, char *cmd);
int cmd_open(char *cmd, pid_t *pid_ptr);
int cmd_close(int fd, pid_t pid, int *status_ptr);
void misc_init(void);
void misc_cleanup(void);
void set_stdout_data(void);
#endif /* MISC_H */

View File

@@ -0,0 +1,26 @@
/*
* dump2tar - tool to dump files and command output into a tar archive
*
* Dynamically growing string arrays
*
* 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 STRARRAY_H
#define STRARRAY_H
/* A string array that can grow in size */
struct strarray {
unsigned int num;
char **str;
};
void free_strarray(struct strarray *array);
void add_str_to_strarray(struct strarray *array, const char *str);
void add_vstr_to_strarray(struct strarray *array, const char *fmt, ...);
int add_file_to_strarray(struct strarray *array, const char *filename);
#endif /* STRARRAY_H */

44
dump2tar/include/tar.h Normal file
View File

@@ -0,0 +1,44 @@
/*
* dump2tar - tool to dump files and command output into a tar archive
*
* TAR file generation
*
* 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 TAR_H
#define TAR_H
#include <stdbool.h>
#include <stdlib.h>
#include <sys/stat.h>
#define TYPE_REGULAR '0'
#define TYPE_LINK '2'
#define TYPE_DIR '5'
#define TAR_BLOCKSIZE 512
struct buffer;
/* emit_cb_t - Callback used for emitting chunks of a byte stream
* @data: Arbitrary pointer passed via the @data parameter of the
* tar_emit_file_* functions
* @addr: Pointer to data
* @len: Size of data
* Return %0 on success. Returning non-zero will indicate failure and abort
* further data emission. */
typedef int (*emit_cb_t)(void *data, void *addr, size_t len);
int tar_emit_file_from_buffer(char *filename, char *link, size_t len,
struct stat *stat, char type,
struct buffer *content, emit_cb_t emit_cb,
void *data);
int tar_emit_file_from_data(char *filename, char *link, size_t len,
struct stat *stat, char type, void *addr,
emit_cb_t emit_cb, void *data);
#endif /* TAR_H */

12
dump2tar/man/Makefile Normal file
View File

@@ -0,0 +1,12 @@
# Common definitions
include ../../common.mak
all:
install:
$(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man1
$(INSTALL) -m 644 -c dump2tar.1 $(DESTDIR)$(MANDIR)/man1
clean:
.PHONY: all clean

458
dump2tar/man/dump2tar.1 Normal file
View File

@@ -0,0 +1,458 @@
.\" 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.
.\"
.\" Macro for inserting an option description prologue.
.\" .OD <long> [<short>] [args]
.de OD
. ds args "
. if !'\\$3'' .as args \fI\\$3\fP
. if !'\\$4'' .as args \\$4
. if !'\\$5'' .as args \fI\\$5\fP
. if !'\\$6'' .as args \\$6
. if !'\\$7'' .as args \fI\\$7\fP
. PD 0
. if !'\\$2'' .IP "\fB\-\\$2\fP \\*[args]" 4
. if !'\\$1'' .IP "\fB\-\-\\$1\fP \\*[args]" 4
. PD
..
.\" Macro for inserting code line.
.\" .CL <text>
.de CL
. ds pfont \\n[.f]
. nh
. na
. ft CW
\\$*
. ft \\*[pfont]
. ad
. hy
. br
..
.\" Macro for inserting a man page reference.
.\" .MP man-page section [suffix]
.de MP
. nh
. na
. BR \\$1 (\\$2)\\$3
. ad
. hy
..
.
.TH "dump2tar" "1" "2016\-09\-02" "" ""
.
.SH "NAME"
dump2tar - Gather file contents and command output into a tar archive
.
.
.SH "SYNOPSIS"
.B "dump2tar "
.RI "[" "OPTIONS" "] " "SPECS"
.
.
.SH "DESCRIPTION"
.B dump2tar
creates a tar archive from the contents of any files, including files of
unknown size.
Examples for files of unknown size are:
.IP \(bu 3
Named pipes (FIFOs)
.PP
.IP \(bu 3
Particular Linux kernel debugfs or sysfs files
.PP
.IP \(bu 3
Character or block devices
.PP
When adding such a file,
.B dump2tar
first reads all available data until an end-of-file indication is found. From
this data, it then creates a regular file entry in the resulting tar archive.
By default, symbolic links and directories are preserved in the archive in
their original form.
.B dump2tar
can also:
.IP \(bu 3
Add files under a different name
.PP
.IP \(bu 3
Run arbitrary commands and add the resulting command output as a
regular file
.PP
.
.
.SH "FILE SPECIFICATIONS"
.
This section describes the format of the
.I SPECS
argument mentioned in the command synopsis.
Use the following command line syntax to identify data sources and
to specify file names within the archive:
.PP
.TP
.I "PATH"
Adds the contents of the file system subtree at file system location
.I PATH
(with possible exceptions described by options) in the archive under the same
file name as on the file system.
.PP
.
.
.TP
.IR "FILENAME" ":=" "PATH"
Adds the contents of the file at file system location
.I PATH
in the archive under the name specified by
.IR FILENAME .
.PP
.
.
.TP
.IR "FILENAME" "|=" "CMDLINE"
Runs the command
.IR CMDLINE
and captures both the resulting standard output and standard error streams.
Adds the collected output as a regular file named
.I FILENAME
in the resulting archive. You can also include the resulting program exit code
by using option \-\-add\-cmd\-status.
.PP
.
You can also specify "\-\-". All specifications that follow are interpreted as
simple file names. This is useful for archiving files that contain ":=" or "|=".
.PP
.
.
.SH "OUTPUT OPTIONS"
.
.OD "output\-file" "o" "TARFILE"
Writes the resulting tar archive to
.IR TARFILE .
An existing file at the specified file system location is overwritten.
If this option is omitted or if "\-" is specified for
.IR TARFILE ,
the archive is written to the standard output stream.
.PP
.
.
.OD "gzip" "z" ""
Compresses the resulting tar archive using gzip.
.PP
.
.
.OD "max\-size" "m" "VALUE"
Sets an upper size limit, in bytes, for the resulting archive. If this limit
is exceeded after adding a file, no further files are added.
.PP
.
.
.OD "timeout" "t" "VALUE"
Sets an upper time limit, in seconds, for the archiving process. If this limit
is exceeded while adding a file, that file is truncated and no
further files are added.
.PP
.
.
.OD "no-eof" "" ""
Does not write an end-of-file marker.
Use this option if you want to create an archive that can be extended by
appending additional tar archive data.
Note: Do not use this option for the final data to be added.
A valid tar archive requires a trailing end-of-file marker.
.PP
.
.
.OD "append" "" ""
Appends data to the end of the archive.
Use this option to incrementally build a tar file by repeatedly calling
.BR dump2tar .
You must specify the \-\-no\-eof option for each but the final call of
.BR dump2tar .
.PP
.
.
.OD "add-cmd-status" "" ""
Adds a separate file named
.RI \(dq FILENAME .cmdstatus\(dq
for each command output added through the
.RI \(dq FILENAME |= CMDLINE \(dq
notation (see FILE SPECIFICATIONS).
This file contains information about the exit status of the
process that executed the command:
.
.RS 8
.TP
.RI EXITSTATUS= VALUE
Unless
.I VALUE
is -1, the process ended normally with the specified exit value.
.PP
.
.TP
.RI TERMSIG= VALUE
Unless
.I VALUE
is -1, the process was stopped by a signal of the specified number.
.PP
.
.TP
.RI WAITPID_ERRNO= VALUE
Unless
.I VALUE
is -1, an attempt to obtain the status of the process failed with the
specified error.
.PP
.RE
.
.
.
.SH "INPUT OPTIONS"
.
.OD "files\-from" "F" "FILENAME"
Reads input data specifications (see FILE SPECIFICATIONS) from
.IR FILENAME ,
one specification per line. Each line contains either a file name or a
.IR FILENAME := PATH
or
.IR FILENAME |= CMDLINE
specification. Empty lines are ignored.
A line can also consist of only "\-\-". All lines following this specification
are interpreted as simple file names. This is useful for archiving files that
contain ":=" or "|=".
.PP
.
.
.OD "ignore\-failed\-read" "i" ""
Continues after read errors.
By default,
.B dump2tar
stops processing after encountering errors while reading an input file.
With this option,
.B dump2tar
prints a warning message and adds an empty entry for the erroneous file in
the archive.
.PP
.
.
.OD "buffer\-size" "b" "VALUE"
Reads data from input files in chunks of
.I VALUE
bytes. Large values can accelerate the archiving process for large files
at the cost of increased memory usage. The default value is 1048576.
.PP
.
.
.OD "file\-timeout" "T" "VALUE"
Sets an upper time limit, in seconds, for reading an input file.
.B dump2tar
stops processing a file when the time limit is exceeded. Archive entries for
such files are truncated to the amount of data that is collected by the time
the limit is reached.
.PP
.
.
.OD "file\-max\-size" "M" "N"
Sets an upper size limit, in bytes, for an input file.
.B dump2tar
stops processing a file when the size limit is exceeded. Archive entries for
such files are truncated to the specified size.
.PP
.
.
.OD "jobs" "j" "N"
By default,
.B dump2tar
processes one file at a time. With this option,
.B dump2tar
processes
.I N
files in parallel.
Parallel processing can accelerate the archiving process,
especially if input files are located on slow devices, or when output from
multiple commands is added to the archive.
Note: Use
.B tar
option \-\-delay\-directory\-restore when extracting files from an archive
created with \-\-jobs to prevent conflicts with directory permissions and
modification times.
.PP
.
.
.OD "jobs\-per\-cpu" "J" "N"
Processes
.I N
files for each online CPU in parallel.
Parallel processing can accelerate the
archiving process, especially if input files are located on slow devices, or
when output from multiple commands is added to the archive.
Note: Use
.B tar
option \-\-delay\-directory\-restore when extracting files from an archive
created with \-\-jobs\-per\-cpu to prevent conflicts with directory permissions
and modification times.
.PP
.
.
.OD "exclude" "x" "PATTERN"
Does not add files to the archive if their file names match
.IR PATTERN .
.I PATTERN
is an expression that uses the shell wildcards.
.PP
.
.
.OD "exclude\-from" "X" "FILENAME"
Does not add files to the archive if their names match at least one of the
patterns listed in the pattern file with name
.IR FILENAME .
In the pattern file, each line specifies an expression that uses the
shell wildcards.
.PP
.
.
.OD "exclude\-type" "" "TYPE"
Does not add files to the archive if they match at least one of the file types
specified with
.IR TYPE .
.I TYPE
uses one or more of the characters "fdcbpls", where:
.RS 8
.IP f 3
regular files
.PP
.IP d 3
directories
.PP
.IP c 3
character devices
.PP
.IP b 3
block devices
.PP
.IP p 3
named pipes (FIFOs)
.PP
.IP l 3
symbolic links
.PP
.IP s 3
sockets
.PP
.RE
.
.PP
.
.
.OD "dereference" "" ""
Adds the content of link targets instead of symbolic links.
.PP
.
.
.OD "no\-recursion" "" ""
Does not add files from sub\-directories.
By default,
.B dump2tar
adds archive entries for specified directories, and for the files within these
directories. With this option, a specified directory results in a single entry
for the directory. Any contained files to be included must be specified
explicitly.
.PP
.
.
.SH "MISC OPTIONS"
.
.OD "help" "h" ""
Prints an overview of available options, then exits.
.PP
.
.
.OD "verbose" "V" ""
Prints additional informational output.
.PP
.
.
.OD "quiet" "q" ""
Suppresses printing of informational output.
.PP
.
.
.
.SH "EXAMPLES"
.
.\fB
.CL # dump2tar a b \-o archive.tar
.\fR
.RS 4
Creates a tar archive named archive.tar containing files a and b.
.RE
.PP
.
.\fB
.CL # dump2tar /proc \-o procdump.tar.gz \-z \-i \-T 1 \-M 1048576
.\fR
.RS 4
Creates a gzip compressed tar archive named procdump.tar.gz that contains
all procfs files. Unreadable files are ignored. Files are truncated when the
first of the two limiting conditions is reached, either 1048576 bytes of
content or the reading time of 1 second.
.RE
.PP
.
.\fB
.CL # dump2tar '|=dmesg' '|=lspci' \-o data.tar
.\fR
.RS 4
Creates a tar archive named data.tar containing the output of the 'dmesg'
and 'lspci' commands.
.RE
.PP
.
.\fB
.CL # dump2tar /sys/kernel/debug/ -x '*/tracing/*' -o debug.tar -i
.\fR
.RS 4
Creates a tar archive named debug.tar containing the contents of directory
/sys/kernel/debug/ while excluding any file that is located in a sub-directory
named 'tracing'.
.RE
.PP
.
.
.SH "EXIT CODES"
.TP
.B 0
The program finished successfully
.TP
.B 1
A run-time error occurred
.TP
.B 2
The specified command was not valid
.PP
.
.
.SH "SEE ALSO"
.MP dump2tar 1 ,
.MP tar 1

31
dump2tar/src/Makefile Normal file
View File

@@ -0,0 +1,31 @@
# Common definitions
include ../../common.mak
ALL_CPPFLAGS += -I../include -std=gnu99 -Wno-unused-parameter
LDLIBS += -lpthread -lrt
ifneq ($(HAVE_ZLIB),0)
ALL_CPPFLAGS += -DHAVE_ZLIB
LDLIBS += -lz
endif
core_objects = buffer.o dref.o global.o dump.o idcache.o misc.o strarray.o tar.o
libs = $(rootdir)/libutil/libutil.a
check_dep_zlib:
$(call check_dep, \
"dump2tar", \
"zlib.h", \
"zlib-devel or libz-dev", \
"HAVE_ZLIB=0")
all: check_dep_zlib dump2tar
dump2tar: $(core_objects) dump2tar.o $(libs)
install: dump2tar
$(INSTALL) -c dump2tar $(DESTDIR)$(USRBINDIR)
clean:
@rm -f dump2tar *.o
.PHONY: all install clean

273
dump2tar/src/buffer.c Normal file
View File

@@ -0,0 +1,273 @@
/*
* dump2tar - tool to dump files and command output into a tar archive
*
* Data buffering 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include "buffer.h"
#include "misc.h"
void buffer_print(struct buffer *buffer)
{
fprintf(stderr, "DEBUG: buffer at %p\n", (void *) buffer);
if (!buffer)
return;
fprintf(stderr, "DEBUG: total=%zu\n", buffer->total);
fprintf(stderr, "DEBUG: off=%zu\n", buffer->off);
fprintf(stderr, "DEBUG: size=%zu\n", buffer->size);
fprintf(stderr, "DEBUG: addr=%p\n", (void *) buffer->addr);
fprintf(stderr, "DEBUG: fd_open=%d\n", buffer->fd_open);
fprintf(stderr, "DEBUG: fd=%d\n", buffer->fd);
if (buffer->fd_open) {
fprintf(stderr, "DEBUG: fd->pos=%zu\n",
lseek(buffer->fd, 0, SEEK_CUR));
}
}
/* Initialize @buffer to hold @size bytes in memory */
void buffer_init(struct buffer *buffer, size_t size)
{
memset(buffer, 0, sizeof(struct buffer));
buffer->addr = mmalloc(size);
buffer->size = size;
}
/* Allocate a new buffer for holding @size bytes in memory */
struct buffer *buffer_alloc(size_t size)
{
struct buffer *buffer;
buffer = mmalloc(sizeof(struct buffer));
buffer_init(buffer, size);
return buffer;
}
/* Forget about any data stored in @buffer */
void buffer_reset(struct buffer *buffer)
{
buffer->total = 0;
buffer->off = 0;
if (buffer->fd_open) {
if (ftruncate(buffer->fd, 0))
mwarn("Cannot truncate temporary file");
if (lseek(buffer->fd, 0, SEEK_SET) == (off_t) -1)
mwarn("Cannot seek in temporary file");
}
}
/* Close buffer file associated with @buffer */
void buffer_close(struct buffer *buffer)
{
if (!buffer->fd_open)
return;
fclose(buffer->file);
buffer->fd = 0;
buffer->fd_open = false;
}
/* Release all resources associated with @buffer. If @dyn is %true, also free
* @buffer itself. */
void buffer_free(struct buffer *buffer, bool dyn)
{
if (!buffer)
return;
buffer_reset(buffer);
buffer_close(buffer);
free(buffer->addr);
if (dyn)
free(buffer);
}
/* Open a buffer file for @buffer. Return %EXIT_OK on success, %EXIT_RUNTIME
* otherwise. */
int buffer_open(struct buffer *buffer)
{
if (buffer->fd_open)
return EXIT_OK;
buffer->file = tmpfile();
if (!buffer->file) {
mwarn("Could not create temporary file");
return EXIT_RUNTIME;
}
buffer->fd = fileno(buffer->file);
buffer->fd_open = true;
return EXIT_OK;
}
/* Write data in memory of @buffer to buffer file. Return %EXIT_OK on success,
* %EXIT_RUNTIME otherwise. */
int buffer_flush(struct buffer *buffer)
{
if (buffer->off == 0)
return EXIT_OK;
if (buffer_open(buffer))
return EXIT_RUNTIME;
if (misc_write_data(buffer->fd, buffer->addr, buffer->off)) {
mwarn("Could not write to temporary file");
return EXIT_RUNTIME;
}
buffer->off = 0;
return EXIT_OK;
}
/* Try to ensure that at least @size bytes are available at
* @buffer->addr[buffer->off]. Return the actual number of bytes available or
* @-1 on error. If @usefile is %true, make use of a buffer file if
* the total buffer size exceeds @max_buffer_size. */
ssize_t buffer_make_room(struct buffer *buffer, size_t size, bool usefile,
size_t max_buffer_size)
{
size_t needsize;
if (size > max_buffer_size && usefile)
size = max_buffer_size;
needsize = buffer->off + size;
if (needsize <= buffer->size) {
/* Room available */
return size;
}
if (needsize > max_buffer_size && usefile) {
/* Need to write out memory buffer to buffer file */
if (buffer_flush(buffer))
return -1;
if (size <= buffer->size)
return size;
needsize = size;
}
/* Need to increase memory buffer size */
buffer->size = needsize;
buffer->addr = mrealloc(buffer->addr, buffer->size);
return size;
}
/* Try to read @chunk bytes from @fd to @buffer. Return the number of bytes
* read on success, %0 on EOF or %-1 on error. */
ssize_t buffer_read_fd(struct buffer *buffer, int fd, size_t chunk,
bool usefile, size_t max_buffer_size)
{
ssize_t c = buffer_make_room(buffer, chunk, usefile, max_buffer_size);
DBG("buffer_read_fd wanted %zd got %zd", chunk, c);
if (c < 0)
return c;
c = read(fd, buffer->addr + buffer->off, c);
if (c > 0) {
buffer->total += c;
buffer->off += c;
}
return c;
}
/* Add @len bytes at @addr to @buffer. If @addr is %NULL, add zeroes. Return
* %EXIT_OK on success, %EXIT_RUNTIME otherwise. */
int buffer_add_data(struct buffer *buffer, char *addr, size_t len, bool usefile,
size_t max_buffer_size)
{
ssize_t c;
while (len > 0) {
c = buffer_make_room(buffer, len, usefile, max_buffer_size);
if (c < 0)
return EXIT_RUNTIME;
if (addr) {
memcpy(buffer->addr + buffer->off, addr, c);
addr += c;
} else {
memset(buffer->addr + buffer->off, 0, c);
}
buffer->total += c;
buffer->off += c;
len -= c;
}
return EXIT_OK;
}
/* Call @cb for all chunks of data in @buffer. @data is passed to @cb. */
int buffer_iterate(struct buffer *buffer, buffer_cb_t cb, void *data)
{
int rc;
ssize_t r;
if (buffer->total == 0)
return EXIT_OK;
if (!buffer->fd_open)
return cb(data, buffer->addr, buffer->off);
/* Free memory buffer to be used as copy buffer */
if (buffer_flush(buffer))
return EXIT_RUNTIME;
if (lseek(buffer->fd, 0, SEEK_SET) == (off_t) -1) {
mwarn("Cannot seek in temporary file");
return EXIT_RUNTIME;
}
/* Copy data from temporary file to target file */
while ((r = misc_read_data(buffer->fd, buffer->addr,
buffer->size)) != 0) {
if (r < 0) {
mwarn("Cannot read from temporary file");
return EXIT_RUNTIME;
}
rc = cb(data, buffer->addr, r);
if (rc)
return rc;
}
return EXIT_OK;
}
/* Truncate @buffer to at most @len bytes */
int buffer_truncate(struct buffer *buffer, size_t len)
{
size_t delta;
if (buffer->total <= len)
return EXIT_OK;
delta = buffer->total - len;
buffer->total = len;
if (buffer->fd_open && delta > buffer->off) {
/* All of memory and some of file buffer is truncated */
buffer->off = 0;
if (ftruncate(buffer->fd, len)) {
mwarn("Cannot truncate temporary file");
return EXIT_RUNTIME;
}
if (lseek(buffer->fd, len, SEEK_SET) == (off_t) -1) {
mwarn("Cannot seek in temporary file");
return EXIT_RUNTIME;
}
} else {
/* Only memory buffer is truncated */
buffer->off -= delta;
}
return EXIT_OK;
}

94
dump2tar/src/dref.c Normal file
View File

@@ -0,0 +1,94 @@
/*
* dump2tar - tool to dump files and command output into a tar archive
*
* Reference counting for directory handles
*
* 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 <dirent.h>
#include <pthread.h>
#include <sys/types.h>
#include "dref.h"
#include "global.h"
#include "misc.h"
/* dref_mutex serializes access to drefs */
static pthread_mutex_t dref_mutex = PTHREAD_MUTEX_INITIALIZER;
static unsigned long num_open_dirs;
static unsigned long num_open_dirs_max;
/* Lock dref mutex */
static void dref_lock(void)
{
if (!global_threaded)
return;
pthread_mutex_lock(&dref_mutex);
}
/* Unlock dref mutex */
static void dref_unlock(void)
{
if (!global_threaded)
return;
pthread_mutex_unlock(&dref_mutex);
}
/* Create a reference count managed directory handle for @dirname */
struct dref *dref_create(const char *dirname)
{
struct dref *dref;
DIR *dd;
dd = opendir(dirname);
DBG("opendir(%s)=%p (total=%lu)", dirname, dd, ++num_open_dirs);
if (!dd) {
num_open_dirs--;
return NULL;
}
if (num_open_dirs > num_open_dirs_max)
num_open_dirs_max = num_open_dirs;
dref = mmalloc(sizeof(struct dref));
dref->dd = dd;
dref->dirfd = dirfd(dd);
dref->count = 1;
return dref;
}
/* Obtain a reference to @dref */
struct dref *dref_get(struct dref *dref)
{
if (dref) {
dref_lock();
dref->count++;
dref_unlock();
}
return dref;
}
/* Release a reference to @dref. If this was the last reference, lose the
* associated directory handle and free @dref. */
void dref_put(struct dref *dref)
{
if (dref) {
dref_lock();
dref->count--;
if (dref->count == 0) {
num_open_dirs--;
DBG("closedir(%p) (total=%lu, max=%lu)", dref->dd,
num_open_dirs, num_open_dirs_max);
closedir(dref->dd);
free(dref);
}
dref_unlock();
}
}

1859
dump2tar/src/dump.c Normal file

File diff suppressed because it is too large Load Diff

478
dump2tar/src/dump2tar.c Normal file
View File

@@ -0,0 +1,478 @@
/*
* dump2tar - tool to dump files and command output into a tar archive
*
* Command line interface
*
* 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 <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "lib/util_opt.h"
#include "lib/util_prg.h"
#include "dump.h"
#include "global.h"
#include "idcache.h"
#include "misc.h"
#include "strarray.h"
#define MIN_BUFFER_SIZE 4096
#define OPT_NOSHORT_BASE 256
#define OPT_DEREFERENCE (OPT_NOSHORT_BASE + 0)
#define OPT_NORECURSION (OPT_NOSHORT_BASE + 1)
#define OPT_EXCLUDETYPE (OPT_NOSHORT_BASE + 2)
/* Program description */
static const struct util_prg dump2tar_prg = {
.desc = "Use dump2tar to create a tar archive from the contents "
"of arbitrary files.\nIt works even when the size of actual "
"file content is not known beforehand,\nsuch as with FIFOs, "
"character devices or certain Linux debugfs or sysfs files.\n"
"\nYou can also add files under different names and add "
"command output using the\nformat described in section SPECS "
"below. When no additional options are\nspecified, the "
"resulting archive is written to the standard output stream\n"
"in uncompressed tar format.",
.args = "SPECS",
.copyright_vec = {
{ "IBM Corp.", 2016, 2016 },
UTIL_PRG_COPYRIGHT_END
},
};
/* Definition of command line options */
static struct util_opt dump2tar_opts[] = {
UTIL_OPT_SECTION("OUTPUT OPTIONS"),
{
.option = { "output-file", required_argument, NULL, 'o' },
.argument = "FILE",
.desc = "Write archive to FILE (default: standard output)",
},
#ifdef HAVE_ZLIB
{
.option = { "gzip", no_argument, NULL, 'z' },
.desc = "Write a gzip compressed archive",
},
#endif /* HAVE_ZLIB */
{
.option = { "max-size", required_argument, NULL, 'm' },
.argument = "N",
.desc = "Stop adding files when archive size exceeds N bytes",
},
{
.option = { "timeout", required_argument, NULL, 't' },
.argument = "SEC",
.desc = "Stop adding files after SEC seconds",
},
{
.option = { "no-eof", no_argument, NULL, 131 },
.desc = "Do not write an end-of-file marker",
.flags = UTIL_OPT_FLAG_NOSHORT,
},
{
.option = { "add-cmd-status", no_argument, NULL, 132 },
.desc = "Add status of commands as separate file",
.flags = UTIL_OPT_FLAG_NOSHORT,
},
{
.option = { "append", no_argument, NULL, 133 },
.desc = "Append output to end of file",
.flags = UTIL_OPT_FLAG_NOSHORT,
},
UTIL_OPT_SECTION("INPUT OPTIONS"),
{
.option = { "files-from", required_argument, NULL, 'F' },
.argument = "FILE",
.desc = "Read filenames from FILE (- for standard input)",
},
{
.option = { "ignore-failed-read", no_argument, NULL, 'i' },
.desc = "Continue after read errors",
},
{
.option = { "buffer-size", required_argument, NULL, 'b' },
.argument = "N",
.desc = "Read data in chunks of N byte (default: 16384)",
},
{
.option = { "file-timeout", required_argument, NULL, 'T' },
.desc = "Stop reading file after SEC seconds",
},
{
.option = { "file-max-size", required_argument, NULL, 'M' },
.argument = "N",
.desc = "Stop reading file after N bytes",
},
{
.option = { "jobs", required_argument, NULL, 'j' },
.argument = "N",
.desc = "Read N files in parallel (default: 1)",
},
{
.option = { "jobs-per-cpu", required_argument, NULL, 'J' },
.argument = "N",
.desc = "Read N files per CPU in parallel",
},
{
.option = { "exclude", required_argument, NULL, 'x' },
.argument = "PATTERN",
.desc = "Don't add files matching PATTERN",
},
{
.option = { "exclude-from", required_argument, NULL, 'X' },
.argument = "FILE",
.desc = "Don't add files matching patterns in FILE",
},
{
.option = { "exclude-type", required_argument, NULL,
OPT_EXCLUDETYPE },
.argument = "TYPE",
.desc = "Don't add files of specified TYPE (one of: fdcbpls)",
.flags = UTIL_OPT_FLAG_NOSHORT,
},
{
.option = { "dereference", no_argument, NULL, OPT_DEREFERENCE },
.desc = "Add link targets instead of links",
.flags = UTIL_OPT_FLAG_NOSHORT,
},
{
.option = { "no-recursion", no_argument, NULL,
OPT_NORECURSION },
.desc = "Don't add files from sub-directories",
.flags = UTIL_OPT_FLAG_NOSHORT,
},
UTIL_OPT_SECTION("MISC OPTIONS"),
UTIL_OPT_HELP,
UTIL_OPT_VERSION,
{
.option = { "verbose", no_argument, NULL, 'V' },
.desc = "Print additional informational output",
},
{
.option = { "quiet", no_argument, NULL, 'q' },
.desc = "Suppress printing of informational output",
},
UTIL_OPT_END,
};
/* Split buffer size specification in @arg into two numbers to be stored in
* @from_ptr and @to_ptr. Return %EXIT_OK on success. */
static int parse_buffer_size(char *arg, size_t *from_ptr, size_t *to_ptr)
{
char *err;
unsigned long from, to;
if (!*arg) {
mwarnx("Empty buffer size specified");
return EXIT_USAGE;
}
from = strtoul(arg, &err, 10);
if (*err == '-')
to = strtoul(err + 1, &err, 10);
else
to = *to_ptr;
if (*err) {
mwarnx("Invalid buffer size: %s", arg);
return EXIT_USAGE;
}
if (from < MIN_BUFFER_SIZE || to < MIN_BUFFER_SIZE) {
mwarnx("Buffer size too low (minimum %u)", MIN_BUFFER_SIZE);
return EXIT_USAGE;
}
if (to < from)
to = from;
*from_ptr = from;
*to_ptr = to;
return EXIT_OK;
}
static void parse_and_add_spec(struct dump_opts *opts, const char *spec)
{
char *op, *s, *inname, *outname = NULL;
bool is_cmd = false;
s = mstrdup(spec);
op = strstr(s, "|=");
if (op)
is_cmd = true;
else
op = strstr(s, ":=");
if (op) {
*op = 0;
inname = op + 2;
outname = s;
} else {
inname = s;
}
dump_opts_add_spec(opts, inname, outname, is_cmd);
free(s);
}
static int add_specs_from_file(struct dump_opts *opts, const char *filename)
{
FILE *fd;
char *line = NULL;
size_t line_size;
int rc = EXIT_RUNTIME;
bool need_close = false, parse_spec = true;
if (strcmp(filename, "-") == 0)
fd = stdin;
else {
fd = fopen(filename, "r");
if (!fd) {
mwarn("%s: Cannot open file", filename);
goto out;
}
need_close = true;
}
while ((getline(&line, &line_size, fd) != -1)) {
chomp(line, "\n");
if (line[0] == 0)
continue;
if (parse_spec && strcmp(line, "--") == 0) {
/* After a line containing --, no more := or |= specs
* are expected */
parse_spec = false;
continue;
}
if (parse_spec)
parse_and_add_spec(opts, line);
else
dump_opts_add_spec(opts, line, NULL, false);
}
if (ferror(fd))
mwarn("%s: Cannot read file", filename);
else
rc = EXIT_OK;
out:
if (need_close)
fclose(fd);
free(line);
return rc;
}
static void print_help(void)
{
static const struct {
const char *name;
const char *desc;
} specs[] = {
{ "PATH", "Add file or directory at PATH" },
{ "NEWPATH:=PATH", "Add file or directory at PATH as NEWPATH" },
{ "NEWPATH|=CMDLINE", "Add output of command line CMDLINE as "
"NEWPATH" },
{ NULL, NULL },
};
int i;
util_prg_print_help();
printf("SPECS\n");
for (i = 0; specs[i].name; i++)
util_opt_print_indented(specs[i].name, specs[i].desc);
printf("\n");
util_opt_print_help();
}
int main(int argc, char *argv[])
{
int rc = EXIT_USAGE, opt;
long i;
struct dump_opts *opts;
if (getenv("DUMP2TAR_DEBUG"))
global_debug = true;
util_prg_init(&dump2tar_prg);
util_opt_init(dump2tar_opts, "-");
misc_init();
opts = dump_opts_new();
opterr = 0;
while ((opt = util_opt_getopt_long(argc, argv)) != -1) {
switch (opt) {
case 'h': /* --help */
print_help();
rc = EXIT_OK;
goto out;
case 'v': /* --version */
util_prg_print_version();
rc = EXIT_OK;
goto out;
case 'V': /* --verbose */
global_verbose = true;
global_quiet = false;
opts->verbose = true;
opts->quiet = false;
break;
case 'q': /* --quiet */
global_quiet = true;
global_verbose = false;
opts->quiet = true;
opts->verbose = false;
break;
case 'i': /* --ignore-failed-read */
opts->ignore_failed_read = true;
break;
case 'j': /* --jobs N */
opts->jobs = atoi(optarg);
if (opts->jobs < 1) {
mwarnx("Invalid number of jobs: %s", optarg);
goto out;
}
break;
case 'J': /* --jobs-per-cpu N */
opts->jobs_per_cpu = atoi(optarg);
if (opts->jobs_per_cpu < 1) {
mwarnx("Invalid number of jobs: %s", optarg);
goto out;
}
break;
case 'b': /* --buffer-size N */
if (parse_buffer_size(optarg, &opts->read_chunk_size,
&opts->max_buffer_size))
goto out;
break;
case 'x': /* --exclude PATTERN */
add_str_to_strarray(&opts->exclude, optarg);
break;
case 'X': /* --exclude-from FILE */
if (add_file_to_strarray(&opts->exclude, optarg))
goto out;
break;
case 'F': /* --files-from FILE */
if (add_specs_from_file(opts, optarg))
goto out;
break;
case 'o': /* --output-file FILE */
if (opts->output_file) {
mwarnx("Output file specified multiple times");
goto out;
}
opts->output_file = optarg;
break;
case OPT_DEREFERENCE: /* --dereference */
opts->dereference = true;
break;
case OPT_NORECURSION: /* --no-recursion */
opts->recursive = false;
break;
case OPT_EXCLUDETYPE: /* --exclude-type TYPE */
for (i = 0; optarg[i]; i++) {
if (dump_opts_set_type_excluded(opts,
optarg[i]))
break;
}
if (optarg[i]) {
mwarnx("Unrecognized file type: %c", optarg[i]);
goto out;
}
break;
case 131: /* --no-eof */
opts->no_eof = true;
break;
case 132: /* --add-cmd-status */
opts->add_cmd_status = true;
break;
case 133: /* --append */
opts->append = true;
break;
case 't': /* --timeout VALUE */
opts->timeout = atoi(optarg);
if (opts->timeout < 1) {
mwarnx("Invalid timeout value: %s", optarg);
goto out;
}
break;
case 'T': /* --file-timeout VALUE */
opts->file_timeout = atoi(optarg);
if (opts->file_timeout < 1) {
mwarnx("Invalid timeout value: %s", optarg);
goto out;
}
break;
case 'm': /* --max-size N */
opts->max_size = atol(optarg);
if (opts->max_size < 2) {
mwarnx("Invalid maximum size: %s", optarg);
goto out;
}
break;
case 'M': /* --file-max-size N */
opts->file_max_size = atol(optarg);
if (opts->file_max_size < 2) {
mwarnx("Invalid maximum size: %s", optarg);
goto out;
}
break;
case 'z': /* --gzip */
opts->gzip = true;
break;
case 1: /* Filename specification or unrecognized option */
if (optarg[0] == '-') {
mwarnx("Invalid option '%s'", optarg);
goto out;
}
parse_and_add_spec(opts, optarg);
break;
case '?': /* Unrecognized option */
if (optopt)
mwarnx("Invalid option '-%c'", optopt);
else
mwarnx("Invalid option '%s'", argv[optind - 1]);
goto out;
case ':': /* Missing argument */
mwarnx("Option '%s' requires an argument",
argv[optind - 1]);
goto out;
default:
break;
}
}
if (optind >= argc && opts->num_specs == 0) {
mwarnx("Please specify files to dump");
goto out;
}
for (i = optind; i < argc; i++)
dump_opts_add_spec(opts, argv[i], NULL, false);
rc = dump_to_tar(opts);
out:
idcache_cleanup();
misc_cleanup();
dump_opts_free(opts);
if (rc == EXIT_USAGE)
util_prg_print_parse_error();
return rc;
}

20
dump2tar/src/global.c Normal file
View File

@@ -0,0 +1,20 @@
/*
* dump2tar - tool to dump files and command output into a tar archive
*
* Global variables
*
* 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 "global.h"
/* Global settings */
bool global_threaded;
bool global_debug;
bool global_verbose;
bool global_quiet;
bool global_timestamps;

155
dump2tar/src/idcache.c Normal file
View File

@@ -0,0 +1,155 @@
/*
* dump2tar - tool to dump files and command output into a tar archive
*
* Caches for user and group ID lookups
*
* 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 <grp.h>
#include <pthread.h>
#include <pwd.h>
#include <stdlib.h>
#include <string.h>
#include "global.h"
#include "idcache.h"
#include "misc.h"
/* Maximum user and group name lengths as defined in tar header */
#define ID_NAME_MAXLEN 32
/* Types for user and group ID caches */
typedef uid_t generic_id_t; /* Assumes that uid_t == gid_t */
struct id_cache_entry {
generic_id_t id;
char name[ID_NAME_MAXLEN];
};
struct id_cache {
unsigned int num;
struct id_cache_entry entries[];
};
/* cache_mutex serializes access to cached uid and gid data */
static pthread_mutex_t id_cache_mutex = PTHREAD_MUTEX_INITIALIZER;
static struct id_cache *id_cache_uid;
static struct id_cache *id_cache_gid;
/* Lock cache mutex */
static void cache_lock(void)
{
if (!global_threaded)
return;
pthread_mutex_lock(&id_cache_mutex);
}
/* Unlock cache mutex */
static void cache_unlock(void)
{
if (!global_threaded)
return;
pthread_mutex_unlock(&id_cache_mutex);
}
/* Copy the name associated with @id in @cache to at most @len bytes at @dest.
* Return %true if name was found in cache, %false otherwise. */
static bool strncpy_id_cache_entry(char *dest, struct id_cache *cache,
generic_id_t id, size_t len)
{
unsigned int i;
bool hit = false;
cache_lock();
if (cache) {
for (i = 0; i < cache->num; i++) {
if (cache->entries[i].id == id) {
strncpy(dest, cache->entries[i].name, len);
hit = true;
break;
}
}
}
cache_unlock();
return hit;
}
/* Add a new entry consisting of @id and @name to ID cache in @*cache_ptr.
* Update @cache_ptr if necessary. */
static void add_id_cache_entry(struct id_cache **cache_ptr, generic_id_t id,
char *name)
{
struct id_cache *cache;
unsigned int cache_num;
size_t new_size;
struct id_cache *new_cache;
cache_lock();
cache = *cache_ptr;
cache_num = cache ? cache->num : 0;
new_size = sizeof(struct id_cache) +
sizeof(struct id_cache_entry) * (cache_num + 1);
new_cache = mrealloc(cache, new_size);
if (cache_num == 0)
new_cache->num = 0;
new_cache->entries[cache_num].id = id;
strncpy(new_cache->entries[cache_num].name, name, ID_NAME_MAXLEN);
new_cache->num++;
*cache_ptr = new_cache;
cache_unlock();
}
/* Copy the user name corresponding to user ID @uid to at most @len bytes
* at @name */
void uid_to_name(uid_t uid, char *name, size_t len)
{
struct passwd pwd, *pwd_ptr;
char buffer[PWD_BUFFER_SIZE], *result;
if (strncpy_id_cache_entry(name, id_cache_uid, uid, len))
return;
/* getpwuid() can be slow so cache results */
getpwuid_r(uid, &pwd, buffer, PWD_BUFFER_SIZE, &pwd_ptr);
if (!pwd_ptr || !pwd_ptr->pw_name)
return;
result = pwd_ptr->pw_name;
add_id_cache_entry(&id_cache_uid, uid, result);
strncpy(name, result, len);
}
/* Copy the group name corresponding to group ID @gid to at most @len bytes
* at @name */
void gid_to_name(gid_t gid, char *name, size_t len)
{
struct group grp, *grp_ptr;
char buffer[GRP_BUFFER_SIZE], *result;
if (strncpy_id_cache_entry(name, id_cache_gid, gid, len))
return;
/* getgrgid() can be slow so cache results */
getgrgid_r(gid, &grp, buffer, GRP_BUFFER_SIZE, &grp_ptr);
if (!grp_ptr || !grp_ptr->gr_name)
return;
result = grp_ptr->gr_name;
add_id_cache_entry(&id_cache_gid, gid, result);
strncpy(name, result, len);
}
void idcache_cleanup(void)
{
free(id_cache_uid);
free(id_cache_gid);
}

492
dump2tar/src/misc.c Normal file
View File

@@ -0,0 +1,492 @@
/*
* dump2tar - tool to dump files and command output into a tar archive
*
* 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 <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#include "dref.h"
#include "global.h"
#include "misc.h"
struct timespec main_start_ts;
static pthread_key_t thread_name_key;
static bool stdout_data;
/* Write @len bytes at @addr to @fd. Return %EXIT_OK on success, %EXIT_RUNTIME
* otherwise. */
int misc_write_data(int fd, char *addr, size_t len)
{
ssize_t w;
while (len > 0) {
w = write(fd, addr, len);
if (w < 0)
return EXIT_RUNTIME;
len -= w;
addr += w;
}
return EXIT_OK;
}
/* Read at most @len bytes from @fd to @addr. Return the number of bytes read
* or %-1 on error. */
ssize_t misc_read_data(int fd, char *addr, size_t len)
{
size_t done = 0;
ssize_t r;
while (len > 0) {
r = read(fd, addr, len);
if (r < 0)
return -1;
if (r == 0)
break;
len -= r;
addr += r;
done += r;
}
return done;
}
/* Advance timespec @ts by @sec seconds and @nsec nanoseconds */
void inc_timespec(struct timespec *ts, time_t sec, long nsec)
{
ts->tv_nsec += nsec;
ts->tv_sec += sec;
if (ts->tv_nsec > NSEC_PER_SEC) {
ts->tv_nsec -= NSEC_PER_SEC;
ts->tv_sec++;
}
}
/* Set timespec @ts to point to @sec seconds and @nsec nanoseconds in the
* future */
void set_timespec(struct timespec *ts, time_t sec, long nsec)
{
clock_gettime(CLOCK_MONOTONIC, ts);
inc_timespec(ts, sec, nsec);
}
/* Return true if timespec @a refers to a point in time before @b */
bool ts_before(struct timespec *a, struct timespec *b)
{
if (a->tv_sec < b->tv_sec ||
(a->tv_sec == b->tv_sec && a->tv_nsec < b->tv_nsec))
return true;
return false;
}
/* Store a string representing the time duration between @start and @end in
* at most @len bytes of @buff. */
int snprintf_duration(char *buff, size_t len, struct timespec *start,
struct timespec *end)
{
time_t sec;
long nsec, msec, s, m, h;
sec = end->tv_sec - start->tv_sec;
nsec = end->tv_nsec - start->tv_nsec;
if (nsec < 0) {
nsec += NSEC_PER_SEC;
sec--;
}
msec = nsec / NSEC_PER_MSEC;
s = sec % 60;
sec /= 60;
m = sec % 60;
sec /= 60;
h = sec;
if (h > 0)
return snprintf(buff, len, "%luh%lum%lu.%03lus", h, m, s, msec);
else if (m > 0)
return snprintf(buff, len, "%lum%lu.%03lus", m, s, msec);
else
return snprintf(buff, len, "%lu.%03lus", s, msec);
}
/* Return the name of the current thread */
char *get_threadname(void)
{
return pthread_getspecific(thread_name_key);
}
static int snprintf_timestamp(char *str, size_t size)
{
struct timespec now_ts;
set_timespec(&now_ts, 0, 0);
now_ts.tv_sec -= main_start_ts.tv_sec;
now_ts.tv_nsec -= main_start_ts.tv_nsec;
if (now_ts.tv_nsec < 0) {
now_ts.tv_nsec += NSEC_PER_SEC;
now_ts.tv_sec--;
}
return snprintf(str, size, "[%3lu.%06lu] ", now_ts.tv_sec,
now_ts.tv_nsec / NSEC_PER_USEC);
}
/* When DUMP2TAR_DEBUG is set to non-zero, print debugging information */
void debug(const char *file, unsigned long line, const char *format, ...)
{
char msg[MSG_LEN];
size_t off = 0;
int rc;
va_list args;
/* Debug marker */
rc = snprintf(&msg[off], MSG_LEN - off, "DEBUG: ");
HANDLE_RC(rc, MSG_LEN, off, out);
/* Timestamp */
rc = snprintf_timestamp(&msg[off], MSG_LEN - off);
HANDLE_RC(rc, MSG_LEN, off, out);
/* Thread name */
rc = snprintf(&msg[off], MSG_LEN - off, "%s: ", get_threadname());
HANDLE_RC(rc, MSG_LEN, off, out);
/* Message */
va_start(args, format);
rc = vsnprintf(&msg[off], MSG_LEN - off, format, args);
va_end(args);
HANDLE_RC(rc, MSG_LEN, off, out);
/* Call site */
rc = snprintf(&msg[off], MSG_LEN - off, " (%s:%lu)", file, line);
out:
fprintf(stderr, "%s\n", msg);
}
/* Print a warning message consisting of @format and variable arguments.
* If @print_errno is true, also print the text corresponding to errno.
* We're not using err.h's warn since we want timestamps and synchronized
* output. */
void _mwarn(bool print_errno, const char *format, ...)
{
char msg[MSG_LEN];
size_t off = 0;
int rc;
va_list args;
if (global_timestamps) {
rc = snprintf_timestamp(&msg[off], MSG_LEN - off);
HANDLE_RC(rc, MSG_LEN, off, out);
}
rc = snprintf(&msg[off], MSG_LEN - off, "%s: ",
program_invocation_short_name);
HANDLE_RC(rc, MSG_LEN, off, out);
va_start(args, format);
rc = vsnprintf(&msg[off], MSG_LEN - off, format, args);
va_end(args);
HANDLE_RC(rc, MSG_LEN, off, out);
if (print_errno)
snprintf(&msg[off], MSG_LEN - off, ": %s", strerror(errno));
out:
fprintf(stderr, "%s\n", msg);
}
/* Provide informational output if --verbose was specified */
void verb(const char *format, ...)
{
char msg[MSG_LEN];
size_t off = 0;
int rc;
va_list args;
FILE *fd;
if (!global_verbose)
return;
if (stdout_data)
fd = stderr;
else
fd = stdout;
if (global_timestamps) {
rc = snprintf_timestamp(&msg[off], MSG_LEN - off);
HANDLE_RC(rc, MSG_LEN, off, out);
}
va_start(args, format);
rc = vsnprintf(&msg[off], MSG_LEN - off, format, args);
va_end(args);
out:
fprintf(fd, "%s", msg);
}
/* Provide informational output. */
void info(const char *format, ...)
{
char msg[MSG_LEN];
size_t off = 0;
int rc;
va_list args;
FILE *fd;
if (global_quiet)
return;
if (stdout_data)
fd = stderr;
else
fd = stdout;
if (global_timestamps) {
rc = snprintf_timestamp(&msg[off], MSG_LEN - off);
HANDLE_RC(rc, MSG_LEN, off, out);
}
va_start(args, format);
rc = vsnprintf(&msg[off], MSG_LEN - off, format, args);
va_end(args);
out:
fprintf(fd, "%s", msg);
}
/* Return a newly allocated buffer containing the result of the specified
* string format arguments */
char *__masprintf(const char *func, const char *file, int line, const char *fmt,
...)
{
char *str;
va_list args;
va_start(args, fmt);
__util_vasprintf(func, file, line, &str, fmt, args);
va_end(args);
return str;
}
/* Set the internal name of the calling thread */
void __set_threadname(const char *func, const char *file, int line,
const char *fmt, ...)
{
char *str;
va_list args;
va_start(args, fmt);
__util_vasprintf(func, file, line, &str, fmt, args);
va_end(args);
pthread_setspecific(thread_name_key, str);
}
/* Clear any previously set thread name */
void clear_threadname(void)
{
void *addr = pthread_getspecific(thread_name_key);
if (addr) {
pthread_setspecific(thread_name_key, NULL);
free(addr);
}
}
/* Remove any number of trailing characters @c in @str */
void chomp(char *str, char *c)
{
ssize_t i;
for (i = strlen(str) - 1; i >= 0 && strchr(c, str[i]); i--)
str[i] = 0;
}
/* Remove any number of leading characters @c in @str */
void lchomp(char *str, char *c)
{
char *from;
for (from = str; *from && strchr(c, *from); from++)
;
if (str != from)
memmove(str, from, strlen(from) + 1);
}
/* Perform a stat on file referenced by either @abs or @rel and @dref. Store
* results in @stat and return stat()'s return code. */
int stat_file(bool dereference, const char *abs, const char *rel,
struct dref *dref, struct stat *st)
{
int rc;
if (dref) {
if (dereference)
rc = fstatat(dref->dirfd, rel, st, 0);
else
rc = fstatat(dref->dirfd, rel, st, AT_SYMLINK_NOFOLLOW);
} else {
if (dereference)
rc = stat(abs, st);
else
rc = lstat(abs, st);
}
return rc;
}
/* Fill stat buffer @st with dummy values. */
void set_dummy_stat(struct stat *st)
{
/* Fake stat */
memset(st, 0, sizeof(struct stat));
st->st_mode = S_IRUSR | S_IWUSR | S_IFREG;
st->st_uid = geteuid();
st->st_gid = getegid();
st->st_mtime = time(NULL);
}
/* Redirect all output streams to @fd and execute command @CMD */
int cmd_child(int fd, char *cmd)
{
char *argv[] = { "/bin/sh", "-c", NULL, NULL };
char *env[] = { NULL };
argv[2] = cmd;
if (dup2(fd, STDOUT_FILENO) == -1 || dup2(fd, STDERR_FILENO) == -1) {
mwarn("Could not redirect command output");
return EXIT_RUNTIME;
}
execve("/bin/sh", argv, env);
return EXIT_RUNTIME;
}
#define PIPE_READ 0
#define PIPE_WRITE 1
/* Run command @cmd as a child process and store its PID in @pid_ptr. On
* success, return a file descriptor that is an output pipe to the standard
* output and standard error streams of the child process. Return %-1 on
* error. */
int cmd_open(char *cmd, pid_t *pid_ptr)
{
int pfd[2];
pid_t pid;
if (pipe(pfd) < 0)
return -1;
pid = fork();
if (pid < 0) {
/* Fork error */
close(pfd[PIPE_READ]);
close(pfd[PIPE_WRITE]);
return -1;
} else if (pid == 0) {
/* Child process */
close(pfd[PIPE_READ]);
exit(cmd_child(pfd[PIPE_WRITE], cmd));
}
/* Parent process */
close(pfd[PIPE_WRITE]);
*pid_ptr = pid;
return pfd[PIPE_READ];
}
/* Close the file descriptor @fd and end the process with PID @pid. When
* not %NULL, use @status_ptr to store the resulting process status. */
int cmd_close(int fd, pid_t pid, int *status_ptr)
{
int status, rc = EXIT_OK;
close(fd);
kill(pid, SIGQUIT);
if (waitpid(pid, &status, 0) == -1) {
status = -errno;
rc = EXIT_RUNTIME;
}
if (status_ptr)
*status_ptr = status;
return rc;
}
void misc_init(void)
{
set_timespec(&main_start_ts, 0, 0);
pthread_key_create(&thread_name_key, free);
set_threadname("main");
}
void misc_cleanup(void)
{
clear_threadname();
pthread_key_delete(thread_name_key);
}
void set_stdout_data(void)
{
stdout_data = true;
}
bool starts_with(const char *str, const char *prefix)
{
size_t len;
len = strlen(prefix);
if (strncmp(str, prefix, len) == 0)
return true;
return false;
}
bool ends_with(const char *str, const char *suffix)
{
size_t str_len, s_len;
str_len = strlen(str);
s_len = strlen(suffix);
if (str_len < s_len)
return false;
if (strcmp(str + str_len - s_len, suffix) != 0)
return false;
return true;
}
/* Remove subsequent slashes in @str */
void remove_double_slashes(char *str)
{
size_t i, to;
char last;
last = 0;
for (i = 0, to = 0; str[i]; i++) {
if (last != '/' || str[i] != '/')
last = str[to++] = str[i];
}
str[to] = 0;
}

83
dump2tar/src/strarray.c Normal file
View File

@@ -0,0 +1,83 @@
/*
* dump2tar - tool to dump files and command output into a tar archive
*
* Dynamically growing string arrays
*
* 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 <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include "misc.h"
#include "strarray.h"
/* Release resources associated with string array @array */
void free_strarray(struct strarray *array)
{
unsigned int i;
for (i = 0; i < array->num; i++)
free(array->str[i]);
free(array->str);
array->str = NULL;
array->num = 0;
}
/* Add string @str to string array @array */
void add_str_to_strarray(struct strarray *array, const char *str)
{
array->str = mrealloc(array->str, sizeof(char *) * (array->num + 2));
array->str[array->num + 1] = NULL;
array->str[array->num] = mstrdup(str);
array->num++;
}
/* Add string resulting from @fmt and additional arguments to @array */
void add_vstr_to_strarray(struct strarray *array, const char *fmt, ...)
{
va_list args;
char *str;
va_start(args, fmt);
util_vasprintf(&str, fmt, args);
va_end(args);
array->str = mrealloc(array->str, sizeof(char *) * (array->num + 2));
array->str[array->num + 1] = NULL;
array->str[array->num] = str;
array->num++;
}
/* Add all lines in file at @filename to @array */
int add_file_to_strarray(struct strarray *array, const char *filename)
{
FILE *fd;
char *line = NULL;
size_t line_size;
int rc = EXIT_OK;
fd = fopen(filename, "r");
if (!fd) {
mwarn("%s: Cannot open file", filename);
return EXIT_RUNTIME;
}
while (!feof(fd) && !ferror(fd)) {
if (getline(&line, &line_size, fd) == -1)
continue;
chomp(line, "\n");
add_str_to_strarray(array, line);
}
if (ferror(fd))
rc = EXIT_RUNTIME;
free(line);
fclose(fd);
return rc;
}

272
dump2tar/src/tar.c Normal file
View File

@@ -0,0 +1,272 @@
/*
* dump2tar - tool to dump files and command output into a tar archive
*
* TAR file generation
*
* 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 <stdio.h>
#include <string.h>
#include "buffer.h"
#include "idcache.h"
#include "misc.h"
#include "tar.h"
#define LONGLINK "././@LongLink"
#define TYPE_LONGLINK 'K'
#define TYPE_LONGNAME 'L'
#define BLOCKSIZE 512
/* Basic TAR header */
struct tar_header {
char name[100];
char mode[8];
char uid[8];
char gid[8];
char size[12];
char mtime[12];
char chksum[8];
char typeflag;
char linkname[100];
char magic[6];
char version[2];
char uname[32];
char gname[32];
char devmajor[8];
char devminor[8];
char prefix[155];
};
/* Store the octal value of @value to at most @len bytes at @dest */
static void set_octal(char *dest, size_t len, unsigned long value)
{
int i;
dest[len - 1] = 0;
for (i = len - 2; i >= 0; i--) {
dest[i] = '0' + (value & 7);
value >>= 3;
}
}
/* Store time @value to at most @len bytes at @dest */
static void set_time(char *dest, size_t len, time_t value)
{
time_t max = (1ULL << (3 * (len - 1))) - 1;
if (value >= 0 && value <= max) {
set_octal(dest, len, value);
return;
}
for (; len > 0; len--) {
dest[len - 1] = value & 0xff;
value >>= 8;
}
dest[0] |= 0x80;
}
#define SET_FIELD(obj, name, value) \
set_octal((obj)->name, sizeof((obj)->name), (unsigned long) (value))
#define SET_TIME_FIELD(obj, name, value) \
set_time((obj)->name, sizeof((obj)->name), (time_t) (value))
#define SET_STR_FIELD(obj, name, value) \
strncpy((obj)->name, (value), sizeof((obj)->name))
/* Initialize the tar file @header with the provided data */
static void init_header(struct tar_header *header, const char *filename,
const char *link, size_t len, struct stat *stat,
char type)
{
unsigned int i, checksum;
unsigned char *c;
memset(header, 0, sizeof(*header));
/* Fill in header fields */
SET_STR_FIELD(header, name, filename);
if (link)
SET_STR_FIELD(header, linkname, link);
SET_FIELD(header, size, len);
if (stat) {
SET_FIELD(header, mode, stat->st_mode & 07777);
SET_FIELD(header, uid, stat->st_uid);
SET_FIELD(header, gid, stat->st_gid);
SET_TIME_FIELD(header, mtime, stat->st_mtime);
uid_to_name(stat->st_uid, header->uname, sizeof(header->uname));
gid_to_name(stat->st_gid, header->gname, sizeof(header->gname));
} else {
SET_FIELD(header, mode, 0644);
SET_FIELD(header, uid, 0);
SET_FIELD(header, gid, 0);
SET_TIME_FIELD(header, mtime, 0);
uid_to_name(0, header->uname, sizeof(header->uname));
gid_to_name(0, header->gname, sizeof(header->gname));
}
header->typeflag = type;
memcpy(header->magic, "ustar ", sizeof(header->magic));
memcpy(header->version, " ", sizeof(header->version));
/* Calculate checksum */
memset(header->chksum, ' ', sizeof(header->chksum));
checksum = 0;
c = (unsigned char *) header;
for (i = 0; i < sizeof(*header); i++)
checksum += c[i];
snprintf(header->chksum, 7, "%06o", checksum);
}
/* Emit zero bytes via @emit_cb to pad @len to a multiple of BLOCKSIZE */
static int emit_padding(emit_cb_t emit_cb, void *data, size_t len)
{
size_t pad = BLOCKSIZE - len % BLOCKSIZE;
char zeroes[BLOCKSIZE];
if (len % BLOCKSIZE > 0) {
memset(zeroes, 0, BLOCKSIZE);
return emit_cb(data, zeroes, pad);
}
return 0;
}
/* Emit @len bytes at @addr via @emit_cb and pad data to BLOCKSIZE with zero
* bytes */
static int emit_data(emit_cb_t emit_cb, void *data, void *addr, size_t len)
{
int rc;
if (len == 0)
return 0;
rc = emit_cb(data, addr, len);
if (rc)
return rc;
return emit_padding(emit_cb, data, len);
}
/* Emit a tar header via @emit_cb */
static int emit_header(emit_cb_t emit_cb, void *data, char *filename,
char *link, size_t len, struct stat *stat, char type)
{
struct tar_header header;
size_t namelen = strlen(filename);
size_t linklen;
int rc;
/* /proc can contain unreadable links which causes tar to complain
* during extract - use a dummy value to handle this more gracefully */
if (link && !*link)
link = " ";
linklen = link ? strlen(link) : 0;
if (linklen > sizeof(header.linkname)) {
rc = emit_header(emit_cb, data, LONGLINK, NULL, linklen + 1,
NULL, TYPE_LONGLINK);
if (rc)
return rc;
rc = emit_data(emit_cb, data, link, linklen + 1);
if (rc)
return rc;
}
if (namelen > sizeof(header.name)) {
rc = emit_header(emit_cb, data, LONGLINK, NULL, namelen + 1,
NULL, TYPE_LONGNAME);
if (rc)
return rc;
rc = emit_data(emit_cb, data, filename, namelen + 1);
if (rc)
return rc;
}
init_header(&header, filename, link, len, stat, type);
return emit_data(emit_cb, data, &header, sizeof(header));
}
struct emit_content_cb_data {
emit_cb_t emit_cb;
void *data;
size_t len;
int rc;
};
/* Callback for emitting a single chunk of data of a buffer */
static int emit_content_cb(void *data, void *addr, size_t len)
{
struct emit_content_cb_data *cb_data = data;
if (len > cb_data->len)
len = cb_data->len;
cb_data->len -= len;
cb_data->rc = cb_data->emit_cb(cb_data->data, addr, len);
if (cb_data->rc || cb_data->len == 0)
return 1;
return 0;
}
/* Emit at most @len bytes of contents of @buffer via @emit_cb and pad output
* to BLOCKSIZE with zero bytes */
static int emit_content(emit_cb_t emit_cb, void *data, struct buffer *buffer,
size_t len)
{
struct emit_content_cb_data cb_data;
cb_data.emit_cb = emit_cb;
cb_data.data = data;
cb_data.len = len;
cb_data.rc = 0;
buffer_iterate(buffer, emit_content_cb, &cb_data);
if (cb_data.rc)
return cb_data.rc;
return emit_padding(emit_cb, data, buffer->total);
}
/* Convert file meta data and content specified as @content into a
* stream of bytes that is reported via the @emit_cb callback. @data is
* passed through to the callback for arbitrary use. */
int tar_emit_file_from_buffer(char *filename, char *link, size_t len,
struct stat *stat, char type,
struct buffer *content, emit_cb_t emit_cb,
void *data)
{
int rc;
DBG("emit tar file=%s type=%d len=%zu", filename, type, len);
rc = emit_header(emit_cb, data, filename, link, len, stat, type);
if (rc)
return rc;
if (content)
rc = emit_content(emit_cb, data, content, len);
return rc;
}
/* Convert file meta data and content specified as @addr and @len into a
* stream of bytes that is reported via the @emit_cb callback. @data is
* passed through to the callback for arbitrary use. */
int tar_emit_file_from_data(char *filename, char *link, size_t len,
struct stat *stat, char type, void *addr,
emit_cb_t emit_cb, void *data)
{
int rc;
DBG("emit tar file=%s type=%d len=%zu", filename, type, len);
rc = emit_header(emit_cb, data, filename, link, len, stat, type);
if (rc)
return rc;
if (addr)
rc = emit_data(emit_cb, data, addr, len);
return rc;
}

166
etc/cpuplugd.conf Normal file
View File

@@ -0,0 +1,166 @@
#
# Exemplary configuration file for the Linux on System z CPU and memory hotplug
# daemon
#
# The file is evaluated by cpuplugd at startup when called with -c.
# It does not contain shell environment variables.
## Type: integer
## Default: 1
#
# The minimum number of CPUs must be > 0.
#
CPU_MIN="1"
## Type: integer
## Default: 0
#
# The maximum number of CPUs to be enabled. If 0 is specified here,
# the maximum number of CPUs equals the number of CPUs detected.
#
CPU_MAX="0"
## Type: integer
## Default: 5
#
# The update interval described how often the current system state
# is checked against the configured set of hotplug and hotunplug rules.
# The update interval is defined in seconds.
#
UPDATE="1"
## Type: integer
## Default: 0
#
# The minimum size of the static page pool (in 4K pages)
#
CMM_MIN="0"
## Type: integer
## Default: 131072
#
# The maximum size of the static page pool (in 4K pages)
#
# Recommended setting is system size minus 256 MB
CMM_MAX="131072" # 512 MB
#
# Variables
#
# User-defined variables are case-sensitive and must not match a pre-defined
# variable or keyword. In the configuration file, definitions
# for user-defined variables must precede their use in expressions.
# Variable names consist of alphanumeric characters (a-z,A-Z,0-9) and
# the "_" character. The maximum name length for a variable is 128 characters,
# and the maximum total size for user-defined variables (names + values) is 4096
# characters.
#
pgscan_d="vmstat.pgscan_direct_dma[0] + vmstat.pgscan_direct_normal[0] + vmstat.pgscan_direct_movable[0]"
pgscan_d1="vmstat.pgscan_direct_dma[1] + vmstat.pgscan_direct_normal[1] + vmstat.pgscan_direct_movable[1]"
# page scan rate in pages / timer tick
pgscanrate="(pgscan_d - pgscan_d1) / (cpustat.total_ticks[0] - cpustat.total_ticks[1])"
# cache usage in kilobytes
avail_cache="meminfo.Cached - meminfo.Shmem"
user_0="(cpustat.user[0] - cpustat.user[1])"
nice_0="(cpustat.nice[0] - cpustat.nice[1])"
system_0="(cpustat.system[0] - cpustat.system[1])"
user_2="(cpustat.user[2] - cpustat.user[3])"
nice_2="(cpustat.nice[2] - cpustat.nice[3])"
system_2="(cpustat.system[2] - cpustat.system[3])"
CP_Active0="(user_0 + nice_0 + system_0) / (cpustat.total_ticks[0] - cpustat.total_ticks[1])"
CP_Active2="(user_2 + nice_2 + system_2) / (cpustat.total_ticks[2] - cpustat.total_ticks[3])"
CP_ActiveAVG="(CP_Active0+CP_Active2) / 2"
idle_0="(cpustat.idle[0] - cpustat.idle[1])"
iowait_0="(cpustat.iowait[0] - cpustat.iowait[1])"
idle_2="(cpustat.idle[2] - cpustat.idle[3])"
iowait_2="(cpustat.iowait[2] - cpustat.iowait[3])"
CP_idle0="(idle_0 + iowait_0) / (cpustat.total_ticks[0] - cpustat.total_ticks[1])"
CP_idle2="(idle_2 + iowait_2) / (cpustat.total_ticks[2] - cpustat.total_ticks[3])"
CP_idleAVG="(CP_idle0 + CP_idle2) / 2"
## Type: string
## Default: "(meminfo.MemFree + cache) / 40"
#
# The amount of pages the static page pool
# is increased if a memunplug rule is matched.
#
# cmm_inc: 10% of free memory, in 4K pages
CMM_INC="meminfo.MemFree / 40"
## Type: string
## Default: "meminfo.MemTotal / 40"
#
# The amount of pages the static page pool
# is decreased if a memplug rule is matched.
#
# cmm_dec: 10% of total memory, in 4K pages
CMM_DEC="meminfo.MemTotal / 40"
#
# Ruledefinitions
#
# Four kinds of rules are distinguished
# (1) hotplug rules, used to enable CPUs
# (2) hotunplug rules, to disable CPUs
# (3) memplug rules, used to increase memory (decrease cmm balloon)
# (4) memunplug rules, used to decrease memory (increase cmm balloon)
#
# Within the hotplug/hotunplug rule definitions the following variables
# can be used:
# - loadavg: the current loadaverage
# - onumcpus: the current number of CPUs which are online
# - runnable_proc: the current amount of runnable processes
# - user: the current user percentage
# - nice: the current nice percentage
# - system: the current system percentage
# - idle: the current idle percentage
# - iowait: the current iowait percentage
# - irq: the current irq percentage
# - softirq: the current softirq percentage
# - steal: the current steal percentage
# - guest: the current guest percentage
# - guest_nice: the current guest_nice percentage
# - cpustat.<name>: data from /proc/stat and /proc/loadavg
# - time: floating point timestamp in "seconds.microseconds"
# since the Unix Epoch (1970-01-01 00:00:00 +0000 (UTC))
#
## Type: string
## Default: "(loadavg > onumcpus + 0.75) & (idle < 10.0)"
#
HOTPLUG="((1 - CP_ActiveAVG) * onumcpus) < 0.08"
## Type: string
## Default: "(loadavg < onumcpus - 0.25) | (idle > 50)"
#
HOTUNPLUG="(CP_idleAVG * onumcpus) > 1.15"
#
# Memplug and memunplug can contain the following keywords:
# - apcr: the amount of page cache operations,
# i.e. pgpin + pgpout from /proc/vmstat
# (in 512 byte blocks / second)
# - freemem: the amount of free memory (in megabytes)
# - swaprate: the number of swap operations, i.e. pswpin + pswpout
# from /proc/vmstat (in pages / second)
# - meminfo.<name>: any value from /proc/meminfo
# - vmstat.<name>: any value from /proc/vmstat
# - time: floating point timestamp in "seconds.microseconds"
# since the Unix Epoch (1970-01-01 00:00:00 +0000 (UTC))
#
# This function is disabled by default, because this rule has to be
# adjusted for each production system, depending on the environment.
#
## Type: string
## Default: "0"
#
#MEMPLUG="pgscanrate > 20"
MEMPLUG="0"
## Type: string
## Default: "0"
#
#MEMUNPLUG="(meminfo.MemFree + avail_cache) > (meminfo.MemTotal / 10)"
MEMUNPLUG="0"

89
etc/init.d/cpacfstatsd Executable file
View File

@@ -0,0 +1,89 @@
#!/bin/bash
### BEGIN INIT INFO
# Provides: cpacfstatsd
# Required-Start: $local_fs
# Required-Stop: $local_fs
# Should-Start:
# Should-Stop:
# Default-Start: 2 3 5
# Default-Stop: 0 1 6
# Short-Description: Start the cpacfstatsd daemon for Linux on System z
# Description: CPACF statistics collection daemon process for Linux on System z
### END INIT INFO
DAEMON=cpacfstatsd
DAEMON_PATH=/usr/sbin/cpacfstatsd
RUN_PID_FILE=/var/run/cpacfstatsd.pid
RETVAL=0
OPTIONS=""
# source function library
. /lib/lsb/init-functions
start()
{
if [ ! -f $RUN_PID_FILE ]; then
echo -n $"Starting $DAEMON:"
$DAEMON_PATH $OPTIONS
if [ $? == "0" ]; then
log_success_msg
else
log_failure_msg
fi
echo
else
echo "$DAEMON (pid $(cat $RUN_PID_FILE)) is already running..."
echo
fi
}
stop()
{
echo -n $"Stopping $DAEMON:"
if [ -f $RUN_PID_FILE ]; then
killproc $DAEMON_PATH -TERM
log_success_msg
rm -f $RUN_PID_FILE
else
log_failure_msg
fi
echo
}
restart() {
stop
sleep 1
start
}
status()
{
if [ ! -f $RUN_PID_FILE ]; then
echo "$DAEMON is not running."
echo
else
echo "$DAEMON (pid $(cat $RUN_PID_FILE)) is running."
echo
fi
}
# How are we called?
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status
;;
restart)
restart
;;
*)
echo "Usage: $DAEMON {start|stop|status|restart}"
RETVAL=1
esac
exit $RETVAL

116
etc/init.d/cpuplugd Executable file
View File

@@ -0,0 +1,116 @@
#!/bin/bash
### BEGIN INIT INFO
# Provides: cpuplugd
# Required-Start: $local_fs $remote_fs
# Required-Stop: $local_fs $remote_fs
# Should-Start:
# Should-Stop:
# Default-Start: 2 3 5
# Default-Stop: 0 1 6
# Short-Description: Start the cpu hotplug daemon for Linux on System z
# Description: Starts the cpuplugd. It uses the configuration
# file /etc/cpuplugd.conf
### END INIT INFO
# chkconfig: 235 01 99
DAEMON=cpuplugd
DAEMON_PATH=/usr/sbin/cpuplugd
CONFIG_FILE=/etc/cpuplugd.conf
RUN_PID_FILE=/var/run/cpuplugd.pid
RETVAL=0
OPTIONS="-c $CONFIG_FILE"
# source function library
. /lib/lsb/init-functions
start()
{
if [ ! -f $RUN_PID_FILE ]; then
echo -n $"Starting $DAEMON:"
$DAEMON_PATH $OPTIONS
if [ $? == "0" ]; then
log_success_msg
else
log_failure_msg
fi
echo
else
echo "$DAEMON (pid $(cat $RUN_PID_FILE)) is already running..."
echo
fi
}
stop()
{
echo -n $"Stopping $DAEMON:"
if [ -f $RUN_PID_FILE ]; then
killproc $DAEMON_PATH -TERM
log_success_msg
rm -f $RUN_PID_FILE
else
log_failure_msg
fi
echo
}
restart() {
stop
#
# We have to wait 2-3 seconds here. When the daemon is stopped it takes
# the time we sleep to reactivate cpus. If we restart to fast and
# cpuplugd wasn't able to restore some settings we may get a undesired
# online cpu count after cpuplugd shutdown
#
sleep 4
start
}
status()
{
if [ ! -f $RUN_PID_FILE ]; then
echo "$DAEMON is not running."
echo
else
echo "$DAEMON (pid $(cat $RUN_PID_FILE), options: $OPTIONS) is running."
echo
fi
}
reload()
{
echo -n $"Reloading $DAEMON: "
if [ -f $RUN_PID_FILE ]; then
killproc $DAEMON_PATH -HUP
log_success_msg
else
log_failure_msg
fi
RETVAL=$?
echo
}
# How are we called?
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status
;;
restart)
restart
;;
reload|force-reload)
reload
;;
*)
echo "Usage: $DAEMON {start|stop|status|restart|reload}"
RETVAL=1
esac
exit $RETVAL

578
etc/init.d/dumpconf Executable file
View File

@@ -0,0 +1,578 @@
#!/bin/bash
### BEGIN INIT INFO
# Provides: dumpconf
# Required-Start: $local_fs
# Required-Stop: $local_fs
# Should-Start:
# Should-Stop:
# Default-Start: 0 1 2 3 5 6
# Default-Stop:
# Short-Description: Configure s390 dump feature
# Description: Configures the s390 dump feature. It uses the configuration file
# /etc/sysconfig/dumpconf
# X-Systemd-RemainAfterExit: true
### END INIT INFO
# chkconfig: 012356 01 99
# This script can be either used stand-alone or as System V init script.
DUMPCONF_BIN=/etc/init.d/dumpconf
DUMP_CONFIG_FILE=/etc/sysconfig/dumpconf
CMDFULL=$0
CMD="dumpconf"
LOCKFILE=/var/lock/$CMD
PIDFILE=/var/run/$CMD.pid
ERRMSG="Check $DUMP_CONFIG_FILE!"
RETVAL=0
BACKGROUND=0
pr_info()
{
if [ $BACKGROUND -eq 0 ]; then
echo "$@"
else
echo "$@" | logger -t dumpconf
fi
}
pr_error()
{
if [ $BACKGROUND -eq 0 ]; then
echo "$@" >&2
else
echo "$@" | logger -t dumpconf
fi
}
check_environment()
{
if [ ! -f $DUMP_CONFIG_FILE ]; then
pr_error "no config file found: $DUMP_CONFIG_FILE"
exit 1
fi
if [ "$(cat /proc/filesystems|grep sysfs)" = "" ]; then
pr_error "no sysfs found"
exit 1
fi
SYSFSDIR=$(cat /proc/mounts|awk '$3=="sysfs"{print $2; exit}')
if [ "$SYSFSDIR" = "" ]; then
pr_error "sysfs not mounted"
exit 1
fi
DUMP_CONFIG_DIR=/$SYSFSDIR/firmware/dump
ON_PANIC_CONFIG_FILE=/$SYSFSDIR/firmware/shutdown_act\
ions/on_panic
ON_RESTART_CONFIG_FILE=/$SYSFSDIR/firmware/shutdown_act\
ions/on_restart
if [ ! -d $DUMP_CONFIG_DIR ]; then
pr_info "kernel has no dump on panic support"
exit 0
fi
REIPL_CONFIG_DIR=/$SYSFSDIR/firmware/reipl
if [ ! -d $REIPL_CONFIG_DIR ]; then
pr_info "kernel has no dump on panic support"
exit 0
fi
VMCMD_CONFIG_DIR=/$SYSFSDIR/firmware/vmcmd
. $DUMP_CONFIG_FILE
}
printhelp()
{
cat <<EOF
Usage: dumpconf [OPTIONS]
This script can be used to configure the dump device which is used by the
Linux kernel in case of a kernel panic.
It uses the configuration file /etc/sysconfig/dumpconf as input.
Options:
-h, --help print this help
-v, --version print version information
start enable configuration defined in /etc/sysconfig/dumpconf
stop disable dump on panic
status show current dump on panic configuration
EOF
}
printversion()
{
cat <<EOF
dumpconf: zSeries dump configuration script version 1.1
Copyright IBM Corp. 2006, 2009
EOF
}
print_invalid_option()
{
cat <<EOF
dumpconf: invalid option -- $1
Try 'dumpconf --help' for more information.
EOF
}
cleanup_pidfile()
{
if [ $(ps $1 | grep $CMD | wc -l) -eq 0 ]; then
rm -f $PIDFILE
fi
}
handle_stop_request()
{
rm -f $PIDFILE 2>/dev/null
exit 0
}
delay_activation()
{
# Open lock file with file descriptor 123
exec 123>$LOCKFILE
if flock -n -x 123; then
if [ -f $PIDFILE ]; then
# concurrent process was faster
exit 0
fi
trap handle_stop_request TERM
echo $$ > $PIDFILE
else
# Nothing to do, "dumpconf start" is already in progress
exit 0
fi
# Close file descriptor 123
exec 123>&-
# Do multiple sleeps in order to be interruptible
for ((i=0; i < $DELAY_MINUTES * 60; i++)); do
sleep 1
done
rm -f $PIDFILE
}
# $1: dump device bus id (e.g. 0.0.4711)
verify_ccw_dump_device()
{
line=$(lsdasd -c $1)
if [ $? -ne 0 ]; then
line=$(lsdasd $1)
fi
if [ "$line" == "" ]; then
pr_info "WARNING: device $1 not found!"
return 1
fi
found=false
for i in $line
do
if [ $found == true ]; then
break
fi
if [ "$i" == "is" ]; then
found=true
fi
done
zgetdump -d /dev/$i > /dev/null 2>&1
if [ $? == 0 ]; then
return 0
else
pr_info "WARNING: $1 is no valid dump device!"
return 1
fi
}
#------------------------------------------------------------------------------
# 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
}
setup_device()
{
DEV="$(CheckDeviceString $DEVICE)"
if [ "$DEV" != "" ]; then
echo $DEV > $1/$2/device
else
RETVAL=1
pr_error "ERROR: Invalid DEVICE '$DEVICE'." $ERRMSG
return
fi
if [ $2 == "fcp" ]; then
echo $WWPN > $1/fcp/wwpn 2>/dev/null || RETVAL=1
if [ $RETVAL -eq 1 ]; then
pr_error "ERROR: Invalid WWPN '$WWPN'." $ERRMSG
return
fi
echo $LUN > $1/fcp/lun 2>/dev/null || RETVAL=1
if [ $RETVAL -eq 1 ]; then
pr_error "ERROR: Invalid LUN '$LUN'." $ERRMSG
return
fi
echo $BOOTPROG > $1/fcp/bootprog 2>/dev/null || RETVAL=1
if [ $RETVAL -eq 1 ]; then
pr_error "ERROR: Invalid BOOTPROG '$BOOTPROG'." $ERRMSG
return
fi
echo $BR_LBA > $1/fcp/br_lba 2>/dev/null || RETVAL=1
if [ $RETVAL -eq 1 ]; then
pr_error "ERROR: Invalid BR_LBA '$BR_LBA'." $ERRMSG
return
fi
fi
}
setup_nss_device()
{
echo $NSS_NAME > $1/nss/name || RETVAL=1
}
setup_reipl()
{
if [ "$REIPL_TYPE" == "" ]; then
pr_info "reipl on panic configured: Using default reipl values."
return
fi
if [ "$REIPL_TYPE" == "ccw" ] || [ "$REIPL_TYPE" == "fcp" ]; then
setup_device $REIPL_CONFIG_DIR $REIPL_TYPE
elif [ "$REIPL_TYPE" == "nss" ]; then
setup_nss_device $REIPL_CONFIG_DIR
else
pr_error "ERROR: Unknown reipl type '$REIPL_TYPE'." $ERRMSG
RETVAL=1
return
fi
echo $REIPL_TYPE > $REIPL_CONFIG_DIR/reipl_type || RETVAL=1
if [ $RETVAL -eq 1 ]; then
return
fi
pr_info "$REIPL_TYPE reipl device configured."
}
setup_dump()
{
if [ "$DUMP_TYPE" == "ccw" ] || [ "$DUMP_TYPE" == "fcp" ]; then
setup_device $DUMP_CONFIG_DIR $DUMP_TYPE
elif [ "$DUMP_TYPE" != "none" ]; then
pr_error "ERROR: Unknown dump type '$DUMP_TYPE'." $ERRMSG
RETVAL=1
return
fi
echo $DUMP_TYPE > $DUMP_CONFIG_DIR/dump_type || RETVAL=1
if [ $RETVAL -eq 1 ]; then
echo none > $DUMP_CONFIG_DIR/dump_type
return
fi
pr_info "$ON_PANIC on panic configured: Using $DUMP_TYPE dump device."
}
setup_on_panic_vmcmd()
{
for I in "$VMCMD_1" "$VMCMD_2" "$VMCMD_3" "$VMCMD_4" "$VMCMD_5" "$VMCMD_6" "$VMCMD_7" "$VMCMD_8";
do
if [ "$I" != "" ]; then
if [ "$VMCMD" != "" ]; then
VMCMD="$VMCMD\\n$I"
else
VMCMD=$I
fi
fi
done
if [ ! -d $VMCMD_CONFIG_DIR ]; then
pr_error "ERROR: No vmcmd support. Are you running on LPAR?"
RETVAL=1
elif [ "$VMCMD" == "" ]; then
pr_error "ERROR: No VMCMD_x keyword specified." $ERRMSG
RETVAL=1
else
echo -en "$VMCMD" | cat > $VMCMD_CONFIG_DIR/on_panic || RETVAL=1
fi
if [ $RETVAL -eq 0 ]; then
pr_info "vmcmd on panic configured:"
pr_info -e "$VMCMD"
fi
}
print_fcp_device()
{
DEVICE=$(cat $1/fcp/device) || RETVAL=1
pr_info "device..: $DEVICE"
WWPN=$(cat $1/fcp/wwpn) || RETVAL=1
pr_info "wwpn....: $WWPN"
LUN=$(cat $1/fcp/lun) || RETVAL=1
pr_info "lun.....: $LUN"
BOOTPROG=$(cat $1/fcp/bootprog) || RETVAL=1
pr_info "bootprog: $BOOTPROG"
BR_LBA=$(cat $1/fcp/br_lba) || RETVAL=1
pr_info "br_lba..: $BR_LBA"
}
print_ccw_device()
{
DEVICE=$(cat $1/ccw/device) || RETVAL=1
pr_info "device..: $DEVICE"
}
print_nss_name()
{
NAME=$(cat $1/nss/device) || RETVAL=1
pr_info "device..: $NAME"
}
status_dump()
{
CONF_DUMP_TYPE=$(cat $DUMP_CONFIG_DIR/dump_type) || RETVAL=1
if [ "$CONF_DUMP_TYPE" == "none" ]; then
pr_info "type....: no dump device configured"
elif [ "$CONF_DUMP_TYPE" == "ccw" ]; then
pr_info "type....: ccw"
print_ccw_device $DUMP_CONFIG_DIR
verify_ccw_dump_device $(cat $DUMP_CONFIG_DIR/ccw/device)
elif [ "$CONF_DUMP_TYPE" == "fcp" ]; then
pr_info "type....: fcp"
print_fcp_device $DUMP_CONFIG_DIR
else
pr_error "ERROR: Unknown dump device type '$CONF_DUMP_TYPE'!"
pr_error " Please check if you have the latest dumpconf package!"
fi
}
status_reipl()
{
REIPL_TYPE=$(cat $REIPL_CONFIG_DIR/reipl_type) || RETVAL=1
pr_info "type....: $REIPL_TYPE"
if [ "$REIPL_TYPE" == "ccw" ]; then
print_ccw_device $REIPL_CONFIG_DIR
elif [ "$REIPL_TYPE" == "fcp" ]; then
print_fcp_device $REIPL_CONFIG_DIR
elif [ "$REIPL_TYPE" == "nss" ]; then
print_nss_name $REIPL_CONFIG_DIR
else
pr_error "ERROR: Unknown reipl device type '$REIPL_TYPE'!"
pr_error " Please check if you have the latest dumpconf package!"
fi
}
status_dump_reipl()
{
pr_info -e "\ndump:"
status_dump
pr_info -e "\nreipl:"
status_reipl
}
status_vmcmd()
{
VMCMD=$(cat $VMCMD_CONFIG_DIR/on_panic) || RETVAL=1
if [ "$VMCMD" == "" ]; then
pr_info "WARNING: No VM command specified!"
else
pr_info "---------------"
pr_info "$VMCMD"
fi
}
start()
{
if [ "$1" == "background" ]; then
BACKGROUND=1
fi
test -n "$DELAY_MINUTES" || DELAY_MINUTES=0
test "$DELAY_MINUTES" -ge 0 2>/dev/null || RETVAL=1
if [ $RETVAL -eq 1 ]; then
pr_error "ERROR: Invalid DELAY_MINUTES parameter" \
"'$DELAY_MINUTES'." $ERRMSG
return
fi
if [ "$ON_PANIC" != "stop" -a $DELAY_MINUTES -gt 0 ]; then
if [ -f $PIDFILE ]; then
pr_info "A delayed instance of" $CMD \
"is already active."
return
fi
if [ $BACKGROUND -eq 1 ]; then
delay_activation
else
pr_info "The activation of dumpconf is being delayed" \
"for" $DELAY_MINUTES "minutes"
$CMDFULL start background > /dev/null 2>&1 &
return
fi
fi
if [ "$ON_PANIC" == "" ]; then
ON_PANIC="$(cat $ON_PANIC_CONFIG_FILE)"
fi
case "$ON_PANIC" in
reipl)
setup_reipl
;;
dump|dump_reipl)
setup_dump
;;
vmcmd)
setup_on_panic_vmcmd
;;
stop)
pr_info "stop on panic configured."
;;
*)
pr_error "ERROR: Unknown 'on panic'" \
"type '$ON_PANIC'." $ERRMSG
RETVAL=1
;;
esac
if [ $RETVAL -eq 1 ]; then
return
fi
if [ -f $ON_RESTART_CONFIG_FILE ]; then
echo $ON_PANIC > $ON_RESTART_CONFIG_FILE 2> /dev/null || RETVAL=1
fi
echo $ON_PANIC > $ON_PANIC_CONFIG_FILE 2> /dev/null || RETVAL=1
# check for errors
if [ $RETVAL -eq 1 ]; then
echo stop > $ON_PANIC_CONFIG_FILE
pr_error "ERROR: $ON_PANIC not supported by hardware!"
fi
}
stop()
{
if [ -f $PIDFILE ]; then
PID=$(cat $PIDFILE)
kill -TERM $PID 2> /dev/null
rm -f $PIDFILE
fi
echo none > $DUMP_CONFIG_DIR/dump_type || RETVAL=1
if [ -f $ON_RESTART_CONFIG_FILE ]; then
echo stop > $ON_RESTART_CONFIG_FILE 2> /dev/null || RETVAL=1
fi
echo stop > $ON_PANIC_CONFIG_FILE || RETVAL=1
if [ $RETVAL -eq 0 ]; then
pr_info "Dump on panic is disabled now"
else
pr_error "Disabling dump on panic failed"
fi
return $RETVAL
}
status()
{
ON_PANIC=$(cat $ON_PANIC_CONFIG_FILE) || RETVAL=1
if [ -f $PIDFILE ]; then
pr_info "on_panic: $ON_PANIC - dumpconf activation is being" \
"delayed for $DELAY_MINUTES minutes"
else
pr_info "on_panic: $ON_PANIC"
fi
case "$ON_PANIC" in
vmcmd)
status_vmcmd
;;
reipl)
status_reipl
;;
dump)
status_dump
;;
dump_reipl)
status_dump_reipl
;;
stop)
;;
*)
pr_error "ERROR: Unknown on_panic type '$ON_PANIC'"
;;
esac
}
case "$1" in
-h|--help)
printhelp
exit 0
;;
-v|--version)
printversion
exit 0
;;
esac
check_environment
# If system crashed, an invalid $PIDFILE might still exist
if [ -f $PIDFILE ]; then
cleanup_pidfile $(cat $PIDFILE)
fi
# See how we were called.
case "$1" in
restart|reload|force-reload|try-restart)
stop
DELAY_MINUTES=0
start
;;
start)
start $2
;;
stop)
stop
;;
status)
status
;;
*)
print_invalid_option $1
RETVAL=1
;;
esac
exit $RETVAL

161
etc/init.d/mon_statd Executable file
View File

@@ -0,0 +1,161 @@
#!/bin/bash
### BEGIN INIT INFO
# Provides: mon_statd
# Required-Start: $local_fs $remote_fs
# Required-Stop: $local_fs $remote_fs
# Should-Start:
# Should-Stop:
# Default-Start: 2 3 5
# Default-Stop: 0 1 6
# Short-Description: Configure the mon_fsstatd and mon_procd daemons.
# Description: Configures the mon_fsstatd and mon_procd daemons. It uses the
# configuration file /etc/sysconfig/mon_statd.
### END INIT INFO
# chkconfig: 235 01 99
DAEMON=mon_statd
FSSTATD=mon_fsstatd
PROCD=mon_procd
FSSTATD_PATH=/usr/sbin/$FSSTATD
PROCD_PATH=/usr/sbin/$PROCD
CONFIG_FILE=/etc/sysconfig/$DAEMON
FSSTATD_PID_FILE=/var/run/$FSSTATD.pid
PROCD_PID_FILE=/var/run/$PROCD.pid
# source function library
. /lib/lsb/init-functions
# Source config file
if [ -f $CONFIG_FILE ]; then
. $CONFIG_FILE
fi
UDEVSETTLE=/sbin/udevadm
if [ ! -e $UDEVSETTLE ]
then
UDEVSETTLE=/sbin/udevsettle
UDEVSETTLE_CALL="$UDEVSETTLE --timeout=10"
else
UDEVSETTLE_CALL="$UDEVSETTLE settle --timeout=10"
fi
load_kernel_module()
{
if [ ! -e /dev/monwriter ]; then
echo "Loading monwriter module..."
modprobe monwriter 2>&1
if [ $? -ne 0 ]; then
exit 1
fi
if [ -e $UDEVSETTLE ]
then
$UDEVSETTLE_CALL
fi
fi
}
start_daemon()
{
local daemon_name=$1
local daemon_interval=$2
local daemon_pid_file=$3
local daemon_path=$4
if [ ! -f $daemon_pid_file ]; then
load_kernel_module
echo -n "Starting $daemon_name:"
$daemon_path -i $daemon_interval
if [ $? -eq 0 ]; then
log_success_msg
else
log_failure_msg
fi
else
echo "$daemon_name (pid $(cat $daemon_pid_file)) is already running..."
fi
}
start()
{
if [ "$FSSTAT" = "yes" ]; then
start_daemon $FSSTATD $FSSTAT_INTERVAL $FSSTATD_PID_FILE \
$FSSTATD_PATH
fi
if [ "$PROC" = "yes" ]; then
start_daemon $PROCD $PROC_INTERVAL $PROCD_PID_FILE \
$PROCD_PATH
fi
}
stop_daemon()
{
local daemon_name=$1
local daemon_pid_file=$2
local daemon_path=$3
echo -n "Stopping $daemon_name:"
if [ -f $daemon_pid_file ]; then
killproc $daemon_path -TERM
log_success_msg
rm -f $daemon_pid_file
else
log_failure_msg
fi
}
stop()
{
if [ "$FSSTAT" = "yes" ]; then
stop_daemon $FSSTATD $FSSTATD_PID_FILE $FSSTATD_PATH
fi
if [ "$PROC" = "yes" ]; then
stop_daemon $PROCD $PROCD_PID_FILE $PROCD_PATH
fi
}
restart() {
stop
start
}
status_daemon()
{
local daemon_name=$1
local daemon_pid_file=$2
local daemon_interval=$3
if [ ! -f $daemon_pid_file ]; then
echo "$daemon_name is not running."
else
echo "$daemon_name (pid $(cat $daemon_pid_file), interval: $daemon_interval) is running."
fi
}
status()
{
status_daemon $FSSTATD $FSSTATD_PID_FILE $FSSTAT_INTERVAL
status_daemon $PROCD $PROCD_PID_FILE $PROC_INTERVAL
}
# How are we called?
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status
;;
restart|reload|force-reload)
restart
;;
*)
echo "Usage: $DAEMON {start|stop|status|restart|reload}"
exit 1
esac
exit 0

15
etc/sysconfig/cpi Normal file
View File

@@ -0,0 +1,15 @@
#
# Apply control program identification (CPI) settings
#
# The system and sysplex names consist of up to eight characters of
# the following set: A-Z, 0-9, $, @, #, and blank.
#
# CPI system name
#
CPI_SYSTEM_NAME=""
#
# CPI sysplex name
#
CPI_SYSPLEX_NAME=""

61
etc/sysconfig/dumpconf Normal file
View File

@@ -0,0 +1,61 @@
#
# s390 dump config
#
# Configures the actions which should be performed after a kernel panic
# and on PSW restart.
#
# The following actions are supported:
#
# * stop: Stop Linux (default)
# * dump: Dump Linux with stand-alone dump tool
# * vmcmd: Issue z/VM CP commands
# * reipl: Re-IPL Linux using setting under /sys/firmware/reipl
# * dump_reipl: First dump Linux with stand-alone dump tool, then re-IPL Linux
# using setting under /sys/firmware/reipl
#
# For the actions "reipl" and "dump_reipl" the DELAY_MINUTES keyword may
# be used to delay the activation of dumpconf.
# Thus potential reipl loops caused by kernel panics
# which persistently occur early in the boot process can be prevented.
# Dump on CCW device (DASD) and re-IPL after dump is complete.
# The re-IPL device, as specified under "/sys/firmware/reipl", is used.
# The activation of dumpconf is delayed by 5 minutes.
#
# ON_PANIC=dump_reipl
# DUMP_TYPE=ccw
# DEVICE=0.0.4e13
# DELAY_MINUTES=5
#
# Dump on fcp device (SCSI Disk)
#
# ON_PANIC=dump
# DUMP_TYPE=fcp
# DEVICE=0.0.4711
# WWPN=0x5005076303004711
# LUN=0x4711000000000000
# BOOTPROG=0
# BR_LBA=0
#
# Use VMDUMP
#
# ON_PANIC=vmcmd
# VMCMD_1="MESSAGE * Starting VMDUMP"
# VMCMD_2="VMDUMP"
# VMCMD_3="IPL 4711"
#
# Stop Linux (default)
#
# ON_PANIC=stop
#
# Re-IPL Linux
# The re-IPL device, as specified under "/sys/firmware/reipl", is used.
# Since the DELAY_MINUTES keyword is omitted, there is no delay and
# dumpconf becomes active immediately during system startup.
#
# ON_PANIC=reipl

View File

@@ -0,0 +1,9 @@
## Path: System/Monitoring/s390-tools/mon_fsstatd
## Description: Linux - z/VM Monitor Daemon for process and filesystem data.
## Type: integer(1:)
## Default: 60
#
# Monitor interval in seconds for filesystem monitoring.
#
FSSTAT_INTERVAL=60

9
etc/sysconfig/mon_procd Normal file
View File

@@ -0,0 +1,9 @@
## Path: System/Monitoring/s390-tools/mon_procd
## Description: Linux - z/VM Monitor Daemon for process and filesystem data.
## Type: integer(1:)
## Default: 60
#
# Monitor interval in seconds for process monitoring.
#
PROC_INTERVAL=60

28
etc/sysconfig/mon_statd Normal file
View File

@@ -0,0 +1,28 @@
## Path: System/Monitoring/s390-tools/mon_statd
## Description: Linux - z/VM Monitor Daemon for process and filesystem data.
## Type: integer(1:)
## Default: 60
#
# Monitor interval in seconds for filesystem monitoring.
#
FSSTAT_INTERVAL=60
## Type: integer(1:)
## Default: 60
#
# Monitor interval in seconds for process monitoring.
#
PROC_INTERVAL=60
## Type: yesno
## Default: no
#
# Set this to "yes" if you want to enable filesystem monitoring.
FSSTAT="no"
## Type: yesno
## Default: no
#
# Set this to "yes" if you want to enable process monitoring.
PROC="no"

View File

@@ -0,0 +1,6 @@
#
# Rule for z90crypt character device node permissions
# This file should be installed in /etc/udev/rules.d
#
KERNEL=="z90crypt", MODE="0666"

View File

@@ -0,0 +1 @@
SUBSYSTEM=="ccwgroup", ACTION=="change", RUN+="/usr/bin/killall -SIGUSR1 osasnmpd"

View File

@@ -0,0 +1,35 @@
#
# Rules for unique DASD device nodes created in /dev/disk/
# This file should be installed in /etc/udev/rules.d
#
SUBSYSTEM!="block", GOTO="dasd_symlinks_end"
KERNEL!="dasd*", GOTO="dasd_symlinks_end"
ACTION!="change", GOTO="dasd_block_end"
# by-id (hardware serial number)
KERNEL=="dasd*[!0-9]", ATTRS{status}=="online", IMPORT{program}="/sbin/dasdinfo -a -e -b $kernel"
KERNEL=="dasd*[!0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}"
KERNEL=="dasd*[!0-9]", ENV{ID_UID}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_UID}"
KERNEL=="dasd*[!0-9]", ENV{ID_XUID}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_XUID}"
LABEL="dasd_block_end"
ACTION!="change|add", GOTO="dasd_symlinks_end"
# for partitions import parent information
KERNEL=="dasd*[0-9]", IMPORT{parent}=="ID_*"
KERNEL=="dasd*[0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}-part%n"
KERNEL=="dasd*[0-9]", ENV{ID_UID}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_UID}-part%n"
KERNEL=="dasd*[0-9]", ENV{ID_XUID}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_XUID}-part%n"
LABEL="dasd_symlinks_end"
# on device add set request queue scheduler to deadline
SUBSYSTEM!="block", GOTO="sched_end"
ACTION!="change", GOTO="sched_end"
KERNEL=="dasd*[!0-9]", TEST=="queue/scheduler", ATTR{queue/scheduler}="deadline"
LABEL="sched_end"

View File

@@ -0,0 +1,18 @@
#
# Rules to set an increased default max readahead size
# This file should be installed in /etc/udev/rules.d
#
SUBSYSTEM!="block", GOTO="ra_end"
ACTION!="add", GOTO="ra_change"
# on device add set initial readahead to 512 (instead of in kernel 128)
KERNEL=="sd*[!0-9]", WAIT_FOR="queue/read_ahead_kb", ATTR{queue/read_ahead_kb}="512"
LABEL="ra_change"
ACTION!="change", GOTO="ra_end"
# on device change set initial readahead to 512 (instead of in kernel 128)
KERNEL=="dasd*[!0-9]", TEST=="queue/read_ahead_kb", ATTR{queue/read_ahead_kb}="512"
LABEL="ra_end"

22
fdasd/Makefile Normal file
View File

@@ -0,0 +1,22 @@
include ../common.mak
libs = $(rootdir)/libvtoc/libvtoc.a \
$(rootdir)/libzds/libzds.a \
$(rootdir)/libdasd/libdasd.a \
$(rootdir)/libu2s/libu2s.a \
$(rootdir)/libutil/libutil.a
all: fdasd
fdasd: fdasd.o $(libs)
install: all
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man8
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 fdasd $(DESTDIR)$(BINDIR)
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 fdasd.8 \
$(DESTDIR)$(MANDIR)/man8
clean:
rm -f *.o *~ fdasd core
.PHONY: all install clean

185
fdasd/fdasd.8 Normal file
View File

@@ -0,0 +1,185 @@
.\" 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 FDASD 8 "Apr 2006" "s390-tools"
.SH NAME
fdasd \- partitioning tool.
.SH SYNOPSIS
interactive mode:
.br
\fBfdasd\fR [-s] [-r] [-C] \fIdevice\fR
.br
command line mode:
.br
\fBfdasd\fR [-s] [-r] [-C] {-a[-k|-l \fIvolser\fR]|-i|-p|-c \fIconf_file\fR}
[-f \fI[type,blocksize]\fR] \fIdevice\fR
.br
help:
.br
\fBfdasd\fR {-h|-v}
.SH DESCRIPTION
\fBfdasd\fR writes a partition table to a cdl (compatible disk layout)
formatted DASD, in the form of
a VTOC (volume table of contents) for usage with Linux for S/390
or zSeries. If fdasd detects a valid \fBVOL1\fR volume label, it
will use it, otherwise it asks to write a new one.
.br
\fBAttention\fR: Careless use of
\fBfdasd\fR can result in loss of data.
.SH OPTIONS
.TP
\fB-h\fR or \fB--help\fR
Print usage information, then exit.
.TP
\fB-v\fR or \fB--version\fR
Print version information, then exit.
.TP
\fB-s\fR or \fB--silent\fR
Suppress messages in non-interactive mode.
.TP
\fB-r\fR or \fB--verbose\fR
Provide more verbose output.
.TP
\fB-a\fR or \fB--auto\fR
Automatically create a partition using the entire disk in non-interactive
mode.
.TP
\fB-k\fR or \fB--keep_volser\fR
Keeps the volume serial when writing the volume label.
.br
This is useful, if the volume serial has been written before and should not
be overwritten. This option is only applicable in non-interactive mode.
.TP
\fB-l\fR \fIvolser\fR or \fB--label\fR \fIvolser\fR
Specify the volume serial.
.br
\fIvolser\fR is interpreted as ASCII string and is automatically converted to
uppercase, padded with blanks and finally converted to EBCDIC to be written
to disk. This option is only applicable in non-interactive mode.
.br
Do not use the following reserved volume serial: SCRTCH, PRIVAT, MIGRAT,
or Lnnnnn (L with five digit number); These are used as keywords by
other operating systems (OS/390).
.br
A volume serial is 1 through 6 alphanumeric characters or one of the
following special characters: $, #, @, %. All other characters are simply
ignored.
.br
Try to avoid using special characters in the volume serial.
This may cause problems accessing a disk by volser.
In case you really have to use special characters, make sure you are using
quotes. In addition there is a special handling for the '$' sign.
Please specify it using '\\$' if necessary.
.br
e.g. -l 'a@b\\$c#' to get A@B$C#
.br
Omitting this parameter causes fdasd to ask for it in case it is needed.
.br
.TP
\fB-c\fR \fIconf_file\fR or \fB--config\fR \fIconf_file\fR
Use this option to create multiple partitions according to
specifications in a configuration file, \fIconf_file\fR.
.br
The configuration file contains one line for each partition. The lines
have this format:
.sp
[<first>,<last>,<type>]
.br
where:
.br
<first> and <last> are numbers that specify the first and the last
track of the partition. Instead of a numerical value, you can
specify 'first' for the first possible track on the disk and 'last'
for the last possible track on disk.
.br
<type> is optional and specifies the partition type. <type> can be one
of: native, swap, raid, lvm, or gpfs.
If omitted, 'native' is used.
.br
The configuration file of the following example specifies three
partitions that use the entire disk:
.sp
[first,1000]
.br
[1001,2000,swap]
.br
[2001,last,lvm]
.br
.TP
\fB-i\fR or \fB--volser\fR
Print the volume serial, then exit.
.TP
\fB-p\fR or \fB--table\fR
Print partition table, then exit.
.br
In combination with the -s option fdasd will display a short version of the
partition table.
.TP
\fB-C\fR or \fB--check_host_count\fR
Force fdasd to check the host access open count to ensure the device
is not online on another operating system instance
.TP
\fB-f\fR \fI[type,blocksize]\fR or \fB--force\fR \fI[type,blocksize]\fR
Force fdasd to work on non DASD devices.
.br
If fdasd is to be used on a block device that is neither a native DASD
nor exposes the proper disk geometry of a DASD of type 3390,
then the --force option can be used to assume the geometry of a
given device type. The default device type is 3390 and the default
block size is 4096. An optional argument of <device type>,<blocksize>
can be used to specify type and blocksize explicitly. For example:
-f
has the same effect as
-f3390,4096 or --force=3390,4096
Valid device types are: 3390, 3380, 9345
.br
Valid block sizes are: 4096, 2048, 1024, 512
You can use the verbose option to get information about the disk
geometry that fdasd has computed from device type and block size.
.TP
\fIdevice\fR
This parameter specifies the device to be partitioned:
.sp
\fB/dev/dasd/\fR\fIxxxx\fR\fB/device\fR
.br
where \fIxxxx\fR is the four-letter devno (device number).
.br
In case your are not using the device file system, please specify:
.sp
\fB/dev/dasd\fR\fIx\fR
.br
where \fIx\fR is one or more lowercase letter(s) or any other device
node specification configured by udev for kernel 2.6 or higher.
.SH SEE ALSO
.BR dasdfmt (8)

Some files were not shown because too many files have changed in this diff Show More