s390utils/s390utils-lcs-revert.patch
Dan Horák 384a4f1d5e - rebased to 2.42.1 (RHEL-174896)
- hyptop: Backward compatibility options causes other issues (RHEL-166158)
- zipl/boot: Fix stage3 secure boot trailer placement (RHEL-169659)
- SEL-EBC: instrumentation of an SEL initramfs to support early boot customization (RHEL-136434)
- SEL-EBC: tool to build basic SEL-qcow2 image supporting early boot customization (RHEL-136437)
- KVM: Enhance pvimg info command to display additional SE image information (RHEL-73137)
- Resolves: RHEL-174896 RHEL-166158 RHEL-169659 RHEL-136434 RHEL-136437 RHEL-73137
2026-06-18 12:02:52 +02:00

1118 lines
29 KiB
Diff

From a424411c8dfc944d31c2813d0845f6fa32a4eb36 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
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?= <dan@danny.cz>
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 <stdlib.h>
+#include <string.h>
+
+#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 <string.h>
+
+#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