mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Initial s390-tools-2.0.0 import
This commit is based on the s390-tools-1.39.0 version. Changes on top of s390-tools-1.39.0: - Add MIT license to all source files - Add LICENSE file - Transform REAMDE to README.md (markdown) - Add AUTHORS.md file - Add CONTRIBUTING.md file - Move changelog from README to CHANGELOG.md file Reviewed-by: Stefan Haberland <sth@linux.vnet.ibm.com> Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
This commit is contained in:
89
.gitignore
vendored
Normal file
89
.gitignore
vendored
Normal 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
93
AUTHORS.md
Normal 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
11
CHANGELOG.md
Normal 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
65
CONTRIBUTING.md
Normal 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
19
LICENSE
Normal 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
48
Makefile
Normal 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
361
README.md
Normal 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
58
cmsfs-fuse/Makefile
Normal 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
292
cmsfs-fuse/amap.c
Normal 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
208
cmsfs-fuse/cmsfs-fuse.1
Normal 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
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
132
cmsfs-fuse/cmsfs-fuse.h
Normal 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
127
cmsfs-fuse/config.c
Normal 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
180
cmsfs-fuse/dasd.c
Normal 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
156
cmsfs-fuse/ebcdic.h
Normal 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
125
cmsfs-fuse/edf.h
Normal 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
|
||||
107
cmsfs-fuse/etc/filetypes.conf
Normal file
107
cmsfs-fuse/etc/filetypes.conf
Normal 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
54
cmsfs-fuse/helper.h
Normal 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
353
common.mak
Normal 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
41
cpacfstats/Makefile
Normal 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
127
cpacfstats/cpacfstats.1
Normal 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
237
cpacfstats/cpacfstats.c
Normal 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
112
cpacfstats/cpacfstats.h
Normal 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
97
cpacfstats/cpacfstatsd.8
Normal 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
477
cpacfstats/cpacfstatsd.c
Normal 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
258
cpacfstats/perf_crypto.c
Normal 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
211
cpacfstats/stats_sock.c
Normal 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
64
cpumf/Makefile
Normal 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
182
cpumf/bin/chcpumf.in
Normal 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
483
cpumf/bin/cpumf_helper.in
Normal 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
387
cpumf/bin/lscpumf.in
Normal 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__
|
||||
96
cpumf/data/cpum-cf-extended-z10.ctr
Normal file
96
cpumf/data/cpum-cf-extended-z10.ctr
Normal 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
|
||||
.
|
||||
316
cpumf/data/cpum-cf-extended-z13.ctr
Normal file
316
cpumf/data/cpum-cf-extended-z13.ctr
Normal 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
|
||||
.
|
||||
122
cpumf/data/cpum-cf-extended-z196.ctr
Normal file
122
cpumf/data/cpum-cf-extended-z196.ctr
Normal 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
|
||||
.
|
||||
195
cpumf/data/cpum-cf-extended-zEC12.ctr
Normal file
195
cpumf/data/cpum-cf-extended-zEC12.ctr
Normal 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
|
||||
.
|
||||
132
cpumf/data/cpum-cf-generic.ctr
Normal file
132
cpumf/data/cpum-cf-generic.ctr
Normal 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
|
||||
.
|
||||
17
cpumf/data/cpum-cf-hw-counter.map
Normal file
17
cpumf/data/cpum-cf-hw-counter.map
Normal 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',
|
||||
};
|
||||
13
cpumf/data/cpum-sf-modes.ctr
Normal file
13
cpumf/data/cpum-sf-modes.ctr
Normal 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
66
cpumf/man/chcpumf.8
Normal 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
70
cpumf/man/lscpumf.1
Normal 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
23
cpuplugd/Makefile
Normal 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
336
cpuplugd/config.c
Normal 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
227
cpuplugd/cpu.c
Normal 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
234
cpuplugd/cpuplugd.h
Normal 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
232
cpuplugd/daemon.c
Normal 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
100
cpuplugd/getopt.c
Normal 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
157
cpuplugd/info.c
Normal 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
485
cpuplugd/main.c
Normal 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(×tamps[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(×tamps[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
64
cpuplugd/man/cpuplugd.8
Normal 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)
|
||||
284
cpuplugd/man/cpuplugd.conf.5
Normal file
284
cpuplugd/man/cpuplugd.conf.5
Normal 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
82
cpuplugd/mem.c
Normal 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
550
cpuplugd/terms.c
Normal 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
21
dasdfmt/Makefile
Normal 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
195
dasdfmt/dasdfmt.8
Normal 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
1713
dasdfmt/dasdfmt.c
Normal file
File diff suppressed because it is too large
Load Diff
350
dasdfmt/dasdfmt.h
Normal file
350
dasdfmt/dasdfmt.h
Normal 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
20
dasdinfo/Makefile
Normal 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
97
dasdinfo/dasdinfo.8
Normal 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
837
dasdinfo/dasdinfo.c
Normal 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
24
dasdview/Makefile
Normal 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
218
dasdview/dasdview.8
Normal 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
2470
dasdview/dasdview.c
Normal file
File diff suppressed because it is too large
Load Diff
269
dasdview/dasdview.h
Normal file
269
dasdview/dasdview.h
Normal 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
12
dump2tar/Makefile
Normal 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
50
dump2tar/include/buffer.h
Normal 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
29
dump2tar/include/dref.h
Normal 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
63
dump2tar/include/dump.h
Normal 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
23
dump2tar/include/global.h
Normal 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 */
|
||||
26
dump2tar/include/idcache.h
Normal file
26
dump2tar/include/idcache.h
Normal 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
101
dump2tar/include/misc.h
Normal 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 */
|
||||
26
dump2tar/include/strarray.h
Normal file
26
dump2tar/include/strarray.h
Normal 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
44
dump2tar/include/tar.h
Normal 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
12
dump2tar/man/Makefile
Normal 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
458
dump2tar/man/dump2tar.1
Normal 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
31
dump2tar/src/Makefile
Normal 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
273
dump2tar/src/buffer.c
Normal 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
94
dump2tar/src/dref.c
Normal 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
1859
dump2tar/src/dump.c
Normal file
File diff suppressed because it is too large
Load Diff
478
dump2tar/src/dump2tar.c
Normal file
478
dump2tar/src/dump2tar.c
Normal 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
20
dump2tar/src/global.c
Normal 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
155
dump2tar/src/idcache.c
Normal 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
492
dump2tar/src/misc.c
Normal 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
83
dump2tar/src/strarray.c
Normal 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
272
dump2tar/src/tar.c
Normal 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
166
etc/cpuplugd.conf
Normal 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
89
etc/init.d/cpacfstatsd
Executable 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
116
etc/init.d/cpuplugd
Executable 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
578
etc/init.d/dumpconf
Executable 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
161
etc/init.d/mon_statd
Executable 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
15
etc/sysconfig/cpi
Normal 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
61
etc/sysconfig/dumpconf
Normal 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
|
||||
9
etc/sysconfig/mon_fsstatd
Normal file
9
etc/sysconfig/mon_fsstatd
Normal 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
9
etc/sysconfig/mon_procd
Normal 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
28
etc/sysconfig/mon_statd
Normal 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"
|
||||
6
etc/udev/rules.d/40-z90crypt.rules
Normal file
6
etc/udev/rules.d/40-z90crypt.rules
Normal 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"
|
||||
1
etc/udev/rules.d/57-osasnmpd.rules
Normal file
1
etc/udev/rules.d/57-osasnmpd.rules
Normal file
@@ -0,0 +1 @@
|
||||
SUBSYSTEM=="ccwgroup", ACTION=="change", RUN+="/usr/bin/killall -SIGUSR1 osasnmpd"
|
||||
35
etc/udev/rules.d/59-dasd.rules
Normal file
35
etc/udev/rules.d/59-dasd.rules
Normal 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"
|
||||
18
etc/udev/rules.d/60-readahead.rules
Normal file
18
etc/udev/rules.d/60-readahead.rules
Normal 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
22
fdasd/Makefile
Normal 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
185
fdasd/fdasd.8
Normal 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
Reference in New Issue
Block a user