Compare commits

..

40 Commits

Author SHA1 Message Date
Jan Höppner
c2db7ce05f New release s390-tools-2.35.0
Signed-off by: Jan Höppner <hoeppner@linux.ibm.com>
2024-10-01 13:23:01 +02:00
Steffen Eiden
0031c762cf rust/pv_core: Fix rusttest owned file descriptor already closed
Newer rust std libary code checks if a fd is already closes and panics.
Test code created a file with fd=17 and relied that test code never
touched the file. This cannot be done anymore. Just use the working dir
as backing file.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-10-01 12:16:19 +02:00
Steffen Eiden
7031429d1e rust/pv: Fix new warnings for rustc 1.80
`sizeof` was added into the prelude in rustc 1.80.
This triggers a lint-warning for `unused_qualifications` if rustc 1.80+
is used. Fix this warning by using a use statement to stay compatible
for <1.80.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-10-01 12:16:19 +02:00
Steffen Eiden
73d8597db1 rust/pv_core: Clarify error reason for 003* return codes
The RCs 30, 31, 32 may be caused by a malicious host or host bug.
Make this clear and advise to stop the SE-guest.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-10-01 12:16:19 +02:00
Marc Hartmayer
77d0d1d1a1 rust/Makefile: Consider HAVE_CARGO in the clean target
This fixes `make clean` errors when cargo is not available:

  $ make clean
  ...
  | make[1]: Entering directory '/home/mhartmay/storage/git/poky/build/workspace/sources/s390-tools/rust'
  | cargo clean  --release
  | /bin/bash: cargo: command not found
  | make[1]: *** [Makefile:72: clean] Error 127

Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-10-01 12:16:19 +02:00
Finn Callies
77a7e11a51 rust/Makefile: Build pvapconfig only on s390x
Configuring the AP bus only makes sense on s390x.

Signed-off-by: Finn Callies <fcallies@linux.ibm.com>
Suggested-by: Steffen Eiden <seiden@linux.ibm.com>
Reviewed-by: Harald Freudenberger <freude@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-10-01 12:16:19 +02:00
Mikhail Zaslonko
279070fa43 zdump/ngdump: Fix seek error on 'zgetdump -d'
When checking CDL formatted DASD for installed dump-tool (zgetdump -d),
we might stumble upon an 'empty' boot record pointer in the volume label
filled with bytes of 0x40 (EBCDIC spaces). This leads to the following
seek error reported:

  # zgetdump -d /dev/dasdb
  zgetdump: Could not seek "/dev/dasdb" (Invalid argument)

Check for empty boot record and return proper error code by
ngdump_get_eckd_part_num() in order to end up with a regular
"No dump tool found" message.

Fixes: f3bcd94524 ("zdump: Add zgetdump -d support for ECKD ldipl-dump")
Signed-off-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-10-01 12:16:19 +02:00
Eduard Shishkin
3989e1b741 zipl/src: fix segfault on '--dry-run' for device-based dumps
This fixes a segfault when the option '--dry-run' is specified for
device-based dumps (for which the bootmap file is created on a RAW
device, not formatted with a file system).

The actual status is that the option '--dry-run' for device-based
dumps is not implemented. The implementation is evaluated as not
trivial: In contrast with filesystem-based dumps, it is not possible
to simply make do with rename/unlink of temporary created bootmap.

The fixup returns error on any not file system based dumps being
created with the option '--dry-run'.

Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Reviewed-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-10-01 12:16:19 +02:00
Eduard Shishkin
c230b5385a zipl/src: move common checks to disk_is_appropriate()
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-10-01 12:16:19 +02:00
Eduard Shishkin
68ecf79b01 zipl/src: cleanups in add_dump_progrem()
Drop a pair of duplicated assignmets
Replace the parameter 'struct job_data' with 'struct job_dump_data'

Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-10-01 12:16:19 +02:00
Eduard Shishkin
f2960eb0df zipl/src: drop check_dump_device_late()
Move all checks from check_dump_device_late() to disk_is_approproate()
and drop check_dump_device_late() entirely

Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-10-01 12:16:19 +02:00
Eduard Shishkin
4d004cf8b1 zipl/src: drop never happening case in check_dump_device_late()
In the function check_dump_device_late() drop the case of
(info->device != target_info->device) which never happens. Indeed,
the @info created in check_dump_device_late() coincides with the
@target_info previously created by
prepare_build_program_table_{file, device}.

Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-10-01 12:16:19 +02:00
Mikhail Zaslonko
d8f504f271 zdump/dfi: Fix vm_info_get() check for non-VR kernel
Since the following kernel commit:
"32db401965f1 s390/mm: Pin identity mapping base to zero"
we can have zero identity_base value in os_info. This makes zgetdump
check for non-VR kernel in vm_info_get() incorrect what leads to missing
data (e.g. UTS kernel version) in 'zgetdump -i' output.
Check for zero kaslr_offset value instead (like crash and makedumpfile
do) in order to identify non-VR kernel.

Signed-off-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Acked-by: Alexander Gordeev <agordeev@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-10-01 12:16:19 +02:00
Jan Höppner
7fb3ee4478 gitignore: Add zipl md files
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-10-01 12:16:19 +02:00
Mikhail Zaslonko
a95bad6c87 zdump/dfo_s390: Support s390 DFO for vr-kernel dumps
Since memory chunks can overlap for vr-kernel dumps stored in elf
format (ngdump, zfcpdump), we need to consider this when converting to
s390 dump output format. For that sort DFI memory chunks by start
address and adjust dfo_s390 logic for identifying memory gaps.
Otherwise we might end up with bogus DFO memory chunks being created.

Signed-off-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-09-13 19:15:01 +02:00
Mete Durlu
147ff1bf49 cpuplugd: Halt cpu hotplugging on vertical polarization
On vertical polarization, kernel can be adjusting CPU capacities
dynamically, and cpuplugd can interfere this with hotplug operations
causing performance degradation. To prevent this, check if system has
switched its polarization state, and act accordingly.
If system is on vertical polarization when daemon starts, no CPU hotplug
action is triggered. If system changes to vertical polarization during
daemon runtime, revert cpuhotplug adjustments and stop further CPU
hotplug actions. If system switches back to horizontal polarization
during runtime of the daemon, start evaluating CPU hotplug rules and
trigger adjustments.

Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Reviewed-by: Gerald Schaefer <gerald.schaefer@linux.ibm.com>
Signed-off-by: Mete Durlu <meted@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-09-13 19:15:01 +02:00
Mete Durlu
c91d172c99 cpuplugd/cpu: Rework CPU management functions
Simplify the cpu management parts of cpuplugd code by introducing
functions from libutil and rework some logic to drop some assumptions
about how kernel assigns cpuids.
Right now cpuplugd assumes that the cpuids are always sequential and
there are no gaps in between, however kernel does not guarantee that.
Make cpuplugd compliant by traversing cpu sysfs entries instead.

Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Reviewed-by: Gerald Schaefer <gerald.schaefer@linux.ibm.com>
Signed-off-by: Mete Durlu <meted@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-09-13 19:15:01 +02:00
Eduard Shishkin
0e4992da3a zipl/src: prevent NULL pointer deref when preparing qcow2 images for IPL
This fixes c0f02d2f6 which results in problems when preparing qcow2
images for IPL:

zipl -V --blsdir /tmp/tmp.kdPooQjoBh/boot//loader/entries/ --config /
Program terminated with signal SIGFPE, Arithmetic exception.

Don't use pointer to not initialized struct disk_info after failed
auto-detection of disk parameters. Make the check that the file
locates on the disk in the form of a separate procedure.

Reported-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Acked-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Tested-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-09-13 19:15:01 +02:00
Eduard Shishkin
41da0f0809 zipl/src: make disk type detecton fail
When detecting disk type, the function disk_get_info() is called.
It can fail for various reasons (e.g. when the logial target is not
eligible for boot record installation).

Once disk_get_info() fails, don't proceed with type detection.
Return error instead. When applicable, mark the dump job with
"is_ngdump" flag to avoid extra type detection calls.

Acked-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-09-13 19:15:01 +02:00
Eduard Shishkin
f43789e16a zipl/src: fix bugs in SCSI dump installation
This fixes d6b702d57 which introduces a problem: SCSI dump
installation fails:

zipl -V -d /dev/mapper/mpathd1
Error: Invalid dump device: Could not read partition table

The reasons:
1) Wrong (logical) device is passed as a target base;
2) In case of 'source_script' the check to make sure that specified
   target is over a partition isn't applicable any more after zipl
   support of complex logical targets (e.g. mirrors).

The fixup passes the physical device as a target base (instead of
the logical one). Also the fixup removes invalid check from
check_dump_device_late() and instead adds a more generic check to
the helper script that data starts beyong the boot area.

Reported-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Tested-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-09-13 19:15:01 +02:00
Marc Hartmayer
136e5eed8c rustfmt: List experimental group_imports option
This option is experimental, but can still be useful.

While at it, sort the options alphabetically.

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-09-13 19:15:01 +02:00
Eduard Shishkin
e09753f2a3 chreipl_helper.device-mapper: complete device resolution process
This fixes 670bf3e8 which actually isn't an equivalent refactoring.
The generic dm-device resolution procedure was modified to not resolve
the topmost dm-device in the path. So that any calles who don't expect
it, should complete the resolutioin process by themselves. In case of
chreipl_helper such completion was missed. chreipl(8) utility doesn't
expect dm-devices at the output of chreipl_helper.device-mapper

Example:

NAME                     MAJ:MIN RM  SIZE RO TYPE  MOUNTPOINT
sda                        8:0    0   20G  0 disk
|-sda1                     8:1    0   20G  0 part
`-mpathb                 253:0    0   20G  0 mpath
  `-mpathb1              253:2    0   20G  0 part  /
sdb                        8:16   0   20G  0 disk
|-sdb1                     8:17   0   20G  0 part
`-mpathb                 253:0    0   20G  0 mpath
  `-mpathb1              253:2    0   20G  0 part  /

Expected result:
8:16

Actual result:
253:0

As a result, the chreipl(8) "node" option stopped working.
The fixup adds the completion.

Reported-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Tested-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-09-13 19:15:01 +02:00
Sertonix
fe2d240656 netboot/mk-s390image: Fix size when argument is a symlink
`du -b` by default returns the size of a symlink when it is passed a
symlink. Adding the `-L` option allows passing symlinks to mk-s390image
without resulting in unbootable image.

Signed-off-by: Sertonix <sertonix@posteo.net>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Fixes: https://github.com/ibm-s390-linux/s390-tools/pull/176
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-09-13 19:15:01 +02:00
Frank Heimes
6fc450f696 (genprotimg|zipl)/boot: discard .note.package ELF section to save memory
The .note.package [1] section is not used by the zipl/genprotimg bootloaders,
therefore discard them via linker script.

This fix solves the error:
 /usr/bin/ld: Heap section doesn't conform to the described memory layout
 collect2: error: ld returned 1 exit status
 make[4]: *** [Makefile:77: stage3a.elf] Error 1
 make[4]: Leaving directory '/<>/genprotimg/boot'
 make[3]: *** [Makefile:20: all-recursive] Error 1
 make[3]: Leaving directory '/<>/genprotimg'
 make[2]: *** [Makefile:56: genprotimg] Error 2

[1] https://systemd.io/ELF_PACKAGE_METADATA/

Fixes: https://github.com/ibm-s390-linux/s390-tools/issues/174
Closes: https://github.com/ibm-s390-linux/s390-tools/pull/176
[seiden@linux.ibm.com: Add/edit fixes tags]
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Frank Heimes <frank.heimes@canonical.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-09-13 19:14:16 +02:00
Steffen Eiden
6a55d0c2e5 rust/pv: Lower most lints to warn
Lower the lint level to warn for the styling lints.
This avoids compile issues during packaging for newer tooling with
potential more lint findings.
Still deny compiling if a public symbol has no documentation.

Fixes: https://github.com/ibm-s390-linux/s390-tools/issues/173
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2024-08-26 10:40:21 +02:00
Eduard Shishkin
0e69dc1262 zipl_helper.device-mapper: add support for 'raid' device-mapper target
Introduce target operations (struct target_ops);
For each supported device-mapper target implement
. check_target_status()
. get_target_data()

Add support for new 'raid' device-mapper target, which is now default
in LVM.

Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2024-08-26 10:40:21 +02:00
Eduard Shishkin
3296d85e35 zipl/src: add support of md-mirrors
Add a script to process mirrored zipl targets managed by Linux "md"
driver (AKA "software mirrors")

. In the file zipl_helper.device-mapper.c implement two handlers
  identified by pairs (ZIPL_UTIL_ID, MD_DRIVER_ID) and
  (CHREIPL_UTIL_ID, MD_DRIVER_ID) for the helpers zipl_helper.md
  and chreipl_helper.md respectively

  . zipl_helper.md: prints sets of target parameters (one such set
    per mirror) for a specified logical zipl target managed by linux
    "md" driver.

  . chreipl_helper.md: for a specified logical zipl target managed
    by linux "md" driver prints a random disk from the set of disks
    participating in the mirrored setup.

. Add zipl_helper.md and chreipl_helper.md as symbolic links to
  zipl_helper.device-mapper

Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2024-08-26 10:40:21 +02:00
Eduard Shishkin
0eeab96f0d zipl_helper.device-mapper: check status of "mirror" dm targets
For "mirror" device-mapper targets:

. Check status of all the mirrors;
. Return error if some mirror contain failures;
. Align the sample output properly

Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2024-08-26 10:40:21 +02:00
Eduard Shishkin
283a61dc2f zipl_helper.device-mapper: support multiple base disks
For "mirror" device-mapper targets:

. Don't assign a logical mirrored device as a zipl target base.
  Instead, add one more branch in complete_physical_device() and
  proceed (in that new branch) to the physical disk participating
  in the mirrored target. This approach allowes to handle
  heterogeneous mirrors properly;
. Provide a set of target parameters for each found physical disk
  by calling dm_dev_to_zipl_params() recursively;
. Don't allow "nested mirrors" by checking/updating the
  fail_on_mirror global;
. Remove the requirements on including block #0 to the mirrored
  device-mapper setup (no more actual);
. Update comments.

Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2024-08-26 10:40:21 +02:00
Eduard Shishkin
670bf3e870 zipl: refactor zipl_helper.device-mapper
This is used by the next patches in the series to support mirror
setups not including boot sectors (e.g. mirrors over partitions).

. Rework the core procedure of device resolution (by walking along
  the device-mapper tree during logical device resolution) to be an
  explicit sequence of pairs (level-lookup, goto-parent-level);
. Add an "extended" device to keep a track of file system offset
  while ascending the device tree;
. Identify all possible results of a single level lookup;
. Rename target_entry to dmpath_entry;
. Make the main() function to look like a call of a handler
  identified by a pair (driver-id, util-id) and found by the name
  of the executable.

Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2024-08-26 10:40:21 +02:00
Eduard Shishkin
9cfaac58ea zipl_helper.device-mapper: fix bug in get_physical_device()
Fix mistake in @fs_offset calculation.

Since any logical dm-device can be built of partitions, @fs_offset
must not decrease while ascending the "device tree" during the
procedure of dm-device resolution (which always starts at the
formatted logical device, for which @fs_offset is 0).

This bug was not reproducible earlier because of restricted
functionality in a combination with side-effects. The fixup is
required however by the next patches in the series implementing
support of mirrored targets by zipl tool

Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2024-08-26 10:40:21 +02:00
Eduard Shishkin
d6b702d579 zipl/src: add basic support for multiple target base disks
. Modify disk_get_info() to process multiple sets of target parameters
  provided by the helper script and store it in the array of "targets"
  of the structure job_target_data;
. Besides the logical device, maintain an array of physical base disks
  in the disk_info structure;
. Use the logical target device only to create bootmap (it is
  automatically mirrored by the respective linux driver (dm, or md)
  managing the mirrored target). In contrast, install bootstrap blocks
  to each physical base disk individually, bypassing that driver;
. Report in verbose mode on which base disks the bootstrap
  installation was performed;
. Use the following logic of setting @info->device (which is printed
  as "Device...:" in verbose mode):
  . source_auto   - the target base disk is set;
  . source_script - the target (logical) device is set;
  . source_user   - the device specified by user (via --targetbase
                    option), or config file is set.

Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2024-08-26 10:40:21 +02:00
Peter Oberparleiter
d9a9bd8dcf zdev: Add option to select IPL device
Add new command-line option --ipldev. When specified and IPL was done
from a supported device type, the IPL device is selected for the
requested tool operation.

Example to list IPL device information:

$ lszdev --ipldev

Example to create a persistent configuration for the IPL device:

$ chzdev --enable --persistent --ipldev

Reviewed-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-08-22 10:32:36 +02:00
Peter Oberparleiter
cad4e9e15b zdev: Add function for reading text files
Reduce code complexity by adding a utility function for reading text
files based on a variable path.

Note: chzdev/lszdev tools apply special path-handling via command line
option --base to allow redirecting hard-coded paths therefore libutil's
util_file functions cannot easily be used.

Reviewed-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-08-22 10:32:36 +02:00
Eduard Shishkin
c0f02d2f68 zipl/src: Fix problems when target parameters are specified by user
Steps to reproduce: Prepare some disk for IPL, specifying its
parameters via zipl "target options", and an image IMAGE_NAME
located on another disk. Don't specify "-a" option.

Actual result: Installation succeeded (resulting in unbootable setup)
Expected result: "Error: Could not add image file 'IMAGE_NAME': File
is not on target device"

The problem is in incorrect evaluation of device number (dev_t)
of the base disk where the image is located by the function
add_component_file_range() in case when target parameters are
specified by user.

Fixup: Retrieve info of the underlying disk without any user hints,
passing zeroed structure job_target_data

This is an improved version of fb0b6263d that was reverted by
63ff07ba3 afterwards. The shortcoming of the original ( fb0b6263d )
fix: it fails to prepare for IPL mounted qcow2 images.
The difference from the original fix: when making sure that boot
files are located on the target disk: in case of failed auto-detection
of disk parameters skip the check with warnings instead of aborting
the whole installation session.

Acked-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-08-22 10:32:36 +02:00
Finn Callies
5218c243f4 cpacfstats: add MSA 10 and MSA 11 counters
MSA 10 and MSA 11 introduce new function codes containing Full XTS
functions for KM instruction and HMAC functions for KMAC instruction.
Additional Full XTS and HMAC for PCKMO instruction is introduced.

Signed-off-by: Finn Callies <fcallies@linux.ibm.com>
Reviewed-by: Harald Freudenberger <freude@linux.ibm.com>
Reviewed-by: Ingo Franzki <ifranzki@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-08-22 10:32:36 +02:00
Finn Callies
c364692e7a cpacfstats: white space fix in cpacfstats/cpacfstatsd.c
Signed-off-by: Finn Callies <fcallies@linux.ibm.com>
Reviewed-by: Harald Freudenberger <freude@linux.ibm.com>
Reviewed-by: Ingo Franzki <ifranzki@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-08-22 10:32:36 +02:00
Nihar Panda
41eb7d076f ziomon/ziorep_config: fix warning when multipath device is not there
Perl logs warning message if a variable is not initialized.
get_device_data() set mp_dev with multipath device for a scsi disk device.
mp_dev remain uninitialized if there is no multipath device.
This can happen if a scsi device is not managed by dm multipath driver.

Warning message:
"Use of uninitialized value in subroutine entry at /usr/sbin/ziorep_config line 136"

Fix it by skipping the code dealing with mp_dev and continue with next
scsi device in the loop.

To reproduce, remove a scsi device from dm multipath.
$multipathd -k'del path sdh'
Then run `ziorep_config -D`.

Signed-off-by: Nihar Panda <niharp@linux.ibm.com>
Reviewed-by: Fedor Loshakov <loshakov@linux.ibm.com>
Reviewed-by: Benjamin Block <bblock@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-08-22 10:32:36 +02:00
Joern Siglen
1062a7cde6 dbginfo.sh: add network sockstat info
Add procfs files to collect network socket data.

Suggested-by: Eberhard Pasch <epasch@de.ibm.com>
Reviewed-by: Mario Held <mario.held@de.ibm.com>
Signed-off-by: Joern Siglen <siglen@de.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-08-22 10:31:33 +02:00
Jan Höppner
54e5e99657 Prepare for next release
Signed-off by: Jan Höppner <hoeppner@linux.ibm.com>
2024-08-01 15:17:58 +02:00
65 changed files with 2542 additions and 968 deletions

2
.gitignore vendored
View File

@@ -124,9 +124,11 @@ zipl/boot/*.exec
zipl/boot/.loaders
zipl/boot/data.h
zipl/src/chreipl_helper.device-mapper
zipl/src/chreipl_helper.md
zipl/src/zipl
zipl/src/zipl-editenv
zipl/src/zipl_helper.device-mapper
zipl/src/zipl_helper.md
zkey/check-dep-zkey
zkey/check-dep-zkey-cryptsetup
zkey/detect-libcryptsetup.dep

View File

@@ -5,8 +5,9 @@ newline_style = "Unix"
# when they become stable.
#
# They are kept here since they are useful to run from time to time.
#format_code_in_doc_comments = true
#reorder_impl_items = true
#comment_width = 100
#wrap_comments = true
#format_code_in_doc_comments = true
#group_imports = "StdExternalCrate"
#normalize_comments = true
#reorder_impl_items = true
#wrap_comments = true

View File

@@ -1,6 +1,28 @@
Release history for s390-tools (MIT version)
--------------------------------------------
* __v2.35.0 (2024-10-01)__
For Linux kernel version: 6.11
Add new tools / libraries:
Changes of existing tools:
- cpacfstats: Add support for FULL XTS (MSA 10) and HMAC (MSA 11) PAI counter
- cpuplugd: Make cpuplugd compatible with hiperdispatch
- dbginfo.sh: Add network sockstat info
- pvapconfig: s390x exclusive build
- zdev: Add option to select IPL device
- zdump/dfo_s390: Support s390 DFO for vr-kernel dumps
- zipl: Add support of mirror devices
Bug Fixes:
- (genprotimg|zipl)/boot: discard .note.package ELF section to save memory
- netboot/mk-s390image: Fix size when argument is a symlink
- ziorep_config: Fix warning message when multipath device is not there.
- zipl: Fix problems when target parameters are specified by user
- zipl: Fix segfault when creating device-based dumps with '--dry-run'
* __v2.34.0 (2024-08-01)__
For Linux kernel version: 6.10

View File

@@ -32,7 +32,7 @@ endif
# The variable "DISTRELEASE" should be overwritten in rpm spec files with:
# "make DISTRELEASE=%{release}" and "make install DISTRELEASE=%{release}"
VERSION := 2
RELEASE := 34
RELEASE := 35
PATCHLEVEL := 0
DISTRELEASE := build-$(shell date +%Y%m%d)
S390_TOOLS_RELEASE := $(VERSION).$(RELEASE).$(PATCHLEVEL)-$(DISTRELEASE)

View File

@@ -23,7 +23,7 @@
* Number of PAI counters. Contains all counters regardless of kernel or user
* space
*/
#define MAX_NUM_PAI 156
#define MAX_NUM_PAI 172
/*
* This is the sysfs directory from which cpacfstatsd daemon application loads

View File

@@ -192,7 +192,27 @@ const struct pai_counter pai[] = {
[152] = {"PCKMO ECC Ed25519", KERNEL_ONLY_COUNTER},
[153] = {"PCKMO ECC Ed448", KERNEL_ONLY_COUNTER},
[154] = {"Reserved 1", KERNEL_ONLY_COUNTER},
[155] = {"Reserved 2", KERNEL_ONLY_COUNTER}
[155] = {"Reserved 2", KERNEL_ONLY_COUNTER},
[156] = {"KM AES-XTS (full) 128bit", KERNEL_AND_USER_COUNTER},
[157] = {"KM AES-XTS (full) 256bit", KERNEL_AND_USER_COUNTER},
[158] = {"KM AES-XTS (full) 128bit protected key",
KERNEL_AND_USER_COUNTER},
[159] = {"KM AES-XTS (full) 256bit protected key",
KERNEL_AND_USER_COUNTER},
[160] = {"KMAC HMAC SHA 224", KERNEL_AND_USER_COUNTER},
[161] = {"KMAC HMAC SHA 256", KERNEL_AND_USER_COUNTER},
[162] = {"KMAC HMAC SHA 384", KERNEL_AND_USER_COUNTER},
[163] = {"KMAC HMAC SHA 512", KERNEL_AND_USER_COUNTER},
[164] = {"KMAC HMAC SHA 224 protected key", KERNEL_AND_USER_COUNTER},
[165] = {"KMAC HMAC SHA 256 protected key", KERNEL_AND_USER_COUNTER},
[166] = {"KMAC HMAC SHA 384 protected key", KERNEL_AND_USER_COUNTER},
[167] = {"KMAC HMAC SHA 512 protected key", KERNEL_AND_USER_COUNTER},
[168] = {"PCKMO HMAC 512 protected key", KERNEL_ONLY_COUNTER},
[169] = {"PCKMO HMAC 1024 protected key", KERNEL_ONLY_COUNTER},
[170] = {"PCKMO AES-XTS 128bit double key protected key",
KERNEL_ONLY_COUNTER},
[171] = {"PCKMO AES-XTS 256bit double key protected key",
KERNEL_ONLY_COUNTER}
};
/*

View File

@@ -39,7 +39,8 @@ static volatile int stopsig;
*/
static const unsigned int pai_idx[] = {
// KM
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 156, 157, 158,
159,
// KMC
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
// KMA
@@ -55,7 +56,8 @@ static const unsigned int pai_idx[] = {
// KLMD
81, 82, 83, 84, 85, 86, 87, 88, 89,
// KMAC
90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101,
90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 160, 161, 162, 163,
164, 165, 166, 167,
// PCC
102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113,
114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124,
@@ -65,7 +67,7 @@ static const unsigned int pai_idx[] = {
128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
140, 141, 142,
// PCKMO
143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153,
143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 168, 169, 170, 171,
// Reserved
154, 155
};
@@ -108,7 +110,6 @@ static int recv_query(int s, enum ctr_e *ctr, enum cmd_e *cmd)
return rc;
}
static int send_answer(int s, int ctr, int state, uint64_t value)
{
struct msg m;
@@ -124,7 +125,6 @@ static int send_answer(int s, int ctr, int state, uint64_t value)
return send_msg(s, &m, DEFAULT_SEND_TIMEOUT);
}
/*
* Print according to protocol for PAI:
* - first the state and the number of PAI counters that follow
@@ -160,7 +160,6 @@ static int do_send_pai(int s, int user, unsigned int *counter)
return rc;
}
static int do_enable(int s, enum ctr_e ctr, unsigned int *supported_counters)
{
uint64_t value = 0;
@@ -201,7 +200,6 @@ static int do_enable(int s, enum ctr_e ctr, unsigned int *supported_counters)
return rc;
}
static int do_disable(int s, enum ctr_e ctr, unsigned int *supported_counters)
{
int i, rc = 0;
@@ -228,7 +226,6 @@ static int do_disable(int s, enum ctr_e ctr, unsigned int *supported_counters)
return rc;
}
static int do_reset(int s, enum ctr_e ctr, unsigned int *supported_counters)
{
int i, rc = 0, state;
@@ -260,7 +257,6 @@ static int do_reset(int s, enum ctr_e ctr, unsigned int *supported_counters)
return rc;
}
static int do_print(int s, enum ctr_e ctr, unsigned int *supported_counters)
{
int i, rc = 0, state;
@@ -292,7 +288,6 @@ static int do_print(int s, enum ctr_e ctr, unsigned int *supported_counters)
return rc;
}
static int become_daemon(int *startup_pipe)
{
int child_initialized = 0, fd;
@@ -385,7 +380,6 @@ static int become_daemon(int *startup_pipe)
return 0;
}
static void remove_sock(void)
{
remove(SOCKET_FILE);
@@ -447,14 +441,12 @@ static void remove_pidfile(void)
remove(PID_FILE);
}
void signalhandler(int sig)
{
perf_stop();
stopsig = sig;
}
int eprint(const char *format, ...)
{
char buf[512];
@@ -476,7 +468,6 @@ int eprint(const char *format, ...)
return n;
}
/*
* returns -1 on error
* returns X where X is the found counters in dir

View File

@@ -4,9 +4,11 @@ all: cpuplugd
LDLIBS += -lm
libs = $(rootdir)/libutil/libutil.a
OBJECTS = daemon.o cpu.o info.o terms.o config.o main.o getopt.o mem.o
cpuplugd: $(OBJECTS)
cpuplugd: $(OBJECTS) $(libs)
$(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@
clean:

View File

@@ -204,6 +204,45 @@ void parse_configfile(char *file)
fclose(filp);
}
void apply_cpu_config(void)
{
/*
* Check that the initial number of cpus is not below the
* minimum
*/
if (num_cpu_start < cfg.cpu_min &&
get_numcpus() >= cfg.cpu_min) {
cpuplugd_debug("The number of online cpus is below ");
cpuplugd_debug("the minimum and will be increased.\n");
while (get_num_online_cpus() < cfg.cpu_min) {
if (hotplug_one_cpu())
break;
}
}
if (get_num_online_cpus() > cfg.cpu_max) {
cpuplugd_debug("The number of online cpus is above the maximum");
cpuplugd_debug(" and will be decreased.\n");
while (get_num_online_cpus() > cfg.cpu_max) {
if (hotunplug_one_cpu())
break;
}
}
if (cfg.cpu_min > get_numcpus()) {
/*
* This check only works if nobody used the
* additional_cpus in the boot parameter section
*/
cpuplugd_debug("The minimum amount of cpus is above the ");
cpuplugd_debug("number of available cpus.\n");
cpuplugd_exit("Detected %d available cpus\n", get_numcpus());
}
if (get_num_online_cpus() < cfg.cpu_min) {
cpuplugd_debug("Failed to set the number of online cpus to ");
cpuplugd_debug("the minimum. ");
cpuplugd_exit("Aborting.\n");
}
}
/*
* Check if the required settings are found in the configuration file.
* "Autodetect" if cpu and/or memory hotplug configuration entries
@@ -211,7 +250,6 @@ void parse_configfile(char *file)
*/
void check_config()
{
int cpuid;
int lpar_status;
lpar_status = check_lpar();
@@ -262,60 +300,23 @@ void check_config()
/*
* Save the number of online cpus and the cmm_pagesize at startup,
* so that we can enable exactly the same amount when the daemon ends
*
* Don't adjust cpus if system is on vertical polarization
*/
if (cpu) {
num_cpu_start = get_num_online_cpus();
cpuplugd_debug("Daemon started with %d active cpus.\n",
num_cpu_start);
/*
* Check that the initial number of cpus is not below the
* minimum
*/
if (num_cpu_start < cfg.cpu_min &&
get_numcpus() >= cfg.cpu_min) {
cpuplugd_debug("The number of online cpus is below "
"the minimum and will be increased.\n");
cpuid = 0;
while (get_num_online_cpus() < cfg.cpu_min &&
cpuid < get_numcpus()) {
if (is_online(cpuid) == 1) {
cpuid++;
continue;
}
cpuplugd_debug("cpu with id %d is currently offline "
"and will be enabled\n", cpuid);
hotplug(cpuid);
cpuid++;
}
}
if (get_num_online_cpus() > cfg.cpu_max) {
cpuplugd_debug("The number of online cpus is above the maximum"
" and will be decreased.\n");
cpuid = 0;
while (get_num_online_cpus() > cfg.cpu_max &&
cpuid < get_numcpus()) {
if (is_online(cpuid) != 1) {
cpuid++;
continue;
}
cpuplugd_debug("cpu with id %d is currently online "
"and will be disabled\n", cpuid);
hotunplug(cpuid);
cpuid++;
}
}
if (cfg.cpu_min > get_numcpus())
/*
* This check only works if nobody used the
* additional_cpus in the boot parameter section
*/
cpuplugd_exit("The minimum amount of cpus is above "
"the number of available cpus.\n"
"Detected %d available cpus\n",
get_numcpus());
if (get_num_online_cpus() < cfg.cpu_min)
cpuplugd_exit("Failed to set the number of online "
"cpus to the minimum. Aborting.\n");
saved_polarization = get_polarization();
num_cpu_start = get_num_online_cpus();
cpuplugd_debug("Daemon started with %d active cpus.\n",
num_cpu_start);
if (saved_polarization < 0) {
cpuplugd_debug("Daemon couldn't determine system polarization\n");
cpuplugd_debug("Starting without evaluating cpu rules\n");
} else if (saved_polarization == PLR_VERTICAL) {
cpuplugd_debug("Daemon started with vertical polarization.\n");
cpuplugd_debug("Cpu adjustments won't be made until system ");
cpuplugd_debug("is in horizontal polarization\n");
} else if (saved_polarization == PLR_HORIZONTAL &&
cpu == 1) {
apply_cpu_config();
}
if (memory == 1) {
/*

View File

@@ -12,217 +12,241 @@
#include <limits.h>
#include "cpuplugd.h"
#define NUM_BASE (10)
#define CPU_OFFLINE (0)
#define CPU_ONLINE (1)
#define CPU_DECONFIGURED (0)
#define CPU_CONFIGURED (1)
#define CPU_LIST_LEN (4096)
/*
* Return overall number of available cpus. This does not necessarily
* mean that those are currently online
*/
int get_numcpus()
static int get_sysfs_attribute_cpu_count(char *path)
{
int i;
char path[PATH_MAX];
int number = 0;
char cpu_list[CPU_LIST_LEN];
int number, start, end;
char *sub_list;
for (i = 0; ; i++) {
/* check whether file exists and is readable */
sprintf(path, "/sys/devices/system/cpu/cpu%d", i);
if (access(path, R_OK) == 0)
if (util_file_read_line(cpu_list, sizeof(cpu_list), path))
cpuplugd_exit("Cannot open %s file: %s\n", path, strerror(errno));
number = 0;
sub_list = strtok(cpu_list, ",");
while (sub_list) {
if (strchr(sub_list, '-')) {
if (sscanf(sub_list, "%d-%d", &start, &end) != 2)
cpuplugd_exit("Malformed content of %s: %s\n", path, sub_list);
number += (end - start) + 1;
} else {
number++;
else
break;
}
sub_list = strtok(NULL, ",");
}
return number;
}
/*
* Return number of online cpus
* get_numcpus() - return number of present cpus by sysfs'
* cpu/present attribute.
* This number represents the total number of usable cpus,
* this includes offline or deconfigured cpus as well.
*/
int get_num_online_cpus()
int get_numcpus(void)
{
FILE *filp;
int i;
char path[PATH_MAX];
int status = 0;
int value_of_onlinefile, rc;
int number;
char *path;
for (i = 0; i < get_numcpus(); i++) {
/* check wether file exists and is readable */
sprintf(path, "/sys/devices/system/cpu/cpu%d/online", i);
if (access(path, R_OK) != 0) {
status++;
continue;
}
filp = fopen(path, "r");
if (!filp)
cpuplugd_exit("Cannot open cpu online file: "
"%s\n", strerror(errno));
else {
rc = fscanf(filp, "%d", &value_of_onlinefile);
if (rc != 1)
cpuplugd_exit("Cannot read cpu online file: "
"%s\n", strerror(errno));
if (value_of_onlinefile == 1)
status++;
}
fclose(filp);
path = util_path_sysfs("devices/system/cpu/present");
number = get_sysfs_attribute_cpu_count(path);
free(path);
if (number <= 0)
cpuplugd_exit("number of present cpus (%d) <= 0\n", number);
return number;
}
/*
* get_num_online_cpus() - return number of online cpus
* by parsing sysfs cpu/online attribute
*/
int get_num_online_cpus(void)
{
int number;
char *path;
path = util_path_sysfs("devices/system/cpu/online");
number = get_sysfs_attribute_cpu_count(path);
free(path);
if (number <= 0)
cpuplugd_exit("number of online cpus (%d) <= 0\n", number);
return number;
}
/*
* get_polarization() - return system polarization
*/
int get_polarization(void)
{
int polarization;
char *path;
path = util_path_sysfs("devices/system/cpu/dispatching");
if (util_file_read_i(&polarization, NUM_BASE, path) < 0) {
polarization = -1;
cpuplugd_debug("failed to read system polarization\n");
}
free(path);
return polarization;
}
/*
* is_cpu_hotpluggable() - check if cpuhotplug operations are supported
* for the given cpu.
*/
static int is_cpu_hotpluggable(int cpuid)
{
char *path;
int rc;
path = util_path_sysfs("devices/system/cpu/cpu%d/online", cpuid);
rc = util_path_exists(path);
free(path);
return rc;
}
/*
* hotplug() - perform cpu hotplug on given cpuid
*/
static int hotplug(int cpuid)
{
char *path;
int rc;
path = util_path_sysfs("devices/system/cpu/cpu%d/online", cpuid);
rc = util_file_write_l(CPU_ONLINE, NUM_BASE, path);
if (rc < 0)
cpuplugd_debug("failed to enable cpu with id %d\n", cpuid);
free(path);
return rc;
}
/*
* hotunplug() - perform cpu hotunplug on given cpuid
*/
static int hotunplug(int cpuid)
{
char *path;
int rc;
path = util_path_sysfs("devices/system/cpu/cpu%d/online", cpuid);
rc = util_file_write_l(CPU_OFFLINE, NUM_BASE, path);
if (rc < 0)
cpuplugd_debug("failed to disable cpu with id %d\n", cpuid);
free(path);
return rc;
}
/*
* get_cpu_attribute() - get a certain cpu's selected attribute
*/
static int get_cpu_attribute(int cpuid, char *attribute)
{
int status;
char *path;
path = util_path_sysfs("devices/system/cpu/cpu%d/%s", cpuid, attribute);
if (util_file_read_i(&status, NUM_BASE, path) < 0) {
status = -1;
cpuplugd_debug("failed to read %s status of cpu with id %d\n", attribute, cpuid);
}
free(path);
return status;
}
/*
* Enable a certain cpu
* hotplug_one_cpu() - perform hotplugging on the first available cpu
*/
int hotplug(int cpuid)
int hotplug_one_cpu(void)
{
FILE *filp;
char path[PATH_MAX];
int status, rc;
struct dirent **cpu_dir;
int cpuid, count, i, rc;
char *path;
sprintf(path, "/sys/devices/system/cpu/cpu%d/online", cpuid);
if (access(path, W_OK) == 0) {
filp = fopen(path, "w");
if (!filp)
cpuplugd_exit("Cannot open cpu online file: %s\n",
strerror(errno));
fprintf(filp, "1");
fclose(filp);
/*
* check if the attempt to enable the cpus really worked
*/
filp = fopen(path, "r");
rc = fscanf(filp, "%d", &status);
if (rc != 1)
cpuplugd_exit("Cannot open cpu online file: %s\n",
strerror(errno));
fclose(filp);
if (status == 1) {
cpuplugd_debug("cpu with id %d enabled\n", cpuid);
return 1;
} else {
cpuplugd_debug("failed to enable cpu with id %d\n",
cpuid);
return -1;
rc = -1;
path = util_path_sysfs("devices/system/cpu/");
count = util_scandir(&cpu_dir, alphasort, path, "cpu[0-9]*");
for (i = 0; (i < count) && (rc != 0); i++) {
if (sscanf(cpu_dir[i]->d_name, "cpu%d", &cpuid) != 1)
cpuplugd_exit("Malformed content of %s: %s\n", path, cpu_dir[i]->d_name);
if (!is_cpu_hotpluggable(cpuid))
continue;
if (get_cpu_attribute(cpuid, "configure") == CPU_CONFIGURED &&
get_cpu_attribute(cpuid, "online") == CPU_OFFLINE) {
cpuplugd_debug("cpu%d will be enabled", cpuid);
rc = hotplug(cpuid);
}
}
cpuplugd_debug("cpu with id %d cannot be hotplugged\n", cpuid);
return -1;
util_scandir_free(cpu_dir, count);
free(path);
return rc;
}
/*
* Disable a certain cpu
* hotunplug_one_cpu() - perform hotunplugging on the first available cpu
*/
int hotunplug(int cpuid)
int hotunplug_one_cpu(void)
{
FILE *filp;
int state, rc;
int retval = -1;
char path[PATH_MAX];
struct dirent **cpu_dir;
int cpuid, count, i, rc;
char *path;
state = -1;
sprintf(path, "/sys/devices/system/cpu/cpu%d/online", cpuid);
if (access(path, W_OK) == 0) {
filp = fopen(path, "w");
fprintf(filp, "0");
fclose(filp);
/*
* Check if the attempt to enable the cpus really worked
*/
filp = fopen(path, "r");
rc = fscanf(filp, "%d", &state);
if (rc != 1)
cpuplugd_error("Failed to disable cpu with id %d\n",
cpuid);
fclose(filp);
if (state == 0)
return 1;
}
cpuplugd_debug("cpu with id %d cannot be hotunplugged\n", cpuid);
return retval;
}
/*
* Check if a certain cpu is currently online
*/
int is_online(int cpuid)
{
FILE *filp;
int state;
int retval, rc;
char path[PATH_MAX];
retval = -1;
sprintf(path, "/sys/devices/system/cpu/cpu%d/online", cpuid);
if (access(path, R_OK) == 0) {
filp = fopen(path, "r");
rc = fscanf(filp, "%d", &state);
if (rc == 1) {
if (state == 1)
retval = 1;
if (state == 0)
retval = 0;
rc = -1;
path = util_path_sysfs("devices/system/cpu/");
count = util_scandir(&cpu_dir, alphasort, path, "cpu[0-9]*");
for (i = 0; (i < count) && (rc != 0); i++) {
if (sscanf(cpu_dir[i]->d_name, "cpu%d", &cpuid) != 1)
cpuplugd_exit("Malformed content of %s: %s\n", path, cpu_dir[i]->d_name);
if (!is_cpu_hotpluggable(cpuid))
continue;
if (get_cpu_attribute(cpuid, "online") == CPU_ONLINE) {
cpuplugd_debug("cpu%d will be disabled\n", cpuid);
rc = hotunplug(cpuid);
}
fclose(filp);
} else {
retval = 1;
}
return retval;
util_scandir_free(cpu_dir, count);
free(path);
return rc;
}
/*
* Cleanup method. If the daemon is stopped, we (re)activate all cpus
*/
void reactivate_cpus()
void reactivate_cpus(void)
{
/*
* Only enable the number of cpus which where
* available at daemon startup time
*/
int cpuid, nc;
struct dirent **cpu_dir;
int cpuid, nc, count, i;
char *path;
cpuid = 0;
/* suppress verbose messages on exit */
debug = 0;
/*
* We check for num_cpu_start != 0 because we might want to
* clean up, before we queried for the number on cpus at
* startup
*/
/*
* Only enable the number of cpus which where available at
* daemon startup time by checking num_cpu_start.
* We check for num_cpu_start != 0 because we might want to
* clean up, before we queried for the number on cpus at
* startup
*/
if (num_cpu_start == 0)
return;
while (get_num_online_cpus() != num_cpu_start && cpuid < get_numcpus()) {
nc = 0;
path = util_path_sysfs("devices/system/cpu/");
count = util_scandir(&cpu_dir, alphasort, path, "cpu[0-9]*");
for (i = 0; (i < count) && (nc != num_cpu_start); i++) {
nc = get_num_online_cpus();
if (nc == num_cpu_start)
return;
if (nc > num_cpu_start && is_online(cpuid) == 1)
if (sscanf(cpu_dir[i]->d_name, "cpu%d", &cpuid) != 1)
cpuplugd_exit("Malformed content of %s: %s\n", path, cpu_dir[i]->d_name);
if (nc > num_cpu_start &&
get_cpu_attribute(cpuid, "online") == CPU_ONLINE)
hotunplug(cpuid);
if (nc < num_cpu_start && is_online(cpuid) == 0)
if (nc < num_cpu_start &&
get_cpu_attribute(cpuid, "online") == CPU_OFFLINE)
hotplug(cpuid);
cpuid++;
}
util_scandir_free(cpu_dir, count);
free(path);
}
/*
* In kernels > 2.6.24 cpus can be deconfigured. The following functions is used
* to check if a certain cpus is in a deconfigured state.
*/
int cpu_is_configured(int cpuid)
{
FILE *filp;
int retval, state, rc;
char path[4096];
retval = -1;
sprintf(path, "/sys/devices/system/cpu/cpu%d/configure", cpuid);
if (access(path, R_OK) == 0) {
filp = fopen(path, "r");
rc = fscanf(filp, "%d", &state);
if (rc == 1) {
if (state == 1)
retval = 1;
if (state == 0)
retval = 0;
}
fclose(filp);
}
return retval;
}

View File

@@ -23,6 +23,9 @@
#include <unistd.h>
#include "lib/util_base.h"
#include "lib/util_path.h"
#include "lib/util_file.h"
#include "lib/util_scandir.h"
#include "lib/zt_common.h"
#define NAME "cpuplugd"
@@ -35,6 +38,8 @@
#define MAX_VARNAME 128
#define MAX_LINESIZE 2048
#define CPUSTATS 10
#define PLR_HORIZONTAL 0
#define PLR_VERTICAL 1
/*
* Precedence of C operators
@@ -173,9 +178,11 @@ extern unsigned int history_max;
extern unsigned int history_current;
extern struct symbol_names sym_names[];
extern unsigned int sym_names_count;
extern int saved_polarization;
int get_numcpus();
int get_num_online_cpus();
int get_polarization(void);
void get_loadavg_runnable(double *loadavg, double *runnable);
void clean_up();
void reactivate_cpus();
@@ -190,9 +197,8 @@ void proc_cpu_read(char *procinfo);
unsigned long proc_read_size(char *path);
char *get_var_rvalue(char *var_name);
void cleanup_cmm(void);
int hotplug(int cpuid);
int hotunplug(int cpuid);
int is_online(int cpuid);
int hotplug_one_cpu(void);
int hotunplug_one_cpu(void);
long get_cmmpages_size();
void parse_options(int argc, char **argv);
void check_if_started_twice();
@@ -202,9 +208,9 @@ void reload_daemon(void);
int daemonize(void);
int check_cmmfiles(void);
void check_config();
void apply_cpu_config(void);
void set_cmm_pages(long size);
int check_lpar();
int cpu_is_configured(int cpuid);
void setup_history(void);

View File

@@ -149,6 +149,8 @@ void clean_up()
cpuplugd_info("terminated\n");
remove(pid_file);
remove(LOCKFILE);
/* suppress verbose messages on exit */
debug = 0;
reactivate_cpus();
if (memory)
cleanup_cmm();
@@ -163,6 +165,8 @@ void kill_daemon(int UNUSED(a))
cpuplugd_info("shutting down\n");
remove(pid_file);
remove(LOCKFILE);
/* suppress verbose messages on exit */
debug = 0;
reactivate_cpus();
if (memory)
cleanup_cmm();

View File

@@ -54,12 +54,12 @@ struct config cfg = {
.hotunplug = NULL,
};
int num_cpu_start, memory, cpu, reload_pending;
long cmm_pagesize_start;
unsigned long meminfo_size, vmstat_size, cpustat_size, varinfo_size;
char *meminfo, *vmstat, *cpustat, *varinfo;
double *timestamps;
unsigned int history_max, history_current, history_prev, sym_names_count;
unsigned long meminfo_size, vmstat_size, cpustat_size, varinfo_size;
int num_cpu_start, memory, cpu, reload_pending, saved_polarization;
char *meminfo, *vmstat, *cpustat, *varinfo;
long cmm_pagesize_start;
double *timestamps;
static struct symbols symbols;
static jmp_buf jmpenv;
@@ -77,8 +77,29 @@ static void eval_cpu_rules(void)
{
double diffs[CPUSTATS], diffs_total, percent_factor;
char *procinfo_current, *procinfo_prev;
int cpu, nr_cpus, on_off;
int nr_cpus, on_off, polarization;
polarization = get_polarization();
if (polarization < 0) {
cpuplugd_debug("couldn't determine system polarization\n");
cpuplugd_debug("skipping cpu rule evaluation\n");
return;
}
if (saved_polarization != polarization) {
saved_polarization = polarization;
if (polarization == PLR_VERTICAL) {
/* revert cpu hotplug adjustments after switching from horizontal */
reactivate_cpus();
} else if (polarization == PLR_HORIZONTAL) {
/* reapply cpu config after switching from vertical */
apply_cpu_config();
}
}
if (polarization == PLR_VERTICAL) {
cpuplugd_debug("system is running vertical polarization\n");
cpuplugd_debug("cpuplugd won't make cpu adjustments\n");
return;
}
nr_cpus = get_numcpus();
procinfo_current = cpustat + history_current * cpustat_size;
procinfo_prev = cpustat + history_prev * cpustat_size;
@@ -157,7 +178,6 @@ static void eval_cpu_rules(void)
printf("\n");
printf("---------------------------------------------\n");
}
on_off = 0;
/* Evaluate the hotplug rule */
if (eval_term(cfg.hotplug, &symbols))
@@ -172,40 +192,16 @@ static void eval_cpu_rules(void)
cpuplugd_debug("maximum cpu limit is reached\n");
return;
}
/* try to find a offline cpu */
for (cpu = 0; cpu < nr_cpus; cpu++)
if (is_online(cpu) == 0 && cpu_is_configured(cpu) != 0)
break;
if (cpu < nr_cpus) {
cpuplugd_debug("cpu with id %d is currently offline "
"and will be enabled\n", cpu);
if (hotplug(cpu) == -1)
cpuplugd_debug("unable to find a cpu which "
"can be enabled\n");
} else {
/*
* In case we tried to enable a cpu but this failed.
* This is the case if a cpu is deconfigured
*/
cpuplugd_debug("unable to find a cpu which can "
"be enabled\n");
}
if (hotplug_one_cpu())
cpuplugd_debug("unable to find a cpu which can be enabled\n");
} else if (on_off < 0) {
/* check cpu nr limit */
if (symbols.onumcpus <= cfg.cpu_min) {
cpuplugd_debug("minimum cpu limit is reached\n");
return;
}
/* try to find a online cpu */
for (cpu = get_numcpus() - 1; cpu >= 0; cpu--) {
if (is_online(cpu) != 0)
break;
}
if (cpu > 0) {
cpuplugd_debug("cpu with id %d is currently online "
"and will be disabled\n", cpu);
hotunplug(cpu);
}
if (hotunplug_one_cpu())
cpuplugd_debug("unable to find a cpu which can be disabled\n");
}
}

View File

@@ -22,6 +22,9 @@ of active CPUs are reset to the values they had before the cpuplugd was started.
This program can be used to control the number of CPUs for Linux on z/VM
and for Linux in LPAR mode. The memory hotplug feature (CMM page pool) applies
to Linux on z/VM only.
The cpuplugd daemon stops any CPU hot-plug operations when the system switches
to vertical polarization, thus avoiding possible performance penalties.
.
.SH OPTIONS
.TP

View File

@@ -93,5 +93,6 @@ SECTIONS
*(.eh_frame)
*(.interp)
*(.note.GNU-stack)
*(.note.package)
}
}

View File

@@ -79,5 +79,6 @@ SECTIONS
*(.eh_frame)
*(.interp)
*(.note.GNU-stack)
*(.note.package)
}
}

View File

@@ -15,5 +15,6 @@ SECTIONS
*(.eh_frame)
*(.interp)
*(.note.GNU-stack)
*(.note.package)
}
}

View File

@@ -119,9 +119,9 @@ dobuild()
# append ramdisk if specified
if [ "$ramdisk" != "" ]
then
ramdisk_size=$(du -b $ramdisk | cut -f1)
kernel_size=$(du -b $kernel | cut -f1)
ramdisk_offset=$(du -b $image | cut -f1)
ramdisk_size=$(du -b -L $ramdisk | cut -f1)
kernel_size=$(du -b -L $kernel | cut -f1)
ramdisk_offset=$(du -b -L $image | cut -f1)
cat $ramdisk >> $image
binval=$(mktemp)
dec2be64 $ramdisk_offset > $binval
@@ -135,7 +135,7 @@ dobuild()
# set cmdline
if [ "$parmfile" != "" ]
then
parmfile_size=$(du -b $parmfile | cut -f1)
parmfile_size=$(du -b -L $parmfile | cut -f1)
if [ $parmfile_size -le $MAX_PARMFILE_SIZE ]
then
# Clear any previous parameters

View File

@@ -19,7 +19,13 @@ ifneq (${HAVE_CARGO},0)
ifneq (${HAVE_OPENSSL},0)
ifneq (${HAVE_LIBCURL},0)
PV_TARGETS := pvsecret pvapconfig pvattest
PV_TARGETS := pvsecret pvattest
ifeq ($(HOST_ARCH),s390x)
PV_TARGETS += pvapconfig
else
BUILD_TARGETS += skip-pvapconfig
endif #HOSTARCH
PV_BUILD_TARGETS := $(PV_TARGETS)
CARGO_TEST_TARGETS += $(addsuffix .test,pv $(PV_TARGETS))
@@ -56,6 +62,9 @@ skip-build:
skip-pv-build:
echo " SKIP rust-pv-tools due to unresolved dependencies"
skip-pvapconfig:
echo " SKIP pvapconfig due to unsupported architecture (s390x only)"
all: $(BUILD_TARGETS)
install: $(INSTALL_TARGETS)
$(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR)
@@ -65,7 +74,9 @@ print-rust-targets:
echo $(BUILD_TARGETS)
clean:
ifneq (${HAVE_CARGO},0)
$(CARGO_CLEAN) ${ALL_CARGOFLAGS}
endif # CARGO
$(RM) -- .check-dep-pvtools .detect-openssl.dep.c .check-cargo
rust-test: $(CARGO_TEST_TARGETS)

View File

@@ -2,8 +2,8 @@
//
// Copyright IBM Corp. 2023, 2024
#![deny(
missing_docs,
#![deny(missing_docs)]
#![warn(
missing_debug_implementations,
trivial_numeric_casts,
unstable_features,

View File

@@ -1,8 +1,8 @@
// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp. 2023, 2024
#![deny(
missing_docs,
#![deny(missing_docs)]
#![warn(
missing_debug_implementations,
trivial_numeric_casts,
unstable_features,

View File

@@ -72,9 +72,9 @@ fn rc_fmt<C: UvCmd>(rc: u16, rrc: u16, cmd: &mut C) -> &'static str {
(0x0000, _) => Some("invalid rc"),
(0x0002, _) => Some("invalid UV command"),
(0x0005, _) => Some("request has an invalid size"),
(0x0030, _) => Some("home address space control bit has R-bit set to one"),
(0x0031, _) => Some("access exception"),
(0x0032, _) => Some("request contains virtual address translating to an invalid address"),
(0x0030, _) => Some("home address space control bit has R-bit set to one. This may be due to a corrupted host or a guest kernel bug. STOP using this guest"),
(0x0031, _) => Some("adress translation exception. This may be due to a corrupted host or a guest kernel bug. STOP using this guest"),
(0x0032, _) => Some("request contains virtual address translating to an invalid address. This may be due to a corrupted host or a guest kernel bug. STOP using this guest"),
(UvDevice::RC_MORE_DATA, _) => unreachable!("This is no Error!!!!"),
(UvDevice::RC_SUCCESS, _) => unreachable!("This is no Error!!!!"),

View File

@@ -2,6 +2,8 @@
//
// Copyright IBM Corp. 2023
use std::mem::size_of;
use crate::{assert_size, static_assert};
use zerocopy::{AsBytes, FromBytes, FromZeroes};
@@ -103,7 +105,7 @@ assert_size!(uvio_attest, 0x138);
/// corresponds to the UV_IOCTL macro
pub(crate) const fn uv_ioctl(nr: u8) -> u64 {
iowr(UVIO_TYPE_UVC, nr, std::mem::size_of::<uvio_ioctl_cb>())
iowr(UVIO_TYPE_UVC, nr, size_of::<uvio_ioctl_cb>())
}
static_assert!(uv_ioctl(UVIO_IOCTL_ATT_NR) == 0xc0407501);

View File

@@ -6,7 +6,6 @@
use std::{
ffi::{c_int, c_ulong},
os::unix::prelude::FromRawFd,
sync::{Mutex, MutexGuard},
};
@@ -65,13 +64,12 @@ impl IoctlCtx {
pub mod mock_libc {
use super::*;
pub unsafe fn ioctl(fd: c_int, cmd: c_ulong, data: *mut ffi::uvio_ioctl_cb) -> c_int {
pub unsafe fn ioctl(_fd: c_int, cmd: c_ulong, data: *mut ffi::uvio_ioctl_cb) -> c_int {
let mut ctx = get_lock(&IOCTL_MTX);
assert!(!ctx.called, "IOCTL called more than once");
ctx.called = true;
assert_eq!(cmd, ctx.exp_cmd, "IOCTL cmd mismatch");
assert_eq!(fd, 17, "IOCTL fd mismatch");
let data_ref: &mut ffi::uvio_ioctl_cb = &mut *data;
@@ -131,10 +129,10 @@ impl UvCmd for TestCmd {
}
impl UvDevice {
/// use some random fd for `uvdevice` its OK, as the ioctl is mocked and never touches the
/// Use this file as backing file for `uvdevice`. This is OK, as the ioctl is mocked and never touches the
/// passed file
fn test_dev() -> Self {
UvDevice(unsafe { File::from_raw_fd(17) })
Self(File::open(".").unwrap())
}
}

View File

@@ -328,6 +328,8 @@ PROCFILES="\
/proc/mounts\
/proc/net/vlan\
/proc/net/bonding\
/proc/net/sockstat\
/proc/net/sockstat6\
/proc/net/softnet_stat\
/proc/partitions\
/proc/qeth\

View File

@@ -23,6 +23,7 @@
#define PATH_MODPROBE "/sbin/modprobe"
#define PATH_CCW_BUS "/sys/bus/ccw"
#define PATH_CCWGROUP_BUS "/sys/bus/ccwgroup"
#define PATH_IPL "/sys/firmware/ipl"
#define PATH_UDEV_RULES "/etc/udev/rules.d"
#define PATH_UDEV_RULES_VOLATILE "/run/udev/rules.d"
#define PATH_PROC "/proc"
@@ -65,6 +66,7 @@ char *path_get_zfcp_lun_dev(struct zfcp_lun_devid *);
char *path_get_zfcp_port_dev(struct zfcp_lun_devid *);
char *path_get_scsi_hctl_dev(const char *);
char *path_get_bus_attr(const char *, const char *);
char *path_read_text_file(int chomp, err_t err, char *fmt, ...);
exit_code_t path_for_each(const char *,
exit_code_t (*callback)(const char *, const char *,

View File

@@ -29,6 +29,7 @@ struct select_opts {
unsigned int online:1;
unsigned int offline:1;
unsigned int failed:1;
unsigned int ipldev:1;
struct util_list devids; /* List of struct strlist_node */
struct util_list by_path; /* List of struct strlist_node */
struct util_list by_node; /* List of struct strlist_node */

View File

@@ -127,6 +127,7 @@ typedef exit_code_t (*subtype_cb_t)(struct subtype *st, const char *id,
* @device_undefine: Optional: Undefine a device
* @add_definable_ids: Optional: Add IDs of all devices that can be defined to
* specified strlist
* @get_ipldev_id: Optional: If subtype provides IPL device, return its ID
*/
struct subtype {
/* Static data. */
@@ -222,6 +223,7 @@ struct subtype {
exit_code_t (*device_undefine)(struct subtype *, struct device *);
void (*add_definable_ids)(struct subtype *,
struct util_list *);
char *(*get_ipldev_id)(struct subtype *);
};
/* Subtype method accessor functions. */
@@ -287,6 +289,7 @@ exit_code_t subtype_detect_definable(struct subtype *, struct device *);
exit_code_t subtype_device_define(struct subtype *, struct device *);
exit_code_t subtype_device_undefine(struct subtype *, struct device *);
void subtype_add_definable_ids(struct subtype *, struct util_list *);
char *subtype_get_ipldev_id(struct subtype *);
/* Subtype helper functions. */
bool subtype_device_exists(struct subtype *st, const char *id,
@@ -308,6 +311,7 @@ struct subtype *subtype_find(const char *name);
struct attrib *subtype_find_dev_attrib(struct subtype *st, const char *str);
exit_code_t subtype_read_all_devices(struct subtype *st, config_t config,
read_scope_t);
bool subtypes_find_ipldev(struct subtype **st_ptr, char **id_ptr);
bool subtypes_find_by_devnode(struct devnode *devnode,
struct subtype **st_ptr, char **id_ptr);
void subtype_devices_print_all(void);

View File

@@ -345,6 +345,12 @@ errors.
.CL lszdev \-\-failed \-\-info
.PP
.
.OD ipldev "" ""
Select IPL device
Select the device used for the most recent IPL operation.
.PP
.
.
.SH SETTINGS
A

View File

@@ -313,7 +313,12 @@ errors.
.B Example:
.CL lszdev \-\-failed \-\-info
.PP
.
.OD ipldev "" ""
Select IPL device
Select the device used for the most recent IPL operation.
.PP
.
.
.SH ACTIONS

View File

@@ -1827,6 +1827,27 @@ static char *ccw_st_get_active_attrib_path(struct subtype *st,
return path;
}
/* Return a newly allocated character string containing the IPL device ID in
* case of CCW IPL. */
static char *ccw_st_get_ipldev_id(struct subtype *st)
{
char *type, *id = NULL;
type = path_read_text_file(1, err_ignore, PATH_IPL "/ipl_type");
if (strcmp(type, "ccw") != 0)
goto out;
id = path_read_text_file(1, err_ignore, PATH_IPL "/device");
if (!ccw_st_exists_active(st, id)) {
free(id);
id = NULL;
}
out:
free(type);
return id;
}
/* The methods of this subtype assume that @data points to a
* struct ccw_subtype_data. */
struct subtype ccw_subtype = {
@@ -1864,4 +1885,6 @@ struct subtype ccw_subtype = {
.add_devnodes = &ccw_st_add_devnodes,
.resolve_devnode = &ccw_st_resolve_devnode,
.get_active_attrib_path = &ccw_st_get_active_attrib_path,
.get_ipldev_id = &ccw_st_get_ipldev_id,
};

View File

@@ -124,6 +124,7 @@ enum {
OPT_ONLINE = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_OFFLINE = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_FAILED = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_IPLDEV = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_BY_PATH = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_BY_NODE = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_BY_INTERFACE = (OPT_ANONYMOUS_BASE+__COUNTER__),
@@ -173,14 +174,14 @@ static struct opts_conflict conflict_list[] = {
OPT_REMOVE_ALL, OPT_CONFIGURED, OPT_EXISTING, OPT_ONLINE,
OPT_OFFLINE, OPT_BY_PATH, OPT_BY_NODE, OPT_BY_INTERFACE,
OPT_BY_ATTRIB, OPT_ACTIVE, OPT_PERSISTENT, OPT_FAILED,
OPT_DECONFIGURE_ALL, 0),
OPT_IPLDEV, OPT_DECONFIGURE_ALL, 0),
OPTS_CONFLICT(OPT_LIST_TYPES,
OPT_DECONFIGURE, OPT_LIST_ATTRIBS, OPT_HELP_ATTRIBS,
OPT_EXPORT, OPT_IMPORT, OPT_APPLY, OPT_REMOVE,
OPT_REMOVE_ALL, OPT_CONFIGURED, OPT_EXISTING, OPT_ONLINE,
OPT_OFFLINE, OPT_BY_PATH, OPT_BY_NODE, OPT_BY_INTERFACE,
OPT_BY_ATTRIB, OPT_ACTIVE, OPT_PERSISTENT, OPT_FAILED,
OPT_DECONFIGURE_ALL, 0),
OPT_IPLDEV, OPT_DECONFIGURE_ALL, 0),
OPTS_CONFLICT(OPT_EXPORT,
OPT_DECONFIGURE, OPT_LIST_ATTRIBS, OPT_HELP_ATTRIBS,
OPT_LIST_TYPES, OPT_IMPORT, OPT_APPLY, OPT_REMOVE,
@@ -197,7 +198,7 @@ static struct opts_conflict conflict_list[] = {
OPT_REMOVE_ALL, OPT_CONFIGURED, OPT_EXISTING, OPT_ONLINE,
OPT_OFFLINE, OPT_BY_PATH, OPT_BY_NODE, OPT_BY_INTERFACE,
OPT_BY_ATTRIB, OPT_ACTIVE, OPT_PERSISTENT, OPT_FAILED,
OPT_DECONFIGURE_ALL, 0),
OPT_IPLDEV, OPT_DECONFIGURE_ALL, 0),
OPTS_CONFLICT(OPT_APPLY,
OPT_DECONFIGURE, OPT_LIST_ATTRIBS, OPT_HELP_ATTRIBS,
OPT_LIST_TYPES, OPT_EXPORT, OPT_IMPORT, OPT_REMOVE,
@@ -223,6 +224,7 @@ static const struct option opt_list[] = {
{ "online", no_argument, NULL, OPT_ONLINE },
{ "offline", no_argument, NULL, OPT_OFFLINE },
{ "failed", no_argument, NULL, OPT_FAILED },
{ "ipldev", no_argument, NULL, OPT_IPLDEV },
{ "by-path", required_argument, NULL, OPT_BY_PATH },
{ "by-node", required_argument, NULL, OPT_BY_NODE },
{ "by-interface", required_argument, NULL, OPT_BY_INTERFACE },
@@ -837,6 +839,11 @@ static exit_code_t parse_options(struct options *opts, int argc, char *argv[])
opts->select->failed = 1;
break;
case OPT_IPLDEV:
/* --ipldev */
opts->select->ipldev = 1;
break;
case OPT_BY_PATH:
/* --by-path MOUNTPOINT */
strlist_add(&opts->select->by_path, "%s", optarg);

View File

@@ -21,6 +21,7 @@ SELECTION
--existing Select devices found in the active configuration
--online/--offline Select devices that are online/offline
--failed Select devices that are not functioning correctly
--ipldev Select device used for IPL/boot
--by-path PATH Select device providing file system path, e.g. /usr
--by-node NODE Select device providing device node, e.g. /dev/sda
--by-interface NAME Select device providing network interface, e.g. eth0

View File

@@ -477,6 +477,26 @@ static void dasd_st_add_modules(struct subtype *st, struct device *dev,
strlist_add_unique(modules, DASD_DIAG_MOD_NAME);
}
static char *dasd_eckd_st_get_ipldev_id(struct subtype *st)
{
char *id, *type;
/* Handle CCW-type IPL first. */
id = st->super->get_ipldev_id(st);
if (id)
return id;
/* Extra-handling for eckd LD-IPL. */
type = path_read_text_file(1, err_ignore, PATH_IPL "/ipl_type");
if (strcmp(type, "eckd") != 0)
goto out;
id = path_read_text_file(1, err_ignore, PATH_IPL "/ipl/device");
out:
free(type);
return id;
}
/*
* DASD methods.
*/
@@ -798,6 +818,7 @@ struct subtype dasd_subtype_eckd = {
.configure_persistent = &dasd_st_configure_persistent,
.check_pre_configure = &dasd_st_check_pre_configure,
.add_modules = &dasd_st_add_modules,
.get_ipldev_id = &dasd_eckd_st_get_ipldev_id,
};
static struct ccw_subtype_data dasd_fba_data = {

View File

@@ -89,6 +89,7 @@ enum {
OPT_ONLINE = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_OFFLINE = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_FAILED = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_IPLDEV = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_BY_PATH = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_BY_NODE = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_BY_INTERFACE = (OPT_ANONYMOUS_BASE+__COUNTER__),
@@ -129,6 +130,7 @@ static struct opts_conflict conflict_list[] = {
OPTS_CONFLICT(OPT_TYPE,
OPT_CONFIGURED, OPT_EXISTING, OPT_ONLINE, OPT_OFFLINE,
OPT_BY_PATH, OPT_BY_NODE, OPT_BY_INTERFACE, OPT_FAILED,
OPT_IPLDEV,
0),
OPTS_CONFLICT(OPT_SITE,
OPT_TYPE),
@@ -148,6 +150,7 @@ static const struct option opt_list[] = {
{ "online", no_argument, NULL, OPT_ONLINE },
{ "offline", no_argument, NULL, OPT_OFFLINE },
{ "failed", no_argument, NULL, OPT_FAILED },
{ "ipldev", no_argument, NULL, OPT_IPLDEV },
{ "by-path", required_argument, NULL, OPT_BY_PATH },
{ "by-node", required_argument, NULL, OPT_BY_NODE },
{ "by-interface", required_argument, NULL, OPT_BY_INTERFACE },
@@ -609,6 +612,11 @@ static exit_code_t parse_options(struct options *opts, int argc, char *argv[])
opts->select->failed = 1;
break;
case OPT_IPLDEV:
/* --ipldev */
opts->select->ipldev = 1;
break;
case OPT_BY_PATH:
/* --by-path MOUNTPOINT */
strlist_add(&opts->select->by_path, "%s", optarg);

View File

@@ -21,6 +21,7 @@ SELECTION
--existing Select devices found in the active configuration
--online/--offline Select devices that are online/offline
--failed Select devices that are not functioning correctly
--ipldev Select device used for IPL/boot
--by-path PATH Select device providing file system path, e.g. /usr
--by-node NODE Select device providing device node, e.g. /dev/sda
--by-interface NAME Select device providing network interface, e.g. eth0

View File

@@ -18,6 +18,7 @@
#include <unistd.h>
#include "devtype.h"
#include "lib/util_libc.h"
#include "misc.h"
#include "path.h"
#include "zfcp.h"
@@ -404,3 +405,20 @@ char *path_get_bus_attr(const char *bus, const char *attr)
{
return path_get("/sys/bus/%s/%s", bus, attr);
}
/* Read text file from adjusted path. */
char *path_read_text_file(int chomp, err_t err, char *fmt, ...)
{
char *path, *path2, *text;
va_list args;
va_start(args, fmt);
util_vasprintf(&path, fmt, args);
va_end(args);
path2 = path_get(path);
text = misc_read_text_file(path2, chomp, err);
free(path2);
free(path);
return text;
}

View File

@@ -69,7 +69,8 @@ bool select_opts_dev_specified(struct select_opts *select)
!util_list_is_empty(&select->by_if) ||
!util_list_is_empty(&select->by_attr) ||
select->all || select->configured || select->existing ||
select->online || select->offline || select->failed)
select->online || select->offline || select->failed ||
select->ipldev)
return true;
return false;
@@ -1207,6 +1208,19 @@ out:
return rc;
}
/* Select device that was used for IPL. */
static void select_ipldev(struct util_list *selected)
{
struct subtype *st;
char *id;
if (!subtypes_find_ipldev(&st, &id))
return;
selected_dev_list_add(selected, st->devtype, st, id, NULL, 0);
free(id);
}
/* Determine list of IDs of selected devices based on selection options.
* Results are stored as list of struct selected_dev_node in SELECTED.
* If @existing is set, only return nodes for existing devices.
@@ -1235,7 +1249,7 @@ exit_code_t select_devices(struct select_opts *select,
!(util_list_is_empty(&select->by_path) &&
util_list_is_empty(&select->by_node) &&
util_list_is_empty(&select->by_if) &&
util_list_is_empty(&select->by_attr)))
util_list_is_empty(&select->by_attr) && !select->ipldev))
goto by_function;
/* Convert device ID specifications to enable tracking of specifications
@@ -1291,6 +1305,9 @@ by_function:
goto out;
}
if (select->ipldev)
select_ipldev(selected);
out:
/* Release device ID specifications tracking list. */
ptrlist_free(specs, 1);

View File

@@ -96,6 +96,30 @@ static exit_code_t devnode_cb(struct subtype *st, const char *id,
return rc;
}
/* Find device which was used for IPL. Note that id_ptr will point to a newly
* allocated string on success. */
bool subtypes_find_ipldev(struct subtype **st_ptr, char **id_ptr)
{
int i, j;
struct devtype *dt;
struct subtype *st;
char *id;
for (i = 0; (dt = devtypes[i]); i++) {
for (j = 0; (st = dt->subtypes[j]); j++) {
id = subtype_get_ipldev_id(st);
if (id) {
*st_ptr = st;
*id_ptr = id;
return true;
}
}
}
return false;
}
/* Find a device which provides the specified devnode. Note that id_ptr will
* point to a newly allocated string on success. */
bool subtypes_find_by_devnode(struct devnode *devnode, struct subtype **st_ptr,
@@ -520,6 +544,16 @@ void subtype_add_definable_ids(struct subtype *st, struct util_list *ids)
super->add_definable_ids(st, ids);
}
char *subtype_get_ipldev_id(struct subtype *st)
{
struct subtype *super = super_get(st, get_ipldev_id, 0);
if (super)
return super->get_ipldev_id(st);
return NULL;
}
/*
* Subtype helpers. These functions combine some of the subtype methods
* to implement more complex functions.

View File

@@ -1105,6 +1105,28 @@ static exit_code_t zfcp_lun_st_detect_definable(struct subtype *st,
return EXIT_OK;
}
static char *zfcp_lun_st_get_ipldev_id(struct subtype *st)
{
char *type, *fcp_dev, *wwpn, *lun, *id = NULL;
type = path_read_text_file(1, err_ignore, PATH_IPL "/ipl_type");
if (strcmp(type, "fcp") != 0)
goto out;
fcp_dev = path_read_text_file(1, err_ignore, PATH_IPL "/device");
wwpn = path_read_text_file(1, err_ignore, PATH_IPL "/wwpn");
lun = path_read_text_file(1, err_ignore, PATH_IPL "/lun");
if (fcp_dev && wwpn && lun)
id = misc_asprintf("%s:%s:%s", fcp_dev, wwpn, lun);
free(lun);
free(wwpn);
free(fcp_dev);
out:
free(type);
return id;
}
/*
* zfcp lun subtype.
*/
@@ -1168,4 +1190,6 @@ struct subtype zfcp_lun_subtype = {
.detect_definable = &zfcp_lun_st_detect_definable,
.device_define = &zfcp_lun_st_device_define,
.device_undefine = &zfcp_lun_st_device_undefine,
.get_ipldev_id = &zfcp_lun_st_get_ipldev_id,
};

View File

@@ -809,6 +809,8 @@ int dfi_init(void)
l.dfi = dfi;
g.fh = dfi_dump_open(g.opts.device);
rc = dfi->init();
if (rc == 0)
dfi_mem_chunk_sort();
if (rc == 0 && dfi_feat_seek()) {
kdump_init();
dfi_vmcoreinfo_init();

View File

@@ -324,6 +324,14 @@ void dfi_mem_chunk_read_zero(struct dfi_mem_chunk *UNUSED(mem_chunk),
memset(buf, 0, cnt);
}
/*
* Sort memory chunks by start value
*/
void dfi_mem_chunk_sort(void)
{
util_list_sort(&l.mem_virt.chunk_list, mem_chunk_cmp_fn, NULL);
}
/*
* Return mem_chunk list head
*/
@@ -350,6 +358,14 @@ u64 dfi_mem_range(void)
return l.mem_virt.end_addr - l.mem_virt.start_addr + 1;
}
/*
* Return maximum memory address
*/
u64 dfi_mem_end(void)
{
return l.mem_virt.end_addr;
}
/*
* Is memory range valid?
*/

View File

@@ -43,6 +43,7 @@ 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);
u64 dfi_mem_end(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);
@@ -54,6 +55,7 @@ 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)
void dfi_mem_chunk_sort(void);
int dfi_mem_virt_read(u64 addr, void *buf, size_t cnt);
int dfi_mem_phys_read(u64 addr, void *buf, size_t cnt);

View File

@@ -82,11 +82,11 @@ static struct vm_info *vm_info_get(void)
util_log_print(UTIL_LOG_TRACE, "DFI read vm_info\n");
vm_info.identity_base = l.os_info->entry[OS_INFO_IDENTITY_BASE].val;
if (vm_info.identity_base == 0)
return NULL;
vm_info.kaslr_offset = l.os_info->entry[OS_INFO_KASLR_OFFSET].val;
if (vm_info.kaslr_offset == 0)
return NULL;
vm_info.kaslr_offset_phys = l.os_info->entry[OS_INFO_KASLR_OFF_PHYS].val;
vm_info.identity_base = l.os_info->entry[OS_INFO_IDENTITY_BASE].val;
vm_info.amode31_start = l.os_info->entry[OS_INFO_AMODE31_START].val;
vm_info.amode31_end = l.os_info->entry[OS_INFO_AMODE31_END].val;

View File

@@ -6,6 +6,7 @@
*/
#include <string.h>
#include "lib/util_log.h"
#include "zg.h"
#include "dfi_mem_chunk.h"
@@ -27,6 +28,10 @@ void dfo_chunk_add(u64 start, u64 size, void *data, dfo_chunk_read_fn read_fn)
{
struct dfo_chunk *dfo_chunk;
util_log_print(UTIL_LOG_DEBUG, "DFO add chunk start=0x%lx, size=0x%lx\n", start, size);
if (size == 0)
return;
dfo_chunk = zg_alloc(sizeof(*dfo_chunk));
dfo_chunk->start = start;
dfo_chunk->end = start + size - 1;

View File

@@ -30,6 +30,7 @@
static struct {
struct df_s390_hdr hdr;
struct df_s390_em em;
u64 dfi_max_end; // Max end address among processed DFI memory chunks
} l;
/*
@@ -159,19 +160,48 @@ static void add_cpu_to_dfo(struct dfi_cpu *cpu)
}
/*
* Add memory chunk to dump layout
* Add memory chunk to DFO dump layout.
* The function is supposed to be called in a loop for each DFI mem chunk in the list.
* DFI memory chunk list is considered to be sorted by start address, thus the
* input memory chunk coming last has a priority for DFO.
*/
static void add_mem_chunk_to_dfo(struct dfi_mem_chunk *mem_chunk)
{
struct dfi_mem_chunk *mem_chunk_prev = dfi_mem_chunk_prev(mem_chunk);
const u64 dfo_current_memsz = dfo_chunk_dump_size() - DF_S390_HDR_SIZE;
if (mem_chunk_prev && (mem_chunk_prev->end + 1 != mem_chunk->start))
dfo_chunk_add(mem_chunk_prev->end + 1 + DF_S390_HDR_SIZE,
mem_chunk->start - mem_chunk_prev->end - 1,
/*
* Consider memory holes and filtered memory pages filling the missing
* areas with zeroes for s390 output format. Keep in mind that dfi_mem_chunks
* can overlap for VR-kernel dumps.
* If no dfi_mem_chunk with zero start address registered, add
* zero DFO memory chunk first.
*
* See the diagram of resulting dfo_s390 for several cases of input memory
* chunks layout below:
*
* DFI DFO(s390)
* ---------------------------------
* aaaa.... HDR|aa|bb|cc|cc
* ..bbbb..
* ....cccc
* ---------------------------------
* ..aa.... HDR|00|aa|00|bb
* ......bb
* ---------------------------------
* aaaaaaaa HDR|aa|bb|aa|cc
* ..bb....
* ......cc
*/
if (l.dfi_max_end < mem_chunk->start &&
mem_chunk->start - l.dfi_max_end > 1)
dfo_chunk_add(dfo_chunk_dump_size(),
mem_chunk->start - dfo_current_memsz,
NULL, dfo_chunk_zero_fn);
dfo_chunk_add(mem_chunk->start + DF_S390_HDR_SIZE, mem_chunk->size,
mem_chunk, dfo_chunk_mem_fn);
l.dfi_max_end = MAX(mem_chunk->end, l.dfi_max_end);
}
/*
@@ -187,7 +217,7 @@ static void dump_chunks_init(void)
add_mem_chunk_to_dfo(mem_chunk);
dfi_cpu_iterate(cpu)
add_cpu_to_dfo(cpu);
dfo_chunk_add(dfi_mem_range() + DF_S390_HDR_SIZE,
dfo_chunk_add(l.hdr.mem_size + DF_S390_HDR_SIZE,
DF_S390_EM_SIZE,
&l.em, dfo_chunk_buf_fn);
}
@@ -209,7 +239,8 @@ static void df_s390_dump_init(void)
else
dh->version = 5;
dh->mem_start = 0;
dh->mem_size = dh->mem_end = dfi_mem_range();
dh->mem_end = dfi_mem_end();
dh->mem_size = dh->mem_end + 1;
dh->num_pages = dh->mem_size / PAGE_SIZE;
dh->arch = df_s390_from_dfi_arch(dfi_arch());
if (dfi_attr_build_arch())

View File

@@ -435,6 +435,13 @@ static int ngdump_get_eckd_part_num(struct zg_fh *zg_fh)
if (!is_vol1(vl.vollbl))
return -1;
/* Read Master Boot Record and check its magic */
if (vl.br.cc == 0x4040 && vl.br.hh == 0x4040 &&
vl.br.b == 0x40) {
util_log_print(UTIL_LOG_TRACE,
"%s: No boot record pointer found in volume label\n",
__func__);
return -1;
}
blk = cchhb2blk(&vl.br, &geo);
if (blk == 0)
return -1;

View File

@@ -5,7 +5,7 @@
#
# Script to list the FCP, SCSI and multipath configuration
#
# Copyright IBM Corp. 2008, 2017
# Copyright IBM Corp. 2008, 2024
#
# s390-tools is free software; you can redistribute it and/or modify
# it under the terms of the MIT license. See LICENSE for details.
@@ -130,8 +130,8 @@ sub get_device_data
unless(!$devices{$hctl}{dev});
$devices{$hctl}{mm} = get_line("dev");
$devices{$hctl}{mp_dev} = glob_last("holders", "dm*");
$devices{$hctl}{mp_dev} =~ s/.*\/(.+)\Z/$1/
unless (!$devices{$hctl}{mp_dev});
next if !defined $devices{$hctl}{mp_dev};
$devices{$hctl}{mp_dev} =~ s/.*\/(.+)\Z/$1/;
$c_src = catdir($base_dir, "sys", "block",
$devices{$hctl}{mp_dev});
$devices{$hctl}{mp_dev_mm} = get_line("dev");
@@ -297,7 +297,7 @@ sub mapper_report
"=====================================================\n";
}
foreach my $hctl (sort keys %devices) {
next if (! $devices{$hctl}{mp_dev});
next if (!defined $devices{$hctl}{mp_dev});
next if (@$adapters && "@$adapters" !~ /\b$devices{$hctl}{hba_id}\b/);
next if (@$ports && "@$ports" !~ /\b$devices{$hctl}{wwpn}\b/);
next if (@$s_devs && "@$s_devs" !~ /\b$devices{$hctl}{dev}\b/);
@@ -391,7 +391,7 @@ MSG
sub version {
print "$PROGRAM_NAME: version %S390_TOOLS_VERSION%\n";
print "Copyright IBM Corp. 2008, 2017\n";
print "Copyright IBM Corp. 2008, 2024\n";
}
sub unpack_config

View File

@@ -72,5 +72,6 @@ SECTIONS
*(.eh_frame)
*(.interp)
*(.note.GNU-stack)
*(.note.package)
}
}

View File

@@ -14,5 +14,6 @@ SECTIONS
*(.eh_frame)
*(.interp)
*(.note.GNU-stack)
*(.note.package)
}
}

View File

@@ -14,5 +14,6 @@ SECTIONS
*(.eh_frame)
*(.interp)
*(.note.GNU-stack)
*(.note.package)
}
}

View File

@@ -14,5 +14,6 @@ SECTIONS
*(.eh_frame)
*(.interp)
*(.note.GNU-stack)
*(.note.package)
}
}

View File

@@ -93,5 +93,6 @@ SECTIONS
*(.eh_frame)
*(.interp)
*(.note.GNU-stack)
*(.note.package)
}
}

View File

@@ -74,5 +74,6 @@ SECTIONS
*(.eh_frame)
*(.interp)
*(.note.GNU-stack)
*(.note.package)
}
}

View File

@@ -53,16 +53,22 @@ typedef enum {
disk_type_eckd_cdl,
} disk_type_t;
struct disk_ext_type {
disk_type_t type;
bool is_nvme;
};
/* targetbase definition */
typedef enum {
defined_as_device,
defined_as_name
defined_as_name,
undefined
} definition_t;
/* Disk information type */
struct disk_info {
disk_type_t type;
dev_t device;
dev_t device; /* logical device for bootmap creation */
dev_t partition;
int devno;
int partnum;
@@ -72,8 +78,11 @@ struct disk_info {
struct hd_geometry geo;
char* name;
char* drv_name;
definition_t targetbase;
definition_t targetbase_def;
int is_nvme;
dev_t basedisks[MAX_TARGETS]; /* array of physical disks for
* bootstrap blocks recording
*/
};
struct file_range {
@@ -83,12 +92,15 @@ struct file_range {
struct job_target_data;
int disk_get_info(const char* device, struct job_target_data* target,
struct disk_info** info);
int disk_is_tape(const char* device);
int disk_is_scsi(const char* device, struct job_target_data* target);
int disk_is_eckd_ldl(const char* device, struct job_target_data* target);
int disk_is_nvme(const char* device, struct job_target_data* target);
int disk_get_info(const char *device, struct job_target_data *target,
struct disk_info **info);
int disk_get_ext_type(const char *device, struct disk_ext_type *ext_type);
int disk_is_tape(const char *device);
int disk_type_is_scsi(struct disk_ext_type *ext_type);
int disk_type_is_eckd_ldl(struct disk_ext_type *ext_type);
int disk_type_is_nvme(struct disk_ext_type *ext_type);
int disk_type_is_eckd(disk_type_t type);
int disk_get_info_from_file(const char* filename,
struct job_target_data* target,
struct disk_info** info);
@@ -113,6 +125,9 @@ blocknum_t disk_write_block_buffer_align(int fd, int fd_is_basedisk,
struct disk_info *info, int align,
off_t *offset);
void disk_print_devt(dev_t d);
void disk_print_devname(dev_t d);
void prepare_footnote_ptr(int source, char *ptr);
void print_footnote_ref(int source, const char *prefix);
void disk_print_info(struct disk_info *info, int source);
int disk_is_zero_block(disk_blockptr_t* block, struct disk_info* info);
blocknum_t disk_compact_blocklist(disk_blockptr_t* list, blocknum_t count,
@@ -122,7 +137,6 @@ blocknum_t disk_get_blocklist_from_file(const char* filename,
disk_blockptr_t** blocklist,
struct disk_info* pinfo);
int disk_check_subchannel_set(int devno, dev_t device, char* dev_name);
void disk_print_geo(struct disk_info *data);
int fs_map(int fd, uint64_t offset, blocknum_t *mapped, int fs_block_size);
#endif /* not DISK_H */

View File

@@ -71,7 +71,7 @@ struct install_set {
struct program_component *components[NR_PROGRAM_COMPONENTS];
int nr_menu_entries;
int fd;
char *device;
char *basetmp[MAX_TARGETS];
char *filename;
unsigned int tmp_filename_created:1;
unsigned int skip_prepare:1;

View File

@@ -18,7 +18,6 @@
#include "disk.h"
#include "zipl.h"
enum job_id {
job_print_usage = 1,
job_print_version = 2,
@@ -30,6 +29,21 @@ enum job_id {
job_mvdump = 8,
};
/*
* Set of parameters per physical disk, which are provided
* either by user, or by helper script
*/
struct target {
char *targetbase;
disk_type_t targettype;
int targetcylinders;
int targetheads;
int targetsectors;
int targetblocksize;
blocknum_t targetoffset;
int check_params;
};
/* target information source */
typedef enum {
source_unknown = 0,
@@ -39,17 +53,21 @@ typedef enum {
} source_t;
struct job_target_data {
char* bootmap_dir;
char* targetbase;
disk_type_t targettype;
int targetcylinders;
int targetheads;
int targetsectors;
int targetblocksize;
blocknum_t targetoffset;
char *bootmap_dir;
int nr_targets;
struct target targets[MAX_TARGETS];
source_t source;
};
enum target_params {
TARGET_BASE,
TARGET_TYPE,
TARGET_GEOMETRY,
TARGET_BLOCKSIZE,
TARGET_OFFSET,
LAST_TARGET_PARAM
};
struct job_common_ipl_data {
char* image;
char* parmline;
@@ -81,6 +99,7 @@ struct job_dump_data {
struct job_common_ipl_data common;
char* device;
uint64_t mem;
bool is_ngdump;
bool no_compress;
};
@@ -142,19 +161,104 @@ struct job_data {
int is_ldipl_dump;
};
static inline struct target *target_at(struct job_target_data *data,
int index)
{
return index >= MAX_TARGETS ? NULL : &data->targets[index];
}
static inline char *get_targetbase(struct job_target_data *data, int index)
{
return target_at(data, index)->targetbase;
}
static inline void set_targetbase(struct job_target_data *data, int index,
char *value)
{
target_at(data, index)->targetbase = value;
}
static inline disk_type_t get_targettype(struct job_target_data *data,
int index)
{
return target_at(data, index)->targettype;
}
int set_targettype(struct job_target_data *data, int index, char *value);
static inline char *job_get_targetbase(struct job_data *job)
{
return get_targetbase(&job->target, 0);
}
static inline void job_set_targetbase(struct job_data *job, char *value)
{
set_targetbase(&job->target, 0, value);
}
static inline int job_get_nr_targets(struct job_data *job)
{
return job->target.nr_targets;
}
static inline void job_set_nr_targets(struct job_data *job, int value)
{
job->target.nr_targets = value;
}
static inline disk_type_t job_get_targettype(struct job_data *job)
{
return get_targettype(&job->target, 0);
}
int job_set_targettype(struct job_data *job, char *value);
#define define_target_param_ops(_TYPE_, _PARAM_) \
static inline _TYPE_ get_target##_PARAM_(struct job_target_data *data, \
int index) \
{ \
return target_at(data, index)->target##_PARAM_; \
} \
\
static inline void set_target##_PARAM_(struct job_target_data *data, \
int index, _TYPE_ value) \
{ \
target_at(data, index)->target##_PARAM_ = value; \
} \
\
static inline _TYPE_ job_get_target##_PARAM_(struct job_data *job) \
{ \
return get_target##_PARAM_(&job->target, 0); \
} \
\
static inline void job_set_target##_PARAM_(struct job_data *job, \
_TYPE_ value) \
{ \
set_target##_PARAM_(&job->target, 0, value); \
}
define_target_param_ops(int, cylinders)
define_target_param_ops(int, heads)
define_target_param_ops(int, sectors)
define_target_param_ops(int, blocksize)
define_target_param_ops(blocknum_t, offset)
/**
* Return true, if target parameters for the base disk are set
* Return true, if target parameters are set at least for one target base disk
*/
static inline int target_parameters_are_set(struct job_target_data *td)
{
return td->targetbase != NULL;
return get_targetbase(td, 0) != NULL;
}
int job_get(int argc, char* argv[], struct job_data** data);
void job_free(struct job_data* job);
void free_target_data(struct job_target_data *td);
int type_from_target(char *target, disk_type_t *type);
int check_job_dump_images(struct job_dump_data* dump, char* name);
int check_job_images_ngdump(struct job_dump_data* dump, char* name);
bool is_ngdump_enabled(struct job_data *job);
void job_dump_check_set_ngdump(struct job_data *job,
struct disk_ext_type *ext_type);
bool job_dump_is_ngdump(struct job_data *job);
#endif /* not JOB_H */

View File

@@ -41,6 +41,7 @@
#define MENU_DEFAULT_TIMEOUT 0
#define MAX_DUMP_VOLUMES 32
#define MAX_TARGETS 32
#define SECURE_BOOT_UNDEFINED -1
#define SECURE_BOOT_DISABLED 0

View File

@@ -13,21 +13,25 @@ objects = misc.o error.o scan.o job.o boot.o bootmap.o fs-map.o disk.o \
bootmap_header.o envblk.o install.o zipl.o
zipl_helpers = $(basename $(wildcard zipl_helper.*.c))
zipl_helpers += zipl_helper.md
chreipl_helpers = $(subst zipl_,chreipl_, $(zipl_helpers))
all: zipl zipl-editenv $(chreipl_helpers)
all: zipl zipl-editenv zipl_helper.md $(chreipl_helpers)
boot.o: ../boot/.loaders
zipl: $(objects) $(libs)
zipl_helper.device-mapper: $(rootdir)/libdasd/libdasd.a \
$(rootdir)/libvtoc/libvtoc.a \
$(libs) zipl_helper.device-mapper.o
$(libs) misc.o error.o zipl_helper.device-mapper.o
zipl-editenv: $(libs) misc.o error.o fs-map.o bootmap_header.o \
envblk.o zipl-editenv.o
chreipl_helper.%: zipl_helper.%
zipl_helper.md: zipl_helper.device-mapper
ln -f -s $< $@
chreipl_helper.%: zipl_helper.device-mapper
ln -f -s $< $@
install: all

View File

@@ -298,13 +298,53 @@ create_component_header(void* buffer, component_header_type type)
header->type = (uint8_t) type;
}
/*
* Not precise check that the file FILENAME locates on specified physical DISK.
*
* Try to auto-detect parameters of the disk which the file locates on
* and compare found device-ID with DISK.
* Return 0, if auto-detection succeeded, and it is proven that the
* file does NOT locate on DISK. Otherwise, return 1.
*/
static int file_is_on_disk(const char *filename, dev_t disk)
{
/*
* Retrieve info of the underlying disk without any user hints
*/
struct job_target_data tmp = {.source = source_unknown};
struct disk_info *info;
int rc;
rc = disk_get_info_from_file(filename, &tmp, &info);
free_target_data(&tmp);
if (rc) {
/*
* In some cases it is impossible to auto-detect
* disk parameters (e.g. when the file is on a
* mounted qcow2 image).
* Skip the check with warnings.
*/
fprintf(stderr,
"Warning: Could not auto-detect disk parameters for %s\n",
filename);
fprintf(stderr,
"Warning: Preparing a logical device for boot might fail\n");
return 1;
}
if (info->device != disk) {
disk_free_info(info);
return 0;
}
disk_free_info(info);
return 1;
}
static int add_component_file_range(struct install_set *bis,
const char *filename,
struct file_range *reg,
address_t load_address,
size_t trailer, void *component,
int add_files,
struct job_target_data *target,
int comp_id, int menu_idx,
int program_table_id)
{
@@ -312,7 +352,6 @@ static int add_component_file_range(struct install_set *bis,
struct component_loc *location = &pc->loc;
disk_blockptr_t **list = &pc->list;
blocknum_t *count = &pc->count;
struct disk_info* file_info;
disk_blockptr_t segment;
char* buffer;
size_t size;
@@ -339,19 +378,13 @@ static int add_component_file_range(struct install_set *bis,
return -1;
}
} else {
/* Make sure file is on correct device */
rc = disk_get_info_from_file(filename, target, &file_info);
if (rc)
return -1;
if (file_info->device != bis->info->device) {
disk_free_info(file_info);
if (!file_is_on_disk(filename, bis->info->device)) {
error_reason("File is not on target device");
return -1;
}
/* Get block list from existing file */
*count = disk_get_blocklist_from_file(filename, reg,
list, file_info);
disk_free_info(file_info);
list, bis->info);
if (*count == 0)
return -1;
*count -= DIV_ROUND_UP(trailer, bis->info->phy_block_size);
@@ -377,12 +410,11 @@ write_segment_table:
static int add_component_file(struct install_set *bis, const char *filename,
address_t load_address, size_t trailer,
void *component, int add_files,
struct job_target_data *target, int comp_id,
int menu_idx, int program_table_id)
int comp_id, int menu_idx, int program_table_id)
{
return add_component_file_range(bis, filename, NULL, load_address,
trailer, component, add_files,
target, comp_id, menu_idx,
comp_id, menu_idx,
program_table_id);
}
@@ -544,8 +576,7 @@ static int add_ipl_program(struct install_set *bis, char *filename,
bool add_envblk, struct job_envblk_data *envblk,
struct job_ipl_data *ipl, disk_blockptr_t *program,
int verbose, int add_files, component_header_type type,
struct job_target_data *target, int is_secure,
int menu_idx, int program_table_id)
int is_secure, int menu_idx, int program_table_id)
{
struct signature_header sig_head;
size_t ramdisk_size, image_size;
@@ -667,7 +698,7 @@ static int add_ipl_program(struct install_set *bis, char *filename,
/* Add stage 3 loader to bootmap */
rc = add_component_file(bis, ZIPL_STAGE3_PATH, STAGE3_LOAD_ADDRESS,
signature_size, VOID_ADD(table, offset), 1,
target, COMPONENT_ID_LOADER, menu_idx,
COMPONENT_ID_LOADER, menu_idx,
program_table_id);
if (rc) {
error_text("Could not add internal loader file '%s'",
@@ -745,7 +776,7 @@ static int add_ipl_program(struct install_set *bis, char *filename,
rc = add_component_file(bis, ipl->common.image, ipl->common.image_addr,
signature_size, VOID_ADD(table, offset),
add_files, target, COMPONENT_ID_KERNEL_IMAGE,
add_files, COMPONENT_ID_KERNEL_IMAGE,
menu_idx, program_table_id);
if (rc) {
error_text("Could not add image file '%s'", ipl->common.image);
@@ -805,7 +836,7 @@ static int add_ipl_program(struct install_set *bis, char *filename,
ipl->common.ramdisk_addr,
signature_size,
VOID_ADD(table, offset),
add_files, target, COMPONENT_ID_RAMDISK,
add_files, COMPONENT_ID_RAMDISK,
menu_idx, program_table_id);
if (rc) {
error_text("Could not add ramdisk '%s'",
@@ -861,7 +892,7 @@ static int add_ipl_program(struct install_set *bis, char *filename,
rc = add_component_file_range(bis, filename, &reg,
ipl->envblk_addr, 0,
VOID_ADD(table, offset),
0, target,
0,
COMPONENT_ID_ENVBLK,
menu_idx,
program_table_id);
@@ -893,7 +924,6 @@ static int add_segment_program(struct install_set *bis,
struct job_segment_data *segment,
disk_blockptr_t *program, int verbose,
int add_files, component_header_type type,
struct job_target_data *target,
int program_table_id)
{
void *table;
@@ -913,7 +943,7 @@ static int add_segment_program(struct install_set *bis,
printf(" segment file......: %s\n", segment->segment);
rc = add_component_file(bis, segment->segment, segment->segment_addr, 0,
VOID_ADD(table, offset), add_files, target,
VOID_ADD(table, offset), add_files,
COMPONENT_ID_SEGMENT_FILE, 0 /* menu_idx */,
program_table_id);
if (rc) {
@@ -939,64 +969,22 @@ static int add_segment_program(struct install_set *bis,
return rc;
}
#define DUMP_PARAM_MAX_LEN 896
static int
check_dump_device_late(char *partition, struct disk_info *target_info,
struct job_target_data *target,
struct job_data *job)
{
struct disk_info* info;
int rc;
/* Get information about partition */
rc = disk_get_info(partition, target, &info);
if (rc) {
error_text("Could not get information for dump partition '%s'",
partition);
return rc;
}
if ((job->is_ldipl_dump && info->type != disk_type_eckd_cdl) ||
(!job->is_ldipl_dump && info->type != disk_type_scsi) ||
info->partnum == 0) {
error_reason("Device '%s' is not a SCSI or DASD partition",
partition);
disk_free_info(info);
return -1;
}
if (info->device != target_info->device) {
error_reason("Target directory is not on same device as "
"'%s'", partition);
disk_free_info(info);
return -1;
}
disk_free_info(info);
return 0;
}
static int add_dump_program(struct install_set *bis, struct job_data *job,
static int add_dump_program(struct install_set *bis,
const struct job_dump_data *dump,
disk_blockptr_t *program, int verbose,
component_header_type type,
int program_table_id)
{
struct job_dump_data *dump = &job->data.dump;
struct job_target_data *target = &job->target;
struct job_ipl_data ipl;
int rc;
/* Convert fs dump job to IPL job */
memset(&ipl, 0, sizeof(ipl));
ipl.common = dump->common;
/* Get file system dump parmline */
rc = check_dump_device_late(dump->device, bis->info, target, job);
if (rc)
return rc;
ipl.common.parmline = dump->common.parmline;
ipl.common.parm_addr = dump->common.parm_addr;
return add_ipl_program(bis, NULL, false, NULL, &ipl, program,
verbose, 1, type, target, SECURE_BOOT_DISABLED,
verbose, 1, type, SECURE_BOOT_DISABLED,
0 /* menu_idx */, program_table_id);
}
@@ -1041,8 +1029,7 @@ static int build_program_table(struct job_data *job,
true, &job->envblk, &job->data.ipl,
&table[0], verbose || job->command_line,
job->add_files, component_header,
&job->target, job->is_secure, 0,
program_table_id);
job->is_secure, 0, program_table_id);
break;
case job_segment:
if (bis->print_details) {
@@ -1055,7 +1042,7 @@ static int build_program_table(struct job_data *job,
rc = add_segment_program(bis, &job->data.segment, &table[0],
verbose || job->command_line,
job->add_files, COMPONENT_HEADER_IPL,
&job->target, program_table_id);
program_table_id);
break;
case job_dump_partition:
/* Only useful for a partition dump that uses a dump kernel*/
@@ -1066,7 +1053,7 @@ static int build_program_table(struct job_data *job,
printf("Adding dump section '%s' (default)\n",
job->name);
}
rc = add_dump_program(bis, job, &table[0],
rc = add_dump_program(bis, &job->data.dump, &table[0],
verbose || job->command_line,
COMPONENT_HEADER_DUMP,
program_table_id);
@@ -1114,7 +1101,7 @@ static int build_program_table(struct job_data *job,
&table[job->data.menu.entry[i].pos],
verbose || job->command_line,
job->add_files, component_header,
&job->target, is_secure, i,
is_secure, i,
program_table_id);
break;
case job_print_usage:
@@ -1379,7 +1366,7 @@ estimate_scsi_dump_size(struct job_data *job, struct disk_info *info, ulong *dum
}
/**
* Check that disk is appropriate for the JOB
* Check that disk with retrieved INFO is appropriate for the JOB
*/
static int disk_is_appropriate(const struct job_data *job,
const struct disk_info *info)
@@ -1390,11 +1377,31 @@ static int disk_is_appropriate(const struct job_data *job,
error_reason("Secure boot forced for improper disk type");
return 0;
}
if (job->id == job_dump_partition &&
job->is_ldipl_dump &&
info->type != disk_type_eckd_cdl) {
error_reason("List-directed dump not support on DASD with LDL format");
return 0;
/* common checks */
if (job->target.source == source_auto &&
info->type == disk_type_diag) {
error_reason("Unsupported disk type (%s)",
disk_get_type_name(info->type));
return -1;
}
/* job-specific checks */
if (job->id == job_dump_partition) {
if (job->is_ldipl_dump && info->type != disk_type_eckd_cdl) {
error_reason("Inappropriate dump device (not DASD-CDL)");
return 0;
}
if (!job->is_ldipl_dump && info->type != disk_type_scsi) {
error_reason("Inappropriate dump device (not SCSI)");
return 0;
}
/* Check that data starts beyond the boot area on the base disk.
* In case of source_script the check is performed by the script
*/
if (job->target.source == source_auto && info->partnum == 0) {
error_reason("Dump device %s is not a partition",
job->data.dump.device);
return 0;
}
}
return 1;
}
@@ -1405,19 +1412,10 @@ check_dump_device(struct job_data *job, const struct disk_info *info,
{
int rc, part_ext;
/* Check for supported disk and driver types */
if (job->target.source == source_auto &&
info->type == disk_type_diag) {
error_reason("Unsupported disk type (%s)",
disk_get_type_name(info->type));
return -1;
}
if (!disk_is_appropriate(job, info))
return -1;
if (is_ngdump_enabled(job))
if (job_dump_is_ngdump(job))
return 0;
rc = util_part_search(device, info->geo.start,
info->phy_blocks, info->phy_block_size, &part_ext);
if (rc <= 0 || part_ext) {
@@ -1444,30 +1442,18 @@ static int prepare_build_program_table_device(struct job_data *job,
if (bis->skip_prepare)
/* skip the preparation work */
return 0;
if (dry_run) {
error_text("Option '--dry-run' is not implemented for this type of dump");
return -1;
}
/* Get full path of bootmap file */
if (!dry_run) {
bis->filename = misc_strdup(job->data.dump.device);
if (!bis->filename)
return -1;
bis->fd = misc_open_exclusive(bis->filename);
if (bis->fd == -1) {
error_text("Could not open file '%s'", bis->filename);
return -1;
}
} else {
bis->filename = misc_make_path(job->target.bootmap_dir,
BOOTMAP_TEMPLATE_FILENAME);
if (!bis->filename)
return -1;
/* Create temporary bootmap file */
bis->fd = mkstemp(bis->filename);
if (bis->fd == -1) {
error_reason(strerror(errno));
error_text("Could not create file '%s':",
bis->filename);
return -1;
}
bis->tmp_filename_created = 1;
bis->filename = misc_strdup(job->data.dump.device);
if (!bis->filename)
return -1;
bis->fd = misc_open_exclusive(bis->filename);
if (bis->fd == -1) {
error_text("Could not open file '%s'", bis->filename);
return -1;
}
/* Retrieve target device information */
if (disk_get_info(bis->filename, &job->target, &bis->info))
@@ -1477,9 +1463,9 @@ static int prepare_build_program_table_device(struct job_data *job,
printf("Target device information\n");
disk_print_info(bis->info, job->target.source);
}
if (misc_temp_dev(bis->info->device, 1, &bis->device))
if (misc_temp_dev(bis->info->basedisks[0], 1, &bis->basetmp[0]))
return -1;
if (check_dump_device(job, bis->info, bis->device))
if (check_dump_device(job, bis->info, bis->basetmp[0]))
return -1;
printf("Building bootmap directly on partition '%s'%s\n",
bis->filename,
@@ -1543,6 +1529,8 @@ static int prepare_bootloader_device(struct job_data *job,
static int prepare_build_program_table_file(struct job_data *job,
struct install_set *bis)
{
int i;
if (bis->skip_prepare)
/* skip the preparation work */
return 0;
@@ -1563,21 +1551,18 @@ static int prepare_build_program_table_file(struct job_data *job,
* block size. */
if (disk_get_info_from_file(bis->filename, &job->target, &bis->info))
return -1;
/* Check for supported disk and driver types */
if (job->target.source == source_auto &&
bis->info->type == disk_type_diag) {
error_reason("Unsupported disk type (%s)",
disk_get_type_name(bis->info->type));
return -1;
}
if (!disk_is_appropriate(job, bis->info))
return -1;
if (verbose) {
printf("Target device information\n");
disk_print_info(bis->info, job->target.source);
}
if (misc_temp_dev(bis->info->device, 1, &bis->device))
return -1;
for (i = 0; i < job_get_nr_targets(job); i++) {
if (misc_temp_dev(bis->info->basedisks[i],
1,
&bis->basetmp[i]))
return -1;
}
/* Check configuration number limits */
if (job->id == job_menu) {
if (check_menu_positions(&job->data.menu, job->name,
@@ -1692,9 +1677,9 @@ static int prepare_bootloader_ngdump(struct job_data *job,
/* Retrieve target device information */
if (disk_get_info(job->data.dump.device, &job->target, &info))
return -1;
if (misc_temp_dev(info->device, 1, &bis->device))
if (misc_temp_dev(info->basedisks[0], 1, &bis->basetmp[0]))
return -1;
if (check_dump_device(job, info, bis->device))
if (check_dump_device(job, info, bis->basetmp[0]))
return -1;
assert(!job->target.bootmap_dir);
@@ -1800,7 +1785,7 @@ int prepare_bootloader(struct job_data *job, struct install_set *bis)
if (init_bis(job, bis))
return -1;
if (job->id == job_dump_partition) {
if (is_ngdump_enabled(job))
if (job_dump_is_ngdump(job))
return prepare_bootloader_ngdump(job, bis);
else
return prepare_bootloader_device(job, bis);
@@ -1816,7 +1801,7 @@ int prepare_bootloader(struct job_data *job, struct install_set *bis)
int post_install_bootloader(struct job_data *job, struct install_set *bis)
{
if (job->id == job_dump_partition) {
if (is_ngdump_enabled(job))
if (job_dump_is_ngdump(job))
return dry_run ? 0 : finalize_create_file(job, bis);
else
return 0;
@@ -1844,6 +1829,9 @@ void free_bootloader(struct install_set *bis)
if (bis->tmp_filename_created)
misc_free_temp_file(bis->filename);
free(bis->filename);
misc_free_temp_dev(bis->device);
for (i = 0; i < MAX_TARGETS; i++) {
if (bis->basetmp[i])
misc_free_temp_dev(bis->basetmp[i]);
}
disk_free_info(bis->info);
}

View File

@@ -92,14 +92,6 @@ disk_determine_dasd_type(struct disk_info *data,
return 0;
}
/* Return non-zero for ECKD type. */
static int
disk_is_eckd(disk_type_t type)
{
return (type == disk_type_eckd_ldl ||
type == disk_type_eckd_cdl);
}
static int
read_block_by_offset(int fd, int blksize, uint64_t offset, char *buffer)
{
@@ -187,43 +179,134 @@ out_err:
return rc;
}
/**
* Process a script output represented by FH and consisting
* of pairs 'key=value' (each such pair is on a separate line).
* Check its consistency and set the extracted target parameters
* to the array of "targets" at TD.
*
* NOTE: this function defines specifications on valid output of
* zipl helper scripts. See zipl-support-for-mirrored-devices.txt
* for details. Before modifying this function, make sure that it
* won't lead to format change.
*/
static int set_target_parameters(FILE *fh, struct job_target_data *td)
{
int checkparm = 0;
int idx[LAST_TARGET_PARAM] = {0};
struct target *t;
char buffer[80];
char value[40];
char *error;
int i;
/**
* Process a stream of 'key=value' pairs and distribute
* them into groups.
* The i-th occurrence of some "key" in the stream means
* that the respective pair belongs to the group #i
*/
error = "Exceeded the maximum number of base disks";
while (fgets(buffer, 80, fh)) {
if (sscanf(buffer, "targetbase=%s", value) == 1) {
td->targetbase = misc_strdup(value);
checkparm++;
t = target_at(td, idx[TARGET_BASE]++);
if (!t)
goto error;
t->targetbase = misc_strdup(value);
goto found;
}
if (sscanf(buffer, "targettype=%s", value) == 1) {
type_from_target(value, &td->targettype);
checkparm++;
t = target_at(td, idx[TARGET_TYPE]++);
if (!t)
goto error;
type_from_target(value, &t->targettype);
goto found;
}
if (sscanf(buffer, "targetgeometry=%s", value) == 1) {
td->targetcylinders =
atoi(strtok(value, ","));
td->targetheads = atoi(strtok(NULL, ","));
td->targetsectors = atoi(strtok(NULL, ","));
checkparm++;
t = target_at(td, idx[TARGET_GEOMETRY]++);
if (!t)
goto error;
t->targetcylinders = atoi(strtok(value, ","));
t->targetheads = atoi(strtok(NULL, ","));
t->targetsectors = atoi(strtok(NULL, ","));
goto found;
}
if (sscanf(buffer, "targetblocksize=%s", value) == 1) {
td->targetblocksize = atoi(value);
checkparm++;
t = target_at(td, idx[TARGET_BLOCKSIZE]++);
if (!t)
goto error;
t->targetblocksize = atoi(value);
goto found;
}
if (sscanf(buffer, "targetoffset=%s", value) == 1) {
td->targetoffset = atol(value);
checkparm++;
t = target_at(td, idx[TARGET_OFFSET]++);
if (!t)
goto error;
t->targetoffset = atol(value);
goto found;
}
continue;
found:
t->check_params++;
}
if ((!disk_is_eckd(td->targettype) && checkparm < 4) ||
(disk_is_eckd(td->targettype) && checkparm != 5)) {
error_reason("Target parameters missing from script");
return -1;
/* Check for consistency */
error = "Inconsistent script output";
/*
* First, calculate total number of groups
*/
td->nr_targets = 0;
for (i = 0; i < MAX_TARGETS; i++) {
t = target_at(td, i);
if (t->check_params == 0)
break;
td->nr_targets++;
}
if (!td->nr_targets)
/* No keywords found in the stream */
goto error;
/*
* Each group has to include targetbase, targettype,
* targetblocksize and targetoffset.
*/
if (td->nr_targets != idx[TARGET_BASE] ||
td->nr_targets != idx[TARGET_TYPE] ||
td->nr_targets != idx[TARGET_BLOCKSIZE] ||
td->nr_targets != idx[TARGET_OFFSET])
goto error;
/*
* In addition, any group of "ECKD" type has to include
* targetgeometry
*/
for (i = 0; i < td->nr_targets; i++) {
t = target_at(td, i);
assert(t->check_params >= 4);
if (disk_type_is_eckd(t->targettype) && t->check_params != 5)
goto error;
}
return 0;
error:
error_reason("%s", error);
return -1;
}
static void print_base_disk_params(struct job_target_data *td, int index)
{
disk_type_t type = get_targettype(td, index);
if (!verbose)
return;
{
fprintf(stderr, "Base disk '%s':\n", get_targetbase(td, index));
fprintf(stderr, " layout........: %s\n", disk_get_type_name(type));
}
if (disk_type_is_eckd(type)) {
fprintf(stderr, " heads.........: %u\n", get_targetheads(td, index));
fprintf(stderr, " sectors.......: %u\n", get_targetsectors(td, index));
fprintf(stderr, " cylinders.....: %u\n", get_targetcylinders(td, index));
}
{
fprintf(stderr, " start.........: %lu\n", get_targetoffset(td, index));
fprintf(stderr, " blksize.......: %u\n", get_targetblocksize(td, index));
}
}
/**
@@ -235,31 +318,57 @@ static int disk_set_info_by_hint(struct job_target_data *td,
{
int majnum, minnum;
struct stat stats;
int i;
/*
* Currently multiple base disks with different parameters
* are not supported
*/
data->devno = -1;
data->phy_block_size = td->targetblocksize;
data->type = td->targettype;
data->partnum = 0;
data->phy_block_size = get_targetblocksize(td, 0);
data->type = get_targettype(td, 0);
if (sscanf(td->targetbase, "%d:%d", &majnum, &minnum) == 2) {
data->device = makedev(majnum, minnum);
data->targetbase = defined_as_device;
data->partnum = minor(stats.st_rdev) - minnum;
} else {
if (stat(td->targetbase, &stats)) {
error_reason(strerror(errno));
error_text("Could not get information for "
"file '%s'", td->targetbase);
assert(td->nr_targets != 0);
for (i = 1; i < td->nr_targets; i++) {
if (data->type != get_targettype(td, i) ||
data->phy_block_size != get_targetblocksize(td, i)) {
print_base_disk_params(td, 0);
print_base_disk_params(td, i);
error_reason("Inconsistent base disk geometry in target device");
return -1;
}
if (!S_ISBLK(stats.st_mode)) {
error_reason("Target base device '%s' is not "
"a block device",
td->targetbase);
}
data->partnum = 0;
data->targetbase_def = undefined;
for (i = 0; i < td->nr_targets; i++) {
definition_t defined_as;
if (sscanf(get_targetbase(td, i),
"%d:%d", &majnum, &minnum) == 2) {
data->basedisks[i] = makedev(majnum, minnum);
defined_as = defined_as_device;
} else {
if (stat(get_targetbase(td, i), &stats)) {
error_reason(strerror(errno));
error_text("Could not get information for "
"file '%s'", get_targetbase(td, i));
return -1;
}
if (!S_ISBLK(stats.st_mode)) {
error_reason("Target base device '%s' is not "
"a block device",
get_targetbase(td, i));
return -1;
}
data->basedisks[i] = stats.st_rdev;
defined_as = defined_as_name;
}
if (data->targetbase_def != undefined &&
data->targetbase_def != defined_as) {
error_reason("Target base disks are defined by different ways");
return -1;
}
data->device = stats.st_rdev;
data->targetbase = defined_as_name;
data->targetbase_def = defined_as;
}
if (data->type == disk_type_scsi && ioctl(fd, NVME_IOCTL_ID) >= 0)
data->is_nvme = 1;
@@ -446,11 +555,28 @@ static int run_targetbase_script(struct job_target_data *td,
static int disk_set_geometry_by_hint(struct job_target_data *td,
struct disk_info *data)
{
data->geo.heads = td->targetheads;
data->geo.sectors = td->targetsectors;
data->geo.cylinders = td->targetcylinders;
data->geo.start = td->targetoffset;
int i;
/*
* Currently multiple base disks with different parameters
* are not supported
*/
data->geo.heads = get_targetheads(td, 0);
data->geo.sectors = get_targetsectors(td, 0);
data->geo.cylinders = get_targetcylinders(td, 0);
data->geo.start = get_targetoffset(td, 0);
assert(td->nr_targets != 0);
for (i = 1; i < td->nr_targets; i++) {
if (data->geo.heads != get_targetheads(td, i) ||
data->geo.sectors != get_targetsectors(td, i) ||
data->geo.cylinders != get_targetcylinders(td, i) ||
data->geo.start != get_targetoffset(td, i)) {
print_base_disk_params(td, 0);
print_base_disk_params(td, i);
error_reason("Inconsistent base disk geometry in target device");
return -1;
}
}
return 0;
}
@@ -483,7 +609,7 @@ static int disk_set_info_complete(struct job_target_data *td,
return -1;
}
/* Check for valid CHS geometry data. */
if (disk_is_eckd(data->type) && (data->geo.cylinders == 0 ||
if (disk_type_is_eckd(data->type) && (data->geo.cylinders == 0 ||
data->geo.heads == 0 || data->geo.sectors == 0)) {
error_reason("Invalid disk geometry (CHS=%d/%d/%d)",
data->geo.cylinders, data->geo.heads,
@@ -515,19 +641,24 @@ static int disk_set_info_complete(struct job_target_data *td,
}
/**
* Prepare INFO required to perform IPL installation on the physical
* disk where the logical DEVICE is located.
* Prepare INFO required to perform IPL installation on physical disks
* participating in the logical DEVICE.
* Preparation is performed in 2 steps:
*
* 1. Find out a physical "base" disk where the logical DEVICE is
* located. Calculate "target" parameters (type, geometry, physical
* block size, data offset, etc);
* 2. Complete INFO by the found base disk and target parameters.
* 1. Find out a set of physical "base" disks participating in the
* logical DEVICE. For each found disk calculate "target" parameters
* (type, geometry, physical block size, data offset, etc) and store
* it in the array of "targets" of TD;
* 2. Complete INFO using the found base disks and calculated target
* parameters.
*
* TD: optionally contains target parameters specified by user via
* config file, or special "target options" of zipl tool.
* If target parameters were specified by user, then the step 1 above
* is skipped.
* To exclude any user assumptions about the DEVICE, this function
* should be called with TD pointing to a zeroed structure.
*
* DEVICE: logical, or physical device, optionally formated with a
* file system.
@@ -566,6 +697,7 @@ int disk_get_info(const char *device, struct job_target_data *td,
goto error;
if (disk_set_info_by_hint(td, data, fd))
goto error;
data->device = stats.st_rdev;
break;
case source_user:
/*
@@ -578,6 +710,12 @@ int disk_get_info(const char *device, struct job_target_data *td,
goto error;
if (disk_set_info_by_hint(td, data, fd))
goto error;
/*
* multiple base disks are not supported
* with this source type
*/
assert(td->nr_targets == 1);
data->device = data->basedisks[0];
break;
case source_auto:
/* no ready target parameters are available */
@@ -585,6 +723,12 @@ int disk_get_info(const char *device, struct job_target_data *td,
goto error;
if (disk_set_info_auto(data, &stats, fd))
goto error;
/*
* multiple base disks are not supported
* with this source type
*/
data->basedisks[0] = data->device;
td->nr_targets = 1;
break;
default:
assert(0);
@@ -617,45 +761,48 @@ disk_is_tape(const char* device)
return rc;
}
int
disk_is_scsi(const char* device, struct job_target_data* target)
{
struct disk_info* info;
int rc = 0;
if (disk_get_info(device, target, &info) == -1)
return 0;
if (info->type == disk_type_scsi)
rc = 1;
disk_free_info(info);
return rc;
}
int disk_is_eckd_ldl(const char *device, struct job_target_data *target)
/**
* Get "extended type" of base disk by logical DEVICE
*
* This function may fail for various reasons. E.g. in case when
* DEVICE is not eligible for boot record installation (not a
* partition, etc). In case of success the resulted disk type is
* stored in EXT_TYPE.
*/
int disk_get_ext_type(const char *device, struct disk_ext_type *ext_type)
{
struct job_target_data tmp = {.source = source_unknown};
struct disk_info *info;
int rc = 0;
if (disk_get_info(device, target, &info) == -1)
return 0;
if (info->type == disk_type_eckd_ldl)
rc = 1;
if (disk_get_info(device, &tmp, &info))
return -1;
ext_type->type = info->type;
ext_type->is_nvme = info->is_nvme;
disk_free_info(info);
return rc;
free_target_data(&tmp);
return 0;
}
int
disk_is_nvme(const char* device, struct job_target_data* target)
int disk_type_is_scsi(struct disk_ext_type *ext_type)
{
struct disk_info* info;
int rc = 0;
return ext_type->type == disk_type_scsi;
}
if (disk_get_info(device, target, &info) == -1)
return 0;
if (info->type == disk_type_scsi && info->is_nvme)
rc = 1;
disk_free_info(info);
return rc;
int disk_type_is_eckd_ldl(struct disk_ext_type *ext_type)
{
return ext_type->type == disk_type_eckd_ldl;
}
int disk_type_is_nvme(struct disk_ext_type *ext_type)
{
return ext_type->is_nvme;
}
int disk_type_is_eckd(disk_type_t type)
{
return (type == disk_type_eckd_ldl ||
type == disk_type_eckd_cdl);
}
int
@@ -940,6 +1087,33 @@ disk_print_devt(dev_t d)
printf("%02x:%02x", major(d), minor(d));
}
void disk_print_devname(dev_t dev)
{
struct util_proc_part_entry part_entry;
if (!util_proc_part_get_entry(dev, &part_entry)) {
printf("%s", part_entry.name);
util_proc_part_free_entry(&part_entry);
} else {
disk_print_devt(dev);
}
}
void prepare_footnote_ptr(int source, char *ptr)
{
if (source == source_user || source == source_script)
strcpy(ptr, " *)");
else
strcpy(ptr, "");
}
void print_footnote_ref(int source, const char *prefix)
{
if (source == source_user)
printf("%s*) Data provided by user.\n", prefix);
else if (source == source_script)
printf("%s*) Data provided by script.\n", prefix);
}
/* Return a name for a given disk TYPE. */
char *
@@ -991,12 +1165,11 @@ disk_is_large_volume(struct disk_info* info)
void disk_print_info(struct disk_info *info, int source)
{
char footnote[4] = "";
if (source == source_user || source == source_script)
strcpy(footnote, " *)");
prepare_footnote_ptr(source, footnote);
printf(" Device..........................: ");
disk_print_devt(info->device);
if (info->targetbase == defined_as_device)
if (info->targetbase_def == defined_as_device)
printf("%s", footnote);
printf("\n");
if (info->partnum != 0) {
@@ -1007,7 +1180,7 @@ void disk_print_info(struct disk_info *info, int source)
if (info->name) {
printf(" Device name.....................: %s",
info->name);
if (info->targetbase == defined_as_name)
if (info->targetbase_def == defined_as_name)
printf("%s", footnote);
printf("\n");
}
@@ -1027,7 +1200,7 @@ void disk_print_info(struct disk_info *info, int source)
(info->partnum != 0) ? "partition" : "device");
printf(" Disk layout.....................: %s%s\n",
disk_get_type_name(info->type), footnote);
if (disk_is_eckd(info->type)) {
if (disk_type_is_eckd(info->type)) {
printf(" Geometry - heads................: %d%s\n",
info->geo.heads, footnote);
printf(" Geometry - sectors..............: %d%s\n",
@@ -1050,21 +1223,7 @@ void disk_print_info(struct disk_info *info, int source)
info->phy_block_size, footnote);
printf(" Device size in physical blocks..: %ld\n",
(long) info->phy_blocks);
if (source == source_user)
printf(" *) Data provided by user.\n");
if (source == source_script)
printf(" *) Data provided by script.\n");
}
/* Print textual representation of geo structure. */
void
disk_print_geo(struct disk_info *data)
{
printf(" geo.heads.........:%u\n", data->geo.heads);
printf(" geo.sectors.......:%u\n", data->geo.sectors);
printf(" geo.cylinders.....:%u\n", data->geo.cylinders);
printf(" geo.start.........:%lu\n", data->geo.start);
printf(" blksize...........:%u\n", data->phy_block_size);
print_footnote_ref(source, " ");
}
/* Check whether a block is a zero block which identifies a hole in a file.

View File

@@ -434,11 +434,14 @@ int install_bootloader(struct job_data *job, struct install_set *bis)
{
disk_blockptr_t *scsi_dump_sb_blockptr = &bis->scsi_dump_sb_blockptr;
struct disk_info *info = bis->info;
char *device = bis->device;
int fd, rc;
char footnote[4];
int rc;
int i;
if (!info)
return 0;
prepare_footnote_ptr(job->target.source, footnote);
/* Inform user about what we're up to */
printf("Preparing boot device for %s%s: ",
disk_get_ipl_type(info->type,
@@ -455,40 +458,58 @@ int install_bootloader(struct job_data *job, struct install_set *bis)
disk_print_devt(info->device);
printf(".\n");
}
/* Open device file */
fd = open(device, O_RDWR);
if (fd == -1) {
error_reason(strerror(errno));
error_text("Could not open temporary device file '%s'",
device);
return -1;
}
/* Ensure that potential cache inconsistencies between disk and
* partition are resolved by flushing the corresponding buffers. */
if (!dry_run) {
if (ioctl(fd, BLKFLSBUF)) {
fprintf(stderr, "Warning: Could not flush disk "
"caches.\n");
}
}
/*
* Depending on disk type, install one or two program tables
* for CCW-type IPL and (or) for List-Directed IPL (see the
* picture in comments above)
*/
if (job->id == job_dump_partition) {
rc = install_bootloader_dump(bis->tables, info,
scsi_dump_sb_blockptr,
is_ngdump_enabled(job),
fd);
} else {
rc = install_bootloader_ipl(bis->tables, info, fd);
}
/* Install independently on each physical target base */
if (fsync(fd))
error_text("Could not sync device file '%s'", device);
if (close(fd))
error_text("Could not close device file '%s'", device);
for (i = 0; i < job_get_nr_targets(job); i++) {
int fd;
if (verbose) {
printf("Installing on base disk: ");
disk_print_devname(info->basedisks[i]);
printf("%s.\n", footnote);
}
/* Open device file */
fd = open(bis->basetmp[i], O_RDWR);
if (fd == -1) {
error_reason(strerror(errno));
error_text("Could not open temporary device file '%s'",
bis->basetmp[i]);
return -1;
}
/* Ensure that potential cache inconsistencies between disk and
* partition are resolved by flushing the corresponding buffers.
*/
if (!dry_run) {
if (ioctl(fd, BLKFLSBUF)) {
fprintf(stderr, "Warning: Could not flush disk "
"caches.\n");
}
}
/*
* Depending on disk type, install one or two program tables
* for CCW-type IPL and (or) for List-Directed IPL (see the
* picture in comments above)
*/
if (job->id == job_dump_partition) {
rc = install_bootloader_dump(bis->tables, info,
scsi_dump_sb_blockptr,
job_dump_is_ngdump(job),
fd);
} else {
rc = install_bootloader_ipl(bis->tables, info,
fd);
}
if (fsync(fd))
error_text("Could not sync device file '%s'",
bis->basetmp[i]);
if (close(fd))
error_text("Could not close device file '%s'",
bis->basetmp[i]);
if (rc)
break;
}
if (verbose)
print_footnote_ref(job->target.source, "");
if (!dry_run && rc == 0) {
if (info->devno >= 0)

View File

@@ -369,12 +369,13 @@ get_command_line(int argc, char* argv[], struct command_line* line)
return 0;
}
static void
free_target_data(struct job_target_data* data)
void free_target_data(struct job_target_data *data)
{
int i;
free(data->bootmap_dir);
free(data->targetbase);
for (i = 0; i < data->nr_targets; i++)
free(get_targetbase(data, i));
}
static void
@@ -1346,6 +1347,27 @@ type_from_target(char *target, disk_type_t *type)
}
}
int set_targettype(struct job_target_data *data, int index, char *value)
{
return type_from_target(value,
&target_at(data, index)->targettype);
}
int job_set_targettype(struct job_data *job, char *value)
{
return set_targettype(&job->target, 0, value);
}
static int job_set_target(struct job_data *job, char *value)
{
job_set_targetbase(job, value);
if (!job_get_targetbase(job))
return -1;
job_set_nr_targets(job, 1);
job->target.source = source_user;
return 0;
}
static int
get_job_from_section_data(char* data[], struct job_data* job, char* section)
{
@@ -1362,32 +1384,28 @@ get_job_from_section_data(char* data[], struct job_data* job, char* section)
return -1;
/* Fill in target */
if (data[(int) scan_keyword_targetbase] != NULL) {
job->target.targetbase =
misc_strdup(data[(int)
scan_keyword_targetbase]);
if (job->target.targetbase == NULL)
if (job_set_target(job, misc_strdup(data[(int)
scan_keyword_targetbase])))
return -1;
job->target.source = source_user;
}
if (data[(int) scan_keyword_targettype] != NULL) {
if (type_from_target(
data[(int) scan_keyword_targettype],
&job->target.targettype))
if (job_set_targettype(job,
data[(int) scan_keyword_targettype]))
return -1;
}
if (data[(int) scan_keyword_targetgeometry] != NULL) {
job->target.targetcylinders =
job_set_targetcylinders(job,
atoi(strtok(data[(int)
scan_keyword_targetgeometry], ","));
job->target.targetheads = atoi(strtok(NULL, ","));
job->target.targetsectors = atoi(strtok(NULL, ","));
scan_keyword_targetgeometry], ",")));
job_set_targetheads(job, atoi(strtok(NULL, ",")));
job_set_targetsectors(job, atoi(strtok(NULL, ",")));
}
if (data[(int) scan_keyword_targetblocksize] != NULL)
job->target.targetblocksize =
atoi(data[(int) scan_keyword_targetblocksize]);
job_set_targetblocksize(job,
atoi(data[(int) scan_keyword_targetblocksize]));
if (data[(int) scan_keyword_targetoffset] != NULL)
job->target.targetoffset =
atol(data[(int) scan_keyword_targetoffset]);
job_set_targetoffset(job,
atol(data[(int) scan_keyword_targetoffset]));
/* Fill in name and address of image file */
job->data.ipl.common.image = misc_strdup(
@@ -1615,37 +1633,32 @@ get_menu_job(struct scan_token* scan, char* menu, struct job_data* job)
return -1;
break;
case scan_keyword_targetbase:
job->target.targetbase = misc_strdup(
scan[i].content.keyword.value);
if (job->target.targetbase == NULL)
if (job_set_target(job, misc_strdup(
scan[i].content.keyword.value)))
return -1;
job->target.source = source_user;
break;
case scan_keyword_targettype:
if (type_from_target(
scan[i].content.keyword.value,
&job->target.targettype))
if (job_set_targettype(job,
scan[i].content.keyword.value))
return -1;
break;
case scan_keyword_targetgeometry:
job->target.targetcylinders =
job_set_targetcylinders(job,
atoi(strtok(
scan[i].content.keyword.value,
","));
job->target.targetheads =
atoi(strtok(NULL, ","));
job->target.targetsectors =
atoi(strtok(NULL, ","));
",")));
job_set_targetheads(job,
atoi(strtok(NULL, ",")));
job_set_targetsectors(job,
atoi(strtok(NULL, ",")));
break;
case scan_keyword_targetblocksize:
job->target.targetblocksize =
atoi(
scan[i].content.keyword.value);
job_set_targetblocksize(job, atoi(
scan[i].content.keyword.value));
break;
case scan_keyword_targetoffset:
job->target.targetoffset =
atol(
scan[i].content.keyword.value);
job_set_targetoffset(job, atol(
scan[i].content.keyword.value));
break;
default:
/* Should not happen */
@@ -2044,10 +2057,18 @@ job_get(int argc, char* argv[], struct job_data** data)
return rc;
}
bool
is_ngdump_enabled(struct job_data *job)
void job_dump_check_set_ngdump(struct job_data *job,
struct disk_ext_type *ext_type)
{
if (job->is_ldipl_dump)
return true;
return disk_is_nvme(job->data.dump.device, &job->target);
assert(job->id == job_dump_partition);
if (job->is_ldipl_dump || disk_type_is_nvme(ext_type))
job->data.dump.is_ngdump = true;
}
bool job_dump_is_ngdump(struct job_data *job)
{
assert(job->id == job_dump_partition);
return job->data.dump.is_ngdump;
}

View File

@@ -128,6 +128,7 @@ check_for_root(void)
int
main(int argc, char* argv[])
{
struct disk_ext_type ext_type = {0};
struct install_set bis;
struct job_data* job;
int rc;
@@ -173,9 +174,18 @@ main(int argc, char* argv[])
/* Do it */
switch (job->id) {
case job_dump_partition:
if (!is_ngdump_enabled(job) &&
rc = disk_get_ext_type(job->data.dump.device, &ext_type);
if (rc)
break;
job_dump_check_set_ngdump(job, &ext_type);
if (!job_dump_is_ngdump(job) &&
(disk_is_tape(job->data.dump.device) ||
!disk_is_scsi(job->data.dump.device, &job->target))) {
!disk_type_is_scsi(&ext_type))) {
if (dry_run) {
error_text("Option '--dry-run' is not implemented for this type of dump");
rc = -1;
break;
}
rc = install_dump(job->data.dump.device, &job->target,
job->data.dump.mem, job->data.dump.no_compress);
break;
@@ -187,8 +197,8 @@ main(int argc, char* argv[])
rc = -1;
break;
}
if (is_ngdump_enabled(job)) {
if (disk_is_eckd_ldl(job->data.dump.device, &job->target)) {
if (job_dump_is_ngdump(job)) {
if (disk_type_is_eckd_ldl(&ext_type)) {
error_reason("List-directed dump on ECKD with LDL not supported");
rc = -1;
break;

File diff suppressed because it is too large Load Diff