mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
@@ -2,6 +2,7 @@ List of all individuals having contributed content to s390-tools
|
||||
----------------------------------------------------------------
|
||||
|
||||
- Alexander Egorenkov
|
||||
- Alexandra Winter
|
||||
- Alexey Ishchuk
|
||||
- Andreas Herrmann
|
||||
- Andre Wild
|
||||
@@ -110,6 +111,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
|
||||
|
||||
19
CHANGELOG.md
19
CHANGELOG.md
@@ -1,5 +1,24 @@
|
||||
Release history for s390-tools (MIT version)
|
||||
--------------------------------------------
|
||||
* __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
|
||||
|
||||
2
Makefile
2
Makefile
@@ -16,7 +16,7 @@ TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \
|
||||
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 = $(BASELIB_DIRS) $(LIB_DIRS) $(TOOL_DIRS)
|
||||
|
||||
|
||||
25
README.md
25
README.md
@@ -270,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"
|
||||
@@ -307,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.
|
||||
@@ -465,3 +469,14 @@ 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 man page and render the README.md as HTML, make
|
||||
further requires pandoc, GNU awk, and GNU Gzip for the build process.
|
||||
|
||||
13
chreipl-fcp-mpath/.gitignore
vendored
Normal file
13
chreipl-fcp-mpath/.gitignore
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
# 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
|
||||
/chreipl-fcp-mpath.7
|
||||
/chreipl-fcp-mpath.7.gz
|
||||
259
chreipl-fcp-mpath/Makefile
Normal file
259
chreipl-fcp-mpath/Makefile
Normal file
@@ -0,0 +1,259 @@
|
||||
# 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
|
||||
# If $(ENABLE_DOC) is `1`:
|
||||
# GNU Gzip:
|
||||
# - gzip
|
||||
# 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)
|
||||
|
||||
.INTERMEDIATE: chreipl-fcp-mpath.md
|
||||
chreipl-fcp-mpath.md: README.md
|
||||
gawk -- '/NOT-IN-MAN \{/,/NOT-IN-MAN \}/ { next } { print }' $(<) > $(@)
|
||||
|
||||
.PHONY: chreipl-fcp-mpath-doc-clean
|
||||
chreipl-fcp-mpath-doc-clean:
|
||||
rm -f README.html README.pdf chreipl-fcp-mpath.md chreipl-fcp-mpath.7
|
||||
|
||||
chreipl-fcp-mpath: chreipl-fcp-mpath-doc
|
||||
chreipl-fcp-mpath-clean: chreipl-fcp-mpath-doc-clean
|
||||
|
||||
endif
|
||||
|
||||
#
|
||||
## 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)
|
||||
|
||||
.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)
|
||||
|
||||
.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)
|
||||
|
||||
.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)
|
||||
|
||||
.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
|
||||
|
||||
ifeq ($(ENABLE_DOC),1)
|
||||
|
||||
# chreipl-fcp-mpath: install man page
|
||||
INSTDIRS += $(MANDIR)
|
||||
|
||||
.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
|
||||
$(GZIP) -fk --best chreipl-fcp-mpath.7
|
||||
$(INSTALL_DATA) -t $(DESTDIR)$(MANDIR)/man7 \
|
||||
chreipl-fcp-mpath.7.gz
|
||||
|
||||
chreipl-fcp-mpath-install: chreipl-fcp-mpath-install-man-page
|
||||
|
||||
.PHONY: chreipl-fcp-mpath-install-man-page-clean
|
||||
chreipl-fcp-mpath-install-man-page-clean:
|
||||
rm -f chreipl-fcp-mpath.7.gz
|
||||
|
||||
chreipl-fcp-mpath-clean: chreipl-fcp-mpath-install-man-page-clean
|
||||
|
||||
endif
|
||||
|
||||
#
|
||||
## 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)
|
||||
269
chreipl-fcp-mpath/README.md
Normal file
269
chreipl-fcp-mpath/README.md
Normal file
@@ -0,0 +1,269 @@
|
||||
<!-- 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);
|
||||
- util-linux (flock, hexdump, logger);
|
||||
- udev / systemd-udev;
|
||||
- multipath-tools.
|
||||
|
||||
To make use of the optional dracut configuration you need: dracut.
|
||||
|
||||
To build and install the documentation (man page) you need:
|
||||
|
||||
- pandoc;
|
||||
- GNU Core Utilities (date);
|
||||
- GNU awk;
|
||||
- GNU Gzip.
|
||||
|
||||
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 and install a 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 and documentation 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.gz
|
||||
|
||||
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)
|
||||
447
chreipl-fcp-mpath/chreipl-fcp-mpath-common.sh.in
Normal file
447
chreipl-fcp-mpath/chreipl-fcp-mpath-common.sh.in
Normal file
@@ -0,0 +1,447 @@
|
||||
#!/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):
|
||||
# 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
|
||||
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"
|
||||
@@ -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 = 18
|
||||
RELEASE = 19
|
||||
PATCHLEVEL = 0
|
||||
DISTRELEASE = build-$(shell date +%Y%m%d)
|
||||
S390_TOOLS_RELEASE = $(VERSION).$(RELEASE).$(PATCHLEVEL)-$(DISTRELEASE)
|
||||
@@ -182,6 +182,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 +207,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
|
||||
|
||||
@@ -46,9 +46,9 @@
|
||||
#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, cfvn, csvn, authorization;
|
||||
static unsigned long loop_count = 1;
|
||||
static unsigned char *ioctlbuffer;
|
||||
static bool allcpu;
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ readonly ZDEV_CONF=$(lszdev --configured 2>/dev/null | wc -l)
|
||||
readonly ZDEV_OFF=$(lszdev --offline 2>/dev/null | wc -l)
|
||||
readonly ZDEV_ONL=$(lszdev --online 2>/dev/null | wc -l)
|
||||
|
||||
paramWORKDIR_BASE="/tmp/" # initial default path
|
||||
paramWORKDIR_BASE="/tmp" # initial default path
|
||||
|
||||
########################################
|
||||
# print dbginfo.sh version info
|
||||
@@ -347,6 +347,7 @@ LOGFILES="\
|
||||
/var/log/openvswitch/ovsdb-server.log\
|
||||
/run/docker/libcontainerd/containerd/events.log\
|
||||
/run/containerd/events.log\
|
||||
/run/udev/chreiplzfcpmp-[0-9][0-9][a-z][a-z][a-z][a-z]-*\
|
||||
"
|
||||
|
||||
########################################
|
||||
@@ -404,6 +405,7 @@ CONFIGFILES="\
|
||||
/lib/systemd/system/docker.service\
|
||||
/usr/lib/systemd/system\
|
||||
/etc/apparmor.d\
|
||||
/run/udev/chreiplzfcpmp-ipl-volume-id\
|
||||
"
|
||||
|
||||
########################################
|
||||
@@ -412,7 +414,6 @@ CMDS="uname -a\
|
||||
:timedatectl\
|
||||
:runlevel\
|
||||
:ulimit -a\
|
||||
:blockdev --report\
|
||||
:env\
|
||||
:df -h\
|
||||
:df -i\
|
||||
@@ -1071,7 +1072,7 @@ call_run_command() {
|
||||
|
||||
# check if calling command and timeout exist
|
||||
if which "${raw_cmd}" >/dev/null 2>&1 && which timeout >/dev/null 2>&1; then
|
||||
eval timeout ${TOS} "${cmd}" >> ${logfile} 2>&1
|
||||
eval timeout -k ${TOS} ${TOS} "${cmd}" >> ${logfile} 2>&1
|
||||
rc=$?
|
||||
# check if command is a builtin (no use of timeout possible)
|
||||
elif command -v "${raw_cmd}" >/dev/null 2>&1; then
|
||||
|
||||
@@ -42,7 +42,7 @@ check_dep_zlib:
|
||||
all: check_dep_fuse check_dep_zlib zgetdump
|
||||
|
||||
OBJECTS = zgetdump.o opts.o zg.o \
|
||||
dfi.o dfi_vmcoreinfo.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 \
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "zgetdump.h"
|
||||
#include "df_s390.h"
|
||||
|
||||
/*
|
||||
* Check, if we can access the lowcore information in the dump
|
||||
|
||||
539
zdump/dfi.c
539
zdump/dfi.c
@@ -9,14 +9,19 @@
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include "lib/util_log.h"
|
||||
|
||||
#include "zgetdump.h"
|
||||
#include "dfi_mem_chunk.h"
|
||||
#include "dfi_vmcoreinfo.h"
|
||||
#include "dfi.h"
|
||||
|
||||
#define TIME_FMT_STR "%a, %d %b %Y %H:%M:%S %z"
|
||||
#define PROGRESS_HASH_CNT 50
|
||||
|
||||
#define KDUMP_OLDMEM_BASE 0x10418
|
||||
#define KDUMP_OLDMEM_SIZE 0x10420
|
||||
|
||||
/*
|
||||
* DFI vector - ensure that tape is the first in the list and devmem the second!
|
||||
*/
|
||||
@@ -49,17 +54,6 @@ struct cpus {
|
||||
unsigned int cnt;
|
||||
};
|
||||
|
||||
/*
|
||||
* Memory information
|
||||
*/
|
||||
struct mem {
|
||||
struct dfi_mem_chunk *chunk_cache;
|
||||
u64 start_addr;
|
||||
u64 end_addr;
|
||||
unsigned int chunk_cnt;
|
||||
struct util_list chunk_list;
|
||||
};
|
||||
|
||||
/*
|
||||
* Dump header attribute information
|
||||
*/
|
||||
@@ -83,8 +77,6 @@ struct attr {
|
||||
static struct {
|
||||
enum dfi_arch arch;
|
||||
struct attr attr;
|
||||
struct mem mem_phys;
|
||||
struct mem mem_virt;
|
||||
struct cpus cpus;
|
||||
struct dfi *dfi;
|
||||
unsigned long kdump_base;
|
||||
@@ -111,129 +103,6 @@ static void date_print(void)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize DFI memory chunks
|
||||
*/
|
||||
static void mem_init(struct mem *mem)
|
||||
{
|
||||
mem->start_addr = U64_MAX;
|
||||
mem->end_addr = 0;
|
||||
util_list_init(&mem->chunk_list, struct dfi_mem_chunk, list);
|
||||
}
|
||||
|
||||
/*
|
||||
* Memory chunk compare function for list sorting
|
||||
*/
|
||||
static int mem_chunk_cmp_fn(void *a, void *b, void *UNUSED(data))
|
||||
{
|
||||
struct dfi_mem_chunk *mem_chunk1 = a;
|
||||
struct dfi_mem_chunk *mem_chunk2 = b;
|
||||
|
||||
return mem_chunk1->start < mem_chunk2->start ? -1 : 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update DFI memory chunks
|
||||
*/
|
||||
static void mem_update(struct mem *mem)
|
||||
{
|
||||
struct dfi_mem_chunk *mem_chunk;
|
||||
|
||||
util_list_sort(&mem->chunk_list, mem_chunk_cmp_fn, NULL);
|
||||
mem->start_addr = U64_MAX;
|
||||
mem->end_addr = 0;
|
||||
util_list_iterate(&mem->chunk_list, mem_chunk) {
|
||||
mem->start_addr = MIN(mem->start_addr, mem_chunk->start);
|
||||
mem->end_addr = MAX(mem->end_addr, mem_chunk->end);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Print memory map
|
||||
*/
|
||||
static void mem_map_print(void)
|
||||
{
|
||||
struct dfi_mem_chunk *mem_chunk;
|
||||
u64 print_start = 0, print_end = 0;
|
||||
const char *zero_str;
|
||||
u32 volnr = 0;
|
||||
|
||||
STDERR("\nMemory map:\n");
|
||||
/*
|
||||
* Print each memory chunk if verbose specified
|
||||
*/
|
||||
if (g.opts.verbose) {
|
||||
dfi_mem_chunk_iterate(mem_chunk) {
|
||||
zero_str = "";
|
||||
if (mem_chunk->read_fn == dfi_mem_chunk_read_zero)
|
||||
zero_str = " zeroes";
|
||||
STDERR(" %016llx - %016llx (%llu MB%s)\n",
|
||||
mem_chunk->start, mem_chunk->end,
|
||||
TO_MIB(mem_chunk->size), zero_str);
|
||||
}
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Merge adjacent memory chunks from the same volume
|
||||
*/
|
||||
dfi_mem_chunk_iterate(mem_chunk) {
|
||||
if (print_end == 0) {
|
||||
print_start = mem_chunk->start;
|
||||
print_end = mem_chunk->end;
|
||||
volnr = mem_chunk->volnr;
|
||||
continue;
|
||||
}
|
||||
if (mem_chunk->start != print_end + 1 ||
|
||||
mem_chunk->volnr != volnr) {
|
||||
STDERR(" %016llx - %016llx (%llu MB)\n", print_start,
|
||||
print_end, TO_MIB(print_end - print_start + 1));
|
||||
print_start = mem_chunk->start;
|
||||
volnr = mem_chunk->volnr;
|
||||
}
|
||||
print_end = mem_chunk->end;
|
||||
}
|
||||
STDERR(" %016llx - %016llx (%llu MB)\n", print_start,
|
||||
print_end, TO_MIB(print_end - print_start + 1));
|
||||
}
|
||||
|
||||
/*
|
||||
* Is memory range valid?
|
||||
*/
|
||||
int dfi_mem_range_valid(u64 addr, u64 len)
|
||||
{
|
||||
struct dfi_mem_chunk *mem_chunk;
|
||||
u64 addr_end = addr + len;
|
||||
|
||||
/* check for unsigned wrap */
|
||||
if (addr_end < addr)
|
||||
return 0;
|
||||
|
||||
do {
|
||||
mem_chunk = dfi_mem_chunk_find(addr);
|
||||
if (!mem_chunk)
|
||||
return 0;
|
||||
addr += MIN(len, mem_chunk->end - addr + 1);
|
||||
} while (addr < addr_end);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Is memory already mapped at range?
|
||||
*/
|
||||
static int mem_range_mapped(u64 start, u64 size)
|
||||
{
|
||||
struct dfi_mem_chunk *mem_chunk;
|
||||
u64 end = start + size - 1;
|
||||
|
||||
dfi_mem_chunk_iterate(mem_chunk) {
|
||||
if (mem_chunk->start > end)
|
||||
continue;
|
||||
if (mem_chunk->end < start)
|
||||
continue;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* Print dump information (--info option)
|
||||
*/
|
||||
@@ -257,7 +126,7 @@ void dfi_info_print(void)
|
||||
STDERR(" Volume number......: %u\n", *l.attr.vol_nr);
|
||||
if (l.attr.build_arch)
|
||||
STDERR(" Build arch.........: %s\n",
|
||||
dfi_arch_str(*l.attr.build_arch));
|
||||
dfi_arch_str(*l.attr.build_arch));
|
||||
STDERR(" System arch........: %s\n", dfi_arch_str(l.arch));
|
||||
if (l.cpus.cnt)
|
||||
STDERR(" CPU count (online).: %u\n", l.cpus.cnt);
|
||||
@@ -268,255 +137,18 @@ void dfi_info_print(void)
|
||||
TO_MIB(dfi_mem_range()));
|
||||
if (l.attr.mem_size_real)
|
||||
STDERR(" Real memory range..: %lld MB\n",
|
||||
TO_MIB(*l.attr.mem_size_real));
|
||||
TO_MIB(*l.attr.mem_size_real));
|
||||
if (l.attr.file_size)
|
||||
STDERR(" Dump file size.....: %lld MB\n",
|
||||
TO_MIB(*l.attr.file_size));
|
||||
TO_MIB(*l.attr.file_size));
|
||||
if (dfi_mem_range())
|
||||
mem_map_print();
|
||||
dfi_mem_map_print(g.opts.verbose);
|
||||
if (l.dfi->info_dump) {
|
||||
STDERR("\nDump device info:\n");
|
||||
l.dfi->info_dump();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Add memory chunk to memory
|
||||
*/
|
||||
static void mem_chunk_create(struct mem *mem, u64 start, u64 size, void *data,
|
||||
dfi_mem_chunk_read_fn read_fn,
|
||||
dfi_mem_chunk_free_fn free_fn)
|
||||
{
|
||||
struct dfi_mem_chunk *mem_chunk;
|
||||
|
||||
mem_chunk = zg_alloc(sizeof(*mem_chunk));
|
||||
mem_chunk->start = start;
|
||||
mem_chunk->end = start + size - 1;
|
||||
mem_chunk->size = size;
|
||||
mem_chunk->read_fn = read_fn;
|
||||
mem_chunk->free_fn = free_fn;
|
||||
mem_chunk->data = data;
|
||||
|
||||
util_list_add_tail(&mem->chunk_list, mem_chunk);
|
||||
mem->start_addr = MIN(mem->start_addr, mem_chunk->start);
|
||||
mem->end_addr = MAX(mem->end_addr, mem_chunk->end);
|
||||
mem->chunk_cache = mem_chunk;
|
||||
mem->chunk_cnt++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if memory chunk contains address
|
||||
*/
|
||||
static int mem_chunk_has_addr(struct dfi_mem_chunk *mem_chunk, u64 addr)
|
||||
{
|
||||
return (addr >= mem_chunk->start && addr <= mem_chunk->end);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find memory chunk that contains address
|
||||
*/
|
||||
static struct dfi_mem_chunk *mem_chunk_find(struct mem *mem, u64 addr)
|
||||
{
|
||||
struct dfi_mem_chunk *mem_chunk;
|
||||
|
||||
if (mem->chunk_cache && mem_chunk_has_addr(mem->chunk_cache, addr))
|
||||
return mem->chunk_cache;
|
||||
util_list_iterate(&mem->chunk_list, mem_chunk) {
|
||||
if (mem_chunk_has_addr(mem_chunk, addr)) {
|
||||
mem->chunk_cache = mem_chunk;
|
||||
return mem_chunk;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read memory at given address
|
||||
*/
|
||||
static void mem_read(struct mem *mem, u64 addr, void *buf, size_t cnt)
|
||||
{
|
||||
struct dfi_mem_chunk *mem_chunk;
|
||||
u64 size, off, copied = 0;
|
||||
|
||||
while (copied != cnt) {
|
||||
mem_chunk = mem_chunk_find(mem, addr);
|
||||
size = MIN(cnt - copied, mem_chunk->end - addr + 1);
|
||||
off = addr - mem_chunk->start;
|
||||
mem_chunk->read_fn(mem_chunk, off, buf + copied, size);
|
||||
copied += size;
|
||||
addr += size;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Read memory for virtual map memory chunk
|
||||
*/
|
||||
static void mem_chunk_map_read_fn(struct dfi_mem_chunk *mem_chunk, u64 off,
|
||||
void *buf, u64 cnt)
|
||||
{
|
||||
u64 *start = mem_chunk->data;
|
||||
|
||||
dfi_mem_phys_read(*start + off, buf, cnt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if memory chunk is a virtual mapping
|
||||
*/
|
||||
static int mem_chunk_is_map(struct dfi_mem_chunk *mem_chunk)
|
||||
{
|
||||
return mem_chunk->read_fn == mem_chunk_map_read_fn;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return physical start address for memory chunk
|
||||
*/
|
||||
static u64 mem_chunk_start_phys(struct dfi_mem_chunk *mem_chunk)
|
||||
{
|
||||
if (mem_chunk_is_map(mem_chunk))
|
||||
return *((u64 *) mem_chunk->data);
|
||||
else
|
||||
return mem_chunk->start;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add virtual memory chunk with simple virtual mapping
|
||||
*/
|
||||
static void mem_chunk_map_add(u64 start, u64 size, u64 start_p)
|
||||
{
|
||||
u64 *data = zg_alloc(sizeof(*data));
|
||||
|
||||
*data = start_p;
|
||||
dfi_mem_chunk_virt_add(start, size, data, mem_chunk_map_read_fn,
|
||||
zg_free);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add virtual memory chunk
|
||||
*/
|
||||
void dfi_mem_chunk_virt_add(u64 start, u64 size, void *data,
|
||||
dfi_mem_chunk_read_fn read_fn,
|
||||
dfi_mem_chunk_free_fn free_fn)
|
||||
{
|
||||
util_log_print(UTIL_LOG_DEBUG,
|
||||
"DFI add %svirt mem chunk start 0x%016lx size 0x%016lx\n",
|
||||
read_fn == dfi_mem_chunk_read_zero ? "zero " : "",
|
||||
start, size);
|
||||
|
||||
if (size == 0)
|
||||
return;
|
||||
mem_chunk_create(&l.mem_virt, start, size, data, read_fn, free_fn);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add memory chunk with volume index
|
||||
*/
|
||||
void dfi_mem_chunk_add_vol(u64 start, u64 size, void *data,
|
||||
dfi_mem_chunk_read_fn read_fn,
|
||||
dfi_mem_chunk_free_fn free_fn,
|
||||
u32 volnr)
|
||||
{
|
||||
util_log_print(UTIL_LOG_DEBUG,
|
||||
"DFI add %svol mem chunk start 0x%016lx size 0x%016lx volnr %u\n",
|
||||
read_fn == dfi_mem_chunk_read_zero ? "zero " : "",
|
||||
start, size, volnr);
|
||||
|
||||
if (size == 0)
|
||||
return;
|
||||
mem_chunk_create(&l.mem_phys, start, size, data, read_fn, free_fn);
|
||||
mem_chunk_create(&l.mem_virt, start, size, data, read_fn, NULL);
|
||||
l.mem_virt.chunk_cache->volnr = volnr;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Add memory chunk
|
||||
*/
|
||||
void dfi_mem_chunk_add(u64 start, u64 size, void *data,
|
||||
dfi_mem_chunk_read_fn read_fn,
|
||||
dfi_mem_chunk_free_fn free_fn)
|
||||
{
|
||||
dfi_mem_chunk_add_vol(start, size, data, read_fn, free_fn, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read zero pages
|
||||
*/
|
||||
void dfi_mem_chunk_read_zero(struct dfi_mem_chunk *UNUSED(mem_chunk),
|
||||
u64 UNUSED(off), void *buf, u64 cnt)
|
||||
{
|
||||
memset(buf, 0, cnt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return mem_chunk list head
|
||||
*/
|
||||
struct util_list *dfi_mem_chunk_list(void)
|
||||
{
|
||||
return &l.mem_virt.chunk_list;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return number of memory chunks in input dump
|
||||
*/
|
||||
unsigned int dfi_mem_chunk_cnt(void)
|
||||
{
|
||||
return l.mem_virt.chunk_cnt;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return maximum memory range
|
||||
*/
|
||||
u64 dfi_mem_range(void)
|
||||
{
|
||||
if (l.mem_virt.start_addr == U64_MAX)
|
||||
return 0;
|
||||
return l.mem_virt.end_addr - l.mem_virt.start_addr + 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return first memory chunk
|
||||
*/
|
||||
struct dfi_mem_chunk *dfi_mem_chunk_first(void)
|
||||
{
|
||||
if (util_list_is_empty(&l.mem_virt.chunk_list))
|
||||
return NULL;
|
||||
return util_list_start(&l.mem_virt.chunk_list);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return last memory chunk
|
||||
*/
|
||||
struct dfi_mem_chunk *dfi_mem_chunk_last(void)
|
||||
{
|
||||
if (util_list_is_empty(&l.mem_virt.chunk_list))
|
||||
return NULL;
|
||||
return util_list_end(&l.mem_virt.chunk_list);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return next memory chunk
|
||||
*/
|
||||
struct dfi_mem_chunk *dfi_mem_chunk_next(struct dfi_mem_chunk *mem_chunk)
|
||||
{
|
||||
return util_list_next(&l.mem_virt.chunk_list, mem_chunk);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return previous memory chunk
|
||||
*/
|
||||
struct dfi_mem_chunk *dfi_mem_chunk_prev(struct dfi_mem_chunk *mem_chunk)
|
||||
{
|
||||
return util_list_prev(&l.mem_virt.chunk_list, mem_chunk);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find memory chunk for given address
|
||||
*/
|
||||
struct dfi_mem_chunk *dfi_mem_chunk_find(u64 addr)
|
||||
{
|
||||
return mem_chunk_find(&l.mem_virt, addr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize CPU info
|
||||
*/
|
||||
@@ -616,39 +248,6 @@ struct util_list *dfi_cpu_list(void)
|
||||
return &l.cpus.list;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read memory at given address and do kdump swap if necessary
|
||||
*/
|
||||
void dfi_mem_read(u64 addr, void *buf, size_t cnt)
|
||||
{
|
||||
util_log_print(UTIL_LOG_TRACE,
|
||||
"DFI virt mem read addr 0x%016lx size 0x%016lx\n",
|
||||
addr, cnt);
|
||||
mem_read(&l.mem_virt, addr, buf, cnt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read physical memory at given address
|
||||
*/
|
||||
void dfi_mem_phys_read(u64 addr, void *buf, size_t cnt)
|
||||
{
|
||||
util_log_print(UTIL_LOG_TRACE,
|
||||
"DFI phys mem read addr 0x%016lx size 0x%016lx\n",
|
||||
addr, cnt);
|
||||
mem_read(&l.mem_phys, addr, buf, cnt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read memory at given address with return code
|
||||
*/
|
||||
int dfi_mem_read_rc(u64 addr, void *buf, size_t cnt)
|
||||
{
|
||||
if (!dfi_mem_range_valid(addr, cnt))
|
||||
return -EINVAL;
|
||||
dfi_mem_read(addr, buf, cnt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get input dump format name
|
||||
*/
|
||||
@@ -908,7 +507,7 @@ static void lc2cpu_64(struct dfi_cpu *cpu, struct dfi_lowcore_64 *lc)
|
||||
/* Add VX registers if available */
|
||||
if (!dfi_cpu_lc_has_vx_sa(lc))
|
||||
return;
|
||||
if (dfi_mem_read_rc(lc->vector_save_area_addr, &vx_sa, sizeof(vx_sa))) {
|
||||
if (dfi_mem_virt_read(lc->vector_save_area_addr, &vx_sa, sizeof(vx_sa))) {
|
||||
STDERR("zgetdump: Vector registers save area is beyond dump memory limit for CPU %d\n", cpu->cpu_id);
|
||||
return;
|
||||
}
|
||||
@@ -952,13 +551,13 @@ int dfi_cpu_add_from_lc(u32 lc_addr)
|
||||
if (l.arch == DFI_ARCH_32) {
|
||||
struct dfi_cpu_32 cpu_32;
|
||||
struct dfi_lowcore_32 lc;
|
||||
if (dfi_mem_read_rc(lc_addr, &lc, sizeof(lc)))
|
||||
if (dfi_mem_virt_read(lc_addr, &lc, sizeof(lc)))
|
||||
return -EINVAL;
|
||||
lc2cpu_32(&cpu_32, &lc);
|
||||
cpu_32_to_64(cpu, &cpu_32);
|
||||
} else {
|
||||
struct dfi_lowcore_64 lc;
|
||||
if (dfi_mem_read_rc(lc_addr, &lc, sizeof(lc)))
|
||||
if (dfi_mem_virt_read(lc_addr, &lc, sizeof(lc)))
|
||||
return -EINVAL;
|
||||
lc2cpu_64(cpu, &lc);
|
||||
}
|
||||
@@ -1009,89 +608,6 @@ unsigned long dfi_kdump_base(void)
|
||||
return l.kdump_base;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unmap memory region
|
||||
*/
|
||||
static void mem_unmap(u64 start, u64 size)
|
||||
{
|
||||
u64 start_phys, end_phys, addr_phys, addr_virt, size_virt;
|
||||
struct dfi_mem_chunk *mem_chunk, *tmp;
|
||||
u64 end = start + size - 1;
|
||||
|
||||
util_list_iterate_safe(&l.mem_virt.chunk_list, mem_chunk, tmp) {
|
||||
/*
|
||||
* Chunk not hit?
|
||||
*/
|
||||
if (mem_chunk->start >= start + size)
|
||||
continue;
|
||||
if (mem_chunk->end < start)
|
||||
continue;
|
||||
/*
|
||||
* Chunk completely unmapped
|
||||
*
|
||||
* UNMAP: UUUUUUUUU || UUUUUU
|
||||
* CHUNK: CCCC || CCCCCC
|
||||
* TO:
|
||||
*/
|
||||
if (mem_chunk->start >= start && mem_chunk->end <= end)
|
||||
goto free;
|
||||
|
||||
/*
|
||||
* Get real start and end addresses
|
||||
*/
|
||||
start_phys = mem_chunk_start_phys(mem_chunk);
|
||||
end_phys = start_phys + mem_chunk->size - 1;
|
||||
|
||||
/*
|
||||
* Chunk hit at start or in the middle?
|
||||
*
|
||||
* UNMAP: UUUUUU || UU || UUU
|
||||
* CHUNK: CCCCC || CCCCCC || CCCC
|
||||
* TO: NN || NN || NNN
|
||||
*/
|
||||
if (mem_chunk->end > end) {
|
||||
addr_virt = end + 1;
|
||||
size_virt = mem_chunk->end - end;
|
||||
addr_phys = end_phys - size_virt + 1;
|
||||
mem_chunk_map_add(addr_virt, size_virt, addr_phys);
|
||||
}
|
||||
/*
|
||||
* Chunk hit at end or in the middle?
|
||||
*
|
||||
* UNMAP: UUUUUU || UU || UUU
|
||||
* CHUNK: CCCCC || CCCCCC || CCC
|
||||
* TO: NN || NN || NN
|
||||
*/
|
||||
if (mem_chunk->start < start) {
|
||||
addr_virt = mem_chunk->start;
|
||||
size_virt = start - addr_virt;
|
||||
addr_phys = start_phys;
|
||||
mem_chunk_map_add(addr_virt, size_virt, addr_phys);
|
||||
}
|
||||
free:
|
||||
util_list_remove(&l.mem_virt.chunk_list, mem_chunk);
|
||||
l.mem_virt.chunk_cnt--;
|
||||
if (mem_chunk->data && mem_chunk->free_fn)
|
||||
mem_chunk->free_fn(mem_chunk->data);
|
||||
zg_free(mem_chunk);
|
||||
}
|
||||
mem_update(&l.mem_virt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Map memory region
|
||||
*/
|
||||
static void mem_map(u64 start, u64 size, u64 start_phys)
|
||||
{
|
||||
if (mem_range_mapped(start, size)) {
|
||||
mem_map_print();
|
||||
ABORT("Map request for already mapped region (%llx/%llx/%llx)",
|
||||
start, size, start_phys);
|
||||
}
|
||||
mem_chunk_map_add(start, size, start_phys);
|
||||
mem_update(&l.mem_virt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if dump contains a kdump dump and initialize kdump_base and kdump_size
|
||||
*/
|
||||
@@ -1101,12 +617,10 @@ static void kdump_init(void)
|
||||
|
||||
util_log_print(UTIL_LOG_TRACE, "DFI kdump initialization\n");
|
||||
|
||||
if (!dfi_mem_range_valid(0x10418, sizeof(base)))
|
||||
if (dfi_mem_phys_read(KDUMP_OLDMEM_BASE, &base, sizeof(base)))
|
||||
return;
|
||||
if (!dfi_mem_range_valid(0x10420, sizeof(size)))
|
||||
if (dfi_mem_phys_read(KDUMP_OLDMEM_SIZE, &size, sizeof(size)))
|
||||
return;
|
||||
dfi_mem_phys_read(0x10418, &base, sizeof(base));
|
||||
dfi_mem_phys_read(0x10420, &size, sizeof(size));
|
||||
if (base == 0 || size == 0)
|
||||
return;
|
||||
if (base % MIB || size % MIB)
|
||||
@@ -1127,11 +641,11 @@ static void kdump_init(void)
|
||||
if (!g.opts.select_specified)
|
||||
return;
|
||||
if (g.opts.select == OPTS_SELECT_PROD) {
|
||||
mem_unmap(0, size);
|
||||
mem_unmap(base, size);
|
||||
mem_map(0, size, base);
|
||||
dfi_mem_unmap(0, size);
|
||||
dfi_mem_unmap(base, size);
|
||||
dfi_mem_map(0, size, base);
|
||||
} else if (g.opts.select == OPTS_SELECT_KDUMP) {
|
||||
mem_unmap(l.kdump_size, U64_MAX - l.kdump_size);
|
||||
dfi_mem_unmap(l.kdump_size, U64_MAX - l.kdump_size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1160,8 +674,8 @@ static void kdump_select_prod_init(void)
|
||||
}
|
||||
dfi_cpu_info_init(DFI_CPU_CONTENT_ALL);
|
||||
for (i = 0; i < count; i++) {
|
||||
if (dfi_mem_read_rc(ptr + i * sizeof(long), &prefix,
|
||||
sizeof(prefix)))
|
||||
if (dfi_mem_virt_read(ptr + i * sizeof(long), &prefix,
|
||||
sizeof(prefix)))
|
||||
continue;
|
||||
if (prefix == 0)
|
||||
continue;
|
||||
@@ -1184,7 +698,7 @@ static void utsname_init(void)
|
||||
|
||||
if (dfi_vmcoreinfo_symbol(&ptr, "init_uts_ns"))
|
||||
return;
|
||||
if (dfi_mem_read_rc(ptr, buf, sizeof(buf)))
|
||||
if (dfi_mem_virt_read(ptr, buf, sizeof(buf)))
|
||||
return;
|
||||
utsname = memchr(buf, 'L', sizeof(buf) - sizeof(*utsname));
|
||||
if (!utsname)
|
||||
@@ -1205,7 +719,7 @@ static void livedump_init(void)
|
||||
|
||||
util_log_print(UTIL_LOG_TRACE, "DFI livedump initialization\n");
|
||||
|
||||
if (dfi_mem_read_rc(0, &magic, sizeof(magic)))
|
||||
if (dfi_mem_virt_read(0, &magic, sizeof(magic)))
|
||||
return;
|
||||
if (magic == dfi_live_dump_magic)
|
||||
dfi_attr_dump_method_set(DFI_DUMP_METHOD_LIVE);
|
||||
@@ -1244,8 +758,9 @@ int dfi_init(void)
|
||||
util_log_print(UTIL_LOG_TRACE, "DFI initialization\n");
|
||||
|
||||
l.arch = DFI_ARCH_UNKNOWN;
|
||||
mem_init(&l.mem_virt);
|
||||
mem_init(&l.mem_phys);
|
||||
rc = dfi_mem_chunk_init();
|
||||
if (rc)
|
||||
return rc;
|
||||
attr_init();
|
||||
dfi_cpu_info_init(DFI_CPU_CONTENT_NONE);
|
||||
while ((dfi = dfi_vec[i])) {
|
||||
|
||||
172
zdump/dfi.h
172
zdump/dfi.h
@@ -12,13 +12,12 @@
|
||||
#ifndef DFI_H
|
||||
#define DFI_H
|
||||
|
||||
#include <time.h>
|
||||
#include <linux/utsname.h>
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
#include "lib/util_list.h"
|
||||
|
||||
#include "zg.h"
|
||||
|
||||
/*
|
||||
* CPU info functions and definitions
|
||||
*/
|
||||
@@ -116,11 +115,11 @@ struct dfi_cpu_32 {
|
||||
struct dfi_vxrs vxrs_high[16];
|
||||
};
|
||||
|
||||
extern void dfi_cpu_64_to_32(struct dfi_cpu_32 *cpu_32, struct dfi_cpu *cpu_64);
|
||||
void dfi_cpu_64_to_32(struct dfi_cpu_32 *cpu_32, struct dfi_cpu *cpu_64);
|
||||
|
||||
extern enum dfi_arch dfi_arch(void);
|
||||
extern void dfi_arch_set(enum dfi_arch arch);
|
||||
extern const char *dfi_arch_str(enum dfi_arch arch);
|
||||
enum dfi_arch dfi_arch(void);
|
||||
void dfi_arch_set(enum dfi_arch arch);
|
||||
const char *dfi_arch_str(enum dfi_arch arch);
|
||||
|
||||
enum dfi_cpu_content {
|
||||
DFI_CPU_CONTENT_NONE, /* No register information available */
|
||||
@@ -129,113 +128,62 @@ enum dfi_cpu_content {
|
||||
};
|
||||
|
||||
#define DFI_CPU_CONTENT_FAC_VX 0x00000001
|
||||
extern int dfi_cpu_content_fac_check(int flags);
|
||||
extern void dfi_cpu_content_fac_add(int flags);
|
||||
int dfi_cpu_content_fac_check(int flags);
|
||||
void dfi_cpu_content_fac_add(int flags);
|
||||
|
||||
#define dfi_cpu_iterate(cpu) \
|
||||
util_list_iterate(dfi_cpu_list(), cpu)
|
||||
|
||||
extern struct util_list *dfi_cpu_list(void);
|
||||
extern void dfi_cpu_info_init(enum dfi_cpu_content content);
|
||||
extern struct dfi_cpu *dfi_cpu_alloc(void);
|
||||
extern struct dfi_cpu *dfi_cpu(unsigned int cpu_nr);
|
||||
extern void dfi_cpu_add(struct dfi_cpu *cpu);
|
||||
extern unsigned int dfi_cpu_cnt(void);
|
||||
extern enum dfi_cpu_content dfi_cpu_content(void);
|
||||
extern int dfi_cpu_add_from_lc(u32 lc_addr);
|
||||
struct util_list *dfi_cpu_list(void);
|
||||
void dfi_cpu_info_init(enum dfi_cpu_content content);
|
||||
struct dfi_cpu *dfi_cpu_alloc(void);
|
||||
struct dfi_cpu *dfi_cpu(unsigned int cpu_nr);
|
||||
void dfi_cpu_add(struct dfi_cpu *cpu);
|
||||
unsigned int dfi_cpu_cnt(void);
|
||||
enum dfi_cpu_content dfi_cpu_content(void);
|
||||
int dfi_cpu_add_from_lc(u32 lc_addr);
|
||||
|
||||
#define DFI_VX_SA_SIZE (32 * 16)
|
||||
extern int dfi_cpu_lc_has_vx_sa(void *lc);
|
||||
extern void dfi_cpu_vx_copy(void *buf, struct dfi_cpu *cpu);
|
||||
|
||||
/*
|
||||
* Mem chunk functions and definitions
|
||||
*/
|
||||
struct dfi_mem_chunk;
|
||||
|
||||
typedef void (*dfi_mem_chunk_read_fn)(struct dfi_mem_chunk *mem_chunk,
|
||||
u64 off, void *buf, u64 cnt);
|
||||
typedef void (*dfi_mem_chunk_free_fn)(void *data);
|
||||
|
||||
struct dfi_mem_chunk {
|
||||
struct util_list_node list; /* List */
|
||||
u64 start; /* Start address in memory */
|
||||
u64 end; /* End address in memory */
|
||||
u64 size; /* Size of chunk in dump file */
|
||||
dfi_mem_chunk_read_fn read_fn; /* Chunk read callback */
|
||||
dfi_mem_chunk_free_fn free_fn; /* Free data callback */
|
||||
void *data; /* Data for callback */
|
||||
u32 volnr; /* Volume id where chunk resides */
|
||||
};
|
||||
|
||||
extern void dfi_mem_chunk_read_zero(struct dfi_mem_chunk *UNUSED(mem_chunk),
|
||||
u64 UNUSED(off), void *buf, u64 cnt);
|
||||
extern void dfi_mem_chunk_add_vol(u64 start, u64 size, void *data,
|
||||
dfi_mem_chunk_read_fn read_fn,
|
||||
dfi_mem_chunk_free_fn free_fn,
|
||||
u32 volnr);
|
||||
extern void dfi_mem_chunk_add(u64 start, u64 size, void *data,
|
||||
dfi_mem_chunk_read_fn read_fn,
|
||||
dfi_mem_chunk_free_fn free_fn);
|
||||
extern void dfi_mem_chunk_virt_add(u64 start, u64 size, void *data,
|
||||
dfi_mem_chunk_read_fn read_fn,
|
||||
dfi_mem_chunk_free_fn free_fn);
|
||||
extern u64 dfi_mem_range(void);
|
||||
extern int dfi_mem_range_valid(u64 addr, u64 len);
|
||||
extern unsigned int dfi_mem_chunk_cnt(void);
|
||||
extern struct dfi_mem_chunk *dfi_mem_chunk_first(void);
|
||||
extern struct dfi_mem_chunk *dfi_mem_chunk_last(void);
|
||||
extern struct dfi_mem_chunk *dfi_mem_chunk_next(struct dfi_mem_chunk *chunk);
|
||||
extern struct dfi_mem_chunk *dfi_mem_chunk_prev(struct dfi_mem_chunk *chunk);
|
||||
extern struct dfi_mem_chunk *dfi_mem_chunk_find(u64 addr);
|
||||
|
||||
extern struct util_list *dfi_mem_chunk_list(void);
|
||||
#define dfi_mem_chunk_iterate(mem_chunk) \
|
||||
util_list_iterate(dfi_mem_chunk_list(), mem_chunk)
|
||||
int dfi_cpu_lc_has_vx_sa(void *lc);
|
||||
void dfi_cpu_vx_copy(void *buf, struct dfi_cpu *cpu);
|
||||
|
||||
/*
|
||||
* Dump header attribute set/get functions
|
||||
*/
|
||||
extern void dfi_attr_time_set(struct timeval *time);
|
||||
extern struct timeval *dfi_attr_time(void);
|
||||
void dfi_attr_time_set(struct timeval *time);
|
||||
struct timeval *dfi_attr_time(void);
|
||||
|
||||
extern void dfi_attr_time_end_set(struct timeval *time_end);
|
||||
extern struct timeval *dfi_attr_time_end(void);
|
||||
void dfi_attr_time_end_set(struct timeval *time_end);
|
||||
struct timeval *dfi_attr_time_end(void);
|
||||
|
||||
extern void dfi_attr_cpu_id_set(u64 cpu_id);
|
||||
extern u64 *dfi_attr_cpu_id(void);
|
||||
void dfi_attr_cpu_id_set(u64 cpu_id);
|
||||
u64 *dfi_attr_cpu_id(void);
|
||||
|
||||
extern void dfi_attr_utsname_set(struct new_utsname *utsname);
|
||||
extern struct new_utsname *dfi_attr_utsname(void);
|
||||
void dfi_attr_utsname_set(struct new_utsname *utsname);
|
||||
struct new_utsname *dfi_attr_utsname(void);
|
||||
|
||||
extern void dfi_attr_dump_method_set(char *dump_method);
|
||||
extern char *dfi_attr_dump_method(void);
|
||||
void dfi_attr_dump_method_set(char *dump_method);
|
||||
char *dfi_attr_dump_method(void);
|
||||
|
||||
extern void dfi_attr_mem_size_real_set(u64 mem_size_real);
|
||||
extern u64 *dfi_attr_mem_size_real(void);
|
||||
void dfi_attr_mem_size_real_set(u64 mem_size_real);
|
||||
u64 *dfi_attr_mem_size_real(void);
|
||||
|
||||
extern void dfi_attr_file_size_set(u64 dump_size);
|
||||
extern u64 *dfi_attr_file_size(void);
|
||||
void dfi_attr_file_size_set(u64 dump_size);
|
||||
u64 *dfi_attr_file_size(void);
|
||||
|
||||
extern void dfi_attr_vol_nr_set(unsigned int vol_nr);
|
||||
extern unsigned int *dfi_attr_vol_nr(void);
|
||||
void dfi_attr_vol_nr_set(unsigned int vol_nr);
|
||||
unsigned int *dfi_attr_vol_nr(void);
|
||||
|
||||
extern void dfi_attr_version_set(unsigned int dfi_version);
|
||||
extern unsigned int *dfi_attr_dfi_version(void);
|
||||
void dfi_attr_version_set(unsigned int dfi_version);
|
||||
unsigned int *dfi_attr_dfi_version(void);
|
||||
|
||||
extern void dfi_attr_build_arch_set(enum dfi_arch build_arch);
|
||||
extern enum dfi_arch *dfi_attr_build_arch(void);
|
||||
void dfi_attr_build_arch_set(enum dfi_arch build_arch);
|
||||
enum dfi_arch *dfi_attr_build_arch(void);
|
||||
|
||||
extern void dfi_attr_real_cpu_cnt_set(u32 real_cpu_cnt);
|
||||
extern u32 *dfi_attr_real_cpu_cnt(void);
|
||||
void dfi_attr_real_cpu_cnt_set(u32 real_cpu_cnt);
|
||||
u32 *dfi_attr_real_cpu_cnt(void);
|
||||
|
||||
/*
|
||||
* DFI external functions
|
||||
*/
|
||||
extern void dfi_mem_read(u64 addr, void *buf, size_t cnt);
|
||||
extern int dfi_mem_read_rc(u64 addr, void *buf, size_t cnt);
|
||||
extern void dfi_mem_phys_read(u64 addr, void *buf, size_t cnt);
|
||||
extern void dfi_info_print(void);
|
||||
void dfi_info_print(void);
|
||||
|
||||
/*
|
||||
* DFI feature bits
|
||||
@@ -243,25 +191,13 @@ extern void dfi_info_print(void);
|
||||
#define DFI_FEAT_SEEK 0x1 /* Necessary for fuse mount */
|
||||
#define DFI_FEAT_COPY 0x2 /* Necessary for stdout */
|
||||
|
||||
extern int dfi_feat_seek(void);
|
||||
extern int dfi_feat_copy(void);
|
||||
int dfi_feat_seek(void);
|
||||
int dfi_feat_copy(void);
|
||||
|
||||
/*
|
||||
* DFI kdump functions
|
||||
*/
|
||||
extern unsigned long dfi_kdump_base(void);
|
||||
|
||||
/*
|
||||
* DFI vmcoreinfo functions
|
||||
*/
|
||||
extern void dfi_vmcoreinfo_init(void);
|
||||
extern char *dfi_vmcoreinfo_get(void);
|
||||
extern int dfi_vmcoreinfo_tag(char *str, int len, const char *sym);
|
||||
extern int dfi_vmcoreinfo_symbol(unsigned long *val, const char *sym);
|
||||
extern int dfi_vmcoreinfo_offset(unsigned long *offs, const char *sym);
|
||||
extern int dfi_vmcoreinfo_size(unsigned long *size, const char *sym);
|
||||
extern int dfi_vmcoreinfo_length(unsigned long *len, const char *sym);
|
||||
extern int dfi_vmcoreinfo_val(unsigned long *val, const char *sym);
|
||||
unsigned long dfi_kdump_base(void);
|
||||
|
||||
/*
|
||||
* DFI operations
|
||||
@@ -274,14 +210,14 @@ struct dfi {
|
||||
int feat_bits;
|
||||
};
|
||||
|
||||
extern const char *dfi_name(void);
|
||||
extern int dfi_init(void);
|
||||
extern void dfi_exit(void);
|
||||
const char *dfi_name(void);
|
||||
int dfi_init(void);
|
||||
void dfi_exit(void);
|
||||
|
||||
/*
|
||||
* Dump access
|
||||
*/
|
||||
extern struct zg_fh *dfi_dump_open(const char *path);
|
||||
struct zg_fh *dfi_dump_open(const char *path);
|
||||
|
||||
/*
|
||||
* Live dump memory magic
|
||||
@@ -293,4 +229,18 @@ extern u64 dfi_live_dump_magic;
|
||||
*/
|
||||
#define DFI_DUMP_METHOD_LIVE "live"
|
||||
|
||||
/*
|
||||
* Supported DFI dump formats
|
||||
*/
|
||||
extern struct dfi dfi_s390tape;
|
||||
extern struct dfi dfi_s390mv;
|
||||
extern struct dfi dfi_s390mv_ext;
|
||||
extern struct dfi dfi_s390;
|
||||
extern struct dfi dfi_s390_ext;
|
||||
extern struct dfi dfi_lkcd;
|
||||
extern struct dfi dfi_elf;
|
||||
extern struct dfi dfi_kdump;
|
||||
extern struct dfi dfi_kdump_flat;
|
||||
extern struct dfi dfi_devmem;
|
||||
|
||||
#endif /* DFI_H */
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include "zgetdump.h"
|
||||
#include "df_s390.h"
|
||||
#include "dfi.h"
|
||||
#include "dfi_mem_chunk.h"
|
||||
|
||||
/*
|
||||
* Add live dump magic to buffer
|
||||
|
||||
@@ -18,7 +18,12 @@
|
||||
|
||||
#include "lib/util_libc.h"
|
||||
#include "lib/util_log.h"
|
||||
|
||||
#include "zgetdump.h"
|
||||
#include "zg.h"
|
||||
#include "df_elf.h"
|
||||
#include "dfi.h"
|
||||
#include "dfi_mem_chunk.h"
|
||||
|
||||
/*
|
||||
* Read memory for given memory chunk
|
||||
|
||||
@@ -9,7 +9,12 @@
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include "zgetdump.h"
|
||||
#include "zg.h"
|
||||
#include "dfi.h"
|
||||
#include "dfi_mem_chunk.h"
|
||||
|
||||
struct df_kdump_hdr {
|
||||
char signature[8];
|
||||
|
||||
@@ -9,8 +9,15 @@
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "zgetdump.h"
|
||||
#include "zg.h"
|
||||
#include "df_lkcd.h"
|
||||
#include "dfi.h"
|
||||
#include "dfi_mem_chunk.h"
|
||||
|
||||
#define MEM_HOLE_SIZE_MIN (1024 * 1024)
|
||||
#define IDX_KIB 64 /* One index entry per IDX_KIB */
|
||||
|
||||
527
zdump/dfi_mem_chunk.c
Normal file
527
zdump/dfi_mem_chunk.c
Normal file
@@ -0,0 +1,527 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2001, 2018
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "lib/util_libc.h"
|
||||
#include "lib/util_log.h"
|
||||
|
||||
#include "zg.h"
|
||||
#include "dfi_mem_chunk.h"
|
||||
|
||||
/*
|
||||
* Memory information
|
||||
*/
|
||||
struct mem {
|
||||
struct dfi_mem_chunk *chunk_cache;
|
||||
u64 start_addr;
|
||||
u64 end_addr;
|
||||
unsigned int chunk_cnt;
|
||||
struct util_list chunk_list;
|
||||
};
|
||||
|
||||
/*
|
||||
* File local static data
|
||||
*/
|
||||
static struct {
|
||||
struct mem mem_phys;
|
||||
struct mem mem_virt;
|
||||
} l;
|
||||
|
||||
/*
|
||||
* Initialize DFI memory chunks
|
||||
*/
|
||||
static void mem_init(struct mem *mem)
|
||||
{
|
||||
mem->start_addr = U64_MAX;
|
||||
mem->end_addr = 0;
|
||||
util_list_init(&mem->chunk_list, struct dfi_mem_chunk, list);
|
||||
}
|
||||
|
||||
/*
|
||||
* Memory chunk compare function for list sorting
|
||||
*/
|
||||
static int mem_chunk_cmp_fn(void *a, void *b, void *UNUSED(data))
|
||||
{
|
||||
struct dfi_mem_chunk *mem_chunk1 = a;
|
||||
struct dfi_mem_chunk *mem_chunk2 = b;
|
||||
|
||||
return mem_chunk1->start < mem_chunk2->start ? -1 : 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update DFI memory chunks
|
||||
*/
|
||||
static void mem_update(struct mem *mem)
|
||||
{
|
||||
struct dfi_mem_chunk *mem_chunk;
|
||||
|
||||
util_list_sort(&mem->chunk_list, mem_chunk_cmp_fn, NULL);
|
||||
mem->start_addr = U64_MAX;
|
||||
mem->end_addr = 0;
|
||||
util_list_iterate(&mem->chunk_list, mem_chunk) {
|
||||
mem->start_addr = MIN(mem->start_addr, mem_chunk->start);
|
||||
mem->end_addr = MAX(mem->end_addr, mem_chunk->end);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Print memory map
|
||||
*/
|
||||
void dfi_mem_map_print(bool verbose)
|
||||
{
|
||||
struct dfi_mem_chunk *mem_chunk;
|
||||
u64 print_start = 0, print_end = 0;
|
||||
const char *zero_str;
|
||||
u32 volnr = 0;
|
||||
|
||||
STDERR("\nMemory map:\n");
|
||||
/*
|
||||
* Print each memory chunk if verbose specified
|
||||
*/
|
||||
if (verbose) {
|
||||
dfi_mem_chunk_iterate(mem_chunk) {
|
||||
zero_str = "";
|
||||
if (mem_chunk->read_fn == dfi_mem_chunk_read_zero)
|
||||
zero_str = " zeroes";
|
||||
STDERR(" %016llx - %016llx (%llu MB%s)\n",
|
||||
mem_chunk->start, mem_chunk->end,
|
||||
TO_MIB(mem_chunk->size), zero_str);
|
||||
}
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Merge adjacent memory chunks from the same volume
|
||||
*/
|
||||
dfi_mem_chunk_iterate(mem_chunk) {
|
||||
if (print_end == 0) {
|
||||
print_start = mem_chunk->start;
|
||||
print_end = mem_chunk->end;
|
||||
volnr = mem_chunk->volnr;
|
||||
continue;
|
||||
}
|
||||
if (mem_chunk->start != print_end + 1 ||
|
||||
mem_chunk->volnr != volnr) {
|
||||
STDERR(" %016llx - %016llx (%llu MB)\n", print_start,
|
||||
print_end, TO_MIB(print_end - print_start + 1));
|
||||
print_start = mem_chunk->start;
|
||||
volnr = mem_chunk->volnr;
|
||||
}
|
||||
print_end = mem_chunk->end;
|
||||
}
|
||||
STDERR(" %016llx - %016llx (%llu MB)\n", print_start,
|
||||
print_end, TO_MIB(print_end - print_start + 1));
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if memory chunk contains address
|
||||
*/
|
||||
static int mem_chunk_has_addr(struct dfi_mem_chunk *mem_chunk, u64 addr)
|
||||
{
|
||||
return (addr >= mem_chunk->start && addr <= mem_chunk->end);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find memory chunk that contains address
|
||||
*/
|
||||
static struct dfi_mem_chunk *mem_chunk_find(struct mem *mem, u64 addr)
|
||||
{
|
||||
struct dfi_mem_chunk *mem_chunk;
|
||||
|
||||
if (mem->chunk_cache && mem_chunk_has_addr(mem->chunk_cache, addr))
|
||||
return mem->chunk_cache;
|
||||
util_list_iterate(&mem->chunk_list, mem_chunk) {
|
||||
if (mem_chunk_has_addr(mem_chunk, addr)) {
|
||||
mem->chunk_cache = mem_chunk;
|
||||
return mem_chunk;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Is memory range valid?
|
||||
*/
|
||||
static int mem_range_valid(struct mem *mem, u64 addr, u64 len)
|
||||
{
|
||||
struct dfi_mem_chunk *mem_chunk;
|
||||
u64 addr_end = addr + len;
|
||||
|
||||
/* check for unsigned wrap */
|
||||
if (addr_end < addr)
|
||||
return 0;
|
||||
|
||||
do {
|
||||
mem_chunk = mem_chunk_find(mem, addr);
|
||||
if (!mem_chunk)
|
||||
return 0;
|
||||
addr += MIN(len, mem_chunk->end - addr + 1);
|
||||
} while (addr < addr_end);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Is memory already mapped at range?
|
||||
*/
|
||||
static int mem_range_mapped(u64 start, u64 size)
|
||||
{
|
||||
struct dfi_mem_chunk *mem_chunk;
|
||||
u64 end = start + size - 1;
|
||||
|
||||
dfi_mem_chunk_iterate(mem_chunk) {
|
||||
if (mem_chunk->start > end)
|
||||
continue;
|
||||
if (mem_chunk->end < start)
|
||||
continue;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add memory chunk to memory
|
||||
*/
|
||||
static void mem_chunk_create(struct mem *mem, u64 start, u64 size, void *data,
|
||||
dfi_mem_chunk_read_fn read_fn,
|
||||
dfi_mem_chunk_free_fn free_fn)
|
||||
{
|
||||
struct dfi_mem_chunk *mem_chunk;
|
||||
|
||||
mem_chunk = util_malloc(sizeof(*mem_chunk));
|
||||
mem_chunk->start = start;
|
||||
mem_chunk->end = start + size - 1;
|
||||
mem_chunk->size = size;
|
||||
mem_chunk->read_fn = read_fn;
|
||||
mem_chunk->free_fn = free_fn;
|
||||
mem_chunk->data = data;
|
||||
|
||||
util_list_add_tail(&mem->chunk_list, mem_chunk);
|
||||
mem->start_addr = MIN(mem->start_addr, mem_chunk->start);
|
||||
mem->end_addr = MAX(mem->end_addr, mem_chunk->end);
|
||||
mem->chunk_cache = mem_chunk;
|
||||
mem->chunk_cnt++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read memory at given address
|
||||
*/
|
||||
static void mem_read(struct mem *mem, u64 addr, void *buf, size_t cnt)
|
||||
{
|
||||
struct dfi_mem_chunk *mem_chunk;
|
||||
u64 size, off, copied = 0;
|
||||
|
||||
while (copied != cnt) {
|
||||
mem_chunk = mem_chunk_find(mem, addr);
|
||||
size = MIN(cnt - copied, mem_chunk->end - addr + 1);
|
||||
off = addr - mem_chunk->start;
|
||||
mem_chunk->read_fn(mem_chunk, off, buf + copied, size);
|
||||
copied += size;
|
||||
addr += size;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Read memory for virtual map memory chunk
|
||||
*/
|
||||
static void mem_chunk_map_read_fn(struct dfi_mem_chunk *mem_chunk, u64 off,
|
||||
void *buf, u64 cnt)
|
||||
{
|
||||
u64 *start = mem_chunk->data;
|
||||
|
||||
dfi_mem_phys_read(*start + off, buf, cnt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if memory chunk is a virtual mapping
|
||||
*/
|
||||
static int mem_chunk_is_map(struct dfi_mem_chunk *mem_chunk)
|
||||
{
|
||||
return mem_chunk->read_fn == mem_chunk_map_read_fn;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return physical start address for memory chunk
|
||||
*/
|
||||
static u64 mem_chunk_start_phys(struct dfi_mem_chunk *mem_chunk)
|
||||
{
|
||||
if (mem_chunk_is_map(mem_chunk))
|
||||
return *((u64 *) mem_chunk->data);
|
||||
else
|
||||
return mem_chunk->start;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add virtual memory chunk
|
||||
*/
|
||||
static void mem_chunk_virt_add(u64 start, u64 size, void *data,
|
||||
dfi_mem_chunk_read_fn read_fn,
|
||||
dfi_mem_chunk_free_fn free_fn)
|
||||
{
|
||||
util_log_print(UTIL_LOG_DEBUG,
|
||||
"DFI add %svirt mem chunk start 0x%016lx size 0x%016lx\n",
|
||||
read_fn == dfi_mem_chunk_read_zero ? "zero " : "",
|
||||
start, size);
|
||||
|
||||
if (size == 0)
|
||||
return;
|
||||
mem_chunk_create(&l.mem_virt, start, size, data, read_fn, free_fn);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add virtual memory chunk with simple virtual mapping
|
||||
*/
|
||||
static void mem_chunk_map_add(u64 start, u64 size, u64 start_p)
|
||||
{
|
||||
u64 *data = util_malloc(sizeof(*data));
|
||||
|
||||
*data = start_p;
|
||||
mem_chunk_virt_add(start, size, data, mem_chunk_map_read_fn, free);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add memory chunk with volume index
|
||||
*/
|
||||
void dfi_mem_chunk_add_vol(u64 start, u64 size, void *data,
|
||||
dfi_mem_chunk_read_fn read_fn,
|
||||
dfi_mem_chunk_free_fn free_fn,
|
||||
u32 volnr)
|
||||
{
|
||||
util_log_print(UTIL_LOG_DEBUG,
|
||||
"DFI add %svol mem chunk start 0x%016lx size 0x%016lx volnr %u\n",
|
||||
read_fn == dfi_mem_chunk_read_zero ? "zero " : "",
|
||||
start, size, volnr);
|
||||
|
||||
if (size == 0)
|
||||
return;
|
||||
mem_chunk_create(&l.mem_phys, start, size, data, read_fn, free_fn);
|
||||
mem_chunk_create(&l.mem_virt, start, size, data, read_fn, NULL);
|
||||
l.mem_virt.chunk_cache->volnr = volnr;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Add memory chunk
|
||||
*/
|
||||
void dfi_mem_chunk_add(u64 start, u64 size, void *data,
|
||||
dfi_mem_chunk_read_fn read_fn,
|
||||
dfi_mem_chunk_free_fn free_fn)
|
||||
{
|
||||
dfi_mem_chunk_add_vol(start, size, data, read_fn, free_fn, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read zero pages
|
||||
*/
|
||||
void dfi_mem_chunk_read_zero(struct dfi_mem_chunk *UNUSED(mem_chunk),
|
||||
u64 UNUSED(off), void *buf, u64 cnt)
|
||||
{
|
||||
memset(buf, 0, cnt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return mem_chunk list head
|
||||
*/
|
||||
struct util_list *dfi_mem_chunk_list(void)
|
||||
{
|
||||
return &l.mem_virt.chunk_list;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return number of memory chunks in input dump
|
||||
*/
|
||||
unsigned int dfi_mem_chunk_cnt(void)
|
||||
{
|
||||
return l.mem_virt.chunk_cnt;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return maximum memory range
|
||||
*/
|
||||
u64 dfi_mem_range(void)
|
||||
{
|
||||
if (l.mem_virt.start_addr == U64_MAX)
|
||||
return 0;
|
||||
return l.mem_virt.end_addr - l.mem_virt.start_addr + 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Is memory range valid?
|
||||
*/
|
||||
int dfi_mem_range_valid(u64 addr, u64 len)
|
||||
{
|
||||
return mem_range_valid(&l.mem_virt, addr, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return first memory chunk
|
||||
*/
|
||||
struct dfi_mem_chunk *dfi_mem_chunk_first(void)
|
||||
{
|
||||
if (util_list_is_empty(&l.mem_virt.chunk_list))
|
||||
return NULL;
|
||||
return util_list_start(&l.mem_virt.chunk_list);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return last memory chunk
|
||||
*/
|
||||
struct dfi_mem_chunk *dfi_mem_chunk_last(void)
|
||||
{
|
||||
if (util_list_is_empty(&l.mem_virt.chunk_list))
|
||||
return NULL;
|
||||
return util_list_end(&l.mem_virt.chunk_list);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return next memory chunk
|
||||
*/
|
||||
struct dfi_mem_chunk *dfi_mem_chunk_next(struct dfi_mem_chunk *mem_chunk)
|
||||
{
|
||||
return util_list_next(&l.mem_virt.chunk_list, mem_chunk);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return previous memory chunk
|
||||
*/
|
||||
struct dfi_mem_chunk *dfi_mem_chunk_prev(struct dfi_mem_chunk *mem_chunk)
|
||||
{
|
||||
return util_list_prev(&l.mem_virt.chunk_list, mem_chunk);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find memory chunk for given address
|
||||
*/
|
||||
struct dfi_mem_chunk *dfi_mem_chunk_find(u64 addr)
|
||||
{
|
||||
return mem_chunk_find(&l.mem_virt, addr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read physical memory at given address
|
||||
*/
|
||||
int dfi_mem_phys_read(u64 addr, void *buf, size_t cnt)
|
||||
{
|
||||
util_log_print(UTIL_LOG_TRACE,
|
||||
"DFI phys mem read addr 0x%016lx size 0x%016lx\n",
|
||||
addr, cnt);
|
||||
if (!mem_range_valid(&l.mem_phys, addr, cnt))
|
||||
return -EINVAL;
|
||||
mem_read(&l.mem_phys, addr, buf, cnt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read virtual memory at given address
|
||||
*/
|
||||
int dfi_mem_virt_read(u64 addr, void *buf, size_t cnt)
|
||||
{
|
||||
util_log_print(UTIL_LOG_TRACE,
|
||||
"DFI virt mem read addr 0x%016lx size 0x%016lx\n",
|
||||
addr, cnt);
|
||||
if (!mem_range_valid(&l.mem_virt, addr, cnt))
|
||||
return -EINVAL;
|
||||
mem_read(&l.mem_virt, addr, buf, cnt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unmap memory region
|
||||
*/
|
||||
void dfi_mem_unmap(u64 start, u64 size)
|
||||
{
|
||||
u64 start_phys, end_phys, addr_phys, addr_virt, size_virt;
|
||||
struct dfi_mem_chunk *mem_chunk, *tmp;
|
||||
u64 end = start + size - 1;
|
||||
|
||||
util_list_iterate_safe(&l.mem_virt.chunk_list, mem_chunk, tmp) {
|
||||
/*
|
||||
* Chunk not hit?
|
||||
*/
|
||||
if (mem_chunk->start >= start + size)
|
||||
continue;
|
||||
if (mem_chunk->end < start)
|
||||
continue;
|
||||
/*
|
||||
* Chunk completely unmapped
|
||||
*
|
||||
* UNMAP: UUUUUUUUU || UUUUUU
|
||||
* CHUNK: CCCC || CCCCCC
|
||||
* TO:
|
||||
*/
|
||||
if (mem_chunk->start >= start && mem_chunk->end <= end)
|
||||
goto free;
|
||||
|
||||
/*
|
||||
* Get real start and end addresses
|
||||
*/
|
||||
start_phys = mem_chunk_start_phys(mem_chunk);
|
||||
end_phys = start_phys + mem_chunk->size - 1;
|
||||
|
||||
/*
|
||||
* Chunk hit at start or in the middle?
|
||||
*
|
||||
* UNMAP: UUUUUU || UU || UUU
|
||||
* CHUNK: CCCCC || CCCCCC || CCCC
|
||||
* TO: NN || NN || NNN
|
||||
*/
|
||||
if (mem_chunk->end > end) {
|
||||
addr_virt = end + 1;
|
||||
size_virt = mem_chunk->end - end;
|
||||
addr_phys = end_phys - size_virt + 1;
|
||||
mem_chunk_map_add(addr_virt, size_virt, addr_phys);
|
||||
}
|
||||
/*
|
||||
* Chunk hit at end or in the middle?
|
||||
*
|
||||
* UNMAP: UUUUUU || UU || UUU
|
||||
* CHUNK: CCCCC || CCCCCC || CCC
|
||||
* TO: NN || NN || NN
|
||||
*/
|
||||
if (mem_chunk->start < start) {
|
||||
addr_virt = mem_chunk->start;
|
||||
size_virt = start - addr_virt;
|
||||
addr_phys = start_phys;
|
||||
mem_chunk_map_add(addr_virt, size_virt, addr_phys);
|
||||
}
|
||||
free:
|
||||
util_list_remove(&l.mem_virt.chunk_list, mem_chunk);
|
||||
l.mem_virt.chunk_cnt--;
|
||||
if (mem_chunk->data && mem_chunk->free_fn)
|
||||
mem_chunk->free_fn(mem_chunk->data);
|
||||
free(mem_chunk);
|
||||
}
|
||||
mem_update(&l.mem_virt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Map memory region
|
||||
*/
|
||||
void dfi_mem_map(u64 start, u64 size, u64 start_phys)
|
||||
{
|
||||
if (mem_range_mapped(start, size)) {
|
||||
dfi_mem_map_print(false);
|
||||
ABORT("Map request for already mapped region (%llx/%llx/%llx)",
|
||||
start, size, start_phys);
|
||||
}
|
||||
mem_chunk_map_add(start, size, start_phys);
|
||||
mem_update(&l.mem_virt);
|
||||
}
|
||||
|
||||
int dfi_mem_chunk_init(void)
|
||||
{
|
||||
mem_init(&l.mem_virt);
|
||||
mem_init(&l.mem_phys);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dfi_mem_chunk_deinit(void)
|
||||
{
|
||||
memset(&l, 0, sizeof(l));
|
||||
}
|
||||
69
zdump/dfi_mem_chunk.h
Normal file
69
zdump/dfi_mem_chunk.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2001, 2018
|
||||
*
|
||||
* 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 DFI_MEM_CHUNK_H
|
||||
#define DFI_MEM_CHUNK_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
#include "lib/util_list.h"
|
||||
|
||||
/*
|
||||
* Mem chunk functions and definitions
|
||||
*/
|
||||
struct dfi_mem_chunk;
|
||||
|
||||
typedef void (*dfi_mem_chunk_read_fn)(struct dfi_mem_chunk *mem_chunk,
|
||||
u64 off, void *buf, u64 cnt);
|
||||
typedef void (*dfi_mem_chunk_free_fn)(void *data);
|
||||
|
||||
struct dfi_mem_chunk {
|
||||
struct util_list_node list; /* List */
|
||||
u64 start; /* Start address in memory */
|
||||
u64 end; /* End address in memory */
|
||||
u64 size; /* Size of chunk in dump file */
|
||||
dfi_mem_chunk_read_fn read_fn; /* Chunk read callback */
|
||||
dfi_mem_chunk_free_fn free_fn; /* Free data callback */
|
||||
void *data; /* Data for callback */
|
||||
u32 volnr; /* Volume id where chunk resides */
|
||||
};
|
||||
|
||||
void dfi_mem_chunk_read_zero(struct dfi_mem_chunk *UNUSED(mem_chunk),
|
||||
u64 UNUSED(off), void *buf, u64 cnt);
|
||||
void dfi_mem_chunk_add_vol(u64 start, u64 size, void *data,
|
||||
dfi_mem_chunk_read_fn read_fn,
|
||||
dfi_mem_chunk_free_fn free_fn,
|
||||
u32 volnr);
|
||||
void dfi_mem_chunk_add(u64 start, u64 size, void *data,
|
||||
dfi_mem_chunk_read_fn read_fn,
|
||||
dfi_mem_chunk_free_fn free_fn);
|
||||
u64 dfi_mem_range(void);
|
||||
int dfi_mem_range_valid(u64 addr, u64 len);
|
||||
unsigned int dfi_mem_chunk_cnt(void);
|
||||
struct dfi_mem_chunk *dfi_mem_chunk_first(void);
|
||||
struct dfi_mem_chunk *dfi_mem_chunk_last(void);
|
||||
struct dfi_mem_chunk *dfi_mem_chunk_next(struct dfi_mem_chunk *chunk);
|
||||
struct dfi_mem_chunk *dfi_mem_chunk_prev(struct dfi_mem_chunk *chunk);
|
||||
struct dfi_mem_chunk *dfi_mem_chunk_find(u64 addr);
|
||||
|
||||
struct util_list *dfi_mem_chunk_list(void);
|
||||
#define dfi_mem_chunk_iterate(mem_chunk) \
|
||||
util_list_iterate(dfi_mem_chunk_list(), mem_chunk)
|
||||
|
||||
int dfi_mem_virt_read(u64 addr, void *buf, size_t cnt);
|
||||
int dfi_mem_phys_read(u64 addr, void *buf, size_t cnt);
|
||||
|
||||
void dfi_mem_map_print(bool verbose);
|
||||
|
||||
void dfi_mem_unmap(u64 start, u64 size);
|
||||
void dfi_mem_map(u64 start, u64 size, u64 start_phys);
|
||||
|
||||
int dfi_mem_chunk_init(void);
|
||||
void dfi_mem_chunk_deinit(void);
|
||||
|
||||
#endif /* DFI_MEM_CHUNK_H */
|
||||
@@ -17,9 +17,16 @@
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "lib/util_log.h"
|
||||
|
||||
#include "zgetdump.h"
|
||||
#include "zg.h"
|
||||
#include "dfi.h"
|
||||
#include "dfi_mem_chunk.h"
|
||||
#include "df_elf.h"
|
||||
#include "df_s390.h"
|
||||
|
||||
/*
|
||||
* File local static data
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include "zgetdump.h"
|
||||
#include "dt.h"
|
||||
#include "df_s390.h"
|
||||
#include "dfi.h"
|
||||
|
||||
/*
|
||||
* Initialize s390 input dump format (extended)
|
||||
|
||||
@@ -20,12 +20,17 @@
|
||||
#include <sys/sysmacros.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "lib/util_file.h"
|
||||
#include "lib/util_path.h"
|
||||
|
||||
#include "zgetdump.h"
|
||||
#include "dt.h"
|
||||
#include "df_s390.h"
|
||||
#include "dfi.h"
|
||||
#include "dfi_s390mv.h"
|
||||
#include "dfi_mem_chunk.h"
|
||||
|
||||
/*
|
||||
* Volume information
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
*/
|
||||
|
||||
#include "zgetdump.h"
|
||||
#include "dt.h"
|
||||
#include "df_s390.h"
|
||||
#include "dfi.h"
|
||||
|
||||
/*
|
||||
* Initialize s390 multi-volume input dump format (extedend)
|
||||
|
||||
@@ -17,8 +17,12 @@
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "zgetdump.h"
|
||||
#include "df_s390.h"
|
||||
#include "dfi.h"
|
||||
#include "dfi_mem_chunk.h"
|
||||
|
||||
#define TAPE_BLK_SIZE 32768 /* Defined by zipl tape dumper */
|
||||
|
||||
|
||||
@@ -9,12 +9,16 @@
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <elf.h>
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
#include "lib/util_log.h"
|
||||
|
||||
#include "zgetdump.h"
|
||||
#include "zg.h"
|
||||
#include "dfi_mem_chunk.h"
|
||||
#include "dfi_vmcoreinfo.h"
|
||||
|
||||
#ifdef __s390x__
|
||||
#define LC_VMCORE_INFO 0xe0c
|
||||
@@ -23,7 +27,7 @@
|
||||
#endif
|
||||
|
||||
#define LC_OS_INFO 0xe18
|
||||
#define OS_INFO_MAGIC 0x4f53494e464f535aULL /* OSINFOSZ */
|
||||
#define OS_INFO_MAGIC 0x4f53494e464f535aULL /* OSINFOSZ */
|
||||
|
||||
struct os_info {
|
||||
u64 magic;
|
||||
@@ -65,11 +69,11 @@ static struct os_info *os_info_get(void)
|
||||
|
||||
util_log_print(UTIL_LOG_TRACE, "DFI get osinfo\n");
|
||||
|
||||
if (dfi_mem_read_rc(LC_OS_INFO, &addr, sizeof(addr)))
|
||||
if (dfi_mem_virt_read(LC_OS_INFO, &addr, sizeof(addr)))
|
||||
return NULL;
|
||||
if (addr % 0x1000)
|
||||
return NULL;
|
||||
if (dfi_mem_read_rc(addr, &os_info, sizeof(os_info)))
|
||||
if (dfi_mem_virt_read(addr, &os_info, sizeof(os_info)))
|
||||
return NULL;
|
||||
if (os_info.magic != OS_INFO_MAGIC)
|
||||
return NULL;
|
||||
@@ -96,16 +100,16 @@ void dfi_vmcoreinfo_init(void)
|
||||
addr = l.os_info->vmcoreinfo_addr;
|
||||
size = l.os_info->vmcoreinfo_size;
|
||||
} else {
|
||||
if (dfi_mem_read_rc(LC_VMCORE_INFO, &addr, sizeof(addr)))
|
||||
if (dfi_mem_virt_read(LC_VMCORE_INFO, &addr, sizeof(addr)))
|
||||
return;
|
||||
if (addr == 0)
|
||||
return;
|
||||
if (dfi_mem_read_rc(addr, ¬e, sizeof(note)))
|
||||
if (dfi_mem_virt_read(addr, ¬e, sizeof(note)))
|
||||
return;
|
||||
if (note.n_namesz == 0 || note.n_namesz > sizeof(str))
|
||||
return;
|
||||
memset(str, 0, sizeof(str));
|
||||
if (dfi_mem_read_rc(addr + sizeof(note), str, note.n_namesz))
|
||||
if (dfi_mem_virt_read(addr + sizeof(note), str, note.n_namesz))
|
||||
return;
|
||||
if (memcmp(str, "VMCOREINFO", sizeof("VMCOREINFO")) != 0)
|
||||
return;
|
||||
@@ -116,7 +120,7 @@ void dfi_vmcoreinfo_init(void)
|
||||
"DFI vmcoreinfo addr 0x%016lx size 0x%016lx\n",
|
||||
addr, size);
|
||||
l.vmcoreinfo = zg_alloc(size + 1);
|
||||
if (dfi_mem_read_rc(addr, l.vmcoreinfo, size)) {
|
||||
if (dfi_mem_virt_read(addr, l.vmcoreinfo, size)) {
|
||||
zg_free(l.vmcoreinfo);
|
||||
l.vmcoreinfo = NULL;
|
||||
return;
|
||||
|
||||
20
zdump/dfi_vmcoreinfo.h
Normal file
20
zdump/dfi_vmcoreinfo.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2001, 2018
|
||||
*
|
||||
* 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 DFI_VMCOREINFO_H
|
||||
#define DFI_VMCOREINFO_H
|
||||
|
||||
void dfi_vmcoreinfo_init(void);
|
||||
char *dfi_vmcoreinfo_get(void);
|
||||
int dfi_vmcoreinfo_tag(char *str, int len, const char *sym);
|
||||
int dfi_vmcoreinfo_symbol(unsigned long *val, const char *sym);
|
||||
int dfi_vmcoreinfo_offset(unsigned long *offs, const char *sym);
|
||||
int dfi_vmcoreinfo_size(unsigned long *size, const char *sym);
|
||||
int dfi_vmcoreinfo_length(unsigned long *len, const char *sym);
|
||||
int dfi_vmcoreinfo_val(unsigned long *val, const char *sym);
|
||||
|
||||
#endif /* DFI_VMCOREINFO_H */
|
||||
@@ -10,7 +10,11 @@
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include "zgetdump.h"
|
||||
|
||||
#include "lib/util_list.h"
|
||||
|
||||
#include "dfi_mem_chunk.h"
|
||||
#include "dfo.h"
|
||||
|
||||
#define dfo_chunk_iterate(dfo_chunk) \
|
||||
util_list_iterate(&l.dump.chunk_list, dfo_chunk)
|
||||
|
||||
@@ -52,4 +52,10 @@ struct dfo {
|
||||
void (*init)(void);
|
||||
};
|
||||
|
||||
/*
|
||||
* Supported DFO dump formats
|
||||
*/
|
||||
extern struct dfo dfo_s390;
|
||||
extern struct dfo dfo_elf;
|
||||
|
||||
#endif /* DFO_H */
|
||||
|
||||
@@ -16,7 +16,11 @@
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "zgetdump.h"
|
||||
#include "df_elf.h"
|
||||
#include "dfi.h"
|
||||
#include "dfi_mem_chunk.h"
|
||||
#include "dfi_vmcoreinfo.h"
|
||||
#include "dfo.h"
|
||||
|
||||
#define HDR_PER_CPU_SIZE 0x4a0
|
||||
#define HDR_PER_MEMC_SIZE 0x100
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "zgetdump.h"
|
||||
#include "df_s390.h"
|
||||
#include "dfi_mem_chunk.h"
|
||||
#include "dfo.h"
|
||||
|
||||
/*
|
||||
* File local static data
|
||||
@@ -98,7 +100,8 @@ static void dfo_s390_dump_chunk_lc_fn(struct dfo_chunk *dump_chunk,
|
||||
struct dfi_cpu *cpu = dump_chunk->data;
|
||||
char lc[0x2000];
|
||||
|
||||
dfi_mem_read(cpu->prefix + off, &lc[off], cnt);
|
||||
if (dfi_mem_virt_read(cpu->prefix + off, &lc[off], cnt))
|
||||
return;
|
||||
if (dfi_arch() == DFI_ARCH_64)
|
||||
cpu2lc_64(lc, cpu);
|
||||
else
|
||||
@@ -142,7 +145,8 @@ static void add_cpu_to_dfo(struct dfi_cpu *cpu)
|
||||
return;
|
||||
if (!dfi_cpu_content_fac_check(DFI_CPU_CONTENT_FAC_VX))
|
||||
return;
|
||||
dfi_mem_read(cpu->prefix, &lc, sizeof(lc));
|
||||
if (dfi_mem_virt_read(cpu->prefix, &lc, sizeof(lc)))
|
||||
return;
|
||||
if (!dfi_cpu_lc_has_vx_sa(&lc))
|
||||
return;
|
||||
vx_regs = zg_alloc(DFI_VX_SA_SIZE);
|
||||
@@ -181,8 +185,8 @@ static void dump_chunks_init(void)
|
||||
dfi_cpu_iterate(cpu)
|
||||
add_cpu_to_dfo(cpu);
|
||||
dfo_chunk_add(dfi_mem_range() + DF_S390_HDR_SIZE,
|
||||
DF_S390_EM_SIZE,
|
||||
&l.em, dfo_chunk_buf_fn);
|
||||
DF_S390_EM_SIZE,
|
||||
&l.em, dfo_chunk_buf_fn);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
*/
|
||||
|
||||
#include "zgetdump.h"
|
||||
#include "zg.h"
|
||||
#include "dt.h"
|
||||
|
||||
/*
|
||||
* Supported dump tools
|
||||
|
||||
26
zdump/dt.h
26
zdump/dt.h
@@ -12,9 +12,10 @@
|
||||
#ifndef DT_H
|
||||
#define DT_H
|
||||
|
||||
#include "dfi.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "dfi.h"
|
||||
|
||||
#define DUMP_EXTENDED true
|
||||
#define DUMP_NON_EXTENDED false
|
||||
|
||||
@@ -24,12 +25,21 @@ struct dt {
|
||||
void (*info)(void);
|
||||
};
|
||||
|
||||
extern void dt_init(void);
|
||||
extern void dt_info_print(void);
|
||||
extern void dt_arch_set(enum dfi_arch arch);
|
||||
extern void dt_version_set(int version);
|
||||
extern void dt_attr_mem_limit_set(u64 mem_limit);
|
||||
extern void dt_attr_force_set(int value);
|
||||
extern void dt_attr_dasd_type_set(const char *dasd_type);
|
||||
void dt_init(void);
|
||||
void dt_info_print(void);
|
||||
void dt_arch_set(enum dfi_arch arch);
|
||||
void dt_version_set(int version);
|
||||
void dt_attr_mem_limit_set(u64 mem_limit);
|
||||
void dt_attr_force_set(int value);
|
||||
void dt_attr_dasd_type_set(const char *dasd_type);
|
||||
|
||||
/*
|
||||
* Supported s390 dumpers
|
||||
*/
|
||||
extern struct dt dt_s390mv;
|
||||
extern struct dt dt_s390mv_ext;
|
||||
extern struct dt dt_s390sv;
|
||||
extern struct dt dt_s390sv_ext;
|
||||
extern struct dt dt_scsi;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include "zgetdump.h"
|
||||
#include "dt.h"
|
||||
#include "df_s390.h"
|
||||
|
||||
/*
|
||||
* Initialize s390 multi-volume dump tool (for -d option)
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include "zgetdump.h"
|
||||
#include "dt.h"
|
||||
#include "df_s390.h"
|
||||
|
||||
/*
|
||||
* Initialize s390 multi-volume dump tool (extedend) for -d option
|
||||
|
||||
@@ -9,8 +9,13 @@
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <linux/fs.h>
|
||||
|
||||
#include "zgetdump.h"
|
||||
#include "zg.h"
|
||||
#include "dt.h"
|
||||
#include "df_s390.h"
|
||||
|
||||
/*
|
||||
* File local static data
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include "zgetdump.h"
|
||||
|
||||
#include "dt.h"
|
||||
#include "df_s390.h"
|
||||
|
||||
/*
|
||||
* Initialize s390 single-volume extended dump tool (for -d option)
|
||||
|
||||
@@ -9,13 +9,16 @@
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <linux/fs.h>
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
#include "lib/util_part.h"
|
||||
|
||||
#include "zgetdump.h"
|
||||
#include "zg.h"
|
||||
#include "dt.h"
|
||||
|
||||
/*
|
||||
* Single volume SCSI dump superblock
|
||||
|
||||
232
zdump/opts.c
232
zdump/opts.c
@@ -16,42 +16,59 @@
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
#include "lib/util_log.h"
|
||||
#include "zgetdump.h"
|
||||
|
||||
#include "opts.h"
|
||||
|
||||
static struct option long_opts[] = {
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
{"version", no_argument, NULL, 'v'},
|
||||
{"info", no_argument, NULL, 'i'},
|
||||
{"device", no_argument, NULL, 'd'},
|
||||
{"mount", no_argument, NULL, 'm'},
|
||||
{"umount", no_argument, NULL, 'u'},
|
||||
{"fmt", required_argument, NULL, 'f'},
|
||||
{"select", required_argument, NULL, 's'},
|
||||
{"debug", no_argument, NULL, 'X'},
|
||||
{"verbose", no_argument, NULL, 'V'},
|
||||
{NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
static const char optstr[] = "hvVidmus:f:X";
|
||||
|
||||
/*
|
||||
* Text for --help option
|
||||
*/
|
||||
static char help_text[] =
|
||||
"Usage: zgetdump DUMP [-s SYS] [-f FMT] > DUMP_FILE\n"
|
||||
" -m DUMP [-s SYS] [-f FMT] DIR\n"
|
||||
" -i DUMP [-s SYS]\n"
|
||||
" -d DUMPDEV\n"
|
||||
" -u DIR\n"
|
||||
"\n"
|
||||
"The zgetdump tool can read different dump formats from a dump device or from\n"
|
||||
"a dump file. You can use zgetdump to:\n"
|
||||
"\n"
|
||||
" - Write the dump content to standard output or to a file\n"
|
||||
" - Mount the dump content to a Linux directory\n"
|
||||
" - Convert a dump to a different dump format\n"
|
||||
" - Check if a dump is valid\n"
|
||||
" - Check if a DASD contains a valid dump tool.\n"
|
||||
"\n"
|
||||
"In the syntax description, DUMP specifies a dump device or dump file to be\n"
|
||||
"read. The following options are available:\n"
|
||||
"\n"
|
||||
"-m, --mount Mount DUMP to mount point DIR\n"
|
||||
"-u, --umount Unmount dump from mount point DIR\n"
|
||||
"-i, --info Print DUMP information\n"
|
||||
"-f, --fmt Specify target dump format FMT (\"elf\" or \"s390\")\n"
|
||||
"-s, --select Select system data SYS (\"kdump\", \"prod\", or \"all\")\n"
|
||||
"-d, --device Print DUMPDEV (dump device) information\n"
|
||||
"-v, --version Print version information, then exit\n"
|
||||
"-V, --verbose Print verbose messages to stdout. Repeat this option\n"
|
||||
" for increased verbosity from just error messages to\n"
|
||||
" also include warning, information, debug, and trace\n"
|
||||
" messages. This option is intended for debugging\n"
|
||||
"-h, --help Print this help, then exit\n";
|
||||
static const char help_text[] =
|
||||
"Usage: zgetdump DUMP [-s SYS] [-f FMT] > DUMP_FILE\n"
|
||||
" -m DUMP [-s SYS] [-f FMT] DIR\n"
|
||||
" -i DUMP [-s SYS]\n"
|
||||
" -d DUMPDEV\n"
|
||||
" -u DIR\n"
|
||||
"\n"
|
||||
"The zgetdump tool can read different dump formats from a dump device or from\n"
|
||||
"a dump file. You can use zgetdump to:\n"
|
||||
"\n"
|
||||
" - Write the dump content to standard output or to a file\n"
|
||||
" - Mount the dump content to a Linux directory\n"
|
||||
" - Convert a dump to a different dump format\n"
|
||||
" - Check if a dump is valid\n"
|
||||
" - Check if a DASD contains a valid dump tool.\n"
|
||||
"\n"
|
||||
"In the syntax description, DUMP specifies a dump device or dump file to be\n"
|
||||
"read. The following options are available:\n"
|
||||
"\n"
|
||||
"-m, --mount Mount DUMP to mount point DIR\n"
|
||||
"-u, --umount Unmount dump from mount point DIR\n"
|
||||
"-i, --info Print DUMP information\n"
|
||||
"-f, --fmt Specify target dump format FMT (\"elf\" or \"s390\")\n"
|
||||
"-s, --select Select system data SYS (\"kdump\", \"prod\", or \"all\")\n"
|
||||
"-d, --device Print DUMPDEV (dump device) information\n"
|
||||
"-v, --version Print version information, then exit\n"
|
||||
"-V, --verbose Print verbose messages to stdout. Repeat this option\n"
|
||||
" for increased verbosity from just error messages to\n"
|
||||
" also include warning, information, debug, and trace\n"
|
||||
" messages. This option is intended for debugging\n"
|
||||
"-h, --help Print this help, then exit\n";
|
||||
|
||||
static const char copyright_str[] = "Copyright IBM Corp. 2001, 2018";
|
||||
|
||||
@@ -65,27 +82,26 @@ const char *OPTS_SELECT_ALL = "all";
|
||||
/*
|
||||
* Initialize default settings
|
||||
*/
|
||||
static void init_defaults(void)
|
||||
static void init_defaults(struct options *opts)
|
||||
{
|
||||
g.prog_name = "zgetdump";
|
||||
g.opts.action = ZG_ACTION_STDOUT;
|
||||
opts->prog_name = "zgetdump";
|
||||
opts->action = ZG_ACTION_COPY;
|
||||
#ifdef __s390x__
|
||||
g.opts.fmt = "elf";
|
||||
opts->fmt = "elf";
|
||||
#else
|
||||
g.opts.fmt = "s390";
|
||||
opts->fmt = "s390";
|
||||
#endif
|
||||
dfo_set(g.opts.fmt);
|
||||
/* Verbose logging */
|
||||
g.opts.verbose = UTIL_LOG_ERROR;
|
||||
util_log_set_level(g.opts.verbose);
|
||||
opts->verbose = UTIL_LOG_ERROR;
|
||||
util_log_set_level(opts->verbose);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print "help" hint
|
||||
*/
|
||||
static void __noreturn print_usage_exit(void)
|
||||
static void __noreturn print_usage_exit(const char *prog_name)
|
||||
{
|
||||
STDERR("Try '%s --help' for more information.\n", g.prog_name);
|
||||
STDERR("Try '%s --help' for more information.\n", prog_name);
|
||||
zg_exit(1);
|
||||
}
|
||||
|
||||
@@ -101,10 +117,10 @@ static void __noreturn print_help_exit(void)
|
||||
/*
|
||||
* Print version information
|
||||
*/
|
||||
static void __noreturn print_version_exit(void)
|
||||
static void __noreturn print_version_exit(const char *prog_name)
|
||||
{
|
||||
STDOUT("%s: Tool for copying and converting dumps version %s\n",
|
||||
g.prog_name, RELEASE_STRING);
|
||||
prog_name, RELEASE_STRING);
|
||||
STDOUT("%s\n", copyright_str);
|
||||
zg_exit(0);
|
||||
}
|
||||
@@ -112,96 +128,94 @@ static void __noreturn print_version_exit(void)
|
||||
/*
|
||||
* Set "--fmt" option
|
||||
*/
|
||||
static void fmt_set(const char *fmt)
|
||||
static void fmt_set(struct options *opts, const char *fmt)
|
||||
{
|
||||
if (dfo_set(fmt) != 0)
|
||||
ERR_EXIT("Invalid target format \"%s\" specified", fmt);
|
||||
g.opts.fmt_specified = 1;
|
||||
g.opts.fmt = fmt;
|
||||
opts->fmt_specified = 1;
|
||||
opts->fmt = fmt;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set "--select" option
|
||||
*/
|
||||
static void select_set(const char *select)
|
||||
static void select_set(struct options *opts, const char *select)
|
||||
{
|
||||
if (strcmp(select, OPTS_SELECT_KDUMP) == 0)
|
||||
g.opts.select = OPTS_SELECT_KDUMP;
|
||||
opts->select = OPTS_SELECT_KDUMP;
|
||||
else if (strcmp(select, OPTS_SELECT_PROD) == 0)
|
||||
g.opts.select = OPTS_SELECT_PROD;
|
||||
opts->select = OPTS_SELECT_PROD;
|
||||
else if (strcmp(select, OPTS_SELECT_ALL) == 0)
|
||||
g.opts.select = OPTS_SELECT_ALL;
|
||||
opts->select = OPTS_SELECT_ALL;
|
||||
else
|
||||
ERR_EXIT("Invalid select argument \"%s\" specified", select);
|
||||
g.opts.select_specified = 1;
|
||||
opts->select_specified = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set mount point
|
||||
*/
|
||||
static void mount_point_set(const char *mount_point)
|
||||
static void mount_point_set(struct options *opts, const char *mount_point)
|
||||
{
|
||||
g.opts.mount_point = zg_strdup(mount_point);
|
||||
opts->mount_point = zg_strdup(mount_point);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set device
|
||||
*/
|
||||
static void device_set(const char *path)
|
||||
static void device_set(struct options *opts, const char *path)
|
||||
{
|
||||
g.opts.device = zg_strdup(path);
|
||||
opts->device = zg_strdup(path);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set FUSE debug options
|
||||
*/
|
||||
static void argv_fuse_set(char **argv, int argc)
|
||||
static void argv_fuse_set(struct options *opts, char **argv, int argc)
|
||||
{
|
||||
int i;
|
||||
|
||||
g.opts.argv_fuse = argv;
|
||||
g.opts.argc_fuse = argc;
|
||||
opts->argv_fuse = argv;
|
||||
opts->argc_fuse = argc;
|
||||
|
||||
STDERR_PR("Fuse Options: ");
|
||||
for (i = 0; i < argc; i++)
|
||||
STDERR("%s ", g.opts.argv_fuse[i]);
|
||||
STDERR("%s ", opts->argv_fuse[i]);
|
||||
STDERR("\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Set action
|
||||
*/
|
||||
static void action_set(enum zg_action action)
|
||||
static void action_set(struct options *opts, enum zg_action action)
|
||||
{
|
||||
if (g.opts.action_specified)
|
||||
if (opts->action_specified)
|
||||
ERR_EXIT("Please specify only one of the \"-i\", \"-d\", "
|
||||
"\"-m\" or \"-u\" option");
|
||||
g.opts.action = action;
|
||||
g.opts.action_specified = 1;
|
||||
opts->action = action;
|
||||
opts->action_specified = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify option combinations
|
||||
*/
|
||||
static void verify_opts(void)
|
||||
static void verify_opts(struct options *opts)
|
||||
{
|
||||
if (g.opts.select_specified) {
|
||||
if (g.opts.action != ZG_ACTION_MOUNT &&
|
||||
g.opts.action != ZG_ACTION_STDOUT &&
|
||||
g.opts.action != ZG_ACTION_DUMP_INFO)
|
||||
if (opts->select_specified) {
|
||||
if (opts->action != ZG_ACTION_MOUNT &&
|
||||
opts->action != ZG_ACTION_COPY &&
|
||||
opts->action != ZG_ACTION_DUMP_INFO)
|
||||
ERR_EXIT("The \"--select\" option can only be "
|
||||
"specified for info, mount, or copy");
|
||||
}
|
||||
if (!g.opts.fmt_specified)
|
||||
if (!opts->fmt_specified)
|
||||
return;
|
||||
|
||||
if (g.opts.action == ZG_ACTION_DUMP_INFO)
|
||||
if (opts->action == ZG_ACTION_DUMP_INFO)
|
||||
ERR_EXIT("The \"--fmt\" option cannot be specified "
|
||||
"together with \"--info\"");
|
||||
if (g.opts.action == ZG_ACTION_DEVICE_INFO)
|
||||
if (opts->action == ZG_ACTION_DEVICE_INFO)
|
||||
ERR_EXIT("The \"--fmt\" option cannot be specified "
|
||||
"together with \"--device\"");
|
||||
if (g.opts.action == ZG_ACTION_UMOUNT)
|
||||
if (opts->action == ZG_ACTION_UMOUNT)
|
||||
ERR_EXIT("The \"--fmt\" option cannot be specified "
|
||||
"together with \"--umount\"");
|
||||
}
|
||||
@@ -209,36 +223,38 @@ static void verify_opts(void)
|
||||
/*
|
||||
* Parse positional arguments
|
||||
*/
|
||||
static void parse_pos_args(char *argv[], int argc)
|
||||
static void parse_pos_args(struct options *opts, char *argv[], int argc)
|
||||
{
|
||||
int pos_args = argc - optind;
|
||||
|
||||
switch (g.opts.action) {
|
||||
case ZG_ACTION_STDOUT:
|
||||
switch (opts->action) {
|
||||
case ZG_ACTION_COPY:
|
||||
case ZG_ACTION_DUMP_INFO:
|
||||
case ZG_ACTION_DEVICE_INFO:
|
||||
if (pos_args == 0)
|
||||
ERR_EXIT("No device or dump specified");
|
||||
if (pos_args > 1 && !g.opts.debug_specified)
|
||||
if (pos_args > 1)
|
||||
ERR_EXIT("Too many positional parameters specified");
|
||||
device_set(argv[optind]);
|
||||
device_set(opts, argv[optind]);
|
||||
break;
|
||||
case ZG_ACTION_MOUNT:
|
||||
if (pos_args == 0)
|
||||
ERR_EXIT("No dump specified");
|
||||
if (pos_args == 1)
|
||||
ERR_EXIT("No mount point specified");
|
||||
if (pos_args > 2 && !g.opts.debug_specified)
|
||||
if (pos_args > 2 && !opts->debug_specified)
|
||||
ERR_EXIT("Too many positional parameters specified");
|
||||
device_set(argv[optind]);
|
||||
mount_point_set(argv[optind + 1]);
|
||||
if (g.opts.debug_specified && pos_args > 2)
|
||||
argv_fuse_set(&argv[optind + 2], pos_args - 2);
|
||||
device_set(opts, argv[optind]);
|
||||
mount_point_set(opts, argv[optind + 1]);
|
||||
if (opts->debug_specified && pos_args > 2)
|
||||
argv_fuse_set(opts, &argv[optind + 2], pos_args - 2);
|
||||
break;
|
||||
case ZG_ACTION_UMOUNT:
|
||||
if (pos_args == 0)
|
||||
ERR_EXIT("No mount point specified");
|
||||
mount_point_set(argv[optind]);
|
||||
if (pos_args > 1)
|
||||
ERR_EXIT("Too many positional parameters specified");
|
||||
mount_point_set(opts, argv[optind]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -246,60 +262,46 @@ static void parse_pos_args(char *argv[], int argc)
|
||||
/*
|
||||
* Main command line parsing function
|
||||
*/
|
||||
void opts_parse(int argc, char *argv[])
|
||||
void opts_parse(int argc, char *argv[], struct options *opts)
|
||||
{
|
||||
int opt, idx;
|
||||
static struct option long_opts[] = {
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
{"version", no_argument, NULL, 'v'},
|
||||
{"info", no_argument, NULL, 'i'},
|
||||
{"device", no_argument, NULL, 'd'},
|
||||
{"mount", no_argument, NULL, 'm'},
|
||||
{"umount", no_argument, NULL, 'u'},
|
||||
{"fmt", required_argument, NULL, 'f'},
|
||||
{"select", required_argument, NULL, 's'},
|
||||
{"debug", no_argument, NULL, 'X'},
|
||||
{"verbose", no_argument, NULL, 'V'},
|
||||
{NULL, 0, NULL, 0 }
|
||||
};
|
||||
static const char optstr[] = "hvVidmus:f:X";
|
||||
|
||||
init_defaults();
|
||||
init_defaults(opts);
|
||||
while ((opt = getopt_long(argc, argv, optstr, long_opts, &idx)) != -1) {
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
print_help_exit();
|
||||
case 'v':
|
||||
print_version_exit();
|
||||
print_version_exit(opts->prog_name);
|
||||
case 'V':
|
||||
g.opts.verbose++;
|
||||
util_log_set_level(g.opts.verbose);
|
||||
opts->verbose++;
|
||||
util_log_set_level(opts->verbose);
|
||||
break;
|
||||
case 'i':
|
||||
action_set(ZG_ACTION_DUMP_INFO);
|
||||
action_set(opts, ZG_ACTION_DUMP_INFO);
|
||||
break;
|
||||
case 'd':
|
||||
action_set(ZG_ACTION_DEVICE_INFO);
|
||||
action_set(opts, ZG_ACTION_DEVICE_INFO);
|
||||
break;
|
||||
case 'm':
|
||||
action_set(ZG_ACTION_MOUNT);
|
||||
action_set(opts, ZG_ACTION_MOUNT);
|
||||
break;
|
||||
case 'u':
|
||||
action_set(ZG_ACTION_UMOUNT);
|
||||
action_set(opts, ZG_ACTION_UMOUNT);
|
||||
break;
|
||||
case 'f':
|
||||
fmt_set(optarg);
|
||||
fmt_set(opts, optarg);
|
||||
break;
|
||||
case 's':
|
||||
select_set(optarg);
|
||||
select_set(opts, optarg);
|
||||
break;
|
||||
case 'X':
|
||||
g.opts.debug_specified = 1;
|
||||
opts->debug_specified = 1;
|
||||
break;
|
||||
default:
|
||||
print_usage_exit();
|
||||
print_usage_exit(opts->prog_name);
|
||||
}
|
||||
}
|
||||
parse_pos_args(argv, argc);
|
||||
verify_opts();
|
||||
parse_pos_args(opts, argv, argc);
|
||||
verify_opts(opts);
|
||||
}
|
||||
|
||||
38
zdump/opts.h
Normal file
38
zdump/opts.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2001, 2018
|
||||
*
|
||||
* 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 OPTS_H
|
||||
#define OPTS_H
|
||||
|
||||
#include "zg.h"
|
||||
|
||||
/*
|
||||
* zgetdump options
|
||||
*/
|
||||
struct options {
|
||||
const char *prog_name;
|
||||
int action_specified;
|
||||
enum zg_action action;
|
||||
char *device;
|
||||
char *mount_point;
|
||||
int fmt_specified;
|
||||
const char *fmt;
|
||||
int debug_specified;
|
||||
char **argv_fuse;
|
||||
int argc_fuse;
|
||||
const char *select;
|
||||
int select_specified;
|
||||
int verbose;
|
||||
};
|
||||
|
||||
extern const char *OPTS_SELECT_KDUMP;
|
||||
extern const char *OPTS_SELECT_PROD;
|
||||
extern const char *OPTS_SELECT_ALL;
|
||||
|
||||
void opts_parse(int argc, char *argv[], struct options *opts);
|
||||
|
||||
#endif /* OPTS_H */
|
||||
@@ -9,13 +9,16 @@
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include "zgetdump.h"
|
||||
#include "zg.h"
|
||||
#include "dfi.h"
|
||||
#include "dfo.h"
|
||||
#include "stdout.h"
|
||||
|
||||
int stdout_write_dump(void)
|
||||
{
|
||||
u64 cnt, written = 0;
|
||||
char buf[32768];
|
||||
ssize_t rc;
|
||||
const u64 output_size = dfo_size();
|
||||
char buf[8UL * PAGE_SIZE];
|
||||
u64 written = 0;
|
||||
|
||||
if (!dfi_feat_copy())
|
||||
ERR_EXIT("Copying not possible for %s dumps", dfi_name());
|
||||
@@ -23,8 +26,11 @@ int stdout_write_dump(void)
|
||||
STDERR(" Source: %s\n", dfi_name());
|
||||
STDERR(" Target: %s\n", dfo_name());
|
||||
STDERR("\n");
|
||||
zg_progress_init("Copying dump", dfo_size());
|
||||
do {
|
||||
zg_progress_init("Copying dump", output_size);
|
||||
while (written != output_size) {
|
||||
ssize_t rc;
|
||||
u64 cnt;
|
||||
|
||||
cnt = dfo_read(buf, sizeof(buf));
|
||||
rc = write(STDOUT_FILENO, buf, cnt);
|
||||
if (rc == -1)
|
||||
@@ -33,7 +39,7 @@ int stdout_write_dump(void)
|
||||
ERR_EXIT("Error: Could not write full block");
|
||||
written += cnt;
|
||||
zg_progress(written);
|
||||
} while (written != dfo_size());
|
||||
};
|
||||
STDERR("\n");
|
||||
STDERR("Success: Dump has been copied\n");
|
||||
return 0;
|
||||
|
||||
13
zdump/stdout.h
Normal file
13
zdump/stdout.h
Normal file
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2001, 2018
|
||||
*
|
||||
* 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 STDOUT_H
|
||||
#define STDOUT_H
|
||||
|
||||
int stdout_write_dump(void);
|
||||
|
||||
#endif /* STDOUT_H */
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <fuse.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
@@ -23,6 +24,10 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include "zgetdump.h"
|
||||
#include "zg.h"
|
||||
#include "dfi.h"
|
||||
#include "dfo.h"
|
||||
#include "zfuse.h"
|
||||
|
||||
#define DUMP_PATH_MAX 100
|
||||
|
||||
|
||||
25
zdump/zfuse.h
Normal file
25
zdump/zfuse.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2001, 2018
|
||||
*
|
||||
* 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 ZFUSE_H
|
||||
#define ZFUSE_H
|
||||
|
||||
#if HAVE_FUSE == 0
|
||||
static inline int zfuse_mount_dump(void)
|
||||
{
|
||||
ERR_EXIT("Program compiled without fuse support");
|
||||
}
|
||||
static inline void zfuse_umount(void)
|
||||
{
|
||||
ERR_EXIT("Program compiled without fuse support");
|
||||
}
|
||||
#else
|
||||
int zfuse_mount_dump(void);
|
||||
void zfuse_umount(void);
|
||||
#endif
|
||||
|
||||
#endif /* ZFUSE_H */
|
||||
@@ -188,7 +188,7 @@ extern enum zg_type zg_type(struct zg_fh *zg_fh);
|
||||
* zgetdump actions
|
||||
*/
|
||||
enum zg_action {
|
||||
ZG_ACTION_STDOUT,
|
||||
ZG_ACTION_COPY,
|
||||
ZG_ACTION_DUMP_INFO,
|
||||
ZG_ACTION_DEVICE_INFO,
|
||||
ZG_ACTION_MOUNT,
|
||||
|
||||
@@ -27,6 +27,11 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include "zgetdump.h"
|
||||
#include "dt.h"
|
||||
#include "dfi.h"
|
||||
#include "dfo.h"
|
||||
#include "stdout.h"
|
||||
#include "zfuse.h"
|
||||
|
||||
/*
|
||||
* Globals
|
||||
@@ -154,7 +159,7 @@ static int do_mount(void)
|
||||
/*
|
||||
* Run "copy to stdout" action
|
||||
*/
|
||||
static int do_stdout(void)
|
||||
static int do_copy(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
@@ -173,11 +178,14 @@ static int do_stdout(void)
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
sig_handler_init();
|
||||
opts_parse(argc, argv);
|
||||
opts_parse(argc, argv, &g.opts);
|
||||
|
||||
if (dfo_set(g.opts.fmt) != 0)
|
||||
ERR_EXIT("Invalid target format \"%s\" specified", g.opts.fmt);
|
||||
|
||||
switch (g.opts.action) {
|
||||
case ZG_ACTION_STDOUT:
|
||||
return do_stdout();
|
||||
case ZG_ACTION_COPY:
|
||||
return do_copy();
|
||||
case ZG_ACTION_DUMP_INFO:
|
||||
return do_dump_info();
|
||||
case ZG_ACTION_DEVICE_INFO:
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* Main include file - Should be included by all source files
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 2018
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
@@ -12,92 +10,17 @@
|
||||
#ifndef ZGETDUMP_H
|
||||
#define ZGETDUMP_H
|
||||
|
||||
#include "df_elf.h"
|
||||
#include "df_lkcd.h"
|
||||
#include "df_s390.h"
|
||||
#include "dfi.h"
|
||||
#include "dfo.h"
|
||||
#include "dt.h"
|
||||
#include "zg.h"
|
||||
|
||||
/*
|
||||
* zgetdump options
|
||||
*/
|
||||
struct options {
|
||||
int action_specified;
|
||||
enum zg_action action;
|
||||
char *device;
|
||||
char *mount_point;
|
||||
int fmt_specified;
|
||||
const char *fmt;
|
||||
int debug_specified;
|
||||
char **argv_fuse;
|
||||
int argc_fuse;
|
||||
const char *select;
|
||||
int select_specified;
|
||||
int verbose;
|
||||
};
|
||||
|
||||
extern const char *OPTS_SELECT_KDUMP;
|
||||
extern const char *OPTS_SELECT_PROD;
|
||||
extern const char *OPTS_SELECT_ALL;
|
||||
#include "opts.h"
|
||||
|
||||
/*
|
||||
* zgetdump globals
|
||||
*/
|
||||
extern struct zgetdump_globals {
|
||||
struct zgetdump_globals {
|
||||
struct zg_fh *fh;
|
||||
const char *prog_name;
|
||||
struct options opts;
|
||||
} g;
|
||||
};
|
||||
|
||||
/*
|
||||
* Misc fuctions
|
||||
*/
|
||||
extern void opts_parse(int argc, char *argv[]);
|
||||
extern int stdout_write_dump(void);
|
||||
|
||||
#if HAVE_FUSE == 0
|
||||
static inline int zfuse_mount_dump(void)
|
||||
{
|
||||
ERR_EXIT("Program compiled without fuse support");
|
||||
}
|
||||
static inline void zfuse_umount(void)
|
||||
{
|
||||
ERR_EXIT("Program compiled without fuse support");
|
||||
}
|
||||
#else
|
||||
int zfuse_mount_dump(void);
|
||||
void zfuse_umount(void);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Supported DFI dump formats
|
||||
*/
|
||||
extern struct dfi dfi_s390tape;
|
||||
extern struct dfi dfi_s390mv;
|
||||
extern struct dfi dfi_s390mv_ext;
|
||||
extern struct dfi dfi_s390;
|
||||
extern struct dfi dfi_s390_ext;
|
||||
extern struct dfi dfi_lkcd;
|
||||
extern struct dfi dfi_elf;
|
||||
extern struct dfi dfi_kdump;
|
||||
extern struct dfi dfi_kdump_flat;
|
||||
extern struct dfi dfi_devmem;
|
||||
|
||||
/*
|
||||
* Supported DFO dump formats
|
||||
*/
|
||||
extern struct dfo dfo_s390;
|
||||
extern struct dfo dfo_elf;
|
||||
|
||||
/*
|
||||
* Supported s390 dumpers
|
||||
*/
|
||||
extern struct dt dt_s390mv;
|
||||
extern struct dt dt_s390mv_ext;
|
||||
extern struct dt dt_s390sv;
|
||||
extern struct dt dt_s390sv_ext;
|
||||
extern struct dt dt_scsi;
|
||||
extern struct zgetdump_globals g;
|
||||
|
||||
#endif /* ZGETDUMP_H */
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Utility classes to print framsets
|
||||
*
|
||||
* Copyright IBM Corp. 2008, 2017
|
||||
* Copyright IBM Corp. 2008, 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.
|
||||
@@ -478,21 +478,22 @@ void VirtAdapterPrinter::print_failures(FILE *fp, const struct ioerr_cnt *cnt)
|
||||
}
|
||||
|
||||
void VirtAdapterPrinter::print_throughput(FILE *fp,
|
||||
const struct blkiomon_stat *stat)
|
||||
const struct blkiomon_stat *stat,
|
||||
const __u64 interval)
|
||||
{
|
||||
double tmp;
|
||||
|
||||
if (!stat || stat->d2c_r.num <= 0 || stat->size_r.num <= 0)
|
||||
if (!stat || interval == 0 || stat->size_r.num <= 0)
|
||||
tmp = 0;
|
||||
else
|
||||
tmp = stat->size_r.sum/(double)stat->d2c_r.sum;
|
||||
tmp = calc_avg(stat->size_r.sum, interval);
|
||||
print_delimiter(fp);
|
||||
print_abbrev_num(fp, tmp);
|
||||
|
||||
if (!stat || stat->d2c_w.num <= 0 || stat->size_w.num <= 0)
|
||||
if (!stat || interval == 0 || stat->size_w.num <= 0)
|
||||
tmp = 0;
|
||||
else
|
||||
tmp = stat->size_w.sum/(double)stat->d2c_w.sum;
|
||||
tmp = calc_avg(stat->size_w.sum, interval);
|
||||
print_delimiter(fp);
|
||||
print_abbrev_num(fp, tmp);
|
||||
}
|
||||
@@ -536,6 +537,12 @@ int VirtAdapterPrinter::print_frame(FILE *fp,
|
||||
const struct blkiomon_stat *blk_stat;
|
||||
const struct zfcpdd_dstat *zfcp_stat;
|
||||
|
||||
/**
|
||||
* Receive Frameset interval and convert it
|
||||
* from seconds to microseconds
|
||||
*/
|
||||
__u64 interval = frameset.get_duration() * 1000000;
|
||||
|
||||
list<__u32> devnos;
|
||||
devnos = ((StagedDeviceFilter*)&dev_filt)->get_filter_devnos();
|
||||
|
||||
@@ -557,7 +564,7 @@ int VirtAdapterPrinter::print_frame(FILE *fp,
|
||||
print_queue_fill(fp, zfcp_stat, util);
|
||||
print_queue_full(fp, util);
|
||||
print_failures(fp, ioerr);
|
||||
print_throughput(fp, blk_stat);
|
||||
print_throughput(fp, blk_stat, interval);
|
||||
print_num_requests(fp, blk_stat);
|
||||
if (lrc) {
|
||||
fprintf(stderr, "%s: Did not find matching data in"
|
||||
@@ -810,6 +817,12 @@ int TrafficPrinter::print_frame(FILE *fp, const Frameset &frameset,
|
||||
const struct zfcpdd_dstat *zfcp_stat = NULL;
|
||||
const AggregationCollapser *agg_col;
|
||||
|
||||
/**
|
||||
* Receive Frameset interval and convert it
|
||||
* from seconds to microseconds
|
||||
*/
|
||||
__u64 interval = frameset.get_duration() * 1000000;
|
||||
|
||||
switch (m_agg_crit) {
|
||||
case none:
|
||||
get_device_list(lst_32, dev_filt);
|
||||
@@ -847,7 +860,7 @@ int TrafficPrinter::print_frame(FILE *fp, const Frameset &frameset,
|
||||
blk_stat = frameset.get_blkiomon_stat_by_wwpn(*i);
|
||||
zfcp_stat = frameset.get_zfcpdd_stat_by_wwpn(*i);
|
||||
print_device_wwpn(fp, *i);
|
||||
print_data_row(fp, blk_stat, zfcp_stat);
|
||||
print_data_row(fp, blk_stat, zfcp_stat, interval);
|
||||
}
|
||||
}
|
||||
else if (m_agg_crit == all) {
|
||||
@@ -856,7 +869,7 @@ int TrafficPrinter::print_frame(FILE *fp, const Frameset &frameset,
|
||||
blk_stat = frameset.get_first_blkiomon_stat();
|
||||
zfcp_stat = frameset.get_first_zfcpdd_stat();
|
||||
print_device_all(fp);
|
||||
print_data_row(fp, blk_stat, zfcp_stat);
|
||||
print_data_row(fp, blk_stat, zfcp_stat, interval);
|
||||
}
|
||||
else {
|
||||
for (list<__u32>::const_iterator i = lst_32.begin();
|
||||
@@ -894,7 +907,7 @@ int TrafficPrinter::print_frame(FILE *fp, const Frameset &frameset,
|
||||
}
|
||||
if (rc )
|
||||
return -1;
|
||||
print_data_row(fp, blk_stat, zfcp_stat);
|
||||
print_data_row(fp, blk_stat, zfcp_stat, interval);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -930,7 +943,8 @@ void SummaryTrafficPrinter::print_topline(FILE *fp)
|
||||
}
|
||||
|
||||
|
||||
void SummaryTrafficPrinter::print_throughput(FILE *fp, const struct blkiomon_stat *stat)
|
||||
void SummaryTrafficPrinter::print_throughput(FILE *fp, const struct blkiomon_stat *stat,
|
||||
const __u64 interval)
|
||||
{
|
||||
struct minmax thrp_data, total_size, total_latency;
|
||||
double tmp;
|
||||
@@ -968,7 +982,7 @@ void SummaryTrafficPrinter::print_throughput(FILE *fp, const struct blkiomon_sta
|
||||
|
||||
tmp = 0;
|
||||
if (stat && total_size.sum > 0)
|
||||
tmp = calc_avg(total_size.sum, total_latency.sum);
|
||||
tmp = calc_avg(total_size.sum, interval);
|
||||
print_delimiter(fp);
|
||||
print_abbrev_num(fp, tmp);
|
||||
|
||||
@@ -1101,14 +1115,15 @@ void SummaryTrafficPrinter::print_fabric_latency(FILE *fp, const struct zfcpdd_d
|
||||
|
||||
void SummaryTrafficPrinter::print_data_row(FILE *fp,
|
||||
const struct blkiomon_stat *blk_stat,
|
||||
const struct zfcpdd_dstat *zfcp_stat)
|
||||
const struct zfcpdd_dstat *zfcp_stat,
|
||||
const __u64 interval)
|
||||
{
|
||||
if (!blk_stat)
|
||||
blk_stat = get_empty_blkiomon_stat();
|
||||
if (!zfcp_stat)
|
||||
zfcp_stat = get_empty_zfcpdd_dstat();
|
||||
|
||||
print_throughput(fp, blk_stat);
|
||||
print_throughput(fp, blk_stat, interval);
|
||||
print_request_stats(fp, blk_stat);
|
||||
print_io_subsystem_latency(fp, blk_stat);
|
||||
print_channel_latency(fp, zfcp_stat);
|
||||
@@ -1177,7 +1192,8 @@ void DetailedTrafficPrinter::print_topline(FILE *fp)
|
||||
|
||||
void DetailedTrafficPrinter::print_data_row(FILE *fp,
|
||||
const struct blkiomon_stat *blk_stat,
|
||||
const struct zfcpdd_dstat *zfcp_stat)
|
||||
const struct zfcpdd_dstat *zfcp_stat,
|
||||
const __u64 interval __attribute__ ((unused)))
|
||||
{
|
||||
if (!blk_stat)
|
||||
blk_stat = get_empty_blkiomon_stat();
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Utility classes to print framsets
|
||||
*
|
||||
* Copyright IBM Corp. 2008, 2017
|
||||
* Copyright IBM Corp. 2008, 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.
|
||||
@@ -128,7 +128,8 @@ private:
|
||||
const struct adapter_utilization *res);
|
||||
void print_queue_full(FILE *fp, const struct adapter_utilization *res);
|
||||
void print_failures(FILE *fp, const struct ioerr_cnt *cnt);
|
||||
void print_throughput(FILE *fp, const struct blkiomon_stat *stat);
|
||||
void print_throughput(FILE *fp, const struct blkiomon_stat *stat,
|
||||
const __u64 interval);
|
||||
void print_num_requests(FILE *fp, const struct blkiomon_stat *stat);
|
||||
};
|
||||
|
||||
@@ -147,7 +148,8 @@ protected:
|
||||
* Print the actual row, excluding the first column */
|
||||
virtual void print_data_row(FILE *fp,
|
||||
const struct blkiomon_stat *blk_stat,
|
||||
const struct zfcpdd_dstat *zfcp_stat) = 0;
|
||||
const struct zfcpdd_dstat *zfcp_stat,
|
||||
const __u64 interval) = 0;
|
||||
|
||||
void print_topline_prefix1(FILE *fp);
|
||||
void print_topline_prefix2(FILE *fp);
|
||||
@@ -185,8 +187,10 @@ public:
|
||||
private:
|
||||
virtual void print_data_row(FILE *fp,
|
||||
const struct blkiomon_stat *blk_stat,
|
||||
const struct zfcpdd_dstat *zfcp_stat);
|
||||
void print_throughput(FILE *fp, const struct blkiomon_stat *stat);
|
||||
const struct zfcpdd_dstat *zfcp_stat,
|
||||
const __u64 interval);
|
||||
void print_throughput(FILE *fp, const struct blkiomon_stat *stat,
|
||||
const __u64 interval);
|
||||
void print_request_stats(FILE *fp, const struct blkiomon_stat *stat);
|
||||
void print_io_subsystem_latency(FILE *fp, const struct blkiomon_stat *stat);
|
||||
void print_channel_latency(FILE *fp, const struct zfcpdd_dstat *stat);
|
||||
@@ -207,7 +211,8 @@ public:
|
||||
private:
|
||||
virtual void print_data_row(FILE *fp,
|
||||
const struct blkiomon_stat *blk_stat,
|
||||
const struct zfcpdd_dstat *zfcp_stat);
|
||||
const struct zfcpdd_dstat *zfcp_stat,
|
||||
const __u64 interval);
|
||||
void print_histogram_io_reqs(FILE *fp,
|
||||
const struct blkiomon_stat *stat);
|
||||
void print_histogram_io_subs_lat(FILE *fp,
|
||||
|
||||
@@ -143,12 +143,13 @@ int menu(void)
|
||||
int rc;
|
||||
|
||||
cmd_line_extra = (char *)COMMAND_LINE_EXTRA;
|
||||
memset(cmd_line_extra, 0, COMMAND_LINE_EXTRA_SIZE);
|
||||
|
||||
rc = sclp_setup(SCLP_INIT);
|
||||
if (rc)
|
||||
/* sclp setup failed boot default */
|
||||
goto boot;
|
||||
|
||||
memset(cmd_line_extra, 0, COMMAND_LINE_SIZE);
|
||||
rc = menu_param(&value);
|
||||
if (rc == 0) {
|
||||
/* got number from loadparm, boot it */
|
||||
|
||||
@@ -675,7 +675,8 @@ static int add_ipl_program(int fd, char *filename,
|
||||
IMAGE_ENTRY,
|
||||
(info->type == disk_type_scsi) ? 0 : 1,
|
||||
flags, ipl->image_addr, image_size,
|
||||
ipl->envblk_addr, envblk->size);
|
||||
ipl->envblk_addr,
|
||||
add_envblk ? envblk->size : 0);
|
||||
if (rc) {
|
||||
free(table);
|
||||
return rc;
|
||||
|
||||
Reference in New Issue
Block a user