mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Compare commits
221 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8b8114e6f | ||
|
|
c4e4b926b4 | ||
|
|
6324f62da7 | ||
|
|
3ade063ea2 | ||
|
|
92a3b30323 | ||
|
|
673ff375d9 | ||
|
|
4e2ebe0370 | ||
|
|
78b053326c | ||
|
|
ab06a5d88a | ||
|
|
a0a71efde0 | ||
|
|
b4b2202ff5 | ||
|
|
a26f61c4fe | ||
|
|
a28b396d9e | ||
|
|
0012eaf68e | ||
|
|
c22d282e67 | ||
|
|
071522f7d2 | ||
|
|
ae7217806b | ||
|
|
9f6150db34 | ||
|
|
b39bdfbf6e | ||
|
|
71fe58111c | ||
|
|
5e46632767 | ||
|
|
1bd64f5b0f | ||
|
|
d311506dc4 | ||
|
|
08e4520a4f | ||
|
|
46fd42af0c | ||
|
|
a8b0d7ace8 | ||
|
|
a29b3c8997 | ||
|
|
27dce3317a | ||
|
|
4382901daa | ||
|
|
bcbb6fcae6 | ||
|
|
b16a6d4fe1 | ||
|
|
0d15a07c0a | ||
|
|
5394cd363c | ||
|
|
0906293cd8 | ||
|
|
a9e13a2d69 | ||
|
|
ee2c6d4160 | ||
|
|
455ad953a9 | ||
|
|
02a0d12988 | ||
|
|
be47b51890 | ||
|
|
2677a4182d | ||
|
|
812df795c3 | ||
|
|
a0d6edf03c | ||
|
|
81920f7cfd | ||
|
|
40dd63e2ac | ||
|
|
a8579a0727 | ||
|
|
2ab27bdaf2 | ||
|
|
03fef264bd | ||
|
|
927a48e607 | ||
|
|
fbcb3f384a | ||
|
|
c7fe21b019 | ||
|
|
50a4740443 | ||
|
|
164d4817ec | ||
|
|
abec41f514 | ||
|
|
fcb503ea3c | ||
|
|
2eea614bbf | ||
|
|
3a13cb43f2 | ||
|
|
e8fca95592 | ||
|
|
b5604850ab | ||
|
|
18efac6306 | ||
|
|
18f8db2225 | ||
|
|
f1a13749df | ||
|
|
b5da337d1e | ||
|
|
a67888f36a | ||
|
|
e1f174ae22 | ||
|
|
7503d88b3b | ||
|
|
43039943cd | ||
|
|
b2e02d202a | ||
|
|
1faa5d2957 | ||
|
|
d62e075450 | ||
|
|
983233730c | ||
|
|
ad024c06e1 | ||
|
|
e2843232d5 | ||
|
|
b79d17d233 | ||
|
|
11b401b599 | ||
|
|
69c5ee2e52 | ||
|
|
3ee625d74f | ||
|
|
832268ecb0 | ||
|
|
5fb6434548 | ||
|
|
5894e3f700 | ||
|
|
e384f06014 | ||
|
|
1627c6a39f | ||
|
|
bb8f17a7da | ||
|
|
8b3d3dd4bd | ||
|
|
a5c04e5746 | ||
|
|
c5ae1a41da | ||
|
|
970c697983 | ||
|
|
cff4b0384f | ||
|
|
724f800dc5 | ||
|
|
4cdfe91ca9 | ||
|
|
a44db0f367 | ||
|
|
03ddcd6267 | ||
|
|
5c468a220e | ||
|
|
4ba65eaa74 | ||
|
|
58cae4fecd | ||
|
|
9b84de4b0a | ||
|
|
e679a88d88 | ||
|
|
6acf6ed76d | ||
|
|
e7abf256f6 | ||
|
|
0d407904dd | ||
|
|
2238eb820f | ||
|
|
81013f0c70 | ||
|
|
697c5dc405 | ||
|
|
e9deec4c62 | ||
|
|
d60b7770fd | ||
|
|
70925f9aff | ||
|
|
ef88fac874 | ||
|
|
4bd16ba8ea | ||
|
|
f6a7da141c | ||
|
|
1bbd34e500 | ||
|
|
c2f8988444 | ||
|
|
d2990db15f | ||
|
|
fd3d79335a | ||
|
|
07b3837e68 | ||
|
|
13a1227a02 | ||
|
|
52118a8d02 | ||
|
|
ee89b850fe | ||
|
|
5160f41079 | ||
|
|
5b0115fdb4 | ||
|
|
ae305b5158 | ||
|
|
04407d24fc | ||
|
|
bcb8ea09d1 | ||
|
|
88619b6dba | ||
|
|
e127b89624 | ||
|
|
3591b94c42 | ||
|
|
2a29a28f78 | ||
|
|
cb831aea44 | ||
|
|
04be704083 | ||
|
|
1bcfcd3253 | ||
|
|
2dbaf9f991 | ||
|
|
2ea20094d0 | ||
|
|
a80c14d72c | ||
|
|
10518da618 | ||
|
|
e9d1e168b2 | ||
|
|
7567506cca | ||
|
|
524017ac06 | ||
|
|
15774dbe32 | ||
|
|
f247850ff0 | ||
|
|
800ef7cb13 | ||
|
|
48cf3b3809 | ||
|
|
99c8c27302 | ||
|
|
a5c3af1361 | ||
|
|
353c23a157 | ||
|
|
d371ba0db8 | ||
|
|
8308143008 | ||
|
|
d8871197ea | ||
|
|
df338a3bac | ||
|
|
1005e7be7e | ||
|
|
afb14cba7a | ||
|
|
1e450e6f7b | ||
|
|
696a007b62 | ||
|
|
ea5f3f4a1c | ||
|
|
6afd5e97d1 | ||
|
|
b1da8bbbe9 | ||
|
|
6ece595532 | ||
|
|
40d4aa38e9 | ||
|
|
c58cfd1ec1 | ||
|
|
02086f26d2 | ||
|
|
7a2666d727 | ||
|
|
2df532c1fa | ||
|
|
969a439aaa | ||
|
|
f8331a4b8e | ||
|
|
895dc80580 | ||
|
|
d93645d690 | ||
|
|
c3bb6307b8 | ||
|
|
6208c0626a | ||
|
|
3949c62f6f | ||
|
|
8c6c2ef30e | ||
|
|
12c84469fd | ||
|
|
0a8e726d05 | ||
|
|
533df585fd | ||
|
|
c4eb2d5fba | ||
|
|
35029ad010 | ||
|
|
296079f70a | ||
|
|
4f1c73d592 | ||
|
|
1fb859729e | ||
|
|
bbeb0f8445 | ||
|
|
2ae44cb794 | ||
|
|
a0b8033088 | ||
|
|
9cc74839b0 | ||
|
|
b7b7002855 | ||
|
|
55e428a4d7 | ||
|
|
cb09223a56 | ||
|
|
28db3523d0 | ||
|
|
f15e32cdb1 | ||
|
|
094057e265 | ||
|
|
025a2198a4 | ||
|
|
2b938b78aa | ||
|
|
de36fc5259 | ||
|
|
e3e5b6422a | ||
|
|
38343be0fa | ||
|
|
957e612720 | ||
|
|
1d2316caef | ||
|
|
64539853ca | ||
|
|
686c331b69 | ||
|
|
01551f98d5 | ||
|
|
0e2c5907e0 | ||
|
|
68ff79e4c3 | ||
|
|
5b96d86538 | ||
|
|
4043137cd7 | ||
|
|
735d8f5493 | ||
|
|
28efa7d120 | ||
|
|
ebde75eeda | ||
|
|
a645ec0fc0 | ||
|
|
761a185938 | ||
|
|
890b15d158 | ||
|
|
07677668a3 | ||
|
|
fde914fd1c | ||
|
|
35c97a07aa | ||
|
|
45e3f016f4 | ||
|
|
38b520ab4c | ||
|
|
3b6920271e | ||
|
|
a3146409da | ||
|
|
76aa8e4a52 | ||
|
|
f5e6f4a8cb | ||
|
|
80cb1553a3 | ||
|
|
706f59b9d9 | ||
|
|
02cc902b90 | ||
|
|
ff2fb42868 | ||
|
|
451bf7fbc6 | ||
|
|
d2a4a8b0f3 | ||
|
|
fe5753d34d |
21
.editorconfig
Normal file
21
.editorconfig
Normal file
@@ -0,0 +1,21 @@
|
||||
# Check https://editorconfig.org for details
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
charset = utf-8
|
||||
indent_style = tab
|
||||
tab_width = 8
|
||||
|
||||
[*.sh]
|
||||
shell_variant = bash # used by `shfmt`
|
||||
|
||||
[*.y{a,}ml]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.py]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -9,6 +9,10 @@
|
||||
tags
|
||||
TAGS
|
||||
|
||||
# Ignore coverage data
|
||||
*.gcda
|
||||
*.gcno
|
||||
|
||||
#
|
||||
# Ignore generated executables and other generated files
|
||||
#
|
||||
@@ -25,6 +29,7 @@ dasdview/dasdview
|
||||
dump2tar/src/dump2tar
|
||||
fdasd/fdasd
|
||||
hmcdrvfs/hmcdrvfs
|
||||
hsavmcore/check-dep-fuse
|
||||
hsavmcore/hsavmcore
|
||||
hyptop/hyptop
|
||||
ip_watcher/xcec-bridge
|
||||
@@ -98,7 +103,9 @@ zipl/boot/*.bin
|
||||
zipl/boot/*.exec
|
||||
zipl/boot/data.h
|
||||
zipl/src/chreipl_helper.device-mapper
|
||||
zdev/src/zdev_id
|
||||
zipl/src/zipl
|
||||
zipl/src/zipl-editenv
|
||||
zipl/src/zipl_helper.device-mapper
|
||||
zkey/check-dep-zkey
|
||||
zkey/check-dep-zkey-cryptsetup
|
||||
|
||||
10
AUTHORS.md
10
AUTHORS.md
@@ -2,9 +2,10 @@ List of all individuals having contributed content to s390-tools
|
||||
----------------------------------------------------------------
|
||||
|
||||
- Alexander Egorenkov
|
||||
- Alexandra Winter
|
||||
- Alexey Ishchuk
|
||||
- Andreas Herrmann
|
||||
- Andre Wild
|
||||
- André Wild
|
||||
- Antoinette Kaschner
|
||||
- Arnd Bergmann
|
||||
- Axel Wirbser
|
||||
@@ -18,6 +19,7 @@ List of all individuals having contributed content to s390-tools
|
||||
- Clemens von Mann
|
||||
- Colin Walters
|
||||
- Dan Horak
|
||||
- Dan Horák
|
||||
- Despina Papadopoulou
|
||||
- Dimitri John Ledkov
|
||||
- Eberhard Pasch
|
||||
@@ -37,6 +39,7 @@ List of all individuals having contributed content to s390-tools
|
||||
- Fritz Elfert
|
||||
- Gerald Schaefer
|
||||
- Gerhard Tonn
|
||||
- Graham Inggs
|
||||
- Guevenc Guelce
|
||||
- Hannes Reinecke
|
||||
- Hans-Joachim Picht
|
||||
@@ -51,7 +54,7 @@ List of all individuals having contributed content to s390-tools
|
||||
- Ingo Franzki
|
||||
- Ingo Tuchscherer
|
||||
- Jan Glauber
|
||||
- Jan Hoeppner
|
||||
- Jan Höppner
|
||||
- Jan Willeke
|
||||
- Jason J. Herne
|
||||
- Javier Martinez Canillas
|
||||
@@ -73,6 +76,7 @@ List of all individuals having contributed content to s390-tools
|
||||
- Martin Peschke
|
||||
- Martin Petermann
|
||||
- Martin Schwidefsky
|
||||
- Matthew Rosato
|
||||
- Maxim Shchetynin
|
||||
- Melissa Howland
|
||||
- Michael Ernst
|
||||
@@ -102,6 +106,7 @@ List of all individuals having contributed content to s390-tools
|
||||
- Stefan Raspl
|
||||
- Stefan Reimbold
|
||||
- Stefan Weinhuber
|
||||
- Steffen Eiden
|
||||
- Steffen Maier
|
||||
- Steffen Thoss
|
||||
- Susanne Wintenberger
|
||||
@@ -110,6 +115,7 @@ List of all individuals having contributed content to s390-tools
|
||||
- Swen Schillig
|
||||
- Taraka R. Bodireddy
|
||||
- Thomas Heidrich
|
||||
- Thomas Huth
|
||||
- Thomas Richter
|
||||
- Thomas Spatzier
|
||||
- Thomas Weber
|
||||
|
||||
104
CHANGELOG.md
104
CHANGELOG.md
@@ -1,5 +1,109 @@
|
||||
Release history for s390-tools (MIT version)
|
||||
--------------------------------------------
|
||||
* __v2.21.0 (2022-04-20)__
|
||||
|
||||
For Linux kernel version: 5.17
|
||||
|
||||
Add new tools / libraries:
|
||||
- libcpumf: Create library libcpumf for CPU Measurement functions
|
||||
|
||||
Changes of existing tools:
|
||||
- chreipl-fcp-mpath: bundle a pre-cooked version of the manpage for build
|
||||
environments without access to `pandoc`
|
||||
- dbginfo.sh: Add multipath info to map paths to FC addressing and prio group
|
||||
- dbginfo.sh: Collect config files of systemd-modules-load.service
|
||||
- dbginfo.sh: Sort list of environment variables for readability
|
||||
- dbginfo.sh: Replace "which" by builtin command "type"
|
||||
- dbginfo.sh: Rework script formatting (indents, order)
|
||||
- dbginfo.sh: Update sysfs collection (excludes, messages)
|
||||
- genprotimg: Add Protected Virtualization (PV) dump support
|
||||
- genprotimg: Remove DigiCert root CA pinning
|
||||
- lszcrypt: Add CEX8S support
|
||||
- zcryptctl: Add control domain handling
|
||||
- zcryptstats: Add CEX8 support
|
||||
- zipl: Allow optional entries that are left out when files are missing
|
||||
- zipl: make IPL sections defined with BLS to inherit a target field
|
||||
- zpcictl: Add option to trigger firmware reset
|
||||
|
||||
Bug Fixes:
|
||||
- cpictl: Handle excessive kernel version numbers
|
||||
- dbginfo.sh: Collect all places where modprobe.d config files could exist
|
||||
- fdasd: Fix endless menu loop on EOF
|
||||
- zdump/dfi: Fix segfault due to double free
|
||||
- zdump: Fix /dev/mem reading
|
||||
- zpcictl: Fix race of SCLP reset and Linux recovery
|
||||
|
||||
* __v2.20.0 (2022-02-04)__
|
||||
|
||||
For Linux kernel version: 5.16
|
||||
|
||||
Add new tools / libraries:
|
||||
- Add EditorConfig configuration
|
||||
|
||||
Changes of existing tools:
|
||||
- s390-tools switches to Fuse 3 as Fuse 2 is deprecated.
|
||||
Affected tools: cmsfs, hmcdrvfs, hsavmcore, zdsfs, zdump
|
||||
- chreipl-fcp-mpath: don't compress the manpage before installing it
|
||||
- cpictl: Report extended version information
|
||||
- genprotimg: Add extended kernel command line support
|
||||
- zdev: modify the lsblk output parser in lszdev
|
||||
- zipl: Add support for longer kernel command lines (now supports up to 64k length)
|
||||
|
||||
Bug Fixes:
|
||||
- cpictl: Suppress messages for unwritable sysfs files
|
||||
- dbginfo.sh: Fix missing syslog for step create_package
|
||||
- lshwc: Fix CPU list parameter setup for device driver
|
||||
- zdev: Check for errors when removing a devtype setting
|
||||
- zdev: Fix path resolution for multi-mount point file systems
|
||||
|
||||
* __v2.19.0 (2021-11-10)__
|
||||
|
||||
For Linux kernel version: 5.15
|
||||
|
||||
Add new tools / libraries:
|
||||
- chreipl-fcp-mpath: New toolset that uses multipath information to change
|
||||
the configured FCP re-IPL path on detecting issues with the current path
|
||||
|
||||
Changes of existing tools:
|
||||
- dbginfo.sh: Add retry timeout and remove possible blocking "blockdev --report"
|
||||
- dbginfo.sh: Collect config- and debug-data for chreipl-fcp-mpath
|
||||
- hsci: Add support for multiple MAC addresses
|
||||
|
||||
Bug Fixes:
|
||||
- lshwc: Fix compile error for gcc <8.1
|
||||
- zdump: Various clean-ups and fixes
|
||||
- ziomon: Correct throughput calculation in ziorep_printers
|
||||
- zipl: Fix segmentation fault when setting stage3_parms
|
||||
|
||||
* __v2.18.0 (2021-10-01)__
|
||||
|
||||
For Linux kernel version: 5.14
|
||||
|
||||
Add new tools:
|
||||
- scripts: Add tool for parsing sclp s390dbf logs
|
||||
- zdev: Add udev rule helper tool
|
||||
- zipl-editenv: Add tool to operate with zIPL environment installed in the boot record
|
||||
|
||||
Changes of existing tools:
|
||||
- Makefile: Fix order of build of libraries for parallel builds
|
||||
- dbginfo.sh: Add collection in area of timedate, coredump and --check option
|
||||
- dbginfo.sh: Add exception on dump2tar for /sys/kernel/mm/page_idle/bitmap
|
||||
- dbginfo.sh: Cleanup of outdated sections and general code rework
|
||||
- dbginfo.sh: Collect zipl boot menu entries from boot loader specification
|
||||
- lszcrypt: Add support for vfio-ap status field
|
||||
- lszcrypt: Improved output for deconfig cards and queues
|
||||
- lszfcp: Add linkdown case to host marker of extended output
|
||||
- zdev: Add auto-config for PCI and crypto devices
|
||||
- zdump: Introduce multi-level message logging
|
||||
- zipl: Add support for environment block interpretation
|
||||
- zkey-cryptsetup: Support LUKS2 volumes with integrity support enabled
|
||||
|
||||
Bug Fixes:
|
||||
- hsavmcore: Avoid recompilation of overlay during install step
|
||||
- libkmipclient: Fix parsing of hex values for XML and JSON encoding
|
||||
- vmur/vmur.cpp: Fix error handling on transfer failure
|
||||
- zdump: Lots of smaller fixes across the board
|
||||
|
||||
* __v2.17.0 (2021-07-07)__
|
||||
|
||||
For Linux kernel version: 5.12 / 5.13
|
||||
|
||||
28
Makefile
28
Makefile
@@ -3,16 +3,22 @@ ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/ar
|
||||
# Include common definitions
|
||||
include common.mak
|
||||
|
||||
LIB_DIRS = libvtoc libutil libzds libdasd libvmdump libccw libvmcp libekmfweb \
|
||||
libseckey libkmipclient
|
||||
#
|
||||
# BASELIBS: Libraries that have no dependency to other libraries in s390-tools
|
||||
# LIBS: Libraries that can have a dependency to base libraries
|
||||
# TOOLS: Tools that can have a dependency to base libraries or libraries
|
||||
#
|
||||
BASELIB_DIRS = libutil libseckey
|
||||
LIB_DIRS = libvtoc libzds libdasd libvmdump libccw libvmcp libekmfweb \
|
||||
libkmipclient libcpumf
|
||||
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 netboot etc zpcictl \
|
||||
genprotimg lsstp hsci hsavmcore
|
||||
genprotimg lsstp hsci hsavmcore chreipl-fcp-mpath
|
||||
|
||||
SUB_DIRS = $(LIB_DIRS) $(TOOL_DIRS)
|
||||
SUB_DIRS = $(BASELIB_DIRS) $(LIB_DIRS) $(TOOL_DIRS)
|
||||
|
||||
all: $(TOOL_DIRS)
|
||||
clean: $(TOOL_DIRS)
|
||||
@@ -26,9 +32,10 @@ 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".
|
||||
# We have to build the base libraries before the other libraries are built,
|
||||
# and then build the other libraries before the tools are built. Otherwise the
|
||||
# other libraries and tools would trigger parallel "make -C" builds for the
|
||||
# base libraries and the other 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>".
|
||||
@@ -45,7 +52,12 @@ $(TOOL_DIRS): $(LIB_DIRS)
|
||||
$(MAKE) -C $@ TOPDIR=$(TOPDIR) ARCH=$(ARCH) $(goal) ;)
|
||||
.PHONY: $(TOOL_DIRS)
|
||||
|
||||
$(LIB_DIRS):
|
||||
$(LIB_DIRS): $(BASELIB_DIRS)
|
||||
$(foreach goal,$(MAKECMDGOALS), \
|
||||
$(MAKE) -C $@ TOPDIR=$(TOPDIR) ARCH=$(ARCH) $(goal) ;)
|
||||
.PHONY: $(LIB_DIRS)
|
||||
|
||||
$(BASELIB_DIRS):
|
||||
$(foreach goal,$(MAKECMDGOALS), \
|
||||
$(MAKE) -C $@ TOPDIR=$(TOPDIR) ARCH=$(ARCH) $(goal) ;)
|
||||
.PHONY: $(BASELIB_DIRS)
|
||||
|
||||
41
README.md
41
README.md
@@ -109,6 +109,9 @@ Package contents
|
||||
feature. Those traces are filtered with the zfcpdbf script, i.e. merge
|
||||
several traces, make it more readable etc.
|
||||
|
||||
* sclpdbf:
|
||||
Display debug data for the sclp kernel component.
|
||||
|
||||
* scsi_logging_level:
|
||||
Create, get or set the logging level for the SCSI logging facility.
|
||||
|
||||
@@ -267,6 +270,10 @@ Package contents
|
||||
can be time consuming and prevent the HSA memory from being reused
|
||||
by other LPARs.
|
||||
|
||||
* chreipl-fcp-mpath:
|
||||
Use multipath information to change the configured FCP re-IPL path on
|
||||
detecting issues with the current path.
|
||||
|
||||
For more information refer to the following publications:
|
||||
|
||||
* "Device Drivers, Features, and Commands" chapter "Useful Linux commands"
|
||||
@@ -285,7 +292,7 @@ build options:
|
||||
|
||||
| __LIBRARY__ | __BUILD OPTION__ | __TOOLS__ |
|
||||
|----------------|:------------------:|:-------------------------------------:|
|
||||
| fuse | `HAVE_FUSE` | cmsfs-fuse, zdsfs, hmcdrvfs, zgetdump,|
|
||||
| fuse3 | `HAVE_FUSE` | cmsfs-fuse, zdsfs, hmcdrvfs, zgetdump,|
|
||||
| | | hsavmcore |
|
||||
| zlib | `HAVE_ZLIB` | zgetdump, dump2tar |
|
||||
| ncurses | `HAVE_NCURSES` | hyptop |
|
||||
@@ -304,11 +311,11 @@ build options:
|
||||
|
||||
This table lists additional build or install options:
|
||||
|
||||
| __COMPONENT__ | __OPTION__ | __TOOLS__ |
|
||||
|------------------|:----------------------------:|:--------------:|
|
||||
| dracut | `HAVE_DRACUT` | zdev |
|
||||
| initramfs-tools | `HAVE_INITRAMFS` | zdev |
|
||||
| | `ZDEV_ALWAYS_UPDATE_INITRD` | zdev |
|
||||
| __COMPONENT__ | __OPTION__ | __TOOLS__ |
|
||||
|------------------|:----------------------------:|:-----------------------:|
|
||||
| dracut | `HAVE_DRACUT` | zdev, chreipl-fcp-mpath |
|
||||
| initramfs-tools | `HAVE_INITRAMFS` | zdev |
|
||||
| | `ZDEV_ALWAYS_UPDATE_INITRD` | zdev |
|
||||
|
||||
The s390-tools build process uses "pkg-config" if available and hard-coded
|
||||
compiler and linker options otherwise.
|
||||
@@ -348,12 +355,12 @@ the different tools are provided:
|
||||
|
||||
* 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
|
||||
FUSE is provided by installing the fuse3 and libfuse3 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
|
||||
the fuse3-devel package is required.
|
||||
The cmsfs-fuse tool requires FUSE version 3.0 or newer for full
|
||||
functionality.
|
||||
For further information about FUSE see: http://fuse.sourceforge.net
|
||||
For further information about FUSE see: https://github.com/libfuse/libfuse
|
||||
|
||||
* hyptop:
|
||||
The ncurses-devel package is required to build hyptop.
|
||||
@@ -449,7 +456,7 @@ the different tools are provided:
|
||||
`HAVE_JSONC=0`, or `HAVE_LIBCURL=0` to the make invocation.
|
||||
|
||||
* hsavmcore:
|
||||
For building the hsavmcore tool you need fuse version 2.6 and optionally
|
||||
For building the hsavmcore tool you need fuse version 3.0 and optionally
|
||||
systemd which is enabled by default, to disable systemd support,
|
||||
add `HAVE_SYSTEMD=0` to the make invocation.
|
||||
Tip: you may skip the hsavmcore build by adding `HAVE_FUSE=0`
|
||||
@@ -462,3 +469,15 @@ the different tools are provided:
|
||||
(libxml2-devel.rpm), and libcurl version 7.59 or newer (libcurl-devel.rpm).
|
||||
Tip: you may skip the libkmipclient build by adding `HAVE_OPENSSL=0`,
|
||||
`HAVE_JSONC=0`, `HAVE_LIBXML2=0`, or `HAVE_LIBCURL=0` to the make invocation.
|
||||
|
||||
* chreipl-fcp-mpath:
|
||||
For a complete list and documentation of the requirements, installation and
|
||||
uninstallation, please see
|
||||
[chreipl-fcp-mpath/README.md](chreipl-fcp-mpath/README.md).
|
||||
|
||||
Summarized: chreipl-fcp-mpath requires GNU Bash, GNU Core Utilities,
|
||||
util-linux, udev, and multipath-tools. When using `HAVE_DRACUT=1` with the
|
||||
make invocation, it also requires dracut. When using `ENABLE_DOC=1` with the
|
||||
make invocation to build a fresh man page (instead of using the pre-cooked
|
||||
version) and render the README.md as HTML, make further requires pandoc and
|
||||
GNU awk for the build process.
|
||||
|
||||
@@ -32,7 +32,7 @@ Usage: $cmd [-t LAST_GIT_TAG]
|
||||
Add all new authors since last release to the $authors_file file. Without
|
||||
the -t option the tool uses the last available git tag as last release.
|
||||
|
||||
The tool should be called form the s390-tools master branch.
|
||||
The tool runs on the s390-tools master branch.
|
||||
|
||||
OPTIONS
|
||||
-t, --tag LAST_GIT_TAG Use git tag LAST_GIT_TAG as starting point
|
||||
@@ -116,7 +116,7 @@ echo "$cmd: Adding new authors since tag: $release_tag_last"
|
||||
# Print authors list without header
|
||||
tail -n +4 $authors_file_path
|
||||
# Add the new authors
|
||||
git log --format="- %an" $release_tag_last..
|
||||
git log --format="- %an" $release_tag_last..master
|
||||
# Then sort everything and remove duplicates
|
||||
} | sort | uniq > $authors_file_tmp
|
||||
# Create new AUTHORS file with header ...
|
||||
|
||||
1
chreipl-fcp-mpath/.chreipl-fcp-mpath.7.cksum
Normal file
1
chreipl-fcp-mpath/.chreipl-fcp-mpath.7.cksum
Normal file
@@ -0,0 +1 @@
|
||||
8e604dab39577678bc9f24bfe5e2ee35354b2f4b1759edd16093157b1ecce9d3 README.md
|
||||
11
chreipl-fcp-mpath/.gitignore
vendored
Normal file
11
chreipl-fcp-mpath/.gitignore
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
# build artifacts
|
||||
/chreipl-fcp-mpath-common.sh
|
||||
/chreipl-fcp-mpath-is-ipl-tgt
|
||||
/chreipl-fcp-mpath-is-ipl-vol
|
||||
/chreipl-fcp-mpath-is-reipl-zfcp
|
||||
/chreipl-fcp-mpath-record-volume-identifier
|
||||
/chreipl-fcp-mpath-try-change-ipl-path
|
||||
/dracut/dracut.conf.d/70-chreipl-fcp-mpath.conf
|
||||
/README.html
|
||||
/README.pdf
|
||||
/chreipl-fcp-mpath.md
|
||||
274
chreipl-fcp-mpath/Makefile
Normal file
274
chreipl-fcp-mpath/Makefile
Normal file
@@ -0,0 +1,274 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# chreipl-fcp-mpath: use multipath information to change FCP IPL target
|
||||
# (C) Copyright IBM Corp. 2021
|
||||
#
|
||||
# Uses the following system-utilities (and shell-builtins):
|
||||
# Utilities list in GNU Make Conventions:
|
||||
# https://www.gnu.org/software/make/manual/make.html#Utilities-in-Makefiles
|
||||
# Those necessary for sourced Makefiles:
|
||||
# - ../common.mak
|
||||
# - chreipl-fcp-mpath.mak
|
||||
# bash:
|
||||
# - bash
|
||||
# GNU coreutils:
|
||||
# - sha256sum
|
||||
# If $(ENABLE_DOC) is `1`:
|
||||
# GNU awk:
|
||||
# - gawk
|
||||
|
||||
override SHELL := /bin/bash
|
||||
override .SHELLFLAGS := -O globstar -O nullglob -O extglob -c
|
||||
|
||||
# Include common s390-tools definitions
|
||||
include ../common.mak
|
||||
|
||||
# Include common chreipl-fcp-mpath definitions
|
||||
include chreipl-fcp-mpath.mak
|
||||
|
||||
# Local setting: .make.config
|
||||
# You may create a file named like this in the same directory as this
|
||||
# Makefile, and customize the build this way (e.g. re-define variables
|
||||
# set in `chreipl-fcp-mpath.mak`, or define a `CHREIPLZFCPMP_POST_INSTALL`
|
||||
# that is automatically called after each installation)
|
||||
ifneq ($(wildcard .make.config),)
|
||||
include $(wildcard .make.config)
|
||||
endif
|
||||
|
||||
#
|
||||
## Build
|
||||
#
|
||||
|
||||
.PHONY: chreipl-fcp-mpath chreipl-fcp-mpath-clean
|
||||
chreipl-fcp-mpath:
|
||||
chreipl-fcp-mpath-clean:
|
||||
all: chreipl-fcp-mpath
|
||||
clean: chreipl-fcp-mpath-clean
|
||||
|
||||
# common function used in the helper scripts
|
||||
CHREIPL_FCP_MPATH_COMMON := \
|
||||
chreipl-fcp-mpath-common.sh
|
||||
CHREIPL_FCP_MPATH_UDEV_HELPER := \
|
||||
chreipl-fcp-mpath-is-ipl-tgt \
|
||||
chreipl-fcp-mpath-is-ipl-vol \
|
||||
chreipl-fcp-mpath-is-reipl-zfcp \
|
||||
chreipl-fcp-mpath-record-volume-identifier \
|
||||
chreipl-fcp-mpath-try-change-ipl-path
|
||||
|
||||
$(CHREIPL_FCP_MPATH_UDEV_HELPER) $(CHREIPL_FCP_MPATH_COMMON): $(MAKEFILE_LIST)
|
||||
$(CHREIPL_FCP_MPATH_UDEV_HELPER) $(CHREIPL_FCP_MPATH_COMMON): % : %.in
|
||||
$(call chreiplzfcpmp-sed-buildvar-replace,$(<),$(@))
|
||||
chmod a+x $(@)
|
||||
|
||||
$(CHREIPL_FCP_MPATH_UDEV_HELPER): $(CHREIPL_FCP_MPATH_COMMON)
|
||||
|
||||
.PHONY: chreipl-fcp-mpath-udev-helper-clean
|
||||
chreipl-fcp-mpath-udev-helper-clean:
|
||||
rm -f $(CHREIPL_FCP_MPATH_UDEV_HELPER) $(CHREIPL_FCP_MPATH_COMMON)
|
||||
|
||||
udev/rules.d/70-chreipl-fcp-mpath.rules: $(CHREIPL_FCP_MPATH_UDEV_HELPER)
|
||||
|
||||
chreipl-fcp-mpath: udev/rules.d/70-chreipl-fcp-mpath.rules
|
||||
chreipl-fcp-mpath-clean: chreipl-fcp-mpath-udev-helper-clean
|
||||
|
||||
dracut/dracut.conf.d/70-chreipl-fcp-mpath.conf: $(MAKEFILE_LIST)
|
||||
dracut/dracut.conf.d/70-chreipl-fcp-mpath.conf: % : %.in
|
||||
$(call chreiplzfcpmp-sed-buildvar-replace,$(<),$(@))
|
||||
|
||||
.PHONY: chreipl-fcp-mpath-dracut-clean
|
||||
chreipl-fcp-mpath-dracut-clean:
|
||||
rm -f dracut/dracut.conf.d/70-chreipl-fcp-mpath.conf
|
||||
|
||||
chreipl-fcp-mpath: dracut/dracut.conf.d/70-chreipl-fcp-mpath.conf
|
||||
chreipl-fcp-mpath-clean: chreipl-fcp-mpath-dracut-clean
|
||||
|
||||
ifeq ($(ENABLE_DOC),1)
|
||||
|
||||
.PHONY: chreipl-fcp-mpath-doc
|
||||
chreipl-fcp-mpath-doc: README.html chreipl-fcp-mpath.7
|
||||
|
||||
ifeq ($(ENABLE_DOC_PDF),1)
|
||||
chreipl-fcp-mpath-doc: README.pdf
|
||||
|
||||
README.pdf: PANDOCFLAGS += -M title="chreipl-fcp-mpath" -M subtitle="README"
|
||||
endif
|
||||
|
||||
README.html: PANDOCFLAGS += -M title="chreipl-fcp-mpath" -M subtitle="README"
|
||||
|
||||
chreiplzfcpmp-doc-man-meta = \
|
||||
-M title="CHREIPL-FCP-MPATH" \
|
||||
-V header="Administrator Manual" \
|
||||
-V section="7" \
|
||||
-V footer="s390-tools $(S390_TOOLS_RELEASE)"
|
||||
|
||||
chreipl-fcp-mpath.7: PANDOCFLAGS += $(chreiplzfcpmp-doc-man-meta)
|
||||
chreipl-fcp-mpath.7: .chreipl-fcp-mpath.7.cksum
|
||||
|
||||
.INTERMEDIATE: chreipl-fcp-mpath.md
|
||||
chreipl-fcp-mpath.md: README.md
|
||||
gawk -- '/NOT-IN-MAN \{/,/NOT-IN-MAN \}/ { next } { print }' $(<) > $(@)
|
||||
|
||||
.chreipl-fcp-mpath.7.cksum: README.md
|
||||
sha256sum $(<) > $(@)
|
||||
|
||||
.PHONY: chreipl-fcp-mpath-doc-clean
|
||||
chreipl-fcp-mpath-doc-clean:
|
||||
rm -f README.html README.pdf chreipl-fcp-mpath.md
|
||||
|
||||
chreipl-fcp-mpath: chreipl-fcp-mpath-doc
|
||||
chreipl-fcp-mpath-clean: chreipl-fcp-mpath-doc-clean
|
||||
|
||||
else # $(ENABLE_DOC) != 1
|
||||
|
||||
# We bundle a pre-cooked man page with the source-code so that distributions
|
||||
# don't need `pandoc` in order to be able to ship the man page. As of this
|
||||
# writing multiple distributions don't have a packaged version of it.
|
||||
#
|
||||
# In order to remember to regenerate this pre-cooked version whenever the
|
||||
# README.md is changed, we also generate a checksum of the README.md, bundle
|
||||
# that as well, and compare that whenever `make` is called. This way, the
|
||||
# freshness of the man page can be checked, even if `pandoc` is not available,
|
||||
# or ENABLE_DOC disabled.
|
||||
|
||||
MANPAGE_FRESH := $(shell sha256sum --check .chreipl-fcp-mpath.7.cksum >/dev/null && echo 1 || echo 0)
|
||||
ifeq ($(MANPAGE_FRESH),0)
|
||||
$(warning chreipl-fcp-mpath.7 is outdated, please regenerate it by calling `make ENABLE_DOC=1`)
|
||||
endif
|
||||
|
||||
endif # $(ENABLE_DOC) == 1
|
||||
|
||||
#
|
||||
## Install
|
||||
#
|
||||
|
||||
.PHONY: chreipl-fcp-mpath-install
|
||||
# The content of `CHREIPLZFCPMP_POST_INSTALL` (bash script) is automatically
|
||||
# called *after* installing chreipl-fcp-mpath during `make install`. If not
|
||||
# defined (the default), nothing happens. You may define this on the make
|
||||
# command line, or by creating a `.make.config` and defining the variable in
|
||||
# there.
|
||||
chreipl-fcp-mpath-install:
|
||||
$(CHREIPLZFCPMP_POST_INSTALL)
|
||||
|
||||
install: chreipl-fcp-mpath-install
|
||||
|
||||
# install udev rules
|
||||
INSTDIRS += $(UDEVRULESDIR)
|
||||
$(DESTDIR)$(UDEVRULESDIR): install_dirs
|
||||
|
||||
.PHONY: chreipl-fcp-mpath-install-udev-rules
|
||||
chreipl-fcp-mpath-install-udev-rules: | $(DESTDIR)$(UDEVRULESDIR)
|
||||
chreipl-fcp-mpath-install-udev-rules: udev/rules.d/70-chreipl-fcp-mpath.rules
|
||||
$(INSTALL_DATA) -t $(DESTDIR)$(UDEVRULESDIR) \
|
||||
udev/rules.d/70-chreipl-fcp-mpath.rules
|
||||
|
||||
chreipl-fcp-mpath-install: chreipl-fcp-mpath-install-udev-rules
|
||||
|
||||
# install udev helper programs
|
||||
INSTDIRS += $(UDEVDIR)
|
||||
$(DESTDIR)$(UDEVDIR): install_dirs
|
||||
|
||||
.PHONY: chreipl-fcp-mpath-install-udev-helper
|
||||
chreipl-fcp-mpath-install-udev-helper: | $(DESTDIR)$(UDEVDIR)
|
||||
chreipl-fcp-mpath-install-udev-helper: $(CHREIPL_FCP_MPATH_UDEV_HELPER)
|
||||
$(INSTALL_EXEC) -t $(DESTDIR)$(UDEVDIR) $(CHREIPL_FCP_MPATH_UDEV_HELPER)
|
||||
|
||||
chreipl-fcp-mpath-install: chreipl-fcp-mpath-install-udev-helper
|
||||
|
||||
# install common library files
|
||||
INSTDIRS += $(CHREIPLZFCPMPDIR)
|
||||
$(DESTDIR)$(CHREIPLZFCPMPDIR): install_dirs
|
||||
|
||||
.PHONY: chreipl-fcp-mpath-install-libfiles
|
||||
chreipl-fcp-mpath-install-libfiles: | $(DESTDIR)$(CHREIPLZFCPMPDIR)
|
||||
chreipl-fcp-mpath-install-libfiles: $(CHREIPL_FCP_MPATH_COMMON)
|
||||
$(INSTALL_DATA) -t $(DESTDIR)$(CHREIPLZFCPMPDIR) \
|
||||
$(CHREIPL_FCP_MPATH_COMMON)
|
||||
|
||||
chreipl-fcp-mpath-install: chreipl-fcp-mpath-install-libfiles
|
||||
|
||||
ifeq ($(HAVE_DRACUT),1)
|
||||
|
||||
# install dracut config files
|
||||
INSTDIRS += $(DRACUTCONFDIR)
|
||||
$(DESTDIR)$(DRACUTCONFDIR): install_dirs
|
||||
|
||||
.PHONY: chreipl-fcp-mpath-install-dracut-config
|
||||
chreipl-fcp-mpath-install-dracut-config: | $(DESTDIR)$(DRACUTCONFDIR)
|
||||
chreipl-fcp-mpath-install-dracut-config: dracut/dracut.conf.d/70-chreipl-fcp-mpath.conf
|
||||
$(INSTALL_DATA) -t $(DESTDIR)$(DRACUTCONFDIR) \
|
||||
dracut/dracut.conf.d/70-chreipl-fcp-mpath.conf
|
||||
|
||||
chreipl-fcp-mpath-install: chreipl-fcp-mpath-install-dracut-config
|
||||
|
||||
endif
|
||||
|
||||
# chreipl-fcp-mpath: install man page
|
||||
INSTDIRS += $(MANDIR)
|
||||
$(DESTDIR)$(MANDIR)/man7: install_dirs
|
||||
|
||||
.PHONY: chreipl-fcp-mpath-install-man-page
|
||||
chreipl-fcp-mpath-install-man-page: | $(DESTDIR)$(MANDIR)/man7
|
||||
chreipl-fcp-mpath-install-man-page: chreipl-fcp-mpath.7
|
||||
$(INSTALL_DATA) -t $(DESTDIR)$(MANDIR)/man7 \
|
||||
chreipl-fcp-mpath.7
|
||||
|
||||
chreipl-fcp-mpath-install: chreipl-fcp-mpath-install-man-page
|
||||
|
||||
#
|
||||
## Utility
|
||||
#
|
||||
|
||||
# Utilities for the debug feature of chreipl-fcp-mpath-common.sh.
|
||||
#
|
||||
# When `chreipl-fcp-mpath` is built with D=1 (default is D=0), each run
|
||||
# of one of the helper scripts will create a debug log in $(DEBUGOUTDIR)
|
||||
# (default: /run/udev) which among other things contains the complete shell
|
||||
# trace of that script run, with some added information that would not be
|
||||
# inspectable otherwise with just the trace.
|
||||
#
|
||||
# There is currently no way of enabling/disabling this at runtime.
|
||||
#
|
||||
# chreipl-fcp-mpath-common.sh defines debug log file name as
|
||||
# "chreiplzfcpmp-${debug_trace_tag}-${SEQNUM:-0}.XXXXXXXXXX" where SEQNUM is a
|
||||
# udev rule environment variable and each X is replaced with some [[:alnum:]]
|
||||
# by mktemp. For a definition of `debug_trace_tag`, please see the comments
|
||||
# in the source.
|
||||
#
|
||||
# The following targets can be used for some simple access and filtering of the
|
||||
# logs during development.
|
||||
|
||||
DEBUG_LOG_GLOB = $(DEBUGOUTDIR)/chreiplzfcpmp-[[:digit:]][[:digit:]][[:alpha:]][[:alpha:]][[:alpha:]][[:alpha:]]-+([[:digit:]]).[[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]]
|
||||
|
||||
# display all debug log files on the system
|
||||
.PHONY: chreipl-fcp-mpath-debug-logs
|
||||
chreipl-fcp-mpath-debug-logs:
|
||||
@ls -1d $(DEBUG_LOG_GLOB)
|
||||
|
||||
# display only debug log files of script runs that exited with status 0 (= good)
|
||||
#
|
||||
# XXX: `sed -n -e '$p'` is used instead of `tail -n1` to prevent an other
|
||||
# dependency just for that (both invocations do the same thing).
|
||||
.PHONY: chreipl-fcp-mpath-debug-logs-filter-good
|
||||
chreipl-fcp-mpath-debug-logs-filter-good:
|
||||
@for lg in $(DEBUG_LOG_GLOB); do \
|
||||
sed -e '/^+ trap_exit$$/,/^+ trap - EXIT$$/d' "$${lg}" \
|
||||
| sed -n -e '$$p' \
|
||||
| grep -q -e '^+ exit 0$$' || continue; \
|
||||
ls -d "$${lg}"; \
|
||||
done
|
||||
|
||||
# display only debug log files of script runs that didn't exit with status 0
|
||||
# (= bad)
|
||||
.PHONY: chreipl-fcp-mpath-debug-logs-filter-bad
|
||||
chreipl-fcp-mpath-debug-logs-filter-bad:
|
||||
@for lg in $(DEBUG_LOG_GLOB); do \
|
||||
sed -e '/^+ trap_exit$$/,/^+ trap - EXIT$$/d' "$${lg}" \
|
||||
| sed -n -e '$$p' \
|
||||
| grep -q -e '^+ exit 0$$' && continue; \
|
||||
ls -d "$${lg}"; \
|
||||
done
|
||||
|
||||
.PHONY: chreipl-fcp-mpath-debug-logs-clean
|
||||
chreipl-fcp-mpath-debug-logs-clean:
|
||||
rm -f $(DEBUG_LOG_GLOB)
|
||||
270
chreipl-fcp-mpath/README.md
Normal file
270
chreipl-fcp-mpath/README.md
Normal file
@@ -0,0 +1,270 @@
|
||||
<!-- markdown documentation: https://github.github.com/gfm/ -->
|
||||
|
||||
NAME
|
||||
====
|
||||
|
||||
chreipl-fcp-mpath - use multipath information for re-IPL path failover on a
|
||||
running Linux instance
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
|
||||
The IPL process of Linux on Z or LinuxONE from an FCP-attached SCSI volume uses
|
||||
exactly one path to the volume. If this path is unavailable, the IPL fails.
|
||||
|
||||
The **chreipl-fcp-mpath** toolset monitors **udev** events about paths to the
|
||||
re-IPL volume. If the currently configured re-IPL path becomes unavailable, the
|
||||
toolset checks for operational paths to the same volume. If available, it
|
||||
reconfigures the re-IPL settings to use an operational path.
|
||||
|
||||
Thus, re-IPL from an FCP-attached SCSI volume can be successful despite path
|
||||
failures on a running Linux instance if at least one path to the re-IPL volume
|
||||
remains operational.
|
||||
|
||||
**Chreipl-fcp-mpath** requires **udev**, **multipathd** and **dm-multipath**.
|
||||
Once installed, the toolset runs automatically and autonomously. No user
|
||||
intervention is possible or required.
|
||||
|
||||
Other than installing the toolset, there is no user interface for
|
||||
**chreipl-fcp-mpath**.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
The **chreipl-fcp-mpath** tool has the following requirements on the
|
||||
Linux instance that is being monitored:
|
||||
|
||||
- The Linux instance must have started successfully, during IPL.
|
||||
|
||||
- The running Linux instance must use **dm-multipath** and **multipathd** for
|
||||
the configured re-IPL volume - a volume that contains a zipl boot record
|
||||
and has one of its paths used in the re-IPL configuration.
|
||||
|
||||
- **udev** must run.
|
||||
|
||||
- The toolset must observe at least one event about the configured re-IPL
|
||||
path. Examples for such events are: the SCSI disk comes online, or a path
|
||||
of the corresponding multipath device goes down or comes back online.
|
||||
|
||||
- The WWID of the re-IPL volume must not change while the Linux instance is
|
||||
running.
|
||||
|
||||
- When the configured re-IPL path becomes unavailable while the Linux
|
||||
instance is running, at least one operational path to the re-IPL volume
|
||||
must be available, or must become available. If no such path is available
|
||||
when the Linux instance is rebooted, the re-IPL path is not changed.
|
||||
|
||||
- The tool assumes that any manually reconfigured re-IPL device is valid and
|
||||
operational.
|
||||
|
||||
The tool treats a newly configured re-IPL device like the initially
|
||||
configured re-IPL device. In particular, if the newly configured re-IPL
|
||||
device fulfills the requirements of the tool, re-IPL path failover takes
|
||||
place if the configured re-IPL path becomes unavailable.
|
||||
|
||||
Caution with Manual Changes to the Configured re-IPL Target
|
||||
-----------------------------------------------------------
|
||||
|
||||
**chreipl-fcp-mpath** is designed to accept operator-inititated changes of the
|
||||
re-IPL device. However, concurrent changes by the operator and tool driven
|
||||
changes can result in the operator change being overwritten.
|
||||
|
||||
To avoid this problem, change the re-IPL device only during steady-state
|
||||
operations, when no path events happen. Alternatively, make sure that no events
|
||||
are processed while you change the device. See [EXAMPLES](#examples) for one
|
||||
way to suspend event processing.
|
||||
|
||||
MESSAGES
|
||||
========
|
||||
|
||||
During monitoring and event processing, **chreipl-fcp-mpath** writes messages
|
||||
to the syslog.
|
||||
|
||||
When the configured re-IPL path is changed to a different path to the same
|
||||
volume (priority *daemon.notice*):
|
||||
|
||||
> Changed re-IPL path to: \<device-bus-id\>:\<wwpn\>:\<lun\>.
|
||||
|
||||
When a path event indicates that the last available path has become
|
||||
non-operational (priority *daemon.alert*):
|
||||
|
||||
> The re-IPL device cannot be changed because no operational path to the
|
||||
> re-IPL volume remains. The next re-IPL might fail unless you re-attach or
|
||||
> enable at least one valid path to the re-IPL volume.
|
||||
|
||||
When changing the configured re-IPL device failed because of an error with the
|
||||
used Linux kernel interface (priority *daemon.crit*):
|
||||
|
||||
> Changing the re-IPL device failed. The current re-IPL settings might be
|
||||
> inconsistent. Check and correct the settings (see the README.md of
|
||||
> chreipl-fcp-mpath) to make sure that the current re-IPL device is valid.
|
||||
|
||||
A failure to change the re-IPL device can indicate an inconsistent setting that
|
||||
cannot be corrected automatically by **chreipl-fcp-mpath**. As a result, the
|
||||
next re-IPL might fail or might not use the intended re-IPL device.
|
||||
|
||||
You can use the following tools to check and correct the current settings:
|
||||
|
||||
- **lsreipl** to confirm that the intended re-IPL device is configured;
|
||||
- **chreipl** to change the re-IPL device;
|
||||
- **lszfcp** to inspect the state of available paths to the re-IPL device.
|
||||
|
||||
<!-- NOT-IN-MAN { -->
|
||||
|
||||
SOFTWARE REQUIREMENTS
|
||||
=====================
|
||||
|
||||
**chreipl-fcp-mpath** integrates into s390-tools's build and install
|
||||
infrastructure. Use **make** to build it. No explicit dependency management is
|
||||
in place, but the toolset has some software dependencies besides the
|
||||
requirements in section [Requirements](#requirements):
|
||||
|
||||
- GNU Bash;
|
||||
- GNU Core Utilities (mktemp, readlink, sync, truncate, sha256sum);
|
||||
- util-linux (flock, hexdump, logger);
|
||||
- udev / systemd-udev;
|
||||
- multipath-tools.
|
||||
|
||||
To make use of the optional dracut configuration you need: dracut.
|
||||
|
||||
To build a fresh version of the documentation (man page) you need:
|
||||
|
||||
- pandoc;
|
||||
- GNU Core Utilities (date);
|
||||
- GNU awk;
|
||||
|
||||
otherwise the pre-cooked version shipped with the source will be used.
|
||||
|
||||
INSTALLATION
|
||||
============
|
||||
|
||||
If your distribution includes a packaged version of **chreipl-fcp-mpath**,
|
||||
either as a separate package or as part of a **s390-tools** package, install
|
||||
that package. Otherwise, you can either install it from source as part of
|
||||
**s390-tools** or separately.
|
||||
|
||||
To install **chreipl-fcp-mpath** as part of **s390-tools**, use **make** on the
|
||||
top-level directory of your **s390-tools** distribution. Installing the entire
|
||||
distribution might overwrite other already installed tools.
|
||||
|
||||
To install the tool separately, change into the **chreipl-fcp-mpath**
|
||||
directory, and use **make** there.
|
||||
|
||||
You need *root* privileges to install the tool into the root file system.
|
||||
|
||||
Calling **make** runs the build steps. Calling **make install** runs the build
|
||||
steps and copies the resulting components to their final destination.
|
||||
**s390-tools** offers more options and targets to customize the build (see
|
||||
**make help**).
|
||||
|
||||
**chreipl-fcp-mpath** has the following optional build options:
|
||||
|
||||
| Option | Values | Default | Effect
|
||||
| :----- | :----: | :-----: | :-----
|
||||
| HAVE_DRACUT | 0, 1 | 0 | Install a dracut configuration file that includes **chreipl-fcp-mpath** in the initial ramdisks built with **dracut**.
|
||||
| ENABLE_DOC | 0, 1 | 0 | Build a fresh version of the man page for **chreipl-fcp-mpath**.
|
||||
|
||||
Specify any options as arguments for both the **make** and **make install**
|
||||
command as shown in the following example:
|
||||
|
||||
~ # cd chreipl-fcp-mpath/
|
||||
~ # make HAVE_DRACUT=1 ENABLE_DOC=1
|
||||
~ # make HAVE_DRACUT=1 ENABLE_DOC=1 install
|
||||
|
||||
After the installation, reload the udev rules database:
|
||||
|
||||
~ # udevadm control --reload
|
||||
|
||||
*The toolset is now active on your running Linux instance.*
|
||||
|
||||
If you use the *HAVE_DRACUT=1* option, also rebuild your
|
||||
initial ramdisk, to immediately include the toolset instead of
|
||||
waiting for the next kernel update.
|
||||
|
||||
How to rebuild the initial ramdisk and the naming scheme for the
|
||||
resulting file or files depends on your distribution.
|
||||
The following example applies to Fedora and to Red Hat Enterprise Linux:
|
||||
|
||||
~ # dracut --force /boot/initramfs-"$(uname -r)".img "$(uname -r)"
|
||||
|
||||
For SUSE Linux Enterprise Server run for example:
|
||||
|
||||
~ # dracut --hostonly --force /boot/initrd-"$(uname -r)" "$(uname -r)"
|
||||
|
||||
These commands replace the initial ramdisk for the currently running kernel.
|
||||
If your distribution uses **zipl** as its boot loader, run **zipl** to refresh
|
||||
the boot record to find the new initial ramdisk.
|
||||
|
||||
~ # zipl
|
||||
|
||||
With dracut enabled, **make install** deploys the following files to these
|
||||
default locations:
|
||||
|
||||
/usr/lib/chreipl-fcp-mpath/chreipl-fcp-mpath-common.sh
|
||||
/usr/lib/dracut/dracut.conf.d/70-chreipl-fcp-mpath.conf
|
||||
/usr/lib/udev/chreipl-fcp-mpath-is-ipl-tgt
|
||||
/usr/lib/udev/chreipl-fcp-mpath-is-ipl-vol
|
||||
/usr/lib/udev/chreipl-fcp-mpath-is-reipl-zfcp
|
||||
/usr/lib/udev/chreipl-fcp-mpath-record-volume-identifier
|
||||
/usr/lib/udev/chreipl-fcp-mpath-try-change-ipl-path
|
||||
/usr/lib/udev/rules.d/70-chreipl-fcp-mpath.rules
|
||||
/usr/share/man/man7/chreipl-fcp-mpath.7
|
||||
|
||||
UNINSTALL
|
||||
=========
|
||||
|
||||
If your distribution includes a separately from **s390-tools** packaged version
|
||||
of **chreipl-fcp-mpath**, uninstall that package.
|
||||
|
||||
For installations without distribution packaging, you cannot uninstall
|
||||
**chreipl-fcp-mpath** with **make**.
|
||||
|
||||
Instead, remove the toolset by deleting the installed files as listed in
|
||||
[INSTALLATION](#installation)), reload the udev rules database, and rebuild all
|
||||
modified initial ramdisks as described in [INSTALLATION](#installation)).
|
||||
|
||||
<!-- NOT-IN-MAN } -->
|
||||
|
||||
EXAMPLES
|
||||
========
|
||||
|
||||
Manual Changes to the Configured re-IPL Device
|
||||
----------------------------------------------
|
||||
|
||||
As outlined in [DESCRIPTION](#description), be cautious when manually changing
|
||||
the configured re-IPL device. Assure that your reconfiguration actions do not
|
||||
collide with concurrent automatic event processing by **chreipl-fcp-mpath**.
|
||||
You can avoid such collisions, by stopping event processing, making your
|
||||
changes, and then re-enabling event processing. You need *root* privileges for
|
||||
running the commands in the following example:
|
||||
|
||||
~ # udevadm settle
|
||||
~ # udevadm control --stop-exec-queue
|
||||
~ # chreipl ...
|
||||
~ # udevadm control --start-exec-queue
|
||||
|
||||
Listing messages with journalctl
|
||||
--------------------------------
|
||||
|
||||
If your Linux instance includes **journalctl**, use the following command to
|
||||
list all messages that are issued by **chreipl-fcp-mpath**:
|
||||
|
||||
~ # journalctl -t chreipl-fcp-mpath
|
||||
|
||||
To list only messages that were issued since the last IPL, use this command:
|
||||
|
||||
~ # journalctl -t chreipl-fcp-mpath -b
|
||||
|
||||
REPORTING BUGS
|
||||
==============
|
||||
|
||||
Use the **Issues** functionality on GitHub to report any bugs in
|
||||
**chreipl-fcp-mpath**:
|
||||
[s390-tools Issues](<https://github.com/ibm-s390-linux/s390-tools/issues> "Link to the s390-tools Issues page").
|
||||
|
||||
SEE ALSO
|
||||
========
|
||||
|
||||
**chreipl**(8), **dracut**(8), **journalctl**(1), **lsreipl(8)**,
|
||||
**lszfcp**(8), **multipath**(8), **multipathd**(8), **udev**(7),
|
||||
**udevadm**(8), **zipl**(8)
|
||||
446
chreipl-fcp-mpath/chreipl-fcp-mpath-common.sh.in
Normal file
446
chreipl-fcp-mpath/chreipl-fcp-mpath-common.sh.in
Normal file
@@ -0,0 +1,446 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# chreipl-fcp-mpath: use multipath information to change FCP IPL target
|
||||
# (C) Copyright IBM Corp. 2021
|
||||
#
|
||||
# Uses the following system-utilities (and shell-builtins):
|
||||
# GNU coreutils:
|
||||
# - mktemp
|
||||
# - readlink
|
||||
# - sync
|
||||
# util-linux:
|
||||
# - flock
|
||||
# - logger
|
||||
|
||||
# Makes use of udev event environment variables:
|
||||
# SEQNUM
|
||||
|
||||
# (1) expand failed globs to an empty string
|
||||
# (2) extended pattern matching to strip leading/trailing whitespaces
|
||||
shopt -s nullglob extglob
|
||||
# (1) don't overwrite existing files using redirects (e.g.: `>`)
|
||||
set -o noclobber
|
||||
|
||||
# make sure any state files created are only writeable by the owning user
|
||||
umask 027
|
||||
|
||||
# create log if DEBUG is enabled (with Make: D=1)
|
||||
#
|
||||
# Each script importing this library and expecting a debug log to be created
|
||||
# must declare a *trace tag* in a variable `debug_trace_tag`. This is used as
|
||||
# identifier in the log file name. The format is:
|
||||
#
|
||||
# [[:digit:]][[:digit:]][[:alpha:]][[:alpha:]][[:alpha:]][[:alpha:]]
|
||||
# \ /\ /
|
||||
# --------\ /-------- -------------------\ /-------------------
|
||||
# \/ \/
|
||||
# relative position of some unique abbreviation for the script
|
||||
# execution in the name, excluding any common prefix
|
||||
# udev rules
|
||||
if '@DEBUG@' && [ -v debug_trace_tag ] && tlg="$(
|
||||
mktemp -p '@debugoutdir@' \
|
||||
"chreiplzfcpmp-${debug_trace_tag}-${SEQNUM:-0}.XXXXXXXXXX" \
|
||||
2>/dev/null)"
|
||||
then
|
||||
readonly tlg
|
||||
exec >|"${tlg}" 2>&1
|
||||
set -x
|
||||
set
|
||||
else
|
||||
unset tlg
|
||||
fi
|
||||
|
||||
declare -gr ID_FILE='@chreiplzfcpmp-id-file@'
|
||||
declare -gr FW_LOCK_FILE='@chreiplzfcpmp-fwlock-file@'
|
||||
|
||||
declare -gA TRAP_EXIT_FN=()
|
||||
declare -gf trap_exit 1>/dev/null
|
||||
function trap_exit() {
|
||||
local fn
|
||||
|
||||
for fn in "${TRAP_EXIT_FN[@]}"; do
|
||||
"${fn}"
|
||||
done
|
||||
|
||||
trap - EXIT
|
||||
}
|
||||
trap trap_exit EXIT
|
||||
|
||||
# Output variables:
|
||||
# id_file_unlock_exclusive_create() - call to unlock when finished with
|
||||
# critical section
|
||||
#
|
||||
# XXX: `id_file_lock_*` can't be taken recursively
|
||||
function id_file_lock_exclusive_create() {
|
||||
declare -g ID_FILE_LOCK=""
|
||||
|
||||
# prevent concurrent file creation
|
||||
#
|
||||
# First, open the file defined in ${ID_FILE} for writing; this will
|
||||
# succeed and create the file only if it doesn't exist already. If the
|
||||
# file already exist, the first open attempt will fail and we fall
|
||||
# back to opening it only for reading; this will always succeed if the
|
||||
# file already exists (the reason why the first attempty failed). In
|
||||
# both cases store the corresponding file descriptor in
|
||||
# ${ID_FILE_LOCK}.
|
||||
#
|
||||
# XXX: This should be race free.
|
||||
# open() with O_EXCL... is atomic (we set `noclobber` as shell
|
||||
# option); at least as long as we talk about a local FS.
|
||||
if ! { exec {ID_FILE_LOCK}>"${ID_FILE}"; } 2>/dev/null; then
|
||||
{ exec {ID_FILE_LOCK}<"${ID_FILE}"; } 2>/dev/null \
|
||||
|| return 1
|
||||
fi
|
||||
|
||||
declare -gf id_file_unlock_exclusive_create 1>/dev/null
|
||||
function id_file_unlock_exclusive_create() {
|
||||
if [ -v ID_FILE_LOCK ]; then
|
||||
sync "${ID_FILE}" 2>/dev/null
|
||||
# release file and implicitly the lock, if taken
|
||||
exec {ID_FILE_LOCK}>&-
|
||||
unset ID_FILE_LOCK
|
||||
fi
|
||||
|
||||
unset "TRAP_EXIT_FN[id_file_unlock_exclusive_create]"
|
||||
}
|
||||
TRAP_EXIT_FN+=(
|
||||
[id_file_unlock_exclusive_create]=id_file_unlock_exclusive_create
|
||||
)
|
||||
|
||||
flock --exclusive --timeout 5 "${ID_FILE_LOCK}" || return 2
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Output variables:
|
||||
# id_file_unlock_exclusive_no_create() - call to unlock when finished with
|
||||
# critical section
|
||||
#
|
||||
# XXX: `id_file_lock_*` can't be taken recursively
|
||||
function id_file_lock_exclusive_no_create() {
|
||||
declare -g ID_FILE_LOCK=""
|
||||
|
||||
# Open the file defined in ${ID_FILE} for reading, and store the
|
||||
# corresponding file descriptor in ${ID_FILE_LOCK}.
|
||||
#
|
||||
# XXX: return code is used in `chreipl-fcp-mpath-try-change-ipl-path`
|
||||
{ exec {ID_FILE_LOCK}<"${ID_FILE}"; } 2>/dev/null || return 1
|
||||
|
||||
declare -gf id_file_unlock_exclusive_no_create 1>/dev/null
|
||||
function id_file_unlock_exclusive_no_create() {
|
||||
if [ -v ID_FILE_LOCK ]; then
|
||||
sync "${ID_FILE}" 2>/dev/null
|
||||
# release file and implicitly the lock, if taken
|
||||
exec {ID_FILE_LOCK}<&-
|
||||
unset ID_FILE_LOCK
|
||||
fi
|
||||
|
||||
unset "TRAP_EXIT_FN[id_file_unlock_exclusive_no_create]"
|
||||
}
|
||||
TRAP_EXIT_FN+=(
|
||||
[id_file_unlock_exclusive_no_create]=id_file_unlock_exclusive_no_create
|
||||
)
|
||||
|
||||
flock --exclusive --timeout 5 "${ID_FILE_LOCK}" || return 2
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Output variables:
|
||||
# id_file_unlock_shared_no_create() - call to unlock when finished with
|
||||
# critical section
|
||||
#
|
||||
# XXX: `id_file_lock_*` can't be taken recursively
|
||||
function id_file_lock_shared_no_create() {
|
||||
declare -g ID_FILE_LOCK=""
|
||||
|
||||
# Open the file defined in ${ID_FILE} for reading, and store the
|
||||
# corresponding file descriptor in ${ID_FILE_LOCK}.
|
||||
{ exec {ID_FILE_LOCK}<"${ID_FILE}"; } 2>/dev/null || return 1
|
||||
|
||||
declare -gf id_file_unlock_shared_no_create 1>/dev/null
|
||||
function id_file_unlock_shared_no_create() {
|
||||
if [ -v ID_FILE_LOCK ]; then
|
||||
# release file and implicitly the lock, if taken
|
||||
exec {ID_FILE_LOCK}<&-
|
||||
unset ID_FILE_LOCK
|
||||
fi
|
||||
|
||||
unset "TRAP_EXIT_FN[id_file_unlock_shared_no_create]"
|
||||
}
|
||||
TRAP_EXIT_FN+=(
|
||||
[id_file_unlock_shared_no_create]=id_file_unlock_shared_no_create
|
||||
)
|
||||
|
||||
flock --shared --timeout 5 "${ID_FILE_LOCK}" || return 2
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Output variables:
|
||||
# firmware_unlock_exclusive() - call to unlock when finished with critical section
|
||||
#
|
||||
# XXX: `firmware_lock_*` can't be taken recursively
|
||||
function firmware_lock_exclusive() {
|
||||
declare -g FIRMWARE_LOCK=""
|
||||
|
||||
# Open the file defined in ${FW_LOCK_FILE} for reading, and store the
|
||||
# corresponding file descriptor in ${FIRMWARE_LOCK} (it doesn't matter
|
||||
# whether this is a normal file or directory). This file descriptor
|
||||
# will only be used for locking - not for actual I/O.
|
||||
{ exec {FIRMWARE_LOCK}<"${FW_LOCK_FILE}"; } 2>/dev/null || return 1
|
||||
|
||||
declare -gf firmware_unlock_exclusive 1>/dev/null
|
||||
function firmware_unlock_exclusive() {
|
||||
if [ -v FIRMWARE_LOCK ]; then
|
||||
# release file and implicitly the lock, if taken
|
||||
exec {FIRMWARE_LOCK}<&-
|
||||
unset FIRMWARE_LOCK
|
||||
fi
|
||||
|
||||
unset "TRAP_EXIT_FN[firmware_unlock_exclusive]"
|
||||
}
|
||||
TRAP_EXIT_FN+=([firmware_unlock_exclusive]=firmware_unlock_exclusive)
|
||||
|
||||
flock --exclusive --timeout 5 "${FIRMWARE_LOCK}" || return 2
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Output variables:
|
||||
# firmware_unlock_shared() - call to unlock when finished with critical section
|
||||
#
|
||||
# XXX: `firmware_lock_*` can't be taken recursively
|
||||
function firmware_lock_shared() {
|
||||
declare -g FIRMWARE_LOCK=""
|
||||
|
||||
# Open the file defined in ${FW_LOCK_FILE} for reading, and store the
|
||||
# corresponding file descriptor in ${FIRMWARE_LOCK} (it doesn't matter
|
||||
# whether this is a normal file or directory). This file descriptor
|
||||
# will only be used for locking - not for actual I/O.
|
||||
{ exec {FIRMWARE_LOCK}<"${FW_LOCK_FILE}"; } 2>/dev/null || return 1
|
||||
|
||||
declare -gf firmware_unlock_shared 1>/dev/null
|
||||
function firmware_unlock_shared() {
|
||||
if [ -v FIRMWARE_LOCK ]; then
|
||||
# release file and implicitly the lock, if taken
|
||||
exec {FIRMWARE_LOCK}<&-
|
||||
unset FIRMWARE_LOCK
|
||||
fi
|
||||
|
||||
unset "TRAP_EXIT_FN[firmware_unlock_shared]"
|
||||
}
|
||||
TRAP_EXIT_FN+=([firmware_unlock_shared]=firmware_unlock_shared)
|
||||
|
||||
flock --shared --timeout 5 "${FIRMWARE_LOCK}" || return 2
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Output variables:
|
||||
# IPL_TYPE
|
||||
# IPL_BUSID
|
||||
# IPL_WWPN
|
||||
# IPL_LUN
|
||||
function firmware_get_ipl_information() {
|
||||
declare -g IPL_TYPE="" IPL_BUSID="" IPL_WWPN="" IPL_LUN=""
|
||||
|
||||
# Take lock so we don't see any intermediate state from other helpers
|
||||
# running in parallel
|
||||
firmware_lock_shared || return 5
|
||||
|
||||
{ read -r IPL_TYPE _ < /sys/firmware/reipl/reipl_type; } 2>/dev/null \
|
||||
|| return 1
|
||||
{ read -r IPL_BUSID _ < /sys/firmware/reipl/fcp/device; } 2>/dev/null \
|
||||
|| return 2
|
||||
{ read -r IPL_WWPN _ < /sys/firmware/reipl/fcp/wwpn; } 2>/dev/null \
|
||||
|| return 3
|
||||
{ read -r IPL_LUN _ < /sys/firmware/reipl/fcp/lun; } 2>/dev/null \
|
||||
|| return 4
|
||||
|
||||
firmware_unlock_shared
|
||||
|
||||
# show read values in debug log if enabled
|
||||
if '@DEBUG@'; then
|
||||
declare -p IPL_TYPE IPL_BUSID IPL_WWPN IPL_LUN 1>&2
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Input:
|
||||
# 1: absolute canonical path to the scsi device in sysfs, e.g.:
|
||||
# /sys/devices/css0/0.0.0014/0.0.1700/host1/rport-1:0-0/target1:0:0/1:0:0:1075789848
|
||||
# Output variables:
|
||||
# SDEV_LUN
|
||||
function sdev_get_lun() {
|
||||
local sdev="${1}" sdev_lun_str
|
||||
# bash uses `intmax_t` as width for integer variables, and glibc
|
||||
# defines this either as `long int` on 64 bit systems, or
|
||||
# `long long int` on other.
|
||||
local -i sdev_lun=0 fcp_lun=0
|
||||
|
||||
sdev_lun_str="${sdev##*:}"
|
||||
# e.g.: 1075789848
|
||||
[[ "${sdev_lun_str}" == +([[:digit:]]) ]] || return 1
|
||||
# "cast" to integer
|
||||
sdev_lun="${sdev_lun_str}"
|
||||
|
||||
# convert the Linux integer LUN format to the hexadecimal 64 bit T10
|
||||
# LUN representation format used by many s390x interfaces
|
||||
(( fcp_lun = (((sdev_lun >> 0) & 0xffff) << 48)
|
||||
| (((sdev_lun >> 16) & 0xffff) << 32)
|
||||
| (((sdev_lun >> 32) & 0xffff) << 16)
|
||||
| (((sdev_lun >> 48) & 0xffff) << 0) ))
|
||||
# the '0x' prefix is part of the length
|
||||
printf -v SDEV_LUN "%#018llx" "${fcp_lun}"
|
||||
|
||||
# show read values in debug log if enabled
|
||||
if '@DEBUG@'; then
|
||||
declare -p SDEV_LUN 1>&2
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Input:
|
||||
# 1: path to the scsi device in sysfs, e.g.:
|
||||
# /sys/devices/css0/0.0.0014/0.0.1700/host1/rport-1:0-0/target1:0:0/1:0:0:1075789848
|
||||
# , or a symlink pointing to the scsi device, e.g.:
|
||||
# /sys/class/block/sds/device
|
||||
# Output variables:
|
||||
# SDEV_BUSID
|
||||
# SDEV_WWPN
|
||||
# SDEV_LUN
|
||||
function sdev_get_fcp_addressing() {
|
||||
local sdev="${1}" fcp_lun rport rport_wwpn zfcp_dev
|
||||
|
||||
declare -g SDEV_BUSID="" SDEV_WWPN="" SDEV_LUN=""
|
||||
|
||||
sdev="$(readlink -se "${sdev}")" || return 1
|
||||
|
||||
# get the LUN for this SDEV
|
||||
#
|
||||
# sets ${SDEV_LUN}
|
||||
sdev_get_lun "${sdev}" || return 2
|
||||
|
||||
# get the WWPN of the remote port this SDEV is attached to
|
||||
printf -v rport "%s" "${sdev}"/../../fc_remote_ports/rport-*:*-*
|
||||
# e.g.: /sys/devices/css0/0.0.0016/0.0.1740/host0/rport-0:0-1/fc_remote_ports/rport-0:0-1
|
||||
[ "${rport}" != "" ] || return 3
|
||||
|
||||
# XXX: This works even if the rport is currently in a bad
|
||||
# state, so e.g. when it has just gone down because of a
|
||||
# cable pull.
|
||||
{ read -r rport_wwpn _ < "${rport}"/port_name; } 2>/dev/null \
|
||||
|| return 4
|
||||
|
||||
# The Linux kernel doesn't guarantee the same format as in
|
||||
# /sys/firmware/..., so make sure it is the one we expect.
|
||||
[[ "${rport_wwpn}" =~ ^0x[[:xdigit:]]{1,16}$ ]] || return 5
|
||||
# the '0x' prefix is part of the length
|
||||
printf -v rport_wwpn "%#018llx" "${rport_wwpn}"
|
||||
|
||||
# get the Device Bus-ID of the device via which this SDEV is attached
|
||||
zfcp_dev="$(readlink -se "${sdev}"/../../../..)" || return 6
|
||||
# e.g.: /sys/devices/css0/0.0.0016/0.0.1740
|
||||
zfcp_dev="${zfcp_dev##*/}"
|
||||
|
||||
# shellcheck disable=2034
|
||||
SDEV_BUSID="${zfcp_dev}"
|
||||
# shellcheck disable=2034
|
||||
SDEV_WWPN="${rport_wwpn}"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Input:
|
||||
# 1: path to the scsi device in sysfs
|
||||
# Output variables:
|
||||
# SDEV_WWID
|
||||
function sdev_get_wwid() {
|
||||
local sdev="${1}"
|
||||
local -a wwid
|
||||
|
||||
declare -g SDEV_WWID=""
|
||||
|
||||
# read the volume identifier without stripping any content
|
||||
#
|
||||
# XXX: we can read the WWID file, even if the SDEV is currently not
|
||||
# operational (e.g.: due to the path has gone away), as long as
|
||||
# the VPD PG 83 is still cached in the kernel; and the page gets
|
||||
# only released on SDEV device release.
|
||||
{ readarray -d "" -t wwid < "${sdev}"/wwid; } 2>/dev/null \
|
||||
|| return 1
|
||||
if '@DEBUG@'; then declare -p wwid 1>&2; fi
|
||||
|
||||
# test whether we read something
|
||||
#
|
||||
# This strips all leading spaces from the beginning of the read WWID
|
||||
# (until the first non-space or NUL character), and checks whether the
|
||||
# result is empty.
|
||||
# Hence, we return early if the WWID consists of only whitespace.
|
||||
#
|
||||
# XXX: there could be unexpected characters in the returned ID.
|
||||
# `scsi_id` from the udev helpers sanitizes the strings it reads
|
||||
# from the devices, so they can be used in environment variables
|
||||
# without much danger.
|
||||
# But we don't export anything here, so it should be fine.
|
||||
[ "${wwid[0]/#*([[:space:]])}" != "" ] || return 2
|
||||
|
||||
# shellcheck disable=2034
|
||||
SDEV_WWID="${wwid[0]}"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Input:
|
||||
# 1: path to the scsi device in sysfs
|
||||
# Return Value:
|
||||
# == 0: SDEV referenced by `1` in good state
|
||||
# != 0: otherwise
|
||||
function sdev_test_path_state() {
|
||||
local sdev="${1}" state zfcp_failed zfcp_in_recovery rport port_state
|
||||
|
||||
sdev="$(readlink -se "${sdev}")" || return 1
|
||||
|
||||
{ read -r state _ < "${sdev}"/state; } 2>/dev/null || return 2
|
||||
{ read -r zfcp_failed _ < "${sdev}"/zfcp_failed; } 2>/dev/null \
|
||||
|| return 3
|
||||
{ read -r zfcp_in_recovery _ < "${sdev}"/zfcp_in_recovery; } 2>/dev/null \
|
||||
|| return 4
|
||||
|
||||
printf -v rport "%s" "${sdev}"/../../fc_remote_ports/rport-*:*-*
|
||||
# e.g.: /sys/devices/css0/0.0.0016/0.0.1740/host0/rport-0:0-1/fc_remote_ports/rport-0:0-1
|
||||
[ "${rport}" != "" ] || return 5
|
||||
{ read -r port_state _ < "${rport}"/port_state; } 2>/dev/null \
|
||||
|| return 6
|
||||
|
||||
if '@DEBUG@'; then
|
||||
declare -p state zfcp_failed zfcp_in_recovery port_state 1>&2
|
||||
fi
|
||||
|
||||
[ "${state}" = "running" ] || return 7
|
||||
[ "${zfcp_failed}" = "0" ] || return 8
|
||||
[ "${zfcp_in_recovery}" = "0" ] || return 9
|
||||
{ [ "${port_state}" = "Online" ] \
|
||||
|| [ "${port_state}" = "Marginal" ]; } || return 10
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Input:
|
||||
# *: all input parameters are used as quoted message
|
||||
function log_note() {
|
||||
logger -p 'daemon.notice' -t 'chreipl-fcp-mpath' "${*}" &>/dev/null
|
||||
}
|
||||
|
||||
# Input:
|
||||
# *: all input parameters are used as quoted message
|
||||
function log_crit() {
|
||||
logger -p 'daemon.crit' -t 'chreipl-fcp-mpath' "${*}" &>/dev/null
|
||||
}
|
||||
|
||||
# Input:
|
||||
# *: all input parameters are used as quoted message
|
||||
function log_alert() {
|
||||
logger -p 'daemon.alert' -t 'chreipl-fcp-mpath' "${*}" &>/dev/null
|
||||
}
|
||||
56
chreipl-fcp-mpath/chreipl-fcp-mpath-is-ipl-tgt.in
Normal file
56
chreipl-fcp-mpath/chreipl-fcp-mpath-is-ipl-tgt.in
Normal file
@@ -0,0 +1,56 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# chreipl-fcp-mpath: use multipath information to change FCP IPL target
|
||||
# (C) Copyright IBM Corp. 2021
|
||||
#
|
||||
# Uses the following system-utilities (and shell-builtins):
|
||||
# Those necessary for sourced library:
|
||||
# - chreipl-fcp-mpath-common.sh
|
||||
|
||||
# Find out whether the device in udev event environment variable ${DEVPATH}
|
||||
# represents the device we want to re-IPL from. We do this by comparing
|
||||
# Device-Bus-ID/Target-WWPN/LUN of the individual SDEVs to the parameters set
|
||||
# in `/sys/firmware/reipl/fcp/`.
|
||||
#
|
||||
# Makes use of udev event environment variables:
|
||||
# DM_UUID
|
||||
# SUBSYSTEM
|
||||
# DEVPATH
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
declare -gr debug_trace_tag=05iilt
|
||||
# shellcheck disable=SC1091
|
||||
source '@chreiplzfcpmp-lib@' || exit 127
|
||||
|
||||
firmware_get_ipl_information || exit 1
|
||||
|
||||
if [[ "${DM_UUID}" == mpath-* ]]; then
|
||||
# Assume Multipath Device Mapper Device;
|
||||
# e.g.: DEVPATH = /devices/virtual/block/dm-0
|
||||
declare sdev
|
||||
|
||||
# depends on `nullglob` from `chreipl-fcp-mpath-common.sh`
|
||||
for sdev in /sys/"${DEVPATH}"/slaves/sd*/device; do
|
||||
sdev_get_fcp_addressing "${sdev}" || continue
|
||||
[ "${SDEV_LUN}" = "${IPL_LUN}" ] || continue
|
||||
[ "${SDEV_WWPN}" = "${IPL_WWPN}" ] || continue
|
||||
[ "${SDEV_BUSID}" = "${IPL_BUSID}" ] || continue
|
||||
|
||||
exit 0
|
||||
done
|
||||
unset sdev
|
||||
|
||||
elif [ "${SUBSYSTEM}" = block ]; then
|
||||
# Assume SCSI Disk;
|
||||
# e.g.: DEVPATH = /devices/css0/0.0.0014/0.0.1700/host0/rport-0:0-0/target0:0:0/0:0:0:1074806808/block/sds
|
||||
|
||||
sdev_get_fcp_addressing /sys/"${DEVPATH}"/device || exit 2
|
||||
[ "${SDEV_LUN}" = "${IPL_LUN}" ] || exit 3
|
||||
[ "${SDEV_WWPN}" = "${IPL_WWPN}" ] || exit 4
|
||||
[ "${SDEV_BUSID}" = "${IPL_BUSID}" ] || exit 5
|
||||
|
||||
exit 0
|
||||
fi
|
||||
|
||||
exit 6
|
||||
86
chreipl-fcp-mpath/chreipl-fcp-mpath-is-ipl-vol.in
Normal file
86
chreipl-fcp-mpath/chreipl-fcp-mpath-is-ipl-vol.in
Normal file
@@ -0,0 +1,86 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# chreipl-fcp-mpath: use multipath information to change FCP IPL target
|
||||
# (C) Copyright IBM Corp. 2021
|
||||
#
|
||||
# Uses the following system-utilities (and shell-builtins):
|
||||
# Those necessary for sourced library:
|
||||
# - chreipl-fcp-mpath-common.sh
|
||||
|
||||
# Find out whether the device in environment variable ${DEVPATH} represents the
|
||||
# _volume_ that we IPL'ed from. We do this by comparing its WWID to the one
|
||||
# recorded in `@chreiplzfcpmp-id-file@`.
|
||||
#
|
||||
# Makes use of udev event environment variables:
|
||||
# DM_UUID
|
||||
# SUBSYSTEM
|
||||
# DEVPATH
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
declare -gr debug_trace_tag=11iilv
|
||||
# shellcheck disable=SC1091
|
||||
source '@chreiplzfcpmp-lib@' || exit 127
|
||||
|
||||
function id_file_read_ipl_information() {
|
||||
local -a records
|
||||
|
||||
declare -g REC_WWID="" REC_BUSID="" REC_WWPN="" REC_LUN=""
|
||||
|
||||
# lock file before reading ID, so we don't see any intermediate state
|
||||
id_file_lock_shared_no_create || return 1
|
||||
|
||||
{ readarray -d "" -t -u "${ID_FILE_LOCK}" records; } 2>/dev/null \
|
||||
|| return 2
|
||||
if '@DEBUG@'; then declare -p records 1>&2; fi
|
||||
|
||||
id_file_unlock_shared_no_create
|
||||
|
||||
[ "${#records[@]}" = "4" ] || return 3
|
||||
# check that none of the array fields contains whitespace only
|
||||
[ "${records[0]/#*([[:space:]])}" != "" ] || return 4
|
||||
[ "${records[1]/#*([[:space:]])}" != "" ] || return 5
|
||||
[ "${records[2]/#*([[:space:]])}" != "" ] || return 6
|
||||
[ "${records[3]/#*([[:space:]])}" != "" ] || return 7
|
||||
|
||||
REC_WWID="${records[0]}"
|
||||
REC_BUSID="${records[1]}"
|
||||
REC_WWPN="${records[2]}"
|
||||
REC_LUN="${records[3]}"
|
||||
return 0
|
||||
}
|
||||
|
||||
id_file_read_ipl_information || exit 1
|
||||
|
||||
if [[ "${DM_UUID}" == mpath-* ]]; then
|
||||
# Assume Multipath Device Mapper Device;
|
||||
# e.g.: DEVPATH = /devices/virtual/block/dm-0
|
||||
declare sdev found=false
|
||||
|
||||
for sdev in /sys/"${DEVPATH}"/slaves/sd*/device; do
|
||||
if sdev_get_wwid "${sdev}"; then
|
||||
found=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
unset sdev
|
||||
|
||||
"${found}" || exit 2
|
||||
|
||||
elif [ "${SUBSYSTEM}" = block ]; then
|
||||
# Assume SCSI Disk;
|
||||
# e.g.: DEVPATH = /devices/css0/0.0.0014/0.0.1700/host0/rport-0:0-0/target0:0:0/0:0:0:1074806808/block/sds
|
||||
|
||||
sdev_get_wwid /sys/"${DEVPATH}"/device || exit 3
|
||||
fi
|
||||
|
||||
# set by `sdev_get_wwid` and `id_file_read_ipl_information`
|
||||
[ "${SDEV_WWID}" = "${REC_WWID}" ] || exit 4
|
||||
|
||||
firmware_get_ipl_information || exit 5
|
||||
# set by `firmware_get_ipl_information` and `id_file_read_ipl_information`
|
||||
[ "${IPL_BUSID}" = "${REC_BUSID}" ] || exit 6
|
||||
[ "${IPL_WWPN}" = "${REC_WWPN}" ] || exit 7
|
||||
[ "${IPL_LUN}" = "${REC_LUN}" ] || exit 8
|
||||
|
||||
exit 0
|
||||
25
chreipl-fcp-mpath/chreipl-fcp-mpath-is-reipl-zfcp.in
Normal file
25
chreipl-fcp-mpath/chreipl-fcp-mpath-is-reipl-zfcp.in
Normal file
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# chreipl-fcp-mpath: use multipath information to change FCP IPL target
|
||||
# (C) Copyright IBM Corp. 2021
|
||||
#
|
||||
# Uses the following system-utilities (and shell-builtins):
|
||||
# Those necessary for sourced library:
|
||||
# - chreipl-fcp-mpath-common.sh
|
||||
|
||||
# Find out whether ReIPL is gonna happen from a SCSI volume attached via zFCP
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
declare -gr debug_trace_tag=00iriz
|
||||
# shellcheck disable=SC1091
|
||||
source '@chreiplzfcpmp-lib@' || exit 127
|
||||
|
||||
declare reipl_type
|
||||
|
||||
{ read -r reipl_type _ < /sys/firmware/reipl/reipl_type; } 2>/dev/null || exit 1
|
||||
if '@DEBUG@'; then declare -p reipl_type 1>&2; fi
|
||||
|
||||
[ "${reipl_type}" = "fcp" ] || exit 2
|
||||
|
||||
exit 0
|
||||
@@ -0,0 +1,73 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# chreipl-fcp-mpath: use multipath information to change FCP IPL target
|
||||
# (C) Copyright IBM Corp. 2021
|
||||
#
|
||||
# Uses the following system-utilities (and shell-builtins):
|
||||
# Those necessary for sourced library:
|
||||
# - chreipl-fcp-mpath-common.sh
|
||||
# GNU coreutils:
|
||||
# - truncate
|
||||
# util-linux:
|
||||
# - hexdump
|
||||
|
||||
# Record the identification of the volume we want to re-IPL from
|
||||
#
|
||||
# Makes use of udev event environment variables:
|
||||
# DM_UUID
|
||||
# SUBSYSTEM
|
||||
# DEVPATH
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
declare -gr debug_trace_tag=10rvid
|
||||
# shellcheck disable=SC1091
|
||||
source '@chreiplzfcpmp-lib@' || exit 127
|
||||
|
||||
function id_file_record_ipl_information() {
|
||||
local sdev_wwid="${1}" ipl_busid="${2}" ipl_wwpn="${3}" ipl_lun="${4}"
|
||||
|
||||
# lock file before writing ID, so noone sees any intermediate state
|
||||
id_file_lock_exclusive_create || return 1
|
||||
|
||||
# reset ID without removing the file (necessary for the locking to work
|
||||
# properly, since the FD we use for locking is on this file/inode)
|
||||
truncate --no-create --size=0 "${ID_FILE}" || return 3
|
||||
echo -ne "${sdev_wwid}\x00${ipl_busid}\x00${ipl_wwpn}\x00${ipl_lun}\x00" \
|
||||
>>"${ID_FILE}" || return 4
|
||||
|
||||
if '@DEBUG@'; then hexdump -vC "${ID_FILE}" 1>&2; fi
|
||||
|
||||
id_file_unlock_exclusive_create
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
if [[ "${DM_UUID}" == mpath-* ]]; then
|
||||
# Assume Multipath Device Mapper Device;
|
||||
# e.g.: DEVPATH = /devices/virtual/block/dm-0
|
||||
declare sdev
|
||||
|
||||
for sdev in /sys/"${DEVPATH}"/slaves/sd*/device; do
|
||||
if sdev_get_wwid "${sdev}"; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
unset sdev
|
||||
|
||||
elif [ "${SUBSYSTEM}" = block ]; then
|
||||
# Assume SCSI Disk;
|
||||
# e.g.: DEVPATH = /devices/css0/0.0.0014/0.0.1700/host0/rport-0:0-0/target0:0:0/0:0:0:1074806808/block/sds
|
||||
|
||||
sdev_get_wwid /sys/"${DEVPATH}"/device
|
||||
fi
|
||||
# shellcheck disable=SC2153
|
||||
[ "${SDEV_WWID}" != "" ] || exit 1
|
||||
|
||||
firmware_get_ipl_information || exit 2
|
||||
# shellcheck disable=SC2153
|
||||
id_file_record_ipl_information \
|
||||
"${SDEV_WWID}" "${IPL_BUSID}" "${IPL_WWPN}" "${IPL_LUN}" \
|
||||
|| exit 3
|
||||
|
||||
exit 0
|
||||
158
chreipl-fcp-mpath/chreipl-fcp-mpath-try-change-ipl-path.in
Normal file
158
chreipl-fcp-mpath/chreipl-fcp-mpath-try-change-ipl-path.in
Normal file
@@ -0,0 +1,158 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# chreipl-fcp-mpath: use multipath information to change FCP IPL target
|
||||
# (C) Copyright IBM Corp. 2021
|
||||
#
|
||||
# Uses the following system-utilities (and shell-builtins):
|
||||
# Those necessary for sourced library:
|
||||
# - chreipl-fcp-mpath-common.sh
|
||||
# GNU coreutils:
|
||||
# - truncate
|
||||
# util-linux:
|
||||
# - hexdump
|
||||
|
||||
# Try to change the current re-IPL target to a dfferent, operational path to
|
||||
# the same volume.
|
||||
#
|
||||
# Makes use of udev event environment variables:
|
||||
# DM_UUID
|
||||
# SUBSYSTEM
|
||||
# DEVPATH
|
||||
# CHREIPL_FCP_MPATH_IS_TGT
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
declare -gr debug_trace_tag=15tcip
|
||||
# shellcheck disable=SC1091
|
||||
source '@chreiplzfcpmp-lib@' || exit 127
|
||||
|
||||
function apply_ipl_information() {
|
||||
local sdev_wwid="${1}"
|
||||
local sdev_busid="${2}" sdev_wwpn="${3}" sdev_lun="${4}"
|
||||
local ipl_type="${5}" ipl_busid="${6}" ipl_wwpn="${7}" ipl_lun="${8}"
|
||||
local -a records
|
||||
local try_update_id_file=true
|
||||
|
||||
[ "${ipl_type}" = "fcp" ] || return 1
|
||||
|
||||
[[ "${sdev_busid}" =~ ^[[:xdigit:]]{1,3}\.[[:xdigit:]]\.[[:xdigit:]]{1,4}$ ]] \
|
||||
|| return 2
|
||||
[[ "${sdev_wwpn}" =~ ^0x[[:xdigit:]]{16}$ ]] || return 3
|
||||
[[ "${sdev_lun}" =~ ^0x[[:xdigit:]]{16}$ ]] || return 4
|
||||
|
||||
# After updating the firmware re-IPL information below we also try to
|
||||
# update the information stored in the ID file (necessary, so it
|
||||
# contains the correct Device-Bus-ID/WWPN/LUN after the update). For
|
||||
# the update of the ID file we try to grab an exclusive lock, so there
|
||||
# are no overlapping reads/writes.
|
||||
#
|
||||
# In case we can't get the lock because the ID file is missing, but we
|
||||
# have a direct TGT match, we may still try to change the re-IPL
|
||||
# information, but skip the ID file update.
|
||||
#
|
||||
# "direct match" means, the event subject is either the SDEV that is
|
||||
# currently set as re-IPL target, or it is the dm-multipath device that
|
||||
# currently contains the re-IPL target.
|
||||
if ! id_file_lock_exclusive_no_create; then
|
||||
# rc == 1 --> could not read ${ID_FILE}
|
||||
[ "${PIPESTATUS[0]}" -eq 1 ] || return 5
|
||||
# if true, we are dealing with a direct TGT match
|
||||
[ "${CHREIPL_FCP_MPATH_IS_TGT}" = "true" ] || return 6
|
||||
try_update_id_file=false
|
||||
fi
|
||||
|
||||
# If we have a direct match (see in the comment above), we know
|
||||
# that we have a path to the current re-IPL volume - no matter of the
|
||||
# WWID. Otherwise, we got here by comparing the WWID of the event
|
||||
# subject with the one recorded in the ID file; in this case we try to
|
||||
# make sure the information is still up-to-date.
|
||||
if [ "${CHREIPL_FCP_MPATH_IS_TGT}" != "true" ]; then
|
||||
# last bail to make sure we don't overwrite user choices..
|
||||
#
|
||||
# XXX: this will *NOT* prevent the race completely, but at least
|
||||
# make it less likely
|
||||
{ readarray -d "" -t -u "${ID_FILE_LOCK}" records; } 2>/dev/null \
|
||||
|| return 7
|
||||
if '@DEBUG@'; then declare -p records 1>&2; fi
|
||||
|
||||
[ "${#records[@]}" = "4" ] || return 8
|
||||
[ "${records[0]}" = "${sdev_wwid}" ] || return 9
|
||||
[ "${records[1]}" = "${ipl_busid}" ] || return 10
|
||||
[ "${records[2]}" = "${ipl_wwpn}" ] || return 11
|
||||
[ "${records[3]}" = "${ipl_lun}" ] || return 12
|
||||
fi
|
||||
|
||||
# Take lock so we don't see any intermediate state from other helpers
|
||||
# running in parallel
|
||||
firmware_lock_exclusive || return 13
|
||||
|
||||
if ! { echo "${sdev_busid}" >| /sys/firmware/reipl/fcp/device \
|
||||
&& echo "${sdev_wwpn}" >| /sys/firmware/reipl/fcp/wwpn \
|
||||
&& echo "${sdev_lun}" >| /sys/firmware/reipl/fcp/lun; };
|
||||
then
|
||||
log_alert "Changing the re-IPL device failed. The current re-IPL settings might be inconsistent. Check and correct the settings (see the README.md of chreipl-fcp-mpath) to make sure that the current re-IPL device is valid."
|
||||
return 14
|
||||
fi
|
||||
|
||||
firmware_unlock_exclusive
|
||||
|
||||
if [ "${sdev_busid}" != "${ipl_busid}" ] \
|
||||
|| [ "${sdev_wwpn}" != "${ipl_wwpn}" ] \
|
||||
|| [ "${sdev_lun}" != "${ipl_lun}" ]; then
|
||||
log_note "Changed re-IPL path to: ${sdev_busid}:${sdev_wwpn}:${sdev_lun}."
|
||||
fi
|
||||
|
||||
# Try to update the information in the ID file if we have gotten the
|
||||
# lock for it.
|
||||
if ${try_update_id_file}; then
|
||||
# reset ID without removing the file
|
||||
truncate --no-create --size=0 "${ID_FILE}" || return 15
|
||||
echo -ne "${sdev_wwid}\x00${sdev_busid}\x00${sdev_wwpn}\x00${sdev_lun}\x00" \
|
||||
>>"${ID_FILE}" || return 16
|
||||
|
||||
id_file_unlock_exclusive_no_create
|
||||
|
||||
if '@DEBUG@'; then hexdump -vC "${ID_FILE}" 1>&2; fi
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
declare -g SDEV=""
|
||||
if [[ "${DM_UUID}" == mpath-* ]]; then
|
||||
# Assume Multipath Device Mapper Device;
|
||||
# e.g.: DEVPATH = /devices/virtual/block/dm-0
|
||||
|
||||
for sdev in /sys/"${DEVPATH}"/slaves/sd*/device; do
|
||||
if sdev_test_path_state "${sdev}"; then
|
||||
SDEV="${sdev}"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# No path of the multipath-device that represents the IPL volume is
|
||||
# online.
|
||||
if [ "${SDEV}" = "" ]; then
|
||||
log_crit "The re-IPL device cannot be changed because no operational path to the re-IPL volume remains. The next re-IPL might fail unless you re-attach or enable at least one valid path to the re-IPL volume."
|
||||
fi
|
||||
|
||||
elif [ "${SUBSYSTEM}" = block ]; then
|
||||
# Assume SCSI Disk;
|
||||
# e.g.: DEVPATH = /devices/css0/0.0.0014/0.0.1700/host0/rport-0:0-0/target0:0:0/0:0:0:1074806808/block/sds
|
||||
if sdev_test_path_state /sys/"${DEVPATH}"/device; then
|
||||
SDEV=/sys/"${DEVPATH}"/device
|
||||
fi
|
||||
|
||||
fi
|
||||
[ "${SDEV}" != "" ] || exit 0
|
||||
|
||||
sdev_get_wwid "${SDEV}" || exit 0
|
||||
sdev_get_fcp_addressing "${SDEV}" || exit 0
|
||||
firmware_get_ipl_information || exit 0
|
||||
|
||||
# shellcheck disable=SC2153
|
||||
apply_ipl_information \
|
||||
"${SDEV_WWID}" "${SDEV_BUSID}" "${SDEV_WWPN}" "${SDEV_LUN}" \
|
||||
"${IPL_TYPE}" "${IPL_BUSID}" "${IPL_WWPN}" "${IPL_LUN}" || exit 0
|
||||
|
||||
exit 0
|
||||
183
chreipl-fcp-mpath/chreipl-fcp-mpath.7
Normal file
183
chreipl-fcp-mpath/chreipl-fcp-mpath.7
Normal file
@@ -0,0 +1,183 @@
|
||||
.\" Automatically generated by Pandoc 2.16.1
|
||||
.\"
|
||||
.TH "CHREIPL-FCP-MPATH" "7" "2022-02-24" "s390-tools 2.20.0-build-20220224" "Administrator Manual"
|
||||
.hy
|
||||
.SH NAME
|
||||
.PP
|
||||
chreipl-fcp-mpath - use multipath information for re-IPL path failover
|
||||
on a running Linux instance
|
||||
.SH DESCRIPTION
|
||||
.PP
|
||||
The IPL process of Linux on Z or LinuxONE from an FCP-attached SCSI
|
||||
volume uses exactly one path to the volume.
|
||||
If this path is unavailable, the IPL fails.
|
||||
.PP
|
||||
The \f[B]chreipl-fcp-mpath\f[R] toolset monitors \f[B]udev\f[R] events
|
||||
about paths to the re-IPL volume.
|
||||
If the currently configured re-IPL path becomes unavailable, the toolset
|
||||
checks for operational paths to the same volume.
|
||||
If available, it reconfigures the re-IPL settings to use an operational
|
||||
path.
|
||||
.PP
|
||||
Thus, re-IPL from an FCP-attached SCSI volume can be successful despite
|
||||
path failures on a running Linux instance if at least one path to the
|
||||
re-IPL volume remains operational.
|
||||
.PP
|
||||
\f[B]Chreipl-fcp-mpath\f[R] requires \f[B]udev\f[R],
|
||||
\f[B]multipathd\f[R] and \f[B]dm-multipath\f[R].
|
||||
Once installed, the toolset runs automatically and autonomously.
|
||||
No user intervention is possible or required.
|
||||
.PP
|
||||
Other than installing the toolset, there is no user interface for
|
||||
\f[B]chreipl-fcp-mpath\f[R].
|
||||
.SS Requirements
|
||||
.PP
|
||||
The \f[B]chreipl-fcp-mpath\f[R] tool has the following requirements on
|
||||
the Linux instance that is being monitored:
|
||||
.IP \[bu] 2
|
||||
The Linux instance must have started successfully, during IPL.
|
||||
.IP \[bu] 2
|
||||
The running Linux instance must use \f[B]dm-multipath\f[R] and
|
||||
\f[B]multipathd\f[R] for the configured re-IPL volume - a volume that
|
||||
contains a zipl boot record and has one of its paths used in the re-IPL
|
||||
configuration.
|
||||
.IP \[bu] 2
|
||||
\f[B]udev\f[R] must run.
|
||||
.IP \[bu] 2
|
||||
The toolset must observe at least one event about the configured re-IPL
|
||||
path.
|
||||
Examples for such events are: the SCSI disk comes online, or a path of
|
||||
the corresponding multipath device goes down or comes back online.
|
||||
.RS 2
|
||||
.IP \[bu] 2
|
||||
The WWID of the re-IPL volume must not change while the Linux instance
|
||||
is running.
|
||||
.RE
|
||||
.IP \[bu] 2
|
||||
When the configured re-IPL path becomes unavailable while the Linux
|
||||
instance is running, at least one operational path to the re-IPL volume
|
||||
must be available, or must become available.
|
||||
If no such path is available when the Linux instance is rebooted, the
|
||||
re-IPL path is not changed.
|
||||
.IP \[bu] 2
|
||||
The tool assumes that any manually reconfigured re-IPL device is valid
|
||||
and operational.
|
||||
.RS 2
|
||||
.PP
|
||||
The tool treats a newly configured re-IPL device like the initially
|
||||
configured re-IPL device.
|
||||
In particular, if the newly configured re-IPL device fulfills the
|
||||
requirements of the tool, re-IPL path failover takes place if the
|
||||
configured re-IPL path becomes unavailable.
|
||||
.RE
|
||||
.SS Caution with Manual Changes to the Configured re-IPL Target
|
||||
.PP
|
||||
\f[B]chreipl-fcp-mpath\f[R] is designed to accept operator-inititated
|
||||
changes of the re-IPL device.
|
||||
However, concurrent changes by the operator and tool driven changes can
|
||||
result in the operator change being overwritten.
|
||||
.PP
|
||||
To avoid this problem, change the re-IPL device only during steady-state
|
||||
operations, when no path events happen.
|
||||
Alternatively, make sure that no events are processed while you change
|
||||
the device.
|
||||
See EXAMPLES for one way to suspend event processing.
|
||||
.SH MESSAGES
|
||||
.PP
|
||||
During monitoring and event processing, \f[B]chreipl-fcp-mpath\f[R]
|
||||
writes messages to the syslog.
|
||||
.PP
|
||||
When the configured re-IPL path is changed to a different path to the
|
||||
same volume (priority \f[I]daemon.notice\f[R]):
|
||||
.RS
|
||||
.PP
|
||||
Changed re-IPL path to: <device-bus-id>:<wwpn>:<lun>.
|
||||
.RE
|
||||
.PP
|
||||
When a path event indicates that the last available path has become
|
||||
non-operational (priority \f[I]daemon.alert\f[R]):
|
||||
.RS
|
||||
.PP
|
||||
The re-IPL device cannot be changed because no operational path to the
|
||||
re-IPL volume remains.
|
||||
The next re-IPL might fail unless you re-attach or enable at least one
|
||||
valid path to the re-IPL volume.
|
||||
.RE
|
||||
.PP
|
||||
When changing the configured re-IPL device failed because of an error
|
||||
with the used Linux kernel interface (priority \f[I]daemon.crit\f[R]):
|
||||
.RS
|
||||
.PP
|
||||
Changing the re-IPL device failed.
|
||||
The current re-IPL settings might be inconsistent.
|
||||
Check and correct the settings (see the README.md of chreipl-fcp-mpath)
|
||||
to make sure that the current re-IPL device is valid.
|
||||
.RE
|
||||
.PP
|
||||
A failure to change the re-IPL device can indicate an inconsistent
|
||||
setting that cannot be corrected automatically by
|
||||
\f[B]chreipl-fcp-mpath\f[R].
|
||||
As a result, the next re-IPL might fail or might not use the intended
|
||||
re-IPL device.
|
||||
.PP
|
||||
You can use the following tools to check and correct the current
|
||||
settings:
|
||||
.IP \[bu] 2
|
||||
\f[B]lsreipl\f[R] to confirm that the intended re-IPL device is
|
||||
configured;
|
||||
.IP \[bu] 2
|
||||
\f[B]chreipl\f[R] to change the re-IPL device;
|
||||
.IP \[bu] 2
|
||||
\f[B]lszfcp\f[R] to inspect the state of available paths to the re-IPL
|
||||
device.
|
||||
.SH EXAMPLES
|
||||
.SS Manual Changes to the Configured re-IPL Device
|
||||
.PP
|
||||
As outlined in DESCRIPTION, be cautious when manually changing the
|
||||
configured re-IPL device.
|
||||
Assure that your reconfiguration actions do not collide with concurrent
|
||||
automatic event processing by \f[B]chreipl-fcp-mpath\f[R].
|
||||
You can avoid such collisions, by stopping event processing, making your
|
||||
changes, and then re-enabling event processing.
|
||||
You need \f[I]root\f[R] privileges for running the commands in the
|
||||
following example:
|
||||
.IP
|
||||
.nf
|
||||
\f[C]
|
||||
\[ti] # udevadm settle
|
||||
\[ti] # udevadm control --stop-exec-queue
|
||||
\[ti] # chreipl ...
|
||||
\[ti] # udevadm control --start-exec-queue
|
||||
\f[R]
|
||||
.fi
|
||||
.SS Listing messages with journalctl
|
||||
.PP
|
||||
If your Linux instance includes \f[B]journalctl\f[R], use the following
|
||||
command to list all messages that are issued by
|
||||
\f[B]chreipl-fcp-mpath\f[R]:
|
||||
.IP
|
||||
.nf
|
||||
\f[C]
|
||||
\[ti] # journalctl -t chreipl-fcp-mpath
|
||||
\f[R]
|
||||
.fi
|
||||
.PP
|
||||
To list only messages that were issued since the last IPL, use this
|
||||
command:
|
||||
.IP
|
||||
.nf
|
||||
\f[C]
|
||||
\[ti] # journalctl -t chreipl-fcp-mpath -b
|
||||
\f[R]
|
||||
.fi
|
||||
.SH REPORTING BUGS
|
||||
.PP
|
||||
Use the \f[B]Issues\f[R] functionality on GitHub to report any bugs in
|
||||
\f[B]chreipl-fcp-mpath\f[R]: s390-tools
|
||||
Issues (https://github.com/ibm-s390-linux/s390-tools/issues).
|
||||
.SH SEE ALSO
|
||||
.PP
|
||||
\f[B]chreipl\f[R](8), \f[B]dracut\f[R](8), \f[B]journalctl\f[R](1),
|
||||
\f[B]lsreipl(8)\f[R], \f[B]lszfcp\f[R](8), \f[B]multipath\f[R](8),
|
||||
\f[B]multipathd\f[R](8), \f[B]udev\f[R](7), \f[B]udevadm\f[R](8),
|
||||
\f[B]zipl\f[R](8)
|
||||
93
chreipl-fcp-mpath/chreipl-fcp-mpath.mak
Normal file
93
chreipl-fcp-mpath/chreipl-fcp-mpath.mak
Normal file
@@ -0,0 +1,93 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# chreipl-fcp-mpath: use multipath information to change FCP IPL target
|
||||
# (C) Copyright IBM Corp. 2021
|
||||
#
|
||||
# Uses the following system-utilities (and shell-builtins):
|
||||
# Utilities list in GNU Make Conventions:
|
||||
# https://www.gnu.org/software/make/manual/make.html#Utilities-in-Makefiles
|
||||
# GNU coreutils:
|
||||
# - mktemp
|
||||
# If $(ENABLE_DOC) is `1`:
|
||||
# Pandoc:
|
||||
# - pandoc
|
||||
# GNU coreutils:
|
||||
# - date
|
||||
|
||||
#
|
||||
## Paths and Build Variables
|
||||
#
|
||||
|
||||
# Install the configuration file for dracut, to automatically pull in the
|
||||
# toolset into the initial ramdisk, when built with it.
|
||||
HAVE_DRACUT = 0
|
||||
|
||||
# Build documentation; requires: Pandoc
|
||||
ENABLE_DOC = 0
|
||||
|
||||
# https://www.gnu.org/software/make/manual/make.html#Directory-Variables
|
||||
CHREIPLZFCPMPDIR = $(USRLIBDIR)/chreipl-fcp-mpath
|
||||
UDEVDIR = $(USRLIBDIR)/udev
|
||||
UDEVRULESDIR = $(UDEVDIR)/rules.d
|
||||
UDEVRUNDIR = /run/udev
|
||||
DRACUTDIR = $(USRLIBDIR)/dracut
|
||||
DRACUTCONFDIR = $(DRACUTDIR)/dracut.conf.d
|
||||
DEBUGOUTDIR = $(UDEVRUNDIR)
|
||||
|
||||
INSTALL_EXEC = $(INSTALL) -g $(GROUP) -o $(OWNER) --preserve-timestamps
|
||||
INSTALL_DATA = $(INSTALL_EXEC) --mode=0644
|
||||
|
||||
# used for data exchange and synchronization across the different helpers
|
||||
chreiplzfcpmp-id-file = $(UDEVRUNDIR)/chreiplzfcpmp-ipl-volume-id
|
||||
# file used to implement mutual exclusion when accessing firmware IPL info:
|
||||
# - this should be something that is (practically) always available, so we
|
||||
# dont have to worry about fallbacks or error-handling;
|
||||
# - at the same time, it should not be used by anything else with flock(2) to
|
||||
# hold a lock for long periods.
|
||||
chreiplzfcpmp-fwlock-file = /sys/firmware/reipl
|
||||
|
||||
.DELETE_ON_ERROR:
|
||||
|
||||
# export build-time definitions to the scripts/built-components
|
||||
define chreiplzfcpmp-sed-buildvar-replace =
|
||||
tmpout=$$(mktemp -p ./ .make.tmp.XXXXXXXXXXXXXXXX) && { \
|
||||
$(SED) -E \
|
||||
-e 's|@DEBUG@|$(if $(filter 1,$(D)),true,false)|g' \
|
||||
-e 's|@chreiplzfcpmp-id-file@|$(chreiplzfcpmp-id-file)|g' \
|
||||
-e 's|@chreiplzfcpmp-fwlock-file@|$(chreiplzfcpmp-fwlock-file)|g' \
|
||||
-e 's|@chreiplzfcpmp-lib@|$(CHREIPLZFCPMPDIR)/chreipl-fcp-mpath-common.sh|g' \
|
||||
-e 's|@debugoutdir@|$(DEBUGOUTDIR)|g' \
|
||||
-e 's|@udevdir@|$(UDEVDIR)|g' \
|
||||
-e 's|@udevrulesdir@|$(UDEVRULESDIR)|g' \
|
||||
$(1) > $${tmpout} \
|
||||
&& mv $${tmpout} $(2) \
|
||||
|| { rm $${tmpout}; false; } \
|
||||
; }
|
||||
endef
|
||||
|
||||
.PHONY: clean-mk-temp
|
||||
clean: clean-mk-temp
|
||||
clean-mk-temp:
|
||||
rm -f .make.tmp.[[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]]
|
||||
|
||||
# Definitions for generating documentation when $(ENABLE_DOC) is set to `1`
|
||||
|
||||
PANDOCFLAGS = --fail-if-warnings
|
||||
ALL_PANDOCFLAGS = --preserve-tabs --tab-stop=8 --strip-comments \
|
||||
--standalone --self-contained \
|
||||
-M date="$(shell date +'%Y-%m-%d')" \
|
||||
$(PANDOCFLAGS)
|
||||
|
||||
$(eval $(call cmd_define, PANDOC," PANDOC ",pandoc))
|
||||
|
||||
%.html : ALL_PANDOCFLAGS += -t html
|
||||
%.html : %.md
|
||||
$(PANDOC) $(ALL_PANDOCFLAGS) -f gfm -o $(@) $(<)
|
||||
|
||||
%.pdf : ALL_PANDOCFLAGS += -t latex --toc
|
||||
%.pdf : %.md
|
||||
$(PANDOC) $(ALL_PANDOCFLAGS) -f gfm -o $(@) $(<)
|
||||
|
||||
%.7 : ALL_PANDOCFLAGS += -t man
|
||||
%.7 : %.md
|
||||
$(PANDOC) $(ALL_PANDOCFLAGS) -f gfm -o $(@) $(<)
|
||||
@@ -0,0 +1,16 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# chreipl-fcp-mpath: use multipath information to change FCP IPL target
|
||||
# (C) Copyright IBM Corp. 2021
|
||||
|
||||
add_dracutmodules+=" bash multipath udev-rules "
|
||||
|
||||
install_items+=" @udevrulesdir@/70-chreipl-fcp-mpath.rules "
|
||||
install_items+=" @chreiplzfcpmp-lib@ "
|
||||
install_items+=" @udevdir@/chreipl-fcp-mpath-is-ipl-tgt "
|
||||
install_items+=" @udevdir@/chreipl-fcp-mpath-is-ipl-vol "
|
||||
install_items+=" @udevdir@/chreipl-fcp-mpath-is-reipl-zfcp "
|
||||
install_items+=" @udevdir@/chreipl-fcp-mpath-record-volume-identifier "
|
||||
install_items+=" @udevdir@/chreipl-fcp-mpath-try-change-ipl-path "
|
||||
|
||||
install_items+=" flock hexdump logger mktemp readlink sync truncate "
|
||||
101
chreipl-fcp-mpath/udev/rules.d/70-chreipl-fcp-mpath.rules
Normal file
101
chreipl-fcp-mpath/udev/rules.d/70-chreipl-fcp-mpath.rules
Normal file
@@ -0,0 +1,101 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# chreipl-fcp-mpath: use multipath information to change FCP IPL target
|
||||
# (C) Copyright IBM Corp. 2021
|
||||
|
||||
# Did the event affect a multipath or scsi disk device?
|
||||
ACTION=="change", KERNEL=="dm-[0-9]*", SUBSYSTEM=="block", \
|
||||
ENV{DM_UUID}=="mpath-*", ENV{DM_ACTION}=="PATH_FAILED", \
|
||||
GOTO="chreipl_fcp_mpath_path_change"
|
||||
ACTION=="change", KERNEL=="dm-[0-9]*", SUBSYSTEM=="block", \
|
||||
ENV{DM_UUID}=="mpath-*", ENV{DM_ACTION}=="PATH_REINSTATED", \
|
||||
GOTO="chreipl_fcp_mpath_path_change"
|
||||
ACTION=="add", KERNEL=="sd[a-z]*", SUBSYSTEM=="block", \
|
||||
GOTO="chreipl_fcp_mpath_path_change"
|
||||
GOTO="chreipl_fcp_mpath_end"
|
||||
LABEL="chreipl_fcp_mpath_path_change"
|
||||
|
||||
# Is this system IPL'ed (IOW, are we on s390x)? And do we ReIPL via zFCP?
|
||||
#
|
||||
# udev(7): If no absolute path is given, the program is expected to live
|
||||
# in /usr/lib/udev; otherwise, the absolute path must be
|
||||
# specified.
|
||||
TEST!="/sys/firmware/ipl", GOTO="chreipl_fcp_mpath_end"
|
||||
PROGRAM!="chreipl-fcp-mpath-is-reipl-zfcp", GOTO="chreipl_fcp_mpath_end"
|
||||
|
||||
# Consider the following scenarios.
|
||||
# Either:
|
||||
#
|
||||
# (A) We recognized a new SCSI Disk. This might represent:
|
||||
# (a) the path we want to ReIPL from;
|
||||
# (b) an alternative path to the volume we want to ReIPL from;
|
||||
# (c) a path to some unrelated volume.
|
||||
#
|
||||
# Or:
|
||||
#
|
||||
# (B) We recognized a PATH_ event for a multipath device. This might represent:
|
||||
# the path we want to ReIPL from:
|
||||
# (a) went away;
|
||||
# (b) came back online;
|
||||
# an alternative path to the volume we want to ReIPL from:
|
||||
# (c) went away;
|
||||
# (d) came back online;
|
||||
# (e) some unrelated multipath device saw an event.
|
||||
|
||||
# Test whether the affected device is, or contains, the current IPL target.
|
||||
#
|
||||
# This covers scenarios:
|
||||
# (A) (a),
|
||||
# (B) (a)/(b)/(c)/(d)
|
||||
PROGRAM!="chreipl-fcp-mpath-is-ipl-tgt", \
|
||||
ENV{CHREIPL_FCP_MPATH_IS_TGT}="false", \
|
||||
GOTO="chreipl_fcp_mpath_not_direct_match"
|
||||
ENV{CHREIPL_FCP_MPATH_IS_TGT}="true"
|
||||
|
||||
# Record the WWID, Device-Bus-ID, Remote WWPN, and LUN of the ReIPL target
|
||||
# (see `chreipl-fcp-mpath-is-ipl-vol` for usecases). This information
|
||||
# might change, depending on whether the machine operator changes the ReIPL
|
||||
# target to a different volume.
|
||||
#
|
||||
# XXX: Because the kernel doesn't generate any events upon changing of
|
||||
# the ReIPL target, the chreipl-fcp-mpath toolset can't take any
|
||||
# actions until the next path event for the new target is
|
||||
# generated. Following that, we assume that when the machine
|
||||
# operator changes the ReIPL target, the new target is reachable
|
||||
# and in a good state at this point in time.
|
||||
PROGRAM!="chreipl-fcp-mpath-record-volume-identifier", \
|
||||
GOTO="chreipl_fcp_mpath_try_change_ipl_path"
|
||||
GOTO="chreipl_fcp_mpath_try_change_ipl_path"
|
||||
|
||||
# If the even subject is not a direct match (not the sdev that is the current
|
||||
# ReIPL target, and not a mpath device that contains the current ReIPL target)
|
||||
LABEL="chreipl_fcp_mpath_not_direct_match"
|
||||
|
||||
# While this sdev/mpath device doesn't directly correspond to the path
|
||||
# currently set as ReIPL target, it might still point to the same volume.
|
||||
#
|
||||
# For mpath devices this can happen if the original ReIPL target is completely
|
||||
# gone from the machine, and so there is no way we can successfully, directly
|
||||
# compare the ReIPL parameters to the sdevs of the mpath device.
|
||||
#
|
||||
# For cases like these we recorded the volume identifier, which we now can
|
||||
# compare, and so still decide whether we are addressing the correct volume.
|
||||
#
|
||||
# This covers scenarios:
|
||||
# (A) (b)/(c),
|
||||
# (B) (c)/(d)/(e)
|
||||
#
|
||||
# XXX: we recorded WWID, Device-Bus-ID, Remote WWPN, LUN of the ReIPL target at
|
||||
# the time; if the latter three don't match the current ReIPL setting
|
||||
# anymore, we have to assume that someone changed the ReIPL target
|
||||
# manually, and we cannot use the WWID anymore since we can't possibly
|
||||
# know whether that stayed the same when the change was done.
|
||||
PROGRAM!="chreipl-fcp-mpath-is-ipl-vol", GOTO="chreipl_fcp_mpath_end"
|
||||
|
||||
# We are here because of scenarios:
|
||||
# (A) (a)/(b),
|
||||
# (B) (a)/(b)/(c)/(d)
|
||||
LABEL="chreipl_fcp_mpath_try_change_ipl_path"
|
||||
RUN{program}+="chreipl-fcp-mpath-try-change-ipl-path"
|
||||
|
||||
LABEL="chreipl_fcp_mpath_end"
|
||||
@@ -16,17 +16,18 @@ check_dep:
|
||||
$(call check_dep, \
|
||||
"cmsfs-fuse", \
|
||||
"fuse.h", \
|
||||
"fuse-devel or libfuse-dev", \
|
||||
"HAVE_FUSE=0")
|
||||
"fuse3-devel or libfuse3-dev", \
|
||||
"HAVE_FUSE=0", \
|
||||
"-DFUSE_USE_VERSION=30")
|
||||
|
||||
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)
|
||||
FUSE_CFLAGS = $(shell pkg-config --silence-errors --cflags fuse3)
|
||||
FUSE_LDLIBS = $(shell pkg-config --silence-errors --libs fuse3)
|
||||
else
|
||||
FUSE_CFLAGS = -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse
|
||||
FUSE_LDLIBS = -lfuse
|
||||
FUSE_CFLAGS = -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse3
|
||||
FUSE_LDLIBS = -lfuse3
|
||||
endif
|
||||
ALL_CFLAGS += -DHAVE_SETXATTR $(FUSE_CFLAGS)
|
||||
LDLIBS += $(FUSE_LDLIBS) -lm
|
||||
|
||||
@@ -80,9 +80,6 @@ Allow access by other users
|
||||
\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
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#define FUSE_USE_VERSION 26
|
||||
#define FUSE_USE_VERSION 30
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
@@ -1516,7 +1516,7 @@ static void walk_dir_block(struct fst_entry *fst, struct walk_file *walk,
|
||||
decode_edf_name(file, fst->name, fst->type);
|
||||
if (!file_unlinked(file)) {
|
||||
cache_fst_addr(walk->addr, file);
|
||||
walk->filler(walk->buf, file, NULL, 0);
|
||||
walk->filler(walk->buf, file, NULL, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1728,8 +1728,11 @@ static off_t get_file_size_logical(struct fst_entry *fst)
|
||||
return total;
|
||||
}
|
||||
|
||||
static int cmsfs_getattr(const char *path, struct stat *stbuf)
|
||||
static int cmsfs_getattr(const char *path, struct stat *stbuf,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
(void) fi;
|
||||
|
||||
int mask = (cmsfs.allow_other) ? 0444 : 0440;
|
||||
struct fst_entry fst;
|
||||
|
||||
@@ -1783,13 +1786,15 @@ static int cmsfs_getattr(const char *path, struct stat *stbuf)
|
||||
}
|
||||
|
||||
static int cmsfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
||||
off_t offset, struct fuse_file_info *fi)
|
||||
off_t offset, struct fuse_file_info *fi,
|
||||
enum fuse_readdir_flags flags)
|
||||
{
|
||||
struct walk_file walk;
|
||||
struct fst_entry fst;
|
||||
|
||||
(void) offset;
|
||||
(void) fi;
|
||||
(void) flags;
|
||||
|
||||
/*
|
||||
* Offset is ignored and 0 passed to the filler fn so the whole
|
||||
@@ -1800,8 +1805,8 @@ static int cmsfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
||||
if (strcmp(path, "/") != 0)
|
||||
return -ENOENT;
|
||||
|
||||
filler(buf, ".", NULL, 0);
|
||||
filler(buf, "..", NULL, 0);
|
||||
filler(buf, ".", NULL, 0, 0);
|
||||
filler(buf, "..", NULL, 0, 0);
|
||||
|
||||
memset(&walk, 0, sizeof(walk));
|
||||
/* readdir is possible without open so fi->fh is not set */
|
||||
@@ -2683,13 +2688,16 @@ static int cmsfs_statfs(const char *path, struct statvfs *buf)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmsfs_utimens(const char *path, const struct timespec ts[2])
|
||||
static int cmsfs_utimens(const char *path, const struct timespec ts[2],
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
struct fst_entry fst;
|
||||
off_t fst_addr;
|
||||
struct tm tm;
|
||||
int rc;
|
||||
|
||||
(void) fi;
|
||||
|
||||
if (cmsfs.readonly)
|
||||
return -EACCES;
|
||||
|
||||
@@ -2825,7 +2833,8 @@ error:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int cmsfs_rename(const char *path, const char *new_path)
|
||||
static int cmsfs_rename(const char *path, const char *new_path,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct fst_entry fst, fst_new;
|
||||
off_t fst_addr, fst_addr_new;
|
||||
@@ -2835,6 +2844,8 @@ static int cmsfs_rename(const char *path, const char *new_path)
|
||||
struct file *f;
|
||||
int rc;
|
||||
|
||||
(void) flags;
|
||||
|
||||
if (cmsfs.readonly)
|
||||
return -EACCES;
|
||||
|
||||
@@ -3232,13 +3243,16 @@ static void update_fst(struct file *f, off_t addr)
|
||||
unhide_null_blocks(f);
|
||||
}
|
||||
|
||||
static int cmsfs_truncate(const char *path, off_t size)
|
||||
static int cmsfs_truncate(const char *path, off_t size,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
struct fst_entry fst;
|
||||
off_t fst_addr, len;
|
||||
struct file *f;
|
||||
int rc = 0;
|
||||
|
||||
(void) fi;
|
||||
|
||||
if (cmsfs.readonly)
|
||||
return -EROFS;
|
||||
|
||||
|
||||
25
common.mak
25
common.mak
@@ -5,7 +5,7 @@ COMMON_INCLUDED = true
|
||||
# The variable "DISTRELEASE" should be overwritten in rpm spec files with:
|
||||
# "make DISTRELEASE=%{release}" and "make install DISTRELEASE=%{release}"
|
||||
VERSION = 2
|
||||
RELEASE = 17
|
||||
RELEASE = 21
|
||||
PATCHLEVEL = 0
|
||||
DISTRELEASE = build-$(shell date +%Y%m%d)
|
||||
S390_TOOLS_RELEASE = $(VERSION).$(RELEASE).$(PATCHLEVEL)-$(DISTRELEASE)
|
||||
@@ -59,11 +59,8 @@ define cmd_define
|
||||
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))
|
||||
@@ -84,6 +81,8 @@ CHECKTOOL = $(call echocmd," CHECK ",/$@)$(CHECK_SILENT)
|
||||
SKIP = echo " SKIP $(call reldir) due to"
|
||||
|
||||
INSTALL = install
|
||||
LINK = $(CC)
|
||||
LINKXX = $(CXX)
|
||||
CP = cp
|
||||
ifneq ("${V}","1")
|
||||
MAKEFLAGS += --quiet
|
||||
@@ -92,13 +91,12 @@ ifneq ("${V}","1")
|
||||
else
|
||||
echocmd=
|
||||
endif
|
||||
DEFAULT_CFLAGS = -g -rdynamic -fstack-protector-all -W -Wall -Wformat-security
|
||||
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
|
||||
DEFAULT_CFLAGS += -Wextra -Wshadow -Wundef -Wuninitialized -Wdouble-promotion -Wconversion
|
||||
endif
|
||||
ifeq ("${D}","1")
|
||||
DEFAULT_CFLAGS += -Og
|
||||
DEFAULT_CFLAGS += -Og -g3 -ggdb3
|
||||
else
|
||||
DEFAULT_CFLAGS += -O3
|
||||
endif
|
||||
@@ -182,6 +180,7 @@ USRSBINDIR = $(INSTALLDIR)/usr/sbin
|
||||
USRBINDIR = $(INSTALLDIR)/usr/bin
|
||||
BINDIR = $(INSTALLDIR)/sbin
|
||||
LIBDIR = $(INSTALLDIR)/lib
|
||||
USRLIBDIR = $(INSTALLDIR)/usr/lib
|
||||
USRLIB64DIR = $(INSTALLDIR)/usr/lib64
|
||||
SYSCONFDIR = $(INSTALLDIR)/etc
|
||||
MANDIR = $(INSTALLDIR)/usr/share/man
|
||||
@@ -206,10 +205,10 @@ INSTDIRS = $(USRSBINDIR) $(USRBINDIR) $(BINDIR) $(LIBDIR) $(MANDIR) \
|
||||
$(TOOLS_LIBDIR) $(TOOLS_DATADIR) \
|
||||
$(ZFCPDUMP_DIR) $(SYSTEMDSYSTEMUNITDIR) \
|
||||
$(USRLIB64DIR) $(USRINCLUDEDIR) $(ZKEYKMSPLUGINDIR) \
|
||||
$(SOINSTALLDIR)
|
||||
$(SOINSTALLDIR) $(USRLIBDIR)
|
||||
OWNER = $(shell id -un)
|
||||
GROUP = $(shell id -gn)
|
||||
export INSTALLDIR BINDIR LIBDIR USRLIB64DIR MANDIR OWNER GROUP
|
||||
export INSTALLDIR BINDIR LIBDIR USRLIBDIR USRLIB64DIR MANDIR OWNER GROUP
|
||||
|
||||
# Special defines for zfcpdump
|
||||
ZFCPDUMP_IMAGE = zfcpdump-image
|
||||
@@ -251,11 +250,9 @@ export AS LD CC CPP AR NM STRIP OBJCOPY OBJDUMP INSTALL CFLAGS CXXFLAGS \
|
||||
|
||||
ifneq ($(shell $(CC_SILENT) -dumpspecs 2>/dev/null | grep -e '[^f]no-pie'),)
|
||||
NO_PIE_CFLAGS := -fno-pie
|
||||
NO_PIE_LINKFLAGS := -no-pie
|
||||
NO_PIE_LDFLAGS := -no-pie
|
||||
else
|
||||
NO_PIE_CFLAGS :=
|
||||
NO_PIE_LINKFLAGS :=
|
||||
NO_PIE_LDFLAGS :=
|
||||
endif
|
||||
|
||||
@@ -370,6 +367,10 @@ $(rootdir)/libvmcp/libvmcp.a: $(rootdir)/libvmcp
|
||||
$(MAKE) -C $(rootdir)/libvmcp/ libvmcp.a
|
||||
.PHONY: $(rootdir)/libvmcp
|
||||
|
||||
$(rootdir)/libcpumf/libcpumf.a: $(rootdir)/libcpumf
|
||||
$(MAKE) -C $(rootdir)/libcpumf/ libcpumf.a
|
||||
.PHONY: $(rootdir)/libcpumf
|
||||
|
||||
$(rootdir)/libekmfweb/libekmfweb.so: $(rootdir)/libekmfweb
|
||||
$(MAKE) -C $(rootdir)/libekmfweb/ libekmfweb.so
|
||||
.PHONY: $(rootdir)/libekmfweb
|
||||
|
||||
@@ -5,7 +5,7 @@ MAN_FILES = lscpumf.1 chcpumf.8 lshwc.1
|
||||
|
||||
all: $(BIN_FILES)
|
||||
|
||||
libs = $(rootdir)/libutil/libutil.a
|
||||
libs = $(rootdir)/libutil/libutil.a $(rootdir)/libcpumf/libcpumf.a
|
||||
|
||||
lscpumf: lscpumf.o $(libs)
|
||||
chcpumf: chcpumf.o $(libs)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* chcpumf - Change CPU Measurement Facility Characteristics
|
||||
*
|
||||
* Copyright IBM Corp. 2020
|
||||
* Copyright IBM Corp. 2020, 2022
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -20,9 +20,9 @@
|
||||
#include "lib/util_prg.h"
|
||||
#include "lib/util_base.h"
|
||||
|
||||
#include "defines.h"
|
||||
#include "lib/libcpumf.h"
|
||||
|
||||
static int verbose;
|
||||
static unsigned int verbose;
|
||||
static unsigned long min_sdb, max_sdb;
|
||||
|
||||
static struct util_opt opt_vec[] = {
|
||||
@@ -85,55 +85,30 @@ static long parse_buffersize(char *string)
|
||||
return bytes;
|
||||
}
|
||||
|
||||
static void read_sfb(unsigned long *min, unsigned long *max)
|
||||
{
|
||||
unsigned long cur_min_sdb, cur_max_sdb;
|
||||
FILE *fp;
|
||||
|
||||
if (geteuid())
|
||||
errx(EXIT_FAILURE, "Must run as root");
|
||||
fp = fopen(PERF_SFB_SIZE, "r");
|
||||
if (!fp)
|
||||
err(EXIT_FAILURE, PERF_SFB_SIZE);
|
||||
if (fscanf(fp, "%ld,%ld", &cur_min_sdb, &cur_max_sdb) != 2) {
|
||||
fclose(fp);
|
||||
errx(EXIT_FAILURE, "Can not parse file " PERF_SFB_SIZE);
|
||||
} else {
|
||||
if (*min == 0)
|
||||
*min = cur_min_sdb;
|
||||
if (*max == 0)
|
||||
*max = cur_max_sdb;
|
||||
}
|
||||
fclose(fp);
|
||||
if (*min >= *max)
|
||||
errx(EXIT_FAILURE,
|
||||
"The specified maximum must be greater than the minimum");
|
||||
}
|
||||
|
||||
static int write_sfb(unsigned long min, unsigned long max)
|
||||
static int write_sfb(unsigned int min, unsigned int max)
|
||||
{
|
||||
int rc = EXIT_SUCCESS;
|
||||
char text[64];
|
||||
size_t len;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(PERF_SFB_SIZE, "w");
|
||||
fp = fopen(S390_CPUMSF_BUFFERSZ, "w");
|
||||
if (!fp)
|
||||
err(EXIT_FAILURE, PERF_SFB_SIZE);
|
||||
snprintf(text, sizeof text, "%ld,%ld", min, max);
|
||||
err(EXIT_FAILURE, S390_CPUMSF_BUFFERSZ);
|
||||
snprintf(text, sizeof(text), "%u,%u", min, max);
|
||||
len = strlen(text) + 1;
|
||||
if (fwrite(text, 1, len, fp) != len) {
|
||||
warn(PERF_SFB_SIZE);
|
||||
warn(S390_CPUMSF_BUFFERSZ);
|
||||
rc = EXIT_FAILURE;
|
||||
}
|
||||
if (fclose(fp)) {
|
||||
warn(PERF_SFB_SIZE);
|
||||
warn(S390_CPUMSF_BUFFERSZ);
|
||||
rc = EXIT_FAILURE;
|
||||
}
|
||||
if (verbose && rc != EXIT_FAILURE)
|
||||
warnx("Sampling buffer sizes:\n"
|
||||
" Minimum:%7ld sample-data-blocks\n"
|
||||
" Maximum:%7ld sample-data-blocks\n",
|
||||
" Minimum:%7d sample-data-blocks\n"
|
||||
" Maximum:%7d sample-data-blocks\n",
|
||||
min, max);
|
||||
return rc;
|
||||
}
|
||||
@@ -183,15 +158,21 @@ static int parse_args(int argc, char **argv)
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct stat sbuf;
|
||||
unsigned long my_min, my_max;
|
||||
|
||||
util_prg_init(&prg);
|
||||
util_opt_init(opt_vec, NULL);
|
||||
|
||||
parse_args(argc, argv);
|
||||
if (stat(PERF_PATH PERF_SF, &sbuf))
|
||||
if (geteuid())
|
||||
errx(EXIT_FAILURE, "Must run as root");
|
||||
if (!libcpumf_have_sfb())
|
||||
errx(EXIT_FAILURE,
|
||||
"No CPU-measurement sampling facility detected");
|
||||
read_sfb(&min_sdb, &max_sdb);
|
||||
libcpumf_sfb_info(&my_min, &my_max);
|
||||
if (!min_sdb)
|
||||
min_sdb = my_min;
|
||||
if (!max_sdb)
|
||||
max_sdb = my_max;
|
||||
return write_sfb(min_sdb, max_sdb);
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
/*
|
||||
* Defines for CPU Measurement Facility Characteristics
|
||||
*
|
||||
* Copyright IBM Corp. 2020
|
||||
*
|
||||
* 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 DEFINES_H
|
||||
#define DEFINES_H
|
||||
|
||||
#define PERF_SFB_SIZE "/sys/module/kernel/parameters/cpum_sfb_size"
|
||||
#define PERF_PATH "/sys/bus/event_source/devices/"
|
||||
#define PERF_SF "cpum_sf"
|
||||
#define PERF_CF "cpum_cf"
|
||||
|
||||
#endif
|
||||
195
cpumf/lscpumf.c
195
cpumf/lscpumf.c
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* lscpumf - Show CPU Measurement Facility Characteristics
|
||||
*
|
||||
* Copyright IBM Corp. 2020
|
||||
* Copyright IBM Corp. 2020, 2022
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -28,7 +28,7 @@
|
||||
#include "lib/util_opt.h"
|
||||
#include "lib/util_prg.h"
|
||||
|
||||
#include "defines.h"
|
||||
#include "lib/libcpumf.h"
|
||||
|
||||
#define ACTION_NONE 0
|
||||
#define ACTION_INFO 1
|
||||
@@ -45,9 +45,6 @@ static bool actions[ACTION_SAMPLE + 1]; /* Specified command line options */
|
||||
#define PER_SDBT_SIZE 511
|
||||
|
||||
/* File names to read data from */
|
||||
#define SERVICELEVEL "/proc/service_levels"
|
||||
#define CPUMF_CF_TYPE "/sys/devices/cpum_cf/type"
|
||||
#define CPUMF_SF_TYPE "/sys/devices/cpum_sf/type"
|
||||
|
||||
static struct util_opt opt_vec[] = {
|
||||
UTIL_OPT_SECTION("OPTIONS"),
|
||||
@@ -92,19 +89,18 @@ static char prefix[32]; /* Counter prefix */
|
||||
static bool show_names;
|
||||
|
||||
static struct cpumf_info {
|
||||
unsigned int first_vn; /* Counter facility first version nr */
|
||||
unsigned int second_vn; /* Counter facility second version nr */
|
||||
unsigned int authorization; /* Counter facility authorization */
|
||||
float version;
|
||||
int first_vn; /* Counter facility first version nr */
|
||||
int second_vn; /* Counter facility second version nr */
|
||||
int authorization; /* Counter facility authorization */
|
||||
unsigned long min_rate; /* Minimum sampling rate */
|
||||
unsigned long max_rate; /* Maximum sampling rate */
|
||||
unsigned long cpu_speed; /* CPU Cycles per micro second */
|
||||
unsigned int basic_sample_sz; /* # of Bytes per basic sample */
|
||||
unsigned int diag_sample_sz; /* # of bytes per diagnostic sample */
|
||||
unsigned char have_counter; /* CPUM counter facility detected */
|
||||
unsigned char have_samples; /* CPUM sampling facility detected */
|
||||
unsigned int min_sfb; /* Minimum sampling buffer size */
|
||||
unsigned int max_sfb; /* Maximum sampling buffer size */
|
||||
int basic_sample_sz; /* # of Bytes per basic sample */
|
||||
int diag_sample_sz; /* # of bytes per diagnostic sample */
|
||||
bool have_counter; /* CPUM counter facility detected */
|
||||
bool have_samples; /* CPUM sampling facility detected */
|
||||
unsigned long min_sfb; /* Minimum sampling buffer size */
|
||||
unsigned long max_sfb; /* Maximum sampling buffer size */
|
||||
unsigned short machine_type; /* Machine Type */
|
||||
} cpumf;
|
||||
|
||||
@@ -2565,26 +2561,12 @@ static struct counters cpumcf_z15_counters[] = {
|
||||
* If perf_pmu_register() kernel function assigned any other (higher) type
|
||||
* number, set the prefix to <type-nr>:
|
||||
*/
|
||||
static int read_cpumf_type(const char *filename, const char *type)
|
||||
static void set_prefix(int nr)
|
||||
{
|
||||
int nr, rc = EXIT_FAILURE;
|
||||
FILE *fp = fopen(filename, "r");
|
||||
|
||||
if (fp == NULL) {
|
||||
warnx("No CPU-measurement %s facility detected", type);
|
||||
return rc;
|
||||
}
|
||||
if (fscanf(fp, "%d", &nr) != 1) {
|
||||
warnx("Can not parse file %s", filename);
|
||||
} else {
|
||||
rc = EXIT_SUCCESS;
|
||||
if (nr == PERF_TYPE_RAW)
|
||||
strcat(prefix, "r");
|
||||
else
|
||||
snprintf(prefix, sizeof prefix, "%d:", nr);
|
||||
}
|
||||
fclose(fp);
|
||||
return rc;
|
||||
if (nr == PERF_TYPE_RAW)
|
||||
strcat(prefix, "r");
|
||||
else
|
||||
snprintf(prefix, sizeof(prefix), "%d:", nr);
|
||||
}
|
||||
|
||||
/* Parse tool parameters. In case of --help or --version, print
|
||||
@@ -2661,8 +2643,6 @@ static unsigned long div_ceil(unsigned long a, unsigned long b)
|
||||
|
||||
static void show_info(struct cpumf_info *p, int details)
|
||||
{
|
||||
struct stat sbuf;
|
||||
|
||||
if (!p->have_counter && !p->have_samples) {
|
||||
warnx("No CPU-measurement facilities detected");
|
||||
return;
|
||||
@@ -2672,7 +2652,7 @@ static void show_info(struct cpumf_info *p, int details)
|
||||
if (details) {
|
||||
printf("----------------------------------------------"
|
||||
"----------------------------\n");
|
||||
printf("Version: %3.1f\n\n", p->version);
|
||||
printf("Version: %d.%d\n\n", p->first_vn, p->second_vn);
|
||||
printf("Authorized counter sets:\n");
|
||||
if (!p->authorization)
|
||||
printf(" None\n");
|
||||
@@ -2689,8 +2669,7 @@ static void show_info(struct cpumf_info *p, int details)
|
||||
if (0x8000 & p->authorization)
|
||||
printf(" Coprocessor Group counter Set\n");
|
||||
printf("\nLinux perf event support: %s\n\n",
|
||||
(stat(PERF_PATH PERF_CF, &sbuf)) ? "No" :
|
||||
"Yes (PMU: " PERF_CF ")");
|
||||
!p->have_counter ? "No" : "Yes (PMU: cpum_cf)");
|
||||
|
||||
}
|
||||
} else
|
||||
@@ -2718,19 +2697,18 @@ static void show_info(struct cpumf_info *p, int details)
|
||||
p->diag_sample_sz);
|
||||
|
||||
printf("\nLinux perf event support: %s\n\n",
|
||||
(stat(PERF_PATH PERF_SF, &sbuf)) ? "No" :
|
||||
"Yes (PMU: " PERF_SF ")");
|
||||
!p->have_samples ? "No" : "Yes (PMU: cpum_sf)");
|
||||
|
||||
printf("Current sampling buffer settings for %s:\n",
|
||||
PERF_SF);
|
||||
printf("Current sampling buffer settings for"
|
||||
" cpum_sf:\n");
|
||||
printf(" Basic-sampling mode\n");
|
||||
total = p->min_sfb + div_ceil(p->min_sfb, PER_SDBT_SIZE);
|
||||
human(text, sizeof text, PAGE_SIZE * total);
|
||||
printf(" Minimum: %6d"
|
||||
human(text, sizeof(text), PAGE_SIZE * total);
|
||||
printf(" Minimum: %6ld"
|
||||
" sample-data-blocks (%6s)\n", p->min_sfb, text);
|
||||
total = p->max_sfb + div_ceil(p->max_sfb, PER_SDBT_SIZE);
|
||||
human(text, sizeof text, PAGE_SIZE * total);
|
||||
printf(" Maximum: %6d"
|
||||
human(text, sizeof(text), PAGE_SIZE * total);
|
||||
printf(" Maximum: %6ld"
|
||||
" sample-data-blocks (%6s)\n\n", p->max_sfb,
|
||||
text);
|
||||
|
||||
@@ -2739,13 +2717,13 @@ static void show_info(struct cpumf_info *p, int details)
|
||||
fdiag = div_ceil(p->diag_sample_sz, p->basic_sample_sz);
|
||||
total = fdiag * p->min_sfb
|
||||
+ div_ceil(p->min_sfb, PER_SDBT_SIZE);
|
||||
human(text, sizeof text, PAGE_SIZE * total);
|
||||
human(text, sizeof(text), PAGE_SIZE * total);
|
||||
printf(" Minimum: %6ld"
|
||||
" sample-data-blocks (%6s)\n",
|
||||
fdiag * p->min_sfb, text);
|
||||
total = fdiag * p->max_sfb
|
||||
+ div_ceil(p->max_sfb * fdiag, PER_SDBT_SIZE);
|
||||
human(text, sizeof text, PAGE_SIZE * total);
|
||||
human(text, sizeof(text), PAGE_SIZE * total);
|
||||
printf(" Maximum: %6ld"
|
||||
" sample-data-blocks (%6s)\n", fdiag * p->max_sfb,
|
||||
text);
|
||||
@@ -2755,27 +2733,6 @@ static void show_info(struct cpumf_info *p, int details)
|
||||
warnx("No CPU-measurement sampling facility detected");
|
||||
}
|
||||
|
||||
/* Read CPU Measurement sampling facility device driver minimum and maximum
|
||||
* buffer size
|
||||
*/
|
||||
static int read_sfb(struct cpumf_info *p)
|
||||
{
|
||||
int rc = EXIT_SUCCESS;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(PERF_SFB_SIZE, "r");
|
||||
if (!fp) {
|
||||
warn(PERF_SFB_SIZE);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (fscanf(fp, "%d,%d", &p->min_sfb, &p->max_sfb) != 2) {
|
||||
warnx("Can not parse %s", PERF_SFB_SIZE);
|
||||
rc = EXIT_FAILURE;
|
||||
}
|
||||
fclose(fp);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Set the counter name for z15 counter numbered 265. It is either named
|
||||
* DFLT_CCERROR or DFLT_CCFINISH, depending on the linux version. The
|
||||
* counter was renamed from CCERROR to CCFINISH in linux version 5.8.
|
||||
@@ -2803,81 +2760,23 @@ static void read_ccerror(struct counters *cp, size_t cp_cnt)
|
||||
/* Read allnecessary information from /sysfs file /proc/service_levels */
|
||||
static int read_info(void)
|
||||
{
|
||||
char *linep = NULL;
|
||||
size_t line_sz;
|
||||
ssize_t nbytes;
|
||||
FILE *slp;
|
||||
int rc;
|
||||
int rc = EXIT_FAILURE;
|
||||
|
||||
memset(&cpumf, 0, sizeof cpumf);
|
||||
slp = fopen(SERVICELEVEL, "r");
|
||||
if (!slp) {
|
||||
warn(SERVICELEVEL);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
while ((nbytes = getline(&linep, &line_sz, slp)) != EOF) {
|
||||
if (!strncmp(linep, "CPU-MF: Counter facility:", 25)) {
|
||||
rc = sscanf(linep, "CPU-MF: Counter facility:"
|
||||
" version=%f authorization=%x",
|
||||
&cpumf.version, &cpumf.authorization);
|
||||
if (rc != 2) {
|
||||
warnx("Can not parse line %s", linep);
|
||||
rc = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
cpumf.have_counter = 1;
|
||||
cpumf.first_vn = (int)cpumf.version;
|
||||
cpumf.second_vn = ((int)(10 * cpumf.version) % 10);
|
||||
}
|
||||
if (!strncmp(linep, "CPU-MF: Sampling facility: min", 30)) {
|
||||
rc = sscanf(linep, "CPU-MF: Sampling facility:"
|
||||
" min_rate=%ld max_rate=%ld cpu_speed=%ld",
|
||||
&cpumf.min_rate, &cpumf.max_rate,
|
||||
&cpumf.cpu_speed);
|
||||
if (rc != 3) {
|
||||
warnx("Can not parse line %s", linep);
|
||||
rc = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
cpumf.have_samples = 1;
|
||||
}
|
||||
if (!strncmp(linep, "CPU-MF: Sampling facility: mode=basic", 37)) {
|
||||
rc = sscanf(linep, "CPU-MF: Sampling facility:"
|
||||
" mode=basic sample_size=%u",
|
||||
&cpumf.basic_sample_sz);
|
||||
if (rc != 1) {
|
||||
warnx("Can not parse line %s", linep);
|
||||
rc = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (!strncmp(linep, "CPU-MF: Sampling facility: mode=diag", 36)) {
|
||||
rc = sscanf(linep, "CPU-MF: Sampling facility:"
|
||||
" mode=diagnostic sample_size=%u",
|
||||
&cpumf.diag_sample_sz);
|
||||
if (rc != 1) {
|
||||
warnx("Can not parse line %s", linep);
|
||||
rc = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cpumf.have_samples) {
|
||||
rc = read_sfb(&cpumf);
|
||||
if (rc == EXIT_FAILURE)
|
||||
goto out;
|
||||
}
|
||||
cpumf.have_counter = libcpumf_cpumcf_info(&cpumf.first_vn,
|
||||
&cpumf.second_vn,
|
||||
&cpumf.authorization);
|
||||
cpumf.have_samples = libcpumf_cpumsf_info(&cpumf.min_rate,
|
||||
&cpumf.max_rate,
|
||||
&cpumf.cpu_speed,
|
||||
&cpumf.basic_sample_sz,
|
||||
&cpumf.diag_sample_sz);
|
||||
if (cpumf.have_samples)
|
||||
libcpumf_sfb_info(&cpumf.min_sfb, &cpumf.max_sfb);
|
||||
cpumf.machine_type = util_arch_machine_type();
|
||||
if (cpumf.machine_type == UTIL_ARCH_MACHINE_TYPE_UNKNOWN) {
|
||||
if (cpumf.machine_type == UTIL_ARCH_MACHINE_TYPE_UNKNOWN)
|
||||
rc = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
rc = EXIT_SUCCESS;
|
||||
out:
|
||||
fclose(slp);
|
||||
free(linep);
|
||||
|
||||
else
|
||||
rc = EXIT_SUCCESS;
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -3051,14 +2950,18 @@ int main(int argc, char **argv)
|
||||
case ACTION_CNT:
|
||||
case ACTION_CNTALL:
|
||||
all = ret == ACTION_CNTALL;
|
||||
ret = read_cpumf_type(CPUMF_CF_TYPE, "counter");
|
||||
if (ret == EXIT_SUCCESS)
|
||||
ret = libcpumf_pmutype(S390_CPUMF_CF);
|
||||
if (ret >= EXIT_SUCCESS) {
|
||||
set_prefix(ret);
|
||||
show_counter(all);
|
||||
}
|
||||
break;
|
||||
case ACTION_SAMPLE:
|
||||
ret = read_cpumf_type(CPUMF_SF_TYPE, "sampling");
|
||||
if (ret == EXIT_SUCCESS)
|
||||
ret = libcpumf_pmutype(S390_CPUMF_SF);
|
||||
if (ret >= EXIT_SUCCESS) {
|
||||
set_prefix(ret);
|
||||
show_sample();
|
||||
}
|
||||
break;
|
||||
case ACTION_NONE:
|
||||
case ACTION_INFO:
|
||||
|
||||
141
cpumf/lshwc.c
141
cpumf/lshwc.c
@@ -17,6 +17,7 @@
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <linux/limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
@@ -37,23 +38,24 @@
|
||||
#include "lib/util_path.h"
|
||||
#include "lib/util_scandir.h"
|
||||
#include "lib/util_libc.h"
|
||||
#include "lib/libcpumf.h"
|
||||
|
||||
#include "lshwc.h"
|
||||
|
||||
#define SERVICELEVEL "/proc/service_levels"
|
||||
#define CPUS_ONLINE "/sys/devices/system/cpu/online"
|
||||
#define CPUS_POSSIBLE "/sys/devices/system/cpu/possible"
|
||||
#define CPUS_KERNELMAX "/sys/devices/system/cpu/kernel_max"
|
||||
#define MAXCTRS 512
|
||||
#define IOCTLSLEEP 60U
|
||||
|
||||
static const unsigned int ioctlsleep = 60;
|
||||
static unsigned int read_interval = ioctlsleep, cfvn, csvn, authorization;
|
||||
static unsigned int read_interval = IOCTLSLEEP;
|
||||
static int cfvn, csvn, authorization;
|
||||
static unsigned long loop_count = 1;
|
||||
static unsigned char *ioctlbuffer;
|
||||
static bool allcpu;
|
||||
|
||||
static unsigned int max_possible_cpus; /* No of possible CPUs */
|
||||
struct ctrname { /* List of defined counters */
|
||||
static struct ctrname { /* List of defined counters */
|
||||
char *name; /* Counter name */
|
||||
bool hitcnt; /* Counter number read from ioctl() */
|
||||
unsigned long total; /* Total counter value */
|
||||
@@ -150,20 +152,6 @@ static unsigned long getnumber(char *word, char stopchar)
|
||||
return no;
|
||||
}
|
||||
|
||||
/* Remove all whitespace from string. */
|
||||
static void kill_whitespace(char *s)
|
||||
{
|
||||
char *cp = s;
|
||||
|
||||
for (; *s != '\0'; ++s) {
|
||||
if (isspace(*s))
|
||||
continue;
|
||||
if (isprint(*s))
|
||||
*cp++ = *s;
|
||||
}
|
||||
*cp = '\0';
|
||||
}
|
||||
|
||||
/* Read file to get all online CPUs */
|
||||
static bool get_cpus(char *file, char *buf, size_t bufsz)
|
||||
{
|
||||
@@ -240,30 +228,35 @@ static char *show_ctrset(unsigned long set)
|
||||
/* Parse CPU list and counter sets */
|
||||
static void parse_cpulist(char *parm, struct s390_hwctr_start *start)
|
||||
{
|
||||
__u64 *words = start->cpumask;
|
||||
unsigned long i, no_a, no_b;
|
||||
char *cp, *tokens[16]; /* Used to parse command line params */
|
||||
char cpubuf[256];
|
||||
uint64_t *words = start->cpumask;
|
||||
unsigned int i, no_a, no_b;
|
||||
cpu_set_t cpulist;
|
||||
int rc;
|
||||
|
||||
CPU_ZERO(&cpulist);
|
||||
start->data_bytes = 0;
|
||||
if (parm)
|
||||
kill_whitespace(parm);
|
||||
if (!parm || *parm == ':') {
|
||||
/* No CPU list or just counter sets */
|
||||
if (!get_cpus(CPUS_ONLINE, cpubuf, sizeof(cpubuf)))
|
||||
exit(EXIT_FAILURE);
|
||||
if (parm)
|
||||
strcat(cpubuf, parm);
|
||||
parm = cpubuf;
|
||||
start->counter_sets = S390_HWCTR_ALL; /* Default all counter sets */
|
||||
|
||||
if (parm) { /* CPU list with optional counter set */
|
||||
char *cp = strchr(parm, ':');
|
||||
|
||||
if (cp) { /* Handle counter set */
|
||||
*cp = '\0';
|
||||
start->counter_sets = parse_ctrset(++cp);
|
||||
}
|
||||
|
||||
if (strlen(parm) > 0) /* Handle CPU list */
|
||||
rc = libcpumf_cpuset(parm, &cpulist);
|
||||
else
|
||||
rc = libcpumf_cpuset_fn(S390_CPUS_ONLINE, &cpulist);
|
||||
if (rc)
|
||||
errx(EXIT_FAILURE, "Cannot use CPU list %s", parm);
|
||||
} else { /* No CPU list and no counter sets */
|
||||
rc = libcpumf_cpuset_fn(S390_CPUS_ONLINE, &cpulist);
|
||||
if (rc)
|
||||
err(EXIT_FAILURE, "Cannot read file " S390_CPUS_ONLINE);
|
||||
}
|
||||
|
||||
cp = strchr(parm, ':');
|
||||
if (cp) { /* Handle counter set */
|
||||
*cp = '\0';
|
||||
start->counter_sets = parse_ctrset(++cp);
|
||||
} else {
|
||||
start->counter_sets = S390_HWCTR_ALL;
|
||||
}
|
||||
/* Check with authorized counter sets */
|
||||
if ((start->counter_sets & authorization) != start->counter_sets) {
|
||||
unsigned int noton = ~(start->counter_sets & authorization);
|
||||
@@ -275,36 +268,21 @@ static void parse_cpulist(char *parm, struct s390_hwctr_start *start)
|
||||
show_ctrset(noton));
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tokens) && (tokens[i] = strtok(parm, ",")) != 0;
|
||||
++i, parm = 0) {
|
||||
cp = strchr(tokens[i], '-'); /* Range character? */
|
||||
if (cp) {
|
||||
no_a = getnumber(tokens[i], *cp);
|
||||
no_b = getnumber(++cp, '\0');
|
||||
} else {
|
||||
no_b = getnumber(tokens[i], '\0');
|
||||
no_a = no_b;
|
||||
}
|
||||
if (!check_set(no_a, no_b, start->counter_sets))
|
||||
errx(EXIT_FAILURE, "Invalid CPU list %s", tokens[i]);
|
||||
}
|
||||
for (rc = 0; rc < CPU_SETSIZE; ++rc)
|
||||
if (CPU_ISSET(rc, &cpulist))
|
||||
if (!check_set(rc, rc, start->counter_sets))
|
||||
errx(EXIT_FAILURE, "Invalid CPU %d", rc);
|
||||
|
||||
/* Convert the CPU list to a bitmask for kernel cpumask_t */
|
||||
for (i = 0, no_b = 0; i < max_possible_cpus; ++i) {
|
||||
if (check[i].cpu_req) {
|
||||
no_a = i % __BITS_PER_LONG;
|
||||
no_b = i / __BITS_PER_LONG;
|
||||
no_a = i % LONG_BIT;
|
||||
no_b = i / LONG_BIT;
|
||||
words[no_b] |= 1ULL << no_a;
|
||||
}
|
||||
}
|
||||
/* no_b is highest used index, swap array */
|
||||
start->cpumask_len = (no_b + 1) * 8;
|
||||
for (no_a = 0; no_a < no_b; ++no_a, --no_b) {
|
||||
__u64 tmp = words[no_a];
|
||||
|
||||
words[no_a] = words[no_b];
|
||||
words[no_b] = tmp;
|
||||
}
|
||||
/* no_b is highest used index */
|
||||
start->cpumask_len = (no_b + 1) * CHAR_BIT;
|
||||
start->version = S390_HWCTR_START_VERSION;
|
||||
}
|
||||
|
||||
@@ -326,7 +304,7 @@ static bool check_setpossible(void)
|
||||
}
|
||||
parm = cpubuf;
|
||||
for (i = 0; i < ARRAY_SIZE(tokens) && (tokens[i] = strtok(parm, ","));
|
||||
++i, parm = 0) {
|
||||
++i, parm = NULL) {
|
||||
cp = strchr(tokens[i], '-');
|
||||
if (cp) { /* Range */
|
||||
no_a = getnumber(tokens[i], *cp);
|
||||
@@ -525,7 +503,7 @@ static int test_read(struct s390_hwctr_read *read)
|
||||
}
|
||||
/* Iterate over all counters in each set */
|
||||
for (unsigned int k = 0; k < sp->no_cnts; ++k) {
|
||||
__u64 value;
|
||||
uint64_t value;
|
||||
void *addr = base + offset;
|
||||
size_t idx = ctrset_offset(sp->set) + k;
|
||||
|
||||
@@ -608,8 +586,8 @@ static int do_it(char *s)
|
||||
int rc;
|
||||
|
||||
memset(&start, 0, sizeof(start));
|
||||
rc = max_possible_cpus / sizeof(__u64);
|
||||
start.cpumask = alloca(max_possible_cpus / sizeof(__u64));
|
||||
rc = max_possible_cpus / sizeof(uint64_t);
|
||||
start.cpumask = alloca(max_possible_cpus / sizeof(uint64_t));
|
||||
memset(start.cpumask, 0, rc);
|
||||
parse_cpulist(s, &start);
|
||||
errno = 0;
|
||||
@@ -637,37 +615,6 @@ static int do_it(char *s)
|
||||
return rc ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* Read counter first and second version number */
|
||||
static bool get_cvn(void)
|
||||
{
|
||||
char *linep = NULL;
|
||||
bool good = false;
|
||||
size_t line_sz;
|
||||
ssize_t nbytes;
|
||||
FILE *slp;
|
||||
|
||||
slp = fopen(SERVICELEVEL, "r");
|
||||
if (!slp) {
|
||||
warn(SERVICELEVEL);
|
||||
return false;
|
||||
}
|
||||
while ((nbytes = getline(&linep, &line_sz, slp)) != EOF) {
|
||||
if (!strncmp(linep, "CPU-MF: Counter facility:", 25)) {
|
||||
int rc;
|
||||
|
||||
rc = sscanf(linep, "CPU-MF: Counter facility: version=%d.%d authorization=%x",
|
||||
&cfvn, &csvn, &authorization);
|
||||
good = rc == 3;
|
||||
if (!good)
|
||||
warnx("Cannot parse line %s", linep);
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(slp);
|
||||
free(linep);
|
||||
return good;
|
||||
}
|
||||
|
||||
static struct util_opt opt_vec[] = {
|
||||
UTIL_OPT_SECTION("OPTIONS"),
|
||||
{
|
||||
@@ -751,7 +698,7 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
have_support();
|
||||
if (!get_cvn())
|
||||
if (!libcpumf_cpumcf_info(&cfvn, &csvn, &authorization))
|
||||
return EXIT_FAILURE;
|
||||
if (!check_setpossible())
|
||||
return EXIT_FAILURE;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#ifndef LSHWC_H
|
||||
#define LSHWC_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
enum {
|
||||
@@ -58,27 +59,27 @@ enum {
|
||||
*/
|
||||
|
||||
struct s390_hwctr_start { /* Set CPUs to operate on */
|
||||
__u64 version; /* Version of interface */
|
||||
__u64 data_bytes; /* # of bytes required */
|
||||
__u64 cpumask_len; /* Length of CPU mask in bytes */
|
||||
__u64 *cpumask; /* Pointer to CPU mask */
|
||||
__u64 counter_sets; /* Bit mask of counter set to get */
|
||||
uint64_t version; /* Version of interface */
|
||||
uint64_t data_bytes; /* # of bytes required */
|
||||
uint64_t cpumask_len; /* Length of CPU mask in bytes */
|
||||
uint64_t *cpumask; /* Pointer to CPU mask */
|
||||
uint64_t counter_sets; /* Bit mask of counter set to get */
|
||||
};
|
||||
|
||||
struct s390_hwctr_setdata { /* Counter set data */
|
||||
__u32 set; /* Counter set number */
|
||||
__u32 no_cnts; /* # of counters stored in cv[] */
|
||||
__u64 cv[0]; /* Counter values (variable length) */
|
||||
uint32_t set; /* Counter set number */
|
||||
uint32_t no_cnts; /* # of counters stored in cv[] */
|
||||
uint64_t cv[0]; /* Counter values (variable length) */
|
||||
};
|
||||
|
||||
struct s390_hwctr_cpudata { /* Counter set data per CPU */
|
||||
__u32 cpu_nr; /* Counter set number */
|
||||
__u32 no_sets; /* # of counters sets in data[] */
|
||||
uint32_t cpu_nr; /* Counter set number */
|
||||
uint32_t no_sets; /* # of counters sets in data[] */
|
||||
struct s390_hwctr_setdata data[0];
|
||||
};
|
||||
|
||||
struct s390_hwctr_read { /* Structure to get all ctr sets */
|
||||
__u64 no_cpus; /* Total # of CPUs data taken from */
|
||||
uint64_t no_cpus; /* Total # of CPUs data taken from */
|
||||
struct s390_hwctr_cpudata data[0];
|
||||
};
|
||||
|
||||
|
||||
@@ -110,6 +110,7 @@ static struct util_opt dump2tar_opts[] = {
|
||||
{
|
||||
.option = { "file-timeout", required_argument, NULL, 'T' },
|
||||
.desc = "Stop reading file after SEC seconds",
|
||||
.argument = "SEC",
|
||||
},
|
||||
{
|
||||
.option = { "file-max-size", required_argument, NULL, 'M' },
|
||||
|
||||
@@ -408,8 +408,10 @@ static int read_line(void)
|
||||
{
|
||||
bzero(line_buffer, LINE_LENGTH);
|
||||
line_ptr = line_buffer;
|
||||
if (!fgets(line_buffer, LINE_LENGTH, stdin))
|
||||
if (!fgets(line_buffer, LINE_LENGTH, stdin)) {
|
||||
clearerr(stdin);
|
||||
return 0;
|
||||
}
|
||||
while (*line_ptr && !isgraph(*line_ptr))
|
||||
line_ptr++;
|
||||
|
||||
|
||||
@@ -74,9 +74,9 @@ stage3b_reloc.elf:
|
||||
|
||||
%.elf: %.o
|
||||
case $* in \
|
||||
stage3a) SFLAGS="$(NO_PIE_LINKFLAGS) -nostdlib -Wl,-T,stage3a.lds";; \
|
||||
stage3b) SFLAGS="$(NO_PIE_LINKFLAGS) -nostdlib -Wl,-T,stage3b.lds";; \
|
||||
stage3b_reloc) SFLAGS="$(NO_PIE_LINKFLAGS) -nostdlib -Wl,-estage3b_reloc_start,-Ttext,0";; \
|
||||
stage3a) SFLAGS="$(NO_PIE_LDFLAGS) -nostdlib -Wl,-T,stage3a.lds";; \
|
||||
stage3b) SFLAGS="$(NO_PIE_LDFLAGS) -nostdlib -Wl,-T,stage3b.lds";; \
|
||||
stage3b_reloc) SFLAGS="$(NO_PIE_LDFLAGS) -nostdlib -Wl,-estage3b_reloc_start,-Ttext,0";; \
|
||||
esac; \
|
||||
$(LINK) $$SFLAGS -m64 $(filter %.o, $^) -o $@
|
||||
@chmod a-x $@
|
||||
|
||||
@@ -28,13 +28,23 @@ static inline void __noreturn load_psw(struct psw_t psw)
|
||||
;
|
||||
}
|
||||
|
||||
static unsigned long get_kernel_cmdline_size(void)
|
||||
{
|
||||
unsigned long size = *(volatile unsigned long *)MAX_COMMAND_LINE_SIZE;
|
||||
|
||||
if (size != 0)
|
||||
return size;
|
||||
|
||||
return LEGACY_COMMAND_LINE_SIZE;
|
||||
}
|
||||
|
||||
void __noreturn start(void)
|
||||
{
|
||||
volatile struct stage3b_args *args = &loader_parms;
|
||||
volatile struct memblob *kernel = &args->kernel;
|
||||
volatile struct memblob *cmdline = &args->cmdline;
|
||||
volatile struct memblob *initrd = &args->initrd;
|
||||
volatile struct psw_t psw = args->psw;
|
||||
struct psw_t psw = args->psw;
|
||||
|
||||
/* set up ASCII and line-mode */
|
||||
sclp_setup(SCLP_LINE_ASCII_INIT);
|
||||
@@ -42,14 +52,14 @@ void __noreturn start(void)
|
||||
if (kernel->size < IMAGE_LOAD_ADDRESS)
|
||||
panic(EINTERNAL, "Invalid kernel\n");
|
||||
|
||||
if (cmdline->size > COMMAND_LINE_SIZE)
|
||||
panic(EINTERNAL, "Command line is too large\n");
|
||||
|
||||
/* move the kernel and cut the kernel header */
|
||||
memmove((void *)IMAGE_LOAD_ADDRESS,
|
||||
(void *)(kernel->src + IMAGE_LOAD_ADDRESS),
|
||||
kernel->size - IMAGE_LOAD_ADDRESS);
|
||||
|
||||
if (cmdline->size > get_kernel_cmdline_size())
|
||||
panic(EINTERNAL, "Command line is too large\n");
|
||||
|
||||
/* move the kernel cmdline */
|
||||
memmove((void *)COMMAND_LINE,
|
||||
(void *)cmdline->src,
|
||||
|
||||
@@ -87,13 +87,23 @@ CRLs. Optional.
|
||||
.TP
|
||||
\fB\-\-root\-ca\fR=\fI\,FILE\/\fR
|
||||
Specifies the root CA certificate for the verification. If omitted,
|
||||
the DigiCert root CA certificate installed on the system is used. Use
|
||||
the system wide root CAs installed on the system is used. Use
|
||||
this only if you trust the specified certificate. Optional.
|
||||
.TP
|
||||
\fB\-\-no-verify\fR
|
||||
Do not require the host-key documents to be valid. For testing
|
||||
purposes, do not use for a production image. Optional.
|
||||
.TP
|
||||
\fB\-\-comm\-key\fR=\fI\,FILE\/\fR
|
||||
Specifies the encryption key you want to use for the PV guest dump. Use a
|
||||
secure, random, plaintext AES-256 GCM key. Optional.
|
||||
.TP
|
||||
\fB\-\-enable\-dump\fR
|
||||
Enable PV guest dumps. Requires the \fB\-\-comm-key\fR option. Optional.
|
||||
.TP
|
||||
\fB\-\-disable\-dump\fR
|
||||
Disable PV guest dumps. This is the default. Optional.
|
||||
.TP
|
||||
\fB\-\-enable\-pckmo\fR
|
||||
Enable the support for the DEA, TDEA, AES, and ECC PCKMO key encryption
|
||||
functions. This is the default. Optional.
|
||||
@@ -105,21 +115,25 @@ functions. Optional.
|
||||
\fB\-v\fR, \fB\-\-version\fR
|
||||
Prints version information, then exits.
|
||||
|
||||
.SH EXAMPLE
|
||||
.SH EXAMPLES
|
||||
|
||||
These are examples to generate a protected virtualization image in
|
||||
\fI\,/boot/vmlinuz.pv\/\fR, using the kernel file \fI\,vmlinuz\/\fR, the
|
||||
initrd in \fI\,initramfs\/\fR, the kernel parameters contained in
|
||||
\fI\,parmfile\/\fR, the intermediate CA in \fI\,DigiCertCA.crt\/\fR, the
|
||||
IBM Z signing key in \fI\,ibm-z-host-key-signing.crt\/\fR, and the
|
||||
host-key document in \fI\,host_key.crt\/\fR. An AES-256 GCM key is stored in
|
||||
\fI\,comm-key\/\fR, which is used for the PV guest dump support in the second
|
||||
example.
|
||||
|
||||
Generate a protected virtualization image:
|
||||
|
||||
.PP
|
||||
Generate a protected virtualization image in
|
||||
\fI\,/boot/vmlinuz.pv\/\fR, using the kernel file \fI\,vmlinuz\/\fR,
|
||||
the initrd in \fI\,initramfs\/\fR, the kernel parameters contained in
|
||||
\fI\,parmfile\/\fR, the intermediate CA in \fI\,DigiCertCA.crt\/\fR,
|
||||
the IBM Z signing key in \fI\,ibm-z-host-key-signing.crt\/\fR, and the
|
||||
host-key document in \fI\,host_key.crt\/\fR:
|
||||
.PP
|
||||
.Vb 1
|
||||
.EX
|
||||
\& genprotimg \-i \fI\,vmlinuz\/\fR \-r \fI\,initramfs\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm-z-host-key-signing.crt\/\fR \-C \fI\,DigiCertCA.crt \-o \fI\,/boot/vmlinuz.pv\/\fR
|
||||
.EE
|
||||
.Ve
|
||||
.B genprotimg \-i \fI\,vmlinuz\/\fR \-r \fI\,initramfs\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm-z-host-key-signing.crt\/\fR \-C \fI\,DigiCertCA.crt\fR \-o \fI\,/boot/vmlinuz.pv\/\fR
|
||||
|
||||
Generate a protected virtualization image with PV guest dump support:
|
||||
.PP
|
||||
.B genprotimg \-i \fI\,vmlinuz\/\fR \-r \fI\,initramfs\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm-z-host-key-signing.crt\/\fR \-C \fI\,DigiCertCA.crt\fR \-o \fI\,/boot/vmlinuz.pv\/\fR \-\-enable\-dump \-\-comm\-key \fI\,comm-key\fR
|
||||
|
||||
.SH NOTES
|
||||
.IP "1." 4
|
||||
|
||||
@@ -23,6 +23,7 @@ BODY_FILE=$(mktemp)
|
||||
ISSUER_DN_FILE=$(mktemp)
|
||||
SUBJECT_DN_FILE=$(mktemp)
|
||||
DEF_ISSUER_DN_FILE=$(mktemp)
|
||||
CANONICAL_ISSUER_DN_FILE=$(mktemp)
|
||||
CRL_SERIAL_FILE=$(mktemp)
|
||||
|
||||
# Cleanup on exit
|
||||
@@ -30,7 +31,7 @@ cleanup()
|
||||
{
|
||||
rm -f $ISSUER_PUBKEY_FILE $SIGNATURE_FILE $BODY_FILE \
|
||||
$ISSUER_DN_FILE $SUBJECT_DN_FILE $DEF_ISSUER_DN_FILE \
|
||||
$CRL_SERIAL_FILE
|
||||
$CANONICAL_ISSUER_DN_FILE $CRL_SERIAL_FILE
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
@@ -47,13 +48,21 @@ set -e
|
||||
usage()
|
||||
{
|
||||
cat <<-EOF
|
||||
Usage: `basename $1` host-key-doc signing-key-cert [-c CA-cert] [-r CRL]
|
||||
Usage: `basename $1` [-d] [-c CA-cert] [-r CRL] host-key-doc signing-key-cert
|
||||
|
||||
Verify an IBM Secure Execution host key document against
|
||||
a signing key.
|
||||
|
||||
Options:
|
||||
-d disable default issuer check of host-key-doc
|
||||
-c CA-cert trusted CA certificate
|
||||
-r CRL list of revoked host-key-docs
|
||||
|
||||
Note that in order to have the full trust chain verified
|
||||
it is necessary to provide the issueing CA's certificate.
|
||||
it is necessary to provide the issuing CA's certificate.
|
||||
The default issuer check may be disabled if a non-default
|
||||
signing key certificate needs to be verified against the
|
||||
CA certificate.
|
||||
|
||||
EOF
|
||||
}
|
||||
@@ -121,20 +130,34 @@ default_issuer()
|
||||
commonName = International Business Machines Corporation
|
||||
countryName = US
|
||||
localityName = Poughkeepsie
|
||||
organizationalUnitName = IBM Z Host Key Signing Service
|
||||
organizationalUnitName = Key Signing Service
|
||||
organizationName = International Business Machines Corporation
|
||||
stateOrProvinceName = New York
|
||||
EOF
|
||||
}
|
||||
|
||||
verify_issuer_files()
|
||||
# As organizationalUnitName can have an arbitrary prefix but must
|
||||
# end with "Key Signing Service" let's normalize the OU name by
|
||||
# stripping off the prefix
|
||||
verify_default_issuer()
|
||||
{
|
||||
default_issuer > $DEF_ISSUER_DN_FILE
|
||||
|
||||
if ! diff $ISSUER_DN_FILE $DEF_ISSUER_DN_FILE
|
||||
sed "s/\(^[ ]*organizationalUnitName[ ]*=[ ]*\).*\(Key Signing Service$\)/\1\2/" \
|
||||
$ISSUER_DN_FILE > $CANONICAL_ISSUER_DN_FILE
|
||||
|
||||
if ! diff $CANONICAL_ISSUER_DN_FILE $DEF_ISSUER_DN_FILE
|
||||
then
|
||||
echo Incorrect default issuer >&2 && exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
verify_issuer_files()
|
||||
{
|
||||
if [ $1 -eq 1 ]
|
||||
then
|
||||
verify_default_issuer
|
||||
fi
|
||||
|
||||
if diff $ISSUER_DN_FILE $SUBJECT_DN_FILE
|
||||
then
|
||||
@@ -196,14 +219,16 @@ check_file()
|
||||
# check args
|
||||
CRL_FILE=
|
||||
CA_FILE=
|
||||
CHECK_DEFAULT_ISSUER=1
|
||||
|
||||
args=$(getopt -qu "r:c:h" $*)
|
||||
args=$(getopt -qu "dr:c:h" $*)
|
||||
if [ $? = 0 ]
|
||||
then
|
||||
set -- $args
|
||||
while [ $1 != "" ]
|
||||
do
|
||||
case $1 in
|
||||
-d) CHECK_DEFAULT_ISSUER=0; shift;;
|
||||
-r) CRL_FILE=$2; shift 2;;
|
||||
-c) CA_FILE=$2; shift 2;;
|
||||
-h) usage $0; exit 0;;
|
||||
@@ -244,7 +269,7 @@ exit 1
|
||||
# Verify the issuer
|
||||
canonical_dn x509 $HKD_FILE issuer $ISSUER_DN_FILE
|
||||
canonical_dn x509 $HKSK_FILE subject $SUBJECT_DN_FILE
|
||||
verify_issuer_files
|
||||
verify_issuer_files $CHECK_DEFAULT_ISSUER
|
||||
|
||||
# Verify dates
|
||||
verify_dates $(cert_time $HKD_FILE startdate) $(cert_time $HKD_FILE enddate)
|
||||
@@ -261,7 +286,7 @@ then
|
||||
echo -n "CRL "
|
||||
canonical_dn crl $CRL_FILE issuer $ISSUER_DN_FILE
|
||||
canonical_dn x509 $HKSK_FILE subject $SUBJECT_DN_FILE
|
||||
verify_issuer_files
|
||||
verify_issuer_files $CHECK_DEFAULT_ISSUER
|
||||
|
||||
verify_dates $(crl_time $CRL_FILE lastupdate) $(crl_time $CRL_FILE nextupdate) 'CRL'
|
||||
|
||||
|
||||
@@ -29,9 +29,6 @@
|
||||
*/
|
||||
#define PV_CERTS_SECURITY_LEVEL 2
|
||||
|
||||
/* SKID for DigiCert Assured ID Root CA */
|
||||
#define DIGICERT_ASSURED_ID_ROOT_CA_SKID "45EBA2AFF492CB82312D518BA7A7219DF36DC80F"
|
||||
|
||||
union ecdh_pub_key {
|
||||
struct {
|
||||
uint8_t x[80];
|
||||
|
||||
@@ -24,11 +24,15 @@
|
||||
#define PV_MAGIC_NUMBER 0x49424d5365634578ULL
|
||||
#define PV_VERSION_1 0x00000100U
|
||||
|
||||
/* Internal helper macro */
|
||||
#define __PV_BIT(nr) (1ULL << (63 - (nr)))
|
||||
|
||||
/* Plaintext control flags */
|
||||
#define PV_PCF_PCKM_ECC (1ULL << 5) /* PCKMO encrypt-ECC-key functions allowed */
|
||||
#define PV_PCF_PCKMO_AES (1ULL << 6) /* PCKMO encrypt-AES-key functions allowed */
|
||||
#define PV_PCF_PCKMO_DEA_TDEA (1ULL << 7) /* PCKMO encrypt-DEA/TDEA-key functions allowed */
|
||||
#define PV_PCF_NO_DECRYPTION (1ULL << 28) /* prevent Ultravisor decryption during unpack operation */
|
||||
#define PV_PCF_ALLOW_DUMPING __PV_BIT(34) /* dumping of the configuration is allowed */
|
||||
#define PV_PCF_NO_DECRYPTION __PV_BIT(35) /* prevent Ultravisor decryption during unpack operation */
|
||||
#define PV_PCF_PCKMO_DEA_TDEA __PV_BIT(56) /* PCKMO encrypt-DEA/TDEA-key functions allowed */
|
||||
#define PV_PCF_PCKMO_AES __PV_BIT(57) /* PCKMO encrypt-AES-key functions allowed */
|
||||
#define PV_PCF_PCKM_ECC __PV_BIT(58) /* PCKMO encrypt-ECC-key functions allowed */
|
||||
|
||||
/* maxima for the PV version 1 */
|
||||
#define PV_V1_IPIB_MAX_SIZE PAGE_SIZE
|
||||
|
||||
@@ -64,13 +64,17 @@ static gint pv_args_validate_options(PvArgs *args, GError **err)
|
||||
{
|
||||
PvComponentType KERNEL = PV_COMP_TYPE_KERNEL;
|
||||
|
||||
if (args->pcf && args->allow_pckmo != PV_NOT_SET) {
|
||||
g_set_error(err, PV_PARSE_ERROR, PV_PARSE_ERROR_SYNTAX,
|
||||
_("The '--x-pcf' and '--(enable|disable)-pckmo' options are mutually"
|
||||
" exclusive.\nUse 'genprotimg --help' for more information"));
|
||||
/* Check for mutually exclusive arguments */
|
||||
if (args->pcf && !(args->allow_pckmo == PV_NOT_SET &&
|
||||
args->allow_dump == PV_NOT_SET)) {
|
||||
g_set_error(
|
||||
err, PV_PARSE_ERROR, PV_PARSE_ERROR_SYNTAX,
|
||||
_("The '--x-pcf' option cannot be used with the '--(enable|disable)-pckmo' or"
|
||||
" '--(enable|disable)-dump' flags.\nUse 'genprotimg --help' for more information"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check for unused arguments */
|
||||
if (args->unused_values->len > 0) {
|
||||
g_autofree gchar *unused = NULL;
|
||||
|
||||
@@ -88,6 +92,14 @@ static gint pv_args_validate_options(PvArgs *args, GError **err)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check for mandatory arguments */
|
||||
if (args->allow_dump == PV_TRUE && !args->cust_comm_key_path) {
|
||||
g_set_error(err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT,
|
||||
_("Option '--allow-dump' requires the '--comm-key' option.\nUse 'genprotimg "
|
||||
"--help' for more information"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!args->output_path) {
|
||||
g_set_error(err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT,
|
||||
_("Option '--output' is required.\nUse 'genprotimg --help' for more information"));
|
||||
@@ -111,7 +123,7 @@ static gint pv_args_validate_options(PvArgs *args, GError **err)
|
||||
g_strv_length(args->untrusted_cert_paths) == 0)) {
|
||||
g_set_error(
|
||||
err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT,
|
||||
_("Either specify the IBM Z signing key and (DigiCert) intermediate CA certificate\n"
|
||||
_("Either specify the IBM Z signing key and intermediate CA certificate\n"
|
||||
"by using the '--cert' option, or use the '--no-verify' flag to disable the\n"
|
||||
"host-key document verification completely (at your own risk)."));
|
||||
return -1;
|
||||
@@ -155,14 +167,14 @@ static gboolean cb_set_string_option(const gchar *option, const gchar *value,
|
||||
{
|
||||
gchar **args_option = NULL;
|
||||
|
||||
if (g_str_equal(option, "--comm-key"))
|
||||
args_option = &args->cust_comm_key_path;
|
||||
if (g_str_equal(option, "--root-ca"))
|
||||
args_option = &args->root_ca_path;
|
||||
if (g_str_equal(option, "-o") || g_str_equal(option, "--output"))
|
||||
args_option = &args->output_path;
|
||||
if (g_str_equal(option, "--x-comp-key"))
|
||||
args_option = &args->xts_key_path;
|
||||
if (g_str_equal(option, "--x-comm-key"))
|
||||
args_option = &args->cust_comm_key_path;
|
||||
if (g_str_equal(option, "--x-header-key"))
|
||||
args_option = &args->cust_root_key_path;
|
||||
if (g_str_equal(option, "--x-pcf"))
|
||||
@@ -188,19 +200,6 @@ static gboolean cb_set_string_option(const gchar *option, const gchar *value,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean cb_enable_disable_flag(const gchar *option, const gchar *value G_GNUC_UNUSED,
|
||||
PvArgs *args, GError **err G_GNUC_UNUSED)
|
||||
{
|
||||
if (g_str_equal(option, "--enable-pckmo"))
|
||||
args->allow_pckmo = PV_TRUE;
|
||||
else if (g_str_equal(option, "--disable-pckmo"))
|
||||
args->allow_pckmo = PV_FALSE;
|
||||
else
|
||||
g_assert_not_reached();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean cb_set_log_level(const gchar *option G_GNUC_UNUSED,
|
||||
const gchar *value G_GNUC_UNUSED, PvArgs *args,
|
||||
GError **err G_GNUC_UNUSED)
|
||||
@@ -217,8 +216,51 @@ static gboolean cb_remaining_values(const gchar *option G_GNUC_UNUSED,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#define MUT_EXCL_BOOL_FLAG_CB_NAME(FLAG, VALUE) (cb_##FLAG##_##VALUE)
|
||||
#define DEFINE_MUT_EXCL_BOOL_FLAG_CB(FLAG, VALUE) \
|
||||
static gboolean MUT_EXCL_BOOL_FLAG_CB_NAME(FLAG, VALUE)( \
|
||||
const gchar *option G_GNUC_UNUSED, const gchar *value G_GNUC_UNUSED, \
|
||||
PvArgs *args, GError **err) \
|
||||
{ \
|
||||
if (!(args->allow_##FLAG == PV_NOT_SET || \
|
||||
args->allow_##FLAG == VALUE)) { \
|
||||
g_set_error(err, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, \
|
||||
"'--enable-" #FLAG "' and '--disable-" #FLAG \
|
||||
"' are mutually exclusive"); \
|
||||
return FALSE; \
|
||||
} \
|
||||
args->allow_##FLAG = VALUE; \
|
||||
return TRUE; \
|
||||
}
|
||||
|
||||
#define DEFINE_MUT_EXCL_BOOL_FLAG_CBS(FLAG) \
|
||||
DEFINE_MUT_EXCL_BOOL_FLAG_CB(FLAG, PV_TRUE) \
|
||||
DEFINE_MUT_EXCL_BOOL_FLAG_CB(FLAG, PV_FALSE)
|
||||
|
||||
#define MUT_EXCL_BOOL_FLAG(FLAG, ENABLE_DESC, DISABLE_DESC) \
|
||||
{ \
|
||||
.long_name = "enable-" #FLAG, \
|
||||
.short_name = 0, \
|
||||
.flags = G_OPTION_FLAG_NO_ARG, \
|
||||
.arg = G_OPTION_ARG_CALLBACK, \
|
||||
.arg_data = MUT_EXCL_BOOL_FLAG_CB_NAME(FLAG, PV_TRUE), \
|
||||
.description = ENABLE_DESC, \
|
||||
}, \
|
||||
{ \
|
||||
.long_name = "disable-" #FLAG, \
|
||||
.short_name = 0, \
|
||||
.flags = G_OPTION_FLAG_NO_ARG, \
|
||||
.arg = G_OPTION_ARG_CALLBACK, \
|
||||
.arg_data = MUT_EXCL_BOOL_FLAG_CB_NAME(FLAG, PV_FALSE), \
|
||||
.description = DISABLE_DESC, \
|
||||
}
|
||||
|
||||
#define INDENT " "
|
||||
|
||||
/* Define the callbacks for mutually exclusive command line flags */
|
||||
DEFINE_MUT_EXCL_BOOL_FLAG_CBS(dump)
|
||||
DEFINE_MUT_EXCL_BOOL_FLAG_CBS(pckmo)
|
||||
|
||||
gint pv_args_parse_options(PvArgs *args, gint *argc, gchar **argv[],
|
||||
GError **err)
|
||||
{
|
||||
@@ -282,21 +324,28 @@ gint pv_args_parse_options(PvArgs *args, gint *argc, gchar **argv[],
|
||||
.description = _("Use the kernel parameters stored in PARMFILE\n" INDENT
|
||||
"(optional)."),
|
||||
.arg_description = _("PARMFILE") },
|
||||
{.long_name = "enable-pckmo",
|
||||
.short_name = 0,
|
||||
.flags = G_OPTION_FLAG_NO_ARG,
|
||||
.arg = G_OPTION_ARG_CALLBACK,
|
||||
.arg_data = cb_enable_disable_flag,
|
||||
.description = _("Enable the support for the DEA, TDEA, AES, and\n" INDENT
|
||||
"ECC PCKMO key encryption functions (default)\n" INDENT
|
||||
"(optional).")},
|
||||
{.long_name = "disable-pckmo",
|
||||
.short_name = 0,
|
||||
.flags = G_OPTION_FLAG_NO_ARG,
|
||||
.arg = G_OPTION_ARG_CALLBACK,
|
||||
.arg_data = cb_enable_disable_flag,
|
||||
.description = _("Disable the support for the DEA, TDEA, AES, and\n" INDENT
|
||||
"ECC PCKMO key encryption functions (optional).")},
|
||||
MUT_EXCL_BOOL_FLAG(
|
||||
dump,
|
||||
_("Enable PV guest dumps (optional). This option\n" INDENT
|
||||
"requires the '--comm-key' option."),
|
||||
_("Disable PV guest dumps (default) (optional).")),
|
||||
MUT_EXCL_BOOL_FLAG(
|
||||
pckmo,
|
||||
_("Enable the support for the DEA, TDEA, AES, and\n" INDENT
|
||||
"ECC PCKMO key encryption functions (default)\n" INDENT
|
||||
"(optional)."),
|
||||
_("Disable the support for the DEA, TDEA, AES, and\n" INDENT
|
||||
"ECC PCKMO key encryption functions (optional).")),
|
||||
{ .long_name = "comm-key",
|
||||
.short_name = 0,
|
||||
.flags = G_OPTION_FLAG_FILENAME,
|
||||
.arg = G_OPTION_ARG_CALLBACK,
|
||||
.arg_data = cb_set_string_option,
|
||||
.description = _(
|
||||
"FILE contains the key with which you encrypt\n" INDENT
|
||||
"the PV guest dump (optional). Required by\n" INDENT
|
||||
"the '--enable-dump' option."),
|
||||
.arg_description = _("FILE") },
|
||||
{ .long_name = "crl",
|
||||
.short_name = 0,
|
||||
.flags = G_OPTION_FLAG_NONE,
|
||||
@@ -355,15 +404,6 @@ gint pv_args_parse_options(PvArgs *args, gint *argc, gchar **argv[],
|
||||
};
|
||||
|
||||
GOptionEntry x_entries[] = {
|
||||
{ .long_name = "x-comm-key",
|
||||
.short_name = 0,
|
||||
.flags = G_OPTION_FLAG_FILENAME,
|
||||
.arg = G_OPTION_ARG_CALLBACK,
|
||||
.arg_data = cb_set_string_option,
|
||||
.description = _(
|
||||
"Use FILE as the customer communication key.\n" INDENT
|
||||
"Optional; default: auto-generated."),
|
||||
.arg_description = _("FILE") },
|
||||
{ .long_name = "x-comp-key",
|
||||
.short_name = 0,
|
||||
.flags = G_OPTION_FLAG_FILENAME,
|
||||
@@ -446,6 +486,7 @@ PvArgs *pv_args_new(void)
|
||||
g_autoptr(PvArgs) args = g_new0(PvArgs, 1);
|
||||
|
||||
args->unused_values = g_ptr_array_new_with_free_func(g_free);
|
||||
args->allow_dump = PV_NOT_SET;
|
||||
args->allow_pckmo = PV_NOT_SET;
|
||||
return g_steal_pointer(&args);
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ typedef struct {
|
||||
gboolean offline;
|
||||
gchar *pcf;
|
||||
gchar *scf;
|
||||
PvTristate allow_dump;
|
||||
PvTristate allow_pckmo;
|
||||
gchar *psw_addr; /* PSW address which will be used for the start of
|
||||
* the actual component (e.g. Linux kernel)
|
||||
|
||||
@@ -248,19 +248,6 @@ gint pv_component_align(PvComponent *component, const gchar *tmp_path,
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
/* Convert uint64_t address to byte array */
|
||||
static void uint64_to_uint8_buf(uint8_t dst[8], uint64_t addr)
|
||||
{
|
||||
uint8_t *p = (uint8_t *)&addr;
|
||||
|
||||
g_assert(dst);
|
||||
|
||||
for (gint i = 0; i < 8; i++) {
|
||||
/* cppcheck-suppress objectIndex */
|
||||
dst[i] = p[i];
|
||||
}
|
||||
}
|
||||
|
||||
int64_t pv_component_update_ald(const PvComponent *comp, EVP_MD_CTX *ctx,
|
||||
GError **err)
|
||||
{
|
||||
@@ -273,11 +260,8 @@ int64_t pv_component_update_ald(const PvComponent *comp, EVP_MD_CTX *ctx,
|
||||
|
||||
do {
|
||||
uint64_t cur_be = GUINT64_TO_BE(cur);
|
||||
uint8_t addr_buf[8];
|
||||
|
||||
uint64_to_uint8_buf(addr_buf, cur_be);
|
||||
|
||||
if (EVP_DigestUpdate(ctx, addr_buf, sizeof(addr_buf)) != 1) {
|
||||
if (EVP_DigestUpdate(ctx, &cur_be, sizeof(cur_be)) != 1) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR,
|
||||
PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("EVP_DigestUpdate failed"));
|
||||
|
||||
@@ -229,7 +229,9 @@ static gint pv_img_set_psw_addr(PvImage *img, const gchar *psw_addr_s,
|
||||
}
|
||||
|
||||
static gint pv_img_set_control_flags(PvImage *img, const gchar *pcf_s,
|
||||
const gchar *scf_s, PvTristate allow_pckmo, GError **err)
|
||||
const gchar *scf_s,
|
||||
PvTristate allow_dump,
|
||||
PvTristate allow_pckmo, GError **err)
|
||||
{
|
||||
uint64_t flags;
|
||||
|
||||
@@ -247,6 +249,11 @@ static gint pv_img_set_control_flags(PvImage *img, const gchar *pcf_s,
|
||||
img->scf = flags;
|
||||
}
|
||||
|
||||
if (allow_dump == PV_TRUE)
|
||||
img->pcf |= PV_PCF_ALLOW_DUMPING;
|
||||
else if (allow_dump == PV_FALSE)
|
||||
img->pcf &= ~PV_PCF_ALLOW_DUMPING;
|
||||
|
||||
if (allow_pckmo == PV_TRUE)
|
||||
img->pcf |= PV_PCF_PCKM_ECC | PV_PCF_PCKMO_AES | PV_PCF_PCKMO_DEA_TDEA;
|
||||
else if (allow_pckmo == PV_FALSE)
|
||||
@@ -304,9 +311,10 @@ static gint pv_img_hostkey_verify(GSList *host_key_certs,
|
||||
}
|
||||
|
||||
/* Load all untrusted certificates (e.g. IBM Z signing key and
|
||||
* DigiCert intermediate CA) that are required to establish a chain of
|
||||
* trust starting from the host-key document up to the root CA (if not
|
||||
* otherwise specified that's the DigiCert Assured ID Root CA).
|
||||
* intermediate CA) that are required to establish a chain of trust
|
||||
* starting from the host-key document up to the root CA (if not
|
||||
* otherwise specified that can be one of the system wide installed
|
||||
* root CAs, e.g. DigiCert).
|
||||
*/
|
||||
untrusted_certs_with_path = load_certificates(untrusted_cert_paths, err);
|
||||
if (!untrusted_certs_with_path)
|
||||
@@ -341,9 +349,8 @@ static gint pv_img_hostkey_verify(GSList *host_key_certs,
|
||||
* For this we must check:
|
||||
*
|
||||
* 1. Can a chain of trust be established ending in a root CA
|
||||
* 2. Is the correct root CA ued? It has either to be the
|
||||
* 'DigiCert Assured ID Root CA' or the root CA specified via
|
||||
* command line.
|
||||
* 2. Is the correct root CA used? It has either to be a system CA
|
||||
* or the root CA specified via command line.
|
||||
*/
|
||||
for (gint i = 0; i < sk_X509_num(ibm_signing_certs); ++i) {
|
||||
X509 *ibm_signing_cert = sk_X509_value(ibm_signing_certs, i);
|
||||
@@ -364,17 +371,12 @@ static gint pv_img_hostkey_verify(GSList *host_key_certs,
|
||||
if (verify_cert(ibm_signing_cert, ctx, err) < 0)
|
||||
goto error;
|
||||
|
||||
/* Verify the build chain of trust chain. If the user passes a
|
||||
* trusted root CA on the command line then the check for the
|
||||
* Subject Key Identifier (SKID) is skipped, otherwise let's
|
||||
* check if the SKID meets our expectation.
|
||||
/* If there is a chain of trust using either the provided root
|
||||
* CA on the command line or a system wide trusted root CA.
|
||||
*/
|
||||
if (!root_ca_path &&
|
||||
check_chain_parameters(X509_STORE_CTX_get0_chain(ctx),
|
||||
get_digicert_assured_id_root_ca_skid(),
|
||||
err) < 0) {
|
||||
if (check_chain_parameters(X509_STORE_CTX_get0_chain(ctx),
|
||||
err) < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
ibm_signing_crls = store_ctx_find_valid_crls(ctx, ibm_signing_cert, err);
|
||||
if (!ibm_signing_crls) {
|
||||
@@ -588,7 +590,7 @@ PvImage *pv_img_new(PvArgs *args, const gchar *stage3a_path, GError **err)
|
||||
g_warning(_("host-key document verification is disabled. Your workload is not secured."));
|
||||
|
||||
if (args->root_ca_path)
|
||||
g_warning(_("A different root CA than the default DigiCert root CA is selected. Ensure that this root CA is trusted."));
|
||||
g_warning(_("The root CA is selected through the command line. Ensure that this root CA is trusted."));
|
||||
|
||||
ret->comps = pv_img_comps_new(EVP_sha512(), EVP_sha512(), EVP_sha512(), err);
|
||||
if (!ret->comps)
|
||||
@@ -608,7 +610,9 @@ PvImage *pv_img_new(PvArgs *args, const gchar *stage3a_path, GError **err)
|
||||
return NULL;
|
||||
|
||||
/* set the control flags: PCF and SCF */
|
||||
if (pv_img_set_control_flags(ret, args->pcf, args->scf, args->allow_pckmo, err) < 0)
|
||||
if (pv_img_set_control_flags(ret, args->pcf, args->scf,
|
||||
args->allow_dump, args->allow_pckmo,
|
||||
err) < 0)
|
||||
return NULL;
|
||||
|
||||
/* read in the keys */
|
||||
|
||||
@@ -440,7 +440,7 @@ static int check_signature_algo_match(const EVP_PKEY *pkey, const X509 *subject,
|
||||
|
||||
static X509_CRL *load_crl_from_bio(BIO *bio)
|
||||
{
|
||||
g_autoptr(X509_CRL) crl = PEM_read_bio_X509_CRL(bio, NULL, 0, NULL);
|
||||
g_autoptr(X509_CRL) crl = PEM_read_bio_X509_CRL(bio, NULL, NULL, NULL);
|
||||
gint rc;
|
||||
|
||||
if (crl)
|
||||
@@ -1079,8 +1079,8 @@ int store_set_verify_param(X509_STORE *store, GError **err)
|
||||
g_abort();
|
||||
|
||||
/* The maximum depth level of the chain of trust for the verification of
|
||||
* the IBM Z signing key is 2, i.e. IBM Z signing key -> (DigiCert)
|
||||
* intermediate CA -> (DigiCert) root CA
|
||||
* the IBM Z signing key is 2, i.e. IBM Z signing key -> intermediate CA
|
||||
* -> root CA
|
||||
*/
|
||||
X509_VERIFY_PARAM_set_depth(param, 2);
|
||||
|
||||
@@ -1267,46 +1267,38 @@ static int security_level_to_bits(int level)
|
||||
return security_bits[level];
|
||||
}
|
||||
|
||||
static ASN1_OCTET_STRING *digicert_assured_id_root_ca;
|
||||
|
||||
const ASN1_OCTET_STRING *get_digicert_assured_id_root_ca_skid(void)
|
||||
{
|
||||
pv_crypto_init();
|
||||
return digicert_assured_id_root_ca;
|
||||
}
|
||||
|
||||
/* Used for the caching of the downloaded CRLs */
|
||||
static GHashTable *cached_crls;
|
||||
|
||||
void pv_crypto_init(void)
|
||||
{
|
||||
if (digicert_assured_id_root_ca)
|
||||
if (cached_crls)
|
||||
return;
|
||||
|
||||
cached_crls = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
|
||||
(GDestroyNotify)X509_CRL_free);
|
||||
digicert_assured_id_root_ca = s2i_ASN1_OCTET_STRING(
|
||||
NULL, NULL, DIGICERT_ASSURED_ID_ROOT_CA_SKID);
|
||||
}
|
||||
|
||||
void pv_crypto_cleanup(void)
|
||||
{
|
||||
if (!digicert_assured_id_root_ca)
|
||||
if (!cached_crls)
|
||||
return;
|
||||
g_clear_pointer(&cached_crls, g_hash_table_destroy);
|
||||
g_clear_pointer(&digicert_assured_id_root_ca, ASN1_OCTET_STRING_free);
|
||||
}
|
||||
|
||||
gint check_chain_parameters(const STACK_OF_X509 *chain,
|
||||
const ASN1_OCTET_STRING *skid, GError **err)
|
||||
GError **err)
|
||||
{
|
||||
const ASN1_OCTET_STRING *ca_skid = NULL;
|
||||
const X509_NAME *ca_x509_subject = NULL;
|
||||
g_autofree gchar *ca_subject = NULL;
|
||||
gint len = sk_X509_num(chain);
|
||||
X509 *ca = NULL;
|
||||
|
||||
g_assert(skid);
|
||||
/* at least one root and one leaf certificate must be defined */
|
||||
g_assert(len >= 2);
|
||||
if (len < 2) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("there must be at least on root and one leaf certificate in the chain of trust"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* get the root certificate of the chain of trust */
|
||||
ca = sk_X509_value(chain, len - 1);
|
||||
@@ -1316,19 +1308,21 @@ gint check_chain_parameters(const STACK_OF_X509 *chain,
|
||||
return -1;
|
||||
}
|
||||
|
||||
ca_skid = X509_get0_subject_key_id(ca);
|
||||
if (!ca_skid) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_MALFORMED_ROOT_CA,
|
||||
_("malformed root certificate"));
|
||||
ca_x509_subject = X509_get_subject_name(ca);
|
||||
if (!ca_x509_subject) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("subject of the root CA cannot be retrieved"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ASN1_STRING_cmp(ca_skid, skid) != 0) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_WRONG_CA_USED,
|
||||
_("expecting DigiCert root CA to be used"));
|
||||
ca_subject = X509_NAME_oneline(ca_x509_subject, NULL, 0);
|
||||
if (!ca_subject) {
|
||||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
_("subject name of the root CA cannot be retrieved"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_info("Root CA used: '%s'", ca_subject);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -125,7 +125,6 @@ int check_crl_valid_for_cert(X509_CRL *crl, X509 *cert,
|
||||
gint verify_flags, GError **err);
|
||||
void pv_crypto_init(void);
|
||||
void pv_crypto_cleanup(void);
|
||||
const ASN1_OCTET_STRING *get_digicert_assured_id_root_ca_skid(void);
|
||||
gint verify_host_key(X509 *host_key, GSList *issuer_pairs,
|
||||
gint verify_flags, int level, GError **err);
|
||||
X509 *load_cert_from_file(const char *path, GError **err);
|
||||
@@ -138,8 +137,7 @@ X509_STORE *store_setup(const gchar *root_ca_path,
|
||||
int store_set_verify_param(X509_STORE *store, GError **err);
|
||||
X509_CRL *load_crl_by_cert(X509 *cert, GError **err);
|
||||
STACK_OF_X509_CRL *try_load_crls_by_certs(GSList *certs_with_path);
|
||||
gint check_chain_parameters(const STACK_OF_X509 *chain,
|
||||
const ASN1_OCTET_STRING *skid, GError **err);
|
||||
gint check_chain_parameters(const STACK_OF_X509 *chain, GError **err);
|
||||
X509_NAME *c2b_name(const X509_NAME *name);
|
||||
|
||||
STACK_OF_X509 *delete_ibm_signing_certs(STACK_OF_X509 *certs);
|
||||
|
||||
@@ -16,17 +16,17 @@ check_dep:
|
||||
$(call check_dep, \
|
||||
"hmcdrvfs", \
|
||||
"fuse.h", \
|
||||
"fuse-devel or libfuse-dev", \
|
||||
"fuse3-devel or libfuse3-dev", \
|
||||
"HAVE_FUSE=0")
|
||||
|
||||
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)
|
||||
FUSE_CFLAGS = $(shell pkg-config --silence-errors --cflags fuse3)
|
||||
FUSE_LDLIBS = $(shell pkg-config --silence-errors --libs fuse3)
|
||||
else
|
||||
FUSE_CFLAGS = -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse
|
||||
FUSE_LDLIBS = -lfuse
|
||||
FUSE_CFLAGS = -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse3
|
||||
FUSE_LDLIBS = -lfuse3
|
||||
endif
|
||||
ALL_CFLAGS += -DFUSE_USE_VERSION=26 -D_LARGEFILE_SOURCE $(FUSE_CFLAGS)
|
||||
ALL_CFLAGS += -DFUSE_USE_VERSION=30 -D_LARGEFILE_SOURCE $(FUSE_CFLAGS)
|
||||
LDLIBS += $(FUSE_LDLIBS) -lpthread -lrt -ldl -lm
|
||||
|
||||
OBJECTS = hmcdrvfs.o
|
||||
|
||||
@@ -111,9 +111,6 @@ allow access by other users
|
||||
.B -o allow_root
|
||||
allow access by root
|
||||
.TP
|
||||
.B -o nonempty
|
||||
allow mounts over non-empty file/dir
|
||||
.TP
|
||||
.B -o default_permissions
|
||||
enable permission checking by kernel
|
||||
.TP
|
||||
|
||||
@@ -990,7 +990,7 @@ static int hmcdrv_cache_dir(const char *dir, fuse_fill_dir_t filler, void *buf)
|
||||
hmcdrv_cache_refresh(path, &st, symlink);
|
||||
|
||||
if ((filler != NULL) &&
|
||||
(filler(buf, fname, &st, 0) != 0))
|
||||
(filler(buf, fname, &st, 0, 0) != 0))
|
||||
filler = NULL; /* stop filling */
|
||||
#ifdef DEBUG
|
||||
strftime(symlink, sizeof(symlink),
|
||||
@@ -1109,7 +1109,8 @@ static struct hmcdrv_fuse_file *hmcdrv_file_get(const char *path)
|
||||
*
|
||||
* Note: The most important function which FUSE calls (very often).
|
||||
*/
|
||||
static int hmcdrv_fuse_getattr(const char *path, struct stat *stbuf)
|
||||
static int hmcdrv_fuse_getattr(const char *path, struct stat *stbuf,
|
||||
struct fuse_file_info *UNUSED(fi))
|
||||
{
|
||||
struct hmcdrv_fuse_file *fp;
|
||||
int rc = 0;
|
||||
@@ -1175,12 +1176,13 @@ static int hmcdrv_fuse_opendir(const char *UNUSED(path),
|
||||
*/
|
||||
static int hmcdrv_fuse_readdir(const char *path, void *buf,
|
||||
fuse_fill_dir_t filler, off_t UNUSED(offset),
|
||||
struct fuse_file_info *UNUSED(fi))
|
||||
struct fuse_file_info *UNUSED(fi),
|
||||
enum fuse_readdir_flags UNUSED(flags))
|
||||
{
|
||||
int ret;
|
||||
|
||||
filler(buf, ".", NULL, 0);
|
||||
filler(buf, "..", NULL, 0);
|
||||
filler(buf, ".", NULL, 0, 0);
|
||||
filler(buf, "..", NULL, 0, 0);
|
||||
|
||||
pthread_mutex_lock(&hmcdrv_ctx.mutex);
|
||||
ret = hmcdrv_cache_dir(path, filler, buf);
|
||||
@@ -1232,7 +1234,8 @@ static int hmcdrv_fuse_read(const char *path, char *buf, size_t size,
|
||||
* Return: value to be passed in the private_data field of fuse_context to
|
||||
* all file operations and as a parameter to the destroy() method
|
||||
*/
|
||||
static void *hmcdrv_fuse_init(struct fuse_conn_info *UNUSED(conn))
|
||||
static void *hmcdrv_fuse_init(struct fuse_conn_info *UNUSED(conn),
|
||||
struct fuse_config *UNUSED(cfg))
|
||||
{
|
||||
pthread_mutexattr_t attr;
|
||||
|
||||
|
||||
@@ -23,11 +23,11 @@ else # HAVE_FUSE
|
||||
# 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)
|
||||
FUSE_CFLAGS = $(shell pkg-config --silence-errors --cflags fuse3)
|
||||
FUSE_LDLIBS = $(shell pkg-config --silence-errors --libs fuse3)
|
||||
else
|
||||
FUSE_CFLAGS = -I/usr/include/fuse
|
||||
FUSE_LDLIBS = -lfuse
|
||||
FUSE_CFLAGS = -I/usr/include/fuse3
|
||||
FUSE_LDLIBS = -lfuse3
|
||||
endif
|
||||
|
||||
#
|
||||
@@ -67,8 +67,10 @@ check-dep-fuse:
|
||||
$(call check_dep, \
|
||||
"hsavmcore", \
|
||||
"fuse.h", \
|
||||
"fuse-devel or libfuse-dev", \
|
||||
"HAVE_FUSE=0")
|
||||
"fuse3-devel or libfuse3-dev", \
|
||||
"HAVE_FUSE=0", \
|
||||
"-DFUSE_USE_VERSION=30")
|
||||
touch check-dep-fuse
|
||||
|
||||
install: all
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 hsavmcore \
|
||||
@@ -81,6 +83,6 @@ install: all
|
||||
endif # HAVE_FUSE
|
||||
|
||||
clean:
|
||||
rm -f hsavmcore $(objects)
|
||||
rm -f hsavmcore $(objects) check-dep-fuse
|
||||
|
||||
.PHONY: all install clean
|
||||
|
||||
@@ -15,7 +15,7 @@ sudo reboot
|
||||
## Dependencies
|
||||
|
||||
```shell
|
||||
sudo dnf install -y fuse fuse-devel systemd-devel
|
||||
sudo dnf install -y fuse3 fuse3-devel systemd-devel
|
||||
```
|
||||
|
||||
## Build hsavmcore
|
||||
|
||||
@@ -18,7 +18,7 @@ sudo reboot
|
||||
## Dependencies
|
||||
|
||||
```shell
|
||||
sudo zypper install -y fuse fuse-devel systemd-devel
|
||||
sudo zypper install -y fuse3 fuse3-devel systemd-devel
|
||||
```
|
||||
|
||||
## Build hsavmcore
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
## Dependencies
|
||||
|
||||
```shell
|
||||
sudo apt-get install -y make gcc kdump-tools fuse libfuse-dev libsystemd-dev
|
||||
sudo apt-get install -y make gcc kdump-tools fuse3 libfuse3-dev libsystemd-dev
|
||||
```
|
||||
|
||||
## Build hsavmcore
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define FUSE_USE_VERSION 26
|
||||
#define FUSE_USE_VERSION 30
|
||||
#include <fuse.h>
|
||||
|
||||
#include "lib/util_log.h"
|
||||
@@ -31,8 +31,11 @@ struct vmcore_overlay {
|
||||
bool fuse_debug;
|
||||
};
|
||||
|
||||
static int vmcore_fuse_getattr(const char *path, struct stat *stbuf)
|
||||
static int vmcore_fuse_getattr(const char *path, struct stat *stbuf,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
(void)fi;
|
||||
|
||||
struct vmcore_overlay *overlay = fuse_get_context()->private_data;
|
||||
int ret = 0;
|
||||
|
||||
@@ -54,18 +57,20 @@ static int vmcore_fuse_getattr(const char *path, struct stat *stbuf)
|
||||
|
||||
static int vmcore_fuse_readdir(const char *path, void *buf,
|
||||
fuse_fill_dir_t filler, off_t offset,
|
||||
struct fuse_file_info *fi)
|
||||
struct fuse_file_info *fi,
|
||||
enum fuse_readdir_flags flags)
|
||||
{
|
||||
(void)offset;
|
||||
(void)fi;
|
||||
(void)flags;
|
||||
|
||||
if (strcmp(path, ROOT_DIR) != 0)
|
||||
return -ENOENT;
|
||||
|
||||
/* We have only one file */
|
||||
filler(buf, ".", NULL, 0);
|
||||
filler(buf, "..", NULL, 0);
|
||||
filler(buf, VMCORE_FILE, NULL, 0);
|
||||
filler(buf, ".", NULL, 0, 0);
|
||||
filler(buf, "..", NULL, 0, 0);
|
||||
filler(buf, VMCORE_FILE, NULL, 0, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
430
hsci/hsci
430
hsci/hsci
@@ -11,11 +11,36 @@
|
||||
hsdev=""
|
||||
ndev=""
|
||||
hsci=""
|
||||
hsdev_mac=""
|
||||
hscibr=""
|
||||
hscibp=""
|
||||
hsci_mac=""
|
||||
hsif_pnetid=""
|
||||
netif_pnetid=""
|
||||
hsci_pnetid=""
|
||||
|
||||
##############################################################################
|
||||
# Concept:
|
||||
# --------
|
||||
# | hsci |
|
||||
# --------
|
||||
# / \
|
||||
# -------- --------
|
||||
# | ndev | | hsdev |
|
||||
# -------- --------
|
||||
#
|
||||
# Detail (bridge w/ bridgeports):
|
||||
# --------
|
||||
# | hsci |
|
||||
# --------
|
||||
# |
|
||||
# ----------------------
|
||||
# | |hsci-bp| |
|
||||
# | hsci-br --------- |
|
||||
# | |
|
||||
# -------- -------- |
|
||||
# | ndev | | hsdev | |
|
||||
# ----------------------
|
||||
|
||||
function usage {
|
||||
cat <<-EOD
|
||||
Usage: hsci COMMAND [OPTION]
|
||||
@@ -42,50 +67,53 @@ function prereqs_check {
|
||||
fi
|
||||
}
|
||||
|
||||
function get_pnetid {
|
||||
local netdev=$1
|
||||
local pnetid=""
|
||||
|
||||
#### ROCE:
|
||||
if [ -e /sys/class/net/$netdev/device/util_string ]; then
|
||||
pnetids="$(cat /sys/class/net/$netdev/device/util_string | tr -d '\000' | iconv -f IBM-1047 -t ASCII)"
|
||||
else
|
||||
#### OSA /HiperSockets:
|
||||
if [ -e /sys/class/net/$netdev/device/chpid ]; then
|
||||
chpid="$(cat /sys/class/net/$netdev/device/chpid | tr [:upper:] [:lower:])"
|
||||
pnetids="$(cat /sys/devices/css0/chp0.$chpid/util_string | tr -d '\000' | iconv -f IBM-1047 -t ASCII)"
|
||||
fi
|
||||
fi
|
||||
if [ "$pnetids" != "" ]; then
|
||||
port_if="$(cat /sys/class/net/$netdev/dev_port)"
|
||||
(( idx=16*$port_if+1 ))
|
||||
(( end=$idx+15 ))
|
||||
pnetid="$(echo "$pnetids" | cut -c $idx-$end | tr -d ' ')"
|
||||
fi
|
||||
|
||||
echo $pnetid
|
||||
}
|
||||
|
||||
function check_pnetids {
|
||||
# get PNETID of the HS
|
||||
local hsif_pnetids=""
|
||||
local netif_pnetids=""
|
||||
|
||||
if [ -e /sys/class/net/$hsdev/device/util_string ]; then
|
||||
hsif_pnetids="$(cat /sys/class/net/$hsdev/device/util_string | tr -d '\000' | iconv -f IBM-1047 -t ASCII)"
|
||||
if [ "$hsdev" != "" ]; then
|
||||
hsif_pnetid="$(get_pnetid $hsdev)"
|
||||
else
|
||||
if [ -e /sys/class/net/$hsdev/device/chpid ]; then
|
||||
chpid="$(cat /sys/class/net/$hsdev/device/chpid | tr [:upper:] [:lower:])"
|
||||
hsif_pnetids="$(cat /sys/devices/css0/chp0.$chpid/util_string | tr -d '\000' | iconv -f IBM-1047 -t ASCII)"
|
||||
fi
|
||||
hsif_pnetid=""
|
||||
fi
|
||||
if [ "$hsif_pnetids" != "" ]; then
|
||||
port_hsif="$(cat /sys/class/net/$hsdev/dev_port)"
|
||||
(( idx=16*$port_hsif+1 ))
|
||||
(( end=$idx+15 ))
|
||||
hsif_pnetid="$(echo "$hsif_pnetids" | cut -c $idx-$end | tr -d ' ')"
|
||||
fi
|
||||
|
||||
# get PNETID of the NET_DEV
|
||||
if [ -e /sys/class/net/$ndev/device/util_string ]; then
|
||||
netif_pnetids="$(cat /sys/class/net/$ndev/device/util_string | tr -d '\000' | iconv -f IBM-1047 -t ASCII)"
|
||||
if [ "$ndev" != "" ]; then
|
||||
netif_pnetid="$(get_pnetid $ndev)"
|
||||
else
|
||||
if [ -e /sys/class/net/$ndev/device/chpid ]; then
|
||||
chpid="$(cat /sys/class/net/$ndev/device/chpid | tr [:upper:] [:lower:])"
|
||||
netif_pnetids="$(cat /sys/devices/css0/chp0.$chpid/util_string | tr -d '\000' | iconv -f IBM-1047 -t ASCII)"
|
||||
fi
|
||||
fi
|
||||
if [ "$netif_pnetids" != "" ]; then
|
||||
port_netif="$(cat /sys/class/net/$ndev/dev_port)"
|
||||
(( idx=16*$port_netif+1 ))
|
||||
(( end=$idx+15 ))
|
||||
netif_pnetid="$(echo "$netif_pnetids" | cut -c $idx-$end | tr -d ' ')"
|
||||
netif_pnetid=""
|
||||
fi
|
||||
|
||||
#Check PNETIDs
|
||||
if [ "$hsif_pnetid" != "" ] && [ "$netif_pnetid" != "" ] && [ "$netif_pnetid" != "$hsif_pnetid" ]; then
|
||||
echo "Error: $hsdev and $ndev have different PNETIDs! They are $hsif_pnetid and $netif_pnetid respectively" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ "$hsif_pnetid" != "" ] && [ "$netif_pnetid" != "" ] && [ "$netif_pnetid" == "$hsif_pnetid" ]; then
|
||||
hsci_pnetid=$hsif_pnetid
|
||||
else
|
||||
if [ "$hsif_pnetid" != "" ]; then
|
||||
hsci_pnetid=$hsif_pnetid
|
||||
else
|
||||
hsci_pnetid=$netif_pnetid
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -96,11 +124,11 @@ function verify_precon {
|
||||
echo "Error: $hsdev does not exist" >&2
|
||||
return 1
|
||||
fi
|
||||
if [ "$(cat /sys/class/net/$hsdev/device/card_type)" != "HiperSockets" ]; then
|
||||
if [ "$(cat /sys/class/net/$hsdev/device/card_type 2>/dev/null)" != "HiperSockets" ]; then
|
||||
echo "Error: $hsdev is not a HiperSockets device" >&2
|
||||
return 1
|
||||
fi
|
||||
if [ "$(cat /sys/class/net/$hsdev/device/layer2)" != "1" ]; then
|
||||
if [ "$(cat /sys/class/net/$hsdev/device/layer2 2>/dev/null)" != "1" ]; then
|
||||
echo "Error: $hsdev is not in layer 2 mode" >&2
|
||||
return 1
|
||||
fi
|
||||
@@ -108,7 +136,7 @@ function verify_precon {
|
||||
echo "Error: Missing vnic-characteristics support" >&2
|
||||
return 1
|
||||
fi
|
||||
if [ "$(cat /sys/class/net/$hsdev/device/vnicc/bridge_invisible)" == "n/a" ]; then
|
||||
if [ "$(cat /sys/class/net/$hsdev/device/vnicc/bridge_invisible 2>/dev/null)" == "n/a" ]; then
|
||||
echo "Error: $hsdev does not support vnicc" >&2
|
||||
return 1
|
||||
fi
|
||||
@@ -121,8 +149,10 @@ function verify_precon {
|
||||
return 1
|
||||
fi
|
||||
if [ $(ip link show $hsdev | grep master | wc -l) -ne 0 ]; then
|
||||
echo "Error: $hsdev is already a bridge port" >&2
|
||||
return 1
|
||||
if [ $(ip link show $hsdev | grep "master $hsci" | wc -l) -eq 0 ]; then
|
||||
echo "Error: $hsdev is already a subordinate to another master" >&2
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
#Pre-verify net_dev
|
||||
@@ -130,17 +160,15 @@ function verify_precon {
|
||||
echo "Error: $ndev does not exist" >&2
|
||||
return 1
|
||||
fi
|
||||
if [ "$(cat /sys/class/net/$ndev/device/card_type)" == "HiperSockets" ]; then
|
||||
echo "Error: $ndev is also a HiperSockets device" >&2
|
||||
return 1
|
||||
fi
|
||||
if [ $(ip link show $ndev | grep UP | wc -l) -eq 0 ]; then
|
||||
echo "Error: $ndev is not in state UP" >&2
|
||||
return 1
|
||||
fi
|
||||
if [ $(ip link show $ndev | grep master | wc -l) -ne 0 ]; then
|
||||
echo "Error: $ndev is already a bridge port" >&2
|
||||
return 1
|
||||
if [ $(ip link show $ndev | grep "master $hsci" | wc -l) -eq 0 ]; then
|
||||
echo "Error: $ndev is already a subordinate to another master" >&2
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
#Check PNETIDs
|
||||
@@ -153,10 +181,12 @@ function verify_precon {
|
||||
}
|
||||
|
||||
function clean_up {
|
||||
ip link del $hsci >/dev/null 2>&1
|
||||
bridge link set dev $hsdev learning_sync off self >/dev/null 2>&1
|
||||
echo 0 > /sys/class/net/$hsdev/device/vnicc/bridge_invisible >/dev/null 2>&1
|
||||
bridge fdb del $hsdev_mac dev $ndev >/dev/null 2>&1
|
||||
ip link del $hsci >/dev/null 2>&1
|
||||
bridge fdb del $hsci_mac dev $hsdev self local >/dev/null 2>&1
|
||||
bridge fdb del $hsci_mac dev $ndev self local >/dev/null 2>&1
|
||||
ip link del $hscibr >/dev/null 2>&1
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
@@ -172,22 +202,33 @@ function add_hsci {
|
||||
hsdev=$1
|
||||
ndev=$2
|
||||
|
||||
hsci_postfix="$(readlink /sys/class/net/$hsdev/device/cdev0 | tail -c5)"
|
||||
hscibr=hsci$hsci_postfix-br
|
||||
hscibp=hsci$hsci_postfix-bp
|
||||
hsci=hsci$hsci_postfix
|
||||
|
||||
#### Verify preconditions
|
||||
verify_precon
|
||||
if [ $? -ne 0 ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
hsci_postfix="$(readlink /sys/class/net/$hsdev/device/cdev0 | tail -c5)"
|
||||
hsci=hsci$hsci_postfix
|
||||
|
||||
echo "Adding $hsci with a HiperSockets dev $hsdev and an external dev $ndev"
|
||||
|
||||
#### Create bridge
|
||||
ip link add name $hsci type bridge stp_state 0 >/dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Could not create a bridge" >&2
|
||||
return 1
|
||||
#### Create bridge (idempotent)
|
||||
if [ ! -e /sys/class/net/$hscibr ]; then
|
||||
# ageing_time of $hscibr defaults to 30000 (300 secs)
|
||||
ip link add name $hscibr type bridge stp_state 0 >/dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Could not create a bridge" >&2
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
ip link set dev $hscibr type bridge stp_state 0 >/dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: $hscibr is not a bridge" >&2
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
#### Prepare hsdev
|
||||
@@ -196,19 +237,24 @@ function add_hsci {
|
||||
echo 1 > /sys/class/net/$hsdev/device/vnicc/bridge_invisible
|
||||
|
||||
#### Create bridge ports
|
||||
ip link set dev $ndev master $hsci >/dev/null 2>&1
|
||||
ip link set dev $ndev master $hscibr >/dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Could not set master for $ndev" >&2
|
||||
clean_up
|
||||
return 1
|
||||
fi
|
||||
ip link set dev $hsdev master $hsci >/dev/null 2>&1
|
||||
ip link set dev $hsdev master $hscibr >/dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Could not set master for $hsdev" >&2
|
||||
clean_up
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Do not learn from ndev, but do learn from hsci-bp:
|
||||
# - First define hsdev and ndev as _isolated_ bridgeports
|
||||
# - Then turn on learning_sync on self on hsdev
|
||||
# - Then define hsci-bp as non-isolated veth bridgeport
|
||||
|
||||
# no forwarding between ndev and hsdev -> isolated on
|
||||
# ndev is default for outgoing unknown targets -> flood on
|
||||
# no need to learn external LAN targets into fdb -> learning off
|
||||
@@ -230,29 +276,102 @@ function add_hsci {
|
||||
clean_up
|
||||
return 1
|
||||
fi
|
||||
|
||||
# NOTE: Although not required, BCs will be sent out on hsdev.
|
||||
# NOTE: We need to receive BCs on hsdev, as z/OS HSCI does ARP requests on HS.
|
||||
|
||||
hsdev_mac="$(cat /sys/class/net/$hsdev/address)"
|
||||
echo "Set $hsdev MAC $hsdev_mac on $ndev and $hsci"
|
||||
|
||||
# set HS MAC on OSA as secondary MAC
|
||||
bridge fdb add $hsdev_mac dev $ndev >/dev/null 2>&1
|
||||
ip link set dev $hscibr up >/dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Failed to set HS MAC on OSA as secondary MAC" >&2
|
||||
echo "Error: Failed to set $hscibr up" >&2
|
||||
clean_up
|
||||
return 1
|
||||
fi
|
||||
|
||||
# set HS MAC (common MAC) on HSCI as primary MAC
|
||||
ip link set address $hsdev_mac dev $hsci >/dev/null 2>&1
|
||||
#### Turn on device for bridge notification
|
||||
#### Toggle is required to learn full list of HS targets,
|
||||
#### not only future changes.
|
||||
bridge link set dev $hsdev learning_sync off self >/dev/null 2>&1
|
||||
bridge link set dev $hsdev learning_sync on self >/dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Failed to set HiperSockets MAC (common MAC) on HSCI as primary MAC" >&2
|
||||
echo "Error: Failed to turn on device for bridge notification" >&2
|
||||
clean_up
|
||||
return 1
|
||||
fi
|
||||
|
||||
# define veth pair for hsci (idempotent)
|
||||
if [ ! -e /sys/class/net/$hsci ]; then
|
||||
ip link add dev $hsci type veth peer name $hscibp >/dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Could not create veth pair $hsci - $hscibp " >&2
|
||||
clean_up
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
if [ $hsci@$hscibp: != "$(ip -o -d link show dev $hsci | awk '/veth/ {print $2}' )" ]; then
|
||||
echo "Error: $hsci@$hscibp is not a veth" >&2
|
||||
clean_up
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
ip link set dev $hscibp master $hscibr >/dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Failed to add $hscibp to $hscibr" >&2
|
||||
clean_up
|
||||
return 1
|
||||
fi
|
||||
bridge link set dev $hscibp isolated off learning on flood on mcast_flood on >/dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Failed to set bridge parameters for $hscibp" >&2
|
||||
clean_up
|
||||
return 1
|
||||
fi
|
||||
|
||||
#### Set a static forwarding rule for hsci MAC, so hsci can be used as a
|
||||
#### single-MAC network interface without being subject to
|
||||
#### ageing and re-learning
|
||||
#### Wait for systemd to change the MAC of hsci, if it wants to:
|
||||
sleep 1
|
||||
hsci_mac="$(cat /sys/class/net/$hsci/address)"
|
||||
#### (idempotent)
|
||||
if [ $(bridge fdb show dev $hscibp | grep "$hsci_mac master $hscibr static" | wc -l) -eq 0 ]; then
|
||||
bridge fdb add $hsci_mac dev $hscibp master static
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Failed to set $hsci_mac to $hscibr fdb" >&2
|
||||
clean_up
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Bridge-to-device learning will set this MAC on hsdev and ndev.
|
||||
# Old kernel code doesn't do hsci bridge-to-device learning.
|
||||
# In this case: Set hsci_mac as local MAC of hsdev and ndev,
|
||||
# so at least the single-MAC scenario works.
|
||||
if [ $(bridge fdb show dev $hsdev | grep "$hsci_mac self permanent" | wc -l) -eq 0 ]; then
|
||||
echo "Warning: $hsci will support only its current static MAC address. Please upgrade your kernel to the latest level." >&2
|
||||
bridge fdb add $hsci_mac dev $hsdev self local
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Failed to add $hsci_mac to $hsdev" >&2
|
||||
clean_up
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
if [ $(bridge fdb show dev $ndev | grep "$hsci_mac self permanent" | wc -l) -eq 0 ]; then
|
||||
bridge fdb add $hsci_mac dev $ndev self local
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Failed to add $hsci_mac to $ndev" >&2
|
||||
clean_up
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
#### Set veth pair to UP
|
||||
ip link set dev $hscibp up >/dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Failed to set $hscibp up" >&2
|
||||
clean_up
|
||||
return 1
|
||||
fi
|
||||
ip link set dev $hsci up >/dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Failed to set $hsci up" >&2
|
||||
@@ -260,14 +379,7 @@ function add_hsci {
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Turn on device for bridge notification
|
||||
bridge link set dev $hsdev learning_sync on self >/dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Failed to turn on device for bridge notification" >&2
|
||||
clean_up
|
||||
return 1
|
||||
fi
|
||||
echo "Successfully added HSCI interface $hsci"
|
||||
echo "Added HSCI interface $hsci"
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -286,51 +398,97 @@ function del_hsci {
|
||||
echo "Error: $hsci does not exit" >&2
|
||||
return 1
|
||||
fi
|
||||
if [ $(ip link show | grep "master $hsci" | wc -l) -eq 0 ]; then
|
||||
echo "Error: $hsci is not an active HSCI interface" >&2
|
||||
return 1
|
||||
hsci_mac="$(cat /sys/class/net/$hsci/address)"
|
||||
|
||||
#### Find hscibp and hscibr
|
||||
hscibp="$(ip -o link show dev $hsci | awk '{print $2}')"
|
||||
if [[ $hscibp != *@* ]]; then
|
||||
# $hsci has no HSCI veth peer
|
||||
echo "Warning: $hsci may have been created by an older version of hsci" >&2
|
||||
mvp=1
|
||||
hscibp=""
|
||||
hscibr=$hsci
|
||||
else
|
||||
mvp=0
|
||||
hscibp=${hscibp##*@}
|
||||
hscibp=${hscibp%:}
|
||||
echo "$hsci is paired with $hscibp" >&2
|
||||
|
||||
hscibr="$(ip link show dev $hscibp | awk '{for(x=1;x<NF;x++) if($x~/master/) print $(x+1)}')"
|
||||
fi
|
||||
|
||||
bports="$(ip link show | grep "master $hsci" | awk '{print $2}')"
|
||||
#### Find hsdev and ndev
|
||||
hsdev=""
|
||||
ndev=""
|
||||
bports="$(ip link show | grep "master $hscibr" | awk '{print $2}')"
|
||||
for bport in $bports; do
|
||||
bport=${bport%:}
|
||||
if [[ $bport == *@* ]]; then
|
||||
bport=${bport%@*}
|
||||
fi
|
||||
if [ $(bridge -d link show dev $bport | grep "learning_sync on" | wc -l) -ne 0 ]; then
|
||||
hsdev=$bport
|
||||
else
|
||||
ndev=$bport
|
||||
bport=${bport%@*}
|
||||
if [ $(bridge -d link show dev $bport | grep "isolated on" | wc -l) -ne 0 ]; then
|
||||
if [ $(bridge -d link show dev $bport | grep "learning_sync on" | wc -l) -ne 0 ]; then
|
||||
hsdev=$bport
|
||||
else
|
||||
ndev=$bport
|
||||
fi
|
||||
fi
|
||||
done
|
||||
if [ "$hsdev" == "" ]; then
|
||||
echo "Error: $hsci has no active HiperSockets port" >&2
|
||||
return 1
|
||||
fi
|
||||
echo "Deleting HSCI interface $hsci with the HiperSockets $hsdev and the external $ndev"
|
||||
|
||||
bridge link set dev $hsdev learning_sync off self >/dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Failed to turn off learning_sync on $hsdev" >&2
|
||||
return 1
|
||||
fi
|
||||
echo 0 > /sys/class/net/$hsdev/device/vnicc/bridge_invisible
|
||||
|
||||
hsdev_mac="$(cat /sys/class/net/$hsdev/address)"
|
||||
echo "Deleting $hsev MAC $hsdev_mac on $ndev"
|
||||
bridge fdb del $hsdev_mac dev $ndev >/dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Failed to delete $hsev MAC $hsdev_mac on $ndev" >&2
|
||||
return 1
|
||||
if [ "$ndev" == "" ]; then
|
||||
echo "Error: $hsci has no active external port" >&2
|
||||
fi
|
||||
echo "Deleting HSCI interface $hsci with HiperSockets interface $hsdev and external interface $ndev"
|
||||
|
||||
#### Delete veth before resetting learning_sync and deleting bridge,
|
||||
#### so fdb entries are cleaned up (synced)!
|
||||
ip link del $hsci >/dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Failed to delete $hsci" >&2
|
||||
return 1
|
||||
fi
|
||||
echo "Successfully deleted device $hsci"
|
||||
# Bridge-to-device learning will remove learned MACs from hsdev and ndev.
|
||||
# Old kernel code doesn't do hsci bridge-to-device learning.
|
||||
# In this case: Remove the hsci_mac that was added during 'add' from
|
||||
# hsdev and ndev.
|
||||
if [ "$hsdev" != "" ] && [ $mvp -eq 0 ]; then
|
||||
if [ $(bridge fdb show dev $hsdev | grep "$hsci_mac self permanent" | wc -l) -ne 0 ]; then
|
||||
echo "Warning: It seems your kernel does not support all hsci features, please upgrade." >&2
|
||||
bridge fdb del $hsci_mac dev $hsdev self local
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Failed to delete $hsci_mac from $hsdev" >&2
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
# In the mvp case hsci_mac == hs_mac and was only set on ndev.
|
||||
if [ "$ndev" != "" ]; then
|
||||
if [ $(bridge fdb show dev $ndev | grep "$hsci_mac self permanent" | wc -l) -ne 0 ]; then
|
||||
bridge fdb del $hsci_mac dev $ndev self local
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Failed to delete $hsci_mac from $ndev" >&2
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
#### Reset learning_sync
|
||||
if [ "$hsdev" != "" ]; then
|
||||
bridge link set dev $hsdev learning_sync off self >/dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Failed to turn off learning_sync on $hsdev" >&2
|
||||
fi
|
||||
fi
|
||||
#### Delete bridge
|
||||
if [ "$hscibr" != "" ]; then
|
||||
ip link del $hscibr >/dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Failed to delete $hscibr" >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$hsdev" != "" ]; then
|
||||
echo 0 > /sys/class/net/$hsdev/device/vnicc/bridge_invisible
|
||||
fi
|
||||
|
||||
echo "Deleted $hsci"
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -338,19 +496,63 @@ function del_hsci {
|
||||
## Show HSCI
|
||||
##############################################################################
|
||||
|
||||
function list_active {
|
||||
hsdev=$1
|
||||
local ext=""
|
||||
function print_row {
|
||||
if [ $mvp -eq 0 ]; then
|
||||
printf '%-8s %-16s %-15s %-15s\n' "$hsci" "$hsci_pnetid" "$hsdev" "$ndev"
|
||||
else
|
||||
printf '%-8s %-16s %-15s %-15s (v1)\n' "$hsci" "$hsci_pnetid" "$hsdev" "$ndev"
|
||||
fi
|
||||
}
|
||||
|
||||
hsci="$(ip link show dev $hsdev | awk '{for(x=1;x<NF;x++) if($x~/master/) print $(x+1)}')"
|
||||
ext="$(ip link show | grep "master $hsci" | grep --invert-match $hsdev | awk '{print $2}')"
|
||||
# remove trailing ':'
|
||||
ndev="${ext%:}"
|
||||
function list_active {
|
||||
hsci=""
|
||||
hsdev=$1
|
||||
ndev=""
|
||||
hscibp=""
|
||||
mvp=1
|
||||
|
||||
hsci_pnetid="$(get_pnetid $hsdev)"
|
||||
|
||||
hscibr="$(ip link show dev $hsdev | awk '{for(x=1;x<NF;x++) if($x~/master/) print $(x+1)}')"
|
||||
if [ "$hscibr" == "" ]; then
|
||||
print_row
|
||||
return 0
|
||||
fi
|
||||
|
||||
#### find ndev (the other isolated subordinate)
|
||||
bports="$(ip link show | grep "master $hscibr" | awk '{print $2}')"
|
||||
for bport in $bports; do
|
||||
bport=${bport%:}
|
||||
bport=${bport%@*}
|
||||
if [ $(ip -d link show dev $bport | grep "isolated on" | wc -l) -ne 0 ]; then
|
||||
if [ $bport != $hsdev ]; then
|
||||
ndev=$bport
|
||||
fi
|
||||
else
|
||||
hscibp=$bport
|
||||
mvp=0
|
||||
fi
|
||||
done
|
||||
|
||||
check_pnetids
|
||||
|
||||
printf '%-8s %-16s %-15s %-15s\n' "$hsci" "$hsci_pnetid" "$hsdev" "$ndev"
|
||||
if [ $mvp -ne 0 ]; then
|
||||
hsci=$hscibr
|
||||
print_row
|
||||
return 0
|
||||
fi
|
||||
|
||||
#### find hsci (veth-peer of non-isolated subordinate of hscibr)
|
||||
hsci="$(ip -o link show dev $hscibp | awk '{print $2}')"
|
||||
if [[ $hsci != *@* ]]; then
|
||||
echo "Error: $hscibp has no HSCI veth peer" >&2
|
||||
print_row
|
||||
return 1
|
||||
fi
|
||||
hsci=${hsci##*@}
|
||||
hsci=${hsci%:}
|
||||
|
||||
print_row
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -358,13 +560,14 @@ function print_header {
|
||||
if [ $header -eq 0 ]; then
|
||||
echo "HSCI PNET_ID HiperSockets External "
|
||||
echo "------------------------------------------------------------"
|
||||
header=1
|
||||
fi
|
||||
}
|
||||
|
||||
function list_one {
|
||||
local hsnetdev=$1
|
||||
|
||||
if [ $(bridge -d link show dev $hsnetdev | grep "learning_sync on" | wc -l) -ne 0 ]; then
|
||||
if [ $(bridge -d link show dev $hsnetdev 2>/dev/null | grep "learning_sync on" | wc -l) -ne 0 ]; then
|
||||
print_header
|
||||
list_active $hsnetdev
|
||||
fi
|
||||
@@ -433,4 +636,3 @@ while true; do
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
|
||||
@@ -23,10 +23,11 @@
|
||||
#define INITRD_SIZE _AC(0x10410, UL)
|
||||
#define OLDMEM_BASE _AC(0x10418, UL)
|
||||
#define OLDMEM_SIZE _AC(0x10420, UL)
|
||||
#define MAX_COMMAND_LINE_SIZE _AC(0x10430, UL)
|
||||
#define COMMAND_LINE _AC(0x10480, UL)
|
||||
|
||||
/* Parameter sizes */
|
||||
#define COMMAND_LINE_SIZE 896
|
||||
#define LEGACY_COMMAND_LINE_SIZE 896
|
||||
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
106
include/lib/libcpumf.h
Normal file
106
include/lib/libcpumf.h
Normal file
@@ -0,0 +1,106 @@
|
||||
/* Copyright IBM Corp. 2022
|
||||
*
|
||||
* 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 LIBCPUMF_H
|
||||
#define LIBCPUMF_H
|
||||
|
||||
#include <sched.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define S390_CPUMF_CF "/sys/devices/cpum_cf/"
|
||||
#define S390_CPUMF_CFDIAG "/sys/devices/cpum_cf_diag/"
|
||||
#define S390_CPUMF_SF "/sys/devices/cpum_sf/"
|
||||
#define S390_CPUS_POSSIBLE "/sys/devices/system/cpu/possible"
|
||||
#define S390_CPUS_ONLINE "/sys/devices/system/cpu/online"
|
||||
#define S390_CPUMSF_BUFFERSZ "/sys/module/kernel/parameters/cpum_sfb_size"
|
||||
|
||||
/**
|
||||
* Read out the PMU type from a given file.
|
||||
*
|
||||
* Return the PMU type number assigned to this PMU by the kernel. This is
|
||||
* a non zero number.
|
||||
* If the PMU does not exist return -1 and set errno.
|
||||
*
|
||||
* @param[in] dirname Name of the event directory in sysfs
|
||||
*/
|
||||
int libcpumf_pmutype(const char *dirname);
|
||||
|
||||
/**
|
||||
* Read out the CPU list from a given file name, for example from files
|
||||
* /sys/devices/system/cpu/online or /sys/devices/system/cpu/possible.
|
||||
*
|
||||
* Return the cpu_set_t created from parsing the CPU list in the second
|
||||
* parameter.
|
||||
* Return code of zero indicates proper conversion and -1 indicates an
|
||||
* error.
|
||||
*
|
||||
* @param[in] buffer Comma separated string of a CPU list
|
||||
* @param[in] filename Name of a sysfs CPU list file name
|
||||
* @param[out] mask Converted buffer into cpu_set_t mask structure
|
||||
*/
|
||||
int libcpumf_cpuset(const char *buffer, cpu_set_t *mask);
|
||||
int libcpumf_cpuset_fn(const char *filename, cpu_set_t *mask);
|
||||
|
||||
/**
|
||||
* Read CPU Measurement Counting Facility hardware information
|
||||
*
|
||||
* Return true if CPU Measurement Counter facility information has been
|
||||
* retrieved and is valid.
|
||||
*
|
||||
* Return false if the information could not be extracted from the file.
|
||||
*
|
||||
* @param[out] cfvn Contains CPUMF counter first version number
|
||||
* @param[out] csvn Contains CPUMF counter second version number
|
||||
* @param[out] auth Contains CPUMF counter set authorization level
|
||||
*/
|
||||
bool libcpumf_cpumcf_info(int *cfvn, int *csvn, int *auth);
|
||||
|
||||
/**
|
||||
* Return true if CPU Measurement Counter Facility is available.
|
||||
*/
|
||||
bool libcpumf_have_cpumcf(void);
|
||||
|
||||
/**
|
||||
* Read CPU Measurement Sampling Facility hardware information
|
||||
*
|
||||
* Read all necessary information from /sysfs file /proc/service_levels
|
||||
* to return CPU Measurement Counter Sampling facility information
|
||||
* characteristics.
|
||||
* Return true on success and false when the data can not be retrieved.
|
||||
*
|
||||
* @param[out] min Minimum supported sampling interval
|
||||
* @param[out] max Maximum supported sampling interval
|
||||
* @param[out] speed Current CPU speed, number of CPU cylces per
|
||||
* microsecond
|
||||
* @param[out] basic_sz Basic sample size in bytes
|
||||
* @param[out] diag_sz Diagnostic sample size in bytes
|
||||
*/
|
||||
bool libcpumf_cpumsf_info(unsigned long *min, unsigned long *max,
|
||||
unsigned long *speed, int *basic_sz, int *diag_sz);
|
||||
|
||||
/**
|
||||
* Return true if CPU Measurement Sampling Facility is available.
|
||||
*/
|
||||
bool libcpumf_have_cpumsf(void);
|
||||
|
||||
/**
|
||||
* Return true if CPU Measurement Sampling Facility buffer sizes are
|
||||
* available.
|
||||
*/
|
||||
bool libcpumf_have_sfb(void);
|
||||
|
||||
/**
|
||||
* Read CPU Measurement Sampling Facility supported sampling buffer sizes.
|
||||
*
|
||||
* Return the minimum and maximum CPU Measurement sampling facitity buffer
|
||||
* sizes supported.
|
||||
* Return true on success and false otherwise.
|
||||
*
|
||||
* @param[out] min Minimum supported sampling buffer size
|
||||
* @param[out] max Maximum supported sampling buffer size
|
||||
*/
|
||||
bool libcpumf_sfb_info(unsigned long *min, unsigned long *max);
|
||||
#endif
|
||||
@@ -20,6 +20,10 @@ void util_hexdump(FILE *fh, const char *tag, const void *data, int cnt);
|
||||
void util_hexdump_grp(FILE *fh, const char *tag, const void *data, int group,
|
||||
int cnt, int indent);
|
||||
void util_print_indented(const char *str, int indent);
|
||||
const char *util_libdir(void);
|
||||
const char *util_libdir_path(const char *filename);
|
||||
const char *util_datadir(void);
|
||||
const char *util_datadir_path(const char *filename);
|
||||
|
||||
static inline void util_ptr_vec_free(void **ptr_vec, int count)
|
||||
{
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
#define TOOLS_LIBDIR STRINGIFY (S390_TOOLS_LIBDIR)
|
||||
#define TOOLS_SYSCONFDIR STRINGIFY (S390_TOOLS_SYSCONFDIR)
|
||||
#define TOOLS_BINDIR STRINGIFY (S390_TOOLS_BINDIR)
|
||||
#define TOOLS_DATADIR STRINGIFY (S390_TOOLS_DATADIR)
|
||||
|
||||
#define __noreturn __attribute__((noreturn))
|
||||
#define __packed __attribute__((packed))
|
||||
|
||||
19
libcpumf/Makefile
Normal file
19
libcpumf/Makefile
Normal file
@@ -0,0 +1,19 @@
|
||||
include ../common.mak
|
||||
|
||||
lib = libcpumf.a
|
||||
|
||||
examples = libcpumf_example
|
||||
|
||||
all: $(lib)
|
||||
examples: $(lib) $(examples)
|
||||
|
||||
objects = libcpumf_pmutype.o libcpumf_cpuset.o libcpumf_support.o
|
||||
|
||||
$(lib): $(objects)
|
||||
|
||||
install: all
|
||||
|
||||
libcpumf_example: libcpumf_example.o $(lib)
|
||||
|
||||
clean:
|
||||
rm -f *.o $(lib) $(examples)
|
||||
75
libcpumf/libcpumf_cpuset.c
Normal file
75
libcpumf/libcpumf_cpuset.c
Normal file
@@ -0,0 +1,75 @@
|
||||
/* Copyright IBM Corp. 2022
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "lib/libcpumf.h"
|
||||
|
||||
int libcpumf_cpuset(const char *parm, cpu_set_t *mask)
|
||||
{
|
||||
char *cp, *buffer = strdup(parm);
|
||||
int to, from, rc;
|
||||
|
||||
if (!buffer) /* Errno set to ENOMEM */
|
||||
return -1;
|
||||
/* Check for invalid characters, such as 11.12 instead 11-12
|
||||
* but allow blanks and newline. Newline is appended
|
||||
* when the string is taken from sysfs files, for example
|
||||
* /sys/devices/system/cpu/online
|
||||
*/
|
||||
if (strspn(buffer, "0123456789-,\n ") != strlen(buffer)) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
CPU_ZERO(mask);
|
||||
for (; (cp = strtok(buffer, ",")); buffer = NULL) {
|
||||
char *dash = strchr(cp, '-'); /* Range character? */
|
||||
bool is_ok;
|
||||
|
||||
if (dash) {
|
||||
rc = sscanf(cp, "%d-%d", &from, &to);
|
||||
is_ok = rc == 2;
|
||||
} else {
|
||||
rc = sscanf(cp, "%d", &to);
|
||||
from = to;
|
||||
is_ok = rc == 1;
|
||||
}
|
||||
if (!is_ok) {
|
||||
errno = ERANGE;
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
for (; from <= to; ++from)
|
||||
CPU_SET(from, mask);
|
||||
}
|
||||
rc = 0;
|
||||
out:
|
||||
free(buffer);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int libcpumf_cpuset_fn(const char *filename, cpu_set_t *mask)
|
||||
{
|
||||
char *txt = NULL;
|
||||
ssize_t ret = -1;
|
||||
size_t len = 0;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(filename, "r");
|
||||
if (!fp)
|
||||
return ret;
|
||||
/* Read out file, one line expected */
|
||||
ret = getline(&txt, &len, fp);
|
||||
fclose(fp);
|
||||
if (ret > 0)
|
||||
ret = libcpumf_cpuset(txt, mask);
|
||||
free(txt);
|
||||
return ret;
|
||||
}
|
||||
76
libcpumf/libcpumf_example.c
Normal file
76
libcpumf/libcpumf_example.c
Normal file
@@ -0,0 +1,76 @@
|
||||
/* Copyright IBM Corp. 2022
|
||||
*
|
||||
* 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 <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "lib/libcpumf.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
unsigned long min, max, speed, sfb_min, sfb_max;
|
||||
int rc, pmu, cfvn, csvn, auth;
|
||||
cpu_set_t set;
|
||||
|
||||
pmu = libcpumf_pmutype(S390_CPUMF_CF);
|
||||
if (pmu >= 0)
|
||||
printf("PMU %stype %d\n", S390_CPUMF_CF, pmu);
|
||||
else
|
||||
printf("PMU %stype error %d\n", S390_CPUMF_CF, errno);
|
||||
pmu = libcpumf_pmutype(S390_CPUMF_SF);
|
||||
if (pmu >= 0)
|
||||
printf("PMU %stype %d\n", S390_CPUMF_SF, pmu);
|
||||
else
|
||||
printf("PMU %stype error %d\n", S390_CPUMF_SF, errno);
|
||||
pmu = libcpumf_pmutype(S390_CPUMF_CFDIAG);
|
||||
if (pmu >= 0)
|
||||
printf("PMU %stype %d\n", S390_CPUMF_CFDIAG, pmu);
|
||||
else
|
||||
printf("PMU %stype error %d\n", S390_CPUMF_CFDIAG, errno);
|
||||
|
||||
rc = libcpumf_cpuset_fn(S390_CPUS_ONLINE, &set);
|
||||
if (rc == 0) {
|
||||
puts("Online CPUs:");
|
||||
for (int i = 0; i < CPU_SETSIZE; ++i)
|
||||
if (CPU_ISSET(i, &set))
|
||||
printf("%d ", i);
|
||||
putchar('\n');
|
||||
}
|
||||
rc = libcpumf_cpuset("0-7,9,11-12 ,15", &set);
|
||||
if (rc == 0) {
|
||||
puts("String CPUs:");
|
||||
for (int i = 0; i < CPU_SETSIZE; ++i)
|
||||
if (CPU_ISSET(i, &set))
|
||||
printf("%d ", i);
|
||||
putchar('\n');
|
||||
} else {
|
||||
printf("libcpumf_cpuset input invalid %d\n", errno);
|
||||
}
|
||||
|
||||
printf("CPUMCF support %d\n", libcpumf_have_cpumcf());
|
||||
rc = libcpumf_cpumcf_info(&cfvn, &csvn, &auth);
|
||||
printf("libcpumf_cpumcf_info %d", rc);
|
||||
if (rc)
|
||||
printf(" cfvn %d csvn %d authorization %#x", cfvn, csvn, auth);
|
||||
putchar('\n');
|
||||
|
||||
printf("CPUMSF support %d\n", libcpumf_have_cpumsf());
|
||||
rc = libcpumf_cpumsf_info(&min, &max, &speed, &cfvn, &csvn);
|
||||
printf("libcpumf_cpumsf_info %d", rc);
|
||||
if (rc)
|
||||
printf(" min %ld max %ld speed %#lx basic %d diag %d", min,
|
||||
max, speed, cfvn, csvn);
|
||||
putchar('\n');
|
||||
|
||||
printf("CPUMSF have sfb %d\n", libcpumf_have_sfb());
|
||||
rc = libcpumf_sfb_info(&sfb_min, &sfb_max);
|
||||
printf("libcpumf_sfb_info %d", rc);
|
||||
if (rc)
|
||||
printf(" sfb_min %lu sfb_max %lu", sfb_min, sfb_max);
|
||||
putchar('\n');
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
33
libcpumf/libcpumf_pmutype.c
Normal file
33
libcpumf/libcpumf_pmutype.c
Normal file
@@ -0,0 +1,33 @@
|
||||
/* Copyright IBM Corp. 2022
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "lib/libcpumf.h"
|
||||
|
||||
int libcpumf_pmutype(const char *dirname)
|
||||
{
|
||||
FILE *file;
|
||||
char *fn;
|
||||
int ret;
|
||||
|
||||
ret = asprintf(&fn, "%s/type", dirname);
|
||||
if (ret == -1) /* No memory, errno set */
|
||||
return ret;
|
||||
file = fopen(fn, "r");
|
||||
free(fn);
|
||||
ret = -1; /* Errno set on file open error */
|
||||
if (file) {
|
||||
/* Read out a single number from that file */
|
||||
if (fscanf(file, "%u", &ret) != 1)
|
||||
/* Unexpected format error, set errno */
|
||||
errno = -ERANGE;
|
||||
fclose(file);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
141
libcpumf/libcpumf_support.c
Normal file
141
libcpumf/libcpumf_support.c
Normal file
@@ -0,0 +1,141 @@
|
||||
/* Copyright IBM Corp. 2022
|
||||
*
|
||||
* 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 <err.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "lib/libcpumf.h"
|
||||
|
||||
#define SERVICELEVEL "/proc/service_levels"
|
||||
|
||||
bool libcpumf_cpumcf_info(int *cfvn, int *csvn, int *auth)
|
||||
{
|
||||
char *linep = NULL;
|
||||
bool rc = false;
|
||||
size_t line_sz;
|
||||
ssize_t nbytes;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(SERVICELEVEL, "r");
|
||||
if (!fp)
|
||||
err(EXIT_FAILURE, SERVICELEVEL);
|
||||
|
||||
while ((nbytes = getline(&linep, &line_sz, fp)) != EOF) {
|
||||
if (!strncmp(linep, "CPU-MF: Counter facility:", 25)) {
|
||||
int cnt = sscanf(linep, "CPU-MF: Counter facility:"
|
||||
" version=%d.%d authorization=%x",
|
||||
cfvn, csvn, auth);
|
||||
if (cnt != 3) {
|
||||
warnx("Can not parse line %s", linep);
|
||||
goto out;
|
||||
}
|
||||
rc = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
out:
|
||||
fclose(fp);
|
||||
free(linep);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool libcpumf_have_cpumcf(void)
|
||||
{
|
||||
int cfvn, csvn, auth;
|
||||
|
||||
return libcpumf_cpumcf_info(&cfvn, &csvn, &auth);
|
||||
}
|
||||
|
||||
bool libcpumf_cpumsf_info(unsigned long *min, unsigned long *max,
|
||||
unsigned long *speed, int *basic_sz, int *diag_sz)
|
||||
{
|
||||
char *linep = NULL;
|
||||
bool rc = true;
|
||||
size_t line_sz;
|
||||
ssize_t nbytes;
|
||||
int hit = 0;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(SERVICELEVEL, "r");
|
||||
if (!fp)
|
||||
err(EXIT_FAILURE, SERVICELEVEL);
|
||||
|
||||
while ((nbytes = getline(&linep, &line_sz, fp)) != EOF) {
|
||||
int ok;
|
||||
|
||||
if (!strncmp(linep, "CPU-MF: Sampling facility: min", 30)) {
|
||||
ok = sscanf(linep, "CPU-MF: Sampling facility:"
|
||||
" min_rate=%ld max_rate=%ld cpu_speed=%ld",
|
||||
min, max, speed);
|
||||
if (ok != 3) {
|
||||
warnx("Can not parse line %s", linep);
|
||||
goto out;
|
||||
}
|
||||
hit += 1;
|
||||
}
|
||||
if (!strncmp(linep, "CPU-MF: Sampling facility: mode=basic", 37)) {
|
||||
ok = sscanf(linep, "CPU-MF: Sampling facility:"
|
||||
" mode=basic sample_size=%u", basic_sz);
|
||||
if (ok != 1) {
|
||||
warnx("Can not parse line %s", linep);
|
||||
goto out;
|
||||
}
|
||||
hit += 1;
|
||||
}
|
||||
if (!strncmp(linep, "CPU-MF: Sampling facility: mode=diag", 36)) {
|
||||
ok = sscanf(linep, "CPU-MF: Sampling facility:"
|
||||
" mode=diagnostic sample_size=%u",
|
||||
diag_sz);
|
||||
if (ok != 1) {
|
||||
warnx("Can not parse line %s", linep);
|
||||
goto out;
|
||||
}
|
||||
hit += 1;
|
||||
}
|
||||
}
|
||||
out:
|
||||
fclose(fp);
|
||||
free(linep);
|
||||
if (hit != 3)
|
||||
rc = false;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool libcpumf_have_cpumsf(void)
|
||||
{
|
||||
unsigned long a, b, c;
|
||||
int basic_sz, diag_sz;
|
||||
|
||||
return libcpumf_cpumsf_info(&a, &b, &c, &basic_sz, &diag_sz);
|
||||
}
|
||||
|
||||
bool libcpumf_have_sfb(void)
|
||||
{
|
||||
unsigned long a, b;
|
||||
|
||||
return libcpumf_sfb_info(&a, &b);
|
||||
}
|
||||
|
||||
bool libcpumf_sfb_info(unsigned long *min, unsigned long *max)
|
||||
{
|
||||
int rc = false;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(S390_CPUMSF_BUFFERSZ, "r");
|
||||
if (!fp)
|
||||
err(EXIT_FAILURE, "%s", S390_CPUMSF_BUFFERSZ);
|
||||
if (fscanf(fp, "%lu,%lu", min, max) == 2)
|
||||
rc = true;
|
||||
fclose(fp);
|
||||
return rc;
|
||||
}
|
||||
@@ -96,7 +96,7 @@ int dasd_sys_ese(char *devnode)
|
||||
return (rc == 1) ? 1 : 0;
|
||||
}
|
||||
|
||||
int dasd_get_pm_from_chpid(char *busid, unsigned int chpid, int *mask)
|
||||
static int dasd_get_pm_from_chpid(char *busid, unsigned int chpid, int *mask)
|
||||
{
|
||||
unsigned int val;
|
||||
int count, i;
|
||||
|
||||
@@ -47,6 +47,8 @@ detect-openssl-version.dep:
|
||||
echo "}" >> $(TMPFILE)
|
||||
mv $(TMPFILE) $@
|
||||
|
||||
CURL_CONFIG ?= curl-config
|
||||
|
||||
check-dep-libekmfweb: detect-openssl-version.dep
|
||||
$(call check_dep, \
|
||||
"libekmfweb", \
|
||||
@@ -64,8 +66,8 @@ check-dep-libekmfweb: detect-openssl-version.dep
|
||||
"curl/curl.h", \
|
||||
"libcurl-devel", \
|
||||
"HAVE_LIBCURL=0" \
|
||||
`curl-config --cflags` `curl-config --libs`)
|
||||
curl-config --ssl-backends | grep OpenSSL >/dev/null 2>&1 || { echo "Error: libcurl is not built with the OpenSSL backend"; exit 1; }
|
||||
`$(CURL_CONFIG) --cflags` `$(CURL_CONFIG) --libs`)
|
||||
$(CURL_CONFIG) --ssl-backends | grep OpenSSL >/dev/null 2>&1 || { echo "Error: libcurl is not built with the OpenSSL backend"; exit 1; }
|
||||
touch check-dep-libekmfweb
|
||||
|
||||
skip-libekmfweb-openssl:
|
||||
@@ -83,8 +85,8 @@ ekmfweb.o: check-dep-libekmfweb ekmfweb.c utilities.h cca.h $(rootdir)include/ek
|
||||
utilities.o: check-dep-libekmfweb utilities.c utilities.h $(rootdir)include/ekmfweb/ekmfweb.h
|
||||
cca.o: check-dep-libekmfweb cca.c cca.h utilities.h $(rootdir)include/ekmfweb/ekmfweb.h
|
||||
|
||||
libekmfweb.so.$(VERSION): ALL_CFLAGS += -fPIC `curl-config --cflags`
|
||||
libekmfweb.so.$(VERSION): LDLIBS = -ljson-c -lcrypto -lssl `curl-config --libs` -ldl
|
||||
libekmfweb.so.$(VERSION): ALL_CFLAGS += -fPIC `$(CURL_CONFIG) --cflags`
|
||||
libekmfweb.so.$(VERSION): LDLIBS = -ljson-c -lcrypto -lssl `$(CURL_CONFIG) --libs` -ldl
|
||||
libekmfweb.so.$(VERSION): ALL_LDFLAGS += -shared -Wl,--version-script=libekmfweb.map \
|
||||
-Wl,-z,defs,-Bsymbolic -Wl,-soname,libekmfweb.so.$(VERM)
|
||||
libekmfweb.so.$(VERSION): ekmfweb.o utilities.o cca.o $(libs)
|
||||
|
||||
@@ -50,6 +50,9 @@ detect-openssl-version.dep:
|
||||
echo "}" >> $(TMPFILE)
|
||||
mv $(TMPFILE) $@
|
||||
|
||||
CURL_CONFIG ?= curl-config
|
||||
XML2_CONFIG ?= xml2-config
|
||||
|
||||
check-dep-libkmipclient: detect-openssl-version.dep
|
||||
$(call check_dep, \
|
||||
"libkmipclient", \
|
||||
@@ -67,14 +70,14 @@ check-dep-libkmipclient: detect-openssl-version.dep
|
||||
"libxml/tree.h", \
|
||||
"libxml2-devel", \
|
||||
"HAVE_LIBXML2=0", \
|
||||
`xml2-config --cflags` `xml2-config --libs`)
|
||||
`$(XML2_CONFIG) --cflags` `$(XML2_CONFIG) --libs`)
|
||||
$(call check_dep, \
|
||||
"libkmipclient", \
|
||||
"curl/curl.h", \
|
||||
"libcurl-devel", \
|
||||
"HAVE_LIBCURL=0" \
|
||||
`curl-config --cflags` `curl-config --libs`)
|
||||
curl-config --ssl-backends | grep OpenSSL >/dev/null 2>&1 || { echo "Error: libcurl is not built with the OpenSSL backend"; exit 1; }
|
||||
`$(CURL_CONFIG) --cflags` `$(CURL_CONFIG) --libs`)
|
||||
$(CURL_CONFIG) --ssl-backends | grep OpenSSL >/dev/null 2>&1 || { echo "Error: libcurl is not built with the OpenSSL backend"; exit 1; }
|
||||
touch check-dep-libkmipclient
|
||||
|
||||
skip-libkmipclient-openssl:
|
||||
@@ -104,8 +107,8 @@ tls.o: check-dep-libkmipclient tls.c kmip.h utils.h $(rootdir)include/kmipclient
|
||||
names.o: check-dep-libkmipclient names.c names.h utils.h $(rootdir)include/kmipclient/kmipclient.h
|
||||
utils.o: check-dep-libkmipclient utils.c names.h utils.h $(rootdir)include/kmipclient/kmipclient.h
|
||||
|
||||
libkmipclient.so.$(VERSION): ALL_CFLAGS += -fPIC `xml2-config --cflags` `curl-config --cflags`
|
||||
libkmipclient.so.$(VERSION): LDLIBS = -ljson-c -lcrypto -lssl `xml2-config --libs` `curl-config --libs`
|
||||
libkmipclient.so.$(VERSION): ALL_CFLAGS += -fPIC `$(XML2_CONFIG) --cflags` `$(CURL_CONFIG) --cflags`
|
||||
libkmipclient.so.$(VERSION): LDLIBS = -ljson-c -lcrypto -lssl `$(XML2_CONFIG) --libs` `$(CURL_CONFIG) --libs`
|
||||
libkmipclient.so.$(VERSION): ALL_LDFLAGS += -shared -Wl,--version-script=libkmipclient.map \
|
||||
-Wl,-z,defs,-Bsymbolic -Wl,-soname,libkmipclient.so.$(VERM)
|
||||
libkmipclient.so.$(VERSION): kmip.o request.o response.o attribute.o key.o ttlv.o json.o \
|
||||
|
||||
@@ -125,7 +125,7 @@ int kmip_parse_hex(const char *str, bool has_prefix, unsigned char **val,
|
||||
{
|
||||
unsigned char *buf;
|
||||
BIGNUM *b = NULL;
|
||||
int len, rc;
|
||||
int len;
|
||||
|
||||
if (str == NULL)
|
||||
return -EINVAL;
|
||||
@@ -133,18 +133,20 @@ int kmip_parse_hex(const char *str, bool has_prefix, unsigned char **val,
|
||||
if (has_prefix && strncmp(str, "0x", 2) != 0)
|
||||
return -EBADMSG;
|
||||
|
||||
rc = BN_hex2bn(&b, str + (has_prefix ? 2 : 0));
|
||||
if (rc <= 0)
|
||||
len = BN_hex2bn(&b, str + (has_prefix ? 2 : 0));
|
||||
if (len <= 0)
|
||||
return -EBADMSG;
|
||||
if (len < (int)strlen(str) - (has_prefix ? 2 : 0))
|
||||
return -EBADMSG;
|
||||
|
||||
len = BN_num_bytes(b);
|
||||
len = len / 2 + (len % 2 > 0 ? 1 : 0);
|
||||
buf = calloc(1, len);
|
||||
if (buf == NULL) {
|
||||
BN_free(b);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (BN_bn2bin(b, buf) != len) {
|
||||
if (BN_bn2binpad(b, buf, len) != len) {
|
||||
BN_free(b);
|
||||
free(buf);
|
||||
return -EIO;
|
||||
|
||||
@@ -1549,6 +1549,59 @@ int SK_EP11_reencipher_key(const struct sk_ext_ep11_lib *ep11_lib,
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
memcpy(blob, lrb.payload, lrb.pllen);
|
||||
|
||||
/* re-encipher MACed SPKI */
|
||||
rb.domain = domain;
|
||||
lrb.domain = domain;
|
||||
|
||||
resp_len = sizeof(resp);
|
||||
req_len = ep11.dll_xcpa_cmdblock(req, sizeof(req), XCP_ADM_REENCRYPT,
|
||||
&rb, NULL, key_token + hdr->len,
|
||||
key_token_length - hdr->len);
|
||||
if (req_len < 0) {
|
||||
sk_debug(debug, "Failed to build XCP command block");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
rv = ep11.dll_m_admin(resp, &resp_len, NULL, NULL, req, req_len, NULL,
|
||||
0, ep11_lib->target);
|
||||
if (rv != CKR_OK || resp_len == 0) {
|
||||
sk_debug(debug, "Command XCP_ADM_REENCRYPT failed. "
|
||||
"rc = 0x%lx, resp_len = %ld", rv, resp_len);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
rc = ep11.dll_xcpa_internal_rv(resp, resp_len, &lrb, &rv);
|
||||
if (rc != 0) {
|
||||
sk_debug(debug, "Failed to parse response. rc = %d", rc);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (rv != CKR_OK) {
|
||||
sk_debug(debug, "Failed to re-encrypt the EP11 secure key. "
|
||||
"rc = 0x%lx", rv);
|
||||
switch (rv) {
|
||||
case CKR_IBM_WKID_MISMATCH:
|
||||
sk_debug(debug, "The EP11 secure key is currently "
|
||||
"encrypted under a different master that does "
|
||||
"not match the master key in the CURRENT "
|
||||
"master key register of APQN %02X.%04X",
|
||||
card, domain);
|
||||
break;
|
||||
}
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (key_token_length - hdr->len != lrb.pllen) {
|
||||
sk_debug(debug, "Re-encrypted EP11 secure key size has "
|
||||
"changed: org-len: %lu, new-len: %lu",
|
||||
hdr->len - sizeof(*hdr), lrb.pllen);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
memcpy(key_token + hdr->len, lrb.payload, lrb.pllen);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <err.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lib/util_base.h"
|
||||
@@ -91,3 +93,91 @@ void util_print_indented(const char *str, int indent)
|
||||
printf("\n");
|
||||
free(desc_ptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the absolute name of an s390-tools system directory
|
||||
* (it could be data, or library directory)
|
||||
*/
|
||||
static const char *util_sysdir(const char *env_var, const char *default_dir)
|
||||
{
|
||||
return secure_getenv(env_var) ?: default_dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the absolute name of a file installed in the s390-tools
|
||||
* system directory
|
||||
*/
|
||||
static const char *util_sysdir_path(const char *filename, const char *dirname)
|
||||
{
|
||||
static char libdir_pathname[PATH_MAX];
|
||||
int ret;
|
||||
|
||||
ret = snprintf(libdir_pathname, sizeof(libdir_pathname),
|
||||
"%s/%s", dirname, filename);
|
||||
|
||||
if (ret < 0 || ret >= (int)sizeof(libdir_pathname))
|
||||
errx(EXIT_FAILURE,
|
||||
"Could not compose absolute pathname of %s and %s",
|
||||
dirname, filename);
|
||||
return libdir_pathname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the absolute name of a s390-tools library directory
|
||||
*
|
||||
* Resources are handled by the library
|
||||
*
|
||||
* @returns Pointer to a buffer, which contains the null-terminated name
|
||||
*/
|
||||
const char *util_libdir(void)
|
||||
{
|
||||
return util_sysdir("S390TOOLS_LIBDIR", TOOLS_LIBDIR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the absolute name of a file installed in the s390-tools
|
||||
* library directory by its relative name.
|
||||
*
|
||||
* Resources are handled by the library. A second call of this function
|
||||
* overwrites previously returned data. Care must be taken when attempting
|
||||
* to do a second call.
|
||||
*
|
||||
* @param[in] filename Null-terminated file name relative to the s390-tools
|
||||
* library directory
|
||||
* @returns Pointer to a buffer of PATH_MAX size, which contains the
|
||||
* null-terminated absolute name
|
||||
*/
|
||||
const char *util_libdir_path(const char *filename)
|
||||
{
|
||||
return util_sysdir_path(filename, util_libdir());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the absolute name of a s390-tools data directory
|
||||
*
|
||||
* Resources are handled by the library
|
||||
*
|
||||
* @returns Pointer to a buffer which contains the null-terminated name
|
||||
*/
|
||||
const char *util_datadir(void)
|
||||
{
|
||||
return util_sysdir("S390TOOLS_DATADIR", TOOLS_DATADIR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the absolute name of a file installed in the s390-tools
|
||||
* data directory by its relative name.
|
||||
*
|
||||
* Resources are handled by the library. A second call of this function
|
||||
* overwrites previously returned data. Care must be taken when attempting
|
||||
* to do a second call.
|
||||
*
|
||||
* @param[in] filename Null-terminated file name relative to the s390-tools
|
||||
* data directory
|
||||
* @returns Pointer to a buffer of PATH_MAX size, which contains the
|
||||
* null-terminated absolute name
|
||||
*/
|
||||
const char *util_datadir_path(const char *filename)
|
||||
{
|
||||
return util_sysdir_path(filename, util_datadir());
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
include ../common.mak
|
||||
|
||||
SCRIPTS = dbginfo.sh zfcpdbf zipl-switch-to-blscfg scsi_logging_level
|
||||
SCRIPTS = dbginfo.sh zfcpdbf zipl-switch-to-blscfg scsi_logging_level
|
||||
SCRIPTS += sclpdbf
|
||||
MAN_PAGES = dbginfo.sh.1 zfcpdbf.1 zipl-switch-to-blscfg.1
|
||||
|
||||
all:
|
||||
@@ -19,7 +20,7 @@ install:
|
||||
sed -e 's/%S390_TOOLS_VERSION%/$(S390_TOOLS_RELEASE)/' cpictl > \
|
||||
$(DESTDIR)$(TOOLS_LIBDIR)/cpictl
|
||||
chown $(OWNER):$(GROUP) $(DESTDIR)$(TOOLS_LIBDIR)/cpictl
|
||||
chmod 775 $(DESTDIR)$(TOOLS_LIBDIR)/cpictl
|
||||
chmod 755 $(DESTDIR)$(TOOLS_LIBDIR)/cpictl
|
||||
|
||||
@for i in $(MAN_PAGES); \
|
||||
do \
|
||||
|
||||
250
scripts/cpictl
250
scripts/cpictl
@@ -24,6 +24,9 @@ readonly SYSTEM_NAME_PATH="/sys/firmware/cpi/system_name"
|
||||
readonly SYSPLEX_NAME_PATH="/sys/firmware/cpi/sysplex_name"
|
||||
readonly CPI_SET="/sys/firmware/cpi/set"
|
||||
|
||||
# Location of os-release file - can be specified externally for testing purpose
|
||||
readonly OS_RELEASE=${CPI_OS_RELEASE:-"/etc/os-release"}
|
||||
|
||||
declare LEVEL
|
||||
declare TYPE
|
||||
declare NAME
|
||||
@@ -37,6 +40,16 @@ readonly EXIT_FAILURE=1
|
||||
readonly EXIT_ARG_TOO_LONG=3
|
||||
readonly EXIT_INVALID_CHARS=4
|
||||
|
||||
# Distro-IDs as supported by SE/HMC firmware
|
||||
readonly DISTRO_GENERIC=0
|
||||
readonly DISTRO_RHEL=1
|
||||
readonly DISTRO_SLES=2
|
||||
readonly DISTRO_UBUNTU=3
|
||||
readonly DISTRO_FEDORA=4
|
||||
readonly DISTRO_OPENSUSE=5
|
||||
readonly DISTRO_DEBIAN=6
|
||||
readonly DISTRO_RHCOS=7
|
||||
|
||||
print_help_and_exit()
|
||||
{
|
||||
cat <<EndHelp
|
||||
@@ -49,7 +62,8 @@ Configure the Control-Program-Information (CPI) settings.
|
||||
sysplex name with values taken from environment
|
||||
variables
|
||||
-h, --help Print this help, then exit
|
||||
-L, --level LEVEL Set and commit OS level to LEVEL (format xx.yy.zzABCD)
|
||||
-L, --level LEVEL Set and commit OS level to LEVEL. Format: 0x<level> or
|
||||
[[[flags:]distro_id:distro_version:]kernel_version]
|
||||
-N, --name SYSTEM Set and commit the system name to SYSTEM
|
||||
-S, --sysplex SYSPLEX Set and commit the sysplex name to SYSPLEX
|
||||
-T, --type TYPE Set and commit OS type to TYPE
|
||||
@@ -109,7 +123,7 @@ fail_with()
|
||||
|
||||
cpi_commit()
|
||||
{
|
||||
echo 1 > "$CPI_SET"
|
||||
echo 1 > "$CPI_SET" 2> /dev/null
|
||||
}
|
||||
|
||||
do_length_check()
|
||||
@@ -129,15 +143,233 @@ cpi_set_bit()
|
||||
LEVEL=$(printf '0x%x' $((LEVEL | (1 << (63 - $1)) )) )
|
||||
}
|
||||
|
||||
#
|
||||
# split_version - Split generic version string into array of sub-versions
|
||||
#
|
||||
# @version: Version string
|
||||
# @delim: Characters that delimit sub-versions in version string
|
||||
# @num: Number of sub-versions
|
||||
#
|
||||
# Print @num sub-versions of @version where each sub-version is delimited by
|
||||
# any of the characters in @delim. Print 0 in place of non-decimal or missing
|
||||
# sub-versions.
|
||||
#
|
||||
# Examples:
|
||||
# version=10 delim=. num=3 => 10 0 0
|
||||
# version=4.8 delim=. num=2 => 4 8
|
||||
#
|
||||
split_version()
|
||||
{
|
||||
local version="$1" delim="$2" num="$3"
|
||||
local list i subver
|
||||
|
||||
IFS="$delim" read -r -a list <<< "$version"
|
||||
|
||||
for (( i=0; i<num; i++ )) ; do
|
||||
subver="${list[i]:-0}"
|
||||
# Handle non-number sub-versions
|
||||
[[ "$subver" =~ ^[0-9]+$ ]] || subver=0
|
||||
# Force decimal interpretation in case of leading zeroes
|
||||
subver=$(( 10#$subver ))
|
||||
printf "%s " "$subver"
|
||||
done
|
||||
}
|
||||
|
||||
#
|
||||
# split_kver - Split Linux kernel version string into array of sub-versions
|
||||
#
|
||||
# @version: Linux kernel version string
|
||||
# @num: Number of sub-versions
|
||||
#
|
||||
# Print @num sub-versions of the specified kernel @version. Print 0 in place
|
||||
# of non-decimal or missing sub-versions.
|
||||
#
|
||||
# Examples:
|
||||
# version=2.4-13 num=4 => 2 4 0 13
|
||||
# version=3.0.93_3.0.101-0.8.2_0.8.1 num=6 => 3 0 93 3 0 101
|
||||
# version=4.12.14-lp150.11.4 num=5 => 4 12 14 0 11
|
||||
#
|
||||
split_kver()
|
||||
{
|
||||
local version="$1" num="$2"
|
||||
local main extra
|
||||
|
||||
# Separate extra version to handle short main version (e.g. 2.4-13)
|
||||
IFS="-_" read -r main extra <<< "$version"
|
||||
|
||||
split_version "$main" "." $(( num > 3 ? 3 : num ))
|
||||
[[ "$num" -gt 3 ]] && split_version "$extra" ".-_" $(( num - 3 ))
|
||||
}
|
||||
|
||||
#
|
||||
# bytes_to_word - Convert byte array to hexadecimal word
|
||||
#
|
||||
# @bytes: List of numbers representing byte values
|
||||
#
|
||||
# Print a big-endian hexadecimal representation of the word that results from
|
||||
# combining the specified byte values.
|
||||
#
|
||||
bytes_to_word()
|
||||
{
|
||||
printf "0x"
|
||||
printf "%02x" "$@"
|
||||
}
|
||||
|
||||
#
|
||||
# get_system_level - Print system level word for specified distribution version
|
||||
#
|
||||
# @distro: Distro ID (ID value from /etc/os-release)
|
||||
# @ver_str: Distro version string (VERSION_ID from /etc/os-release)
|
||||
# @kver_str: Kernel version string (output of 'uname -r')
|
||||
# @flags: Optional statistics flags
|
||||
#
|
||||
# Print a 64 bit hexadecimal system level in a format as understood by firmware.
|
||||
#
|
||||
# The format is 0xabccddeeeeffgghh, where
|
||||
# - a=statistics flags
|
||||
# - b=distro id
|
||||
# - c=distro major version
|
||||
# - d=distro minor version(s)
|
||||
# - e=kernel sublevel 2
|
||||
# - f=kernel version
|
||||
# - g=kernel patchlevel
|
||||
# - h=kernel sublevel 1
|
||||
#
|
||||
get_system_level()
|
||||
{
|
||||
local distro="$1" ver_str="$2" kver_str="$3" flags="${4:-0}"
|
||||
local distro_id d_major d_minor d_minor2
|
||||
local k_ver k_patchlvl k_sublvl k_sublvl2 bytes=()
|
||||
|
||||
# Extract list of sub-version numbers from version strings
|
||||
read -r d_major d_minor d_minor2 <<< "$(split_version "$ver_str" "._-" 3)"
|
||||
read -r k_ver k_patchlvl k_sublvl k_sublvl2 <<< "$(split_kver "$kver_str" 4)"
|
||||
|
||||
# Handle excessive sublevel numbers consistently
|
||||
if [[ "$k_sublvl2" -gt 65535 ]] ; then
|
||||
k_sublvl2=0
|
||||
fi
|
||||
|
||||
# Apply distro-specific logic
|
||||
case "$distro" in
|
||||
"rhel")
|
||||
distro_id=$DISTRO_RHEL
|
||||
;;
|
||||
|
||||
"sles")
|
||||
distro_id=$DISTRO_SLES
|
||||
;;
|
||||
|
||||
"ubuntu")
|
||||
distro_id=$DISTRO_UBUNTU
|
||||
|
||||
# Encode minor and update version numbers in minor field
|
||||
(( d_minor=((d_minor & 0xf) * 0x10) + (d_minor2 & 0xf) ))
|
||||
;;
|
||||
|
||||
"fedora")
|
||||
distro_id=$DISTRO_FEDORA
|
||||
;;
|
||||
|
||||
"opensuse-leap")
|
||||
distro_id=$DISTRO_OPENSUSE
|
||||
;;
|
||||
|
||||
"debian")
|
||||
distro_id=$DISTRO_DEBIAN
|
||||
;;
|
||||
|
||||
"rhcos")
|
||||
distro_id=$DISTRO_RHCOS
|
||||
;;
|
||||
|
||||
*)
|
||||
distro_id=$DISTRO_GENERIC
|
||||
|
||||
# Reset unsupported fields
|
||||
d_major=0
|
||||
d_minor=0
|
||||
k_sublvl2=0
|
||||
;;
|
||||
esac
|
||||
|
||||
# Assemble byte data
|
||||
(( bytes[0] = (flags & 0xf) * 0x10 + distro_id ))
|
||||
(( bytes[1] = d_major ))
|
||||
(( bytes[2] = d_minor ))
|
||||
(( bytes[3] = (k_sublvl2 / 256) & 0xff ))
|
||||
(( bytes[4] = k_sublvl2 & 0xff ))
|
||||
(( bytes[5] = k_ver ))
|
||||
(( bytes[6] = k_patchlvl ))
|
||||
(( bytes[7] = k_sublvl ))
|
||||
|
||||
# Print as single hex word
|
||||
bytes_to_word "${bytes[@]}"
|
||||
}
|
||||
|
||||
get_distro()
|
||||
{
|
||||
local line ID="linux" VERSION_ID="0" VERSION="" update
|
||||
|
||||
[[ ! -e "$OS_RELEASE" ]] && return
|
||||
|
||||
# Only import required variables
|
||||
while read -r line ; do
|
||||
if [[ "$line" =~ ^ID= ]] || [[ "$line" =~ ^VERSION_ID= ]] ||
|
||||
[[ "$line" =~ ^VERSION= ]] ; then
|
||||
eval "$line"
|
||||
fi
|
||||
done <"$OS_RELEASE"
|
||||
|
||||
if [[ "$ID" == "ubuntu" ]] ; then
|
||||
# Extract update version number only found in VERSION, e.g.
|
||||
# VERSION_ID="18.04" VERSION="18.04.5 LTS"
|
||||
update="${VERSION/*$VERSION_ID/}"
|
||||
update="${update%% *}"
|
||||
VERSION_ID="$VERSION_ID$update"
|
||||
fi
|
||||
|
||||
echo "$ID:$VERSION_ID"
|
||||
}
|
||||
|
||||
cpi_set_oslevel()
|
||||
{
|
||||
local kver=$(echo "${1:-$(uname -r)}" | grep -E -o '^[0-9]+[.][0-9]+[.][0-9]+')
|
||||
local maj=$((${kver%%.*} % 256))
|
||||
local min=${kver#*.}
|
||||
min=$((${min%.*} % 256))
|
||||
local rev=$((${kver##*.} % 256))
|
||||
local hexlevel=$(printf '0x%02x%02x%02x' $maj $min $rev)
|
||||
LEVEL=$(printf '0x%016x' $(((LEVEL & 0xFFFFFFFFFF000000) | hexlevel)))
|
||||
local level="${1:-}"
|
||||
local flags list distro_id distro_ver kver id ver
|
||||
|
||||
if [[ "$level" =~ ^0x ]] && ! [[ "$level" =~ : ]] ; then
|
||||
# Format: level=0x<hex>
|
||||
printf -v LEVEL "0x%016x" "$level" 2>/dev/null ||
|
||||
fail_with "$PRG: Invalid hexadecimal number in $level" \
|
||||
$EXIT_INVALID_CHARS
|
||||
return
|
||||
fi
|
||||
|
||||
# Format: level=[[[flags:]distro_id:distro_ver:]kver]
|
||||
IFS=":" read -r -a list <<< "$level:"
|
||||
kver="${list[*]: -1: 1}"
|
||||
distro_ver="${list[*]: -2: 1}"
|
||||
distro_id="${list[*]: -3: 1}"
|
||||
flags="${list[*]: -4: 1}"
|
||||
|
||||
if [[ -z "$kver" ]] ; then
|
||||
# Use version of currently running kernel
|
||||
kver="$(uname -r)"
|
||||
fi
|
||||
|
||||
if [[ -z "$distro_ver" ]] || [[ -z "$distro_id" ]] ; then
|
||||
# Use distro ID and version from os-release file
|
||||
IFS=":" read -r id ver <<< "$(get_distro)"
|
||||
distro_id=${distro_id:-$id}
|
||||
distro_ver=${distro_ver:-$ver}
|
||||
fi
|
||||
|
||||
if [[ -z "$flags" ]] ; then
|
||||
# Keep statistics flags from current system level
|
||||
flags=$(( (LEVEL >> 60) & 0xf ))
|
||||
fi
|
||||
|
||||
LEVEL=$(get_system_level "$distro_id" "$distro_ver" "$kver" "$flags")
|
||||
}
|
||||
|
||||
cpi_set_type()
|
||||
|
||||
1647
scripts/dbginfo.sh
1647
scripts/dbginfo.sh
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
.TH DBGINFO.SH 1 "April 2021" "s390-tools"
|
||||
.TH DBGINFO.SH 1 "September 2021" "s390-tools"
|
||||
|
||||
.SH NAME
|
||||
dbginfo.sh \- collect runtime, configuration and trace information
|
||||
@@ -14,8 +14,10 @@ for debugging Linux on IBM Z
|
||||
This script collects runtime, configuration and trace information that can
|
||||
be used to debug a Linux on IBM Z instance.
|
||||
For Linux on z/VM, the script also traces information about the z/VM system.
|
||||
KVM or DOCKER data ist collected on a host serving this.
|
||||
|
||||
The debug information is written to a file
|
||||
/tmp/DBGINFO\-<date>\-<time>\-<hostname>\-<processorid>.tgz
|
||||
/<DIRECTORY>/DBGINFO\-<date>\-<time>\-<hostname>\-<processorid>.tgz
|
||||
where [date] and [time] are the date and time when the debug data was
|
||||
collected. [hostname] indicates the hostname of the system the data was
|
||||
collected from. The [processorid] is taken from the processor 0 and indicates
|
||||
@@ -32,7 +34,12 @@ Print version information, then exit.
|
||||
|
||||
.TP
|
||||
\fB\-d <DIRECTORY>\fP, \fB\-\-directory <DIRECTORY>\fP
|
||||
Specify the DIRECTORY where the data collection stores the temporary data and the final archive. The specified directory must already exist. If this parameter is not specified, /tmp is used by default.
|
||||
Specify the DIRECTORY where the data collection stores the temporary data and the final archive.
|
||||
The specified directory must already exist. If this parameter is not specified, /tmp is used by default.
|
||||
|
||||
.TP
|
||||
\fB\-c\fP, \fB\-\-check\fP
|
||||
Print online quick check (not saving any data) - can be combind with a preceding -d option.
|
||||
|
||||
.SH FILES
|
||||
A .tgz file of the form
|
||||
@@ -56,48 +63,57 @@ Copyright IBM Corp. 2002, 2021
|
||||
.PP
|
||||
Hardware platform = s390x
|
||||
.br
|
||||
Kernel version = 5.4.0 (5.4.0-70-generic)
|
||||
.br
|
||||
Runtime environment = z/VM
|
||||
.br
|
||||
Kernel version = 4.18.0 (4.18.0-305.el8.s390x)
|
||||
.br
|
||||
OS version / distro = Red Hat Enterprise Linux 8.4 (Ootpa)
|
||||
.br
|
||||
Date and time of info = 2021-08-26-15-27-58
|
||||
.PP
|
||||
1 of 16: Collecting command output
|
||||
1 of 18: Collecting sysfs
|
||||
.PP
|
||||
2 of 16: Collecting z/VM command output
|
||||
2 of 18: Collecting procfs
|
||||
.PP
|
||||
3 of 16: Collecting procfs
|
||||
3 of 18: Collecting config files
|
||||
.PP
|
||||
4 of 16: Collecting sysfs
|
||||
4 of 18: Collecting command output
|
||||
.PP
|
||||
5 of 16: Collecting log files
|
||||
5 of 18: Collecting hyptop for z/VM - 5s output
|
||||
.PP
|
||||
6 of 16: Collecting config files
|
||||
6 of 18: Collecting z/VM output
|
||||
.PP
|
||||
7 of 16: Collecting osa oat output
|
||||
7 of 18: Collecting network output
|
||||
.PP
|
||||
8 of 16: Collecting ethtool output
|
||||
8 of 18: Collecting osa oat output
|
||||
.PP
|
||||
9 of 16: Collecting tc output
|
||||
9 of 18: Collecting ethtool output
|
||||
.PP
|
||||
10 of 16: Collecting bridge output
|
||||
10 of 18: Collecting Trafic Control output
|
||||
.PP
|
||||
11 of 16: Collecting OpenVSwitch output
|
||||
11 of 18: Collecting bridge output
|
||||
.PP
|
||||
12a of 16: Collecting docker container
|
||||
12b of 16: Collecting docker network
|
||||
12 of 18: Skip OpenVSwitch: ovs-vsctl not available
|
||||
.PP
|
||||
13 of 16: Collecting nvme output
|
||||
13 of 18: Skip KVM: no virsh command
|
||||
.PP
|
||||
14 of 16: Collecting KVM data
|
||||
14 of 18: Collecting docker output
|
||||
.PP
|
||||
15 of 16: Postprocessing
|
||||
15 of 18: Collecting NVME storage output
|
||||
.PP
|
||||
16 of 16: Finalizing: Creating archive with collected data
|
||||
16 of 18: Collecting log files
|
||||
.PP
|
||||
0 logfiles over 50 MB
|
||||
.PP
|
||||
17 of 18: Postprocessing
|
||||
.PP
|
||||
18 of 18: Finalizing: Creating archive with collected data
|
||||
.PP
|
||||
Collected data was saved to:
|
||||
.br
|
||||
>> /data\-collection/DBGINFO\-2021\-04\-20\-14\-00\-07\-host\-012345.tgz <<
|
||||
>> /data\-collection/DBGINFO\-2021\-08\-26\-15\-27\-58\-host\-012345.tgz <<
|
||||
.br
|
||||
Review the collected data before sending to your service organization.
|
||||
Please review all collected data before sending to your service organization.
|
||||
.SH HINTS
|
||||
Run the script with root authority.
|
||||
.br
|
||||
|
||||
1172
scripts/sclpdbf
Executable file
1172
scripts/sclpdbf
Executable file
File diff suppressed because it is too large
Load Diff
@@ -887,14 +887,13 @@ sub print_scsi_common
|
||||
print "FCP rsp inf cod: 0x", substr($rec->[2], 44, 2), "\n";
|
||||
print "FCP rsp IU : ", payload_format(substr($rec->[2], 114, 48));
|
||||
|
||||
if (defined($pay_sns)) {
|
||||
print "Sense len : ", $payload_length, "\n"
|
||||
if ($payload_length);
|
||||
print_payload($payload_length, $pay_sns, "Sense info")
|
||||
}
|
||||
if (defined($pay_riu) && ($payload_length > FCP_RESP_WITH_EXT)) {
|
||||
if ($payload_length && defined($pay_sns)) {
|
||||
print "Sense len : ", $payload_length, "\n";
|
||||
print_payload($payload_length, $pay_sns, "Sense info");
|
||||
} elsif ($payload_length) {
|
||||
print "FCP rsp IU len : ", $payload_length, "\n";
|
||||
print_payload($payload_length, $pay_riu, "FCP rsp IU all");
|
||||
print_payload($payload_length, $pay_riu, "FCP rsp IU all")
|
||||
if ($payload_length > FCP_RESP_WITH_EXT);
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
|
||||
@@ -1863,7 +1863,6 @@ static void close_ur_device(struct vmur *info)
|
||||
}
|
||||
cpcmd_cs(cmd, &response, &cprc, 0);
|
||||
memcpy(spoolid, &response[9], 4);
|
||||
free(response);
|
||||
if (cprc == 439) {
|
||||
if (info->action == PUNCH)
|
||||
sprintf(cmd, "PURGE * PUN %s", spoolid);
|
||||
@@ -1872,7 +1871,10 @@ static void close_ur_device(struct vmur *info)
|
||||
cpcmd(cmd, NULL, NULL, 0);
|
||||
ERR_EXIT("User %s spool fileid limit exceeded.\n"
|
||||
, info->user);
|
||||
} else if (cprc) {
|
||||
ERR_EXIT("CP command failed with rc=%i\n%s\n", cprc, response);
|
||||
}
|
||||
free(response);
|
||||
if (info->rdr_specified) {
|
||||
if (info->node_specified)
|
||||
printf("Reader file with spoolid %s created and "
|
||||
|
||||
@@ -463,7 +463,15 @@ static int fill_io_devid(struct util_rec *rec, char *path)
|
||||
util_scandir_free(de_vec, count);
|
||||
return 1;
|
||||
}
|
||||
strncpy(buf, "none", sizeof(buf));
|
||||
if (util_file_read_line(buf, sizeof(buf), "%s/dev_busid",
|
||||
path) == 0) {
|
||||
if (cmd.opt_short) {
|
||||
if (strncmp(buf, "0.0.", PREFIX_ID_LENGTH) != 0)
|
||||
return 1;
|
||||
memmove(buf, buf + PREFIX_ID_LENGTH,
|
||||
(sizeof(buf) - PREFIX_ID_LENGTH));
|
||||
}
|
||||
}
|
||||
fill_device_info(rec, NULL, NULL);
|
||||
}
|
||||
if (cmd.opt_uppercase)
|
||||
|
||||
@@ -203,6 +203,7 @@ $(readlink -e $HOST_PATH)/-"
|
||||
|
||||
read ONLINE < $ADAPTER_PATH/online
|
||||
read AVAILABILITY < $ADAPTER_PATH/availability
|
||||
[ -r "$SYSFS/class/fc_host/$SCSI_HOST/port_state" ] && read PORT_STATE < "$SYSFS/class/fc_host/$SCSI_HOST/port_state"
|
||||
[ -r $ADAPTER_PATH/failed ] && read FAILED < $ADAPTER_PATH/failed
|
||||
DEFUNCT=${ADAPTER_PATH%/*} # strip devbusid
|
||||
DEFUNCT=${DEFUNCT##*/} # basename
|
||||
@@ -212,6 +213,8 @@ $(readlink -e $HOST_PATH)/-"
|
||||
HOSTMARKER=" defunct"
|
||||
elif [ "$ONLINE" = "0" ]; then
|
||||
HOSTMARKER=" offline"
|
||||
elif [ "$PORT_STATE" = "Linkdown" ]; then
|
||||
HOSTMARKER=" linkdown"
|
||||
elif [ "$FAILED" = "1" ]; then
|
||||
HOSTMARKER=" failed"
|
||||
elif [ "$AVAILABILITY" != "good" ]; then
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" lszcrypt.8
|
||||
.\"
|
||||
.\" Copyright 2019 IBM Corp.
|
||||
.\" Copyright IBM Corp. 2019, 2022
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
@@ -10,13 +10,14 @@
|
||||
.\" nroff -man lszcrypt.8
|
||||
.\" to process this source
|
||||
.\"
|
||||
.TH LSZCRYPT 8 "OCT 2020" "s390-tools"
|
||||
.TH LSZCRYPT 8 "FEB 2022" "s390-tools"
|
||||
.SH NAME
|
||||
lszcrypt \- display zcrypt device and configuration information
|
||||
.SH SYNOPSIS
|
||||
.TP 9
|
||||
.B lszcrypt
|
||||
.RB "[ " -V " ] "
|
||||
.RB "[" <filteroptions> "]"
|
||||
.RB "[" -V "]"
|
||||
[
|
||||
.I <device-id>
|
||||
[...]]
|
||||
@@ -31,7 +32,12 @@ lszcrypt \- display zcrypt device and configuration information
|
||||
.TP
|
||||
.B lszcrypt -h
|
||||
.TP
|
||||
.B lszcrypt -s
|
||||
.TP
|
||||
.B lszcrypt -v
|
||||
. TP
|
||||
.B <filteroptions>
|
||||
[--accelonly|--ccaonly|--ep11only] [--cardonly|--queueonly]
|
||||
.SH DESCRIPTION
|
||||
The
|
||||
.B lszcrypt
|
||||
@@ -109,16 +115,39 @@ B - indicate both (control and usage domain)
|
||||
.B -h, --help
|
||||
Displays help text and exits.
|
||||
.TP 8
|
||||
.B -s, --serial
|
||||
Shows the serial numbers for CCA and EP11 crypto cards.
|
||||
.TP 8
|
||||
.B -v, --version
|
||||
Displays version information and exits.
|
||||
.TP 8
|
||||
.B --accelonly
|
||||
Show only information for cards/queues in Accelerator mode.
|
||||
.TP 8
|
||||
.B --ccaonly
|
||||
Show only information for cards/queues in CCA-Coprocessor mode.
|
||||
.TP 8
|
||||
.B --ep11only
|
||||
Show only information for cards/queues in EP11-Coprocessor mode.
|
||||
.TP 8
|
||||
.B --cardonly
|
||||
Show only information for cards but no queue info.
|
||||
.TP 8
|
||||
.B --queueonly
|
||||
Show only information for queues but no card info.
|
||||
.SH LISTING DETAILS
|
||||
Here is an explanation of the columns displayed. Please note that some
|
||||
of the columns show up in verbose mode only.
|
||||
.TP
|
||||
.B CARD.DOM
|
||||
The crypto card number in hexadecimal for a crypto card line or
|
||||
the crypto card number and the domain id both in hex separated by a single
|
||||
dot for a queue line.
|
||||
.TP
|
||||
.B TYPE and HWTYPE
|
||||
The HWTYPE is a numeric value showing which type of hardware the zcrypt
|
||||
device driver presumes that this crypto card is. The currently known values
|
||||
are 7=CEX3C, 8=CEX3A, 10=CEX4, 11=CEX5, 12=CEX6 and 13=CEX7.
|
||||
are 7=CEX3C, 8=CEX3A, 10=CEX4, 11=CEX5, 12=CEX6, 13=CEX7 and 14=CEX8.
|
||||
.br
|
||||
The TYPE is a human readable value showing the hardware type and the basic
|
||||
function type (A=Accelerator, C=CCA Coprocessor, P=EP11 Coprocessor). So
|
||||
@@ -145,9 +174,19 @@ may be adjusted on the HMC or SE. The chzcrypt application can also
|
||||
trigger this state with the --config-on and --config-off options.
|
||||
.br
|
||||
lszcrypt shows 'online' when a card or queue is available for
|
||||
cryptograhic operations. 'offline' is displayed when a card or queue
|
||||
cryptographic operations. 'offline' is displayed when a card or queue
|
||||
is switched to (software) offline. If a card is 'deconfigured' via
|
||||
HMC, SE or chzcrypt the field shows 'deconfig'.
|
||||
.br
|
||||
A crypto card may also reach a 'checkstopped' state. lszcrypt shows
|
||||
this as 'chkstop'.
|
||||
.br
|
||||
If a queue is not bound to a device driver there is no detailed
|
||||
information available and thus the status shows only '-'.
|
||||
.br
|
||||
If a queue is bound to the vfio-ap device driver it is up to this driver
|
||||
to give some status information and what exactly this means. So lszcrypt
|
||||
shows the text retrieved from the underlying sysfs attribute here.
|
||||
.TP
|
||||
.B REQUESTS
|
||||
This is the counter value of successful processed requests on card or queue
|
||||
@@ -168,7 +207,7 @@ S - APSC available: card/queue can handle requests with the special bit
|
||||
enabled.
|
||||
.br
|
||||
M - Accelerator card/queue with support for RSA ME with up to 4k key size.
|
||||
.br
|
||||
.Sr
|
||||
C - Accelerator card/queue with support for RSA CRT with up to 4k key size.
|
||||
.br
|
||||
D - Card/queue is providing CCA functions (this is the CCA Coprocessor mode).
|
||||
@@ -192,12 +231,17 @@ operations within the guests.
|
||||
.B DRIVER
|
||||
.br
|
||||
Shows which card or queue device driver currently handles this crypto
|
||||
resource. Currently known drivers are cex4card/cex4queue (CEX4-CEX7
|
||||
resource. Currently known drivers are cex4card/cex4queue (CEX4-CEX8
|
||||
hardware), cex2card/cex2cqueue (CEX2C and CEX3C hardware),
|
||||
cex2acard/cex2aqueue (CEX2A and CEX3A hardware) and vfio_ap (queue reserved
|
||||
for use by kvm hypervisor for kvm guests and not accessible to host
|
||||
applications). It is also valid to have no driver handling a queue which is
|
||||
shown as a -no-driver- entry.
|
||||
.SH NOTES
|
||||
Use only one of the mode filtering options --accelonly, --ccaonly, --ep11only.
|
||||
Same with card/queue filtering: Use only one of --cardonly, --queueonly.
|
||||
However, one of the mode filtering options and one of the card/queue filtering
|
||||
can be combined.
|
||||
.SH EXAMPLES
|
||||
.TP
|
||||
.B lszcrypt
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* lszcrypt - Display zcrypt devices and configuration settings
|
||||
*
|
||||
* Copyright IBM Corp. 2008, 2020
|
||||
* Copyright IBM Corp. 2008, 2022
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -22,11 +22,18 @@
|
||||
#include "lib/util_scandir.h"
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
#include "misc.h"
|
||||
|
||||
/*
|
||||
* Private data
|
||||
*/
|
||||
static struct lszcrypt_l {
|
||||
int verbose;
|
||||
int showaccel;
|
||||
int showcca;
|
||||
int showep11;
|
||||
int showcard;
|
||||
int showqueue;
|
||||
} l;
|
||||
|
||||
/*
|
||||
@@ -37,6 +44,7 @@ static struct lszcrypt_l {
|
||||
#define CAP_CCA "CCA Secure Key"
|
||||
#define CAP_RNG "Long RNG"
|
||||
#define CAP_EP11 "EP11 Secure Key"
|
||||
#define CAP_APMMS "AP bus max message size limit %ld Kb"
|
||||
|
||||
/*
|
||||
* Card types
|
||||
@@ -93,6 +101,13 @@ static const struct util_prg prg = {
|
||||
/*
|
||||
* Configuration of command line options
|
||||
*/
|
||||
|
||||
#define OPT_ACCELONLY 0x81
|
||||
#define OPT_CCAONLY 0x82
|
||||
#define OPT_EP11ONLY 0x83
|
||||
#define OPT_CARDONLY 0x84
|
||||
#define OPT_QUEUEONLY 0x85
|
||||
|
||||
static struct util_opt opt_vec[] = {
|
||||
{
|
||||
.option = {"bus", 0, NULL, 'b'},
|
||||
@@ -111,6 +126,35 @@ static struct util_opt opt_vec[] = {
|
||||
.option = {"verbose", 0, NULL, 'V'},
|
||||
.desc = "Print verbose messages",
|
||||
},
|
||||
{
|
||||
.option = {"accelonly", 0, NULL, OPT_ACCELONLY},
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
.desc = "Show only information from cards/queues in Accelerator mode",
|
||||
},
|
||||
{
|
||||
.option = {"ccaonly", 0, NULL, OPT_CCAONLY},
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
.desc = "Show only information from cards/queues in CCA-Coprocessor mode",
|
||||
},
|
||||
{
|
||||
.option = {"ep11only", 0, NULL, OPT_EP11ONLY},
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
.desc = "Show only information from cards/queues in EP11-Coprocessor mode",
|
||||
},
|
||||
{
|
||||
.option = {"cardonly", 0, NULL, OPT_CARDONLY},
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
.desc = "Show only information from cards but no queue info",
|
||||
},
|
||||
{
|
||||
.option = {"queueonly", 0, NULL, OPT_QUEUEONLY},
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
.desc = "Show only information from queues but no card info",
|
||||
},
|
||||
{
|
||||
.option = {"serial", 0, NULL, 's'},
|
||||
.desc = "Show the serial numbers for CCA and EP11 crypto cards",
|
||||
},
|
||||
UTIL_OPT_HELP,
|
||||
UTIL_OPT_VERSION,
|
||||
UTIL_OPT_END
|
||||
@@ -245,13 +289,94 @@ static void show_domains(void)
|
||||
show_domains_util_rec(domain_array);
|
||||
}
|
||||
|
||||
/*
|
||||
* Show serialnumbers
|
||||
*/
|
||||
static void show_serialnumbers(void)
|
||||
{
|
||||
struct util_rec *rec = util_rec_new_wide("-");
|
||||
struct dirent **dev_vec;
|
||||
int i, count;
|
||||
char *ap, *path, *device, *grp_dev, card[16], buf[256];
|
||||
long config = -1, online = -1, chkstop = -1;
|
||||
unsigned long facility;
|
||||
|
||||
/* check if ap driver is available */
|
||||
ap = util_path_sysfs("bus/ap");
|
||||
if (!util_path_is_dir(ap))
|
||||
errx(EXIT_FAILURE, "Crypto device driver not available.");
|
||||
|
||||
/* define the record */
|
||||
util_rec_def(rec, "card", UTIL_REC_ALIGN_LEFT, 8, "CARD.DOM");
|
||||
util_rec_def(rec, "type", UTIL_REC_ALIGN_LEFT, 5, "TYPE");
|
||||
util_rec_def(rec, "mode", UTIL_REC_ALIGN_LEFT, 11, "MODE");
|
||||
util_rec_def(rec, "status", UTIL_REC_ALIGN_LEFT, 10, "STATUS");
|
||||
util_rec_def(rec, "serialnr", UTIL_REC_ALIGN_LEFT, 8, "SERIALNR");
|
||||
|
||||
/* scan the devices */
|
||||
path = util_path_sysfs("devices/ap/");
|
||||
count = util_scandir(&dev_vec, alphasort, path, "card[0-9a-fA-F]+");
|
||||
if (count < 1)
|
||||
errx(EXIT_FAILURE, "No crypto card devices found.");
|
||||
util_rec_print_hdr(rec);
|
||||
for (i = 0; i < count; i++) {
|
||||
device = dev_vec[i]->d_name;
|
||||
grp_dev = util_path_sysfs("devices/ap/%s", device);
|
||||
if (!util_path_is_dir(grp_dev))
|
||||
errx(EXIT_FAILURE, "Error - cryptographic device %s does not exist.", device);
|
||||
if (!util_path_is_readable("%s/type", grp_dev) ||
|
||||
!util_path_is_readable("%s/online", grp_dev))
|
||||
goto next;
|
||||
strcpy(card, device + 4);
|
||||
util_rec_set(rec, "card", card);
|
||||
util_file_read_line(buf, sizeof(buf), "%s/type", grp_dev);
|
||||
util_rec_set(rec, "type", buf);
|
||||
util_file_read_ul(&facility, 16, "%s/ap_functions", grp_dev);
|
||||
if (facility & MASK_COPRO)
|
||||
util_rec_set(rec, "mode", "CCA-Coproc");
|
||||
else if (facility & MASK_EP11)
|
||||
util_rec_set(rec, "mode", "EP11-Coproc");
|
||||
else
|
||||
goto next;
|
||||
if (util_path_is_readable("%s/config", grp_dev))
|
||||
util_file_read_l(&config, 10, "%s/config", grp_dev);
|
||||
if (util_path_is_readable("%s/chkstop", grp_dev))
|
||||
util_file_read_l(&chkstop, 10, "%s/chkstop", grp_dev);
|
||||
if (util_path_is_readable("%s/online", grp_dev))
|
||||
util_file_read_l(&online, 10, "%s/online", grp_dev);
|
||||
if (config == 0) {
|
||||
util_rec_set(rec, "status", "deconfig");
|
||||
} else {
|
||||
if (chkstop > 0)
|
||||
util_rec_set(rec, "status", "chkstop");
|
||||
else if (online > 0)
|
||||
util_rec_set(rec, "status", "online");
|
||||
else if (online == 0)
|
||||
util_rec_set(rec, "status", "offline");
|
||||
else
|
||||
util_rec_set(rec, "status", "-");
|
||||
}
|
||||
if (util_file_read_line(buf, sizeof(buf), "%s/serialnr", grp_dev))
|
||||
util_rec_set(rec, "serialnr", "-");
|
||||
else {
|
||||
buf[8] = '\0';
|
||||
util_rec_set(rec, "serialnr", buf);
|
||||
}
|
||||
util_rec_print(rec);
|
||||
next:
|
||||
free(grp_dev);
|
||||
}
|
||||
|
||||
free(path);
|
||||
}
|
||||
|
||||
/*
|
||||
* Show capability
|
||||
*/
|
||||
static void show_capability(const char *id_str)
|
||||
{
|
||||
unsigned long func_val;
|
||||
long hwtype, id;
|
||||
long hwtype, id, max_msg_size;
|
||||
char *p, *ap, *dev, card[16], cbuf[256];
|
||||
|
||||
/* check if ap driver is available */
|
||||
@@ -270,6 +395,9 @@ static void show_capability(const char *id_str)
|
||||
/* If sysfs attribute is missing, set functions to 0 */
|
||||
if (util_file_read_ul(&func_val, 16, "%s/ap_functions", dev))
|
||||
func_val = 0x00000000;
|
||||
/* try to read the ap bus max message size for this card */
|
||||
if (util_file_read_l(&max_msg_size, 10, "%s/max_msg_size", dev))
|
||||
max_msg_size = 0;
|
||||
/* Skip devices, which are not supported by zcrypt layer */
|
||||
if (!util_path_is_readable("%s/type", dev) ||
|
||||
!util_path_is_readable("%s/online", dev)) {
|
||||
@@ -287,9 +415,9 @@ static void show_capability(const char *id_str)
|
||||
case 6:
|
||||
case 8:
|
||||
if (func_val & MASK_RSA4K)
|
||||
printf("%s", CAP_RSA4K);
|
||||
printf("%s\n", CAP_RSA4K);
|
||||
else
|
||||
printf("%s", CAP_RSA2K);
|
||||
printf("%s\n", CAP_RSA2K);
|
||||
break;
|
||||
case 7:
|
||||
case 9:
|
||||
@@ -298,37 +426,75 @@ static void show_capability(const char *id_str)
|
||||
printf("%s (%s)\n", CAP_CCA, cbuf);
|
||||
else
|
||||
printf("%s\n", CAP_CCA);
|
||||
printf("%s", CAP_RNG);
|
||||
printf("%s\n", CAP_RNG);
|
||||
break;
|
||||
case 10: /* CEX4S */
|
||||
case 11: /* CEX5S */
|
||||
case 12: /* CEX6S */
|
||||
case 13: /* CEX7S */
|
||||
case 14: /* CEX8S */
|
||||
if (func_val & MASK_ACCEL) {
|
||||
if (func_val & MASK_RSA4K)
|
||||
printf("%s", CAP_RSA4K);
|
||||
printf("%s\n", CAP_RSA4K);
|
||||
else
|
||||
printf("%s", CAP_RSA2K);
|
||||
printf("%s\n", CAP_RSA2K);
|
||||
} else if (func_val & MASK_COPRO) {
|
||||
printf("%s\n", CAP_RSA4K);
|
||||
if (cbuf[0])
|
||||
printf("%s (%s)\n", CAP_CCA, cbuf);
|
||||
else
|
||||
printf("%s\n", CAP_CCA);
|
||||
printf("%s", CAP_RNG);
|
||||
printf("%s\n", CAP_RNG);
|
||||
} else if (func_val & MASK_EP11) {
|
||||
printf("%s", CAP_EP11);
|
||||
printf("%s\n", CAP_EP11);
|
||||
} else {
|
||||
printf("Detailed capability information for %s (hardware type %ld) is not available.",
|
||||
printf("Detailed capability information for %s (hardware type %ld) is not available.\n",
|
||||
card, hwtype);
|
||||
}
|
||||
if (max_msg_size > 0)
|
||||
printf(CAP_APMMS "\n", max_msg_size / 1024);
|
||||
break;
|
||||
default:
|
||||
printf("Detailed capability information for %s (hardware type %ld) is not available.",
|
||||
card, hwtype);
|
||||
printf("Detailed capability information for %s (hardware type %ld) is not available.\n",
|
||||
card, hwtype);
|
||||
break;
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Read driver entry in dir or in dir/subdir and store driver into buf.
|
||||
* Returns:
|
||||
* 0 if there is no driver link (no driver bound to this device or error)
|
||||
* 1 if one of the cexXcard or cexXqueue driver is bound to this device
|
||||
* 2 if the vfio is bound to this device
|
||||
*/
|
||||
static int read_driver(const char *dir, const char *subdir, char *buf, size_t buflen)
|
||||
{
|
||||
char drvlink[PATH_MAX], linktarget[PATH_MAX];
|
||||
char *p, driver[256];
|
||||
|
||||
if (subdir)
|
||||
snprintf(drvlink, sizeof(drvlink), "%s/%s/driver", dir, subdir);
|
||||
else
|
||||
snprintf(drvlink, sizeof(drvlink), "%s/driver", dir);
|
||||
drvlink[sizeof(drvlink) - 1] = '\0';
|
||||
|
||||
memset(linktarget, 0, sizeof(linktarget));
|
||||
|
||||
if (readlink(drvlink, linktarget, sizeof(linktarget)) > 0) {
|
||||
p = strrchr(linktarget, '/');
|
||||
if (p) {
|
||||
strncpy(driver, p + 1, sizeof(driver));
|
||||
driver[sizeof(driver) - 1] = '\0';
|
||||
strncpy(buf, driver, buflen);
|
||||
if (misc_regex_match(driver, "cex[0-9](card|queue)"))
|
||||
return 1;
|
||||
else if (misc_regex_match(driver, "vfio.ap"))
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -337,7 +503,7 @@ static void show_capability(const char *id_str)
|
||||
static void read_subdev_rec_default(struct util_rec *rec, const char *grp_dev,
|
||||
const char *sub_dev)
|
||||
{
|
||||
long value;
|
||||
long config = -1, online = -1, chkstop = -1;
|
||||
char buf[256];
|
||||
unsigned long facility;
|
||||
|
||||
@@ -346,24 +512,33 @@ static void read_subdev_rec_default(struct util_rec *rec, const char *grp_dev,
|
||||
else
|
||||
util_rec_set(rec, "type", buf);
|
||||
|
||||
if (util_path_is_readable("%s/%s/online", grp_dev, sub_dev)) {
|
||||
util_file_read_l(&value, 10, "%s/%s/online", grp_dev, sub_dev);
|
||||
if (value > 0)
|
||||
util_rec_set(rec, "online", "online");
|
||||
else {
|
||||
/* device is offline, check config (if available) */
|
||||
if (util_path_is_readable("%s/%s/config", grp_dev, sub_dev)) {
|
||||
util_file_read_l(&value, 10, "%s/%s/config", grp_dev, sub_dev);
|
||||
if (value > 0)
|
||||
util_rec_set(rec, "online", "offline");
|
||||
else
|
||||
util_rec_set(rec, "online", "deconfig");
|
||||
} else
|
||||
util_rec_set(rec, "online", "offline");
|
||||
}
|
||||
if (util_path_is_readable("%s/%s/config", grp_dev, sub_dev))
|
||||
util_file_read_l(&config, 10, "%s/%s/config", grp_dev, sub_dev);
|
||||
if (util_path_is_readable("%s/%s/chkstop", grp_dev, sub_dev))
|
||||
util_file_read_l(&chkstop, 10, "%s/%s/chkstop", grp_dev, sub_dev);
|
||||
if (util_path_is_readable("%s/%s/online", grp_dev, sub_dev))
|
||||
util_file_read_l(&online, 10, "%s/%s/online", grp_dev, sub_dev);
|
||||
|
||||
util_rec_set(rec, "status", "-");
|
||||
if (config == 0) {
|
||||
util_rec_set(rec, "status", "deconfig");
|
||||
} else {
|
||||
/* no online attribute */
|
||||
util_rec_set(rec, "online", "-");
|
||||
if (chkstop > 0)
|
||||
util_rec_set(rec, "status", "chkstop");
|
||||
else if (online > 0)
|
||||
util_rec_set(rec, "status", "online");
|
||||
else if (online == 0)
|
||||
util_rec_set(rec, "status", "offline");
|
||||
else {
|
||||
/* no online attribute, maybe use status attribute */
|
||||
if (util_path_is_readable("%s/%s/status",
|
||||
grp_dev, sub_dev)) {
|
||||
util_file_read_line(buf, sizeof(buf),
|
||||
"%s/%s/status",
|
||||
grp_dev, sub_dev);
|
||||
util_rec_set(rec, "status", buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
util_file_read_ul(&facility, 16, "%s/ap_functions", grp_dev);
|
||||
@@ -376,9 +551,13 @@ static void read_subdev_rec_default(struct util_rec *rec, const char *grp_dev,
|
||||
else
|
||||
util_rec_set(rec, "mode", "Unknown");
|
||||
|
||||
util_file_read_line(buf, sizeof(buf), "%s/%s/request_count",
|
||||
grp_dev, sub_dev);
|
||||
util_rec_set(rec, "requests", buf);
|
||||
if (config == 0) {
|
||||
util_rec_set(rec, "requests", "-");
|
||||
} else {
|
||||
util_file_read_line(buf, sizeof(buf), "%s/%s/request_count",
|
||||
grp_dev, sub_dev);
|
||||
util_rec_set(rec, "requests", buf);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -387,19 +566,29 @@ static void read_subdev_rec_default(struct util_rec *rec, const char *grp_dev,
|
||||
static void read_subdev_rec_verbose(struct util_rec *rec, const char *grp_dev,
|
||||
const char *sub_dev)
|
||||
{
|
||||
int i;
|
||||
int i, drvinfo;
|
||||
unsigned long facility;
|
||||
char buf[256], afile[PATH_MAX];
|
||||
long depth, pending1, pending2;
|
||||
char buf[256];
|
||||
long depth, pending1, pending2, config = -1;
|
||||
|
||||
if (l.verbose == 0)
|
||||
return;
|
||||
|
||||
util_file_read_l(&pending1, 10, "%s/%s/pendingq_count",
|
||||
grp_dev, sub_dev);
|
||||
util_file_read_l(&pending2, 10, "%s/%s/requestq_count",
|
||||
grp_dev, sub_dev);
|
||||
util_rec_set(rec, "pending", "%ld", pending1 + pending2);
|
||||
drvinfo = read_driver(grp_dev, sub_dev, buf, sizeof(buf));
|
||||
util_rec_set(rec, "driver", drvinfo > 0 ? buf : "-no-driver-");
|
||||
|
||||
if (util_path_is_readable("%s/config", grp_dev))
|
||||
util_file_read_l(&config, 10, "%s/config", grp_dev);
|
||||
|
||||
if (config == 0 || drvinfo != 1) {
|
||||
util_rec_set(rec, "pending", "-");
|
||||
} else {
|
||||
util_file_read_l(&pending1, 10, "%s/%s/pendingq_count",
|
||||
grp_dev, sub_dev);
|
||||
util_file_read_l(&pending2, 10, "%s/%s/requestq_count",
|
||||
grp_dev, sub_dev);
|
||||
util_rec_set(rec, "pending", "%ld", pending1 + pending2);
|
||||
}
|
||||
|
||||
util_file_read_line(buf, sizeof(buf), "%s/hwtype", grp_dev);
|
||||
util_rec_set(rec, "hwtype", buf);
|
||||
@@ -412,14 +601,6 @@ static void read_subdev_rec_verbose(struct util_rec *rec, const char *grp_dev,
|
||||
buf[i] = facility & fac_bits[i].mask ? fac_bits[i].c : '-';
|
||||
buf[i] = '\0';
|
||||
util_rec_set(rec, "facility", buf);
|
||||
|
||||
snprintf(afile, sizeof(afile), "%s/%s/driver", grp_dev, sub_dev);
|
||||
afile[sizeof(afile) - 1] = '\0';
|
||||
memset(buf, 0, sizeof(buf));
|
||||
if (readlink(afile, buf, sizeof(buf)) > 0)
|
||||
util_rec_set(rec, "driver", strrchr(buf, '/') + 1);
|
||||
else
|
||||
util_rec_set(rec, "driver", "-no-driver-");
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -440,6 +621,9 @@ static void show_subdevice(struct util_rec *rec, const char *grp_dev,
|
||||
!util_path_is_readable("%s/%s/online", grp_dev, sub_dev)))
|
||||
return;
|
||||
|
||||
if (!l.showqueue)
|
||||
return;
|
||||
|
||||
util_rec_set(rec, "card", sub_dev);
|
||||
read_subdev_rec_default(rec, grp_dev, sub_dev);
|
||||
read_subdev_rec_verbose(rec, grp_dev, sub_dev);
|
||||
@@ -467,7 +651,7 @@ static void show_subdevices(struct util_rec *rec, const char *grp_dev)
|
||||
*/
|
||||
static void read_rec_default(struct util_rec *rec, const char *grp_dev)
|
||||
{
|
||||
long value;
|
||||
long config = -1, online = -1, chkstop = -1;
|
||||
char buf[256];
|
||||
unsigned long facility;
|
||||
|
||||
@@ -486,24 +670,31 @@ static void read_rec_default(struct util_rec *rec, const char *grp_dev)
|
||||
else
|
||||
util_rec_set(rec, "mode", "Unknown");
|
||||
|
||||
if (util_path_is_readable("%s/online", grp_dev)) {
|
||||
util_file_read_l(&value, 10, "%s/online", grp_dev);
|
||||
if (value > 0)
|
||||
util_rec_set(rec, "online", "online");
|
||||
else {
|
||||
if (util_path_is_readable("%s/config", grp_dev)) {
|
||||
util_file_read_l(&value, 10, "%s/config", grp_dev);
|
||||
if (value > 0)
|
||||
util_rec_set(rec, "online", "offline");
|
||||
else
|
||||
util_rec_set(rec, "online", "deconfig");
|
||||
} else
|
||||
util_rec_set(rec, "online", "offline");
|
||||
}
|
||||
if (util_path_is_readable("%s/config", grp_dev))
|
||||
util_file_read_l(&config, 10, "%s/config", grp_dev);
|
||||
if (util_path_is_readable("%s/chkstop", grp_dev))
|
||||
util_file_read_l(&chkstop, 10, "%s/chkstop", grp_dev);
|
||||
if (util_path_is_readable("%s/online", grp_dev))
|
||||
util_file_read_l(&online, 10, "%s/online", grp_dev);
|
||||
if (config == 0) {
|
||||
util_rec_set(rec, "status", "deconfig");
|
||||
} else {
|
||||
if (chkstop > 0)
|
||||
util_rec_set(rec, "status", "chkstop");
|
||||
else if (online > 0)
|
||||
util_rec_set(rec, "status", "online");
|
||||
else if (online == 0)
|
||||
util_rec_set(rec, "status", "offline");
|
||||
else
|
||||
util_rec_set(rec, "status", "-");
|
||||
}
|
||||
|
||||
util_file_read_line(buf, sizeof(buf), "%s/request_count", grp_dev);
|
||||
util_rec_set(rec, "requests", buf);
|
||||
if (config == 0) {
|
||||
util_rec_set(rec, "requests", "-");
|
||||
} else {
|
||||
util_file_read_line(buf, sizeof(buf), "%s/request_count", grp_dev);
|
||||
util_rec_set(rec, "requests", buf);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -513,15 +704,22 @@ static void read_rec_verbose(struct util_rec *rec, const char *grp_dev)
|
||||
{
|
||||
int i;
|
||||
unsigned long facility;
|
||||
char buf[256], afile[PATH_MAX];
|
||||
long depth, pending1, pending2;
|
||||
char buf[256];
|
||||
long depth, pending1, pending2, config = -1;
|
||||
|
||||
if (l.verbose == 0)
|
||||
return;
|
||||
|
||||
util_file_read_l(&pending1, 10, "%s/pendingq_count", grp_dev);
|
||||
util_file_read_l(&pending2, 10, "%s/requestq_count", grp_dev);
|
||||
util_rec_set(rec, "pending", "%ld", pending1 + pending2);
|
||||
if (util_path_is_readable("%s/config", grp_dev))
|
||||
util_file_read_l(&config, 10, "%s/config", grp_dev);
|
||||
|
||||
if (config == 0) {
|
||||
util_rec_set(rec, "pending", "-");
|
||||
} else {
|
||||
util_file_read_l(&pending1, 10, "%s/pendingq_count", grp_dev);
|
||||
util_file_read_l(&pending2, 10, "%s/requestq_count", grp_dev);
|
||||
util_rec_set(rec, "pending", "%ld", pending1 + pending2);
|
||||
}
|
||||
|
||||
util_file_read_line(buf, sizeof(buf), "%s/hwtype", grp_dev);
|
||||
util_rec_set(rec, "hwtype", buf);
|
||||
@@ -535,13 +733,8 @@ static void read_rec_verbose(struct util_rec *rec, const char *grp_dev)
|
||||
buf[i] = '\0';
|
||||
util_rec_set(rec, "facility", buf);
|
||||
|
||||
snprintf(afile, sizeof(afile), "%s/driver", grp_dev);
|
||||
afile[sizeof(afile) - 1] = '\0';
|
||||
memset(buf, 0, sizeof(buf));
|
||||
if (readlink(afile, buf, sizeof(buf)) > 0)
|
||||
util_rec_set(rec, "driver", strrchr(buf, '/') + 1);
|
||||
else
|
||||
util_rec_set(rec, "driver", "-no-driver-");
|
||||
i = read_driver(grp_dev, NULL, buf, sizeof(buf));
|
||||
util_rec_set(rec, "driver", i > 0 ? buf : "-no-driver-");
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -549,7 +742,7 @@ static void read_rec_verbose(struct util_rec *rec, const char *grp_dev)
|
||||
*/
|
||||
static void show_device(struct util_rec *rec, const char *device)
|
||||
{
|
||||
char *grp_dev, card[16];
|
||||
char *grp_dev, card[16], type[16], t = '\0';
|
||||
|
||||
strcpy(card, &device[4]);
|
||||
grp_dev = util_path_sysfs("devices/ap/%s", device);
|
||||
@@ -567,11 +760,21 @@ static void show_device(struct util_rec *rec, const char *device)
|
||||
}
|
||||
util_rec_set(rec, "card", card);
|
||||
|
||||
if (util_file_read_line(type, sizeof(type), "%s/type", grp_dev) == 0)
|
||||
t = type[strlen(type) - 1];
|
||||
|
||||
if ((t == 'A' && !l.showaccel) ||
|
||||
(t == 'C' && !l.showcca) ||
|
||||
(t == 'P' && !l.showep11))
|
||||
goto out_free;
|
||||
|
||||
read_rec_default(rec, grp_dev);
|
||||
read_rec_verbose(rec, grp_dev);
|
||||
|
||||
util_rec_print(rec);
|
||||
show_subdevices(rec, grp_dev);
|
||||
if (l.showcard)
|
||||
util_rec_print(rec);
|
||||
if (l.showqueue)
|
||||
show_subdevices(rec, grp_dev);
|
||||
out_free:
|
||||
free(grp_dev);
|
||||
}
|
||||
@@ -581,10 +784,10 @@ out_free:
|
||||
*/
|
||||
static void define_rec_default(struct util_rec *rec)
|
||||
{
|
||||
util_rec_def(rec, "card", UTIL_REC_ALIGN_LEFT, 11, "CARD.DOMAIN");
|
||||
util_rec_def(rec, "card", UTIL_REC_ALIGN_LEFT, 8, "CARD.DOM");
|
||||
util_rec_def(rec, "type", UTIL_REC_ALIGN_LEFT, 5, "TYPE");
|
||||
util_rec_def(rec, "mode", UTIL_REC_ALIGN_LEFT, 11, "MODE");
|
||||
util_rec_def(rec, "online", UTIL_REC_ALIGN_LEFT, 8, "STATUS");
|
||||
util_rec_def(rec, "status", UTIL_REC_ALIGN_LEFT, 10, "STATUS");
|
||||
util_rec_def(rec, "requests", UTIL_REC_ALIGN_RIGHT, 8, "REQUESTS");
|
||||
}
|
||||
|
||||
@@ -745,9 +948,27 @@ int main(int argc, char **argv)
|
||||
case 'd':
|
||||
show_domains();
|
||||
return EXIT_SUCCESS;
|
||||
case 's':
|
||||
show_serialnumbers();
|
||||
return EXIT_SUCCESS;
|
||||
case 'V':
|
||||
l.verbose++;
|
||||
break;
|
||||
case OPT_ACCELONLY:
|
||||
l.showaccel = 1;
|
||||
break;
|
||||
case OPT_CCAONLY:
|
||||
l.showcca = 1;
|
||||
break;
|
||||
case OPT_EP11ONLY:
|
||||
l.showep11 = 1;
|
||||
break;
|
||||
case OPT_CARDONLY:
|
||||
l.showcard = 1;
|
||||
break;
|
||||
case OPT_QUEUEONLY:
|
||||
l.showqueue = 1;
|
||||
break;
|
||||
case 'h':
|
||||
util_prg_print_help();
|
||||
util_opt_print_help();
|
||||
@@ -761,6 +982,29 @@ int main(int argc, char **argv)
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
switch (l.showaccel + l.showcca + l.showep11) {
|
||||
case 0:
|
||||
l.showaccel = l.showcca = l.showep11 = 1;
|
||||
break;
|
||||
case 1:
|
||||
break;
|
||||
default:
|
||||
warnx("Only one of --accelonly or --ccaonly or --ep11only can be specified");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
switch (l.showcard + l.showqueue) {
|
||||
case 0:
|
||||
l.showcard = l.showqueue = 1;
|
||||
break;
|
||||
case 1:
|
||||
break;
|
||||
default:
|
||||
warnx("Only one of --cardonly or --queueonly can be specified");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (optind == argc)
|
||||
show_devices_all();
|
||||
else
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" zcryptctl.8
|
||||
.\"
|
||||
.\" Copyright 2018 IBM Corp.
|
||||
.\" Copyright 2018, 2022 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.
|
||||
.\"
|
||||
@@ -10,7 +10,7 @@
|
||||
.\" nroff -man zcryptctl.8
|
||||
.\" to process this source
|
||||
.\"
|
||||
.TH ZCRYPTCTL 8 "AUG 2018" "s390-tools"
|
||||
.TH ZCRYPTCTL 8 "JAN 2022" "s390-tools"
|
||||
.SH NAME
|
||||
zcryptctl \- display information and administrate zcrypt multiple device nodes
|
||||
.SH SYNOPSIS
|
||||
@@ -35,6 +35,11 @@ zcryptctl \- display information and administrate zcrypt multiple device nodes
|
||||
.B deldom
|
||||
.I node-name domain-nr
|
||||
.TP
|
||||
.B zcryptctl addctrl
|
||||
.R |
|
||||
.B delctrl
|
||||
.I node-name domain-nr
|
||||
.TP
|
||||
.B zcryptctl addioctl
|
||||
.R |
|
||||
.B delioctl
|
||||
@@ -57,6 +62,7 @@ driver. Each zcrypt device node can be restricted in terms of crypto
|
||||
cards, domains, and available ioctls. Such a device node can be used
|
||||
as a base for container solutions like Docker to control and restrict
|
||||
the access to crypto resources.
|
||||
See the NOTES section below for information on control domains.
|
||||
.SH COMMANDS
|
||||
.TP 8
|
||||
.B zcryptctl list
|
||||
@@ -96,6 +102,14 @@ Update the filter for the specified zcrypt device node and add or
|
||||
delete a domain to be accessible through this node. The symbol
|
||||
\fBALL\fP can be used to enable or disable all domains.
|
||||
.TP
|
||||
.B zcryptctl addctrl
|
||||
.R |
|
||||
.B delctrl
|
||||
.I node-name domain-nr
|
||||
Update the filter for the specified zcrypt device node and add or
|
||||
delete a control domain to be accessible through this node. The symbol
|
||||
\fBALL\fP can be used to enable or disable all domains.
|
||||
.TP
|
||||
.B zcryptctl addioctl
|
||||
.R |
|
||||
.B delioctl
|
||||
@@ -116,19 +130,20 @@ line and the settings are applied. Syntax is simple:
|
||||
.IP "node=<node-name>"
|
||||
.IP "aps=<list of ap numbers separated by space, tab or ','>"
|
||||
.IP "doms=<list of domain numbers separated by space, tab or ','>"
|
||||
.IP "ctrls=<list of control domain numbers separated by space, tab or ','>"
|
||||
.IP "ioctls=<list of ioctl as numeric or symbolic number separated by space, tab or ','>"
|
||||
.LP
|
||||
Empty lines are ignored and the '#' marks the rest of the
|
||||
line as comment.
|
||||
.LP
|
||||
The \fBnode=\fP line creates a new zcrypt device node, the \fBaps=\fP,
|
||||
\fBdoms=\fP and \fBioctls=\fP lines customize the previously created
|
||||
node. The symbol \fBALL\fP is also recognized for aps, doms, and
|
||||
ioctls.
|
||||
\fBdoms=\fP, \fBctrls=\fP and \fBioctls=\fP lines customize the
|
||||
previously created node. The symbol \fBALL\fP is also recognized for
|
||||
aps, doms, and ioctls.
|
||||
.LP
|
||||
Each action must fit into one line, spreading over multiple lines is
|
||||
not supported. But you can use more than one \fBaps=\fP, \fBdoms=\fP
|
||||
and \fBioctls=\fP lines to customize the very same node.
|
||||
not supported. But you can use more than one \fBaps=\fP, \fBdoms=\fP,
|
||||
\fBctrls=\fP and \fBioctls=\fP lines to customize the very same node.
|
||||
.LP
|
||||
Processing stops when a line cannot be parsed or the current action
|
||||
fails. In this case the exit status is non zero but the successful
|
||||
@@ -139,6 +154,17 @@ actions until the failure occurs are not rolled back.
|
||||
List the current configuration in a form suitable for input to the
|
||||
\fBzcryptctl config\fP command.
|
||||
.LP
|
||||
.SH NOTES
|
||||
|
||||
Control domain filtering is only supported for custom device
|
||||
<nodename> if the \fBadmask\fP file in sysfs under
|
||||
/sys/class/zcrypt/<nodename>/admask exists. If this file does not
|
||||
exist, the kernel is too old and does not support control command
|
||||
filtering. This is the same effect as setting the control domain mask
|
||||
to ALL. Note that, even though you can allow more than available to
|
||||
the system, you can only send control commands to control domains
|
||||
available to the system.
|
||||
|
||||
.SH EXIT STATUS
|
||||
On successful completion of the command the exit status is 0. A non
|
||||
zero return code (and some kind of failure message) is emitted if the
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* zcryptctl - Maintain zcrypt multi device nodes.
|
||||
*
|
||||
* by Harald Freudenberger <freude@linux.ibm.com>
|
||||
* Copyright IBM Corp. 2018
|
||||
* Copyright IBM Corp. 2018, 2022
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -58,6 +58,8 @@
|
||||
#define CMD_DEL_IOCTL 0x0009
|
||||
#define CMD_CONFIG 0x000A
|
||||
#define CMD_LISTCONFIG 0x000B
|
||||
#define CMD_ADD_CTRL 0x000C
|
||||
#define CMD_DEL_CTRL 0x000D
|
||||
|
||||
/*
|
||||
* Program configuration
|
||||
@@ -70,7 +72,7 @@ static const struct util_prg prg = {
|
||||
{
|
||||
.owner = "IBM Corp.",
|
||||
.pub_first = 2018,
|
||||
.pub_last = 2018,
|
||||
.pub_last = 2022,
|
||||
},
|
||||
UTIL_PRG_COPYRIGHT_END
|
||||
}
|
||||
@@ -345,6 +347,18 @@ static int cmd_list(int cmd,
|
||||
if (test_bit(i, buf))
|
||||
printf("%c%d", n++ == 0 ? tab : ',', i);
|
||||
putchar('\n');
|
||||
if (cmd == CMD_LISTCONFIG)
|
||||
printf(" ctrls =");
|
||||
else
|
||||
printf(" control domains:");
|
||||
if (read_dn_attr(de->d_name, "admask", buf, sizeof(buf)) != 0)
|
||||
errx(EXIT_FAILURE,
|
||||
"Could not fetch admask attribute from sysfs for zcrypt node '%s'",
|
||||
de->d_name);
|
||||
for (i = n = 0; i < MAX_ZDEV_DOMAINS_EXT; i++)
|
||||
if (test_bit(i, buf))
|
||||
printf("%c%d", n++ == 0 ? tab : ',', i);
|
||||
putchar('\n');
|
||||
if (cmd == CMD_LISTCONFIG)
|
||||
printf(" ioctls =");
|
||||
else
|
||||
@@ -526,6 +540,52 @@ static int cmd_add_del_dom(int cmd, const char *node, const char *arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void add_del_ctrl(int cmd, const char *node, int dom)
|
||||
{
|
||||
int rc;
|
||||
char buf[PATH_MAX];
|
||||
|
||||
if (cmd == CMD_ADD_CTRL)
|
||||
sprintf(buf, "+%d", dom);
|
||||
else
|
||||
sprintf(buf, "-%d", dom);
|
||||
rc = write_dn_attr(node, "admask", buf);
|
||||
if (rc != 0)
|
||||
errx(EXIT_FAILURE,
|
||||
"Could not write into sysfs entry to %s domain %d for zdev node '%s'",
|
||||
cmd == CMD_ADD_CTRL ? "add" : "remove", dom, node);
|
||||
}
|
||||
|
||||
static int cmd_add_del_ctrl(int cmd, const char *node, const char *arg)
|
||||
{
|
||||
int dom, all = 0;
|
||||
|
||||
if (strcasecmp(arg, "ALL") == 0) {
|
||||
all = 1;
|
||||
} else {
|
||||
if (sscanf(arg, "%i", &dom) != 1)
|
||||
errx(EXIT_FAILURE,
|
||||
"Invalid domain argument '%s'", arg);
|
||||
if (dom < 0 || dom >= MAX_ZDEV_DOMAINS_EXT)
|
||||
errx(EXIT_FAILURE,
|
||||
"Domain argument '%s' out of range [0..%d]",
|
||||
arg, MAX_ZDEV_DOMAINS_EXT - 1);
|
||||
}
|
||||
|
||||
if (!all) {
|
||||
add_del_ctrl(cmd, node, dom);
|
||||
printf("Control domain %d %s\n", dom,
|
||||
(cmd == CMD_ADD_CTRL ? "added" : "removed"));
|
||||
} else {
|
||||
for (dom = 0; dom < MAX_ZDEV_DOMAINS_EXT; dom++)
|
||||
add_del_ctrl(cmd, node, dom);
|
||||
printf("All control domains %s\n",
|
||||
(cmd == CMD_ADD_CTRL ? "added" : "removed"));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void add_del_ioctl(int cmd, const char *node, int ioctlnr)
|
||||
{
|
||||
int rc;
|
||||
@@ -694,12 +754,34 @@ static int cmd_config(int cmd _UNUSED_,
|
||||
break;
|
||||
if (!_match_string(&p, buf))
|
||||
errx(EXIT_FAILURE,
|
||||
"Missing argument(s) for aps=... at '%-8.8s...' in line %d '%s'",
|
||||
"Missing argument(s) for doms=... at '%-8.8s...' in line %d '%s'",
|
||||
p, nr, line);
|
||||
cmd_add_del_dom(CMD_ADD_DOM, node, buf);
|
||||
while (isblank(*p) || *p == ',')
|
||||
p++;
|
||||
}
|
||||
} else if (_match_keyword(&p, "ctrls")) {
|
||||
if (!havenode)
|
||||
errx(EXIT_FAILURE,
|
||||
"Missing node=... before processing any ctrls=... statements in line %d '%s'",
|
||||
nr, line);
|
||||
if (!_match_character(&p, '='))
|
||||
errx(EXIT_FAILURE,
|
||||
"Missing '=' at '%-8.8s...' in line %d '%s'",
|
||||
p, nr, line);
|
||||
while (1) {
|
||||
while (isspace(*p))
|
||||
p++;
|
||||
if (*p == '\0' || *p == '#')
|
||||
break;
|
||||
if (!_match_string(&p, buf))
|
||||
errx(EXIT_FAILURE,
|
||||
"Missing argument(s) for ctrls=... at '%-8.8s...' in line %d '%s'",
|
||||
p, nr, line);
|
||||
cmd_add_del_ctrl(CMD_ADD_CTRL, node, buf);
|
||||
while (isblank(*p) || *p == ',')
|
||||
p++;
|
||||
}
|
||||
} else if (_match_keyword(&p, "ioctls")) {
|
||||
if (!havenode)
|
||||
errx(EXIT_FAILURE,
|
||||
@@ -810,13 +892,35 @@ static struct zcryptctl_cmds_s {
|
||||
.cmd = CMD_DEL_DOM,
|
||||
.command = "deldom",
|
||||
.function = cmd_add_del_dom,
|
||||
.usage = "zcryptctl deldom <adapter>",
|
||||
.usage = "zcryptctl deldom <domain>",
|
||||
.description =
|
||||
"Update the filter for the specified zcrypt device node and\n"
|
||||
"remove a crypto domain from the allowed domains list. The\n"
|
||||
"domain argument may be a number in the range 0-255 or the\n"
|
||||
"symbol ALL.",
|
||||
},
|
||||
{
|
||||
.cmd = CMD_ADD_CTRL,
|
||||
.command = "addctrl",
|
||||
.function = cmd_add_del_ctrl,
|
||||
.usage = "zcryptctl addctrl <domain>",
|
||||
.description =
|
||||
"Update the filter for the specified zcrypt device node and\n"
|
||||
"add a crypto control domain to be accessible via this node.\n"
|
||||
"The domain argument may be a number in the range 0-255 or\n"
|
||||
"the symbol ALL.",
|
||||
},
|
||||
{
|
||||
.cmd = CMD_DEL_CTRL,
|
||||
.command = "delctrl",
|
||||
.function = cmd_add_del_ctrl,
|
||||
.usage = "zcryptctl delctrl <domain>",
|
||||
.description =
|
||||
"Update the filter for the specified zcrypt device node and\n"
|
||||
"remove a crypto control domain from the allowed domains list.\n"
|
||||
"The domain argument may be a number in the range 0-255 or\n"
|
||||
"the symbol ALL.",
|
||||
},
|
||||
{
|
||||
.cmd = CMD_ADD_IOCTL,
|
||||
.command = "addioctl",
|
||||
@@ -1003,6 +1107,19 @@ int main(int argc, char *argv[])
|
||||
argv[optind + 1],
|
||||
argv[optind + 2]);
|
||||
break;
|
||||
case CMD_ADD_CTRL:
|
||||
case CMD_DEL_CTRL:
|
||||
if (optind + 1 >= argc)
|
||||
errx(EXIT_FAILURE, "Missing node name argument");
|
||||
if (optind + 2 >= argc)
|
||||
errx(EXIT_FAILURE, "Missing domain argument");
|
||||
if (check_nodename(argv[optind + 1]) != 0)
|
||||
errx(EXIT_FAILURE, "Invalid or unknown nodename '%s'",
|
||||
argv[optind + 1]);
|
||||
rc = zcryptctl_cmds[cmdindex].function(c,
|
||||
argv[optind + 1],
|
||||
argv[optind + 2]);
|
||||
break;
|
||||
case CMD_ADD_IOCTL:
|
||||
case CMD_DEL_IOCTL:
|
||||
if (optind + 1 >= argc)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* zcryptstats - Show usage statistics of IBM Crypto Express adapters
|
||||
*
|
||||
* Copyright IBM Corp. 2019
|
||||
* Copyright IBM Corp. 2019, 2022
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -148,8 +148,9 @@ struct chsc_cmb_area {
|
||||
#define CRYPTO_TYPE_CEX5S 11
|
||||
#define CRYPTO_TYPE_CEX6S 12
|
||||
#define CRYPTO_TYPE_CEX7S 13
|
||||
#define CRYPTO_TYPE_CEX8S 14
|
||||
|
||||
#define CRYPTO_TYPE_TOLERATION CRYPTO_TYPE_CEX7S
|
||||
#define CRYPTO_TYPE_TOLERATION CRYPTO_TYPE_CEX8S
|
||||
|
||||
struct crypto_counter {
|
||||
const char *name;
|
||||
@@ -236,8 +237,8 @@ static const struct crypto_mode mode_pcica[1] = {
|
||||
.counters = counter_pcica },
|
||||
};
|
||||
|
||||
#define NUM_CEX4567_MODES 11
|
||||
static const struct crypto_mode mode_cex4567[NUM_CEX4567_MODES] = {
|
||||
#define NUM_CEX45678_MODES 11
|
||||
static const struct crypto_mode mode_cex45678[NUM_CEX45678_MODES] = {
|
||||
{ 0 },
|
||||
{ 0 },
|
||||
{ 0 },
|
||||
@@ -257,7 +258,7 @@ static const struct crypto_mode mode_cex4567[NUM_CEX4567_MODES] = {
|
||||
.counters = counter_ep11 },
|
||||
};
|
||||
|
||||
#define NUM_CRYPTO_TYPES 14
|
||||
#define NUM_CRYPTO_TYPES 15
|
||||
static const struct crypto_type crypto_types[NUM_CRYPTO_TYPES] = {
|
||||
{ 0 },
|
||||
{ 0 },
|
||||
@@ -276,14 +277,16 @@ static const struct crypto_type crypto_types[NUM_CRYPTO_TYPES] = {
|
||||
.modes = mode_accel },
|
||||
{ .name = "CEX3C", .num_modes = NUM_COPROC_MODES,
|
||||
.modes = mode_coproc },
|
||||
{ .name = "CEX4", .num_modes = NUM_CEX4567_MODES,
|
||||
.modes = mode_cex4567 },
|
||||
{ .name = "CEX5", .num_modes = NUM_CEX4567_MODES,
|
||||
.modes = mode_cex4567 },
|
||||
{ .name = "CEX6", .num_modes = NUM_CEX4567_MODES,
|
||||
.modes = mode_cex4567 },
|
||||
{ .name = "CEX7", .num_modes = NUM_CEX4567_MODES,
|
||||
.modes = mode_cex4567 },
|
||||
{ .name = "CEX4", .num_modes = NUM_CEX45678_MODES,
|
||||
.modes = mode_cex45678 },
|
||||
{ .name = "CEX5", .num_modes = NUM_CEX45678_MODES,
|
||||
.modes = mode_cex45678 },
|
||||
{ .name = "CEX6", .num_modes = NUM_CEX45678_MODES,
|
||||
.modes = mode_cex45678 },
|
||||
{ .name = "CEX7", .num_modes = NUM_CEX45678_MODES,
|
||||
.modes = mode_cex45678 },
|
||||
{ .name = "CEX8", .num_modes = NUM_CEX45678_MODES,
|
||||
.modes = mode_cex45678 },
|
||||
};
|
||||
|
||||
|
||||
@@ -2424,4 +2427,3 @@ out:
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ all:
|
||||
install: all
|
||||
$(MAKE) -C src install
|
||||
$(MAKE) -C man install
|
||||
$(MAKE) -C udev install
|
||||
$(MAKE) -C dracut install
|
||||
$(MAKE) -C initramfs install
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ check() {
|
||||
[ "$_arch" = "s390" -o "$_arch" = "s390x" ] || return 1
|
||||
|
||||
# Ensure that required tools are available
|
||||
require_binaries chzdev lszdev || return 1
|
||||
require_binaries chzdev lszdev /lib/s390-tools/zdev_id || return 1
|
||||
|
||||
return 0
|
||||
}
|
||||
@@ -40,11 +40,14 @@ install() {
|
||||
local _tempfile
|
||||
|
||||
# Ensure that required tools are available
|
||||
inst_multiple chzdev lszdev vmcp
|
||||
inst_multiple chzdev lszdev vmcp /lib/s390-tools/zdev_id
|
||||
|
||||
# Hook to parse zdev kernel parameter
|
||||
inst_hook cmdline 95 "$moddir/parse-zdev.sh"
|
||||
|
||||
# Rule to automatically enable devices when running in DPM
|
||||
inst_rules "81-dpm.rules"
|
||||
|
||||
# Obtain early + root device configuration
|
||||
_tempfile=$(mktemp --tmpdir dracut-zdev.XXXXXX)
|
||||
chzdev --export "$_tempfile" --persistent --by-path / --quiet \
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright IBM Corp. 2017
|
||||
# Copyright IBM Corp. 2017,2021
|
||||
#
|
||||
# s390-tools is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -15,11 +15,14 @@
|
||||
# where
|
||||
#
|
||||
# no-auto: Indicates that firmware-provided I/O configuration data
|
||||
# should not be applied.
|
||||
# should not be applied. This also affects automatic
|
||||
# activation of PCI and crypto devices when running in a DPM
|
||||
# LPAR.
|
||||
#
|
||||
|
||||
zdev_fw_file="/sys/firmware/sclp_sd/config/data"
|
||||
zdev_base_args="--force --yes --no-root-update --no-settle --auto-conf --quiet"
|
||||
zdev_id="/lib/s390-tools/zdev_id"
|
||||
|
||||
if [ -e "$zdev_fw_file" ] ; then
|
||||
zdev_auto=1
|
||||
@@ -35,4 +38,22 @@ done
|
||||
|
||||
if [ $zdev_auto -eq 1 ] ; then
|
||||
chzdev --import "$zdev_fw_file" $zdev_base_args
|
||||
|
||||
# Get information about DPM environment
|
||||
for line in $($zdev_id) ; do
|
||||
eval "$line"
|
||||
done
|
||||
|
||||
if [ "$ZDEV_IS_DPM,$ZDEV_NEST_LEVEL,$ZDEV_HYPERVISOR_0" = "1,1,LPAR" ] ; then
|
||||
# Manually iterate over existing PCI devices - there is a udev rule
|
||||
# that handles this for PCI devices added at runtime but that doesn't
|
||||
# work for PCI devices defined before boot because there is no coldplug
|
||||
# trigger for /sys/bus/pci/slots
|
||||
for slot in /sys/bus/pci/slots/* ; do
|
||||
read power < "$slot/power"
|
||||
if [ "$power" = "0" ] ; then
|
||||
echo 1 > "$slot/power"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -38,6 +38,9 @@ done
|
||||
copy_exec /sbin/chzdev
|
||||
copy_exec /sbin/lszdev
|
||||
copy_exec /sbin/vmcp
|
||||
copy_exec /lib/s390-tools/zdev_id
|
||||
|
||||
cp -p /usr/lib/udev/rules.d/81-dpm.rules "$DESTDIR/usr/lib/udev/rules.d/"
|
||||
|
||||
_tempfile=$(mktemp --tmpdir initramfs-zdev.XXXXXX)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright IBM Corp. 2017
|
||||
# Copyright IBM Corp. 2017,2021
|
||||
#
|
||||
# s390-tools is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -15,7 +15,9 @@
|
||||
# where
|
||||
#
|
||||
# no-auto: Indicates that firmware-provided I/O configuration data
|
||||
# should not be applied.
|
||||
# should not be applied. This also affects automatic
|
||||
# activation of PCI and crypto devices when running in a DPM
|
||||
# LPAR.
|
||||
#
|
||||
|
||||
PREREQ="udev"
|
||||
@@ -36,6 +38,7 @@ esac
|
||||
|
||||
zdev_fw_file="/sys/firmware/sclp_sd/config/data"
|
||||
zdev_base_args="--force --yes --no-root-update --no-settle --auto-conf --quiet"
|
||||
zdev_id="/lib/s390-tools/zdev_id"
|
||||
|
||||
if [ -e "$zdev_fw_file" ] ; then
|
||||
zdev_auto=1
|
||||
@@ -64,4 +67,22 @@ if [ $zdev_auto -eq 1 ] ; then
|
||||
udevadm trigger --type=subsystems --action=add
|
||||
udevadm trigger --action=add
|
||||
udevadm settle || true
|
||||
|
||||
# Get information about DPM environment
|
||||
for line in $($zdev_id) ; do
|
||||
eval "$line"
|
||||
done
|
||||
|
||||
if [ "$ZDEV_IS_DPM,$ZDEV_NEST_LEVEL,$ZDEV_HYPERVISOR_0" = "1,1,LPAR" ] ; then
|
||||
# Manually iterate over existing PCI devices - there is a udev
|
||||
# rule that handles this for PCI devices added at runtime but
|
||||
# that doesn't work for PCI devices defined before boot because
|
||||
# there is no coldplug trigger for /sys/bus/pci/slots
|
||||
for slot in /sys/bus/pci/slots/* ; do
|
||||
read power < "$slot/power"
|
||||
if [ "$power" = "0" ] ; then
|
||||
echo 1 > "$slot/power"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -73,7 +73,7 @@ lszdev_objects += lcs.o lcs_auto.o
|
||||
# Generic CCW devtype
|
||||
lszdev_objects += generic_ccw.o
|
||||
|
||||
all: chzdev lszdev
|
||||
all: chzdev lszdev zdev_id
|
||||
|
||||
chzdev_usage.c: chzdev_usage.txt
|
||||
$(SED) $< >$@ -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/^/"/g' \
|
||||
@@ -95,16 +95,17 @@ libs = $(rootdir)/libutil/libutil.a
|
||||
chzdev: $(chzdev_objects) $(libs)
|
||||
lszdev: $(lszdev_objects) $(libs)
|
||||
|
||||
install: chzdev
|
||||
install: chzdev zdev_id
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -c chzdev $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -c lszdev $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -c zdev_id $(DESTDIR)$(TOOLS_LIBDIR)
|
||||
ifeq ($(HAVE_DRACUT),1)
|
||||
$(INSTALL) -m 755 zdev-root-update.dracut \
|
||||
$(DESTDIR)$(TOOLS_LIBDIR)/zdev-root-update
|
||||
endif
|
||||
|
||||
clean:
|
||||
@rm -f *.o chzdev lszdev chzdev_usage.c lszdev_usage.c gmon.out *.gcda *.gcno
|
||||
@rm -f *.o chzdev lszdev chzdev_usage.c lszdev_usage.c gmon.out *.gcda *.gcno zdev_id
|
||||
|
||||
.PHONY: all install clean
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -16,6 +17,7 @@
|
||||
#include "misc.h"
|
||||
|
||||
#define LSBLK_CMDLINE "lsblk -P -o NAME,MAJ:MIN,FSTYPE,UUID,MOUNTPOINT,PKNAME 2>/dev/null"
|
||||
#define LSBLK_CMDLINE2 "lsblk -P -o NAME,MAJ:MIN,FSTYPE,UUID,MOUNTPOINTS,PKNAME 2>/dev/null"
|
||||
|
||||
struct blkinfo {
|
||||
struct devnode *devnode;
|
||||
@@ -82,6 +84,26 @@ void blkinfo_print(struct blkinfo *blkinfo, int level)
|
||||
printf("%*sparent=%s\n", level, "", blkinfo->parent);
|
||||
}
|
||||
|
||||
/* Convert each occurrence of '\xnn' in @str to character with hex code <nn>. */
|
||||
static void hex_unescape(char *str)
|
||||
{
|
||||
unsigned int c;
|
||||
|
||||
while ((str = strstr(str, "\\x"))) {
|
||||
if (isxdigit(str[2]) && isxdigit(str[3]) &&
|
||||
sscanf(str + 2, "%2x", &c) == 1) {
|
||||
str[0] = (char)c;
|
||||
|
||||
/* Move remainder of str including nul behind <c>. */
|
||||
memmove(str + /* <c> */ 1,
|
||||
str + /* '\xnn' */ 4,
|
||||
strlen(str + 4) + /* <nul> */ 1);
|
||||
}
|
||||
|
||||
str++;
|
||||
}
|
||||
}
|
||||
|
||||
static char *isolate_keyword(char **line_ptr, const char *keyword)
|
||||
{
|
||||
char *start, *end;
|
||||
@@ -102,30 +124,58 @@ static char *isolate_keyword(char **line_ptr, const char *keyword)
|
||||
return start;
|
||||
}
|
||||
|
||||
static struct blkinfo *blkinfo_from_line(char *line)
|
||||
static void add_blkinfos_from_line(struct util_list *blkinfos,
|
||||
char *line)
|
||||
{
|
||||
char *name, *majmin, *fstype, *uuid, *mountpoint, *parent;
|
||||
char *name, *majmin, *fstype, *uuid, *mountpoint, *mountpoints, *parent;
|
||||
struct blkinfo *blkinfo;
|
||||
|
||||
name = isolate_keyword(&line, "NAME=\"");
|
||||
majmin = isolate_keyword(&line, "MAJ:MIN=\"");
|
||||
if (!majmin)
|
||||
majmin = isolate_keyword(&line, "MAJ_MIN=\"");
|
||||
fstype = isolate_keyword(&line, "FSTYPE=\"");
|
||||
uuid = isolate_keyword(&line, "UUID=\"");
|
||||
mountpoint = isolate_keyword(&line, "MOUNTPOINT=\"");
|
||||
mountpoints = isolate_keyword(&line, "MOUNTPOINTS=\"");
|
||||
parent = isolate_keyword(&line, "PKNAME=\"");
|
||||
|
||||
return blkinfo_new(name, majmin, fstype, uuid, mountpoint, parent);
|
||||
if (!mountpoints) {
|
||||
/* Handle old lsblk output format. */
|
||||
blkinfo = blkinfo_new(name, majmin, fstype, uuid, mountpoint,
|
||||
parent);
|
||||
ptrlist_add(blkinfos, blkinfo);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Restore newline mount point separator encoded as hex. */
|
||||
hex_unescape(mountpoints);
|
||||
|
||||
/* Represent each mount point as a separate blkinfo to support
|
||||
* resolution of multi-mount point file systems like btrfs
|
||||
* subvolumes. */
|
||||
while ((mountpoint = strsep(&mountpoints, "\n"))) {
|
||||
blkinfo = blkinfo_new(name, majmin, fstype, uuid, mountpoint,
|
||||
parent);
|
||||
ptrlist_add(blkinfos, blkinfo);
|
||||
}
|
||||
}
|
||||
|
||||
static struct util_list *blkinfos_read(void)
|
||||
{
|
||||
char *output, *curr, *next;
|
||||
struct util_list *blkinfos;
|
||||
struct blkinfo *blkinfo;
|
||||
|
||||
if (cached_blkinfos)
|
||||
return cached_blkinfos;
|
||||
|
||||
output = misc_read_cmd_output(LSBLK_CMDLINE, 0, 1);
|
||||
output = misc_read_cmd_output(LSBLK_CMDLINE2, 0, 1);
|
||||
if (output && !*output) {
|
||||
/* No output might indicate no support for new lsblk command-
|
||||
* line format - fall back to old format. */
|
||||
free(output);
|
||||
output = misc_read_cmd_output(LSBLK_CMDLINE, 0, 1);
|
||||
}
|
||||
if (!output)
|
||||
return NULL;
|
||||
|
||||
@@ -134,9 +184,7 @@ static struct util_list *blkinfos_read(void)
|
||||
/* Iterate over each line. */
|
||||
next = output;
|
||||
while ((curr = strsep(&next, "\n"))) {
|
||||
blkinfo = blkinfo_from_line(curr);
|
||||
if (blkinfo)
|
||||
ptrlist_add(blkinfos, blkinfo);
|
||||
add_blkinfos_from_line(blkinfos, curr);
|
||||
}
|
||||
|
||||
free(output);
|
||||
|
||||
@@ -804,17 +804,17 @@ void ccw_unblacklist_id_range(const char *range)
|
||||
|
||||
static char ***id_bitmap_new(void)
|
||||
{
|
||||
return misc_malloc(sizeof(char **) * CSSID_MAX);
|
||||
return misc_malloc(sizeof(char **) * (CSSID_MAX + 1));
|
||||
}
|
||||
|
||||
static void id_bitmap_free(char ***id_bitmap)
|
||||
{
|
||||
unsigned int cssid, ssid;
|
||||
|
||||
for (cssid = 0; cssid < CSSID_MAX; cssid++) {
|
||||
for (cssid = 0; cssid <= CSSID_MAX; cssid++) {
|
||||
if (!id_bitmap[cssid])
|
||||
continue;
|
||||
for (ssid = 0; ssid < SSID_MAX; ssid++)
|
||||
for (ssid = 0; ssid <= SSID_MAX; ssid++)
|
||||
free(id_bitmap[cssid][ssid]);
|
||||
free(id_bitmap[cssid]);
|
||||
}
|
||||
@@ -965,15 +965,15 @@ static struct util_list *cio_ignore_get_ranges(bool autoconf)
|
||||
ranges = strlist_new();
|
||||
|
||||
id_bitmap = id_bitmap_collect(autoconf);
|
||||
for (cssid = 0; cssid < CSSID_MAX; cssid++) {
|
||||
for (cssid = 0; cssid <= CSSID_MAX; cssid++) {
|
||||
if (!id_bitmap[cssid])
|
||||
continue;
|
||||
for (ssid = 0; ssid < SSID_MAX; ssid++) {
|
||||
for (ssid = 0; ssid <= SSID_MAX; ssid++) {
|
||||
if (!id_bitmap[cssid][ssid])
|
||||
continue;
|
||||
first = NULL;
|
||||
last = NULL;
|
||||
for (devno = 0; devno < DEVNO_MAX; devno++) {
|
||||
for (devno = 0; devno <= DEVNO_MAX; devno++) {
|
||||
if (!id_bitmap_get(id_bitmap, cssid, ssid,
|
||||
devno)) {
|
||||
if (first) {
|
||||
|
||||
@@ -2088,12 +2088,18 @@ static exit_code_t devtype_remove_settings(struct devtype *dt, config_t config,
|
||||
found = strlist_new();
|
||||
notfound = strlist_new();
|
||||
|
||||
if (SCOPE_ACTIVE(config))
|
||||
remove_settings(dt->active_settings, names, found, notfound, 1);
|
||||
if (SCOPE_ACTIVE(config)) {
|
||||
rc = remove_settings(dt->active_settings, names, found,
|
||||
notfound, 1);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (SCOPE_PERSISTENT(config)) {
|
||||
remove_settings(dt->persistent_settings, names, found,
|
||||
notfound, 0);
|
||||
rc = remove_settings(dt->persistent_settings, names, found,
|
||||
notfound, 0);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!util_list_is_empty(notfound)) {
|
||||
@@ -2104,6 +2110,7 @@ static exit_code_t devtype_remove_settings(struct devtype *dt, config_t config,
|
||||
rc = EXIT_SETTING_NOT_FOUND;
|
||||
}
|
||||
|
||||
out:
|
||||
strlist_free(found);
|
||||
strlist_free(notfound);
|
||||
|
||||
|
||||
154
zdev/src/zdev_id.c
Normal file
154
zdev/src/zdev_id.c
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* zdev_id - Determine system characteristics required by zdev udev rules
|
||||
*
|
||||
* Copyright IBM Corp. 2021
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <err.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define SYSINFO "/proc/sysinfo"
|
||||
#define CMDLINE "/proc/cmdline"
|
||||
#define WHITESPACE " \t\n"
|
||||
|
||||
static void array_add(char ***array_p, int *num_p, const char *str)
|
||||
{
|
||||
char **array = *array_p, *copy;
|
||||
int num = *num_p + 1;
|
||||
|
||||
array = realloc(array, num * sizeof(char *));
|
||||
copy = strdup(str);
|
||||
if (!array || !copy)
|
||||
err(1, "Could not allocate memory");
|
||||
array[num - 1] = copy;
|
||||
*array_p = array;
|
||||
*num_p = num;
|
||||
}
|
||||
|
||||
static void array_free(char **array, int num)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num; i++)
|
||||
free(array[i]);
|
||||
free(array);
|
||||
}
|
||||
|
||||
static void process_sysinfo(const char *filename)
|
||||
{
|
||||
char *line = NULL, *substr, **cps = NULL;
|
||||
int is_dpm = 0, num_cps = 0, level, i;
|
||||
FILE *fd;
|
||||
size_t n;
|
||||
|
||||
fd = fopen(filename, "r");
|
||||
if (!fd)
|
||||
err(1, "Could not open sysinfo file '%s'", filename);
|
||||
|
||||
while (getline(&line, &n, fd) != -1) {
|
||||
if (sscanf(line, "LPAR %m[^:]", &substr) == 1) {
|
||||
if (strcmp(substr, "UUID") == 0) {
|
||||
/* Heuristic: only DPM LPARs have UUIDs. */
|
||||
is_dpm = 1;
|
||||
} else if (strcmp(substr, "Number") == 0) {
|
||||
array_add(&cps, &num_cps, "LPAR");
|
||||
}
|
||||
free(substr);
|
||||
} else if (sscanf(line, "VM%*d Control Program: %ms ",
|
||||
&substr) == 1) {
|
||||
array_add(&cps, &num_cps, substr);
|
||||
free(substr);
|
||||
}
|
||||
}
|
||||
|
||||
free(line);
|
||||
fclose(fd);
|
||||
|
||||
/*
|
||||
* ZDEV_NEST_LEVEL=n
|
||||
*
|
||||
* Virtualization nesting level of running system:
|
||||
* 1: LPAR
|
||||
* 2: first level VM
|
||||
* 3: second level VM
|
||||
*/
|
||||
printf("ZDEV_NEST_LEVEL=%d\n", num_cps);
|
||||
|
||||
/*
|
||||
* ZDEV_HYPERVISOR_<n>=LPAR|z/VM|KVM/Linux
|
||||
*
|
||||
* Type of hypervisor that provides virtualization at nesting level <n>.
|
||||
*/
|
||||
for (i = 0; i < num_cps; i++) {
|
||||
/* Sysinfo lists VMs in reverse order .*/
|
||||
level = (i == 0) ? i : (num_cps - i);
|
||||
printf("ZDEV_HYPERVISOR_%d=\"%s\"\n", i, cps[level]);
|
||||
}
|
||||
array_free(cps, num_cps);
|
||||
|
||||
/*
|
||||
* ZDEV_IS_DPM=0|1
|
||||
*
|
||||
* Indicator if top-level LPAR is managed by Dynamic Partition
|
||||
* Manager (DPM):
|
||||
* 0: Classic mode
|
||||
* 1: DPM mode
|
||||
*/
|
||||
printf("ZDEV_IS_DPM=%d\n", is_dpm);
|
||||
}
|
||||
|
||||
static void process_cmdline(const char *filename)
|
||||
{
|
||||
char *line = NULL, *substr, *next = NULL, *value;
|
||||
int no_auto = 0;
|
||||
size_t n;
|
||||
FILE *fd;
|
||||
|
||||
fd = fopen(filename, "r");
|
||||
if (!fd)
|
||||
err(1, "Could not open cmdline file '%s'", filename);
|
||||
|
||||
if (getline(&line, &n, fd) == -1)
|
||||
goto out;
|
||||
|
||||
for (substr = strtok_r(line, WHITESPACE, &next); substr;
|
||||
substr = strtok_r(NULL, WHITESPACE, &next)) {
|
||||
if (sscanf(substr, "rd.zdev=%ms ", &value) != 1)
|
||||
continue;
|
||||
if (strstr(value, "no-auto"))
|
||||
no_auto = 1;
|
||||
|
||||
free(value);
|
||||
}
|
||||
|
||||
out:
|
||||
free(line);
|
||||
fclose(fd);
|
||||
|
||||
/*
|
||||
* ZDEV_NO_AUTO=0|1
|
||||
*
|
||||
* Indicator if auto-configuration is requested.
|
||||
* 0: Perform auto-configuration
|
||||
* 1: Skip auto-configuration
|
||||
*/
|
||||
printf("ZDEV_NO_AUTO=%d\n", no_auto);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char *sysinfo, *cmdline;
|
||||
|
||||
sysinfo = argc < 2 ? SYSINFO : argv[1];
|
||||
cmdline = argc < 3 ? CMDLINE : argv[2];
|
||||
|
||||
process_sysinfo(sysinfo);
|
||||
process_cmdline(cmdline);
|
||||
|
||||
return 0;
|
||||
}
|
||||
40
zdev/udev/81-dpm.rules
Normal file
40
zdev/udev/81-dpm.rules
Normal file
@@ -0,0 +1,40 @@
|
||||
# Automatically activate certain s390-specific device types that are attached
|
||||
# in offline state when running in an LPAR managed by the Dynamic Partition
|
||||
# Manager (DPM).
|
||||
#
|
||||
# Note that processing is restricted to DPM as there may be use cases for
|
||||
# not enabling these device types in non-DPM mode.
|
||||
#
|
||||
# To deactivate this processing, specify the following on the kernel command
|
||||
# line:
|
||||
#
|
||||
# rd.zdev=no-auto
|
||||
|
||||
ACTION!="add", GOTO="end_dpm"
|
||||
|
||||
# Handle newly added PCI devices in offline state
|
||||
SUBSYSTEM=="slots", ATTR{power}=="0", GOTO="check_dpm"
|
||||
# Handle newly added crypto devices in offline state
|
||||
SUBSYSTEM=="ap", ENV{DEVTYPE}=="ap_card", ATTR{config}=="0", GOTO="check_dpm"
|
||||
|
||||
GOTO="end_dpm"
|
||||
|
||||
LABEL="check_dpm"
|
||||
|
||||
IMPORT{program}="/lib/s390-tools/zdev_id"
|
||||
|
||||
# Check for off-switch
|
||||
ENV{ZDEV_NO_AUTO}=="1", GOTO="end_dpm"
|
||||
|
||||
# Check for DPM LPAR
|
||||
ENV{ZDEV_IS_DPM}!="1", GOTO="end_dpm"
|
||||
ENV{ZDEV_NEST_LEVEL}!="1", GOTO="end_dpm"
|
||||
ENV{ZDEV_HYPERVISOR_0}!="LPAR", GOTO="end_dpm"
|
||||
|
||||
# Enable PCI device
|
||||
SUBSYSTEM=="slots", ATTR{power}="1"
|
||||
# Enable crypto device
|
||||
SUBSYSTEM=="ap", ATTR{config}="1"
|
||||
|
||||
# End of rule
|
||||
LABEL="end_dpm"
|
||||
9
zdev/udev/Makefile
Normal file
9
zdev/udev/Makefile
Normal file
@@ -0,0 +1,9 @@
|
||||
# Common definitions
|
||||
include ../../common.mak
|
||||
|
||||
UDEVDIR := /usr/lib/udev/rules.d/
|
||||
RULES := 81-dpm.rules
|
||||
|
||||
install:
|
||||
$(INSTALL) -m 755 -d $(DESTDIR)$(UDEVDIR)
|
||||
$(INSTALL) -m 644 $(RULES) $(DESTDIR)$(UDEVDIR)/
|
||||
@@ -30,8 +30,9 @@ check_dep:
|
||||
$(call check_dep, \
|
||||
"zdsfs", \
|
||||
"fuse.h", \
|
||||
"fuse-devel or libfuse-dev", \
|
||||
"HAVE_FUSE=0")
|
||||
"fuse3-devel or libfuse3-dev", \
|
||||
"HAVE_FUSE=0", \
|
||||
"-DFUSE_USE_VERSION=30")
|
||||
$(call check_dep, \
|
||||
"zdsfs", \
|
||||
"curl/curl.h", \
|
||||
@@ -39,13 +40,13 @@ check_dep:
|
||||
"HAVE_CURL=0")
|
||||
|
||||
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)
|
||||
FUSE_CFLAGS = $(shell pkg-config --silence-errors --cflags fuse3)
|
||||
FUSE_LDLIBS = $(shell pkg-config --silence-errors --libs fuse3)
|
||||
CURL_CFLAGS = $(shell pkg-config --silence-errors --cflags libcurl)
|
||||
CURL_LDLIBS = $(shell pkg-config --silence-errors --libs libcurl)
|
||||
else
|
||||
FUSE_CFLAGS = -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse
|
||||
FUSE_LDLIBS = -lfuse
|
||||
FUSE_CFLAGS = -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse3
|
||||
FUSE_LDLIBS = -lfuse3
|
||||
CURL_CFLAGS = -I/usr/include/s390x-linux-gnu/curl
|
||||
CURL_LDLIBS = -lcurl
|
||||
endif
|
||||
|
||||
@@ -227,9 +227,6 @@ Allow access by other users
|
||||
\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
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
/* The fuse version define tells fuse that we want to use the new API */
|
||||
#define FUSE_USE_VERSION 26
|
||||
#define FUSE_USE_VERSION 30
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
@@ -312,7 +312,8 @@ void keepalive_start(void)
|
||||
setup_timer(zdsfsinfo.keepalive);
|
||||
}
|
||||
|
||||
static int zdsfs_getattr(const char *path, struct stat *stbuf)
|
||||
static int zdsfs_getattr(const char *path, struct stat *stbuf,
|
||||
struct fuse_file_info *UNUSED(fi))
|
||||
{
|
||||
char normds[MAXDSNAMELENGTH];
|
||||
size_t dssize;
|
||||
@@ -524,7 +525,8 @@ static int zdsfs_update_vtoc(void)
|
||||
}
|
||||
|
||||
static int zdsfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
||||
off_t UNUSED(offset), struct fuse_file_info *UNUSED(fi))
|
||||
off_t UNUSED(offset), struct fuse_file_info *UNUSED(fi),
|
||||
enum fuse_readdir_flags UNUSED(flags))
|
||||
{
|
||||
char normds[MAXDSNAMELENGTH];
|
||||
char *mbrname;
|
||||
@@ -544,9 +546,9 @@ static int zdsfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
||||
* type one: the root directory contains all data sets
|
||||
*/
|
||||
if (strcmp(path, "/") == 0) {
|
||||
filler(buf, ".", NULL, 0);
|
||||
filler(buf, "..", NULL, 0);
|
||||
filler(buf, METADATAFILE, NULL, 0);
|
||||
filler(buf, ".", NULL, 0, 0);
|
||||
filler(buf, "..", NULL, 0, 0);
|
||||
filler(buf, METADATAFILE, NULL, 0, 0);
|
||||
/* note that we do not need to distinguish between
|
||||
* normal files and directories here, that is done
|
||||
* in the rdf_getattr function
|
||||
@@ -558,7 +560,7 @@ static int zdsfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
||||
lzds_dataset_get_is_supported(ds, &issupported);
|
||||
if (issupported) {
|
||||
lzds_dataset_get_name(ds, &dsname);
|
||||
filler(buf, dsname, NULL, 0);
|
||||
filler(buf, dsname, NULL, 0, 0);
|
||||
}
|
||||
}
|
||||
lzds_dsiterator_free(dsit);
|
||||
@@ -572,14 +574,14 @@ static int zdsfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
||||
return -ENOENT;
|
||||
lzds_dataset_get_is_PDS(ds, &ispds);
|
||||
if (ispds) {
|
||||
filler(buf, ".", NULL, 0);
|
||||
filler(buf, "..", NULL, 0);
|
||||
filler(buf, ".", NULL, 0, 0);
|
||||
filler(buf, "..", NULL, 0, 0);
|
||||
rc = lzds_dataset_alloc_memberiterator(ds, &it);
|
||||
if (rc)
|
||||
return -ENOMEM;
|
||||
while (!lzds_memberiterator_get_next_member(it, &member)) {
|
||||
lzds_pdsmember_get_name(member, &mbrname);
|
||||
filler(buf, mbrname, NULL, 0);
|
||||
filler(buf, mbrname, NULL, 0, 0);
|
||||
}
|
||||
lzds_memberiterator_free(it);
|
||||
} else
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
include ../common.mak
|
||||
|
||||
ALL_CFLAGS += -Wno-address-of-packed-member
|
||||
|
||||
#
|
||||
# HAVE_FUSE: Allow to build zgetdump without mount support
|
||||
#
|
||||
@@ -13,8 +15,9 @@ check_dep_fuse:
|
||||
$(call check_dep, \
|
||||
"zgetdump mount support", \
|
||||
"fuse.h", \
|
||||
"fuse-devel or libfuse-dev", \
|
||||
"HAVE_FUSE=0")
|
||||
"fuse3-devel or libfuse3-dev", \
|
||||
"HAVE_FUSE=0", \
|
||||
"-DFUSE_USE_VERSION=30")
|
||||
endif
|
||||
|
||||
#
|
||||
@@ -39,28 +42,28 @@ check_dep_zlib:
|
||||
|
||||
all: check_dep_fuse check_dep_zlib zgetdump
|
||||
|
||||
OBJECTS = zgetdump.o opts.o zg.o \
|
||||
dfi.o dfi_vmcoreinfo.o \
|
||||
OBJECTS = zgetdump.o opts.o zg.o zg_error.o zg_print.o \
|
||||
dfi.o dfi_mem_chunk.o dfi_vmcoreinfo.o \
|
||||
dfi_lkcd.o dfi_elf.o \
|
||||
dfi_s390.o dfi_s390_ext.o\
|
||||
dfi_s390mv.o dfi_s390mv_ext.o \
|
||||
dfi_s390tape.o dfi_kdump.o \
|
||||
dfi_devmem.o dfo.o \
|
||||
dfi_devmem.o dfo.o dfo_mem_chunk.o \
|
||||
dfo_elf.o dfo_s390.o \
|
||||
df_s390.o \
|
||||
df_elf.o df_s390.o \
|
||||
dt.o dt_s390sv.o dt_s390sv_ext.o \
|
||||
dt_s390mv.o dt_s390mv_ext.o \
|
||||
dt_scsi.o stdout.o \
|
||||
dt_scsi.o output.o
|
||||
|
||||
ifeq ("$(HAVE_FUSE)","0")
|
||||
FUSE_CFLAGS = -DHAVE_FUSE=0 -D_FILE_OFFSET_BITS=64
|
||||
FUSE_LDLIBS =
|
||||
else ifneq ($(shell sh -c 'command -v pkg-config'),)
|
||||
FUSE_CFLAGS = -DHAVE_FUSE=1 $(shell pkg-config --silence-errors --cflags fuse)
|
||||
FUSE_LDLIBS = $(shell pkg-config --silence-errors --libs fuse)
|
||||
FUSE_CFLAGS = -DHAVE_FUSE=1 $(shell pkg-config --silence-errors --cflags fuse3)
|
||||
FUSE_LDLIBS = $(shell pkg-config --silence-errors --libs fuse3)
|
||||
else
|
||||
FUSE_CFLAGS = -DHAVE_FUSE=1 -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse
|
||||
FUSE_LDLIBS = -lfuse
|
||||
FUSE_CFLAGS = -DHAVE_FUSE=1 -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse3
|
||||
FUSE_LDLIBS = -lfuse3
|
||||
endif
|
||||
LDLIBS += -lz $(FUSE_LDLIBS)
|
||||
ALL_CFLAGS += $(FUSE_CFLAGS)
|
||||
|
||||
191
zdump/df_elf.c
Normal file
191
zdump/df_elf.c
Normal file
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2001, 2018, 2021
|
||||
*
|
||||
* 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 <string.h>
|
||||
|
||||
#include "df_elf.h"
|
||||
|
||||
#define NOTE_NAME_CORE "CORE"
|
||||
#define NOTE_NAME_LINUX "LINUX"
|
||||
#define NOTE_NAME_VMCOREINFO "VMCOREINFO"
|
||||
|
||||
/*
|
||||
* Initialize ELF header
|
||||
*/
|
||||
void *ehdr_init(Elf64_Ehdr *ehdr, Elf64_Half phnum)
|
||||
{
|
||||
memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
|
||||
ehdr->e_ident[EI_CLASS] = ELFCLASS64;
|
||||
ehdr->e_ident[EI_DATA] = ELFDATA2MSB;
|
||||
ehdr->e_ident[EI_VERSION] = EV_CURRENT;
|
||||
ehdr->e_ident[EI_OSABI] = ELFOSABI_SYSV;
|
||||
ehdr->e_ident[EI_ABIVERSION] = 0;
|
||||
memset(ehdr->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);
|
||||
ehdr->e_type = ET_CORE;
|
||||
ehdr->e_machine = EM_S390;
|
||||
ehdr->e_version = EV_CURRENT;
|
||||
ehdr->e_entry = 0;
|
||||
ehdr->e_phoff = sizeof(Elf64_Ehdr);
|
||||
ehdr->e_shoff = 0;
|
||||
ehdr->e_flags = 0;
|
||||
ehdr->e_ehsize = sizeof(Elf64_Ehdr);
|
||||
ehdr->e_phentsize = sizeof(Elf64_Phdr);
|
||||
ehdr->e_phnum = phnum;
|
||||
ehdr->e_shentsize = 0;
|
||||
ehdr->e_shnum = 0;
|
||||
ehdr->e_shstrndx = 0;
|
||||
return ehdr + 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize ELF note
|
||||
*/
|
||||
void *nt_init(void *buf, Elf64_Word type, const void *desc, int d_len,
|
||||
const char *name)
|
||||
{
|
||||
Elf64_Nhdr *note;
|
||||
u64 len;
|
||||
|
||||
note = (Elf64_Nhdr *)buf;
|
||||
note->n_namesz = strlen(name) + 1;
|
||||
note->n_descsz = d_len;
|
||||
note->n_type = type;
|
||||
len = sizeof(Elf64_Nhdr);
|
||||
|
||||
memcpy(buf + len, name, note->n_namesz);
|
||||
len = ROUNDUP(len + note->n_namesz, 4);
|
||||
|
||||
memcpy(buf + len, desc, note->n_descsz);
|
||||
len = ROUNDUP(len + note->n_descsz, 4);
|
||||
|
||||
return PTR_ADD(buf, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize prstatus note
|
||||
*/
|
||||
void *nt_prstatus(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
struct nt_prstatus_64 nt_prstatus;
|
||||
static int cpu_nr = 1;
|
||||
|
||||
memset(&nt_prstatus, 0, sizeof(nt_prstatus));
|
||||
memcpy(&nt_prstatus.gprs, cpu->gprs, sizeof(cpu->gprs));
|
||||
memcpy(&nt_prstatus.psw, cpu->psw, sizeof(cpu->psw));
|
||||
memcpy(&nt_prstatus.acrs, cpu->acrs, sizeof(cpu->acrs));
|
||||
nt_prstatus.pr_pid = cpu_nr;
|
||||
cpu_nr++;
|
||||
|
||||
return nt_init(ptr, NT_PRSTATUS, &nt_prstatus, sizeof(nt_prstatus),
|
||||
NOTE_NAME_CORE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize fpregset (floating point) note
|
||||
*/
|
||||
void *nt_fpregset(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
struct nt_fpregset_64 nt_fpregset;
|
||||
|
||||
memset(&nt_fpregset, 0, sizeof(nt_fpregset));
|
||||
memcpy(&nt_fpregset.fpc, &cpu->fpc, sizeof(cpu->fpc));
|
||||
memcpy(&nt_fpregset.fprs, &cpu->fprs, sizeof(cpu->fprs));
|
||||
|
||||
return nt_init(ptr, NT_FPREGSET, &nt_fpregset, sizeof(nt_fpregset),
|
||||
NOTE_NAME_CORE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize timer note
|
||||
*/
|
||||
void *nt_s390_timer(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_TIMER, &cpu->timer, sizeof(cpu->timer),
|
||||
NOTE_NAME_LINUX);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize TOD clock comparator note
|
||||
*/
|
||||
void *nt_s390_tod_cmp(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_TODCMP, &cpu->todcmp,
|
||||
sizeof(cpu->todcmp), NOTE_NAME_LINUX);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize TOD programmable register note
|
||||
*/
|
||||
void *nt_s390_tod_preg(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_TODPREG, &cpu->todpreg,
|
||||
sizeof(cpu->todpreg), NOTE_NAME_LINUX);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize control register note
|
||||
*/
|
||||
void *nt_s390_ctrs(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_CTRS, &cpu->ctrs, sizeof(cpu->ctrs),
|
||||
NOTE_NAME_LINUX);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize prefix register note
|
||||
*/
|
||||
void *nt_s390_prefix(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_PREFIX, &cpu->prefix,
|
||||
sizeof(cpu->prefix), NOTE_NAME_LINUX);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize vxrs_low register note
|
||||
*/
|
||||
void *nt_s390_vxrs_low(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_VXRS_LOW, &cpu->vxrs_low,
|
||||
sizeof(cpu->vxrs_low), NOTE_NAME_LINUX);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize vxrs_high register note
|
||||
*/
|
||||
void *nt_s390_vxrs_high(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_VXRS_HIGH, &cpu->vxrs_high,
|
||||
sizeof(cpu->vxrs_high), NOTE_NAME_LINUX);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize prpsinfo note
|
||||
*/
|
||||
void *nt_prpsinfo(void *ptr)
|
||||
{
|
||||
struct nt_prpsinfo_64 prpsinfo;
|
||||
|
||||
memset(&prpsinfo, 0, sizeof(prpsinfo));
|
||||
prpsinfo.pr_state = 0;
|
||||
prpsinfo.pr_sname = 'R';
|
||||
prpsinfo.pr_zomb = 0;
|
||||
strcpy(prpsinfo.pr_fname, "vmlinux");
|
||||
|
||||
return nt_init(ptr, NT_PRPSINFO, &prpsinfo, sizeof(prpsinfo),
|
||||
NOTE_NAME_CORE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize vmcoreinfo note
|
||||
*/
|
||||
void *nt_vmcoreinfo(void *ptr, const char *vmcoreinfo)
|
||||
{
|
||||
if (!vmcoreinfo)
|
||||
return ptr;
|
||||
return nt_init(ptr, 0, vmcoreinfo, strlen(vmcoreinfo),
|
||||
NOTE_NAME_VMCOREINFO);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user