- 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
1118 lines
29 KiB
Diff
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
|
|
|