diff --git a/s390-tools-sles15sp1-01-zdev-use-libutil-provided-path-functions.patch b/s390-tools-sles15sp1-01-zdev-use-libutil-provided-path-functions.patch new file mode 100644 index 0000000..580ca4b --- /dev/null +++ b/s390-tools-sles15sp1-01-zdev-use-libutil-provided-path-functions.patch @@ -0,0 +1,797 @@ +Subject: zdev: use libutil provided path functions +From: Peter Oberparleiter + +Summary: zdev: Add support for handling I/O configuration data +Description: LPARs that are running in IBM Dynamic Partition Manager (DPM) mode + can access a firmware-generated I/O configuration data file that + contains s390-specific information about available I/O devices + such as qeth device numbers and parameters, and FCP device IDs. + + This data file is intended to remove the need for users to + manually enter the corresponding device data during installation. + + Linux kernels with the corresponding support make the I/O + configuration data available at the following location: + + /sys/firmware/sclp_sd/config/data + + This patch set adds support for handling this data file using the + chzdev and lszdev tools: + + - I/O configuration data can be applied using chzdev's --import + option + - Initial RAM-Disk scripts automatically apply the + I/O configuration data to the system configuration + - lszdev can be used to display the applied auto-configuration + data + - chzdev can be used to manually override the + auto-configuration data + +Upstream-ID: d542138868153a36c3de6a21c3dea56125157e26 +Problem-ID: LS1604 + +Upstream-Description: + + zdev: use libutil provided path functions + + Closes: #20 + + Signed-off-by: Rafael Fonseca + Acked-by: Peter Oberparleiter + Signed-off-by: Michael Holzheu + + +Signed-off-by: Peter Oberparleiter +--- + zdev/include/misc.h | 4 -- + zdev/src/ccw.c | 20 ++++-------- + zdev/src/ccwgroup.c | 7 ++-- + zdev/src/chzdev.c | 3 + + zdev/src/ctc_auto.c | 6 ++- + zdev/src/device.c | 5 ++- + zdev/src/devnode.c | 8 +++- + zdev/src/generic_ccw.c | 4 +- + zdev/src/lcs_auto.c | 6 ++- + zdev/src/misc.c | 63 --------------------------------------- + zdev/src/modprobe.c | 8 +++- + zdev/src/module.c | 8 +++- + zdev/src/qeth_auto.c | 4 +- + zdev/src/root.c | 4 +- + zdev/src/scsi.c | 4 +- + zdev/src/select.c | 4 +- + zdev/src/udev.c | 4 +- + zdev/src/udev_ccw.c | 10 +++--- + zdev/src/udev_ccwgroup.c | 10 +++--- + zdev/src/udev_zfcp_lun.c | 8 +++- + zdev/src/zfcp_lun.c | 16 +++++---- + 21 files changed, 84 insertions(+), 122 deletions(-) + +--- a/zdev/include/misc.h ++++ b/zdev/include/misc.h +@@ -157,10 +157,6 @@ char *misc_asprintf(const char *, ...); + int misc_system(err_t, const char *, ...); + bool misc_read_dir(const char *, struct util_list *, + bool (*)(const char *, void *), void *); +-bool path_exists(const char *); +-bool file_exists(const char *); +-bool file_writable(const char *); +-bool dir_exists(const char *); + bool file_is_devnode(const char *); + exit_code_t remove_file(const char *); + char *misc_read_text_file(const char *, int, err_t); +--- a/zdev/src/ccw.c ++++ b/zdev/src/ccw.c +@@ -14,6 +14,7 @@ + #include + + #include "lib/util_base.h" ++#include "lib/util_path.h" + + #include "attrib.h" + #include "ccw.h" +@@ -1165,15 +1166,10 @@ out: + + static void read_grouped(struct ccw_devinfo *info, const char *path) + { +- char *file_path; +- +- file_path = misc_asprintf("%s/group_device", path); +- if (path_exists(file_path)) ++ if (util_path_exists("%s/group_device", path)) + info->grouped = 1; + else + info->grouped = 0; +- +- free(file_path); + } + + static void read_cutype(struct ccw_devinfo *info, const char *path) +@@ -1229,7 +1225,7 @@ static struct ccw_devinfo *ccw_devinfo_r + + id = ccw_devid_to_str(devid); + path = path_get_ccw_device(NULL, id); +- if (!dir_exists(path)) ++ if (!util_path_is_dir(path)) + goto out; + + info->exists = 1; +@@ -1354,7 +1350,7 @@ bool ccw_exists(const char *drv, const c + if (!path) + return false; + +- rc = dir_exists(path); ++ rc = util_path_is_dir(path); + free(path); + + return rc; +@@ -1428,7 +1424,7 @@ static exit_code_t ccw_st_read_active(st + state->definable = 0; + + path = path_get_ccw_device(drv, id); +- if (path_exists(path)) { ++ if (util_path_exists(path)) { + state->exists = 1; + device_read_active_settings(dev, scope); + } else +@@ -1641,7 +1637,7 @@ static void ccw_st_add_errors(struct sub + if (!path) + return; + +- if (!path_exists(path)) { ++ if (!util_path_exists(path)) { + strlist_add(errors, "CCW device %s does not exist", id); + goto out; + } +@@ -1662,9 +1658,7 @@ static void ccw_st_add_errors(struct sub + "paths", id); + goto out; + } +- free(apath); +- apath = misc_asprintf("%s/driver", path); +- if (!path_exists(apath)) { ++ if (!util_path_exists("%s/driver", path)) { + strlist_add(errors, "CCW device %s is not bound to a driver", + id); + goto out; +--- a/zdev/src/ccwgroup.c ++++ b/zdev/src/ccwgroup.c +@@ -12,6 +12,7 @@ + #include + + #include "lib/util_base.h" ++#include "lib/util_path.h" + + #include "attrib.h" + #include "ccw.h" +@@ -557,7 +558,7 @@ static bool read_full_id(struct ccwgroup + bool result = false; + + path = path_get_ccwgroup_device(drv, id); +- if (!dir_exists(path)) ++ if (!util_path_is_dir(path)) + goto out; + + memset(&devid, 0, sizeof(struct ccwgroup_devid)); +@@ -625,7 +626,7 @@ static void ccwgroup_add_ids(const char + path = path_get_ccwgroup_devices(drv); + if (mod) + module_try_load_once(mod, path); +- if (dir_exists(path)) ++ if (util_path_is_dir(path)) + path_for_each(path, get_ids_cb, &cb_data); + free(path); + } +@@ -663,7 +664,7 @@ static exit_code_t ccwgroup_st_read_acti + state->definable = 0; + + path = ccwgroup_get_dev_path_by_devid(drv, devid); +- if (path_exists(path)) { ++ if (util_path_exists(path)) { + state->exists = 1; + device_read_active_settings(dev, scope); + } else +--- a/zdev/src/chzdev.c ++++ b/zdev/src/chzdev.c +@@ -16,6 +16,7 @@ + #include + #include + ++#include "lib/util_path.h" + #include "lib/zt_common.h" + + #include "attrib.h" +@@ -2502,7 +2503,7 @@ static exit_code_t do_export(struct opti + info("Exporting configuration data to standard output\n"); + } else { + info("Exporting configuration data to %s\n", opts->export); +- if (!path_exists(opts->export)) { ++ if (!util_path_exists(opts->export)) { + rc = path_create(opts->export); + if (rc) + return rc; +--- a/zdev/src/ctc_auto.c ++++ b/zdev/src/ctc_auto.c +@@ -9,6 +9,8 @@ + + #include + ++#include "lib/util_path.h" ++ + #include "ccw.h" + #include "ccwgroup.h" + #include "ctc.h" +@@ -125,13 +127,13 @@ static struct util_list *read_sorted_ctc + /* Add CCW devices bound to the CTC CCW device driver. */ + module_try_load_once(CTC_MOD_NAME, NULL); + path = path_get_sys_bus_drv(CCW_BUS_NAME, CTC_CCWDRV_NAME); +- if (dir_exists(path)) ++ if (util_path_is_dir(path)) + path_for_each(path, add_cb, infos); + free(path); + + /* Add CCW devices bound to the LCS CCW device driver. */ + path = path_get_sys_bus_drv(CCW_BUS_NAME, LCS_CCWDRV_NAME); +- if (dir_exists(path)) ++ if (util_path_is_dir(path)) + path_for_each(path, add_cb, infos); + free(path); + +--- a/zdev/src/device.c ++++ b/zdev/src/device.c +@@ -11,6 +11,8 @@ + #include + #include + ++#include "lib/util_path.h" ++ + #include "attrib.h" + #include "device.h" + #include "devtype.h" +@@ -480,7 +482,8 @@ void device_read_active_settings(struct + a = attrib_find(st->dev_attribs, name); + s = setting_list_apply_actual(dev->active.settings, a, name, + value); +- if (link || (scope == scope_all && !file_writable(path))) ++ if (link || (scope == scope_all && ++ !util_path_is_writable(path))) + s->readonly = 1; + if (link) + free(link); +--- a/zdev/src/devnode.c ++++ b/zdev/src/devnode.c +@@ -15,6 +15,8 @@ + #include + #include + ++#include "lib/util_path.h" ++ + #include "devnode.h" + #include "misc.h" + #include "path.h" +@@ -230,7 +232,7 @@ static exit_code_t add_block_cb(const ch + + /* Add additional nodes. */ + cb_data->prefix = filename; +- if (dir_exists(path)) ++ if (util_path_is_dir(path)) + path_for_each(path, add_part_cb, cb_data); + + return EXIT_OK; +@@ -249,7 +251,7 @@ int devnode_add_block_from_sysfs(struct + cb_data.prefix = NULL; + + blkpath = misc_asprintf("%s/block", path); +- if (dir_exists(blkpath)) ++ if (util_path_is_dir(blkpath)) + path_for_each(blkpath, add_block_cb, &cb_data); + free(blkpath); + +@@ -283,7 +285,7 @@ int devnode_add_net_from_sysfs(struct ut + cb_data.prefix = NULL; + + netpath = misc_asprintf("%s/net", path); +- if (dir_exists(netpath)) ++ if (util_path_is_dir(netpath)) + path_for_each(netpath, add_net_cb, &cb_data); + free(netpath); + +--- a/zdev/src/generic_ccw.c ++++ b/zdev/src/generic_ccw.c +@@ -9,6 +9,8 @@ + + #include + ++#include "lib/util_path.h" ++ + #include "attrib.h" + #include "ccw.h" + #include "ccwgroup.h" +@@ -162,7 +164,7 @@ static void generic_ccw_st_add_devnodes( + cb_data.devnodes = devnodes; + cb_data.id = id; + path = path_get_sys_dev_char_devices(); +- if (dir_exists(path)) ++ if (util_path_is_dir(path)) + path_for_each(path, add_cb, &cb_data); + free(path); + } +--- a/zdev/src/lcs_auto.c ++++ b/zdev/src/lcs_auto.c +@@ -9,6 +9,8 @@ + + #include + ++#include "lib/util_path.h" ++ + #include "ccw.h" + #include "ccwgroup.h" + #include "ctc.h" +@@ -129,13 +131,13 @@ static struct util_list *read_sorted_lcs + /* Add CCW devices bound to the LCS CCW device driver. */ + module_try_load_once(LCS_MOD_NAME, NULL); + path = path_get_sys_bus_drv(CCW_BUS_NAME, LCS_CCWDRV_NAME); +- if (dir_exists(path)) ++ if (util_path_is_dir(path)) + path_for_each(path, add_cb, infos); + free(path); + + /* Add CCW devices bound to the CTC CCW device driver. */ + path = path_get_sys_bus_drv(CCW_BUS_NAME, CTC_CCWDRV_NAME); +- if (dir_exists(path)) ++ if (util_path_is_dir(path)) + path_for_each(path, add_cb, infos); + free(path); + +--- a/zdev/src/misc.c ++++ b/zdev/src/misc.c +@@ -889,69 +889,6 @@ void ptrlist_move(struct util_list *to, + util_list_add_tail(to, node); + } + +-/* Check if a path exists. */ +-bool path_exists(const char *path) +-{ +- struct stat s; +- +- debug("Checking if file exists: %s\n", path); +- if (stat(path, &s) != 0) +- return false; +- +- return true; +-} +- +-/* Check if a file exists. */ +-bool file_exists(const char *path) +-{ +- struct stat s; +- +- debug("Checking if regular file exists: %s\n", path); +- if (stat(path, &s) != 0) +- return false; +- +- if (!S_ISREG(s.st_mode)) +- return false; +- +- return true; +-} +- +-/* Check if a file is writable. */ +-bool file_writable(const char *path) +-{ +- struct stat s; +- +- debug("Checking if regular file is writable: %s\n", path); +- if (stat(path, &s) != 0) +- return false; +- +- if (!S_ISREG(s.st_mode)) +- return false; +- +- if (!(s.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH))) +- return false; +- +- return true; +-} +- +-/* Check if a directory exists. */ +-bool dir_exists(const char *path) +-{ +- bool result = false; +- struct stat s; +- +- debug("Checking if directory exists: %s\n", path); +- if (stat(path, &s) != 0) +- goto out; +- if (!S_ISDIR(s.st_mode)) +- goto out; +- result = true; +-out: +- debug("Result: %d\n", result); +- +- return result; +-} +- + /* Check if file is a block or character special file. */ + bool file_is_devnode(const char *path) + { +--- a/zdev/src/modprobe.c ++++ b/zdev/src/modprobe.c +@@ -14,6 +14,8 @@ + #include + #include + ++#include "lib/util_path.h" ++ + #include "attrib.h" + #include "misc.h" + #include "modprobe.h" +@@ -376,7 +378,7 @@ exit_code_t modprobe_read_settings(const + struct modprobe_file *mf; + exit_code_t rc; + +- if (!file_exists(path)) { ++ if (!util_path_is_reg_file(path)) { + *settings = NULL; + return EXIT_OK; + } +@@ -398,7 +400,7 @@ exit_code_t modprobe_write_settings(cons + exit_code_t rc; + unsigned long lines; + +- if (file_exists(path)) { ++ if (util_path_is_reg_file(path)) { + rc = modprobe_read(path, &mf); + if (rc) + return rc; +@@ -414,7 +416,7 @@ exit_code_t modprobe_write_settings(cons + lines = util_list_len(&mf->lines); + if (lines == 0 || (lines == 1 && find_chzdev_comment(mf))) { + /* Do not write empty files. */ +- if (file_exists(path)) ++ if (util_path_is_reg_file(path)) + rc = remove_file(path); + } else + rc = modprobe_write(mf); +--- a/zdev/src/module.c ++++ b/zdev/src/module.c +@@ -12,6 +12,8 @@ + #include + #include + ++#include "lib/util_path.h" ++ + #include "attrib.h" + #include "misc.h" + #include "module.h" +@@ -34,7 +36,7 @@ bool module_loaded(const char *mod) + char *path = path_get_sys_module(mod); + bool rc; + +- rc = dir_exists(path); ++ rc = util_path_is_dir(path); + free(path); + + return rc; +@@ -229,7 +231,7 @@ void module_try_load_once(const char *mo + } else + tried_loading = strlist_new(); + strlist_add(tried_loading, mod); +- if (path && path_exists(path)) ++ if (path && util_path_exists(path)) + return; + if (module_loaded(mod)) + return; +@@ -275,7 +277,7 @@ bool module_set_params(const char *mod, + return false; + } + path = path_get_sys_module_param(mod, s->name); +- result = file_writable(path); ++ result = util_path_is_writable(path); + free(path); + if (!result) { + /* Sysfs file is not writable. */ +--- a/zdev/src/qeth_auto.c ++++ b/zdev/src/qeth_auto.c +@@ -11,6 +11,8 @@ + #include + #include + ++#include "lib/util_path.h" ++ + #include "ccw.h" + #include "ccwgroup.h" + #include "device.h" +@@ -277,7 +279,7 @@ static struct util_list *read_sorted_qet + /* Get CHPID information for all devices bound to the QETH driver. */ + infos = ptrlist_new(); + path = path_get_sys_bus_drv(CCW_BUS_NAME, QETH_CCWDRV_NAME); +- if (dir_exists(path)) ++ if (util_path_is_dir(path)) + path_for_each(path, add_cb, infos); + free(path); + +--- a/zdev/src/root.c ++++ b/zdev/src/root.c +@@ -9,6 +9,8 @@ + + #include + ++#include "lib/util_path.h" ++ + #include "device.h" + #include "devtype.h" + #include "misc.h" +@@ -74,7 +76,7 @@ exit_code_t root_check(void) + "required.\n"); + + /* Check if script is available. */ +- if (!file_exists(PATH_ROOT_SCRIPT)) ++ if (!util_path_is_reg_file(PATH_ROOT_SCRIPT)) + goto out; + + /* Ask for confirmation. */ +--- a/zdev/src/scsi.c ++++ b/zdev/src/scsi.c +@@ -12,6 +12,8 @@ + #include + #include + ++#include "lib/util_path.h" ++ + #include "misc.h" + #include "path.h" + #include "scsi.h" +@@ -253,7 +255,7 @@ static struct util_list *read_scsi_zfcp_ + + list = ptrlist_new(); + path = path_get_sys_bus_dev("scsi", NULL); +- if (dir_exists(path)) ++ if (util_path_is_dir(path)) + path_for_each(path, add_ids_cb, list); + free(path); + +--- a/zdev/src/select.c ++++ b/zdev/src/select.c +@@ -10,6 +10,8 @@ + #include + #include + ++#include "lib/util_path.h" ++ + #include "blkinfo.h" + #include "ccw.h" + #include "device.h" +@@ -622,7 +624,7 @@ exit_code_t select_by_path(struct select + struct ptrlist_node *p; + exit_code_t rc; + +- if (!path_exists(path)) { ++ if (!util_path_exists(path)) { + err_t_print(err, "Path not found: %s\n", path); + return EXIT_DEVICE_NOT_FOUND; + } +--- a/zdev/src/udev.c ++++ b/zdev/src/udev.c +@@ -13,6 +13,8 @@ + #include + #include + ++#include "lib/util_path.h" ++ + #include "attrib.h" + #include "ccw.h" + #include "device.h" +@@ -391,7 +393,7 @@ exit_code_t udev_remove_rule(const char + exit_code_t rc = EXIT_OK; + + path = path_get_udev_rule(type, id); +- if (file_exists(path)) ++ if (util_path_is_reg_file(path)) + rc = remove_file(path); + free(path); + +--- a/zdev/src/udev_ccw.c ++++ b/zdev/src/udev_ccw.c +@@ -13,6 +13,8 @@ + #include + #include + ++#include "lib/util_path.h" ++ + #include "attrib.h" + #include "ccw.h" + #include "device.h" +@@ -33,7 +35,7 @@ bool udev_ccw_exists(const char *type, c + return false; + + path = path_get_udev_rule(type, normid); +- rc = file_exists(path); ++ rc = util_path_is_reg_file(path); + free(path); + free(normid); + +@@ -150,7 +152,7 @@ exit_code_t udev_ccw_write_device(struct + + path = path_get_udev_rule(type, id); + debug("Writing %s udev rule file %s\n", type, path); +- if (!path_exists(path)) { ++ if (!util_path_exists(path)) { + rc = path_create(path); + if (rc) + goto out; +@@ -243,7 +245,7 @@ exit_code_t udev_ccw_write_cio_ignore(co + + if (!*id_list) { + /* Empty id_list string - remove file. */ +- if (!file_exists(path)) { ++ if (!util_path_is_reg_file(path)) { + /* Already removed. */ + goto out; + } +@@ -256,7 +258,7 @@ exit_code_t udev_ccw_write_cio_ignore(co + goto out; + + debug("Writing cio-ignore udev rule file %s\n", path); +- if (!path_exists(path)) { ++ if (!util_path_exists(path)) { + rc = path_create(path); + if (rc) + goto out; +--- a/zdev/src/udev_ccwgroup.c ++++ b/zdev/src/udev_ccwgroup.c +@@ -13,6 +13,8 @@ + #include + #include + ++#include "lib/util_path.h" ++ + #include "attrib.h" + #include "ccwgroup.h" + #include "device.h" +@@ -53,7 +55,7 @@ bool udev_ccwgroup_exists(const char *ty + path = get_rule_path(type, id); + if (!path) + return false; +- rc = file_exists(path); ++ rc = util_path_is_reg_file(path); + free(path); + + return rc; +@@ -215,7 +217,7 @@ exit_code_t udev_ccwgroup_write_device(s + list = setting_list_get_sorted(dev->persistent.settings); + + debug("Writing %s udev rule file %s\n", type, path); +- if (!path_exists(path)) { ++ if (!util_path_exists(path)) { + rc = path_create(path); + if (rc) + goto out; +@@ -364,7 +366,7 @@ void udev_ccwgroup_add_device_ids(const + cb_data.prefix = misc_asprintf("%s-%s-", UDEV_PREFIX, type); + cb_data.ids = list; + +- if (dir_exists(path)) ++ if (util_path_is_dir(path)) + path_for_each(path, get_ids_cb, &cb_data); + + free(cb_data.prefix); +@@ -382,7 +384,7 @@ exit_code_t udev_ccwgroup_remove_rule(co + return EXIT_INVALID_ID; + + path = path_get_udev_rule(type, partial_id); +- if (file_exists(path)) ++ if (util_path_is_reg_file(path)) + rc = remove_file(path); + free(path); + free(partial_id); +--- a/zdev/src/udev_zfcp_lun.c ++++ b/zdev/src/udev_zfcp_lun.c +@@ -14,6 +14,8 @@ + #include + #include + ++#include "lib/util_path.h" ++ + #include "attrib.h" + #include "device.h" + #include "misc.h" +@@ -376,7 +378,7 @@ void udev_zfcp_lun_add_device_ids(struct + cb_data.list = list; + path = path_get_udev_rules(); + +- if (dir_exists(path)) ++ if (util_path_is_dir(path)) + path_for_each(path, lun_cb, &cb_data); + + free(path); +@@ -497,7 +499,7 @@ static exit_code_t write_luns_rule(const + return EXIT_INTERNAL_ERROR; + hba_id = ccw_devid_to_str(&node->id.fcp_dev); + debug("Writing FCP LUN udev rule file %s\n", path); +- if (!path_exists(path)) { ++ if (!util_path_exists(path)) { + rc = path_create(path); + if (rc) + goto out; +@@ -614,7 +616,7 @@ static exit_code_t update_lun_rule(const + + /* Get previous rule data. */ + luns = zfcp_lun_node_list_new(); +- exists = file_exists(path); ++ exists = util_path_is_reg_file(path); + if (exists) + udev_read_zfcp_lun_rule(path, luns); + +--- a/zdev/src/zfcp_lun.c ++++ b/zdev/src/zfcp_lun.c +@@ -13,6 +13,8 @@ + #include + #include + ++#include "lib/util_path.h" ++ + #include "attrib.h" + #include "ccw.h" + #include "device.h" +@@ -430,7 +432,7 @@ static exit_code_t zfcp_lun_st_read_acti + + /* Check for FC unit. */ + fc_path = path_get_zfcp_lun_dev(dev->devid); +- fc_exists = path_exists(fc_path); ++ fc_exists = util_path_exists(fc_path); + free(fc_path); + + /* Check for SCSI device. */ +@@ -478,7 +480,7 @@ static exit_code_t zfcp_lun_add(struct d + /* Check if LUN already exists. */ + fcp_dev_id = ccw_devid_to_str(&devid->fcp_dev); + lunpath = path_get_zfcp_lun_dev(devid); +- if (dir_exists(lunpath)) { ++ if (util_path_is_dir(lunpath)) { + hctl = scsi_hctl_from_zfcp_lun_devid(devid); + if (!hctl) + goto check_failed; +@@ -486,7 +488,7 @@ static exit_code_t zfcp_lun_add(struct d + } + + portpath = path_get_zfcp_port_dev(devid); +- if (!dir_exists(portpath)) { ++ if (!util_path_is_dir(portpath)) { + delayed_err("Target port not found\n"); + rc = EXIT_ZFCP_WWPN_NOT_FOUND; + goto out; +@@ -681,14 +683,14 @@ static exit_code_t zfcp_lun_st_device_un + + remove_lun: + path = path_get_zfcp_lun_dev(devid); +- if (!path_exists(path)) ++ if (!util_path_exists(path)) + goto out; + free(path); + path = NULL; + + /* Remove FCP LUN. */ + devpath = path_get_zfcp_port_dev(devid); +- if (!dir_exists(devpath)) { ++ if (!util_path_is_dir(devpath)) { + rc = EXIT_ZFCP_WWPN_NOT_FOUND; + goto out; + } +@@ -779,7 +781,7 @@ static bool zfcp_lun_fc_lun_exists(const + if (zfcp_lun_parse_devid(&devid, id, err_ignore) != EXIT_OK) + return false; + path = path_get_zfcp_lun_dev(&devid); +- result = path_exists(path); ++ result = util_path_exists(path); + free(path); + + return result; +@@ -910,7 +912,7 @@ static void add_sg_from_sysfs(struct uti + char *sgpath; + + sgpath = misc_asprintf("%s/scsi_generic", path); +- if (dir_exists(sgpath)) ++ if (util_path_is_dir(sgpath)) + path_for_each(sgpath, add_sg_cb, list); + free(sgpath); + } diff --git a/s390-tools-sles15sp1-02-zdev-Prepare-for-firmware-configuration-file-support.patch b/s390-tools-sles15sp1-02-zdev-Prepare-for-firmware-configuration-file-support.patch new file mode 100644 index 0000000..6c528d1 --- /dev/null +++ b/s390-tools-sles15sp1-02-zdev-Prepare-for-firmware-configuration-file-support.patch @@ -0,0 +1,287 @@ +Subject: zdev: Prepare for firmware configuration file support +From: Peter Oberparleiter + +Summary: zdev: Add support for handling I/O configuration data +Description: LPARs that are running in IBM Dynamic Partition Manager (DPM) mode + can access a firmware-generated I/O configuration data file that + contains s390-specific information about available I/O devices + such as qeth device numbers and parameters, and FCP device IDs. + + This data file is intended to remove the need for users to + manually enter the corresponding device data during installation. + + Linux kernels with the corresponding support make the I/O + configuration data available at the following location: + + /sys/firmware/sclp_sd/config/data + + This patch set adds support for handling this data file using the + chzdev and lszdev tools: + + - I/O configuration data can be applied using chzdev's --import + option + - Initial RAM-Disk scripts automatically apply the + I/O configuration data to the system configuration + - lszdev can be used to display the applied auto-configuration + data + - chzdev can be used to manually override the + auto-configuration data + +Upstream-ID: ab4445c261749caa7aee2154e3b26c767b6c5e60 +Problem-ID: LS1604 + +Upstream-Description: + + zdev: Prepare for firmware configuration file support + + Apply some changes to existing functions and data structures to simplify + the firmware configuration file support implementation. + + - Make qeth and dasd subtype objects non-static + - Change the existing helper functions for reading file contents into + memory to also support binary functions + - Move some configuration file import functions to make them available + for use in other source files + + Signed-off-by: Peter Oberparleiter + Signed-off-by: Jan Höppner + + +Signed-off-by: Peter Oberparleiter +--- + zdev/include/dasd.h | 3 ++ + zdev/include/device.h | 2 + + zdev/include/export.h | 1 + zdev/include/misc.h | 1 + zdev/include/qeth.h | 2 + + zdev/src/dasd.c | 4 +-- + zdev/src/device.c | 13 +++++++++++ + zdev/src/export.c | 17 +------------- + zdev/src/misc.c | 48 ++++++++++++++++++++++++++++++------------ + zdev/src/qeth.c | 2 - + 10 files changed, 62 insertions(+), 31 deletions(-) + +--- a/zdev/include/dasd.h ++++ b/zdev/include/dasd.h +@@ -11,7 +11,10 @@ + #define DASD_H + + struct devtype; ++struct subtype; + + extern struct devtype dasd_devtype; ++extern struct subtype dasd_subtype_eckd; ++extern struct subtype dasd_subtype_fba; + + #endif /* DASD_H */ +--- a/zdev/include/device.h ++++ b/zdev/include/device.h +@@ -94,5 +94,7 @@ void device_list_add(struct device_list + struct device *device_list_find(struct device_list *, const char *, + struct device *); + void device_list_print(struct device_list *, int); ++struct setting_list *device_get_setting_list(struct device *dev, ++ config_t config); + + #endif /* DEVICE_H */ +--- a/zdev/include/export.h ++++ b/zdev/include/export.h +@@ -31,6 +31,7 @@ struct export_object { + } ptr; + }; + ++struct export_object *object_new(export_t type, void *ptr); + exit_code_t export_write_device(FILE *, struct device *, config_t, int *); + exit_code_t export_write_devtype(FILE *, struct devtype *, config_t, int *); + exit_code_t export_read(FILE *, const char *, struct util_list *); +--- a/zdev/include/misc.h ++++ b/zdev/include/misc.h +@@ -159,6 +159,7 @@ bool misc_read_dir(const char *, struct + bool (*)(const char *, void *), void *); + bool file_is_devnode(const char *); + exit_code_t remove_file(const char *); ++exit_code_t misc_read_fd(FILE *fd, void **buffer, size_t *size_ptr); + char *misc_read_text_file(const char *, int, err_t); + char *misc_read_cmd_output(const char *, int, err_t); + char *config_read_cmd_output(const char *, int, err_t); +--- a/zdev/include/qeth.h ++++ b/zdev/include/qeth.h +@@ -17,9 +17,11 @@ + #define QETH_NUM_DEVS 3 + + struct devtype; ++struct subtype; + struct namespace; + + extern struct devtype qeth_devtype; ++extern struct subtype qeth_subtype_qeth; + extern struct namespace qeth_namespace; + + #endif /* QETH_H */ +--- a/zdev/src/dasd.c ++++ b/zdev/src/dasd.c +@@ -589,7 +589,7 @@ static struct ccw_subtype_data dasd_eckd + .mod = "dasd_eckd_mod", + }; + +-static struct subtype dasd_subtype_eckd = { ++struct subtype dasd_subtype_eckd = { + .super = &ccw_subtype, + .devtype = &dasd_devtype, + .name = "dasd-eckd", +@@ -626,7 +626,7 @@ static struct ccw_subtype_data dasd_fba_ + .mod = "dasd_fba_mod", + }; + +-static struct subtype dasd_subtype_fba = { ++struct subtype dasd_subtype_fba = { + .super = &ccw_subtype, + .devtype = &dasd_devtype, + .name = "dasd-fba", +--- a/zdev/src/device.c ++++ b/zdev/src/device.c +@@ -570,3 +570,16 @@ exit_code_t device_check_settings(struct + + return EXIT_OK; + } ++ ++struct setting_list *device_get_setting_list(struct device *dev, ++ config_t config) ++{ ++ struct setting_list *settings = NULL; ++ ++ if (config == config_active) ++ settings = dev->active.settings; ++ else ++ settings = dev->persistent.settings; ++ ++ return settings; ++} +--- a/zdev/src/export.c ++++ b/zdev/src/export.c +@@ -282,19 +282,6 @@ static bool parse_setting(const char *li + return true; + } + +-static struct setting_list *dev_get_setting_list(struct device *dev, +- config_t config) +-{ +- struct setting_list *settings = NULL; +- +- if (config == config_active) +- settings = dev->active.settings; +- else +- settings = dev->persistent.settings; +- +- return settings; +-} +- + static struct setting_list *dt_get_setting_list(struct devtype *dt, + config_t config) + { +@@ -426,7 +413,7 @@ static exit_code_t handle_setting(const + } else if (dev) { + /* We're inside a device section. */ + attribs = dev->subtype->dev_attribs; +- list = dev_get_setting_list(dev, config); ++ list = device_get_setting_list(dev, config); + } else + return EXIT_OK; + +@@ -444,7 +431,7 @@ static exit_code_t handle_setting(const + return EXIT_OK; + } + +-static struct export_object *object_new(export_t type, void *ptr) ++struct export_object *object_new(export_t type, void *ptr) + { + struct export_object *obj; + +--- a/zdev/src/misc.c ++++ b/zdev/src/misc.c +@@ -98,26 +98,47 @@ static void dryrun_end_data(void) + + #define READ_CHUNK_SIZE 4096 + +-/* Read text from @fd and return resulting NULL-terminated text buffer. +- * If @chomp is non-zero, remove trailing newline character. Return %NULL +- * on error or when unprintable characters are read. */ +-static char *read_fd(FILE *fd, int chomp) ++/* Read all data from @fd and return address of resulting buffer in ++ * @buffer_ptr. If @size_ptr is non-zero, use it to store the size of the ++ * resulting buffer. Return %EXIT_OK on success. */ ++exit_code_t misc_read_fd(FILE *fd, void **buffer_ptr, size_t *size_ptr) + { + char *buffer = NULL; +- size_t done, i; ++ size_t done = 0; + +- done = 0; + while (!feof(fd)) { +- buffer = realloc(buffer, done + READ_CHUNK_SIZE + 1); ++ buffer = realloc(buffer, done + READ_CHUNK_SIZE); + if (!buffer) + oom(); + done += fread(&buffer[done], 1, READ_CHUNK_SIZE, fd); + if (ferror(fd)) { + free(buffer); +- return NULL; ++ return EXIT_RUNTIME_ERROR; + } + } + ++ buffer = realloc(buffer, done); ++ if (!buffer && done > 0) ++ oom(); ++ ++ *buffer_ptr = buffer; ++ if (size_ptr) ++ *size_ptr = done; ++ ++ return EXIT_OK; ++} ++ ++/* Read text from @fd and return resulting NULL-terminated text buffer. ++ * If @chomp is non-zero, remove trailing newline character. Return %NULL ++ * on error or when unprintable characters are read. */ ++static char *read_fd(FILE *fd, int chomp) ++{ ++ char *buffer; ++ size_t done, i; ++ ++ if (misc_read_fd(fd, (void **) &buffer, &done)) ++ return NULL; ++ + /* Check if this is a text file at all (required to filter out + * binary sysfs attributes). */ + for (i = 0; i < done; i++) { +@@ -131,12 +152,13 @@ static char *read_fd(FILE *fd, int chomp + if (chomp && done > 0 && buffer[done - 1] == '\n') + done--; + +- if (buffer) { +- /* NULL-terminate. */ +- buffer[done++] = 0; +- } ++ /* NULL-terminate. */ ++ buffer = realloc(buffer, done + 1); ++ if (!buffer) ++ oom(); ++ buffer[done] = 0; + +- return realloc(buffer, done); ++ return buffer; + } + + static int count_newline(const char *str) +--- a/zdev/src/qeth.c ++++ b/zdev/src/qeth.c +@@ -1369,7 +1369,7 @@ static struct ccwgroup_subtype_data qeth + .num_devs = QETH_NUM_DEVS, + }; + +-static struct subtype qeth_subtype_qeth = { ++struct subtype qeth_subtype_qeth = { + .super = &ccwgroup_subtype, + .devtype = &qeth_devtype, + .name = "qeth", diff --git a/s390-tools-sles15sp1-03-zdev-Add-support-for-reading-firmware-configuration-.patch b/s390-tools-sles15sp1-03-zdev-Add-support-for-reading-firmware-configuration-.patch new file mode 100644 index 0000000..a20a3fa --- /dev/null +++ b/s390-tools-sles15sp1-03-zdev-Add-support-for-reading-firmware-configuration-.patch @@ -0,0 +1,874 @@ +Subject: zdev: Add support for reading firmware configuration files +From: Peter Oberparleiter + +Summary: zdev: Add support for handling I/O configuration data +Description: LPARs that are running in IBM Dynamic Partition Manager (DPM) mode + can access a firmware-generated I/O configuration data file that + contains s390-specific information about available I/O devices + such as qeth device numbers and parameters, and FCP device IDs. + + This data file is intended to remove the need for users to + manually enter the corresponding device data during installation. + + Linux kernels with the corresponding support make the I/O + configuration data available at the following location: + + /sys/firmware/sclp_sd/config/data + + This patch set adds support for handling this data file using the + chzdev and lszdev tools: + + - I/O configuration data can be applied using chzdev's --import + option + - Initial RAM-Disk scripts automatically apply the + I/O configuration data to the system configuration + - lszdev can be used to display the applied auto-configuration + data + - chzdev can be used to manually override the + auto-configuration data + +Upstream-ID: 7d355b0fec964ad84ecaf88eb946121d39486070 +Problem-ID: LS1604 + +Upstream-Description: + + zdev: Add support for reading firmware configuration files + + Add support for reading firmware-provided I/O configuration data files. + Such configuration files are generated by the Dynamic Partition Manager + and made available via a kernel interface for consumption by Linux. + + To read a firmware configuration file, use the existing --import option: + + # chzdev --import /sys/firmware/sclp_sd/config/data + + This will apply all I/O configuration data found in the specified file + to the persistent configuration. + + Signed-off-by: Peter Oberparleiter + Signed-off-by: Jan Höppner + + +Signed-off-by: Peter Oberparleiter +--- + zdev/include/firmware.h | 25 + + zdev/man/chzdev.8 | 16 + zdev/src/Makefile | 2 + zdev/src/chzdev.c | 21 - + zdev/src/firmware.c | 676 ++++++++++++++++++++++++++++++++++++++++ + 5 files changed, 730 insertions(+), 10 deletions(-) + +--- /dev/null ++++ b/zdev/include/firmware.h +@@ -0,0 +1,25 @@ ++/* ++ * zdev - Modify and display the persistent configuration of devices ++ * ++ * Copyright IBM Corp. 2017 ++ * ++ * s390-tools is free software; you can redistribute it and/or modify ++ * it under the terms of the MIT license. See LICENSE for details. ++ */ ++ ++#ifndef FIRMWARE_H ++#define FIRMWARE_H ++ ++#include ++#include ++ ++#include "exit_code.h" ++#include "misc.h" ++ ++struct util_list; ++ ++bool firmware_detect(FILE *fd); ++exit_code_t firmware_read(FILE *fd, const char *filename, long skip, ++ config_t config, struct util_list *objects); ++ ++#endif /* FIRMWARE_H */ +--- a/zdev/man/chzdev.8 ++++ b/zdev/man/chzdev.8 +@@ -409,13 +409,23 @@ default value. + .PP + . + .OD import "" "FILENAME" "|-" +-Import configuration data from a text file. ++Import configuration data from a text or machine-provided file. + + Reads configuration data from FILENAME and applies it. If a single hyphen ("-") + is specified as FILENAME data is read from the standard input stream. The +-input format of the data read must be the same format as produced by the +-chzdev \-\-export action. ++input format must be either in the format as produced by the chzdev \-\-export ++action, or in the format of a machine-provided I/O configuration data file. + ++.B Machine-provided data: ++Some machine models provide I/O configuration data which is made available ++by the Linux kernel via a sysfs interface. While this data is intended for ++automatic consumption during the boot phase, you can also apply it manually ++using the \-\-import action like in the following example ++ ++.B Example: ++.CL chzdev --import /sys/firmware/sclp_sd/config/data ++ ++.B Note: + By default all configuration data that is read is also applied. To reduce the + scope of imported configuration data, you can select specific devices, a device + type, or define whether only data for the active or persistent configuration +--- a/zdev/src/Makefile ++++ b/zdev/src/Makefile +@@ -8,7 +8,7 @@ ALL_CPPFLAGS += -I ../include -std=gnu99 + chzdev_objects += attrib.o chzdev.o device.o devnode.o devtype.o exit_code.o \ + export.o hash.o inuse.o misc.o namespace.o opts.o path.o \ + root.o select.o setting.o subtype.o table.o table_attribs.o \ +- table_types.o net.o ++ table_types.o net.o firmware.o + + # Devtype Helpers + chzdev_objects += blkinfo.o ccw.o ccwgroup.o findmnt.o modprobe.o module.o \ +--- a/zdev/src/chzdev.c ++++ b/zdev/src/chzdev.c +@@ -27,6 +27,7 @@ + #include "devnode.h" + #include "devtype.h" + #include "export.h" ++#include "firmware.h" + #include "inuse.h" + #include "misc.h" + #include "module.h" +@@ -2500,9 +2501,9 @@ static exit_code_t do_export(struct opti + /* Open output stream. */ + if (strcmp(opts->export, "-") == 0) { + fd = stdout; +- info("Exporting configuration data to standard output\n"); ++ info("Exporting data to standard output\n"); + } else { +- info("Exporting configuration data to %s\n", opts->export); ++ info("Exporting data to %s\n", opts->export); + if (!util_path_exists(opts->export)) { + rc = path_create(opts->export); + if (rc) +@@ -2735,6 +2736,7 @@ static exit_code_t do_import(struct opti + exit_code_t drc = EXIT_OK; + const char *filename; + int found; ++ bool is_firmware; + + /* Open input stream. */ + if (strcmp(opts->import, "-") == 0) { +@@ -2744,16 +2746,23 @@ static exit_code_t do_import(struct opti + fd = fopen(opts->import, "r"); + filename = opts->import; + } +- info("Importing configuration data from %s\n", filename); ++ + if (!fd) { + error("Could not open file %s: %s\n", opts->import, + strerror(errno)); + return EXIT_RUNTIME_ERROR; + } + ++ is_firmware = firmware_detect(fd); ++ info("Importing data from %s%s\n", filename, ++ is_firmware ? " (firmware format)" : ""); ++ + /* Read data. */ + objects = ptrlist_new(); +- rc = export_read(fd, filename, objects); ++ if (is_firmware) ++ rc = firmware_read(fd, filename, -1, opts->config, objects); ++ else ++ rc = export_read(fd, filename, objects); + if (rc) + goto out; + +@@ -2766,8 +2775,8 @@ static exit_code_t do_import(struct opti + "selection\n", filename); + rc = EXIT_EMPTY_SELECTION; + } else { +- error("%s: No settings found to import\n", filename); +- rc = EXIT_NO_DATA; ++ info("%s: No settings found to import\n", filename); ++ rc = EXIT_OK; + } + goto out; + } +--- /dev/null ++++ b/zdev/src/firmware.c +@@ -0,0 +1,676 @@ ++/* ++ * zdev - Modify and display the persistent configuration of devices ++ * ++ * Copyright IBM Corp. 2017 ++ * ++ * s390-tools is free software; you can redistribute it and/or modify ++ * it under the terms of the MIT license. See LICENSE for details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "attrib.h" ++#include "ccw.h" ++#include "ccwgroup.h" ++#include "dasd.h" ++#include "device.h" ++#include "export.h" ++#include "firmware.h" ++#include "misc.h" ++#include "qeth.h" ++#include "subtype.h" ++#include "zfcp_host.h" ++#include "zfcp_lun.h" ++ ++/* In-memory firmware file representation. */ ++struct fw_file { ++ const char *name; ++ char *buffer; ++ size_t size; ++ char *last_access; ++ size_t last_size; ++}; ++ ++/* Record access to fields of the buffered file for use in warning messages. */ ++#define fwacc(f, x) ((f)->last_access = (char *) &(x), \ ++ (f)->last_size = sizeof(x), x) ++ ++/* ++ * Firmware file format definitions. ++ */ ++ ++/* Firmware file header. */ ++struct fw_filehdr { ++ uint32_t magic; ++ uint16_t ver; ++ uint16_t hdr_len; ++ uint32_t file_len; ++ uint32_t seq; ++ uint32_t zeroes; ++ uint16_t de_count; ++ char unused[10]; ++} __packed; ++ ++#define FW_HDR_MAGIC 0x7a646576 /* ASCII "zdev" */ ++#define FW_HDR_VER_Z14 0x0000 ++ ++/* I/O device ID. */ ++struct fw_iodevid { ++ uint8_t cssid; ++ uint8_t ssid; ++ uint16_t devno; ++} __packed; ++ ++#define FW_IODEVID_FLAG_MCSS 0x01 ++ ++/* Device setting. */ ++struct fw_setting { ++ uint16_t len; ++ uint8_t key_type; ++ uint8_t key_len; ++ uint8_t val_type; ++ uint8_t val_len; ++ char data[]; ++} __packed; ++ ++#define FW_SETTING_KEYTYPE_ASCII 0x00 ++#define FW_SETTING_VALTYPE_ASCII 0x00 ++#define FW_SETTING_VALTYPE_UINT 0x01 ++ ++/* Device settings list. */ ++struct fw_setlist { ++ uint16_t len; ++ char data[]; ++} __packed; ++ ++/* Device entry header. */ ++struct fw_dehdr { ++ uint16_t type; ++ uint16_t len; ++ uint32_t seq; ++} __packed; ++ ++#define FW_DE_HDR_TYPE_DASD 0x0001 ++#define FW_DE_HDR_TYPE_ZFCP_HOST 0x0002 ++#define FW_DE_HDR_TYPE_ZFCP_LUN 0x0003 ++#define FW_DE_HDR_TYPE_QETH 0x0004 ++ ++/* DASD device entry. */ ++struct fw_dasd { ++ struct fw_dehdr hdr; ++ uint8_t id_flags; ++ struct fw_iodevid id; ++ char settings[]; ++} __packed; ++ ++/* zFCP host device entry. */ ++struct fw_zfcp_host { ++ struct fw_dehdr hdr; ++ uint8_t id_flags; ++ struct fw_iodevid id; ++ char settings[]; ++} __packed; ++ ++/* zFCP LUN device entry. */ ++struct fw_zfcp_lun { ++ struct fw_dehdr hdr; ++ uint8_t id_flags; ++ struct fw_iodevid id; ++ uint64_t wwpn; ++ uint64_t fcp_lun; ++ char settings[]; ++} __packed; ++ ++/* QETH device entry. */ ++struct fw_qeth { ++ struct fw_dehdr hdr; ++ uint8_t id_flags; ++ struct fw_iodevid read_id; ++ struct fw_iodevid write_id; ++ struct fw_iodevid data_id; ++ char settings[]; ++} __packed; ++ ++/* Emit a warning that refers to a position in a firmware file. */ ++static void fwwarn(struct fw_file *f, const char *fmt, ...) ++{ ++ va_list args; ++ off_t start = (off_t) (f->last_access - f->buffer), ++ end = start + f->last_size - 1; ++ ++ fprintf(stderr, "%s: ", f->name); ++ if (start == end) ++ fprintf(stderr, "Byte 0x%zx: ", start); ++ else ++ fprintf(stderr, "Bytes 0x%zx-0x%zx: ", start, end); ++ ++ va_start(args, fmt); ++ vfprintf(stderr, fmt, args); ++ va_end(args); ++ fprintf(stderr, "\n"); ++} ++ ++/* Basic file format header sanity check. */ ++static bool check_header(struct fw_file *f, struct fw_filehdr *hdr) ++{ ++ if (fwacc(f, hdr->magic) != FW_HDR_MAGIC) ++ fwwarn(f, "Invalid file magic (0x%08x)", hdr->magic); ++ else if (fwacc(f, hdr->ver) != FW_HDR_VER_Z14) ++ fwwarn(f, "Unsupported file version (0x%04x)", hdr->ver); ++ else ++ return true; ++ ++ return false; ++} ++ ++#define READ_RETRY 3 ++ ++/* Read a firmware configuration file. */ ++static exit_code_t read_fw(struct fw_file *file, FILE *fd, const char *filename) ++{ ++ struct fw_file f = { NULL }; ++ struct fw_filehdr *hdr; ++ char *buffer, *buffer2; ++ size_t size, size2; ++ int retry; ++ exit_code_t rc = EXIT_OK; ++ ++ for (retry = 0; retry < READ_RETRY; retry++) { ++ /* Read complete file once */ ++ rc = misc_read_fd(fd, (void **) &buffer, &size); ++ if (rc) { ++ warn("%s: Could not read file", filename); ++ return rc; ++ } ++ if (!buffer) { ++ /* Empty file - skip silently as this is the default ++ * on machines without firmware support. */ ++ return rc; ++ } ++ ++ /* Re-read complete file to detect in-flight modifications. */ ++ if (fseek(fd, 0, SEEK_SET) == -1) { ++ /* Could be a pipe, socket, or FIFO - accept v1. */ ++ break; ++ } ++ rc = misc_read_fd(fd, (void **) &buffer2, &size2); ++ if (rc || !buffer2) { ++ /* Could not get second version - accept v1. */ ++ break; ++ } ++ ++ if (size == size2 && memcmp(buffer, buffer2, size) == 0) { ++ /* No change */ ++ free(buffer2); ++ break; ++ } ++ ++ free(buffer); ++ free(buffer2); ++ } ++ ++ if (retry >= READ_RETRY) { ++ warnx("%s: File changed %d times while reading - aborting", ++ filename, retry); ++ return EXIT_RUNTIME_ERROR; ++ } ++ ++ /* Perform basic checks */ ++ f.name = filename; ++ f.buffer = buffer; ++ f.size = size; ++ hdr = (void *) buffer; ++ if (!check_header(&f, hdr)) { ++ free(buffer); ++ return EXIT_FORMAT_ERROR; ++ } ++ if (fwacc(&f, hdr->file_len) > size) { ++ fwwarn(&f, "File length mismatch (expect %zu) - adjusting", ++ size); ++ hdr->file_len = size; ++ } ++ ++ *file = f; ++ ++ return EXIT_OK; ++} ++ ++/* Return textual representation of a device entry type. */ ++static const char *type_to_str(uint16_t type) ++{ ++ switch (type) { ++ case FW_DE_HDR_TYPE_DASD: ++ return "dasd"; ++ case FW_DE_HDR_TYPE_ZFCP_HOST: ++ return "zfcp-host"; ++ case FW_DE_HDR_TYPE_ZFCP_LUN: ++ return "zfcp-lun"; ++ case FW_DE_HDR_TYPE_QETH: ++ return "qeth"; ++ default: ++ return ""; ++ } ++} ++ ++/* Convert a binary format device setting of the specified length to integer. */ ++static unsigned long parse_value(char *data, uint8_t len) ++{ ++ switch (len) { ++ case 1: ++ return (unsigned long) *((uint8_t *) data); ++ case 2: ++ return (unsigned long) *((uint16_t *) data); ++ case 4: ++ return (unsigned long) *((uint32_t *) data); ++ case 8: ++ return (unsigned long) *((uint64_t *) data); ++ default: ++ return 0; ++ } ++} ++ ++/* Perform sanity checks on device setting. */ ++static bool check_setting(struct fw_file *f, struct fw_setting *set) ++{ ++ /* Key sanity checks */ ++ if (fwacc(f, set->key_type) != FW_SETTING_KEYTYPE_ASCII) { ++ fwwarn(f, "Unsupported key type: %d", set->key_type); ++ return false; ++ } ++ if (fwacc(f, set->key_len) < 1) { ++ fwwarn(f, "Unsupported key length: %d", set->key_len); ++ return false; ++ } ++ if (sizeof(struct fw_setting) + fwacc(f, set->key_len) > set->len) { ++ fwwarn(f, "Key length exceeds setting"); ++ return false; ++ } ++ if (fwacc(f, set->data[set->key_len - 1])) { ++ fwwarn(f, "Key not null-terminated"); ++ return false; ++ } ++ ++ /* Value sanity checks */ ++ if (fwacc(f, set->val_type) != FW_SETTING_VALTYPE_UINT && ++ fwacc(f, set->val_type) != FW_SETTING_VALTYPE_ASCII) { ++ fwwarn(f, "Unsupported value type: %d", set->val_type); ++ return false; ++ } ++ if (fwacc(f, set->val_len) < 1) { ++ fwwarn(f, "Unsupported value length: %d", set->val_len); ++ return false; ++ } ++ if (sizeof(struct fw_setting) + set->key_len + ++ fwacc(f, set->val_len) > set->len) { ++ fwwarn(f, "Value length exceeds setting"); ++ return false; ++ } ++ if ((set->val_type == FW_SETTING_VALTYPE_ASCII) && ++ fwacc(f, set->data[set->key_len + set->val_len - 1])) { ++ fwwarn(f, "Value not null-terminated"); ++ return false; ++ } ++ if (set->val_type == FW_SETTING_VALTYPE_UINT) { ++ switch (fwacc(f, set->val_len)) { ++ case 1: ++ case 2: ++ case 4: ++ case 8: ++ break; ++ default: ++ fwwarn(f, "Unsupported integer value length: %d", ++ set->val_len); ++ return false; ++ } ++ } ++ ++ return true; ++} ++ ++/* Add a setting to the device. Emit a warning if the setting is not known. */ ++static void _add_setting(const char *filename, struct device *dev, ++ config_t config, const char *key, const char *value) ++{ ++ struct attrib *a; ++ struct setting_list *list; ++ ++ list = device_get_setting_list(dev, config); ++ a = attrib_find(dev->subtype->dev_attribs, key); ++ if (!a) { ++ warnx("%s: Applying unknown device setting %s=%s", filename, ++ key, value); ++ } ++ setting_list_apply(list, a, key, value); ++} ++ ++static void add_setting(const char *filename, struct device *dev, ++ config_t config, const char *key, const char *value) ++{ ++ if (SCOPE_ACTIVE(config)) ++ _add_setting(filename, dev, config_active, key, value); ++ if (SCOPE_PERSISTENT(config)) ++ _add_setting(filename, dev, config_persistent, key, value); ++} ++ ++/* Parse a single device setting in firmware format and apply it to the ++ * specified device. */ ++static void parse_setting(struct fw_file *f, struct fw_setting *set, ++ struct device *dev, config_t config) ++{ ++ char *ascii_key, *ascii_val; ++ unsigned long ulong_val; ++ ++ if (!check_setting(f, set)) ++ return; ++ ++ ascii_key = &set->data[0]; ++ if (set->val_type == FW_SETTING_VALTYPE_UINT) { ++ ulong_val = parse_value(&set->data[set->key_len], set->val_len); ++ ascii_val = misc_asprintf("%lu", ulong_val); ++ add_setting(f->name, dev, config, ascii_key, ascii_val); ++ free(ascii_val); ++ } else { ++ ascii_val = &set->data[set->key_len]; ++ add_setting(f->name, dev, config, ascii_key, ascii_val); ++ } ++} ++ ++/* Parse a device settings list in firmware format and apply the resulting ++ * settings to the specified device. */ ++static void parse_settings(struct fw_file *f, char *data, struct device *dev, ++ config_t config) ++{ ++ struct fw_setlist *list = (struct fw_setlist *) data; ++ struct fw_setting *set; ++ uint16_t off; ++ ++ for (off = sizeof(struct fw_setlist); off < list->len; ++ off += set->len) { ++ set = (struct fw_setting *) &data[off]; ++ if (fwacc(f, set->len) < sizeof(struct fw_setting)) { ++ fwwarn(f, "Setting too short"); ++ break; ++ } ++ if (off + fwacc(f, set->len) > list->len) { ++ fwwarn(f, "Setting too long"); ++ break; ++ } ++ parse_setting(f, set, dev, config); ++ } ++} ++ ++/* Perform sanity checks on an I/O device ID. */ ++static bool check_iodevid(struct fw_file *f, uint8_t *flags, ++ struct fw_iodevid *id) ++{ ++ ++ if (fwacc(f, *flags) & FW_IODEVID_FLAG_MCSS) { ++ fwwarn(f, "Unsupported entry in non-default CSS"); ++ return false; ++ } ++ if (fwacc(f, id->cssid) != 0) { ++ fwwarn(f, "Non-zero CSS-ID"); ++ return false; ++ } ++ return true; ++} ++ ++/* Perform sanity checks on a device entry. */ ++static bool check_de_size(struct fw_file *f, struct fw_dehdr *de, size_t size) ++{ ++ if (fwacc(f, de->len) < size) { ++ fwwarn(f, "Device entry too short (expect %zu)", size); ++ return false; ++ } ++ return true; ++} ++ ++/* Convert an I/O device ID to CCW device ID format. */ ++static void io_to_ccw(struct ccw_devid *c, struct fw_iodevid *i) ++{ ++ c->cssid = i->cssid; ++ c->ssid = i->ssid; ++ c->devno = i->devno; ++} ++ ++/* Register a new device configuration. */ ++static struct device *add_device(struct fw_file *f, struct subtype *st, ++ const char *id, config_t config, ++ struct util_list *objects) ++{ ++ struct device *dev; ++ ++ if (!st->devices) ++ st->devices = device_list_new(st); ++ ++ dev = device_list_find(st->devices, id, NULL); ++ if (!dev) { ++ dev = device_new(st, id); ++ if (!dev) { ++ warnx("%s: Skipping invalid %s device ID %s", f->name, ++ st->name, id); ++ return NULL; ++ } ++ device_list_add(st->devices, dev); ++ } ++ ptrlist_add(objects, object_new(export_device, dev)); ++ ++ /* Prepare device for new settings. */ ++ if (SCOPE_ACTIVE(config)) { ++ setting_list_clear(dev->active.settings); ++ if (dev->subtype->support_definable) ++ dev->active.definable = 1; ++ else ++ dev->active.exists = 1; ++ } ++ if (SCOPE_PERSISTENT(config)) { ++ setting_list_clear(dev->persistent.settings); ++ dev->persistent.exists = 1; ++ } ++ ++ return dev; ++} ++ ++/* Parse a DASD device entry. */ ++static void parse_dasd(struct fw_file *f, struct fw_dehdr *de, config_t config, ++ struct util_list *objects) ++{ ++ struct fw_dasd *dasd = (struct fw_dasd *) de; ++ struct ccw_devid devid; ++ struct device *dev_eckd, *dev_fba; ++ char *id; ++ ++ if (!check_de_size(f, de, sizeof(struct fw_dasd))) ++ return; ++ if (!check_iodevid(f, &dasd->id_flags, &dasd->id)) ++ return; ++ ++ /* Could be either dasd_eckd or dasd_fba - add both entries */ ++ io_to_ccw(&devid, &dasd->id); ++ id = ccw_devid_to_str(&devid); ++ dev_eckd = add_device(f, &dasd_subtype_eckd, id, config, objects); ++ dev_fba = add_device(f, &dasd_subtype_fba, id, config, objects); ++ free(id); ++ ++ if (dasd->hdr.len > sizeof(struct fw_dasd)) { ++ if (dev_eckd) ++ parse_settings(f, dasd->settings, dev_eckd, config); ++ if (dev_fba) ++ parse_settings(f, dasd->settings, dev_fba, config); ++ } ++} ++ ++/* Parse a zFCP host device entry. */ ++static void parse_zfcp_host(struct fw_file *f, struct fw_dehdr *de, ++ config_t config, struct util_list *objects) ++{ ++ struct fw_zfcp_host *zfcp_host = (struct fw_zfcp_host *) de; ++ struct ccw_devid devid; ++ struct device *dev; ++ char *id; ++ ++ if (!check_de_size(f, de, sizeof(struct fw_zfcp_host))) ++ return; ++ if (!check_iodevid(f, &zfcp_host->id_flags, &zfcp_host->id)) ++ return; ++ ++ /* Add zfcp_host entry */ ++ io_to_ccw(&devid, &zfcp_host->id); ++ id = ccw_devid_to_str(&devid); ++ dev = add_device(f, &zfcp_host_subtype, id, config, objects); ++ free(id); ++ ++ if (dev && zfcp_host->hdr.len > sizeof(struct fw_zfcp_host)) ++ parse_settings(f, zfcp_host->settings, dev, config); ++} ++ ++/* Parse a zFCP LUN device entry. */ ++static void parse_zfcp_lun(struct fw_file *f, struct fw_dehdr *de, ++ config_t config, struct util_list *objects) ++{ ++ struct fw_zfcp_lun *zfcp_lun = (struct fw_zfcp_lun *) de; ++ struct zfcp_lun_devid devid; ++ struct device *dev; ++ char *id; ++ ++ if (!check_de_size(f, de, sizeof(struct fw_zfcp_lun))) ++ return; ++ if (!check_iodevid(f, &zfcp_lun->id_flags, &zfcp_lun->id)) ++ return; ++ ++ /* Add zfcp_lun entry */ ++ io_to_ccw(&devid.fcp_dev, &zfcp_lun->id); ++ devid.wwpn = zfcp_lun->wwpn; ++ devid.lun = zfcp_lun->fcp_lun; ++ id = zfcp_lun_devid_to_str(&devid); ++ dev = add_device(f, &zfcp_lun_subtype, id, config, objects); ++ free(id); ++ ++ if (dev && zfcp_lun->hdr.len > sizeof(struct fw_zfcp_lun)) ++ parse_settings(f, zfcp_lun->settings, dev, config); ++ ++} ++ ++/* Parse a QETH device entry. */ ++static void parse_qeth(struct fw_file *f, struct fw_dehdr *de, config_t config, ++ struct util_list *objects) ++{ ++ struct fw_qeth *qeth = (struct fw_qeth *) de; ++ struct ccwgroup_devid devid; ++ struct device *dev; ++ char *id; ++ ++ if (!check_de_size(f, de, sizeof(struct fw_qeth))) ++ return; ++ if (!check_iodevid(f, &qeth->id_flags, &qeth->read_id) || ++ !check_iodevid(f, &qeth->id_flags, &qeth->write_id) || ++ !check_iodevid(f, &qeth->id_flags, &qeth->data_id)) ++ return; ++ ++ /* Add qeth entry */ ++ devid.num = 3; ++ io_to_ccw(&devid.devid[0], &qeth->read_id); ++ io_to_ccw(&devid.devid[1], &qeth->write_id); ++ io_to_ccw(&devid.devid[2], &qeth->data_id); ++ id = ccwgroup_devid_to_str(&devid); ++ dev = add_device(f, &qeth_subtype_qeth, id, config, objects); ++ free(id); ++ ++ if (dev && qeth->hdr.len > sizeof(struct fw_qeth)) ++ parse_settings(f, qeth->settings, dev, config); ++} ++ ++/* Parse a firmware file. */ ++static void parse_fw(struct fw_file *f, long skip, config_t config, ++ struct util_list *objects) ++{ ++ char *data = f->buffer; ++ struct fw_filehdr *hdr = (struct fw_filehdr *) data; ++ struct fw_dehdr *de; ++ uint16_t count = 0; ++ uint32_t off; ++ ++ for (off = hdr->hdr_len; off < hdr->file_len; off += de->len) { ++ count++; ++ de = (struct fw_dehdr *) &data[off]; ++ if (fwacc(f, de->len) == 0) { ++ fwwarn(f, "Empty device entry"); ++ break; ++ } ++ if (off + fwacc(f, de->len) > hdr->file_len) { ++ fwwarn(f, "Device entry too long"); ++ break; ++ } ++ if (skip >= 0 && de->seq <= skip) { ++ debug("Skipping %s entry due to sequence (%08x)\n", ++ type_to_str(de->type), de->seq); ++ continue; ++ } ++ switch (fwacc(f, de->type)) { ++ case FW_DE_HDR_TYPE_DASD: ++ parse_dasd(f, de, config, objects); ++ break; ++ case FW_DE_HDR_TYPE_ZFCP_HOST: ++ parse_zfcp_host(f, de, config, objects); ++ break; ++ case FW_DE_HDR_TYPE_ZFCP_LUN: ++ parse_zfcp_lun(f, de, config, objects); ++ break; ++ case FW_DE_HDR_TYPE_QETH: ++ parse_qeth(f, de, config, objects); ++ break; ++ default: ++ fwwarn(f, "Unknown entry (type=%04x)", de->type); ++ break; ++ } ++ } ++ ++ if (count != fwacc(f, hdr->de_count)) ++ fwwarn(f, "Device entry count mismatch"); ++} ++ ++/* Read configuration objects from @fd in firmware file format. Add pointers to ++ * newly allocated struct export_objects to ptrlist @objects. If @skip is a ++ * positive number, skip over entries with a sequence number equal to or ++ * greater than @skip. */ ++exit_code_t firmware_read(FILE *fd, const char *filename, long skip, ++ config_t config, struct util_list *objects) ++{ ++ struct fw_file file; ++ exit_code_t rc; ++ ++ rc = read_fw(&file, fd, filename); ++ if (rc) ++ return rc; ++ ++ parse_fw(&file, skip, config, objects); ++ free(file.buffer); ++ ++ return rc; ++} ++ ++/* Check if @fd refers to a file in binary firmware format. */ ++bool firmware_detect(FILE *fd) ++{ ++ int c; ++ ++ c = fgetc(fd); ++ ungetc(c, fd); ++ ++ /* Note: A full check would require looking at least at the first 4 ++ * bytes, but fd might be non-seekable (e.g. pipe). Since there is no ++ * way that a textual import file can start with a 'z', looking at ++ * the first char should be enough. */ ++ ++ return c == 'z'; ++} diff --git a/s390-tools-sles15sp1-04-zdev-Implement-no-settle.patch b/s390-tools-sles15sp1-04-zdev-Implement-no-settle.patch new file mode 100644 index 0000000..9d1d794 --- /dev/null +++ b/s390-tools-sles15sp1-04-zdev-Implement-no-settle.patch @@ -0,0 +1,157 @@ +Subject: zdev: Implement --no-settle +From: Peter Oberparleiter + +Summary: zdev: Add support for handling I/O configuration data +Description: LPARs that are running in IBM Dynamic Partition Manager (DPM) mode + can access a firmware-generated I/O configuration data file that + contains s390-specific information about available I/O devices + such as qeth device numbers and parameters, and FCP device IDs. + + This data file is intended to remove the need for users to + manually enter the corresponding device data during installation. + + Linux kernels with the corresponding support make the I/O + configuration data available at the following location: + + /sys/firmware/sclp_sd/config/data + + This patch set adds support for handling this data file using the + chzdev and lszdev tools: + + - I/O configuration data can be applied using chzdev's --import + option + - Initial RAM-Disk scripts automatically apply the + I/O configuration data to the system configuration + - lszdev can be used to display the applied auto-configuration + data + - chzdev can be used to manually override the + auto-configuration data + +Upstream-ID: f32bff96881a04bb68b895c23b13ae50daa9e7b4 +Problem-ID: LS1604 + +Upstream-Description: + + zdev: Implement --no-settle + + There are some situations where running "udevadm settle" can result in + a deadlock, such as in the early stages of initial RAM-disk processing. + + Introduce a new command-line option --no-settle that can be used to + suppress calling "udevadm settle" to allow chzdev to be run in such + situations. + + Signed-off-by: Peter Oberparleiter + Signed-off-by: Jan Höppner + + +Signed-off-by: Peter Oberparleiter +--- + zdev/include/udev.h | 1 + + zdev/man/chzdev.8 | 9 +++++++++ + zdev/src/chzdev.c | 9 +++++++++ + zdev/src/chzdev_usage.txt | 1 + + zdev/src/udev.c | 3 +++ + 5 files changed, 23 insertions(+) + +--- a/zdev/include/udev.h ++++ b/zdev/include/udev.h +@@ -14,6 +14,7 @@ + #include "exit_code.h" + + extern int udev_need_settle; ++extern int udev_no_settle; + + /* Single key-operator-value entry in a udev rule line.*/ + struct udev_entry_node { +--- a/zdev/man/chzdev.8 ++++ b/zdev/man/chzdev.8 +@@ -528,6 +528,15 @@ device configuration persistent. Typical + initial RAM disk, or modifying the kernel command line. + .PP + . ++.OD no-settle "" "" ++Do not wait for udev processing to complete. ++ ++Skips all calls to the udevadm tool that are intended to wait for udev to ++finish processing before continuing. There is typically no need to use this ++option unless chzdev is run in an environment where udev is not fully ++functional (such as in the early phase of an initial RAM disk). ++.PP ++. + .OD persistent "p" "" + Apply changes to persistent configuration only. + +--- a/zdev/src/chzdev.c ++++ b/zdev/src/chzdev.c +@@ -95,6 +95,7 @@ struct options { + struct util_list *base; /* List of struct strlist_node */ + unsigned int verbose:1; + unsigned int quiet:1; ++ unsigned int no_settle:1; + }; + + /* Makefile converts chzdev_usage.txt into C file which we include here. */ +@@ -136,6 +137,7 @@ enum { + OPT_VERSION = 'v', + OPT_VERBOSE = 'V', + OPT_QUIET = 'q', ++ OPT_NO_SETTLE = (OPT_ANONYMOUS_BASE+__COUNTER__), + }; + + static struct opts_conflict conflict_list[] = { +@@ -217,6 +219,7 @@ static const struct option opt_list[] = + { "base", required_argument, NULL, OPT_BASE }, + { "verbose", no_argument, NULL, OPT_VERBOSE }, + { "quiet", no_argument, NULL, OPT_QUIET }, ++ { "no-settle", no_argument, NULL, OPT_NO_SETTLE }, + { NULL, no_argument, NULL, 0 }, + }; + +@@ -937,6 +940,11 @@ static exit_code_t parse_options(struct + opts->quiet = 1; + break; + ++ case OPT_NO_SETTLE: ++ /* --no-settle */ ++ opts->no_settle = 1; ++ break; ++ + case ':': + /* Missing option argument. */ + syntax("Option '%s' requires an argument\n", +@@ -2904,6 +2912,7 @@ int main(int argc, char *argv[]) + force = opts.force; + yes = opts.yes; + dryrun = opts.dryrun; ++ udev_no_settle = opts.no_settle; + path_set_base(opts.base); + + if (dryrun) +--- a/zdev/src/chzdev_usage.txt ++++ b/zdev/src/chzdev_usage.txt +@@ -54,5 +54,6 @@ OPTIONS + --no-root-update Skip root device update + --dry-run Display changes without applying + --base PATH Use PATH as base for accessing files ++ --no-settle Do not wait for udev to settle + -V, --verbose Print additional run-time information + -q, --quiet Print only minimal run-time information +--- a/zdev/src/udev.c ++++ b/zdev/src/udev.c +@@ -24,6 +24,7 @@ + #include "udev.h" + + int udev_need_settle = 0; ++int udev_no_settle; + + /* Create a newly allocated udev entry. */ + static struct udev_entry_node *udev_entry_node_new(const char *key, +@@ -403,5 +404,7 @@ exit_code_t udev_remove_rule(const char + /* Wait for all current udev events to finish. */ + void udev_settle(void) + { ++ if (udev_no_settle) ++ return; + misc_system(err_ignore, "%s settle", PATH_UDEVADM); + } diff --git a/s390-tools-sles15sp1-05-zdev-Write-zfcp-lun-udev-rules-to-separate-files.patch b/s390-tools-sles15sp1-05-zdev-Write-zfcp-lun-udev-rules-to-separate-files.patch new file mode 100644 index 0000000..9c386ce --- /dev/null +++ b/s390-tools-sles15sp1-05-zdev-Write-zfcp-lun-udev-rules-to-separate-files.patch @@ -0,0 +1,217 @@ +Subject: zdev: Write zfcp-lun udev rules to separate files +From: Peter Oberparleiter + +Summary: zdev: Add support for handling I/O configuration data +Description: LPARs that are running in IBM Dynamic Partition Manager (DPM) mode + can access a firmware-generated I/O configuration data file that + contains s390-specific information about available I/O devices + such as qeth device numbers and parameters, and FCP device IDs. + + This data file is intended to remove the need for users to + manually enter the corresponding device data during installation. + + Linux kernels with the corresponding support make the I/O + configuration data available at the following location: + + /sys/firmware/sclp_sd/config/data + + This patch set adds support for handling this data file using the + chzdev and lszdev tools: + + - I/O configuration data can be applied using chzdev's --import + option + - Initial RAM-Disk scripts automatically apply the + I/O configuration data to the system configuration + - lszdev can be used to display the applied auto-configuration + data + - chzdev can be used to manually override the + auto-configuration data + +Upstream-ID: a86fb8b09118e6de7463882f889eff7e278163cd +Problem-ID: LS1604 + +Upstream-Description: + + zdev: Write zfcp-lun udev rules to separate files + + Change chzdev's udev rule generation from the previous approach of + combining all zfcp-lun udev rules associated with an FCP device into a + single file to storing zfcp-lun udev rules in one file per zfcp-lun. + This is done to enable per-device udev rule masking. + + With udev rule masking, if a udev rule file by the same name exists in + both /etc and /run, the udev daemon will only consider the rules found + in /etc. + + The auto-configuration feature will make use of per-device udev rule + masking to introduce a new class of configuration data (stored in /run) + that is only active if no user-provided configuration data (in /etc) + exists. + + In addition, change chzdev to allow the regeneration of udev rules by + using the --force command line like in the following example: + + # chzdev zfcp-lun --configured --enable --persistent --force + + This can be used to convert all existing zfcp-lun udev rules from the + old format to the new one. + + Signed-off-by: Peter Oberparleiter + Signed-off-by: Jan Höppner + + +Signed-off-by: Peter Oberparleiter +--- + zdev/src/chzdev.c | 6 +++- + zdev/src/root.c | 2 - + zdev/src/udev_zfcp_lun.c | 56 ++++++++++++++++++++++++++++++++++----- + 3 files changed, 55 insertions(+), 9 deletions(-) + +--- a/zdev/src/chzdev.c ++++ b/zdev/src/chzdev.c +@@ -1333,7 +1333,7 @@ static exit_code_t cfg_write(struct devi + struct subtype *st = dev->subtype; + exit_code_t rc = EXIT_OK; + +- if (!device_needs_writing(dev, config)) ++ if (!device_needs_writing(dev, config) && !force) + goto out; + + if (check_active && config == config_persistent && +@@ -1624,6 +1624,10 @@ static exit_code_t print_config_result(s + already = device_needs_writing(dev, config) ? 0 : 1; + } + ++ /* Re-do actions if run with --force */ ++ if (force) ++ already = 0; ++ + if (dev) { + devname = dev->subtype->devname; + devid = dev->id; +--- a/zdev/src/root.c ++++ b/zdev/src/root.c +@@ -60,7 +60,7 @@ exit_code_t root_check(void) + /* Check devices. */ + dev = device_list_find(sel->st->devices, sel->id, NULL); + if (dev && dev->persistent.exists && +- device_needs_writing(dev, config_persistent)) { ++ (device_needs_writing(dev, config_persistent) || force)) { + strlist_add(mod, "%s %s", dev->subtype->devname, + dev->id); + } +--- a/zdev/src/udev_zfcp_lun.c ++++ b/zdev/src/udev_zfcp_lun.c +@@ -385,6 +385,8 @@ void udev_zfcp_lun_add_device_ids(struct + free(cb_data.prefix); + } + ++/* Return path to zfcp lun udev rule file containing configuration data for ++ * all LUNs of a zfcp device. */ + static char *get_zfcp_lun_path(const char *id) + { + char *copy, *e, *path; +@@ -399,6 +401,13 @@ static char *get_zfcp_lun_path(const cha + return path; + } + ++/* Return path to zfcp lun udev rule file containing configuration data for ++ * a single LUN. */ ++static char *get_single_zfcp_lun_path(const char *id) ++{ ++ return path_get_udev_rule(ZFCP_LUN_NAME, id); ++} ++ + /* Apply the settings found in NODE to STATE. */ + static void zfcp_lun_node_to_state(struct zfcp_lun_node *node, + struct attrib **attribs, +@@ -437,7 +446,12 @@ exit_code_t udev_zfcp_lun_read_device(st + exit_code_t rc = EXIT_OK; + char *path; + +- path = get_zfcp_lun_path(dev->id); ++ /* Check for single lun file first then try multi lun file. */ ++ path = get_single_zfcp_lun_path(dev->id); ++ if (!util_path_exists(path)) { ++ free(path); ++ path = get_zfcp_lun_path(dev->id); ++ } + + /* Get previous rule data. */ + luns = zfcp_lun_node_list_new(); +@@ -599,8 +613,10 @@ out: + + /* Update the udev rule file that configures the zfcp lun with the specified + * ID. If @state is %NULL, remove the rule, otherwise create a rule that +- * applies the corresponding parameters. */ +-static exit_code_t update_lun_rule(const char *id, struct device_state *state) ++ * applies the corresponding parameters. If @single is set, update a single ++ * lun rule file, otherwise update a multi lun rule file. */ ++static exit_code_t update_lun_rule(const char *id, struct device_state *state, ++ bool single) + { + struct zfcp_lun_devid devid; + struct util_list *luns; +@@ -612,7 +628,7 @@ static exit_code_t update_lun_rule(const + rc = zfcp_lun_parse_devid(&devid, id, err_delayed_print); + if (rc) + return rc; +- path = get_zfcp_lun_path(id); ++ path = single ? get_single_zfcp_lun_path(id) : get_zfcp_lun_path(id); + + /* Get previous rule data. */ + luns = zfcp_lun_node_list_new(); +@@ -650,24 +666,50 @@ static exit_code_t update_lun_rule(const + * device state. */ + exit_code_t udev_zfcp_lun_write_device(struct device *dev) + { +- return update_lun_rule(dev->id, &dev->persistent); ++ exit_code_t rc; ++ ++ rc = update_lun_rule(dev->id, &dev->persistent, true); ++ ++ /* We only want single lun rule files so remove any remaining ++ * references in multi lun rule files. */ ++ update_lun_rule(dev->id, NULL, false); ++ ++ return rc; + } + + /* Remove the UDEV rule used to configure the zfcp lun with the specified ID. */ + exit_code_t udev_zfcp_lun_remove_rule(const char *id) + { +- return update_lun_rule(id, NULL); ++ exit_code_t rc, rc2; ++ ++ rc = update_lun_rule(id, NULL, true); ++ rc2 = update_lun_rule(id, NULL, false); ++ ++ if (rc) ++ return rc; ++ ++ return rc2; + } + + /* Determine if a udev rule exists for configuring the specified zfcp lun. */ + bool udev_zfcp_lun_exists(const char *id) + { + struct zfcp_lun_devid devid; +- char *path, *rule, *pattern = NULL; ++ char *path, *rule = NULL, *pattern = NULL; + bool rc = false; + + if (zfcp_lun_parse_devid(&devid, id, err_ignore) != EXIT_OK) + return false; ++ ++ /* Check for single lun rule file first. */ ++ path = get_single_zfcp_lun_path(id); ++ if (util_path_exists(path)) { ++ rc = true; ++ goto out; ++ } ++ free(path); ++ ++ /* Check multi lun rule file next. */ + path = get_zfcp_lun_path(id); + rule = misc_read_text_file(path, 1, err_ignore); + if (!rule) diff --git a/s390-tools-sles15sp1-06-zdev-Add-support-for-handling-auto-configuration-dat.patch b/s390-tools-sles15sp1-06-zdev-Add-support-for-handling-auto-configuration-dat.patch new file mode 100644 index 0000000..f7c3833 --- /dev/null +++ b/s390-tools-sles15sp1-06-zdev-Add-support-for-handling-auto-configuration-dat.patch @@ -0,0 +1,3040 @@ +Subject: zdev: Add support for handling auto-configuration data +From: Peter Oberparleiter + +Summary: zdev: Add support for handling I/O configuration data +Description: LPARs that are running in IBM Dynamic Partition Manager (DPM) mode + can access a firmware-generated I/O configuration data file that + contains s390-specific information about available I/O devices + such as qeth device numbers and parameters, and FCP device IDs. + + This data file is intended to remove the need for users to + manually enter the corresponding device data during installation. + + Linux kernels with the corresponding support make the I/O + configuration data available at the following location: + + /sys/firmware/sclp_sd/config/data + + This patch set adds support for handling this data file using the + chzdev and lszdev tools: + + - I/O configuration data can be applied using chzdev's --import + option + - Initial RAM-Disk scripts automatically apply the + I/O configuration data to the system configuration + - lszdev can be used to display the applied auto-configuration + data + - chzdev can be used to manually override the + auto-configuration data + +Upstream-ID: fe68ec513dc1c73c1961f4e15b83d51291df20eb +Problem-ID: LS1604 + +Upstream-Description: + + zdev: Add support for handling auto-configuration data + + Auto-configuration is the name of a new configuration target that is + supported by chzdev and lszdev besides the existing active and + persistent configuration targets. Directives created in this new + configuration are stored as udev rules in the /run/udev/rules.d + directory. + + Auto-configuration directives are only in effect if there are no + directives for the same device in the user-provided persistent + configuration. This allows users to override auto-configuration + directives if necessary. + + Due to the volatile nature of the /run directory, auto-configuration + directives are cleared on reboot. Therefore mechanisms that generate + auto-configuration directives must recreate them on every boot. + + The lszdev tool displays auto-configuration data both in list view + as well as in detail view. Users can specify the new option --auto-conf + to only show data from this configuration target. + + Mechanisms that generate automated configuration directives can use + chzdev together with the --auto-conf option to create the corresponding + udev rules. + + Note: This change does not include a mechanism that generates + auto-configuration directives. + + Signed-off-by: Peter Oberparleiter + Signed-off-by: Jan Höppner + + +Signed-off-by: Peter Oberparleiter +--- + zdev/include/device.h | 4 + zdev/include/misc.h | 28 +++--- + zdev/include/path.h | 5 - + zdev/include/setting.h | 3 + zdev/include/subtype.h | 22 ++++ + zdev/include/udev.h | 5 - + zdev/include/udev_ccw.h | 8 - + zdev/include/udev_ccwgroup.h | 12 +- + zdev/include/udev_zfcp_lun.h | 10 +- + zdev/man/chzdev.8 | 16 +++ + zdev/man/lszdev.8 | 22 +++- + zdev/src/ccw.c | 99 ++++++++++++++++++--- + zdev/src/ccwgroup.c | 76 ++++++++++++++-- + zdev/src/chzdev.c | 162 +++++++++++++++++++++++------------ + zdev/src/chzdev_usage.txt | 1 + zdev/src/dasd.c | 48 +++++----- + zdev/src/device.c | 55 +++++++++++ + zdev/src/export.c | 96 +++++++++++++++----- + zdev/src/firmware.c | 6 + + zdev/src/lszdev.c | 154 +++++++++++++++++++++++++-------- + zdev/src/lszdev_usage.txt | 1 + zdev/src/misc.c | 25 +++-- + zdev/src/path.c | 14 +-- + zdev/src/qeth.c | 32 ++++++ + zdev/src/select.c | 17 ++- + zdev/src/setting.c | 14 ++- + zdev/src/subtype.c | 83 +++++++++++++++++ + zdev/src/udev.c | 9 + + zdev/src/udev_ccw.c | 30 +++--- + zdev/src/udev_ccwgroup.c | 41 ++++---- + zdev/src/udev_zfcp_lun.c | 46 +++++---- + zdev/src/zfcp_lun.c | 51 +++++++++-- + 32 files changed, 910 insertions(+), 285 deletions(-) + +--- a/zdev/include/device.h ++++ b/zdev/include/device.h +@@ -45,6 +45,7 @@ struct device_state { + * @node: Node for adding this device to a list + * @active: Device state in the active configuration + * @persistent: Device state in the persistent configuration ++ * @autoconf: Auto-configured device state + * @errors: A strlist of error and warning messages issued for the device + * @processed: Device has been processed + */ +@@ -59,6 +60,7 @@ struct device { + + struct device_state active; + struct device_state persistent; ++ struct device_state autoconf; + + unsigned int processed:1; + }; +@@ -97,4 +99,6 @@ void device_list_print(struct device_lis + struct setting_list *device_get_setting_list(struct device *dev, + config_t config); + ++config_t device_get_config(struct device *dev); ++ + #endif /* DEVICE_H */ +--- a/zdev/include/misc.h ++++ b/zdev/include/misc.h +@@ -19,9 +19,14 @@ + #include "exit_code.h" + + #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +-#define SCOPE_ACTIVE(x) (((x) == config_active) || ((x) == config_all)) +-#define SCOPE_PERSISTENT(x) (((x) == config_persistent) || \ +- ((x) == config_all)) ++#define SCOPE_ACTIVE(x) ((x) & config_active ? 1 : 0) ++#define SCOPE_PERSISTENT(x) ((x) & config_persistent ? 1 : 0) ++#define SCOPE_AUTOCONF(x) ((x) & config_autoconf ? 1 : 0) ++#define SCOPE_ALL(x) ((x) == (config_active | config_persistent |\ ++ config_autoconf)) ++#define SCOPE_SINGLE(x) ((x) == config_active || \ ++ (x) == config_persistent || \ ++ (x) == config_autoconf) + + #define DELAY_INDENT 4 + +@@ -34,12 +39,15 @@ + + #define EVEN(x) (((x) & 1) == 0) + +-/* Enumeration of configuration sets. */ +-typedef enum { +- config_active, +- config_persistent, +- config_all, +-} config_t; ++/* Enumeration of configuration sets. Multiple configuration sets can be ++ * combined using bit-wise or. */ ++enum { ++ config_active = 1, ++ config_persistent = 2, ++ config_autoconf = 4, ++ config_all = 7, ++}; ++typedef int config_t; + + typedef enum { + err_ignore, +@@ -167,7 +175,7 @@ exit_code_t misc_write_text_file(const c + exit_code_t misc_write_text_file_retry(const char *, const char *, err_t); + exit_code_t misc_mktemp(char **, int *); + char *misc_readlink(const char *path); +-config_t get_config(int, int); ++config_t get_config(int act, int pers, int ac); + bool is_zvm(void); + bool is_terminal(void); + const char *config_to_str(config_t); +--- a/zdev/include/path.h ++++ b/zdev/include/path.h +@@ -24,6 +24,7 @@ + #define PATH_CCW_BUS "/sys/bus/ccw" + #define PATH_CCWGROUP_BUS "/sys/bus/ccwgroup" + #define PATH_UDEV_RULES "/etc/udev/rules.d" ++#define PATH_UDEV_RULES_VOLATILE "/run/udev/rules.d" + #define PATH_PROC "/proc" + + #define PATH_UDEVADM "udevadm" +@@ -55,8 +56,8 @@ char *path_get_ccw_device(const char *, + char *path_get_ccw_devices(const char *); + char *path_get_ccwgroup_device(const char *, const char *); + char *path_get_ccwgroup_devices(const char *); +-char *path_get_udev_rule(const char *, const char *); +-char *path_get_udev_rules(void); ++char *path_get_udev_rule(const char *type, const char *id, bool vol); ++char *path_get_udev_rules(bool vol); + char *path_get_proc(const char *); + char *path_get_sys_bus_dev(const char *, const char *); + char *path_get_sys_bus_drv(const char *, const char *); +--- a/zdev/include/setting.h ++++ b/zdev/include/setting.h +@@ -94,7 +94,8 @@ void setting_list_map_values(struct sett + void setting_list_mark_default_derived(struct setting_list *); + int setting_list_count_set(struct setting_list *); + void setting_list_remove_derived(struct setting_list *); +-char *setting_get_changes(struct setting_list *, struct setting_list *); ++char *setting_get_changes(struct setting_list *act, struct setting_list *pers, ++ struct setting_list *ac); + bool setting_match_value(struct setting *, const char *); + + #endif /* SETTING_H */ +--- a/zdev/include/subtype.h ++++ b/zdev/include/subtype.h +@@ -67,17 +67,22 @@ typedef exit_code_t (*subtype_cb_t)(stru + * + * @exists_active: Check if device exists in active configuration + * @exists_persistent: Check if device exists in persistent configuration ++ * @exists_autoconf: Check if device exists in autoconf configuration + * + * @add_active_ids: Add IDs of all devices existing in active configuration to + * specified strlist + * @add_persistent_ids: Add IDs of all devices existing in persistent + * configuration to specified strlist ++ * @add_autoconf_ids: Add IDs of all devices existing in autoconf ++ * configuration to specified strlist + * + * @read_active: Read device configuration from active configuration + * @read_persistent: Read device configuration from persistent configuration ++ * @read_autoconf: Read device configuration from autoconf configuration + * + * @configure_active: Apply configuration to active configuration + * @configure_persistent: Apply configuration to persistent configuration ++ * @configure_autoconf: Apply configuration to autoconf configuration + * + * @check_pre_write: Optional: Determine if the given configuration is valid + * for the specified device. If not, emit warning messages +@@ -150,24 +155,33 @@ struct subtype { + + bool (*exists_active)(struct subtype *, const char *); + bool (*exists_persistent)(struct subtype *, const char *); ++ bool (*exists_autoconf)(struct subtype *, const char *); + + void (*add_active_ids)(struct subtype *, struct util_list *); + void (*add_persistent_ids)(struct subtype *, + struct util_list *); ++ void (*add_autoconf_ids)(struct subtype *, ++ struct util_list *); + + exit_code_t (*read_active)(struct subtype *, struct device *, + read_scope_t); + exit_code_t (*read_persistent)(struct subtype *, struct device *, + read_scope_t); ++ exit_code_t (*read_autoconf)(struct subtype *, struct device *, ++ read_scope_t); + + exit_code_t (*configure_active)(struct subtype *, struct device *); + exit_code_t (*configure_persistent)(struct subtype *, + struct device *); ++ exit_code_t (*configure_autoconf)(struct subtype *, ++ struct device *); + + exit_code_t (*deconfigure_active)(struct subtype *, + struct device *); + exit_code_t (*deconfigure_persistent)(struct subtype *, + struct device *); ++ exit_code_t (*deconfigure_autoconf)(struct subtype *, ++ struct device *); + + exit_code_t (*check_pre_configure)(struct subtype *, + struct device *, int, config_t); +@@ -217,23 +231,31 @@ void subtype_exit(struct subtype *); + + bool subtype_device_exists_active(struct subtype *, const char *); + bool subtype_device_exists_persistent(struct subtype *, const char *); ++bool subtype_device_exists_autoconf(struct subtype *st, const char *id); + + void subtype_add_active_ids(struct subtype *, struct util_list *); + void subtype_add_persistent_ids(struct subtype *, struct util_list *); ++void subtype_add_autoconf_ids(struct subtype *st, struct util_list *ids); + + exit_code_t subtype_device_read_active(struct subtype *, struct device *, + read_scope_t); + exit_code_t subtype_device_read_persistent(struct subtype *, struct device *, + read_scope_t); ++exit_code_t subtype_device_read_autoconf(struct subtype *st, struct device *dev, ++ read_scope_t scope); + + exit_code_t subtype_device_configure_active(struct subtype *, struct device *); + exit_code_t subtype_device_configure_persistent(struct subtype *, + struct device *); ++exit_code_t subtype_device_configure_autoconf(struct subtype *st, ++ struct device *dev); + + exit_code_t subtype_device_deconfigure_active(struct subtype *, + struct device *); + exit_code_t subtype_device_deconfigure_persistent(struct subtype *st, + struct device *); ++exit_code_t subtype_device_deconfigure_autoconf(struct subtype *st, ++ struct device *dev); + + exit_code_t subtype_check_pre_configure(struct subtype *, struct device *, int, + config_t); +--- a/zdev/include/udev.h ++++ b/zdev/include/udev.h +@@ -40,8 +40,9 @@ exit_code_t udev_read_file(const char *, + void udev_free_file(struct udev_file *); + void udev_file_print(struct udev_file *); + +-void udev_get_device_ids(const char *, struct util_list *); +-exit_code_t udev_remove_rule(const char *, const char *); ++void udev_get_device_ids(const char *type, struct util_list *list, ++ bool autoconf); ++exit_code_t udev_remove_rule(const char *type, const char *id, bool autoconf); + + void udev_settle(void); + +--- a/zdev/include/udev_ccw.h ++++ b/zdev/include/udev_ccw.h +@@ -15,9 +15,9 @@ + + struct device; + +-bool udev_ccw_exists(const char *, const char *); +-exit_code_t udev_ccw_read_device(struct device *); +-exit_code_t udev_ccw_write_device(struct device *); +-exit_code_t udev_ccw_write_cio_ignore(const char *); ++bool udev_ccw_exists(const char *type, const char *id, bool autoconf); ++exit_code_t udev_ccw_read_device(struct device *dev, bool autoconf); ++exit_code_t udev_ccw_write_device(struct device *dev, bool autoconf); ++exit_code_t udev_ccw_write_cio_ignore(const char *id_list, bool autoconf); + + #endif /* UDEV_CCW_H */ +--- a/zdev/include/udev_ccwgroup.h ++++ b/zdev/include/udev_ccwgroup.h +@@ -16,10 +16,12 @@ + struct device; + struct util_list; + +-bool udev_ccwgroup_exists(const char *, const char *); +-exit_code_t udev_ccwgroup_read_device(struct device *); +-exit_code_t udev_ccwgroup_write_device(struct device *); +-void udev_ccwgroup_add_device_ids(const char *, struct util_list *); +-exit_code_t udev_ccwgroup_remove_rule(const char *, const char *); ++bool udev_ccwgroup_exists(const char *type, const char *id, bool autoconf); ++exit_code_t udev_ccwgroup_read_device(struct device *dev, bool autoconf); ++exit_code_t udev_ccwgroup_write_device(struct device *dev, bool autoconf); ++void udev_ccwgroup_add_device_ids(const char *type, struct util_list *list, ++ bool autoconf); ++exit_code_t udev_ccwgroup_remove_rule(const char *type, const char *id, ++ bool autoconf); + + #endif /* UDEV_CCWGROUP_H */ +--- a/zdev/include/udev_zfcp_lun.h ++++ b/zdev/include/udev_zfcp_lun.h +@@ -15,10 +15,10 @@ + + struct device; + +-void udev_zfcp_lun_add_device_ids(struct util_list *); +-bool udev_zfcp_lun_exists(const char *); +-exit_code_t udev_zfcp_lun_read_device(struct device *); +-exit_code_t udev_zfcp_lun_write_device(struct device *); +-exit_code_t udev_zfcp_lun_remove_rule(const char *); ++void udev_zfcp_lun_add_device_ids(struct util_list *list, bool autoconf); ++bool udev_zfcp_lun_exists(const char *id, bool autoconf); ++exit_code_t udev_zfcp_lun_read_device(struct device *dev, bool autoconf); ++exit_code_t udev_zfcp_lun_write_device(struct device *dev, bool autoconf); ++exit_code_t udev_zfcp_lun_remove_rule(const char *id, bool autoconf); + + #endif /* UDEV_ZFCP_LUN_H */ +--- a/zdev/man/chzdev.8 ++++ b/zdev/man/chzdev.8 +@@ -428,8 +428,8 @@ using the \-\-import action like in the + .B Note: + By default all configuration data that is read is also applied. To reduce the + scope of imported configuration data, you can select specific devices, a device +-type, or define whether only data for the active or persistent configuration +-should be imported. ++type, or define whether only data for the active, persistent or ++auto-configuration should be imported. + .PP + . + .OD list-attributes "l" "" +@@ -472,6 +472,18 @@ on reboot, or when a device becomes unav + unloaded. + .PP + . ++.OD auto-conf "" "" ++Apply changes to the auto-configuration only. ++ ++This option is used internally to apply machine-provided I/O configuration data ++to a Linux system. ++ ++.B Note: ++There is typically no need for users to specify this option directly. ++In particular, user-initiated changes to this configuration will be lost ++the next time that machine-provided data is obtained (i.e. during boot). ++.PP ++. + .OD base "" "PATH" | "KEY" = "VALUE" + Change file system paths used to access files. + +--- a/zdev/man/lszdev.8 ++++ b/zdev/man/lszdev.8 +@@ -69,12 +69,13 @@ With no further options specified, the o + . + . + .SS "Configurations" +-There are two sources for configuration information: the active configuration +-of the currently running system, and the persistent configuration stored in +-configuration files. ++There are three sources for configuration information: the active configuration ++of the currently running system, the persistent configuration stored in ++configuration files, and the auto-configuration that is provided by some ++machine types to automatically enable I/O devices. + .PP + By default lszdev displays information from both the active and the persistent +-configuration. ++configuration, and, if available, from the auto-configuration. + .PP + . + . +@@ -319,6 +320,19 @@ Restricts output to information obtained + is information from the running system. + .PP + . ++.OD auto-conf "" "" ++List information from the auto-configuration only. ++ ++Restricts output to information obtained from the auto-configuration. ++The auto-configuration is the collection of configuration data obtained ++automatically on some machine models during boot. ++ ++.B Note: ++This data is refreshed during each boot. Also configuration directives in ++the auto-configuration only take effect if there is no directive for the ++same device in the persistent configuration. ++.PP ++. + .OD base "" "PATH" | "KEY" = "VALUE" + Change file system paths used to access files. + +--- a/zdev/src/ccw.c ++++ b/zdev/src/ccw.c +@@ -892,12 +892,13 @@ static exit_code_t collect_group_cb(stru + } + + /* Set bits for all persistently configured CCW devices. */ +-static char ***id_bitmap_collect(void) ++static char ***id_bitmap_collect(bool autoconf) + { + char ***id_bitmap; + int i, j; + struct devtype *dt; + struct subtype *st; ++ config_t config = autoconf ? config_autoconf : config_persistent; + + id_bitmap = id_bitmap_new(); + +@@ -906,14 +907,14 @@ static char ***id_bitmap_collect(void) + for (j = 0; (st = dt->subtypes[j]); j++) { + /* Collect CCW device IDs. */ + if (st->namespace == &ccw_namespace) { +- subtype_for_each_id(st, config_persistent, ++ subtype_for_each_id(st, config, + collect_cb, id_bitmap); + } + /* Collect CCWGROUP device IDs. Since there may be + * multiple namespaces, we need to make use of this + * hack. */ + if (ccwgroup_compatible_namespace(st->namespace)) { +- subtype_for_each_id(st, config_persistent, ++ subtype_for_each_id(st, config, + collect_group_cb, + id_bitmap); + } +@@ -952,7 +953,7 @@ static void range_add(struct util_list * + free(f_str); + } + +-static struct util_list *cio_ignore_get_ranges(void) ++static struct util_list *cio_ignore_get_ranges(bool autoconf) + { + char ***id_bitmap; + unsigned int cssid, ssid, devno; +@@ -962,7 +963,7 @@ static struct util_list *cio_ignore_get_ + + ranges = strlist_new(); + +- id_bitmap = id_bitmap_collect(); ++ id_bitmap = id_bitmap_collect(autoconf); + for (cssid = 0; cssid < CSSID_MAX; cssid++) { + if (!id_bitmap[cssid]) + continue; +@@ -1003,24 +1004,34 @@ static struct util_list *cio_ignore_get_ + } + + /* Persistently configure cio_ignore. */ +-exit_code_t ccw_blacklist_persist(void) ++static exit_code_t _ccw_blacklist_persist(bool autoconf) + { + struct util_list *ranges; + char *id_list; + exit_code_t rc; + + /* Get string to write to /proc/cio_ignore. */ +- ranges = cio_ignore_get_ranges(); ++ ranges = cio_ignore_get_ranges(autoconf); + id_list = strlist_flatten(ranges, ","); + strlist_free(ranges); + + /* Write udev rule to automatically write cio_ignore. */ +- rc = udev_ccw_write_cio_ignore(id_list); ++ rc = udev_ccw_write_cio_ignore(id_list, autoconf); + free(id_list); + + return rc; + } + ++exit_code_t ccw_blacklist_persist(void) ++{ ++ exit_code_t rc1, rc2; ++ ++ rc1 = _ccw_blacklist_persist(false); ++ rc2 = _ccw_blacklist_persist(true); ++ ++ return rc1 ? rc1 : rc2; ++} ++ + /* + * CCW device ID namespace. + */ +@@ -1368,7 +1379,13 @@ static bool ccw_st_exists_active(struct + /* Check if a configuration exists for a CCW device with the specified @id. */ + static bool ccw_st_exists_persistent(struct subtype *st, const char *id) + { +- return udev_ccw_exists(st->name, id); ++ return udev_ccw_exists(st->name, id, false); ++} ++ ++/* Check if a configuration exists for a CCW device with the specified @id. */ ++static bool ccw_st_exists_autoconf(struct subtype *st, const char *id) ++{ ++ return udev_ccw_exists(st->name, id, true); + } + + static bool get_ids_cb(const char *file, void *data) +@@ -1404,7 +1421,14 @@ static void ccw_st_add_active_ids(struct + * to strlist @ids. */ + static void ccw_st_add_persistent_ids(struct subtype *st, struct util_list *ids) + { +- udev_get_device_ids(st->name, ids); ++ udev_get_device_ids(st->name, ids, false); ++} ++ ++/* Add the IDs of all CCW devices for which a autoconf configuration exists ++ * to strlist @ids. */ ++static void ccw_st_add_autoconf_ids(struct subtype *st, struct util_list *ids) ++{ ++ udev_get_device_ids(st->name, ids, true); + } + + /* Read the configuration of the CCW device with the specified @id from the +@@ -1440,7 +1464,16 @@ static exit_code_t ccw_st_read_persisten + struct device *dev, + read_scope_t scope) + { +- return udev_ccw_read_device(dev); ++ return udev_ccw_read_device(dev, false); ++} ++ ++/* Read the configuration of the CCW device with the specified @id from the ++ * autoconf configuration and add the resulting data to @dev. */ ++static exit_code_t ccw_st_read_autoconf(struct subtype *st, ++ struct device *dev, ++ read_scope_t scope) ++{ ++ return udev_ccw_read_device(dev, true); + } + + static int get_online(struct setting_list *list) +@@ -1505,7 +1538,14 @@ static exit_code_t ccw_st_configure_acti + static exit_code_t ccw_st_configure_persistent(struct subtype *st, + struct device *dev) + { +- return udev_ccw_write_device(dev); ++ return udev_ccw_write_device(dev, false); ++} ++ ++/* Create a autoconf configuration for the specified device @dev. */ ++static exit_code_t ccw_st_configure_autoconf(struct subtype *st, ++ struct device *dev) ++{ ++ return udev_ccw_write_device(dev, true); + } + + +@@ -1552,7 +1592,22 @@ static exit_code_t ccw_st_deconfigure_ac + static exit_code_t ccw_st_deconfigure_persistent(struct subtype *st, + struct device *dev) + { +- return udev_remove_rule(st->name, dev->id); ++ return udev_remove_rule(st->name, dev->id, false); ++} ++ ++/** ++ * ccw_st_deconfigure_autoconf - Deconfigure device in autoconf ++ * configuration set ++ * @st: Subtype of target device ++ * @dev: Target device ++ * ++ * Deconfigure device @dev in the autoconf configuration set. Return %EXIT_OK ++ * on success, an error code otherwise. ++ */ ++static exit_code_t ccw_st_deconfigure_autoconf(struct subtype *st, ++ struct device *dev) ++{ ++ return udev_remove_rule(st->name, dev->id, true); + } + + /* Perform basic sanity checks. */ +@@ -1589,6 +1644,8 @@ static void ccw_st_online_set(struct sub + setting_list_apply(dev->active.settings, a, name, value); + if (SCOPE_PERSISTENT(config)) + setting_list_apply(dev->persistent.settings, a, name, value); ++ if (SCOPE_AUTOCONF(config)) ++ setting_list_apply(dev->autoconf.settings, a, name, value); + } + + /* Determine the online state of the specified CCW device (0=offline, 1=online, +@@ -1596,14 +1653,16 @@ static void ccw_st_online_set(struct sub + static int ccw_st_online_get(struct subtype *st, struct device *dev, + config_t config) + { +- int act_online = 1, pers_online = 1; ++ int act_online = 1, pers_online = 1, auto_online = 1; + + if (SCOPE_ACTIVE(config)) + act_online = get_online(dev->active.settings); + if (SCOPE_PERSISTENT(config)) + pers_online = get_online(dev->persistent.settings); ++ if (SCOPE_AUTOCONF(config)) ++ auto_online = get_online(dev->autoconf.settings); + +- return MIN(act_online, pers_online); ++ return MIN(MIN(act_online, pers_online), auto_online); + } + + /* Determine if the online state of the specified CCW device was specified */ +@@ -1622,6 +1681,11 @@ static bool ccw_st_online_specified(stru + if (s && s->specified) + return true; + } ++ if (SCOPE_AUTOCONF(config) && dev->autoconf.settings) { ++ s = setting_list_find(dev->autoconf.settings, "online"); ++ if (s && s->specified) ++ return true; ++ } + + return false; + } +@@ -1769,18 +1833,23 @@ struct subtype ccw_subtype = { + + .exists_active = &ccw_st_exists_active, + .exists_persistent = &ccw_st_exists_persistent, ++ .exists_autoconf = &ccw_st_exists_autoconf, + + .add_active_ids = &ccw_st_add_active_ids, + .add_persistent_ids = &ccw_st_add_persistent_ids, ++ .add_autoconf_ids = &ccw_st_add_autoconf_ids, + + .read_active = &ccw_st_read_active, + .read_persistent = &ccw_st_read_persistent, ++ .read_autoconf = &ccw_st_read_autoconf, + + .configure_active = &ccw_st_configure_active, + .configure_persistent = &ccw_st_configure_persistent, ++ .configure_autoconf = &ccw_st_configure_autoconf, + + .deconfigure_active = &ccw_st_deconfigure_active, + .deconfigure_persistent = &ccw_st_deconfigure_persistent, ++ .deconfigure_autoconf = &ccw_st_deconfigure_autoconf, + + .check_pre_configure = &ccw_st_check_pre_configure, + +--- a/zdev/src/ccwgroup.c ++++ b/zdev/src/ccwgroup.c +@@ -426,6 +426,8 @@ static void ccwgroup_st_online_set(struc + } + if (SCOPE_PERSISTENT(config)) + setting_list_apply(dev->persistent.settings, a, name, value); ++ if (SCOPE_AUTOCONF(config)) ++ setting_list_apply(dev->autoconf.settings, a, name, value); + } + + static int get_online(struct setting_list *list) +@@ -446,18 +448,20 @@ static int get_online(struct setting_lis + } + + /* Return -1 if online state is not configured, 0 for offline and 1 for online. +- * If multiple configurations are specified, return the minimum of both. */ ++ * If multiple configurations are specified, return the minimum of all. */ + static int ccwgroup_st_online_get(struct subtype *st, struct device *dev, + config_t config) + { +- int act_online = 1, pers_online = 1; ++ int act_online = 1, pers_online = 1, auto_online = 1; + + if (SCOPE_ACTIVE(config)) + act_online = get_online(dev->active.settings); + if (SCOPE_PERSISTENT(config)) + pers_online = get_online(dev->persistent.settings); ++ if (SCOPE_AUTOCONF(config)) ++ auto_online = get_online(dev->autoconf.settings); + +- return MIN(act_online, pers_online); ++ return MIN(MIN(act_online, pers_online), auto_online); + } + + /* Determine if the online state of the specified device was modified. */ +@@ -476,6 +480,11 @@ static bool ccwgroup_st_online_specified + if (s && s->specified) + return true; + } ++ if (SCOPE_AUTOCONF(config) && dev->autoconf.settings) { ++ s = setting_list_find(dev->autoconf.settings, "online"); ++ if (s && s->specified) ++ return true; ++ } + + return false; + } +@@ -682,7 +691,18 @@ static exit_code_t ccwgroup_st_read_pers + + expand_id(data->ccwgroupdrv, dev); + +- return udev_ccwgroup_read_device(dev); ++ return udev_ccwgroup_read_device(dev, false); ++} ++ ++static exit_code_t ccwgroup_st_read_autoconf(struct subtype *st, ++ struct device *dev, ++ read_scope_t scope) ++{ ++ struct ccwgroup_subtype_data *data = st->data; ++ ++ expand_id(data->ccwgroupdrv, dev); ++ ++ return udev_ccwgroup_read_device(dev, true); + } + + static exit_code_t ccwgroup_st_configure_active(struct subtype *st, +@@ -693,8 +713,9 @@ static exit_code_t ccwgroup_st_configure + return device_write_active_settings(dev); + } + +-static exit_code_t ccwgroup_st_configure_persistent(struct subtype *st, +- struct device *dev) ++static exit_code_t _ccwgroup_st_configure_persistent(struct subtype *st, ++ struct device *dev, ++ bool autoconf) + { + struct ccwgroup_subtype_data *data = st->data; + struct ccwgroup_devid *devid = dev->devid; +@@ -703,9 +724,22 @@ static exit_code_t ccwgroup_st_configure + delayed_err("Incomplete device ID specified\n"); + return EXIT_INCOMPLETE_ID; + } +- return udev_ccwgroup_write_device(dev); ++ return udev_ccwgroup_write_device(dev, autoconf); ++} ++ ++static exit_code_t ccwgroup_st_configure_persistent(struct subtype *st, ++ struct device *dev) ++{ ++ return _ccwgroup_st_configure_persistent(st, dev, false); ++} ++ ++static exit_code_t ccwgroup_st_configure_autoconf(struct subtype *st, ++ struct device *dev) ++{ ++ return _ccwgroup_st_configure_persistent(st, dev, true); + } + ++ + static char *ccwgroup_st_get_active_attrib_path(struct subtype *, + struct device *, const char *); + +@@ -733,7 +767,13 @@ static exit_code_t ccwgroup_st_deconfigu + static exit_code_t ccwgroup_st_deconfigure_persistent(struct subtype *st, + struct device *dev) + { +- return udev_ccwgroup_remove_rule(st->name, dev->id); ++ return udev_ccwgroup_remove_rule(st->name, dev->id, false); ++} ++ ++static exit_code_t ccwgroup_st_deconfigure_autoconf(struct subtype *st, ++ struct device *dev) ++{ ++ return udev_ccwgroup_remove_rule(st->name, dev->id, true); + } + + /* Check if a CCWGROUP device with the specified ID exists. */ +@@ -774,7 +814,12 @@ static bool ccwgroup_st_exists_active(st + + static bool ccwgroup_st_exists_persistent(struct subtype *st, const char *id) + { +- return udev_ccwgroup_exists(st->name, id); ++ return udev_ccwgroup_exists(st->name, id, false); ++} ++ ++static bool ccwgroup_st_exists_autoconf(struct subtype *st, const char *id) ++{ ++ return udev_ccwgroup_exists(st->name, id, true); + } + + int ccwgroup_qsort_cmp(const void *a_ptr, const void *b_ptr) +@@ -796,7 +841,13 @@ static void ccwgroup_st_add_active_ids(s + static void ccwgroup_st_add_persistent_ids(struct subtype *st, + struct util_list *ids) + { +- udev_ccwgroup_add_device_ids(st->name, ids); ++ udev_ccwgroup_add_device_ids(st->name, ids, false); ++} ++ ++static void ccwgroup_st_add_autoconf_ids(struct subtype *st, ++ struct util_list *ids) ++{ ++ udev_ccwgroup_add_device_ids(st->name, ids, true); + } + + /* Check if CCW device ID @b is part of CCW group device ID @a. */ +@@ -1185,18 +1236,23 @@ struct subtype ccwgroup_subtype = { + + .exists_active = &ccwgroup_st_exists_active, + .exists_persistent = &ccwgroup_st_exists_persistent, ++ .exists_autoconf = &ccwgroup_st_exists_autoconf, + + .add_active_ids = &ccwgroup_st_add_active_ids, + .add_persistent_ids = &ccwgroup_st_add_persistent_ids, ++ .add_autoconf_ids = &ccwgroup_st_add_autoconf_ids, + + .read_active = &ccwgroup_st_read_active, + .read_persistent = &ccwgroup_st_read_persistent, ++ .read_autoconf = &ccwgroup_st_read_autoconf, + + .configure_active = &ccwgroup_st_configure_active, + .configure_persistent = &ccwgroup_st_configure_persistent, ++ .configure_autoconf = &ccwgroup_st_configure_autoconf, + + .deconfigure_active = &ccwgroup_st_deconfigure_active, + .deconfigure_persistent = &ccwgroup_st_deconfigure_persistent, ++ .deconfigure_autoconf = &ccwgroup_st_deconfigure_autoconf, + + .online_set = &ccwgroup_st_online_set, + .online_get = &ccwgroup_st_online_get, +--- a/zdev/src/chzdev.c ++++ b/zdev/src/chzdev.c +@@ -86,6 +86,7 @@ struct options { + config_t config; + unsigned int active:1; + unsigned int persistent:1; ++ unsigned int auto_conf:1; + struct util_list *remove; /* List of struct strlist_node */ + unsigned int remove_all:1; + unsigned int force:1; +@@ -138,6 +139,7 @@ enum { + OPT_VERBOSE = 'V', + OPT_QUIET = 'q', + OPT_NO_SETTLE = (OPT_ANONYMOUS_BASE+__COUNTER__), ++ OPT_AUTO_CONF = (OPT_ANONYMOUS_BASE+__COUNTER__), + }; + + static struct opts_conflict conflict_list[] = { +@@ -172,11 +174,13 @@ static struct opts_conflict conflict_lis + OPTS_CONFLICT(OPT_APPLY, + OPT_DECONFIGURE, OPT_LIST_ATTRIBS, OPT_HELP_ATTRIBS, + OPT_LIST_TYPES, OPT_EXPORT, OPT_IMPORT, OPT_REMOVE, +- OPT_REMOVE_ALL, OPT_ACTIVE, OPT_PERSISTENT, 0), ++ OPT_REMOVE_ALL, OPT_ACTIVE, 0), + OPTS_CONFLICT(OPT_ONLINE, + OPT_OFFLINE), + OPTS_CONFLICT(OPT_QUIET, + OPT_VERBOSE), ++ OPTS_CONFLICT(OPT_AUTO_CONF, ++ OPT_TYPE, OPT_LIST_ATTRIBS, OPT_LIST_TYPES), + OPTS_CONFLICT(0, 0), + }; + +@@ -210,6 +214,7 @@ static const struct option opt_list[] = + /* Options. */ + { "active", no_argument, NULL, OPT_ACTIVE }, + { "persistent", no_argument, NULL, OPT_PERSISTENT }, ++ { "auto-conf", no_argument, NULL, OPT_AUTO_CONF }, + { "remove", required_argument, NULL, OPT_REMOVE }, + { "remove-all", no_argument, NULL, OPT_REMOVE_ALL }, + { "force", no_argument, NULL, OPT_FORCE }, +@@ -883,6 +888,11 @@ static exit_code_t parse_options(struct + opts->persistent = 1; + break; + ++ case OPT_AUTO_CONF: ++ /* --auto-conf */ ++ opts->auto_conf = 1; ++ break; ++ + case OPT_REMOVE: + /* --remove ATTRIB */ + strlist_add(opts->remove, "%s", optarg); +@@ -977,7 +987,13 @@ static exit_code_t parse_options(struct + goto out; + + /* Determine configuration set. */ +- opts->config = get_config(opts->active, opts->persistent); ++ if (!opts->active && !opts->persistent && !opts->auto_conf) { ++ /* Default configuration targets are active + persistent */ ++ opts->config = config_active | config_persistent; ++ } else { ++ opts->config = get_config(opts->active, opts->persistent, ++ opts->auto_conf); ++ } + + /* Handle positional parameters. */ + rc = parse_positional(opts, argc, argv, optind); +@@ -1049,13 +1065,15 @@ static void remove_all_settings(struct s + /* Perform --remove-all operation for specified config on device. */ + static exit_code_t device_remove_all(struct device *dev, config_t config) + { +- int active, persistent; ++ int active, persistent, autoconf; + + active = SCOPE_ACTIVE(config) ? + count_removable(dev->active.settings, 1) : 0; + persistent = SCOPE_PERSISTENT(config) ? + count_removable(dev->persistent.settings, 0) : 0; +- if (active == 0 && persistent == 0) { ++ autoconf = SCOPE_AUTOCONF(config) ? ++ count_removable(dev->autoconf.settings, 0) : 0; ++ if (active == 0 && persistent == 0 && autoconf == 0) { + delayed_err("No removable settings found\n"); + return EXIT_SETTING_NOT_FOUND; + } +@@ -1063,6 +1081,8 @@ static exit_code_t device_remove_all(str + remove_all_settings(dev->active.settings, 1); + if (persistent) + remove_all_settings(dev->persistent.settings, 0); ++ if (autoconf) ++ remove_all_settings(dev->autoconf.settings, 0); + + return EXIT_OK; + } +@@ -1132,6 +1152,13 @@ static exit_code_t device_remove_setting + goto out; + } + ++ if (SCOPE_AUTOCONF(config)) { ++ rc = remove_settings(dev->autoconf.settings, names, found, ++ notfound, 0); ++ if (rc) ++ goto out; ++ } ++ + if (!util_list_is_empty(notfound)) { + flat = strlist_flatten(notfound, " "); + delayed_err("Setting not found: %s\n", flat); +@@ -1199,7 +1226,8 @@ static void print_dev_config_info(struct + + changes = setting_get_changes( + SCOPE_ACTIVE(config) ? dev->active.settings : NULL, +- SCOPE_PERSISTENT(config) ? dev->persistent.settings : NULL); ++ SCOPE_PERSISTENT(config) ? dev->persistent.settings : NULL, ++ SCOPE_AUTOCONF(config) ? dev->autoconf.settings : NULL); + if (changes) + info(" Changes: %s\n", changes); + free(changes); +@@ -1257,6 +1285,12 @@ static exit_code_t cfg_mod_existence(str + dev->persistent.modified = 1; + } + ++ /* Create autoconf config if necessary. */ ++ if (SCOPE_AUTOCONF(config) && !dev->autoconf.exists) { ++ dev->autoconf.exists = 1; ++ dev->autoconf.modified = 1; ++ } ++ + return EXIT_OK; + } + +@@ -1296,11 +1330,14 @@ online: + /* Make sure there's an online attribute. */ + ensure_online(dev, config_active); + ensure_online(dev, config_persistent); ++ ensure_online(dev, config_autoconf); + + mand: + /* Ensure default values for mandatory attributes. */ + setting_list_apply_defaults(dev->persistent.settings, + dev->subtype->dev_attribs, true); ++ setting_list_apply_defaults(dev->autoconf.settings, ++ dev->subtype->dev_attribs, true); + + return EXIT_OK; + } +@@ -1336,7 +1373,8 @@ static exit_code_t cfg_write(struct devi + if (!device_needs_writing(dev, config) && !force) + goto out; + +- if (check_active && config == config_persistent && ++ if (check_active && !SCOPE_ACTIVE(config) && ++ (SCOPE_PERSISTENT(config) || SCOPE_AUTOCONF(config)) && + (!dev->active.exists && !dev->active.definable)) { + rc = handle_nonexistent(dev); + if (rc) +@@ -1377,7 +1415,7 @@ static exit_code_t cfg_configure(struct + if (proc_ptr) + *proc_ptr = 0; + +- /* Read current device data. Note that we read data from both ++ /* Read current device data. Note that we read data from all + * configurations to enable QETH autodetection and subtype + * detection (e.g. dasd -> dasd_fba).*/ + dev = NULL; +@@ -1398,23 +1436,17 @@ static exit_code_t cfg_configure(struct + + /* Exit here if we're only trying and device cannot be configured. */ + if (try && !(dev->active.exists || dev->active.definable || +- dev->persistent.exists)) { ++ dev->persistent.exists || dev->autoconf.exists)) { + return EXIT_DEVICE_NOT_FOUND; + } + +- if (config == config_persistent) { +- /* Abort if a modification action is triggered for a +- * non-existing persistent device. */ +- if (!dev->persistent.exists && +- (!util_list_is_empty(opts->remove) || opts->remove_all)) +- return EXIT_DEVICE_NOT_FOUND; ++ /* Create device if needed by action. */ ++ if (opts->enable || !util_list_is_empty(opts->settings)) { ++ rc = cfg_mod_existence(dev, config); ++ if (rc) ++ return rc; + } + +- /* Apply changes to device existence. */ +- rc = cfg_mod_existence(dev, config); +- if (rc) +- return rc; +- + /* Apply changes to device settings. */ + rc = cfg_mod_settings(dev, opts, prereq); + if (rc) +@@ -1426,10 +1458,12 @@ static exit_code_t cfg_configure(struct + + /* Apply persistent configuration to active configuration. */ + static exit_code_t cfg_apply(struct subtype *st, const char *id, int prereq, +- struct device **dev_ptr, int *proc_ptr) ++ struct device **dev_ptr, int *proc_ptr, ++ bool autoconf) + { + exit_code_t rc; + struct device *dev; ++ struct device_state *state; + + /* Reset processed flag. */ + if (proc_ptr) +@@ -1452,8 +1486,9 @@ static exit_code_t cfg_apply(struct subt + *proc_ptr = 1; + dev->processed = 1; + ++ state = autoconf ? &dev->autoconf : &dev->persistent; + /* Exit here if there is no persistent configuration. */ +- if (!dev->persistent.exists) ++ if (!state->exists) + return EXIT_NO_DATA; + + /* Apply changes to device existence. */ +@@ -1462,8 +1497,7 @@ static exit_code_t cfg_apply(struct subt + return rc; + + /* Copy persistent settings to active configuration. */ +- rc = device_apply_settings(dev, config_active, +- &dev->persistent.settings->list); ++ rc = device_apply_settings(dev, config_active, &state->settings->list); + if (rc) + return rc; + +@@ -1477,7 +1511,8 @@ static exit_code_t cfg_import(struct sub + struct device **dev_ptr, int *proc_ptr) + { + exit_code_t rc; +- struct setting_list *active = NULL, *persistent = NULL; ++ struct setting_list *active = NULL, *persistent = NULL, ++ *autoconf = NULL; + struct device *dev; + + /* Reset processed flag. */ +@@ -1495,6 +1530,7 @@ static exit_code_t cfg_import(struct sub + return EXIT_OK; + active = setting_list_copy(dev->active.settings); + persistent = setting_list_copy(dev->persistent.settings); ++ autoconf = setting_list_copy(dev->autoconf.settings); + } else if (!prereq) { + /* Should not happen. */ + return EXIT_OK; +@@ -1524,14 +1560,20 @@ static exit_code_t cfg_import(struct sub + &persistent->list); + if (rc) + goto out; ++ rc = device_apply_settings(dev, config_autoconf, ++ &autoconf->list); ++ if (rc) ++ goto out; + } + ensure_online(dev, config_active); + ensure_online(dev, config_persistent); ++ ensure_online(dev, config_autoconf); + + /* Write resulting device data. */ + rc = cfg_write(dev, prereq, config, 0); + + out: ++ setting_list_free(autoconf); + setting_list_free(persistent); + setting_list_free(active); + +@@ -1609,11 +1651,13 @@ static exit_code_t print_config_result(s + if (opts->deconfigure) { + op = "deconfigure"; + verb = "deconfigured"; +- if (dev) ++ if (dev) { + already = !dev->active.modified && +- !dev->persistent.modified; +- else if (rc == EXIT_DEVICE_NOT_FOUND && +- config == config_persistent) { ++ !dev->persistent.modified && ++ !dev->autoconf.modified; ++ } else if (rc == EXIT_DEVICE_NOT_FOUND && ++ (!SCOPE_ACTIVE(config) && (SCOPE_PERSISTENT(config) || ++ SCOPE_AUTOCONF(config)))) { + rc = EXIT_OK; + already = 1; + } +@@ -1685,9 +1729,10 @@ static exit_code_t cfg_prereqs(struct su + prereqs = selected_dev_list_new(); + subtype_add_prereqs(st, id, prereqs); + util_list_iterate(prereqs, sel) { +- if (opts->apply) +- rc = cfg_apply(sel->st, sel->id, 1, &dev, &proc); +- else if (opts->import) { ++ if (opts->apply) { ++ rc = cfg_apply(sel->st, sel->id, 1, &dev, &proc, ++ opts->auto_conf); ++ } else if (opts->import) { + rc = cfg_import(sel->st, sel->id, config, 1, &dev, + &proc); + } else { +@@ -1760,9 +1805,11 @@ static exit_code_t configure_devices(str + struct namespace *ns; + const char *param; + struct device *dev; ++ config_t config = opts->config; + + /* Determine list of selected devices. */ +- if (opts->config == config_persistent || ++ if ((!SCOPE_ACTIVE(config) && ++ (SCOPE_PERSISTENT(config) || SCOPE_AUTOCONF(config))) || + (opts->select->subtype && opts->select->subtype->support_definable)) + existing = 0; + else +@@ -1808,9 +1855,10 @@ static exit_code_t configure_devices(str + goto next; + + /* Configure actual target device. */ +- if (opts->apply) +- rc = cfg_apply(sel->st, sel->id, 0, &dev, &proc); +- else { ++ if (opts->apply) { ++ rc = cfg_apply(sel->st, sel->id, 0, &dev, &proc, ++ opts->auto_conf); ++ } else { + rc = cfg_configure(sel->st, sel->id, opts, 0, + 0, &dev, &proc); + } +@@ -1906,7 +1954,7 @@ static exit_code_t deconfigure_one_devic + dev->processed = 1; + + if (try && !(dev->active.exists || dev->active.definable || +- dev->persistent.exists)) { ++ dev->persistent.exists || dev->autoconf.exists)) { + /* Attempt to deconfigure this device will fail. */ + return EXIT_DEVICE_NOT_FOUND; + } +@@ -1916,7 +1964,8 @@ static exit_code_t deconfigure_one_devic + * dev. */ + if (SCOPE_ACTIVE(config) && + !(dev->active.exists || dev->active.definable)) { +- if (!SCOPE_PERSISTENT(config) || !dev->persistent.exists) ++ if (!((SCOPE_PERSISTENT(config) && dev->persistent.exists) || ++ (SCOPE_AUTOCONF(config) && dev->autoconf.exists))) + return EXIT_DEVICE_NOT_FOUND; + } + +@@ -1940,7 +1989,12 @@ static exit_code_t deconfigure_one_devic + dev->persistent.deconfigured = 1; + dev->persistent.modified = 1; + } +- if (!dev->active.modified && !dev->persistent.modified) ++ if (SCOPE_AUTOCONF(config) && dev->autoconf.exists) { ++ dev->autoconf.deconfigured = 1; ++ dev->autoconf.modified = 1; ++ } ++ if (!dev->active.modified && !dev->persistent.modified && ++ !dev->autoconf.modified) + return EXIT_OK; + + /* Pre-write check. */ +@@ -2084,7 +2138,8 @@ static void print_type_config_info(struc + + changes = setting_get_changes( + SCOPE_ACTIVE(config) ? dt->active_settings : NULL, +- SCOPE_PERSISTENT(config) ? dt->persistent_settings : NULL); ++ SCOPE_PERSISTENT(config) ? dt->persistent_settings : NULL, ++ NULL); + if (changes) + info(" %s: %s\n", title, changes); + free(changes); +@@ -2498,6 +2553,8 @@ static void action_note(const char *msg, + info("%s the active configuration only\n", msg); + else if (config == config_persistent) + info("%s the persistent configuration only\n", msg); ++ else if (config == config_autoconf) ++ info("%s the auto-configuration only\n", msg); + } + + /* Export configuration of selected devices and/or device type to file. */ +@@ -2619,7 +2676,7 @@ static exit_code_t import_device(struct + struct subtype *st = dev->subtype; + exit_code_t rc; + config_t config; +- int proc = 0, active, persistent; ++ int proc = 0, active, persistent, autoconf; + + if (SCOPE_ACTIVE(opts->config)) + active = (dev->active.exists || dev->active.definable); +@@ -2629,7 +2686,16 @@ static exit_code_t import_device(struct + persistent = dev->persistent.exists; + else + persistent = 0; +- config = get_config(active, persistent); ++ if (SCOPE_AUTOCONF(opts->config)) ++ autoconf = dev->autoconf.exists; ++ else ++ autoconf = 0; ++ ++ if (!active && !persistent && !autoconf) { ++ /* Nothing to import */ ++ return EXIT_OK; ++ } ++ config = get_config(active, persistent, autoconf); + + /* First check for prerequisite devices that need to be configured. */ + rc = cfg_prereqs(st, dev->id, opts, config, 0); +@@ -2656,17 +2722,9 @@ static bool import_device_selected(struc + if (!select_opts_dev_specified(select)) + return false; + +- /* --active */ +- if (opts->config == config_active) { +- if (!dev->active.exists && !dev->active.definable) +- return false; +- } +- +- /* --persistent */ +- if (opts->config == config_persistent) { +- if (!dev->persistent.exists) +- return false; +- } ++ /* Target configuration */ ++ if ((device_get_config(dev) & opts->config) == 0) ++ return false; + + /* Devtype */ + if (select->devtype && dt != select->devtype) +--- a/zdev/src/chzdev_usage.txt ++++ b/zdev/src/chzdev_usage.txt +@@ -55,5 +55,6 @@ OPTIONS + --dry-run Display changes without applying + --base PATH Use PATH as base for accessing files + --no-settle Do not wait for udev to settle ++ --auto-conf Apply changes to auto-configuration only + -V, --verbose Print additional run-time information + -q, --quiet Print only minimal run-time information +--- a/zdev/src/dasd.c ++++ b/zdev/src/dasd.c +@@ -316,34 +316,34 @@ static struct attrib dasd_attr_safe_offl + * DASD subtype methods. + */ + +-/* Check if use_diag setting can be correctly applied. */ +-static exit_code_t check_use_diag(struct device *dev, config_t config) ++static bool _check_use_diag(struct setting_list *list) + { + struct setting *u; +- int zvm = is_zvm(); + +- if (SCOPE_ACTIVE(config)) { +- u = setting_list_find(dev->active.settings, +- dasd_attr_use_diag.name); +- if (u && u->modified) { +- if (u->value && atoi(u->value) == 1 && !zvm) { +- delayed_warn("Cannot set 'use_diag=1' on " +- "non-z/VM system\n"); +- return EXIT_INVALID_CONFIG; +- } +- } +- } +- if (SCOPE_PERSISTENT(config)) { +- u = setting_list_find(dev->persistent.settings, +- dasd_attr_use_diag.name); +- if (u && u->modified) { +- if (u->value && atoi(u->value) == 1 && !zvm) { +- delayed_warn("Cannot set 'use_diag=1' on " +- "non-z/VM system\n"); +- return EXIT_INVALID_CONFIG; +- } ++ u = setting_list_find(list, dasd_attr_use_diag.name); ++ if (u && u->modified) { ++ if (u->value && atoi(u->value) == 1) { ++ delayed_warn("Cannot set 'use_diag=1' on non-z/VM system\n"); ++ return false; + } + } ++ return true; ++} ++ ++/* Check if use_diag setting can be correctly applied. */ ++static exit_code_t check_use_diag(struct device *dev, config_t config) ++{ ++ if (is_zvm()) ++ return EXIT_OK; ++ ++ if (SCOPE_ACTIVE(config) && !_check_use_diag(dev->active.settings)) ++ return EXIT_INVALID_CONFIG; ++ if (SCOPE_PERSISTENT(config) && ++ !_check_use_diag(dev->persistent.settings)) ++ return EXIT_INVALID_CONFIG; ++ if (SCOPE_AUTOCONF(config) && ++ !_check_use_diag(dev->autoconf.settings)) ++ return EXIT_INVALID_CONFIG; + + return EXIT_OK; + } +@@ -376,6 +376,8 @@ static void dasd_st_add_modules(struct s + dasd_attr_use_diag.name, &changed, &aset); + setting_list_get_bool_state(dev->persistent.settings, + dasd_attr_use_diag.name, &changed, &pset); ++ setting_list_get_bool_state(dev->autoconf.settings, ++ dasd_attr_use_diag.name, &changed, &pset); + + if (aset || pset) + strlist_add_unique(modules, DASD_DIAG_MOD_NAME); +--- a/zdev/src/device.c ++++ b/zdev/src/device.c +@@ -40,6 +40,7 @@ struct device *device_new(struct subtype + + dev->active.settings = setting_list_new(); + dev->persistent.settings = setting_list_new(); ++ dev->autoconf.settings = setting_list_new(); + + return dev; + } +@@ -53,6 +54,7 @@ void device_free(struct device *dev) + free(dev->devid); + setting_list_free(dev->active.settings); + setting_list_free(dev->persistent.settings); ++ setting_list_free(dev->autoconf.settings); + free(dev); + } + +@@ -83,6 +85,15 @@ void device_print(struct device *dev, in + setting_list_print(dev->persistent.settings, level + 8); + else + printf("%*s\n", level + 8, ""); ++ ++ printf("%*sautoconf:\n", level + 4, ""); ++ printf("%*sexists=%d mod=%d deconf=%d\n", ++ level + 8, "", dev->autoconf.exists, dev->autoconf.modified, ++ dev->autoconf.deconfigured); ++ if (dev->autoconf.settings) ++ setting_list_print(dev->autoconf.settings, level + 8); ++ else ++ printf("%*s\n", level + 8, ""); + } + + static const void *device_hash_get_id(void *dev_ptr) +@@ -182,6 +193,10 @@ bool device_needs_writing(struct device + (dev->persistent.modified || dev->persistent.deconfigured || + setting_list_modified(dev->persistent.settings))) + return true; ++ if (SCOPE_AUTOCONF(config) && ++ (dev->autoconf.modified || dev->autoconf.deconfigured || ++ setting_list_modified(dev->autoconf.settings))) ++ return true; + + return false; + } +@@ -204,6 +219,8 @@ static exit_code_t apply_setting(struct + /* Check for activeonly. */ + if (!force && SCOPE_PERSISTENT(config) && a->activeonly) + goto err_activeonly_forceable; ++ if (!force && SCOPE_AUTOCONF(config) && a->activeonly) ++ goto err_activeonly_forceable; + /* Check for multiple values. */ + if (!force && !a->multi && strlist_find(processed, key)) + goto err_multi_forceable; +@@ -231,8 +248,15 @@ static exit_code_t apply_setting(struct + a, key, value); + } + ++ /* Apply to autoconf config. */ ++ if (SCOPE_AUTOCONF(config)) { ++ setting_list_apply_specified(dev->autoconf.settings, ++ a, key, value); ++ } ++ + /* Additional warning when trying to persist read-only setting. */ +- if (config == config_persistent) { ++ if (!SCOPE_ACTIVE(config) && (SCOPE_PERSISTENT(config) || ++ SCOPE_AUTOCONF(config))) { + s = setting_list_find(dev->active.settings, key); + if (s && s->readonly) + warn_readonly = true; +@@ -341,6 +365,8 @@ void device_reset(struct device *dev, co + reset_device_state(&dev->active); + if (SCOPE_PERSISTENT(config)) + reset_device_state(&dev->persistent); ++ if (SCOPE_AUTOCONF(config)) ++ reset_device_state(&dev->autoconf); + dev->processed = 0; + } + +@@ -567,6 +593,12 @@ exit_code_t device_check_settings(struct + err)) + return EXIT_INVALID_CONFIG; + } ++ if (SCOPE_AUTOCONF(config)) { ++ if (!setting_list_check_conflict(dev->autoconf.settings, ++ config_autoconf, ++ err)) ++ return EXIT_INVALID_CONFIG; ++ } + + return EXIT_OK; + } +@@ -578,8 +610,27 @@ struct setting_list *device_get_setting_ + + if (config == config_active) + settings = dev->active.settings; +- else ++ else if (config == config_persistent) + settings = dev->persistent.settings; ++ else if (config == config_autoconf) ++ settings = dev->autoconf.settings; + + return settings; + } ++ ++/* Return configuration set in which device exists. */ ++config_t device_get_config(struct device *dev) ++{ ++ config_t config = 0; ++ ++ if (dev->active.exists || dev->active.definable) ++ config |= config_active; ++ ++ if (dev->persistent.exists) ++ config |= config_persistent; ++ ++ if (dev->autoconf.exists) ++ config |= config_autoconf; ++ ++ return config; ++} +--- a/zdev/src/export.c ++++ b/zdev/src/export.c +@@ -83,7 +83,7 @@ static bool is_exportable(struct setting + return true; + } + } +- if (SCOPE_PERSISTENT(config)) { ++ if (SCOPE_PERSISTENT(config) || SCOPE_AUTOCONF(config)) { + if (setting_is_set(s)) + return true; + } +@@ -148,10 +148,9 @@ static int count_exportable(struct devic + struct setting *s; + int count; + +- if (config == config_active) +- list = dev->active.settings; +- else +- list = dev->persistent.settings; ++ list = device_get_setting_list(dev, config); ++ if (!list) ++ return 0; + count = 0; + + util_list_iterate(&list->list, s) { +@@ -169,12 +168,26 @@ exit_code_t export_write_device(FILE *fd + exit_code_t rc; + struct setting_list *settings; + +- if (config == config_all) { +- rc = export_write_device(fd, dev, config_active, first_ptr); +- if (rc) +- return rc; +- return export_write_device(fd, dev, config_persistent, +- first_ptr); ++ if (!SCOPE_SINGLE(config)) { ++ if (SCOPE_ACTIVE(config)) { ++ rc = export_write_device(fd, dev, config_active, ++ first_ptr); ++ if (rc) ++ return rc; ++ } ++ if (SCOPE_PERSISTENT(config)) { ++ rc = export_write_device(fd, dev, config_persistent, ++ first_ptr); ++ if (rc) ++ return rc; ++ } ++ if (SCOPE_AUTOCONF(config)) { ++ rc = export_write_device(fd, dev, config_autoconf, ++ first_ptr); ++ if (rc) ++ return rc; ++ } ++ return EXIT_OK; + } + + if (config == config_active) { +@@ -185,10 +198,14 @@ exit_code_t export_write_device(FILE *fd + !dev->subtype->support_definable) + return EXIT_OK; + settings = dev->active.settings; +- } else { ++ } else if (config == config_persistent) { + if (!dev->persistent.exists) + return EXIT_OK; + settings = dev->persistent.settings; ++ } else { ++ if (!dev->autoconf.exists) ++ return EXIT_OK; ++ settings = dev->autoconf.settings; + } + + write_header(fd, config, dev->subtype->name, dev->id, first_ptr); +@@ -204,12 +221,21 @@ exit_code_t export_write_devtype(FILE *f + exit_code_t rc; + struct setting_list *settings; + +- if (config == config_all) { +- rc = export_write_devtype(fd, dt, config_active, first_ptr); +- if (rc) +- return rc; +- return export_write_devtype(fd, dt, config_persistent, +- first_ptr); ++ if (!SCOPE_SINGLE(config)) { ++ if (SCOPE_ACTIVE(config)) { ++ rc = export_write_devtype(fd, dt, config_active, ++ first_ptr); ++ if (rc) ++ return rc; ++ } ++ if (SCOPE_PERSISTENT(config)) { ++ rc = export_write_devtype(fd, dt, config_persistent, ++ first_ptr); ++ if (rc) ++ return rc; ++ } ++ /* Scope autoconf not supported for devtype */ ++ return EXIT_OK; + } + + if (config == config_active) +@@ -379,6 +405,10 @@ static exit_code_t handle_header(const c + setting_list_clear(dev->persistent.settings); + dev->persistent.exists = 1; + } ++ if (SCOPE_AUTOCONF(hdr->config)) { ++ setting_list_clear(dev->autoconf.settings); ++ dev->autoconf.exists = 1; ++ } + } + + out: +@@ -397,13 +427,26 @@ static exit_code_t handle_setting(const + struct attrib **attribs, *a; + struct setting_list *list; + +- if (config == config_all) { +- rc = handle_setting(filename, lineno, key, value, dt, dev, +- config_active); +- if (rc) +- return rc; +- return handle_setting(filename, lineno, key, value, dt, dev, +- config_persistent); ++ if (!SCOPE_SINGLE(config)) { ++ if (SCOPE_ACTIVE(config)) { ++ rc = handle_setting(filename, lineno, key, value, dt, ++ dev, config_active); ++ if (rc) ++ return rc; ++ } ++ if (SCOPE_PERSISTENT(config)) { ++ rc = handle_setting(filename, lineno, key, value, dt, ++ dev, config_persistent); ++ if (rc) ++ return rc; ++ } ++ if (SCOPE_AUTOCONF(config)) { ++ rc = handle_setting(filename, lineno, key, value, dt, ++ dev, config_autoconf); ++ if (rc) ++ return rc; ++ } ++ return EXIT_OK; + } + + if (dt) { +@@ -452,7 +495,8 @@ static bool empty_device(struct device * + if (dev->subtype->support_definable) + return false; + if (util_list_is_empty(&dev->active.settings->list) && +- util_list_is_empty(&dev->persistent.settings->list)) ++ util_list_is_empty(&dev->persistent.settings->list) && ++ util_list_is_empty(&dev->autoconf.settings->list)) + return true; + return false; + } +--- a/zdev/src/firmware.c ++++ b/zdev/src/firmware.c +@@ -360,6 +360,8 @@ static void add_setting(const char *file + _add_setting(filename, dev, config_active, key, value); + if (SCOPE_PERSISTENT(config)) + _add_setting(filename, dev, config_persistent, key, value); ++ if (SCOPE_AUTOCONF(config)) ++ _add_setting(filename, dev, config_autoconf, key, value); + } + + /* Parse a single device setting in firmware format and apply it to the +@@ -477,6 +479,10 @@ static struct device *add_device(struct + setting_list_clear(dev->persistent.settings); + dev->persistent.exists = 1; + } ++ if (SCOPE_AUTOCONF(config)) { ++ setting_list_clear(dev->autoconf.settings); ++ dev->autoconf.exists = 1; ++ } + + return dev; + } +--- a/zdev/src/lszdev.c ++++ b/zdev/src/lszdev.c +@@ -59,6 +59,7 @@ struct options { + config_t config; + unsigned int active:1; + unsigned int persistent:1; ++ unsigned int auto_conf:1; + struct util_list *columns; /* List of struct strlist_node */ + unsigned int no_headings:1; + struct util_list *base; /* List of struct strlist_node */ +@@ -98,6 +99,7 @@ enum { + OPT_VERBOSE = 'V', + OPT_QUIET = 'q', + OPT_PAIRS = 'P', ++ OPT_AUTO_CONF = (OPT_ANONYMOUS_BASE+__COUNTER__), + }; + + static struct opts_conflict conflict_list[] = { +@@ -150,6 +152,7 @@ static const struct option opt_list[] = + /* Options. */ + { "active", no_argument, NULL, OPT_ACTIVE }, + { "persistent", no_argument, NULL, OPT_PERSISTENT }, ++ { "auto-conf", no_argument, NULL, OPT_AUTO_CONF }, + { "columns", required_argument, NULL, OPT_COLUMNS }, + { "no-headings", no_argument, NULL, OPT_NO_HEADINGS }, + { "base", required_argument, NULL, OPT_BASE }, +@@ -623,6 +626,11 @@ static exit_code_t parse_options(struct + opts->persistent = 1; + break; + ++ case OPT_AUTO_CONF: ++ /* --auto-conf */ ++ opts->auto_conf = 1; ++ break; ++ + case OPT_COLUMNS: + /* --columns COLUMN */ + strlist_add_multi(opts->columns, optarg, ",", 0); +@@ -685,7 +693,15 @@ static exit_code_t parse_options(struct + goto out; + + /* Determine configuration set. */ +- opts->config = get_config(opts->active, opts->persistent); ++ if (!opts->active && !opts->persistent && !opts->auto_conf) { ++ /* Default display targets are active + persistent - note that ++ * autoconf data is still shown when available to make users ++ * aware of this type of data. */ ++ opts->config = config_active | config_persistent; ++ } else { ++ opts->config = get_config(opts->active, opts->persistent, ++ opts->auto_conf); ++ } + + /* Handle positional parameters. */ + rc = parse_positional(opts, argc, argv, optind); +@@ -725,6 +741,7 @@ enum dev_table_id { + dev_netdevs, + dev_exists, + dev_pers, ++ dev_auto, + dev_online, + dev_failed, + dev_modules, +@@ -744,6 +761,8 @@ static struct column *dev_table = COLUMN + "Device exists in the active configuration"), + COLUMN("PERS", align_left, dev_pers, 1, + "Device is configured persistently"), ++ COLUMN("AUTO", align_left, dev_auto, 0, ++ "Auto-configuration exists for device"), + COLUMN("FAILED", align_left, dev_failed, 0, + "Device is in error"), + COLUMN("NAMES", align_left, dev_names, 1, +@@ -762,21 +781,34 @@ static struct column *dev_table = COLUMN + "Path to specific attribute in active configuration") + ); + +-static char *merge_str(const char *act, const char *pers) ++static char *merge_str(const char *act, const char *pers, const char *ac, ++ config_t config) + { + char *str; ++ size_t len; + +- if (act && pers) { +- if (strcmp(act, pers) == 0) +- str = misc_strdup(act); +- else +- str = misc_asprintf("%s/%s", act, pers); +- } else if (act) +- str = misc_strdup(act); +- else if (pers) +- str = misc_strdup(pers); +- else +- str = misc_strdup("-"); ++ act = act ? act : "-"; ++ pers = pers ? pers : "-"; ++ ac = ac ? ac : "-"; ++ ++ if (strcmp(act, pers) == 0 && strcmp(act, ac) == 0) ++ return misc_strdup(act); ++ ++ len = strlen(act) + strlen(pers) + strlen(ac) + /* 3 * / + NUL */ 4; ++ str = misc_malloc(len); ++ ++ if (SCOPE_ACTIVE(config)) ++ strcat(str, act); ++ if (SCOPE_PERSISTENT(config)) { ++ if (*str) ++ strcat(str, "/"); ++ strcat(str, pers); ++ } ++ if (SCOPE_AUTOCONF(config)) { ++ if (*str) ++ strcat(str, "/"); ++ strcat(str, ac); ++ } + + return str; + } +@@ -821,10 +853,9 @@ static char *get_attr(struct device *dev + struct setting_list *list; + struct setting *s; + +- if (config == config_active) +- list = dev->active.settings; +- else +- list = dev->persistent.settings; ++ list = device_get_setting_list(dev, config); ++ if (!list) ++ return NULL; + s = setting_list_find(list, name); + if (!s) { + if (config == config_active) { +@@ -841,7 +872,7 @@ static char *get_attr(struct device *dev + static char *dev_table_get_attr(struct device *dev, const char *attr, + config_t config) + { +- char *act = NULL, *pers = NULL, *str; ++ char *act = NULL, *pers = NULL, *ac = NULL, *str; + const char *name; + + name = strchr(attr, ':'); +@@ -853,10 +884,13 @@ static char *dev_table_get_attr(struct d + act = get_attr(dev, name, config_active); + if (SCOPE_PERSISTENT(config)) + pers = get_attr(dev, name, config_persistent); ++ if (SCOPE_AUTOCONF(config)) ++ ac = get_attr(dev, name, config_autoconf); + +- str = merge_str(act, pers); ++ str = merge_str(act, pers, ac, config); + free(act); + free(pers); ++ free(ac); + + return str; + } +@@ -904,7 +938,11 @@ static char *dev_table_get_value(void *i + return misc_strdup(YESNO(dev->active.exists || + dev->active.definable)); + case dev_pers: ++ if (!dev->persistent.exists && dev->autoconf.exists) ++ return misc_strdup("auto"); + return misc_strdup(YESNO(dev->persistent.exists)); ++ case dev_auto: ++ return misc_strdup(YESNO(dev->autoconf.exists)); + case dev_online: + return dev_table_get_online(dev, config_active); + case dev_failed: +@@ -942,6 +980,7 @@ static read_scope_t get_scope(struct opt + case dev_netdevs: + case dev_exists: + case dev_pers: ++ case dev_auto: + case dev_online: + case dev_modules: + continue; +@@ -960,14 +999,21 @@ static struct util_list *dev_table_build + struct util_list *selected, *devices = NULL; + struct selected_dev_node *sel; + struct device *dev; +- int active, persistent; ++ int active, persistent, autoconf; + read_scope_t scope; + exit_code_t rc; ++ config_t config = opts->config; + + scope = get_scope(opts); + selected = selected_dev_list_new(); ++ ++ /* Read auto-config data when no configuration set was specified to ++ * make user aware of auto-config data. */ ++ if (!opts->active && !opts->persistent && !opts->auto_conf) ++ config |= config_autoconf; ++ + rc = select_devices(opts->select, selected, 1, 0, opts->pairs, +- opts->config, scope, err_print); ++ config, scope, err_print); + if (rc) + goto out; + devices = ptrlist_new(); +@@ -987,7 +1033,8 @@ static struct util_list *dev_table_build + /* Only process existing devices. */ + active = dev->active.exists || dev->active.definable; + persistent = dev->persistent.exists; +- if (!active && !persistent) ++ autoconf = dev->autoconf.exists; ++ if (!active && !persistent && !autoconf) + continue; + + ptrlist_add(devices, dev); +@@ -1025,9 +1072,11 @@ static exit_code_t do_list_devices(struc + if (!items) + return EXIT_EMPTY_SELECTION; + +- /* Adjust columns visible depending on --active and --persistent. */ ++ /* Adjust columns visible depending on selected config set. */ + table_set_default(dev_table, dev_online, SCOPE_ACTIVE(opts->config)); + table_set_default(dev_table, dev_pers, SCOPE_PERSISTENT(opts->config)); ++ table_set_default(dev_table, dev_auto, SCOPE_AUTOCONF(opts->config) && ++ !SCOPE_PERSISTENT(opts->config)); + table_set_default(dev_table, dev_names, SCOPE_ACTIVE(opts->config)); + + /* Display table. */ +@@ -1057,17 +1106,20 @@ enum settings_table_id { + settings_readonly, + settings_active, + settings_persistent, ++ settings_autoconf, + }; + + static struct column *settings_table = COLUMN_ARRAY( + COLUMN("ATTRIBUTE", align_left, settings_attribute, 1, ""), + COLUMN("READONLY", align_left, settings_readonly, 1, ""), + COLUMN("ACTIVE", align_left, settings_active, 1, ""), +- COLUMN("PERSISTENT", align_left, settings_persistent, 1, "") ++ COLUMN("PERSISTENT", align_left, settings_persistent, 1, ""), ++ COLUMN("AUTOCONF", align_left, settings_autoconf, 0, "") + ); + + static struct util_list *settings_table_build(struct setting_list *active, + struct setting_list *persistent, ++ struct setting_list *autoconf, + bool readonly) + { + struct util_list *names, *items; +@@ -1088,6 +1140,10 @@ static struct util_list *settings_table_ + util_list_iterate(&persistent->list, s) + strlist_add(names, s->name); + } ++ if (autoconf && !readonly) { ++ util_list_iterate(&autoconf->list, s) ++ strlist_add(names, s->name); ++ } + strlist_sort_unique(names, str_cmp); + + /* Convert strlist to ptrlist. */ +@@ -1102,6 +1158,7 @@ static struct util_list *settings_table_ + struct settings_table_data { + struct setting_list *active; + struct setting_list *persistent; ++ struct setting_list *autoconf; + int pairs; + bool readonly; + }; +@@ -1124,12 +1181,17 @@ static char *settings_table_get_value(vo + case settings_persistent: + list = stdata->persistent; + break; ++ case settings_autoconf: ++ list = stdata->autoconf; ++ break; + default: + break; + } + if (list) { + s = setting_list_find(list, name); +- if (s && !(id == settings_persistent && s->derived)) { ++ if (s && ++ !((id == settings_persistent || id == settings_autoconf) && ++ s->derived)) { + if (stdata->pairs) + return misc_strdup(s->value); + else +@@ -1145,6 +1207,7 @@ static char *settings_table_get_value(vo + + static void settings_table_print(struct setting_list *active, + struct setting_list *persistent, ++ struct setting_list *autoconf, + struct options *opts, int ind, bool readonly, + bool neednl) + { +@@ -1154,7 +1217,7 @@ static void settings_table_print(struct + items = settings_table_build( + SCOPE_ACTIVE(opts->config) ? active : NULL, + SCOPE_PERSISTENT(opts->config) ? persistent : NULL, +- readonly); ++ autoconf, readonly); + if (util_list_is_empty(items)) { + if (!opts->pairs && !readonly) + indent(ind, "%sNo settings found\n", +@@ -1170,8 +1233,12 @@ static void settings_table_print(struct + SCOPE_ACTIVE(opts->config)); + table_set_default(settings_table, settings_persistent, + SCOPE_PERSISTENT(opts->config) && !readonly ? 1 : 0); ++ table_set_default(settings_table, settings_autoconf, ++ (SCOPE_AUTOCONF(opts->config) || autoconf) && ++ !readonly ? 1 : 0); + data.active = active; + data.persistent = persistent; ++ data.autoconf = autoconf; + data.pairs = opts->pairs; + table_print(settings_table, settings_table_get_value, &data, items, + NULL, 1, opts->pairs, ind, 0); +@@ -1253,7 +1320,7 @@ static exit_code_t do_info_type(struct o + } + } else { + settings_table_print(dt->active_settings, +- dt->persistent_settings, opts, ++ dt->persistent_settings, NULL, opts, + INFO_INDENT, false, false); + } + +@@ -1280,7 +1347,7 @@ static void do_info_one_device(struct de + { + struct subtype *st = dev->subtype; + char *names, *bdevs, *cdevs, *ndevs, *modules, *online, *path, *key; +- const char *id, *type, *exists, *persist, *prefix; ++ const char *id, *type, *exists, *persist, *ac, *prefix; + struct util_list *resources, *errors; + struct strlist_node *s; + bool first; +@@ -1299,6 +1366,7 @@ static void do_info_one_device(struct de + errors = subtype_get_errors(dev->subtype, dev->id); + exists = YESNO(dev->active.exists || dev->active.definable); + persist = YESNO(dev->persistent.exists); ++ ac = YESNO(dev->autoconf.exists); + + /* Print information. */ + if (opts->pairs) { +@@ -1316,6 +1384,8 @@ static void do_info_one_device(struct de + print_pair("ONLINE", online); + print_pair("EXISTS", exists); + print_pair("PERSISTENT", persist); ++ if (SCOPE_AUTOCONF(opts->config) || dev->autoconf.exists) ++ print_pair("AUTOCONF", ac); + if (errors) { + util_list_iterate(errors, s) + print_pair("ERROR", s->str); +@@ -1341,6 +1411,8 @@ static void do_info_one_device(struct de + print_info("Online", online); + print_info("Exists", exists); + print_info("Persistent", persist); ++ if (SCOPE_AUTOCONF(opts->config) || dev->autoconf.exists) ++ print_info("Auto-configured", ac); + if (errors) { + first = true; + util_list_iterate(errors, s) { +@@ -1382,11 +1454,17 @@ static void do_info_one_device(struct de + + settings_table_print(dev->active.exists ? dev->active.settings : NULL, + dev->persistent.exists ? dev->persistent.settings : +- NULL, opts, INFO_INDENT, false, !opts->pairs); ++ NULL, ++ dev->autoconf.exists ? dev->autoconf.settings : ++ NULL, ++ opts, INFO_INDENT, false, !opts->pairs); + + settings_table_print(dev->active.exists ? dev->active.settings : NULL, + dev->persistent.exists ? dev->persistent.settings : +- NULL, opts, INFO_INDENT, true, !opts->pairs); ++ NULL, ++ dev->autoconf.exists ? dev->autoconf.settings : ++ NULL, ++ opts, INFO_INDENT, true, !opts->pairs); + + strlist_free(errors); + free(online); +@@ -1404,9 +1482,10 @@ static exit_code_t do_info_devices(struc + struct util_list *selected; + struct selected_dev_node *sel; + struct device *dev; +- int found, active, persistent; ++ int found, active, persistent, autoconf; + exit_code_t rc, drc = EXIT_OK; + read_scope_t scope; ++ config_t config = opts->config; + + if (opts->info > 1) + scope = scope_all; +@@ -1415,7 +1494,13 @@ static exit_code_t do_info_devices(struc + + /* Get list of selected devices. */ + selected = selected_dev_list_new(); +- select_devices(opts->select, selected, 1, 0, opts->pairs, opts->config, ++ ++ /* Read auto-config data when no configuration set was specified to ++ * make user aware of auto-config data. */ ++ if (!opts->active && !opts->persistent && !opts->auto_conf) ++ config |= config_autoconf; ++ ++ select_devices(opts->select, selected, 1, 0, opts->pairs, config, + scope, err_print); + + /* Process selected devices. */ +@@ -1434,9 +1519,8 @@ static exit_code_t do_info_devices(struc + /* Only process existing devices. */ + active = dev->active.exists || dev->active.definable; + persistent = dev->persistent.exists; +- if ((opts->config == config_active && !active) || +- (opts->config == config_persistent && !persistent) || +- (opts->config == config_all && !active && !persistent)) ++ autoconf = dev->autoconf.exists; ++ if (!active && !persistent && !autoconf) + continue; + if (found > 0) + printf("\n"); +--- a/zdev/src/lszdev_usage.txt ++++ b/zdev/src/lszdev_usage.txt +@@ -41,5 +41,6 @@ OPTIONS + -n, --no-headings Do not print column headings + --base PATH Use PATH as base for accessing files + --pairs Produce output in KEY="VALUE" format ++ --auto-conf Only show auto-configuration data + -V, --verbose Print additional run-time information + -q, --quiet Print only minimal run-time information +--- a/zdev/src/misc.c ++++ b/zdev/src/misc.c +@@ -1107,14 +1107,18 @@ char *misc_readlink(const char *path) + } + + /* Determine configuration set. */ +-config_t get_config(int active, int persistent) ++config_t get_config(int active, int persistent, int autoconf) + { +- if ((!active && !persistent) || (active && persistent)) +- return config_all; +- else if (active) +- return config_active; +- else +- return config_persistent; ++ config_t config = 0; ++ ++ if (active) ++ config |= config_active; ++ if (persistent) ++ config |= config_persistent; ++ if (autoconf) ++ config |= config_autoconf; ++ ++ return config; + } + + /* Determine if program is running under z/VM. */ +@@ -1248,11 +1252,12 @@ const char *config_to_str(config_t confi + return "active"; + case config_persistent: + return "persistent"; ++ case config_autoconf: ++ return "autoconf"; + case config_all: + return "all"; + } +- /* Should not happen. */ +- return ""; ++ return "multiple"; + } + + /* Convert textual config representation to config_t. */ +@@ -1262,6 +1267,8 @@ bool str_to_config(const char *str, conf + *config_ptr = config_active; + else if (strcasecmp(str, "persistent") == 0) + *config_ptr = config_persistent; ++ else if (strcasecmp(str, "autoconf") == 0) ++ *config_ptr = config_autoconf; + else if (strcasecmp(str, "all") == 0) + *config_ptr = config_all; + else +--- a/zdev/src/path.c ++++ b/zdev/src/path.c +@@ -297,21 +297,25 @@ char *path_get_ccwgroup_devices(const ch + } + + /* Return path to udev rule. */ +-char *path_get_udev_rule(const char *type, const char *id) ++char *path_get_udev_rule(const char *type, const char *id, bool vol) + { ++ const char *path = vol ? PATH_UDEV_RULES_VOLATILE : PATH_UDEV_RULES; ++ + if (id) { +- return path_get("%s/%s-%s-%s%s", PATH_UDEV_RULES, ++ return path_get("%s/%s-%s-%s%s", path, + UDEV_PREFIX, type, id, UDEV_SUFFIX); + } + +- return path_get("%s/%s-%s%s", PATH_UDEV_RULES, ++ return path_get("%s/%s-%s%s", path, + UDEV_PREFIX, type, UDEV_SUFFIX); + } + + /* Return path to directory containing all udev rules. */ +-char *path_get_udev_rules(void) ++char *path_get_udev_rules(bool vol) + { +- return path_get("%s", PATH_UDEV_RULES); ++ const char *path = vol ? PATH_UDEV_RULES_VOLATILE : PATH_UDEV_RULES; ++ ++ return path_get("%s", path); + } + + /* Return path to the specified file in the proc file system. */ +--- a/zdev/src/qeth.c ++++ b/zdev/src/qeth.c +@@ -1207,7 +1207,7 @@ static exit_code_t check_layer2(struct d + { + struct setting *l; + int layer2_detected, layer2_active = -1, layer2_persistent = -1, +- layer2_modified = 0; ++ layer2_autoconf = -1, layer2_modified = 0; + exit_code_t rc = EXIT_OK; + + layer2_detected = detect_layer2(dev); +@@ -1231,13 +1231,25 @@ static exit_code_t check_layer2(struct d + if (rc) + goto out; + } ++ l = setting_list_find(dev->autoconf.settings, qeth_attr_layer2.name); ++ if (l) { ++ layer2_autoconf = atoi(l->value); ++ layer2_modified |= l->modified; ++ } else if (SCOPE_AUTOCONF(config)) { ++ rc = generate_layer2("autoconf", dev->autoconf.settings, ++ &layer2_autoconf, &layer2_modified); ++ if (rc) ++ goto out; ++ } + + /* Check correct layer2 setting. */ + if (layer2_detected != -1) { + if ((SCOPE_ACTIVE(config) && layer2_active != -1 && + layer2_active != layer2_detected) || + (SCOPE_PERSISTENT(config) && layer2_persistent != -1 && +- layer2_persistent != layer2_detected)) { ++ layer2_persistent != layer2_detected) || ++ (SCOPE_AUTOCONF(config) && layer2_autoconf != -1 && ++ layer2_autoconf != layer2_detected)) { + rc = layer2_mismatch(layer2_detected, layer2_modified); + if (rc) + goto out; +@@ -1256,14 +1268,25 @@ static exit_code_t check_layer2(struct d + if (rc) + goto out; + } ++ if (SCOPE_AUTOCONF(config)) { ++ rc = check_ineffective_settings(dev->autoconf.settings, ++ layer2_autoconf); ++ if (rc) ++ goto out; ++ } + /* check for conflicting layer2 attribute groups */ + if (SCOPE_ACTIVE(config)) { + rc = check_conflicting_settings(dev->active.settings); + if (rc) + goto out; + } +- if (SCOPE_PERSISTENT(config)) ++ if (SCOPE_PERSISTENT(config)) { + rc = check_conflicting_settings(dev->persistent.settings); ++ if (rc) ++ goto out; ++ } ++ if (SCOPE_AUTOCONF(config)) ++ rc = check_conflicting_settings(dev->autoconf.settings); + + out: + return rc; +@@ -1277,7 +1300,8 @@ static exit_code_t qeth_st_check_pre_con + + /* No need to check if device is deconfigured. */ + if ((SCOPE_ACTIVE(config) && dev->active.deconfigured) || +- (SCOPE_PERSISTENT(config) && dev->persistent.deconfigured)) ++ (SCOPE_PERSISTENT(config) && dev->persistent.deconfigured) || ++ (SCOPE_AUTOCONF(config) && dev->autoconf.deconfigured)) + return EXIT_OK; + + rc = check_layer2(dev, config); +--- a/zdev/src/select.c ++++ b/zdev/src/select.c +@@ -248,12 +248,13 @@ static void unblacklist_devices(struct s + bool select_match_state(struct device *dev, struct select_opts *select) + { + struct subtype *st = dev->subtype; +- int exists, configured, online; ++ int exists, configured, autoconf, online; + struct util_list *errors; + + exists = dev->active.exists || dev->active.definable; + configured = dev->persistent.exists; +- if (select->all && !(exists || configured)) ++ autoconf = dev->autoconf.exists; ++ if (select->all && !(exists || configured || autoconf)) + return false; + if (select->existing && !exists) + return false; +@@ -862,7 +863,8 @@ static exit_code_t select_nocreate(struc + select->offline || select->online || + SCOPE_ACTIVE(config), + select->configured || select->all || +- SCOPE_PERSISTENT(config)); ++ SCOPE_PERSISTENT(config), ++ select->all || SCOPE_AUTOCONF(config)); + + longrun_total = 0; + if (quiet) +@@ -870,7 +872,7 @@ static exit_code_t select_nocreate(struc + + /* Count devices. */ + verb("Scanning for devices in %s configuration%s:\n", +- config_to_str(id_config), id_config == config_all ? "s" : ""); ++ config_to_str(id_config), !SCOPE_SINGLE(id_config) ? "s" : ""); + for (i = 0; (dt = devtypes[i]); i++) { + if (select->devtype && dt != select->devtype) + continue; +@@ -1128,6 +1130,13 @@ static exit_code_t by_attr_cb(struct sub + + /* Check for attribute value in persistent configuration. */ + s = setting_list_find(dev->persistent.settings, cb_data->key); ++ if (s) { ++ match = setting_match_value(s, cb_data->value); ++ if (match) ++ goto out; ++ } ++ /* Check for attribute value in autoconf configuration. */ ++ s = setting_list_find(dev->autoconf.settings, cb_data->key); + if (s) + match = setting_match_value(s, cb_data->value); + +--- a/zdev/src/setting.c ++++ b/zdev/src/setting.c +@@ -646,7 +646,8 @@ static void add_changes(struct util_list + + /* Return a newly allocated string containing the setting changes found + * in PERS and ACT or NULL if no change was found. */ +-char *setting_get_changes(struct setting_list *act, struct setting_list *pers) ++char *setting_get_changes(struct setting_list *act, struct setting_list *pers, ++ struct setting_list *ac) + { + struct util_list *out; + struct util_list *processed; +@@ -662,6 +663,17 @@ char *setting_get_changes(struct setting + if (s->removed) + strlist_add(out, "-%s", s->name); + else if (s->modified) ++ add_changes(out, s); ++ else ++ continue; ++ strlist_add(processed, s->name); ++ } ++ } ++ if (ac) { ++ util_list_iterate(&ac->list, s) { ++ if (s->removed) ++ strlist_add(out, "-%s", s->name); ++ else if (s->modified) + add_changes(out, s); + else + continue; +--- a/zdev/src/subtype.c ++++ b/zdev/src/subtype.c +@@ -195,6 +195,13 @@ bool subtype_device_exists_persistent(st + return super->exists_persistent(st, id); + } + ++bool subtype_device_exists_autoconf(struct subtype *st, const char *id) ++{ ++ struct subtype *super = super_get(st, exists_autoconf, 1); ++ ++ return super->exists_autoconf(st, id); ++} ++ + void subtype_add_active_ids(struct subtype *st, struct util_list *ids) + { + struct subtype *super = super_get(st, add_active_ids, 1); +@@ -209,6 +216,13 @@ void subtype_add_persistent_ids(struct s + super->add_persistent_ids(st, ids); + } + ++void subtype_add_autoconf_ids(struct subtype *st, struct util_list *ids) ++{ ++ struct subtype *super = super_get(st, add_autoconf_ids, 1); ++ ++ super->add_autoconf_ids(st, ids); ++} ++ + exit_code_t subtype_device_read_active(struct subtype *st, struct device *dev, + read_scope_t scope) + { +@@ -226,6 +240,15 @@ exit_code_t subtype_device_read_persiste + return super->read_persistent(st, dev, scope); + } + ++exit_code_t subtype_device_read_autoconf(struct subtype *st, ++ struct device *dev, ++ read_scope_t scope) ++{ ++ struct subtype *super = super_get(st, read_autoconf, 1); ++ ++ return super->read_autoconf(st, dev, scope); ++} ++ + exit_code_t subtype_device_configure_active(struct subtype *st, + struct device *dev) + { +@@ -242,6 +265,14 @@ exit_code_t subtype_device_configure_per + return super->configure_persistent(st, dev); + } + ++exit_code_t subtype_device_configure_autoconf(struct subtype *st, ++ struct device *dev) ++{ ++ struct subtype *super = super_get(st, configure_autoconf, 1); ++ ++ return super->configure_autoconf(st, dev); ++} ++ + exit_code_t subtype_device_deconfigure_active(struct subtype *st, + struct device *dev) + { +@@ -258,6 +289,14 @@ exit_code_t subtype_device_deconfigure_p + return super->deconfigure_persistent(st, dev); + } + ++exit_code_t subtype_device_deconfigure_autoconf(struct subtype *st, ++ struct device *dev) ++{ ++ struct subtype *super = super_get(st, deconfigure_autoconf, 1); ++ ++ return super->deconfigure_autoconf(st, dev); ++} ++ + exit_code_t subtype_check_pre_configure(struct subtype *st, struct device *dev, + int prereq, config_t config) + { +@@ -299,7 +338,7 @@ void subtype_online_set(struct subtype * + int subtype_online_get(struct subtype *st, struct device *dev, config_t config) + { + struct subtype *super = super_get(st, online_get, 0); +- int act_online = 1, pers_online = 1; ++ int act_online = 1, pers_online = 1, auto_online = 1; + + if (super) + return super->online_get(st, dev, config); +@@ -310,8 +349,10 @@ int subtype_online_get(struct subtype *s + act_online = dev->active.exists; + if (SCOPE_PERSISTENT(config)) + pers_online = dev->persistent.exists; ++ if (SCOPE_AUTOCONF(config)) ++ auto_online = dev->autoconf.exists; + +- return MIN(act_online, pers_online); ++ return MIN(MIN(act_online, pers_online), auto_online); + } + + bool subtype_online_specified(struct subtype *st, struct device *dev, +@@ -495,6 +536,10 @@ bool subtype_device_exists(struct subtyp + if (!subtype_device_exists_persistent(st, id)) + return false; + } ++ if (SCOPE_AUTOCONF(config)) { ++ if (!subtype_device_exists_autoconf(st, id)) ++ return false; ++ } + + return true; + } +@@ -512,6 +557,8 @@ static struct util_list *get_ids(struct + } + if (SCOPE_PERSISTENT(config)) + subtype_add_persistent_ids(st, ids); ++ if (SCOPE_AUTOCONF(config)) ++ subtype_add_autoconf_ids(st, ids); + + /* Provide a sorted view. */ + strlist_sort_unique(ids, st->namespace->qsort_cmp); +@@ -683,6 +730,19 @@ static exit_code_t read_device(struct su + goto out; + } + ++ if (SCOPE_AUTOCONF(config)) { ++ if (subtype_device_exists_autoconf(st, id)) ++ rc = subtype_device_read_autoconf(st, dev, scope); ++ else if (defined) { ++ /* Need to copy detected settings to autoconf ++ * config. */ ++ setting_list_merge(dev->autoconf.settings, ++ dev->active.settings, false, false); ++ } ++ if (rc) ++ goto out; ++ } ++ + if (add) + device_list_add(st->devices, dev); + +@@ -756,6 +816,25 @@ exit_code_t subtype_write_device(struct + if (!rc) + namespace_set_modified(dev->subtype->namespace); + } ++ if (SCOPE_AUTOCONF(config)) { ++ if (dev->autoconf.deconfigured) { ++ /* Deconfigure device. */ ++ if (dev->autoconf.exists) { ++ rc = subtype_device_deconfigure_autoconf(st, ++ dev); ++ if (rc) ++ return rc; ++ } ++ } else if (dev->autoconf.exists) { ++ /* Configure device. */ ++ rc = subtype_device_configure_autoconf(st, dev); ++ if (rc) ++ return rc; ++ } ++ if (!rc) ++ namespace_set_modified(dev->subtype->namespace); ++ } ++ + + return EXIT_OK; + } +--- a/zdev/src/udev.c ++++ b/zdev/src/udev.c +@@ -360,7 +360,8 @@ static bool get_ids_cb(const char *filen + + /* Add the IDs for all devices of the specified subtype name for which a + * udev rule exists to strlist LIST. */ +-void udev_get_device_ids(const char *type, struct util_list *list) ++void udev_get_device_ids(const char *type, struct util_list *list, ++ bool autoconf) + { + char *path, *prefix; + struct util_list *files; +@@ -369,7 +370,7 @@ void udev_get_device_ids(const char *typ + + prefix = misc_asprintf("%s-%s-", UDEV_PREFIX, type); + plen = strlen(prefix); +- path = path_get_udev_rules(); ++ path = path_get_udev_rules(autoconf); + files = strlist_new(); + if (!misc_read_dir(path, files, get_ids_cb, prefix)) + goto out; +@@ -388,12 +389,12 @@ out: + } + + /* Remove UDEV rule for device. */ +-exit_code_t udev_remove_rule(const char *type, const char *id) ++exit_code_t udev_remove_rule(const char *type, const char *id, bool autoconf) + { + char *path; + exit_code_t rc = EXIT_OK; + +- path = path_get_udev_rule(type, id); ++ path = path_get_udev_rule(type, id, autoconf); + if (util_path_is_reg_file(path)) + rc = remove_file(path); + free(path); +--- a/zdev/src/udev_ccw.c ++++ b/zdev/src/udev_ccw.c +@@ -25,7 +25,7 @@ + #include "udev_ccw.h" + + /* Check if a udev rule for the specified ccw device exists. */ +-bool udev_ccw_exists(const char *type, const char *id) ++bool udev_ccw_exists(const char *type, const char *id, bool autoconf) + { + char *path, *normid; + bool rc; +@@ -34,7 +34,7 @@ bool udev_ccw_exists(const char *type, c + if (!normid) + return false; + +- path = path_get_udev_rule(type, normid); ++ path = path_get_udev_rule(type, normid, autoconf); + rc = util_path_is_reg_file(path); + free(path); + free(normid); +@@ -88,15 +88,16 @@ static void udev_file_get_settings(struc + } + + /* Read the persistent configuration of a CCW device from a udev rule. */ +-exit_code_t udev_ccw_read_device(struct device *dev) ++exit_code_t udev_ccw_read_device(struct device *dev, bool autoconf) + { + struct subtype *st = dev->subtype; +- struct device_state *state = &dev->persistent; ++ struct device_state *state = autoconf ? &dev->autoconf : ++ &dev->persistent; + struct udev_file *file = NULL; + exit_code_t rc; + char *path; + +- path = path_get_udev_rule(st->name, dev->id); ++ path = path_get_udev_rule(st->name, dev->id, autoconf); + rc = udev_read_file(path, &file); + if (rc) + goto out; +@@ -128,12 +129,13 @@ static char *get_label_id(const char *pr + } + + /* Write the persistent configuration of a CCW device to a udev rule. */ +-exit_code_t udev_ccw_write_device(struct device *dev) ++exit_code_t udev_ccw_write_device(struct device *dev, bool autoconf) + { + struct subtype *st = dev->subtype; + struct ccw_subtype_data *data = st->data; + const char *type = st->name, *drv = data->ccwdrv, *id = dev->id; +- struct device_state *state = &dev->persistent; ++ struct device_state *state = autoconf ? &dev->autoconf : ++ &dev->persistent; + char *path, *cfg_label = NULL, *end_label = NULL; + struct util_list *list; + struct ptrlist_node *p; +@@ -142,7 +144,7 @@ exit_code_t udev_ccw_write_device(struct + FILE *fd; + + if (!state->exists) +- return udev_remove_rule(type, id); ++ return udev_remove_rule(type, id, autoconf); + + cfg_label = get_label_id("cfg", type, id); + end_label = get_label_id("end", type, id); +@@ -150,7 +152,7 @@ exit_code_t udev_ccw_write_device(struct + /* Apply attributes in correct order. */ + list = setting_list_get_sorted(state->settings); + +- path = path_get_udev_rule(type, id); ++ path = path_get_udev_rule(type, id, autoconf); + debug("Writing %s udev rule file %s\n", type, path); + if (!util_path_exists(path)) { + rc = path_create(path); +@@ -234,14 +236,18 @@ out: + } + + /* Write a udev rule to free devices from the cio-ignore blacklist. */ +-exit_code_t udev_ccw_write_cio_ignore(const char *id_list) ++exit_code_t udev_ccw_write_cio_ignore(const char *id_list, bool autoconf) + { +- char *path, *curr = NULL; ++ char *path, *prefix, *curr = NULL; + FILE *fd; + exit_code_t rc = EXIT_OK; + ++ /* Ensure that autoconf version of cio-ignore is not masked ++ * by normal one. */ ++ prefix = autoconf ? "cio-ignore-autoconf" : "cio-ignore"; ++ + /* Create file. */ +- path = path_get_udev_rule("cio-ignore", NULL); ++ path = path_get_udev_rule(prefix, NULL, autoconf); + + if (!*id_list) { + /* Empty id_list string - remove file. */ +--- a/zdev/src/udev_ccwgroup.c ++++ b/zdev/src/udev_ccwgroup.c +@@ -25,34 +25,34 @@ + #include "udev_ccwgroup.h" + + static char *get_rule_path_by_devid(const char *type, +- struct ccwgroup_devid *devid) ++ struct ccwgroup_devid *devid, bool autoconf) + { + char *ccw_id, *path; + + ccw_id = ccw_devid_to_str(&devid->devid[0]); +- path = path_get_udev_rule(type, ccw_id); ++ path = path_get_udev_rule(type, ccw_id, autoconf); + free(ccw_id); + + return path; + } + +-static char *get_rule_path(const char *type, const char *id) ++static char *get_rule_path(const char *type, const char *id, bool autoconf) + { + struct ccwgroup_devid devid; + + if (ccwgroup_parse_devid(&devid, id, err_ignore) != EXIT_OK) + return NULL; + +- return get_rule_path_by_devid(type, &devid); ++ return get_rule_path_by_devid(type, &devid, autoconf); + } + + /* Check if a udev rule for the specified CCWGROUP device exists. */ +-bool udev_ccwgroup_exists(const char *type, const char *id) ++bool udev_ccwgroup_exists(const char *type, const char *id, bool autoconf) + { + char *path; + bool rc; + +- path = get_rule_path(type, id); ++ path = get_rule_path(type, id, autoconf); + if (!path) + return false; + rc = util_path_is_reg_file(path); +@@ -143,15 +143,16 @@ static void expand_id(struct device *dev + } + + /* Read the persistent configuration of a CCWGROUP device from a udev rule. */ +-exit_code_t udev_ccwgroup_read_device(struct device *dev) ++exit_code_t udev_ccwgroup_read_device(struct device *dev, bool autoconf) + { + struct subtype *st = dev->subtype; +- struct device_state *state = &dev->persistent; ++ struct device_state *state = autoconf ? &dev->autoconf : ++ &dev->persistent; + struct udev_file *file = NULL; + exit_code_t rc; + char *path; + +- path = get_rule_path_by_devid(st->name, dev->devid); ++ path = get_rule_path_by_devid(st->name, dev->devid, autoconf); + rc = udev_read_file(path, &file); + if (rc) + goto out; +@@ -184,9 +185,11 @@ static char *get_label_id(const char *pr + } + + /* Write the persistent configuration of a CCWGROUP device to a udev rule. */ +-exit_code_t udev_ccwgroup_write_device(struct device *dev) ++exit_code_t udev_ccwgroup_write_device(struct device *dev, bool autoconf) + { + struct subtype *st = dev->subtype; ++ struct device_state *state = autoconf ? &dev->autoconf : ++ &dev->persistent; + struct ccwgroup_subtype_data *data = st->data; + const char *type = st->name, *drv = data->ccwgroupdrv, *id = dev->id; + struct ccwgroup_devid devid; +@@ -200,21 +203,21 @@ exit_code_t udev_ccwgroup_write_device(s + FILE *fd; + unsigned int i; + +- if (!dev->persistent.exists) +- return udev_ccwgroup_remove_rule(type, id); ++ if (!state->exists) ++ return udev_ccwgroup_remove_rule(type, id, autoconf); + + if (ccwgroup_parse_devid(&devid, id, err_ignore) != EXIT_OK) + return EXIT_INVALID_ID; + + ccw_id = ccw_devid_to_str(&devid.devid[0]); +- path = get_rule_path(type, ccw_id); ++ path = get_rule_path(type, ccw_id, autoconf); + + group_label = get_label_id("group", type, ccw_id); + cfg_label = get_label_id("cfg", type, ccw_id); + end_label = get_label_id("end", type, ccw_id); + + /* Apply attributes in correct order. */ +- list = setting_list_get_sorted(dev->persistent.settings); ++ list = setting_list_get_sorted(state->settings); + + debug("Writing %s udev rule file %s\n", type, path); + if (!util_path_exists(path)) { +@@ -357,12 +360,13 @@ static exit_code_t get_ids_cb(const char + + /* Add the IDs for all devices of the specified subtype name for which a + * udev rule exists to strlist LIST. */ +-void udev_ccwgroup_add_device_ids(const char *type, struct util_list *list) ++void udev_ccwgroup_add_device_ids(const char *type, struct util_list *list, ++ bool autoconf) + { + struct get_ids_cb_data cb_data; + char *path; + +- path = path_get_udev_rules(); ++ path = path_get_udev_rules(autoconf); + cb_data.prefix = misc_asprintf("%s-%s-", UDEV_PREFIX, type); + cb_data.ids = list; + +@@ -374,7 +378,8 @@ void udev_ccwgroup_add_device_ids(const + } + + /* Remove UDEV rule for CCWGROUP device. */ +-exit_code_t udev_ccwgroup_remove_rule(const char *type, const char *id) ++exit_code_t udev_ccwgroup_remove_rule(const char *type, const char *id, ++ bool autoconf) + { + char *partial_id, *path; + exit_code_t rc = EXIT_OK; +@@ -383,7 +388,7 @@ exit_code_t udev_ccwgroup_remove_rule(co + if (!partial_id) + return EXIT_INVALID_ID; + +- path = path_get_udev_rule(type, partial_id); ++ path = path_get_udev_rule(type, partial_id, autoconf); + if (util_path_is_reg_file(path)) + rc = remove_file(path); + free(path); +--- a/zdev/src/udev_zfcp_lun.c ++++ b/zdev/src/udev_zfcp_lun.c +@@ -369,14 +369,14 @@ static exit_code_t lun_cb(const char *pa + + /* Add the IDs for all zfcp lun devices for which a configuration exists to + * LIST. */ +-void udev_zfcp_lun_add_device_ids(struct util_list *list) ++void udev_zfcp_lun_add_device_ids(struct util_list *list, bool autoconf) + { + struct lun_cb_data cb_data; + char *path; + + cb_data.prefix = misc_asprintf("%s-%s-", UDEV_PREFIX, ZFCP_LUN_NAME); + cb_data.list = list; +- path = path_get_udev_rules(); ++ path = path_get_udev_rules(autoconf); + + if (util_path_is_dir(path)) + path_for_each(path, lun_cb, &cb_data); +@@ -387,7 +387,7 @@ void udev_zfcp_lun_add_device_ids(struct + + /* Return path to zfcp lun udev rule file containing configuration data for + * all LUNs of a zfcp device. */ +-static char *get_zfcp_lun_path(const char *id) ++static char *get_zfcp_lun_path(const char *id, bool autoconf) + { + char *copy, *e, *path; + +@@ -395,7 +395,7 @@ static char *get_zfcp_lun_path(const cha + e = strchr(copy, ':'); + if (e) + *e = 0; +- path = path_get_udev_rule(ZFCP_LUN_NAME, copy); ++ path = path_get_udev_rule(ZFCP_LUN_NAME, copy, autoconf); + free(copy); + + return path; +@@ -403,9 +403,9 @@ static char *get_zfcp_lun_path(const cha + + /* Return path to zfcp lun udev rule file containing configuration data for + * a single LUN. */ +-static char *get_single_zfcp_lun_path(const char *id) ++static char *get_single_zfcp_lun_path(const char *id, bool autoconf) + { +- return path_get_udev_rule(ZFCP_LUN_NAME, id); ++ return path_get_udev_rule(ZFCP_LUN_NAME, id, autoconf); + } + + /* Apply the settings found in NODE to STATE. */ +@@ -437,20 +437,21 @@ static void zfcp_lun_node_to_state(struc + } + + /* Read the persistent configuration of a zfcp lun from a udev rule. */ +-exit_code_t udev_zfcp_lun_read_device(struct device *dev) ++exit_code_t udev_zfcp_lun_read_device(struct device *dev, bool autoconf) + { + struct subtype *st = dev->subtype; +- struct device_state *state = &dev->persistent; ++ struct device_state *state = autoconf ? &dev->autoconf : ++ &dev->persistent; + struct util_list *luns; + struct zfcp_lun_node *node; + exit_code_t rc = EXIT_OK; + char *path; + + /* Check for single lun file first then try multi lun file. */ +- path = get_single_zfcp_lun_path(dev->id); ++ path = get_single_zfcp_lun_path(dev->id, autoconf); + if (!util_path_exists(path)) { + free(path); +- path = get_zfcp_lun_path(dev->id); ++ path = get_zfcp_lun_path(dev->id, autoconf); + } + + /* Get previous rule data. */ +@@ -616,7 +617,7 @@ out: + * applies the corresponding parameters. If @single is set, update a single + * lun rule file, otherwise update a multi lun rule file. */ + static exit_code_t update_lun_rule(const char *id, struct device_state *state, +- bool single) ++ bool single, bool autoconf) + { + struct zfcp_lun_devid devid; + struct util_list *luns; +@@ -628,7 +629,8 @@ static exit_code_t update_lun_rule(const + rc = zfcp_lun_parse_devid(&devid, id, err_delayed_print); + if (rc) + return rc; +- path = single ? get_single_zfcp_lun_path(id) : get_zfcp_lun_path(id); ++ path = single ? get_single_zfcp_lun_path(id, autoconf) : ++ get_zfcp_lun_path(id, autoconf); + + /* Get previous rule data. */ + luns = zfcp_lun_node_list_new(); +@@ -664,26 +666,28 @@ static exit_code_t update_lun_rule(const + + /* Write a udev-rule to configure the specified zfcp lun and associated + * device state. */ +-exit_code_t udev_zfcp_lun_write_device(struct device *dev) ++exit_code_t udev_zfcp_lun_write_device(struct device *dev, bool autoconf) + { + exit_code_t rc; ++ struct device_state *state = autoconf ? &dev->autoconf : ++ &dev->persistent; + +- rc = update_lun_rule(dev->id, &dev->persistent, true); ++ rc = update_lun_rule(dev->id, state, true, autoconf); + + /* We only want single lun rule files so remove any remaining + * references in multi lun rule files. */ +- update_lun_rule(dev->id, NULL, false); ++ update_lun_rule(dev->id, NULL, false, autoconf); + + return rc; + } + + /* Remove the UDEV rule used to configure the zfcp lun with the specified ID. */ +-exit_code_t udev_zfcp_lun_remove_rule(const char *id) ++exit_code_t udev_zfcp_lun_remove_rule(const char *id, bool autoconf) + { + exit_code_t rc, rc2; + +- rc = update_lun_rule(id, NULL, true); +- rc2 = update_lun_rule(id, NULL, false); ++ rc = update_lun_rule(id, NULL, true, autoconf); ++ rc2 = update_lun_rule(id, NULL, false, autoconf); + + if (rc) + return rc; +@@ -692,7 +696,7 @@ exit_code_t udev_zfcp_lun_remove_rule(co + } + + /* Determine if a udev rule exists for configuring the specified zfcp lun. */ +-bool udev_zfcp_lun_exists(const char *id) ++bool udev_zfcp_lun_exists(const char *id, bool autoconf) + { + struct zfcp_lun_devid devid; + char *path, *rule = NULL, *pattern = NULL; +@@ -702,7 +706,7 @@ bool udev_zfcp_lun_exists(const char *id + return false; + + /* Check for single lun rule file first. */ +- path = get_single_zfcp_lun_path(id); ++ path = get_single_zfcp_lun_path(id, autoconf); + if (util_path_exists(path)) { + rc = true; + goto out; +@@ -710,7 +714,7 @@ bool udev_zfcp_lun_exists(const char *id + free(path); + + /* Check multi lun rule file next. */ +- path = get_zfcp_lun_path(id); ++ path = get_zfcp_lun_path(id, autoconf); + rule = misc_read_text_file(path, 1, err_ignore); + if (!rule) + goto out; +--- a/zdev/src/zfcp_lun.c ++++ b/zdev/src/zfcp_lun.c +@@ -467,7 +467,14 @@ static exit_code_t zfcp_lun_st_read_pers + struct device *dev, + read_scope_t scope) + { +- return udev_zfcp_lun_read_device(dev); ++ return udev_zfcp_lun_read_device(dev, false); ++} ++ ++static exit_code_t zfcp_lun_st_read_autoconf(struct subtype *st, ++ struct device *dev, ++ read_scope_t scope) ++{ ++ return udev_zfcp_lun_read_device(dev, true); + } + + static exit_code_t zfcp_lun_add(struct device *dev) +@@ -564,7 +571,13 @@ static exit_code_t zfcp_lun_st_configure + static exit_code_t zfcp_lun_st_configure_persistent(struct subtype *st, + struct device *dev) + { +- return udev_zfcp_lun_write_device(dev); ++ return udev_zfcp_lun_write_device(dev, false); ++} ++ ++static exit_code_t zfcp_lun_st_configure_autoconf(struct subtype *st, ++ struct device *dev) ++{ ++ return udev_zfcp_lun_write_device(dev, true); + } + + static exit_code_t zfcp_lun_st_deconfigure_active(struct subtype *st, +@@ -578,7 +591,13 @@ static exit_code_t zfcp_lun_st_deconfigu + static exit_code_t zfcp_lun_st_deconfigure_persistent(struct subtype *st, + struct device *dev) + { +- return udev_zfcp_lun_remove_rule(dev->id); ++ return udev_zfcp_lun_remove_rule(dev->id, false); ++} ++ ++static exit_code_t zfcp_lun_st_deconfigure_autoconf(struct subtype *st, ++ struct device *dev) ++{ ++ return udev_zfcp_lun_remove_rule(dev->id, true); + } + + static exit_code_t check_npiv(struct subtype *st, struct device *dev, +@@ -606,7 +625,8 @@ static exit_code_t check_npiv(struct sub + if (!allow_lun_scan) + goto out; + +- if (!(dev->active.deconfigured || dev->persistent.deconfigured)) { ++ if (!(dev->active.deconfigured || dev->persistent.deconfigured || ++ dev->autoconf.deconfigured)) { + delayed_info("Note: Auto LUN scan enabled - manual LUN " + "configuration is redundant for %s %s\n", + zfcp_host_subtype.devname, fcp_dev_id); +@@ -643,7 +663,8 @@ static exit_code_t zfcp_lun_st_check_pos + + /* Don't show note when an attribute was modified. */ + if (setting_list_modified(dev->active.settings) || +- setting_list_modified(dev->persistent.settings)) ++ setting_list_modified(dev->persistent.settings) || ++ setting_list_modified(dev->autoconf.settings)) + return EXIT_OK; + + /* Check for NPIV but don't route exit code to caller - we only +@@ -795,7 +816,12 @@ static bool zfcp_lun_st_exists_active(st + + static bool zfcp_lun_st_exists_persistent(struct subtype *st, const char *id) + { +- return udev_zfcp_lun_exists(id); ++ return udev_zfcp_lun_exists(id, false); ++} ++ ++static bool zfcp_lun_st_exists_autoconf(struct subtype *st, const char *id) ++{ ++ return udev_zfcp_lun_exists(id, true); + } + + static exit_code_t zfcp_lun_st_is_definable(struct subtype *st, const char *id, +@@ -889,7 +915,13 @@ static void zfcp_lun_st_add_active_ids(s + static void zfcp_lun_st_add_persistent_ids(struct subtype *st, + struct util_list *ids) + { +- udev_zfcp_lun_add_device_ids(ids); ++ udev_zfcp_lun_add_device_ids(ids, false); ++} ++ ++static void zfcp_lun_st_add_autoconf_ids(struct subtype *st, ++ struct util_list *ids) ++{ ++ udev_zfcp_lun_add_device_ids(ids, true); + } + + static exit_code_t add_sg_cb(const char *path, const char *filename, void *data) +@@ -1072,18 +1104,23 @@ struct subtype zfcp_lun_subtype = { + + .exists_active = &zfcp_lun_st_exists_active, + .exists_persistent = &zfcp_lun_st_exists_persistent, ++ .exists_autoconf = &zfcp_lun_st_exists_autoconf, + + .add_active_ids = &zfcp_lun_st_add_active_ids, + .add_persistent_ids = &zfcp_lun_st_add_persistent_ids, ++ .add_autoconf_ids = &zfcp_lun_st_add_autoconf_ids, + + .read_active = &zfcp_lun_st_read_active, + .read_persistent = &zfcp_lun_st_read_persistent, ++ .read_autoconf = &zfcp_lun_st_read_autoconf, + + .configure_active = &zfcp_lun_st_configure_active, + .configure_persistent = &zfcp_lun_st_configure_persistent, ++ .configure_autoconf = &zfcp_lun_st_configure_autoconf, + + .deconfigure_active = &zfcp_lun_st_deconfigure_active, + .deconfigure_persistent = &zfcp_lun_st_deconfigure_persistent, ++ .deconfigure_autoconf = &zfcp_lun_st_deconfigure_autoconf, + + .check_pre_configure = &zfcp_lun_st_check_pre_configure, + .check_post_configure = &zfcp_lun_st_check_post_configure, diff --git a/s390-tools-sles15sp1-07-zdev-Integrate-firmware-auto-configuration-with-drac.patch b/s390-tools-sles15sp1-07-zdev-Integrate-firmware-auto-configuration-with-drac.patch new file mode 100644 index 0000000..7134aa4 --- /dev/null +++ b/s390-tools-sles15sp1-07-zdev-Integrate-firmware-auto-configuration-with-drac.patch @@ -0,0 +1,155 @@ +Subject: zdev: Integrate firmware auto-configuration with dracut +From: Peter Oberparleiter + +Summary: zdev: Add support for handling I/O configuration data +Description: LPARs that are running in IBM Dynamic Partition Manager (DPM) mode + can access a firmware-generated I/O configuration data file that + contains s390-specific information about available I/O devices + such as qeth device numbers and parameters, and FCP device IDs. + + This data file is intended to remove the need for users to + manually enter the corresponding device data during installation. + + Linux kernels with the corresponding support make the I/O + configuration data available at the following location: + + /sys/firmware/sclp_sd/config/data + + This patch set adds support for handling this data file using the + chzdev and lszdev tools: + + - I/O configuration data can be applied using chzdev's --import + option + - Initial RAM-Disk scripts automatically apply the + I/O configuration data to the system configuration + - lszdev can be used to display the applied auto-configuration + data + - chzdev can be used to manually override the + auto-configuration data + +Upstream-ID: 3fb356ebd297e4384208b7688d49cb3eb8f5b3e1 +Problem-ID: LS1604 + +Upstream-Description: + + zdev: Integrate firmware auto-configuration with dracut + + Add a dracut hook that applies firmware-provided I/O configuration data + as auto-configuration during boot. This way, all I/O devices configured + by DPM are automatically brought online without further user + interaction. + + This mechanism is active by default. It can be deactivated by specifying + the following parameter on the kernel command line: + + rd.zdev=no-auto + + Signed-off-by: Peter Oberparleiter + Signed-off-by: Jan Höppner + + +Signed-off-by: Peter Oberparleiter +--- + zdev/dracut/95zdev/module-setup.sh | 19 ++++++++++---- + zdev/dracut/95zdev/parse-zdev.sh | 38 +++++++++++++++++++++++++++++ + zdev/dracut/Makefile | 3 ++ + 3 files changed, 55 insertions(+), 5 deletions(-) + +--- a/zdev/dracut/95zdev/module-setup.sh ++++ b/zdev/dracut/95zdev/module-setup.sh +@@ -9,7 +9,8 @@ + # 95zdev/module_setup.sh + # This module installs configuration files (udev rules and modprobe.conf + # files) required to enable the root device on s390. It will only work when +-# the root device was configured using the chzdev tool. ++# the root device was configured using the chzdev tool. In addition, ++# a hook is installed to parse rd.zdev= kernel parameters. + # + + check() { +@@ -29,15 +30,23 @@ depends() { + } + + installkernel() { +- local _modules=$(lszdev --by-path / --columns MODULES --no-headings 2>/dev/null) +- +- [ -z "$_modules" ] && return 0 +- [ ! -z "$_modules" ] && instmods $_modules ++ # Add modules for all device types supported by chzdev (required for ++ # auto-configuration) ++ instmods lcs qeth qeth_l2 qeth_l3 dasd_mod dasd_eckd_mod dasd_fba_mod \ ++ dasd_diag_mod zfcp + } + + install() { + local _tempfile + ++ # Ensure that required tools are available ++ inst_multiple chzdev lszdev vmcp ++ ++ # Hook to parse zdev kernel parameter ++ inst_hook cmdline 95 "$moddir/parse-zdev.sh" ++ ++ # Obtain root device configuration ++ + # Exit early if root device type is unknown + if ! lszdev --by-path / >/dev/null 2>&1 ; then + return 0 +--- /dev/null ++++ b/zdev/dracut/95zdev/parse-zdev.sh +@@ -0,0 +1,38 @@ ++#!/bin/sh ++# ++# Copyright IBM Corp. 2017 ++# ++# s390-tools is free software; you can redistribute it and/or modify ++# it under the terms of the MIT license. See LICENSE for details. ++# ++# 95zdev/parse-zdev.sh ++# Parse the kernel command line for rd.zdev kernel parameters. These ++# parameters are evaluated and used to configure z Systems specific devices. ++# ++# Format: ++# rd.zdev=no-auto ++# ++# where ++# ++# no-auto: Indicates that firmware-provided I/O configuration data ++# should not be applied. ++# ++ ++zdev_fw_file="/sys/firmware/sclp_sd/config/data" ++zdev_base_args="--force --yes --no-root-update --no-settle --auto-conf --quiet" ++ ++if [ -e "$zdev_fw_file" ] ; then ++ zdev_auto=1 ++else ++ zdev_auto=0 ++fi ++ ++for zdev_arg in $(getargs rd.zdev); do ++ if [ "$zdev_arg" = "no-auto" ] ; then ++ zdev_auto=0 ++ fi ++done ++ ++if [ $zdev_auto -eq 1 ] ; then ++ chzdev --import "$zdev_fw_file" $zdev_base_args ++fi +--- a/zdev/dracut/Makefile ++++ b/zdev/dracut/Makefile +@@ -11,11 +11,14 @@ ZDEVDIR := 95zdev + # performs the following functions when dracut is run: + # + # - copy the persistent root device configuration to the initial ram disk ++# - install a boot-time hook to apply firmware-provided configuration data ++# to the system + # + ifeq ($(HAVE_DRACUT),1) + install: + $(INSTALL) -m 755 -d $(DESTDIR)$(MODDIR) + $(INSTALL) -m 755 -d $(DESTDIR)$(MODDIR)/$(ZDEVDIR) + $(INSTALL) -m 755 $(ZDEVDIR)/module-setup.sh \ ++ $(ZDEVDIR)/parse-zdev.sh \ + $(DESTDIR)$(MODDIR)/$(ZDEVDIR)/ + endif diff --git a/s390-tools-sles15sp1-08-zdev-Integrate-firmware-auto-configuration-with-init.patch b/s390-tools-sles15sp1-08-zdev-Integrate-firmware-auto-configuration-with-init.patch new file mode 100644 index 0000000..a6ae078 --- /dev/null +++ b/s390-tools-sles15sp1-08-zdev-Integrate-firmware-auto-configuration-with-init.patch @@ -0,0 +1,216 @@ +Subject: zdev: Integrate firmware auto-configuration with initramfs-tools +From: Peter Oberparleiter + +Summary: zdev: Add support for handling I/O configuration data +Description: LPARs that are running in IBM Dynamic Partition Manager (DPM) mode + can access a firmware-generated I/O configuration data file that + contains s390-specific information about available I/O devices + such as qeth device numbers and parameters, and FCP device IDs. + + This data file is intended to remove the need for users to + manually enter the corresponding device data during installation. + + Linux kernels with the corresponding support make the I/O + configuration data available at the following location: + + /sys/firmware/sclp_sd/config/data + + This patch set adds support for handling this data file using the + chzdev and lszdev tools: + + - I/O configuration data can be applied using chzdev's --import + option + - Initial RAM-Disk scripts automatically apply the + I/O configuration data to the system configuration + - lszdev can be used to display the applied auto-configuration + data + - chzdev can be used to manually override the + auto-configuration data + +Upstream-ID: 3c5644ccfd46aab27df6e0ed783e94a620bc3fe6 +Problem-ID: LS1604 + +Upstream-Description: + + zdev: Integrate firmware auto-configuration with initramfs-tools + + Add initramfs-tools scripts that apply firmware-provided I/O + configuration data as auto-configuration during boot. This way, all I/O + devices configured by DPM are automatically brought online without + further user interaction. + + This mechanism is active by default. It can be deactivated by specifying + the following parameter on the kernel command line: + + rd.zdev=no-auto + + Signed-off-by: Peter Oberparleiter + Signed-off-by: Jan Höppner + + +Signed-off-by: Peter Oberparleiter +--- + README.md | 1 + zdev/Makefile | 1 + zdev/initramfs/Makefile | 22 ++++++++ + zdev/initramfs/hooks/zdev | 39 +++++++++++++++ + zdev/initramfs/scripts/init-top/zdev | 67 +++++++++++++++++++++++++++ + 5 files changed, 130 insertions(+) + +--- a/README.md ++++ b/README.md +@@ -272,6 +272,7 @@ This table lists additional build or ins + | __COMPONENT__ | __OPTION__ | __TOOLS__ | + |----------------|:----------------:|:-------------------------------:| + | dracut | `HAVE_DRACUT` | zdev | ++| initramfs-tools| `HAVE_INITRAMFS` | zdev | + + The s390-tools build process uses "pkg-config" if available and hard-coded + compiler and linker options otherwise. +--- a/zdev/Makefile ++++ b/zdev/Makefile +@@ -8,6 +8,7 @@ install: all + $(MAKE) -C src install + $(MAKE) -C man install + $(MAKE) -C dracut install ++ $(MAKE) -C initramfs install + + clean: + $(MAKE) -C src clean +--- /dev/null ++++ b/zdev/initramfs/Makefile +@@ -0,0 +1,22 @@ ++# Common definitions ++include ../../common.mak ++ ++INITRAMFSDIR := /usr/share/initramfs-tools ++HOOKDIR := $(INITRAMFSDIR)/hooks ++INITTOP := $(INITRAMFSDIR)/scripts/init-top ++ ++# HAVE_INITRAMFS ++# ++# This install time parameter determines whether the zdev initramfs support is ++# installed (HAVE_INITRAMFS=1) or not (default). When installed, the module ++# performs the following functions when mkinitramfs is run: ++# ++# - install a boot-time hook to apply firmware-provided configuration data ++# to the system ++# ++ifeq ($(HAVE_INITRAMFS),1) ++install: ++ $(INSTALL) -m 755 -d $(DESTDIR)/$(HOOKDIR) $(DESTDIR)/$(INITTOP) ++ $(INSTALL) -m 755 hooks/zdev $(DESTDIR)/$(HOOKDIR) ++ $(INSTALL) -m 755 scripts/init-top/zdev $(DESTDIR)/$(INITTOP) ++endif +--- /dev/null ++++ b/zdev/initramfs/hooks/zdev +@@ -0,0 +1,39 @@ ++#!/bin/sh ++# ++# Copyright IBM Corp. 2016, 2017 ++# ++# s390-tools is free software; you can redistribute it and/or modify ++# it under the terms of the MIT license. See LICENSE for details. ++# ++# hooks/zdev ++# This hook script adds files required to apply firmware-provided I/O ++# configuration data during boot. ++# ++ ++PREREQ="" ++ ++prereqs() ++{ ++ echo "$PREREQ" ++} ++ ++case $1 in ++ prereqs) ++ prereqs ++ exit 0 ++ ;; ++esac ++ ++. /usr/share/initramfs-tools/hook-functions ++ ++# Add modules for all device types supported by chzdev (required for ++# auto-configuration) ++zdev_modules="lcs qeth qeth_l2 qeth_l3 dasd_mod dasd_eckd_mod dasd_fba_mod dasd_diag_mod zfcp" ++ ++for x in $zdev_modules ; do ++ manual_add_modules ${x} ++done ++ ++copy_exec /sbin/chzdev ++copy_exec /sbin/lszdev ++copy_exec /sbin/vmcp +--- /dev/null ++++ b/zdev/initramfs/scripts/init-top/zdev +@@ -0,0 +1,67 @@ ++#!/bin/sh ++# ++# Copyright IBM Corp. 2017 ++# ++# s390-tools is free software; you can redistribute it and/or modify ++# it under the terms of the MIT license. See LICENSE for details. ++# ++# scripts/init-top/zdev ++# Parse the kernel command line for rd.zdev kernel parameters. These ++# parameters are evaluated and used to configure z Systems specific devices. ++# ++# Format: ++# rd.zdev=no-auto ++# ++# where ++# ++# no-auto: Indicates that firmware-provided I/O configuration data ++# should not be applied. ++# ++ ++PREREQ="udev" ++ ++prereqs() ++{ ++ echo "$PREREQ" ++} ++ ++case $1 in ++prereqs) ++ prereqs ++ exit 0 ++ ;; ++esac ++ ++. /scripts/functions ++ ++zdev_fw_file="/sys/firmware/sclp_sd/config/data" ++zdev_base_args="--force --yes --no-root-update --no-settle --auto-conf --quiet" ++ ++if [ -e "$zdev_fw_file" ] ; then ++ zdev_auto=1 ++else ++ zdev_auto=0 ++fi ++ ++for x in $(cat /proc/cmdline); do ++ case ${x} in ++ rd.zdev=*) ++ zdev_arg=${x#*=} ++ if [ "$zdev_arg" = "no-auto" ] ; then ++ zdev_auto=0 ++ fi ++ ;; ++ esac ++done ++ ++if [ $zdev_auto -eq 1 ] ; then ++ log_begin_msg "Starting firmware auto-configuration" ++ chzdev --import "$zdev_fw_file" $zdev_base_args ++ log_end_msg ++ ++ # Repeat cold-plug after creating udev rules ++ udevadm control --reload ++ udevadm trigger --type=subsystems --action=add ++ udevadm trigger --action=add ++ udevadm settle || true ++fi diff --git a/s390-tools-sles15sp1-09-zdev-Implement-internal-device-attributes.patch b/s390-tools-sles15sp1-09-zdev-Implement-internal-device-attributes.patch new file mode 100644 index 0000000..1c92216 --- /dev/null +++ b/s390-tools-sles15sp1-09-zdev-Implement-internal-device-attributes.patch @@ -0,0 +1,507 @@ +Subject: zdev: Implement internal device attributes +From: Peter Oberparleiter + +Summary: zdev: Add support for handling I/O configuration data +Description: LPARs that are running in IBM Dynamic Partition Manager (DPM) mode + can access a firmware-generated I/O configuration data file that + contains s390-specific information about available I/O devices + such as qeth device numbers and parameters, and FCP device IDs. + + This data file is intended to remove the need for users to + manually enter the corresponding device data during installation. + + Linux kernels with the corresponding support make the I/O + configuration data available at the following location: + + /sys/firmware/sclp_sd/config/data + + This patch set adds support for handling this data file using the + chzdev and lszdev tools: + + - I/O configuration data can be applied using chzdev's --import + option + - Initial RAM-Disk scripts automatically apply the + I/O configuration data to the system configuration + - lszdev can be used to display the applied auto-configuration + data + - chzdev can be used to manually override the + auto-configuration data + +Upstream-ID: c0392efa39e48cb12fdf3524b2f9e683e46f0f14 +Problem-ID: LS1604 + +Upstream-Description: + + zdev: Implement internal device attributes + + This change adds base infrastructure for implementing internal device + attributes. In the context of the zdev tools, an internal device + attribute is a new type of device attribute with the following + characteristics: + + - Can be set and removed like normal device attributes + - Affects zdev-internal handling only + - Does not correspond to an actual device attribute, that is + it has no representation in SysFS + - Can not be set in the active configuration + - Name starts with "zdev:" to prevent conflicts with actual + device attributes + + Values for internal device attributes are stored in udev rules alongside + the normal persistent configuration of a device. They are encoded as + udev environment variables. Note that they have no further effect on + udev processing. + + Signed-off-by: Peter Oberparleiter + Signed-off-by: Jan Höppner + + +Signed-off-by: Peter Oberparleiter +--- + zdev/include/attrib.h | 2 + zdev/include/internal.h | 20 +++++++++ + zdev/include/misc.h | 1 + zdev/include/udev.h | 7 +++ + zdev/src/Makefile | 4 - + zdev/src/device.c | 15 +++++++ + zdev/src/internal.c | 25 ++++++++++++ + zdev/src/misc.c | 15 +++++++ + zdev/src/udev.c | 32 +++++++++++++++ + zdev/src/udev_ccw.c | 16 +++++++ + zdev/src/udev_ccwgroup.c | 11 +++++ + zdev/src/udev_zfcp_lun.c | 80 ++++++++++++++++++++++++++++++++++++--- + 12 files changed, 220 insertions(+), 8 deletions(-) + +--- a/zdev/include/attrib.h ++++ b/zdev/include/attrib.h +@@ -189,6 +189,7 @@ struct value_map { + * in the persistent configuration + * @nounload: (Device type attributes only) This attribute can be set while + * the corresponding kernel module remains loaded. ++ * @internal: This attribute only affects internal handling + * @order: A number indicating the order in which to apply attribute + * @order_cmp: A function determining if a setting for this attribute should + * be applied before (-1) or after (1) another setting, or +@@ -216,6 +217,7 @@ struct attrib { + unsigned int activerem :1; + unsigned int defunset :1; + unsigned int nounload :1; ++ unsigned int internal :1; + + /* Optional */ + int order; +--- /dev/null ++++ b/zdev/include/internal.h +@@ -0,0 +1,20 @@ ++/* ++ * zdev - Modify and display the persistent configuration of devices ++ * ++ * Copyright IBM Corp. 2017 ++ * ++ * s390-tools is free software; you can redistribute it and/or modify ++ * it under the terms of the MIT license. See LICENSE for details. ++ */ ++ ++#ifndef INTERNAL_H ++#define INTERNAL_H ++ ++#include ++ ++#define INTERNAL_ATTR_PREFIX "zdev:" ++ ++const char *internal_get_name(const char *name); ++bool internal_by_name(const char *name); ++ ++#endif /* INTERNAL_H */ +--- a/zdev/include/misc.h ++++ b/zdev/include/misc.h +@@ -183,6 +183,7 @@ bool str_to_config(const char *, config_ + char *quote_str(const char *, int); + char *unquote_str(const char *); + char *shrink_str(const char *); ++char *misc_strrstr(const char *haystack, const char *needle); + + struct util_list *strlist_new(void); + void strlist_free(struct util_list *); +--- a/zdev/include/udev.h ++++ b/zdev/include/udev.h +@@ -13,6 +13,9 @@ + #include "lib/util_list.h" + #include "exit_code.h" + ++struct attrib; ++struct setting_list; ++ + extern int udev_need_settle; + extern int udev_no_settle; + +@@ -46,4 +49,8 @@ exit_code_t udev_remove_rule(const char + + void udev_settle(void); + ++void udev_add_internal_from_entry(struct setting_list *list, ++ struct udev_entry_node *entry, ++ struct attrib **attribs); ++ + #endif /* UDEV_H */ +--- a/zdev/src/Makefile ++++ b/zdev/src/Makefile +@@ -8,7 +8,7 @@ ALL_CPPFLAGS += -I ../include -std=gnu99 + chzdev_objects += attrib.o chzdev.o device.o devnode.o devtype.o exit_code.o \ + export.o hash.o inuse.o misc.o namespace.o opts.o path.o \ + root.o select.o setting.o subtype.o table.o table_attribs.o \ +- table_types.o net.o firmware.o ++ table_types.o net.o firmware.o internal.o + + # Devtype Helpers + chzdev_objects += blkinfo.o ccw.o ccwgroup.o findmnt.o modprobe.o module.o \ +@@ -38,7 +38,7 @@ chzdev_objects += generic_ccw.o + lszdev_objects += attrib.o lszdev.o device.o devnode.o devtype.o exit_code.o \ + export.o hash.o inuse.o misc.o namespace.o opts.o path.o \ + root.o select.o setting.o subtype.o table.o table_types.o \ +- net.o ++ net.o internal.o + + # Devtype Helpers + lszdev_objects += blkinfo.o ccw.o ccwgroup.o findmnt.o modprobe.o module.o \ +--- a/zdev/src/device.c ++++ b/zdev/src/device.c +@@ -16,6 +16,7 @@ + #include "attrib.h" + #include "device.h" + #include "devtype.h" ++#include "internal.h" + #include "misc.h" + #include "namespace.h" + #include "setting.h" +@@ -221,6 +222,9 @@ static exit_code_t apply_setting(struct + goto err_activeonly_forceable; + if (!force && SCOPE_AUTOCONF(config) && a->activeonly) + goto err_activeonly_forceable; ++ /* Check for internal. */ ++ if (config == config_active && a->internal) ++ goto err_int_noactive; + /* Check for multiple values. */ + if (!force && !a->multi && strlist_find(processed, key)) + goto err_multi_forceable; +@@ -230,6 +234,9 @@ static exit_code_t apply_setting(struct + goto err_unknown; + if (!force) + goto err_unknown_forceable; ++ /* Check for internal. */ ++ if (config == config_active && internal_by_name(key)) ++ goto err_int_noactive; + } + + strlist_add(processed, "%s", key); +@@ -294,6 +301,11 @@ err_activeonly_forceable: + delayed_forceable("Attribute '%s' should only be changed in the active " + "config\n", a->name); + return EXIT_INVALID_SETTING; ++ ++err_int_noactive: ++ delayed_err("Internal attribute '%s' cannot be set in the active config\n", ++ key); ++ return EXIT_INVALID_SETTING; + } + + /* Apply device settings from strlist to device. */ +@@ -542,6 +554,9 @@ exit_code_t device_write_active_settings + s = p->ptr; + if (!s->modified || s->removed) + continue; ++ if ((s->attrib && s->attrib->internal) || ++ internal_by_name(s->name)) ++ continue; + + path = subtype_get_active_attrib_path(st, dev, s->name); + if (!path) { +--- /dev/null ++++ b/zdev/src/internal.c +@@ -0,0 +1,25 @@ ++/* ++ * zdev - Modify and display the persistent configuration of devices ++ * ++ * Copyright IBM Corp. 2017 ++ * ++ * s390-tools is free software; you can redistribute it and/or modify ++ * it under the terms of the MIT license. See LICENSE for details. ++ */ ++ ++#include ++ ++#include "internal.h" ++#include "misc.h" ++ ++/* Return identifier of internal attribute with specified @name. */ ++const char *internal_get_name(const char *name) ++{ ++ return name + sizeof(INTERNAL_ATTR_PREFIX) - 1; ++} ++ ++/* Check if attribute is internal by name. */ ++bool internal_by_name(const char *name) ++{ ++ return starts_with(name, INTERNAL_ATTR_PREFIX); ++} +--- a/zdev/src/misc.c ++++ b/zdev/src/misc.c +@@ -1717,3 +1717,18 @@ void debug_init(int argc, char *argv[]) + fprintf(stderr, "%s\"%s\"", i > 0 ? ", " : "", argv[i]); + fprintf(stderr, "\n"); + } ++ ++/* Return the last occurrence of @needle in @haystack, or %NULL if @needle ++ * was not found. */ ++char *misc_strrstr(const char *haystack, const char *needle) ++{ ++ char *result, *next; ++ ++ result = strstr(haystack, needle); ++ if (result) { ++ while ((next = strstr(result + 1, needle))) ++ result = next; ++ } ++ ++ return result; ++} +--- a/zdev/src/udev.c ++++ b/zdev/src/udev.c +@@ -409,3 +409,35 @@ void udev_settle(void) + return; + misc_system(err_ignore, "%s settle", PATH_UDEVADM); + } ++ ++/* Extract internal attribute settings from @entry and add to @list. ++ * Associate corresponding attribute if found in @attribs. */ ++void udev_add_internal_from_entry(struct setting_list *list, ++ struct udev_entry_node *entry, ++ struct attrib **attribs) ++{ ++ char *copy, *name, *end, *u; ++ struct attrib *a; ++ ++ /* ENV{zdev_var}="1" */ ++ copy = misc_strdup(entry->key); ++ ++ /* Find attribute name start. */ ++ name = strchr(copy, '{'); ++ end = strrchr(copy, '}'); ++ if (!name || !end) ++ goto out; ++ *end = 0; ++ name++; ++ ++ /* zdev_ => zdev: */ ++ u = strchr(name, '_'); ++ if (u) ++ *u = ':'; ++ ++ a = attrib_find(attribs, name); ++ setting_list_apply_actual(list, a, name, entry->value); ++ ++out: ++ free(copy); ++} +--- a/zdev/src/udev_ccw.c ++++ b/zdev/src/udev_ccw.c +@@ -18,6 +18,7 @@ + #include "attrib.h" + #include "ccw.h" + #include "device.h" ++#include "internal.h" + #include "misc.h" + #include "path.h" + #include "setting.h" +@@ -49,6 +50,12 @@ static void add_setting_from_entry(struc + char *copy, *name, *end; + struct attrib *a; + ++ /* ENV{zdev_var}="1" */ ++ if (starts_with(entry->key, "ENV{zdev_") && ++ strcmp(entry->op, "=") == 0) { ++ udev_add_internal_from_entry(list, entry, attribs); ++ return; ++ } + /* ATTR{[ccw/0.0.37bf]online}=1 */ + if (strncmp(entry->key, "ATTR{[ccw/", 10) != 0 || + strcmp(entry->op, "=") != 0) +@@ -190,7 +197,14 @@ exit_code_t udev_ccw_write_device(struct + s = p->ptr; + if (s->removed) + continue; +- fprintf(fd, "ATTR{[ccw/%s]%s}=\"%s\"\n", id, s->name, s->value); ++ if ((s->attrib && s->attrib->internal) || ++ internal_by_name(s->name)) { ++ fprintf(fd, "ENV{zdev_%s}=\"%s\"\n", ++ internal_get_name(s->name), s->value); ++ } else { ++ fprintf(fd, "ATTR{[ccw/%s]%s}=\"%s\"\n", id, s->name, ++ s->value); ++ } + } + + /* Write udev rule epilog. */ +--- a/zdev/src/udev_ccwgroup.c ++++ b/zdev/src/udev_ccwgroup.c +@@ -18,6 +18,7 @@ + #include "attrib.h" + #include "ccwgroup.h" + #include "device.h" ++#include "internal.h" + #include "misc.h" + #include "path.h" + #include "setting.h" +@@ -68,6 +69,12 @@ static void add_setting_from_entry(struc + char *copy, *name, *end; + struct attrib *a; + ++ /* ENV{zdev_var}="1" */ ++ if (starts_with(entry->key, "ENV{zdev_") && ++ strcmp(entry->op, "=") == 0) { ++ udev_add_internal_from_entry(list, entry, attribs); ++ return; ++ } + /* ATTR{[ccwgroup/0.0.f5f0]online}=1 */ + if (strncmp(entry->key, "ATTR{[ccwgroup/", 10) != 0 || + strcmp(entry->op, "=") != 0) +@@ -282,6 +289,10 @@ exit_code_t udev_ccwgroup_write_device(s + fprintf(fd, "ATTR{[ccwgroup/%s]%s}=\"%s\"\n", + ccw_id, s->name, str->str); + } ++ } else if ((s->attrib && s->attrib->internal) || ++ internal_by_name(s->name)) { ++ fprintf(fd, "ENV{zdev_%s}=\"%s\"\n", ++ internal_get_name(s->name), s->value); + } else { + fprintf(fd, "ATTR{[ccwgroup/%s]%s}=\"%s\"\n", ccw_id, + s->name, s->value); +--- a/zdev/src/udev_zfcp_lun.c ++++ b/zdev/src/udev_zfcp_lun.c +@@ -18,6 +18,7 @@ + + #include "attrib.h" + #include "device.h" ++#include "internal.h" + #include "misc.h" + #include "path.h" + #include "scsi.h" +@@ -128,7 +129,7 @@ static bool zfcp_lun_devid_from_entry(st + struct udev_entry_node *entry) + { + struct zfcp_lun_devid id; +- char *copy = NULL, *s; ++ char *copy = NULL, *s, *e, *u; + int i; + bool rc = false; + +@@ -182,6 +183,28 @@ static bool zfcp_lun_devid_from_entry(st + goto out; + } + ++ /*ENV{zdev_var__0_0_1941_0x500507630510c1ae_0x402340d400000000}="1"*/ ++ if (starts_with(entry->key, "ENV{zdev_")) { ++ copy = misc_strdup(entry->key); ++ ++ /* Find ID start (last __) and end (last }). */ ++ s = misc_strrstr(copy, "__"); ++ e = strrchr(copy, '}'); ++ if (!s || !e) ++ goto out; ++ *e = 0; ++ s += 2; ++ /* Convert variable name to ID format. */ ++ for (i = 0, u = s; (u = strchr(u, '_')); i++, u++) { ++ if (i < 2) ++ *u = '.'; ++ else ++ *u = ':'; ++ } ++ rc = zfcp_lun_parse_devid(&id, s, err_ignore) == EXIT_OK ? ++ true : false; ++ } ++ + out: + free(copy); + if (rc) +@@ -211,11 +234,46 @@ static struct zfcp_lun_node *zfcp_lun_no + return node; + } + ++static void add_internal_setting_from_entry(struct udev_entry_node *entry, ++ struct zfcp_lun_node *node) ++{ ++ char *copy, *name, *end, *u; ++ struct attrib *a; ++ ++ /*ENV{zdev_var__0_0_1941_0x500507630510c1ae_0x402340d400000000}="1"*/ ++ copy = misc_strdup(entry->key); ++ ++ /* Find attribute name start and end. */ ++ name = strchr(copy, '{'); ++ end = misc_strrstr(copy, "__"); ++ if (!name || !end) ++ goto out; ++ *end = 0; ++ name++; ++ ++ /* zdev_ => zdev: */ ++ u = strchr(name, '_'); ++ if (u) ++ *u = ':'; ++ ++ a = attrib_find(zfcp_lun_subtype.dev_attribs, name); ++ setting_list_apply_actual(node->fc_settings, a, name, entry->value); ++ ++out: ++ free(copy); ++} ++ + static void add_fc_setting_from_entry(struct udev_entry_node *entry, + struct zfcp_lun_node *node) + { + char *copy, *s, *e; + ++ /*ENV{zdev_var__0_0_1941_0x500507630510c1ae_0x402340d400000000}="1"*/ ++ if (starts_with(entry->key, "ENV{zdev_") && ++ strcmp(entry->op, "=") == 0) { ++ add_internal_setting_from_entry(entry, node); ++ return; ++ } + /*ATTR{[ccw/0.0.1941]0x500507630510c1ae/0x402340d400000000/failed}="0"*/ + if (!starts_with(entry->key, "ATTR{[ccw/")) + return; +@@ -490,7 +548,7 @@ static struct zfcp_lun_node *state_to_zf + s->value); + setting_list_add(node->scsi_settings, n); + } else { +- n = setting_new(NULL, s->name, s->value); ++ n = setting_new(s->attrib, s->name, s->value); + setting_list_add(node->fc_settings, n); + } + } +@@ -580,9 +638,21 @@ static exit_code_t write_luns_rule(const + node->id.lun); + + util_list_iterate(&node->fc_settings->list, s) { +- fprintf(fd, "ATTR{[ccw/%s]0x%016" PRIx64 "/0x%016" +- PRIx64 "/%s}=\"%s\"\n", hba_id, node->id.wwpn, +- node->id.lun, s->name, s->value); ++ if ((s->attrib && s->attrib->internal) || ++ internal_by_name(s->name)) { ++ fprintf(fd, "ENV{zdev_%s__%x_%x_%04x_0x%016" ++ PRIx64 "_0x%016" PRIx64 "}=\"%s\"\n", ++ internal_get_name(s->name), ++ node->id.fcp_dev.cssid, ++ node->id.fcp_dev.ssid, ++ node->id.fcp_dev.devno, node->id.wwpn, ++ node->id.lun, s->value); ++ } else { ++ fprintf(fd, "ATTR{[ccw/%s]0x%016" PRIx64 ++ "/0x%016" PRIx64 "/%s}=\"%s\"\n", ++ hba_id, node->id.wwpn, node->id.lun, ++ s->name, s->value); ++ } + } + last_node = node; + } diff --git a/s390-tools-sles15sp1-10-zdev-Implement-support-for-early-device-configuratio.patch b/s390-tools-sles15sp1-10-zdev-Implement-support-for-early-device-configuratio.patch new file mode 100644 index 0000000..9de2443 --- /dev/null +++ b/s390-tools-sles15sp1-10-zdev-Implement-support-for-early-device-configuratio.patch @@ -0,0 +1,505 @@ +Subject: zdev: Implement support for early device configuration +From: Peter Oberparleiter + +Summary: zdev: Add support for handling I/O configuration data +Description: LPARs that are running in IBM Dynamic Partition Manager (DPM) mode + can access a firmware-generated I/O configuration data file that + contains s390-specific information about available I/O devices + such as qeth device numbers and parameters, and FCP device IDs. + + This data file is intended to remove the need for users to + manually enter the corresponding device data during installation. + + Linux kernels with the corresponding support make the I/O + configuration data available at the following location: + + /sys/firmware/sclp_sd/config/data + + This patch set adds support for handling this data file using the + chzdev and lszdev tools: + + - I/O configuration data can be applied using chzdev's --import + option + - Initial RAM-Disk scripts automatically apply the + I/O configuration data to the system configuration + - lszdev can be used to display the applied auto-configuration + data + - chzdev can be used to manually override the + auto-configuration data + +Upstream-ID: 156450f359bb13776fac2130a1cac00a48c2deda +Problem-ID: LS1604 + +Upstream-Description: + + zdev: Implement support for early device configuration + + Enable user to specify that a device should be configured early, that is + during the initial RAM-disk boot phase. This may be necessary, e.g. to + override auto-configuration for a device which is also applied during + that boot phase. It may also be used to manually specify devices that + are required to access the root file system, such as networking devices. + + Users can mark devices as requiring early configuration by specifying + a value of 1 for the newly added internal attribute zdev:early: + + # chzdev dasd-eckd 0.0.1234 -p zdev:early=1 + + This can be changed back by removing the attribute setting, or by + setting the attribute value to 0: + + # chzdev dasd-eckd 0.0.1234 -p -r zdev:early + + or + + # chzdev dasd-eckd 0.0.1234 -p zdev:early=0 + + Signed-off-by: Peter Oberparleiter + Signed-off-by: Jan Höppner + + +Signed-off-by: Peter Oberparleiter +--- + zdev/dracut/95zdev/module-setup.sh | 21 +------ + zdev/include/internal.h | 5 + + zdev/initramfs/hooks/zdev | 19 ++++++ + zdev/man/chzdev.8 | 29 ++++++++++ + zdev/src/chzdev.c | 4 - + zdev/src/ctc.c | 2 + zdev/src/dasd.c | 3 + + zdev/src/generic_ccw.c | 2 + zdev/src/internal.c | 13 ++++ + zdev/src/lcs.c | 2 + zdev/src/qeth.c | 2 + zdev/src/root.c | 82 ++++++++++++++++++++++------- + zdev/src/zfcp_host.c | 2 + zdev/src/zfcp_lun.c | 2 + 14 files changed, 152 insertions(+), 36 deletions(-) + +--- a/zdev/dracut/95zdev/module-setup.sh ++++ b/zdev/dracut/95zdev/module-setup.sh +@@ -45,23 +45,12 @@ install() { + # Hook to parse zdev kernel parameter + inst_hook cmdline 95 "$moddir/parse-zdev.sh" + +- # Obtain root device configuration +- +- # Exit early if root device type is unknown +- if ! lszdev --by-path / >/dev/null 2>&1 ; then +- return 0 +- fi +- ++ # Obtain early + root device configuration + _tempfile=$(mktemp --tmpdir dracut-zdev.XXXXXX) +- +- if chzdev --export - --persistent --by-path / >/dev/null 2>&1 ; then +- # Use persistent configuration +- chzdev --export "$_tempfile" --persistent --by-path / --quiet --type +- else +- # Use active configuration +- chzdev --export "$_tempfile" --active --by-path / --quiet --type +- sed -i -e 's/active/persistent/g' "$_tempfile" +- fi ++ chzdev --export "$_tempfile" --persistent --by-path / --quiet \ ++ --type 2>/dev/null ++ chzdev --export - --persistent --by-attrib "zdev:early=1" --quiet \ ++ --type 2>/dev/null >> "$_tempfile" + + # Apply via --import to prevent other devices from being configured + chzdev --import "$_tempfile" --persistent --base "/etc=$initdir/etc" \ +--- a/zdev/include/internal.h ++++ b/zdev/include/internal.h +@@ -12,7 +12,12 @@ + + #include + ++#include "attrib.h" ++ + #define INTERNAL_ATTR_PREFIX "zdev:" ++#define INTERNAL_ATTR_EARLY INTERNAL_ATTR_PREFIX "early" ++ ++extern struct attrib internal_attr_early; + + const char *internal_get_name(const char *name); + bool internal_by_name(const char *name); +--- a/zdev/initramfs/hooks/zdev ++++ b/zdev/initramfs/hooks/zdev +@@ -10,7 +10,8 @@ + # configuration data during boot. + # + +-PREREQ="" ++# Needs to run after udev or resulting udev rules could be overwritten ++PREREQ="udev" + + prereqs() + { +@@ -37,3 +38,19 @@ done + copy_exec /sbin/chzdev + copy_exec /sbin/lszdev + copy_exec /sbin/vmcp ++ ++_tempfile=$(mktemp --tmpdir initramfs-zdev.XXXXXX) ++ ++# Obtain early + root device configuration ++chzdev --export "$_tempfile" --persistent --by-path / \ ++ --by-attrib "zdev:early=1" --quiet --type 2>/dev/null ++ ++# Apply via --import to prevent other devices from being configured. ++# Rename the resulting cio-ignore rule to ensure that it does not override ++# the one copied by the initramfs udev hook to /lib/udev. ++chzdev --import "$_tempfile" --persistent \ ++ --base "/etc/udev/rules.d/41-cio-ignore.rules=$DESTDIR/etc/udev/rules.d/41-cio-ignore-root.rules" \ ++ --base "/etc=$DESTDIR/etc" --yes --quiet --no-root-update --force \ ++ >/dev/null ++ ++rm -f "$_tempfile" +--- a/zdev/man/chzdev.8 ++++ b/zdev/man/chzdev.8 +@@ -334,6 +334,35 @@ a specific attribute. + .PP + . + . ++.SS "Special settings" ++The following special settings affect how devices are handled by chzdev: ++.PP ++. ++.BR zdev:early =0|1 ++.RS 4 ++Control in which stage of the boot process a device is activated: ++.TP 4 ++.B 0 ++Device is activated normally during boot (default). ++.PP ++.TP 4 ++.B 1 ++Device is activated early in the boot process, by the initial RAM-disk. ++.PP ++Specify a value of 1 for this attribute in any of the following situations: ++.TP 4 ++\(bu ++To ensure that your settings override auto-configuration settings. ++.PP ++.TP 4 ++\(bu ++To ensure that a device required to access the root file system is correctly ++enabled during boot. An example would be a networking device, or a device that ++is intended to extend a logical volume that provides the root file system. ++.PP ++.RE ++. ++. + .SH ACTIONS + You can use one of the action options listed below to specify the + .B main tool action +--- a/zdev/src/chzdev.c ++++ b/zdev/src/chzdev.c +@@ -3025,8 +3025,8 @@ int main(int argc, char *argv[]) + + if ((pers_mod_devs || pers_mod_devtypes) && !opts.no_root_check && + !dryrun) { +- /* If the root device/device type has been modified, additional +- * work might be necessary. */ ++ /* If the root device/device type or early devices have been ++ * modified, additional work might be necessary. */ + rc = root_check(); + if (rc && !drc) + drc = rc; +--- a/zdev/src/ctc.c ++++ b/zdev/src/ctc.c +@@ -17,6 +17,7 @@ + #include "ctc_auto.h" + #include "device.h" + #include "devtype.h" ++#include "internal.h" + #include "misc.h" + #include "namespace.h" + #include "path.h" +@@ -425,6 +426,7 @@ static struct subtype ctc_subtype = { + &ccw_attr_online, + &ctc_attr_buffer, + &ctc_attr_protocol, ++ &internal_attr_early, + ), + .unknown_dev_attribs = 1, + .support_definable = 1, +--- a/zdev/src/dasd.c ++++ b/zdev/src/dasd.c +@@ -16,6 +16,7 @@ + #include "dasd.h" + #include "device.h" + #include "devtype.h" ++#include "internal.h" + #include "misc.h" + #include "modprobe.h" + #include "module.h" +@@ -616,6 +617,7 @@ struct subtype dasd_subtype_eckd = { + &dasd_attr_reservation_policy, + &dasd_attr_last_known_reservation_state, + &dasd_attr_safe_offline, ++ &internal_attr_early, + ), + .unknown_dev_attribs = 1, + +@@ -651,6 +653,7 @@ struct subtype dasd_subtype_fba = { + &dasd_attr_reservation_policy, + &dasd_attr_last_known_reservation_state, + &dasd_attr_safe_offline, ++ &internal_attr_early, + ), + .unknown_dev_attribs = 1, + +--- a/zdev/src/generic_ccw.c ++++ b/zdev/src/generic_ccw.c +@@ -17,6 +17,7 @@ + #include "devnode.h" + #include "devtype.h" + #include "generic_ccw.h" ++#include "internal.h" + #include "namespace.h" + #include "path.h" + #include "subtype.h" +@@ -182,6 +183,7 @@ static struct subtype generic_ccw_subtyp + .dev_attribs = ATTRIB_ARRAY( + &ccw_attr_online, + &ccw_attr_cmb_enable, ++ &internal_attr_early, + ), + .unknown_dev_attribs = 1, + .generic = 1, +--- a/zdev/src/internal.c ++++ b/zdev/src/internal.c +@@ -9,9 +9,22 @@ + + #include + ++#include "attrib.h" + #include "internal.h" + #include "misc.h" + ++struct attrib internal_attr_early = { ++ .name = INTERNAL_ATTR_EARLY, ++ .title = "Activate device early during boot", ++ .desc = "Control the time of activation of a device:\n" ++ " 0: Device is activated normally during boot\n" ++ " 1: Device is activated early in the boot process, by the\n" ++ " initial RAM-disk\n", ++ .defval = "0", ++ .accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 1)), ++ .internal = 1, ++}; ++ + /* Return identifier of internal attribute with specified @name. */ + const char *internal_get_name(const char *name) + { +--- a/zdev/src/lcs.c ++++ b/zdev/src/lcs.c +@@ -15,6 +15,7 @@ + #include "ccwgroup.h" + #include "device.h" + #include "devtype.h" ++#include "internal.h" + #include "lcs.h" + #include "lcs_auto.h" + #include "misc.h" +@@ -362,6 +363,7 @@ static struct subtype lcs_subtype = { + &ccw_attr_online, + &lcs_attr_lancmd_timeout, + &lcs_attr_recover, ++ &internal_attr_early, + ), + .unknown_dev_attribs = 1, + .support_definable = 1, +--- a/zdev/src/qeth.c ++++ b/zdev/src/qeth.c +@@ -15,6 +15,7 @@ + #include "ccwgroup.h" + #include "device.h" + #include "devtype.h" ++#include "internal.h" + #include "misc.h" + #include "namespace.h" + #include "nic.h" +@@ -1445,6 +1446,7 @@ struct subtype qeth_subtype_qeth = { + &qeth_attr_vnicc_takeover_learning, + &qeth_attr_vnicc_bridge_invisible, + &qeth_attr_vnicc_rx_bcast, ++ &internal_attr_early, + ), + .unknown_dev_attribs = 1, + .support_definable = 1, +--- a/zdev/src/root.c ++++ b/zdev/src/root.c +@@ -8,11 +8,13 @@ + */ + + #include ++#include + + #include "lib/util_path.h" + + #include "device.h" + #include "devtype.h" ++#include "internal.h" + #include "misc.h" + #include "path.h" + #include "root.h" +@@ -20,31 +22,78 @@ + #include "setting.h" + #include "subtype.h" + +-/* Determine if the root device was modified. If it was modified, run the +- * corresponding root-install scripts. */ ++static bool is_early_removed(struct device *dev) ++{ ++ struct setting *s; ++ ++ s = setting_list_find(dev->persistent.settings, ++ internal_attr_early.name); ++ if (!s || !s->modified) ++ return false; ++ if (!s->actual_value || strcmp(s->actual_value, "1") != 0) ++ return false; ++ if (s->removed || strcmp(s->value, "0") == 0) ++ return true; ++ return false; ++} ++ ++static void add_early_removed(struct util_list *selected) ++{ ++ int i, j; ++ struct devtype *dt; ++ struct subtype *st; ++ struct device *dev; ++ ++ for (i = 0; devtypes[i]; i++) { ++ dt = devtypes[i]; ++ for (j = 0; dt->subtypes[j]; j++) { ++ st = dt->subtypes[j]; ++ util_list_iterate(&st->devices->hash.list, dev) { ++ if (is_early_removed(dev)) { ++ selected_dev_list_add(selected, dt, st, ++ dev->id, NULL, EXIT_OK); ++ } ++ } ++ } ++ } ++} ++ ++/* Determine if initial RAM-disk needs updating. If so, run the corresponding ++ * scripts if available. */ + exit_code_t root_check(void) + { + struct util_list *selected, *params, *mod = NULL; + struct selected_dev_node *sel; + struct device *dev; + char *params_str; +- exit_code_t rc; ++ exit_code_t rc = EXIT_OK; + struct strlist_node *s; + struct devtype *dt; ++ struct select_opts *select; + +- debug("Checking for modified root device configuration\n"); ++ debug("Checking for required initial RAM-disk update\n"); + +- /* Get list of devices that provide the root device. */ ++ /* Get list of devices that provide the root device or require ++ * early configuration. */ + selected = selected_dev_list_new(); +- rc = select_by_path(NULL, selected, config_active, scope_mandatory, +- NULL, NULL, PATH_ROOT, err_ignore); +- if (rc) { ++ /* First add devices that had zdev:early removed or changed to 0. ++ * The subsequent call to select_devices() will filter out any ++ * duplicates. */ ++ add_early_removed(selected); ++ /* Now add devices required for root file system. */ ++ if (select_by_path(NULL, selected, config_active, scope_mandatory, ++ NULL, NULL, PATH_ROOT, err_ignore)) { + /* Running from an unknown root device is not an error. */ + verb("Note: Could not determine if root device configuration " + "needs to be updated\n"); +- rc = 0; +- goto out; + } ++ /* Finally add devices with zdev:early=1. */ ++ select = select_opts_new(); ++ strlist_add(&select->by_attr, "%s=1", INTERNAL_ATTR_EARLY); ++ select_devices(select, selected, 1, 0, 0, ++ config_active | config_persistent, scope_mandatory, ++ err_ignore); ++ select_opts_free(select); + + /* Determine if any of the devices or device types has been modified. */ + mod = strlist_new(); +@@ -68,19 +117,18 @@ exit_code_t root_check(void) + + if (util_list_is_empty(mod)) + goto out; +- info("Note: Some of the changes affect devices providing the root " +- "file system:\n"); ++ info("Note: The initial RAM-disk must be updated for these changes to take effect:\n"); + util_list_iterate(mod, s) + info(" - %s\n", s->str); +- info(" Additional steps such as rebuilding the RAM-disk might be " +- "required.\n"); + + /* Check if script is available. */ +- if (!util_path_is_reg_file(PATH_ROOT_SCRIPT)) ++ if (!util_path_is_reg_file(PATH_ROOT_SCRIPT)) { ++ warn("A manual update of the initial RAM-disk is required.\n"); + goto out; ++ } + + /* Ask for confirmation. */ +- if (!confirm("Update persistent root device configuration now?")) { ++ if (!confirm("Update initial RAM-disk now?")) { + rc = EXIT_ABORTED; + goto out; + } +@@ -97,7 +145,7 @@ exit_code_t root_check(void) + /* Run update command. */ + if (misc_system(err_delayed_print, "%s %s", PATH_ROOT_SCRIPT, + params_str) != 0) { +- error("Failure while updating root device configuration\n"); ++ error("Failure while updating initial RAM-disk\n"); + delayed_print(DELAY_INDENT); + rc = EXIT_RUNTIME_ERROR; + } +--- a/zdev/src/zfcp_host.c ++++ b/zdev/src/zfcp_host.c +@@ -17,6 +17,7 @@ + #include "ccw.h" + #include "device.h" + #include "devtype.h" ++#include "internal.h" + #include "misc.h" + #include "path.h" + #include "setting.h" +@@ -247,6 +248,7 @@ struct subtype zfcp_host_subtype = { + &zfcp_host_attr_failed, + &zfcp_host_attr_port_remove, + &zfcp_host_attr_port_rescan, ++ &internal_attr_early, + ), + .unknown_dev_attribs = 1, + +--- a/zdev/src/zfcp_lun.c ++++ b/zdev/src/zfcp_lun.c +@@ -21,6 +21,7 @@ + #include "devnode.h" + #include "devtype.h" + #include "misc.h" ++#include "internal.h" + #include "namespace.h" + #include "path.h" + #include "scsi.h" +@@ -1097,6 +1098,7 @@ struct subtype zfcp_lun_subtype = { + &zfcp_lun_attr_scsi_timeout, + &zfcp_lun_attr_scsi_state, + &zfcp_lun_attr_scsi_delete, ++ &internal_attr_early, + ), + .prefixes = STRING_ARRAY(SCSI_ATTR_PREFIX), + .unknown_dev_attribs = 1, diff --git a/s390-tools-sles15sp1-11-zdev-Do-not-call-zipl-on-initrd-update.patch b/s390-tools-sles15sp1-11-zdev-Do-not-call-zipl-on-initrd-update.patch new file mode 100644 index 0000000..4f51a0a --- /dev/null +++ b/s390-tools-sles15sp1-11-zdev-Do-not-call-zipl-on-initrd-update.patch @@ -0,0 +1,50 @@ +Subject: zdev: Add support for handling I/O configuration data +From: Peter Oberparleiter + +Summary: zdev: Add support for handling I/O configuration data +Description: LPARs that are running in IBM Dynamic Partition Manager (DPM) mode + can access a firmware-generated I/O configuration data file that + contains s390-specific information about available I/O devices + such as qeth device numbers and parameters, and FCP device IDs. + + This data file is intended to remove the need for users to + manually enter the corresponding device data during installation. + + Linux kernels with the corresponding support make the I/O + configuration data available at the following location: + + /sys/firmware/sclp_sd/config/data + + This patch set adds support for handling this data file using the + chzdev and lszdev tools: + + - I/O configuration data can be applied using chzdev's --import + option + - Initial RAM-Disk scripts automatically apply the + I/O configuration data to the system configuration + - lszdev can be used to display the applied auto-configuration + data + - chzdev can be used to manually override the + auto-configuration data + +Upstream-ID: - +Problem-ID: LS1604 + +Signed-off-by: Peter Oberparleiter +--- + zdev/src/zdev-root-update.dracut | 6 ------ + 1 file changed, 6 deletions(-) + +--- a/zdev/src/zdev-root-update.dracut ++++ b/zdev/src/zdev-root-update.dracut +@@ -20,10 +20,4 @@ dracut -f || { + exit 1 + } + +-echo "Installing IPL record" +-zipl --noninteractive || { +- echo "${TOOLNAME}: Error: Could not install IPL record" >&2 +- exit 1 +-} +- + exit 0 diff --git a/s390-tools-sles15-zdev-fix-qeth-BridgePort-and-VNICC-conflict-checking.patch b/s390-tools-sles15sp1-zdev-fix-qeth-BridgePort-and-VNICC-conflict-checking.patch similarity index 94% rename from s390-tools-sles15-zdev-fix-qeth-BridgePort-and-VNICC-conflict-checking.patch rename to s390-tools-sles15sp1-zdev-fix-qeth-BridgePort-and-VNICC-conflict-checking.patch index 607162a..37bf18f 100644 --- a/s390-tools-sles15-zdev-fix-qeth-BridgePort-and-VNICC-conflict-checking.patch +++ b/s390-tools-sles15sp1-zdev-fix-qeth-BridgePort-and-VNICC-conflict-checking.patch @@ -21,7 +21,7 @@ Signed-off-by: Hans Wippel --- a/zdev/src/qeth.c +++ b/zdev/src/qeth.c -@@ -1170,6 +1170,37 @@ static exit_code_t check_ineffective_set +@@ -1171,6 +1171,37 @@ static exit_code_t check_ineffective_set return rc; } @@ -59,7 +59,7 @@ Signed-off-by: Hans Wippel /* Check if there are conflicting attribute settings */ static exit_code_t check_conflicting_settings(struct setting_list *list) { -@@ -1181,6 +1212,8 @@ static exit_code_t check_conflicting_set +@@ -1182,6 +1213,8 @@ static exit_code_t check_conflicting_set util_list_iterate(&list->list, s) { if (s->removed) continue; diff --git a/s390-tools.changes b/s390-tools.changes index 03d55a7..8d7440f 100644 --- a/s390-tools.changes +++ b/s390-tools.changes @@ -1,3 +1,24 @@ +------------------------------------------------------------------- +Thu Dec 6 21:03:08 UTC 2018 - mpost@suse.com + +- Added the following patches for Fate#326825 (bsc#1113329) + I/O device pre-configuration + * s390-tools-sles15sp1-01-zdev-use-libutil-provided-path-functions.patch + * s390-tools-sles15sp1-02-zdev-Prepare-for-firmware-configuration-file-support.patch + * s390-tools-sles15sp1-03-zdev-Add-support-for-reading-firmware-configuration-.patch + * s390-tools-sles15sp1-04-zdev-Implement-no-settle.patch + * s390-tools-sles15sp1-05-zdev-Write-zfcp-lun-udev-rules-to-separate-files.patch + * s390-tools-sles15sp1-06-zdev-Add-support-for-handling-auto-configuration-dat.patch + * s390-tools-sles15sp1-07-zdev-Integrate-firmware-auto-configuration-with-drac.patch + * s390-tools-sles15sp1-08-zdev-Integrate-firmware-auto-configuration-with-init.patch + * s390-tools-sles15sp1-09-zdev-Implement-internal-device-attributes.patch + * s390-tools-sles15sp1-10-zdev-Implement-support-for-early-device-configuratio.patch + * s390-tools-sles15sp1-11-zdev-Do-not-call-zipl-on-initrd-update.patch +- Removed the obsolete customize-zdev-root-update-script.patch +- Replaced s390-tools-sles15-zdev-fix-qeth-BridgePort-and-VNICC-conflict-checking.patch + with s390-tools-sles15sp1-zdev-fix-qeth-BridgePort-and-VNICC-conflict-checking.patch + to fit the current version. + ------------------------------------------------------------------- Thu Nov 29 00:03:01 UTC 2018 - mpost@suse.com diff --git a/s390-tools.spec b/s390-tools.spec index e72d81a..f765d7d 100644 --- a/s390-tools.spec +++ b/s390-tools.spec @@ -152,51 +152,60 @@ Patch40: s390-tools-sles15-5-lstape-fix-to-prevent-error-messages-if-ther Patch41: s390-tools-sles15-6-lstape-fix-description-of-type-and-devbusid-filter-f.patch Patch42: s390-tools-sles15-7-lstape-fix-SCSI-output-description-in-man-page.patch Patch43: s390-tools-sles15-8-lstape-fix-SCSI-HBA-CCW-device-bus-ID-e.g.-for-virti.patch -Patch44: s390-tools-sles15-zdev-fix-qeth-BridgePort-and-VNICC-conflict-checking.patch -Patch45: s390-tools-sles15sp1-0001-zkey-Add-properties-file-handling-routines.patch -Patch46: s390-tools-sles15sp1-0002-zkey-Add-build-dependency-to-OpenSSL-libcrypto.patch -Patch47: s390-tools-sles15sp1-0003-zkey-Add-helper-functions-for-comma-separated-string.patch -Patch48: s390-tools-sles15sp1-0004-zkey-Externalize-secure-key-back-end-functions.patch -Patch49: s390-tools-sles15sp1-0005-zkey-Add-keystore-implementation.patch -Patch50: s390-tools-sles15sp1-0006-zkey-Add-keystore-related-commands.patch -Patch51: s390-tools-sles15sp1-0007-zkey-Create-key-repository-and-group-during-make-ins.patch -Patch52: s390-tools-sles15sp1-0008-zkey-Man-page-updates.patch -Patch53: s390-tools-sles15sp1-0009-zkey-let-packaging-create-the-zkeyadm-group-and-perm.patch -Patch54: s390-tools-sles15sp1-0010-zkey-Update-README-to-add-info-about-packaging-requi.patch -Patch55: s390-tools-sles15sp1-0011-zkey-Typo-in-message.patch -Patch56: s390-tools-sles15sp1-0012-zkey-Fix-memory-leak.patch -Patch57: s390-tools-sles15sp1-0013-zkey-Fix-APQN-validation-routine.patch -Patch58: s390-tools-sles15sp1-0014-zkey-Fix-generate-and-import-leaving-key-in-an-incon.patch -Patch59: s390-tools-sles15sp1-0015-zkey-Add-zkey-cryptsetup-tool.patch -Patch60: s390-tools-sles15sp1-0016-zkey-Add-man-page-for-zkey-cryptsetup.patch -Patch61: s390-tools-sles15sp1-0017-zkey-Add-build-dependency-for-libcryptsetup-and-json.patch -Patch62: s390-tools-sles15sp1-0018-zkey-Add-key-verification-pattern-property.patch -Patch63: s390-tools-sles15sp1-0019-zkey-Add-volume-type-property-to-support-LUKS2-volum.patch -Patch64: s390-tools-sles15sp1-01-lszcrypt-CEX6S-exploitation.patch -Patch65: s390-tools-sles15sp1-02-lszcrypt-fix-date-and-wrong-indentation.patch -Patch66: s390-tools-sles15sp1-01-chzcrypt-Corrections-at-the-chzcrypt-man-page.patch -Patch67: s390-tools-sles15sp1-02-lszcrypt-support-for-alternate-zcrypt-device-drivers.patch -Patch68: s390-tools-sles15sp1-01-zcryptctl-new-tool-zcryptctl-for-multiple-zcrypt-node.patch -Patch69: s390-tools-sles15sp1-01-cpumf-Add-extended-counter-defintion-files-for-IBM-z.patch -Patch70: s390-tools-sles15sp1-02-cpumf-z14-split-counter-sets-according-to-CFVN-CSVN-.patch -Patch71: s390-tools-sles15sp1-03-cpumf-cpumf_helper-read-split-counter-sets-part-2-2.patch -Patch72: s390-tools-sles15sp1-04-cpumf-correct-z14-counter-number.patch -Patch73: s390-tools-sles15sp1-05-cpumf-add-missing-Description-tag-for-z13-z14-ctr-12.patch -Patch74: s390-tools-sles15sp1-06-cpumf-correct-counter-name-for-z13-and-z14.patch -Patch75: s390-tools-sles15sp1-07-cpumf-Add-IBM-z14-ZR1-to-the-CPU-Measurement-Facilit.patch -Patch76: s390-tools-sles15sp1-01-util_path-add-function-to-check-if-a-path-exists.patch -Patch77: s390-tools-sles15sp1-02-util_path-Add-description-for-util_path_exists.patch -Patch78: s390-tools-sles15sp1-03-util_path-Make-true-false-handling-consistent-with-o.patch -Patch79: s390-tools-sles15sp1-04-zpcictl-Introduce-new-tool-zpcictl.patch -Patch80: s390-tools-sles15sp1-05-zpcictl-include-sys-sysmacros.h-to-avoid-minor-major.patch -Patch81: s390-tools-sles15sp1-06-zpcictl-Rephrase-man-page-entries-and-tool-output.patch -Patch82: s390-tools-sles15sp1-07-zpcictl-Use-fopen-instead-of-open-for-writes.patch -Patch83: s390-tools-sles15sp1-08-zpcictl-Read-device-link-to-obtain-device-address.patch -Patch84: s390-tools-sles15sp1-09-zpcictl-Make-device-node-for-NVMe-optional.patch -Patch85: s390-tools-sles15sp1-10-zpcictl-Change-wording-of-man-page-and-help-output.patch -Patch86: s390-tools-sles15sp1-dbginfo-gather-nvme-related-data.patch - -Patch999: customize-zdev-root-update-script.patch +Patch44: s390-tools-sles15sp1-0001-zkey-Add-properties-file-handling-routines.patch +Patch45: s390-tools-sles15sp1-0002-zkey-Add-build-dependency-to-OpenSSL-libcrypto.patch +Patch46: s390-tools-sles15sp1-0003-zkey-Add-helper-functions-for-comma-separated-string.patch +Patch47: s390-tools-sles15sp1-0004-zkey-Externalize-secure-key-back-end-functions.patch +Patch48: s390-tools-sles15sp1-0005-zkey-Add-keystore-implementation.patch +Patch49: s390-tools-sles15sp1-0006-zkey-Add-keystore-related-commands.patch +Patch50: s390-tools-sles15sp1-0007-zkey-Create-key-repository-and-group-during-make-ins.patch +Patch51: s390-tools-sles15sp1-0008-zkey-Man-page-updates.patch +Patch52: s390-tools-sles15sp1-0009-zkey-let-packaging-create-the-zkeyadm-group-and-perm.patch +Patch53: s390-tools-sles15sp1-0010-zkey-Update-README-to-add-info-about-packaging-requi.patch +Patch54: s390-tools-sles15sp1-0011-zkey-Typo-in-message.patch +Patch55: s390-tools-sles15sp1-0012-zkey-Fix-memory-leak.patch +Patch56: s390-tools-sles15sp1-0013-zkey-Fix-APQN-validation-routine.patch +Patch57: s390-tools-sles15sp1-0014-zkey-Fix-generate-and-import-leaving-key-in-an-incon.patch +Patch58: s390-tools-sles15sp1-0015-zkey-Add-zkey-cryptsetup-tool.patch +Patch59: s390-tools-sles15sp1-0016-zkey-Add-man-page-for-zkey-cryptsetup.patch +Patch60: s390-tools-sles15sp1-0017-zkey-Add-build-dependency-for-libcryptsetup-and-json.patch +Patch61: s390-tools-sles15sp1-0018-zkey-Add-key-verification-pattern-property.patch +Patch62: s390-tools-sles15sp1-0019-zkey-Add-volume-type-property-to-support-LUKS2-volum.patch +Patch63: s390-tools-sles15sp1-01-lszcrypt-CEX6S-exploitation.patch +Patch64: s390-tools-sles15sp1-02-lszcrypt-fix-date-and-wrong-indentation.patch +Patch65: s390-tools-sles15sp1-01-chzcrypt-Corrections-at-the-chzcrypt-man-page.patch +Patch66: s390-tools-sles15sp1-02-lszcrypt-support-for-alternate-zcrypt-device-drivers.patch +Patch67: s390-tools-sles15sp1-01-zcryptctl-new-tool-zcryptctl-for-multiple-zcrypt-node.patch +Patch68: s390-tools-sles15sp1-01-cpumf-Add-extended-counter-defintion-files-for-IBM-z.patch +Patch69: s390-tools-sles15sp1-02-cpumf-z14-split-counter-sets-according-to-CFVN-CSVN-.patch +Patch70: s390-tools-sles15sp1-03-cpumf-cpumf_helper-read-split-counter-sets-part-2-2.patch +Patch71: s390-tools-sles15sp1-04-cpumf-correct-z14-counter-number.patch +Patch72: s390-tools-sles15sp1-05-cpumf-add-missing-Description-tag-for-z13-z14-ctr-12.patch +Patch73: s390-tools-sles15sp1-06-cpumf-correct-counter-name-for-z13-and-z14.patch +Patch74: s390-tools-sles15sp1-07-cpumf-Add-IBM-z14-ZR1-to-the-CPU-Measurement-Facilit.patch +Patch75: s390-tools-sles15sp1-01-util_path-add-function-to-check-if-a-path-exists.patch +Patch76: s390-tools-sles15sp1-02-util_path-Add-description-for-util_path_exists.patch +Patch77: s390-tools-sles15sp1-03-util_path-Make-true-false-handling-consistent-with-o.patch +Patch78: s390-tools-sles15sp1-04-zpcictl-Introduce-new-tool-zpcictl.patch +Patch79: s390-tools-sles15sp1-05-zpcictl-include-sys-sysmacros.h-to-avoid-minor-major.patch +Patch80: s390-tools-sles15sp1-06-zpcictl-Rephrase-man-page-entries-and-tool-output.patch +Patch81: s390-tools-sles15sp1-07-zpcictl-Use-fopen-instead-of-open-for-writes.patch +Patch82: s390-tools-sles15sp1-08-zpcictl-Read-device-link-to-obtain-device-address.patch +Patch83: s390-tools-sles15sp1-09-zpcictl-Make-device-node-for-NVMe-optional.patch +Patch84: s390-tools-sles15sp1-10-zpcictl-Change-wording-of-man-page-and-help-output.patch +Patch85: s390-tools-sles15sp1-dbginfo-gather-nvme-related-data.patch +Patch86: s390-tools-sles15sp1-01-zdev-use-libutil-provided-path-functions.patch +Patch87: s390-tools-sles15sp1-02-zdev-Prepare-for-firmware-configuration-file-support.patch +Patch88: s390-tools-sles15sp1-03-zdev-Add-support-for-reading-firmware-configuration-.patch +Patch89: s390-tools-sles15sp1-04-zdev-Implement-no-settle.patch +Patch90: s390-tools-sles15sp1-05-zdev-Write-zfcp-lun-udev-rules-to-separate-files.patch +Patch91: s390-tools-sles15sp1-06-zdev-Add-support-for-handling-auto-configuration-dat.patch +Patch92: s390-tools-sles15sp1-07-zdev-Integrate-firmware-auto-configuration-with-drac.patch +Patch93: s390-tools-sles15sp1-08-zdev-Integrate-firmware-auto-configuration-with-init.patch +Patch94: s390-tools-sles15sp1-09-zdev-Implement-internal-device-attributes.patch +Patch95: s390-tools-sles15sp1-10-zdev-Implement-support-for-early-device-configuratio.patch +Patch96: s390-tools-sles15sp1-11-zdev-Do-not-call-zipl-on-initrd-update.patch +Patch97: s390-tools-sles15sp1-zdev-fix-qeth-BridgePort-and-VNICC-conflict-checking.patch BuildRoot: %{_tmppath}/%{name}-%{version}-build ExclusiveArch: s390x @@ -339,8 +348,17 @@ to list files and directories. %patch84 -p1 %patch85 -p1 %patch86 -p1 - -%patch999 -p1 +%patch87 -p1 +%patch88 -p1 +%patch89 -p1 +%patch90 -p1 +%patch91 -p1 +%patch92 -p1 +%patch93 -p1 +%patch94 -p1 +%patch95 -p1 +%patch96 -p1 +%patch97 -p1 cp -vi %{S:22} CAUTION