From a424411c8dfc944d31c2813d0845f6fa32a4eb36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Hor=C3=A1k?= Date: Thu, 18 Jun 2026 09:55:59 +0200 Subject: [PATCH 1/2] Revert "lsznet: Remove support for lcs device type" This reverts commit 69c89416b0dda98df7ab1651cfa34aab4ead7433. --- zconf/lsznet.raw | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/zconf/lsznet.raw b/zconf/lsznet.raw index fee8636e..5c0e3c22 100755 --- a/zconf/lsznet.raw +++ b/zconf/lsznet.raw @@ -35,6 +35,7 @@ readonly -a CU_CARDTYPE=( "CTC adapter" "escon channel" "ficon channel" + "LCS OSA" "OSX" "OSM" ) @@ -47,6 +48,7 @@ readonly -a CU_DEVNAME=( ctc eth eth + eth ) readonly -a CU_DEVDRV=( @@ -55,6 +57,7 @@ readonly -a CU_DEVDRV=( ctcm ctcm ctcm + lcs qeth qeth ) @@ -65,11 +68,13 @@ readonly -a CU_GROUPCHANNELS=( 2 2 2 + 2 3 3 ) readonly -a CHPIDTYPES=( + [0x10]=OSE [0x11]=OSD [0x24]=IQD [0x30]=OSX @@ -84,6 +89,7 @@ readonly -a CU_TCPIP=( 3088/08 3088/1f 3088/1e + 3088/60 1731/02 1731/02 ) -- 2.54.0 From 763a3ac7e2e3bb3e4ba15026b8fde55f2c0d2c83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Hor=C3=A1k?= Date: Thu, 18 Jun 2026 09:57:30 +0200 Subject: [PATCH 2/2] Revert "zdev: Remove support for lcs device type" This reverts commit b6bb87f377bb829e470fb643c18ce732f58cbf51. --- zdev/dracut/95zdev-kdump/module-setup.sh | 2 +- zdev/dracut/95zdev/module-setup.sh | 2 +- zdev/include/lcs.h | 25 ++ zdev/include/lcs_auto.h | 24 ++ zdev/initramfs/hooks/s390-tools-zdev | 2 +- zdev/man/chzdev.8 | 2 +- zdev/man/lszdev.8 | 2 +- zdev/src/Makefile | 6 + zdev/src/ctc_auto.c | 13 +- zdev/src/devtype.c | 2 + zdev/src/lcs.c | 429 +++++++++++++++++++++++ zdev/src/lcs_auto.c | 348 ++++++++++++++++++ zdev/src/namespace.c | 2 + 13 files changed, 852 insertions(+), 7 deletions(-) create mode 100644 zdev/include/lcs.h create mode 100644 zdev/include/lcs_auto.h create mode 100644 zdev/src/lcs.c create mode 100644 zdev/src/lcs_auto.c diff --git a/zdev/dracut/95zdev-kdump/module-setup.sh b/zdev/dracut/95zdev-kdump/module-setup.sh index 1052190f..71855bc6 100755 --- a/zdev/dracut/95zdev-kdump/module-setup.sh +++ b/zdev/dracut/95zdev-kdump/module-setup.sh @@ -55,7 +55,7 @@ cmdline() { installkernel() { # Add modules for all device types supported by chzdev (required for # auto-configuration) - instmods ctcm qeth qeth_l2 qeth_l3 dasd_mod dasd_eckd_mod dasd_fba_mod \ + instmods ctcm lcs qeth qeth_l2 qeth_l3 dasd_mod dasd_eckd_mod dasd_fba_mod \ dasd_diag_mod zfcp } diff --git a/zdev/dracut/95zdev/module-setup.sh b/zdev/dracut/95zdev/module-setup.sh index d4ba2ed8..ae5c9811 100644 --- a/zdev/dracut/95zdev/module-setup.sh +++ b/zdev/dracut/95zdev/module-setup.sh @@ -61,7 +61,7 @@ installkernel() { # Add modules for all device types supported by chzdev (required for # auto-configuration) hostonly="$(optional_hostonly)" \ - instmods ctcm qeth qeth_l2 qeth_l3 dasd_mod dasd_eckd_mod dasd_fba_mod \ + instmods ctcm lcs qeth qeth_l2 qeth_l3 dasd_mod dasd_eckd_mod dasd_fba_mod \ dasd_diag_mod zfcp } diff --git a/zdev/include/lcs.h b/zdev/include/lcs.h new file mode 100644 index 00000000..4b9a01b4 --- /dev/null +++ b/zdev/include/lcs.h @@ -0,0 +1,25 @@ +/* + * zdev - Modify and display the persistent configuration of devices + * + * 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. + */ + +#ifndef LCS_H +#define LCS_H + +#define LCS_MOD_NAME "lcs" +#define LCS_CCWGROUPDRV_NAME "lcs" +#define LCS_CCWDRV_NAME "lcs" +#define LCS_ROOTDRV_NAME "lcs" +#define LCS_NUM_DEVS 2 + +struct devtype; +struct namespace; + +extern struct devtype lcs_devtype; +extern struct namespace lcs_namespace; + +#endif /* LCS_H */ diff --git a/zdev/include/lcs_auto.h b/zdev/include/lcs_auto.h new file mode 100644 index 00000000..31b9c367 --- /dev/null +++ b/zdev/include/lcs_auto.h @@ -0,0 +1,24 @@ +/* + * zdev - Modify and display the persistent configuration of devices + * + * 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. + */ + +#ifndef LCS_AUTO_H +#define LCS_AUTO_H + +#include "misc.h" + +struct util_list; +struct ccwgroup_devid; +struct ccw_devid; + +void lcs_auto_add_ids(struct util_list *); +exit_code_t lcs_auto_get_devid(struct ccwgroup_devid *, struct ccw_devid *, + err_t); +exit_code_t lcs_auto_is_possible(struct ccwgroup_devid *, err_t); + +#endif /* LCS_AUTO_H */ diff --git a/zdev/initramfs/hooks/s390-tools-zdev b/zdev/initramfs/hooks/s390-tools-zdev index 9ac5a6b6..06351995 100644 --- a/zdev/initramfs/hooks/s390-tools-zdev +++ b/zdev/initramfs/hooks/s390-tools-zdev @@ -29,7 +29,7 @@ esac # Add modules for all device types supported by chzdev (required for # auto-configuration) -zdev_modules="qeth qeth_l2 qeth_l3 dasd_mod dasd_eckd_mod dasd_fba_mod dasd_diag_mod zfcp" +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} diff --git a/zdev/man/chzdev.8 b/zdev/man/chzdev.8 index 6e402719..91dddc35 100644 --- a/zdev/man/chzdev.8 +++ b/zdev/man/chzdev.8 @@ -61,7 +61,7 @@ chzdev - Configure z Systems specific devices .B chzdev is a tool for configuring devices and device drivers which are specific to the IBM z Systems platform. Supported device types include storage devices (DASD -and zFCP) and networking devices (QETH and CTC). +and zFCP) and networking devices (QETH, CTC and LCS). .PP . .SS "Configurations" diff --git a/zdev/man/lszdev.8 b/zdev/man/lszdev.8 index 03b80c30..3aa1b941 100644 --- a/zdev/man/lszdev.8 +++ b/zdev/man/lszdev.8 @@ -60,7 +60,7 @@ lszdev - Display configuration of z Systems specific devices .B lszdev displays the configuration of devices and device drivers that are specific to IBM z Systems. Supported device types include -storage devices (DASD and zFCP) and networking devices (QETH and CTC). +storage devices (DASD and zFCP) and networking devices (QETH, CTC and LCS). .PP . .PP diff --git a/zdev/src/Makefile b/zdev/src/Makefile index 57e28015..07943073 100644 --- a/zdev/src/Makefile +++ b/zdev/src/Makefile @@ -36,6 +36,9 @@ chzdev_objects += nic.o qeth.o qeth_auto.o # CTC devtype chzdev_objects += ctc.o ctc_auto.o +# LCS devtype +chzdev_objects += lcs.o lcs_auto.o + # AP devtype chzdev_objects += ap.o @@ -69,6 +72,9 @@ lszdev_objects += nic.o qeth.o qeth_auto.o # CTC devtype lszdev_objects += ctc.o ctc_auto.o +# LCS devtype +lszdev_objects += lcs.o lcs_auto.o + # AP devtype lszdev_objects += ap.o diff --git a/zdev/src/ctc_auto.c b/zdev/src/ctc_auto.c index 11c0a5e0..e2b7701b 100644 --- a/zdev/src/ctc_auto.c +++ b/zdev/src/ctc_auto.c @@ -15,6 +15,7 @@ #include "ccwgroup.h" #include "ctc.h" #include "ctc_auto.h" +#include "lcs.h" #include "module.h" #include "path.h" @@ -38,7 +39,9 @@ static struct cutype ctc_cutypes[] = { * 1. A CTC device can be grouped from 2 CCW devices * a) Read device * b) Write device - * 2. All CCW devices must be bound to the CTC CCW device driver. + * 2. All CCW devices must be bound to the CTC CCW device driver. Note that + * due to an overlap in CU-Types, CCW device could also be bound to + * the LCS device driver. * 3. The subchannel of all CCW devices must be defined with the same CHPID * 4. None of the CCW devices is part of an existing CCWGROUP device */ @@ -110,7 +113,7 @@ static exit_code_t add_cb(const char *path, const char *filename, void *data) } /* Return a sorted ptrlist of struct ccw_devinfos for all CCW devices - * bound to the ctc CCW device driver with matching CUTYPE. + * bound to the ctc or lcs CCW device driver with matching CUTYPE. * The result must be freed using ptrlist_free(,1); */ static struct util_list *read_sorted_ctc_devinfos(void) { @@ -127,6 +130,12 @@ static struct util_list *read_sorted_ctc_devinfos(void) 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 (util_path_is_dir(path)) + path_for_each(path, add_cb, infos); + free(path); + /* For each CHPID: Find groups. Add to result list. */ util_list_sort(infos, info_cmp, NULL); diff --git a/zdev/src/devtype.c b/zdev/src/devtype.c index fd34bb5a..3085fdd6 100644 --- a/zdev/src/devtype.c +++ b/zdev/src/devtype.c @@ -18,6 +18,7 @@ #include "devnode.h" #include "devtype.h" #include "generic_ccw.h" +#include "lcs.h" #include "misc.h" #include "module.h" #include "namespace.h" @@ -35,6 +36,7 @@ struct devtype *devtypes[] = { &zfcp_devtype, &qeth_devtype, &ctc_devtype, + &lcs_devtype, &ap_devtype, &virtio_devtype, &generic_ccw_devtype, /* Generic types should come last. */ diff --git a/zdev/src/lcs.c b/zdev/src/lcs.c new file mode 100644 index 00000000..861d030d --- /dev/null +++ b/zdev/src/lcs.c @@ -0,0 +1,429 @@ +/* + * zdev - Modify and display the persistent configuration of devices + * + * 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. + */ + +#include +#include + +#include "attrib.h" +#include "ccw.h" +#include "ccwgroup.h" +#include "device.h" +#include "devtype.h" +#include "internal.h" +#include "lcs.h" +#include "lcs_auto.h" +#include "misc.h" +#include "namespace.h" +#include "setting.h" + +#define DEVNAME "LCS device" + +/* + * LCS device ID namespace methods. + */ +static exit_code_t lcs_parse_devid(struct ccwgroup_devid *devid_ptr, + const char *id, err_t err) +{ + struct ccwgroup_devid devid; + const char *reason = NULL; + exit_code_t rc; + + rc = ccwgroup_parse_devid(&devid, id, err); + if (rc) + return rc; + + if (devid.num > LCS_NUM_DEVS) { + reason = "Too many CCW device IDs specified"; + rc = EXIT_INVALID_ID; + } else if (devid_ptr) + *devid_ptr = devid; + + if (reason) { + err_t_print(err, "Error in %s ID format: %s: %s\n", DEVNAME, + reason, id); + } + + return rc; +} + +static exit_code_t lcs_parse_devid_range(struct ccwgroup_devid *from_ptr, + struct ccwgroup_devid *to_ptr, + const char *range, err_t err) +{ + char *from_str, *to_str; + struct ccwgroup_devid from, to; + exit_code_t rc; + const char *reason = NULL; + + /* Split range. */ + from_str = misc_strdup(range); + to_str = strchr(from_str, '-'); + if (!to_str) { + rc = EXIT_INVALID_ID; + reason = "Missing hyphen"; + goto out; + } + *to_str = 0; + to_str++; + + /* Parse range start end end ID. */ + rc = lcs_parse_devid(&from, from_str, err); + if (rc) + goto out; + + rc = lcs_parse_devid(&to, to_str, err); + if (rc) + goto out; + + /* Only allow ranges on CCWGROUP devices specified as single ID. */ + if (from.num != 1 || to.num != 1) { + rc = EXIT_INVALID_ID; + reason = "Ranges only supported on single CCW device IDs"; + goto out; + } + + rc = EXIT_OK; + if (from_ptr) + *from_ptr = from; + if (to_ptr) + *to_ptr = to; + +out: + free(from_str); + + if (reason) { + err_t_print(err, "Error in %s ID range format: %s: %s\n", + DEVNAME, reason, range); + } + + return rc; +} + +static bool lcs_parse_devid_range_simple(struct ccwgroup_devid *from, + struct ccwgroup_devid *to, + const char *range) +{ + if (lcs_parse_devid_range(from, to, range, err_ignore) == EXIT_OK) + return true; + + return false; +} + +static exit_code_t lcs_ns_is_id_valid(const char *id, err_t err) +{ + return lcs_parse_devid(NULL, id, err); +} + +static char *lcs_ns_normalize_id(const char *id) +{ + struct ccwgroup_devid devid; + + if (lcs_parse_devid(&devid, id, err_ignore) != EXIT_OK) + return NULL; + + return ccwgroup_devid_to_str(&devid); +} + +static void *lcs_ns_parse_id(const char *id, err_t err) +{ + struct ccwgroup_devid *devid; + + devid = misc_malloc(sizeof(struct ccwgroup_devid)); + if (lcs_parse_devid(devid, id, err) != EXIT_OK) { + free(devid); + return NULL; + } + + return devid; +} + +static exit_code_t lcs_ns_is_id_range_valid(const char *range, err_t err) +{ + return lcs_parse_devid_range(NULL, NULL, range, err); +} + +static unsigned long lcs_ns_num_ids_in_range(const char *range) +{ + struct ccwgroup_devid f, t; + + if (!lcs_parse_devid_range_simple(&f, &t, range)) + return 0; + + if (f.devid[0].cssid != t.devid[0].cssid || + f.devid[0].ssid != t.devid[0].ssid) + return 0; + + if (f.devid[0].devno > t.devid[0].devno) + return 0; + + return t.devid[0].devno - f.devid[0].devno + 1; +} + +static void lcs_ns_range_start(struct ns_range_iterator *it, const char *range) +{ + struct ccwgroup_devid from, to; + + if (!lcs_parse_devid_range_simple(&from, &to, range)) { + memset(it, 0, sizeof(struct ns_range_iterator)); + return; + } + + it->devid = ccwgroup_copy_devid(&from); + it->devid_last = ccwgroup_copy_devid(&to); + it->id = ccwgroup_devid_to_str(it->devid); +} + +static bool lcs_ns_is_id_blacklisted(const char *id) +{ + struct ccwgroup_devid devid; + char *ccw_id; + unsigned int i; + bool result = false; + + if (lcs_parse_devid(&devid, id, err_ignore) != EXIT_OK) + return false; + for (i = 0; i < devid.num; i++) { + ccw_id = ccw_devid_to_str(&devid.devid[i]); + result = ccw_is_id_blacklisted(ccw_id); + free(ccw_id); + if (result) + break; + } + + return result; +} + +static void lcs_ns_unblacklist_id(const char *id) +{ + struct ccwgroup_devid devid; + char *ccw_id; + unsigned int i; + + if (lcs_parse_devid(&devid, id, err_ignore) != EXIT_OK) + return; + for (i = 0; i < devid.num; i++) { + ccw_id = ccw_devid_to_str(&devid.devid[i]); + if (ccw_is_id_blacklisted(ccw_id)) + ccw_unblacklist_id(ccw_id); + free(ccw_id); + } +} + +/* + * LCS device ID namespace. + */ + +struct namespace lcs_namespace = { + .devname = DEVNAME, + .is_id_valid = lcs_ns_is_id_valid, + .is_id_similar = ccwgroup_is_id_similar, + .cmp_ids = ccwgroup_cmp_ids, + .normalize_id = lcs_ns_normalize_id, + .parse_id = lcs_ns_parse_id, + .cmp_parsed_ids = ccwgroup_cmp_parsed_ids, + .qsort_cmp = ccwgroup_qsort_cmp, + .is_id_range_valid = lcs_ns_is_id_range_valid, + .num_ids_in_range = lcs_ns_num_ids_in_range, + .is_id_in_range = ccwgroup_is_id_in_range, + .range_start = lcs_ns_range_start, + .range_next = ccwgroup_range_next, + + /* Blacklist handling. */ + .is_blacklist_active = ccw_is_blacklist_active, + .is_id_blacklisted = lcs_ns_is_id_blacklisted, + .is_id_range_blacklisted = ccw_is_id_range_blacklisted, + .unblacklist_id = lcs_ns_unblacklist_id, + .unblacklist_id_range = ccw_unblacklist_id_range, + .blacklist_persist = ccw_blacklist_persist, +}; + +/* + * LCS device attributes. + */ + +static struct attrib lcs_attr_lancmd_timeout = { + .name = "lancmd_timeout", + .title = "Modify LAN command timeout", + .desc = + "Specify the time in seconds that the LCS driver waits for a reply\n" + "after issuing a LAN command to the LAN adapter.\n", + .defval = "5", + .accept = ACCEPT_ARRAY(ACCEPT_NUM_GE(1)), +}; + +static struct attrib lcs_attr_recover = { + .name = "recover", + .title = "Trigger device recovery", + .desc = + "Write '1' to this attribute to restart the recovery process for the\n" + "QETH device.\n", + .accept = ACCEPT_ARRAY(ACCEPT_NUM(1)), + .writeonly = 1, + .activeonly = 1, +}; + +/* + * LCS subtype methods. + */ + +static exit_code_t lcs_st_is_definable(struct subtype *st, const char *id, + err_t err) +{ + struct ccwgroup_subtype_data *data = st->data; + struct ccwgroup_devid devid; + exit_code_t rc; + + rc = ccwgroup_parse_devid(&devid, id, err); + if (rc) + return rc; + + if (subtype_device_exists_active(st, id)) + return EXIT_OK; + + if (devid.num == data->num_devs) + return lcs_auto_is_possible(&devid, err); + + if (devid.num == 1) + return lcs_auto_get_devid(NULL, &devid.devid[0], err); + + err_t_print(err, "Invalid number of CCW device IDs\n"); + + return EXIT_INVALID_ID; +} + +/** + * device_detect_definable - Detect configuration of definable device + * @st: Device subtype + * @dev: Device + * + * Detect the full ID and default parameters for non-existing but definable + * device @dev and update active.definable. Return %EXIT_OK on success, or an + * error code otherwise. + */ +static exit_code_t lcs_st_detect_definable(struct subtype *st, + struct device *dev) +{ + struct ccwgroup_devid *devid; + exit_code_t rc; + + devid = dev->devid; + if (devid->num == 1) { + /* Detect possible group for this device. */ + rc = lcs_auto_get_devid(devid, &devid->devid[0], + err_delayed_print); + if (rc) { + error("Auto-detection failed for %s %s\n" + "Please be sure to specify full CCWGROUP ID!\n", + st->devname, dev->id); + return rc; + } + free(dev->id); + dev->id = ccwgroup_devid_to_str(dev->devid); + } + + dev->active.definable = 1; + + return EXIT_OK; +} + +static void lcs_st_add_definable_ids(struct subtype *st, struct util_list *ids) +{ + lcs_auto_add_ids(ids); +} + +/* + * LCS subtype. + */ + +static struct ccwgroup_subtype_data lcs_data = { + .ccwgroupdrv = LCS_CCWGROUPDRV_NAME, + .ccwdrv = LCS_CCWDRV_NAME, + .rootdrv = LCS_ROOTDRV_NAME, + .mod = LCS_MOD_NAME, + .num_devs = LCS_NUM_DEVS, +}; + +static struct subtype lcs_subtype = { + .super = &ccwgroup_subtype, + .devtype = &lcs_devtype, + .name = "lcs", + .title = "LAN-Channel-Station (LCS) network devices", + .devname = DEVNAME, + .modules = STRING_ARRAY(LCS_MOD_NAME), + .namespace = &lcs_namespace, + .data = &lcs_data, + + .dev_attribs = ATTRIB_ARRAY( + &ccw_attr_online, + &lcs_attr_lancmd_timeout, + &lcs_attr_recover, + &internal_attr_early, + ), + .unknown_dev_attribs = 1, + .support_definable = 1, + + .is_definable = &lcs_st_is_definable, + .detect_definable = &lcs_st_detect_definable, + .add_definable_ids = &lcs_st_add_definable_ids, +}; + +/* + * LCS devtype methods. + */ + +/* Clean up all resources used by devtype object. */ +static void lcs_devtype_exit(struct devtype *dt) +{ + setting_list_free(dt->active_settings); + setting_list_free(dt->persistent_settings); +} + +static exit_code_t lcs_devtype_read_settings(struct devtype *dt, + config_t config) +{ + /* No kernel or module parameters exist for the lcs device driver, + * but at least determine module loaded state. */ + dt->active_settings = setting_list_new(); + dt->persistent_settings = setting_list_new(); + + if (SCOPE_ACTIVE(config)) + dt->active_exists = devtype_is_module_loaded(dt); + + return EXIT_OK; +} + +static exit_code_t lcs_devtype_write_settings(struct devtype *dt, + config_t config) +{ + /* No kernel or module parameters exist for the lcs device driver. */ + + return EXIT_OK; +} + +/* + * LCS devtype. + */ + +struct devtype lcs_devtype = { + .name = "lcs", + .title = "", /* Only use subtypes. */ + .devname = "LCS", + + .subtypes = SUBTYPE_ARRAY( + &lcs_subtype, + ), + + .type_attribs = ATTRIB_ARRAY( + ), + + .exit = &lcs_devtype_exit, + + .read_settings = &lcs_devtype_read_settings, + .write_settings = &lcs_devtype_write_settings, +}; diff --git a/zdev/src/lcs_auto.c b/zdev/src/lcs_auto.c new file mode 100644 index 00000000..848897cb --- /dev/null +++ b/zdev/src/lcs_auto.c @@ -0,0 +1,348 @@ +/* + * zdev - Modify and display the persistent configuration of devices + * + * 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. + */ + +#include + +#include "lib/util_path.h" + +#include "ccw.h" +#include "ccwgroup.h" +#include "ctc.h" +#include "lcs.h" +#include "lcs_auto.h" +#include "module.h" +#include "path.h" + +struct cutype { + unsigned int cutype:16; + unsigned int cumodel:8; +}; + +static struct cutype lcs_cutypes[] = { + { .cutype = 0x3088, .cumodel = 0x08, }, + { .cutype = 0x3088, .cumodel = 0x1f, }, + { .cutype = 0x3088, .cumodel = 0x60, }, +}; + +/* + * LCS autodetection + * + * A LCS device must be grouped before it can be used. The following + * rules apply to grouping: + * + * 1. A LCS device can be grouped from 2 CCW devices + * a) Read device + * b) Write device + * 2. All CCW devices must be bound to the LCS CCW device driver. Note that + * due to an overlap in CU-Types, CCW device could also be bound to + * the LCS device driver. + * 3. The subchannel of all CCW devices must be defined with the same CHPID + * 4. None of the CCW devices is part of an existing CCWGROUP device + */ + +/* Compare by: 1. CHPID, 2. CUTYPE, 3. DEVTYPE, 4. CCW device ID + * -1 = a < b 1 = a > b 0 = a == b. */ +static int info_cmp(void *a, void *b, void *data) +{ + struct ptrlist_node *pa = a, *pb = b; + struct ccw_devinfo *ia = pa->ptr, *ib = pb->ptr; + int r; + + r = ccw_devinfo_chpids_cmp(ia, ib); + if (r) + return r; + + r = ccw_devinfo_cutype_cmp(ia, ib); + if (r) + return r; + + r = ccw_devinfo_devtype_cmp(ia, ib); + if (r) + return r; + + return ccw_cmp_devids(&ia->devid, &ib->devid); +} + +static bool is_compatible(struct ccw_devinfo *a, struct ccw_devinfo *b) +{ + if (ccw_devinfo_chpids_cmp(a, b) == 0 && + ccw_devinfo_cutype_cmp(a, b) == 0 && + ccw_devinfo_devtype_cmp(a, b) == 0) + return true; + + return false; +} + +static bool is_lcs(struct ccw_devinfo *info) +{ + unsigned int i; + + /* Rule out devices which can be confirmed to be CTC devices. */ + if (ctc_confirm(&info->devid)) + return false; + + for (i = 0; i < ARRAY_SIZE(lcs_cutypes); i++) { + if (info->cutype == lcs_cutypes[i].cutype && + info->cumodel == lcs_cutypes[i].cumodel) + return true; + } + + return false; +} + +/* Add device info for all LCS CCW devices to ptrlist in data. */ +static exit_code_t add_cb(const char *path, const char *filename, void *data) +{ + struct ccw_devid devid; + struct util_list *infos = data; + struct ccw_devinfo *devinfo; + + if (!strchr(filename, '.')) + return EXIT_OK; + if (!ccw_parse_devid_simple(&devid, filename)) + return EXIT_OK; + devinfo = ccw_devinfo_get(&devid, 0); + if (devinfo->exists && is_lcs(devinfo)) + ptrlist_add(infos, devinfo); + else + free(devinfo); + + return EXIT_OK; +} + +/* Return a sorted ptrlist of struct ccw_devinfos for all CCW devices + * bound to the lcs or ctc CCW device driver with matching CUTYPE. + * The result must be freed using ptrlist_free(,1); */ +static struct util_list *read_sorted_lcs_devinfos(void) +{ + struct util_list *infos; + char *path; + + /* Get CHPID information for all devices handled by the LCS driver. */ + infos = ptrlist_new(); + + /* 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 (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 (util_path_is_dir(path)) + path_for_each(path, add_cb, infos); + free(path); + + /* For each CHPID: Find groups. Add to result list. */ + util_list_sort(infos, info_cmp, NULL); + + return infos; +} + +static void add_ccwgroup_devid(struct util_list *devids, struct ccw_devid *read, + struct ccw_devid *write) +{ + struct ccwgroup_devid devid; + + devid.devid[0] = *read; + devid.devid[1] = *write; + devid.num = LCS_NUM_DEVS; + + ptrlist_add(devids, ccwgroup_copy_devid(&devid)); +} + +static void add_groupable_devids(struct util_list *devids, + struct util_list *infos) +{ + struct ptrlist_node *curr, *next; + struct ccw_devinfo *r, *w; + + /* For each CHPID: Find groups. Add to result list. */ + curr = util_list_start(infos); + while (curr) { + next = util_list_next(infos, curr); + if (!next) + break; + r = curr->ptr; + w = next->ptr; + if (is_compatible(r, w) && EVEN(r->devid.devno) && + ccw_devid_distance(&r->devid, &w->devid) == 1) { + add_ccwgroup_devid(devids, &r->devid, &w->devid); + curr = util_list_next(infos, next); + } else + curr = next; + } +} + +/* Add CCWGROUP IDs of lcs devices that can be grouped to strlist @ids. */ +void lcs_auto_add_ids(struct util_list *ids) +{ + struct util_list *infos, *devids; + struct ptrlist_node *p; + char *id; + + infos = read_sorted_lcs_devinfos(); + devids = ptrlist_new(); + add_groupable_devids(devids, infos); + + util_list_iterate(devids, p) { + id = ccwgroup_devid_to_str(p->ptr); + strlist_add(ids, id); + free(id); + } + + ptrlist_free(devids, 1); + ptrlist_free(infos, 1); +} + +exit_code_t lcs_auto_get_devid(struct ccwgroup_devid *devid_ptr, + struct ccw_devid *ccw_devid, err_t err) +{ + struct util_list *devids, *infos; + struct ptrlist_node *p, *read, *write; + struct ccw_devinfo *r, *w; + struct ccwgroup_devid *devid; + exit_code_t rc; + + infos = read_sorted_lcs_devinfos(); + + /* Try to find an ID from the canonical auto-generated list. */ + devids = ptrlist_new(); + add_groupable_devids(devids, infos); + + util_list_iterate(devids, p) { + devid = p->ptr; + if (ccw_cmp_devids(ccw_devid, &devid->devid[0]) != 0) + continue; + rc = EXIT_OK; + if (devid_ptr) + *devid_ptr = *devid; + goto out; + } + + /* Try to create a CCWGROUP ID with the specified ID as read device. */ + + /* Get CCW device info for read device. */ + read = NULL; + util_list_iterate(infos, read) { + r = read->ptr; + if (ccw_cmp_devids(&r->devid, ccw_devid) == 0) + break; + } + if (!read) { + err_t_print(err, "Read CCW device not found\n"); + rc = EXIT_GROUP_NOT_FOUND; + goto out; + } + if (!EVEN(r->devid.devno)) { + err_t_print(err, "Read CCW device number not even\n"); + rc = EXIT_GROUP_INVALID; + goto out; + } + + /* Get CCW device ID for write device. */ + write = util_list_next(infos, read); + if (!write) { + err_t_print(err, "Write CCW device not found\n"); + rc = EXIT_GROUP_NOT_FOUND; + goto out; + } + w = write->ptr; + if (!is_compatible(r, w)) { + err_t_print(err, "No compatible write CCW device found\n"); + rc = EXIT_GROUP_INVALID; + goto out; + } + if (ccw_devid_distance(&r->devid, &w->devid) != 1) { + err_t_print(err, "Write CCW device ID must be read plus one\n"); + rc = EXIT_GROUP_INVALID; + goto out; + } + + rc = EXIT_OK; + if (devid_ptr) { + devid_ptr->devid[0] = r->devid; + devid_ptr->devid[1] = w->devid; + devid_ptr->num = LCS_NUM_DEVS; + } + +out: + ptrlist_free(devids, 1); + ptrlist_free(infos, 1); + + return rc; +} + +exit_code_t lcs_auto_is_possible(struct ccwgroup_devid *devid, err_t err) +{ + struct ccw_devinfo *info[LCS_NUM_DEVS]; + unsigned int i; + char *ccwid; + const char *msg; + exit_code_t rc; + + if (devid->num < LCS_NUM_DEVS) { + err_t_print(err, "Not enough CCW device IDs in LCS device " + "ID\n"); + return EXIT_INCOMPLETE_ID; + } + if (devid->num > LCS_NUM_DEVS) { + err_t_print(err, "LCS device ID contains too many CCW device " + "IDs\n"); + return EXIT_INVALID_ID; + } + if (ccw_devid_distance(&devid->devid[0], &devid->devid[1]) != 1) { + err_t_print(err, "Write device ID must be read plus one\n"); + return EXIT_GROUP_INVALID; + } + if (!EVEN(devid->devid[0].devno)) { + err_t_print(err, "Device number of read device ID must be " + "even\n"); + return EXIT_GROUP_INVALID; + } + + for (i = 0; i < ARRAY_SIZE(info); i++) + info[i] = ccw_devinfo_get(&devid->devid[i], 1); + + rc = EXIT_OK; + msg = NULL; + for (i = 0; i < ARRAY_SIZE(info); i++) { + if (!info[i]->exists) { + msg = "CCW device %s does not exist\n"; + rc = EXIT_GROUP_NOT_FOUND; + } else if (info[i]->grouped) { + msg = "CCW device %s already grouped\n"; + rc = EXIT_GROUP_ALREADY; + } else if (i > 0 && + ccw_devinfo_chpids_cmp(info[i - 1], info[i]) != 0) { + msg = "CCW device %s is not on the same CHPID\n"; + rc = EXIT_GROUP_INVALID; + } else if (i > 0 && + ccw_devinfo_cutype_cmp(info[i - 1], info[i]) != 0) { + msg = "CUTYPE of CCW device %s differs\n"; + rc = EXIT_GROUP_INVALID; + } else if (i > 0 && + ccw_devinfo_devtype_cmp(info[i - 1], info[i]) != 0) { + msg = "DEVTYPE of CCW device %s differs\n"; + rc = EXIT_GROUP_INVALID; + } + if (!msg) + continue; + ccwid = ccw_devid_to_str(&devid->devid[i]); + err_t_print(err, msg, ccwid); + free(ccwid); + break; + } + + for (i = 0; i < ARRAY_SIZE(info); i++) + free(info[i]); + + return rc; +} diff --git a/zdev/src/namespace.c b/zdev/src/namespace.c index b4c3d754..f0ed3c97 100644 --- a/zdev/src/namespace.c +++ b/zdev/src/namespace.c @@ -13,6 +13,7 @@ #include "ccw.h" #include "ctc.h" #include "devtype.h" +#include "lcs.h" #include "namespace.h" #include "qeth.h" #include "zfcp_lun.h" @@ -23,6 +24,7 @@ struct namespace *namespaces[] = { &zfcp_lun_namespace, &qeth_namespace, &ctc_namespace, + &lcs_namespace, &ap_namespace, NULL, }; -- 2.54.0