mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2db7ce05f | ||
|
|
0031c762cf | ||
|
|
7031429d1e | ||
|
|
73d8597db1 | ||
|
|
77d0d1d1a1 | ||
|
|
77a7e11a51 | ||
|
|
279070fa43 | ||
|
|
3989e1b741 | ||
|
|
c230b5385a | ||
|
|
68ecf79b01 | ||
|
|
f2960eb0df | ||
|
|
4d004cf8b1 | ||
|
|
d8f504f271 | ||
|
|
7fb3ee4478 | ||
|
|
a95bad6c87 | ||
|
|
147ff1bf49 | ||
|
|
c91d172c99 | ||
|
|
0e4992da3a | ||
|
|
41da0f0809 | ||
|
|
f43789e16a | ||
|
|
136e5eed8c | ||
|
|
e09753f2a3 | ||
|
|
fe2d240656 | ||
|
|
6fc450f696 | ||
|
|
6a55d0c2e5 | ||
|
|
0e69dc1262 | ||
|
|
3296d85e35 | ||
|
|
0eeab96f0d | ||
|
|
283a61dc2f | ||
|
|
670bf3e870 | ||
|
|
9cfaac58ea | ||
|
|
d6b702d579 | ||
|
|
d9a9bd8dcf | ||
|
|
cad4e9e15b | ||
|
|
c0f02d2f68 | ||
|
|
5218c243f4 | ||
|
|
c364692e7a | ||
|
|
41eb7d076f | ||
|
|
1062a7cde6 | ||
|
|
54e5e99657 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
22
CHANGELOG.md
22
CHANGELOG.md
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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) {
|
||||
/*
|
||||
|
||||
362
cpuplugd/cpu.c
362
cpuplugd/cpu.c
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -93,5 +93,6 @@ SECTIONS
|
||||
*(.eh_frame)
|
||||
*(.interp)
|
||||
*(.note.GNU-stack)
|
||||
*(.note.package)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,5 +79,6 @@ SECTIONS
|
||||
*(.eh_frame)
|
||||
*(.interp)
|
||||
*(.note.GNU-stack)
|
||||
*(.note.package)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,5 +15,6 @@ SECTIONS
|
||||
*(.eh_frame)
|
||||
*(.interp)
|
||||
*(.note.GNU-stack)
|
||||
*(.note.package)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
//
|
||||
// Copyright IBM Corp. 2023, 2024
|
||||
|
||||
#![deny(
|
||||
missing_docs,
|
||||
#![deny(missing_docs)]
|
||||
#![warn(
|
||||
missing_debug_implementations,
|
||||
trivial_numeric_casts,
|
||||
unstable_features,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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!!!!"),
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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\
|
||||
|
||||
@@ -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 *,
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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?
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -72,5 +72,6 @@ SECTIONS
|
||||
*(.eh_frame)
|
||||
*(.interp)
|
||||
*(.note.GNU-stack)
|
||||
*(.note.package)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,5 +14,6 @@ SECTIONS
|
||||
*(.eh_frame)
|
||||
*(.interp)
|
||||
*(.note.GNU-stack)
|
||||
*(.note.package)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,5 +14,6 @@ SECTIONS
|
||||
*(.eh_frame)
|
||||
*(.interp)
|
||||
*(.note.GNU-stack)
|
||||
*(.note.package)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,5 +14,6 @@ SECTIONS
|
||||
*(.eh_frame)
|
||||
*(.interp)
|
||||
*(.note.GNU-stack)
|
||||
*(.note.package)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,5 +93,6 @@ SECTIONS
|
||||
*(.eh_frame)
|
||||
*(.interp)
|
||||
*(.note.GNU-stack)
|
||||
*(.note.package)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,5 +74,6 @@ SECTIONS
|
||||
*(.eh_frame)
|
||||
*(.interp)
|
||||
*(.note.GNU-stack)
|
||||
*(.note.package)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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, ®,
|
||||
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);
|
||||
}
|
||||
|
||||
373
zipl/src/disk.c
373
zipl/src/disk.c
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
109
zipl/src/job.c
109
zipl/src/job.c
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user