Add ccwgroup node device type supporting qeth generic driver.
Signed-off-by: Boris Fiuczynski <fiuczy(a)linux.ibm.com>
---
docs/manpages/virsh.rst | 6 +-
include/libvirt/libvirt-nodedev.h | 1 +
src/conf/node_device_conf.c | 212 ++++++++++++++++++
src/conf/node_device_conf.h | 29 ++-
src/conf/schemas/nodedev.rng | 43 ++++
src/conf/virnodedeviceobj.c | 4 +-
src/libvirt_private.syms | 2 +
src/node_device/node_device_driver.c | 5 +
src/node_device/node_device_udev.c | 41 ++++
src/util/virccw.c | 102 +++++++++
src/util/virccw.h | 22 ++
tests/nodedevschemadata/ccwgroup_0_0_bd00.xml | 20 ++
tests/nodedevxml2xmlout/ccwgroup_0_0_bd00.xml | 1 +
tests/nodedevxml2xmltest.c | 1 +
tools/virsh-nodedev.c | 3 +
15 files changed, 487 insertions(+), 5 deletions(-)
create mode 100644 tests/nodedevschemadata/ccwgroup_0_0_bd00.xml
create mode 120000 tests/nodedevxml2xmlout/ccwgroup_0_0_bd00.xml
diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst
index 868b354b2f..5e5734dff1 100644
--- a/docs/manpages/virsh.rst
+++ b/docs/manpages/virsh.rst
@@ -5532,9 +5532,9 @@ List all of the devices available on the node that are known by
libvirt.
separated by comma, e.g. --cap pci,scsi. Valid capability types include
'system', 'pci', 'usb_device', 'usb', 'net',
'scsi_host', 'scsi_target',
'scsi', 'storage', 'fc_host', 'vports',
'scsi_generic', 'drm', 'mdev',
-'mdev_types', 'ccw', 'css', 'ap_card',
'ap_queue', 'ap_matrix'. By default,
-only active devices are listed. *--inactive* is used to list only inactive
-devices, and *--all* is used to list both active and inactive devices.
+'mdev_types', 'ccw', 'ccwgroup', 'css',
'ap_card', 'ap_queue', 'ap_matrix'.
+By default, only active devices are listed. *--inactive* is used to list only
+inactive devices, and *--all* is used to list both active and inactive devices.
*--persistent* is used to list only persistent devices, and *--transient* is
used to list only transient devices. Not providing *--persistent* or
*--transient* will list all devices unless filtered otherwise. *--transient*
diff --git a/include/libvirt/libvirt-nodedev.h b/include/libvirt/libvirt-nodedev.h
index ec26c7a5e1..79bee4fb04 100644
--- a/include/libvirt/libvirt-nodedev.h
+++ b/include/libvirt/libvirt-nodedev.h
@@ -90,6 +90,7 @@ typedef enum {
VIR_CONNECT_LIST_NODE_DEVICES_CAP_AP_QUEUE = 1 << 19, /* s390 AP Queue
(Since: 7.0.0) */
VIR_CONNECT_LIST_NODE_DEVICES_CAP_AP_MATRIX = 1 << 20, /* s390 AP Matrix
(Since: 7.0.0) */
VIR_CONNECT_LIST_NODE_DEVICES_CAP_VPD = 1 << 21, /* Device with VPD
(Since: 7.9.0) */
+ VIR_CONNECT_LIST_NODE_DEVICES_CAP_CCWGROUP_DEV = 1 << 22, /* s390 CCWGROUP
device (Since: 11.1.0) */
VIR_CONNECT_LIST_NODE_DEVICES_PERSISTENT = 1 << 28, /* Persisted devices
(Since: 10.1.0) */
VIR_CONNECT_LIST_NODE_DEVICES_TRANSIENT = 1 << 29, /* Transient devices
(Since: 10.1.0) */
diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c
index bfd81b1692..1649df09a1 100644
--- a/src/conf/node_device_conf.c
+++ b/src/conf/node_device_conf.c
@@ -71,6 +71,12 @@ VIR_ENUM_IMPL(virNodeDevCap,
"ap_queue",
"ap_matrix",
"vpd",
+ "ccwgroup",
+);
+
+VIR_ENUM_IMPL(virNodeDevCCWGroupCap,
+ VIR_NODE_DEV_CAP_CCWGROUP_LAST,
+ "qeth_generic",
);
VIR_ENUM_IMPL(virNodeDevNetCap,
@@ -670,6 +676,53 @@ virNodeDeviceCapCCWStateTypeFormat(virBuffer *buf,
}
+static void
+virNodeDeviceCapCCWGroupQethFormat(virBuffer *buf,
+ const virCCWGroupTypeQeth *qeth)
+{
+ virBufferAsprintf(buf, "<card_type>%s</card_type>\n",
qeth->card_type);
+ virBufferAsprintf(buf, "<chpid>%s</chpid>\n", qeth->chpid);
+}
+
+
+static void
+virNodeDeviceCapCCWGroupDefFormat(virBuffer *buf,
+ const virNodeDevCapData *data)
+{
+ virNodeDevCapCCWGroup ccwgroup_dev = data->ccwgroup_dev;
+ size_t i;
+
+ virNodeDeviceCapCCWStateTypeFormat(buf, ccwgroup_dev.state);
+ virCCWDeviceAddressFormat(buf, ccwgroup_dev.address);
+
+ if (ccwgroup_dev.members) {
+ virBufferAddLit(buf, "<members>\n");
+ virBufferAdjustIndent(buf, 2);
+ for (i = 0; i < ccwgroup_dev.nmembers; i++) {
+ virBufferEscapeString(buf, "<ccw_device ref='%s'>",
+ ccwgroup_dev.members[i]->ref);
+ virBufferEscapeString(buf, "%s</ccw_device>\n",
+ ccwgroup_dev.members[i]->device);
+ }
+ virBufferAdjustIndent(buf, -2);
+ virBufferAddLit(buf, "</members>\n");
+ }
+
+ virBufferAsprintf(buf, "<capability type='%s'>\n",
+ virNodeDevCCWGroupCapTypeToString(ccwgroup_dev.type));
+ virBufferAdjustIndent(buf, 2);
+ switch (ccwgroup_dev.type) {
+ case VIR_NODE_DEV_CAP_CCWGROUP_QETH_GENERIC:
+ virNodeDeviceCapCCWGroupQethFormat(buf, &ccwgroup_dev.qeth);
+ break;
+ case VIR_NODE_DEV_CAP_CCWGROUP_LAST:
+ break;
+ }
+ virBufferAdjustIndent(buf, -2);
+ virBufferAddLit(buf, "</capability>\n");
+}
+
+
char *
virNodeDeviceDefFormat(const virNodeDeviceDef *def, unsigned int flags)
{
@@ -787,6 +840,9 @@ virNodeDeviceDefFormat(const virNodeDeviceDef *def, unsigned int
flags)
data->mdev_parent.mdev_types,
data->mdev_parent.nmdev_types);
break;
+ case VIR_NODE_DEV_CAP_CCWGROUP_DEV:
+ virNodeDeviceCapCCWGroupDefFormat(&buf, data);
+ break;
case VIR_NODE_DEV_CAP_FC_HOST:
case VIR_NODE_DEV_CAP_VPORTS:
case VIR_NODE_DEV_CAP_VPD:
@@ -1303,6 +1359,107 @@ virNodeDevCapCSSParseXML(xmlXPathContextPtr ctxt,
}
+static int
+virNodeDevCapCCWGroupQethParseXML(xmlXPathContextPtr ctxt,
+ xmlNodePtr node,
+ virCCWGroupTypeQeth *qeth)
+{
+ VIR_XPATH_NODE_AUTORESTORE(ctxt)
+ ctxt->node = node;
+
+ qeth->card_type = virXPathString("string(./card_type[1])", ctxt);
+ qeth->chpid = virXPathString("string(./chpid[1])", ctxt);
+
+ return 0;
+}
+
+
+static int
+virNodeDevCapCCWGroupParseXML(xmlXPathContextPtr ctxt,
+ virNodeDeviceDef *def,
+ xmlNodePtr node,
+ virNodeDevCapCCWGroup *ccwgroup_dev)
+{
+ VIR_XPATH_NODE_AUTORESTORE(ctxt)
+ g_autofree virCCWGroupMemberType **members = NULL;
+ g_autofree virCCWDeviceAddress *address = NULL;
+ g_autofree xmlNodePtr *ccw_device_nodes = NULL;
+ xmlNodePtr cap_node = NULL;
+ g_autofree char *state = NULL;
+ size_t i = 0;
+ int n = 0;
+
+ ctxt->node = node;
+
+ /* state is optional */
+ ccwgroup_dev->state = VIR_NODE_DEV_CCW_STATE_LAST;
+ if ((state = virXPathString("string(./state[1])", ctxt))) {
+ int val;
+ if ((val = virNodeDevCCWStateTypeFromString(state)) < 0) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("unknown state '%1$s' for
'%2$s'"), state, def->name);
+ return -1;
+ }
+ ccwgroup_dev->state = val;
+ }
+
+ address = g_new0(virCCWDeviceAddress, 1);
+
+ if (virNodeDevCCWDeviceAddressParseXML(ctxt,
+ node,
+ def->name,
+ address) < 0)
+ return -1;
+
+ ccwgroup_dev->address = g_steal_pointer(&address);
+
+ /* Parse ccw_devices in members */
+ if ((n = virXPathNodeSet("./members/ccw_device", ctxt,
&ccw_device_nodes)) < 0)
+ return -1;
+
+ ccwgroup_dev->members = g_new0(virCCWGroupMemberType *, n);
+
+ for (i = 0; i < n; i++) {
+ g_autoptr(virCCWGroupMemberType) ccwMember = g_new0(virCCWGroupMemberType, 1);
+
+ if (!(ccwMember->ref = virXMLPropString(ccw_device_nodes[i],
"ref"))) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Missing ref property on ccw_device in members for
'%1$s'"),
+ def->name);
+ return -1;
+ }
+ if (!(ccwMember->device = virXMLNodeContentString(ccw_device_nodes[i]))) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Missing data in ccw_device with ref '%1$s' in
members for '%2$s'"),
+ ccwMember->ref, def->name);
+ return -1;
+ }
+
+ VIR_APPEND_ELEMENT(ccwgroup_dev->members,
+ ccwgroup_dev->nmembers,
+ ccwMember);
+ }
+
+ /* Parse capability */
+ cap_node = virXPathNode("./capability", ctxt);
+ if (cap_node && virXMLPropEnum(cap_node, "type",
+ virNodeDevCCWGroupCapTypeFromString,
+ VIR_XML_PROP_REQUIRED, &ccwgroup_dev->type)
< 0)
+ return -1;
+
+ switch (ccwgroup_dev->type) {
+ case VIR_NODE_DEV_CAP_CCWGROUP_QETH_GENERIC:
+ if (virNodeDevCapCCWGroupQethParseXML(ctxt, cap_node, &ccwgroup_dev->qeth)
< 0)
+ return -1;
+ break;
+ case VIR_NODE_DEV_CAP_CCWGROUP_LAST:
+ break;
+ }
+
+ return 0;
+}
+
+
static int
virNodeDevCapAPAdapterParseXML(xmlXPathContextPtr ctxt,
virNodeDeviceDef *def,
@@ -2343,6 +2500,10 @@ virNodeDevCapsDefParseXML(xmlXPathContextPtr ctxt,
ret = virNodeDevCapAPMatrixParseXML(ctxt, def, node,
&caps->data.ap_matrix);
break;
+ case VIR_NODE_DEV_CAP_CCWGROUP_DEV:
+ ret = virNodeDevCapCCWGroupParseXML(ctxt, def, node,
+ &caps->data.ccwgroup_dev);
+ break;
case VIR_NODE_DEV_CAP_MDEV_TYPES:
case VIR_NODE_DEV_CAP_FC_HOST:
case VIR_NODE_DEV_CAP_VPORTS:
@@ -2635,6 +2796,19 @@ virNodeDevCapsDefFree(virNodeDevCapsDef *caps)
case VIR_NODE_DEV_CAP_CCW_DEV:
g_free(data->ccw_dev.dev_addr);
break;
+ case VIR_NODE_DEV_CAP_CCWGROUP_DEV:
+ g_free(data->ccwgroup_dev.address);
+ for (i = 0; i < data->ccwgroup_dev.nmembers; i++)
+ virCCWGroupMemberTypeFree(data->ccwgroup_dev.members[i]);
+ g_free(data->ccwgroup_dev.members);
+ switch (data->ccwgroup_dev.type) {
+ case VIR_NODE_DEV_CAP_CCWGROUP_QETH_GENERIC:
+ virCCWGroupTypeQethFree(&data->ccwgroup_dev.qeth);
+ break;
+ case VIR_NODE_DEV_CAP_CCWGROUP_LAST:
+ break;
+ }
+ break;
case VIR_NODE_DEV_CAP_DRM:
case VIR_NODE_DEV_CAP_FC_HOST:
case VIR_NODE_DEV_CAP_VPORTS:
@@ -2694,6 +2868,11 @@ virNodeDeviceUpdateCaps(virNodeDeviceDef *def)
&cap->data.mdev_parent) <
0)
return -1;
break;
+ case VIR_NODE_DEV_CAP_CCWGROUP_DEV:
+ if (virNodeDeviceGetCCWGroupDynamicCaps(def->sysfs_path,
+ &cap->data.ccwgroup_dev) <
0)
+ return -1;
+ break;
/* all types that (supposedly) don't require any updates
* relative to what's in the cache.
@@ -3194,6 +3373,31 @@ virNodeDeviceGetMdevParentDynamicCaps(const char *sysfsPath,
return 0;
}
+/* virNodeDeviceGetCCWGroupDynamicCaps() get info that is stored in sysfs
+ * about devices related to this device, i.e. things that can change
+ * without this device itself changing. These must be refreshed
+ * anytime full XML of the device is requested, because they can
+ * change with no corresponding notification from the kernel/udev.
+ */
+int
+virNodeDeviceGetCCWGroupDynamicCaps(const char *sysfsPath,
+ virNodeDevCapCCWGroup *ccwgroup)
+{
+ size_t i;
+
+ for (i = 0; i < ccwgroup->nmembers; i++)
+ virCCWGroupMemberTypeFree(ccwgroup->members[i]);
+ VIR_FREE(ccwgroup->members);
+ ccwgroup->nmembers = 0;
+
+ if (virCCWGroupDeviceGetMembers(sysfsPath,
+ &ccwgroup->members,
+ &ccwgroup->nmembers) < 0)
+ return -1;
+
+ return 0;
+}
+
#else
@@ -3239,4 +3443,12 @@ virNodeDeviceGetMdevParentDynamicCaps(const char *sysfsPath
G_GNUC_UNUSED,
}
+int
+virNodeDeviceGetCCWGroupDynamicCaps(const char *sysfsPath G_GNUC_UNUSED,
+ virNodeDevCapCCWGroup *ccwgroup G_GNUC_UNUSED)
+{
+ return -1;
+}
+
+
#endif /* __linux__ */
diff --git a/src/conf/node_device_conf.h b/src/conf/node_device_conf.h
index ad2258e90d..d94670e074 100644
--- a/src/conf/node_device_conf.h
+++ b/src/conf/node_device_conf.h
@@ -71,10 +71,17 @@ typedef enum {
VIR_NODE_DEV_CAP_AP_QUEUE, /* s390 AP Queue */
VIR_NODE_DEV_CAP_AP_MATRIX, /* s390 AP Matrix device */
VIR_NODE_DEV_CAP_VPD, /* Device provides VPD */
+ VIR_NODE_DEV_CAP_CCWGROUP_DEV, /* s390 CCWGROUP device */
VIR_NODE_DEV_CAP_LAST
} virNodeDevCapType;
+typedef enum {
+ /* Keep in sync with VIR_ENUM_IMPL in node_device_conf.c */
+ VIR_NODE_DEV_CAP_CCWGROUP_QETH_GENERIC, /* s390 CCWGROUP QETH generic device */
+ VIR_NODE_DEV_CAP_CCWGROUP_LAST
+} virNodeDevCCWGroupCapType;
+
typedef enum {
/* Keep in sync with VIR_ENUM_IMPL in node_device_conf.c */
VIR_NODE_DEV_CAP_NET_80203, /* 802.03 network device */
@@ -83,6 +90,7 @@ typedef enum {
} virNodeDevNetCapType;
VIR_ENUM_DECL(virNodeDevCap);
+VIR_ENUM_DECL(virNodeDevCCWGroupCap);
VIR_ENUM_DECL(virNodeDevNetCap);
typedef enum {
@@ -321,6 +329,19 @@ struct _virNodeDevCapMdevParent {
char *address;
};
+typedef struct _virNodeDevCapCCWGroup virNodeDevCapCCWGroup;
+struct _virNodeDevCapCCWGroup {
+ virNodeDevCCWStateType state; // online attribute
+ virCCWDeviceAddress *address;
+ virCCWGroupMemberType **members;
+ size_t nmembers;
+
+ virNodeDevCCWGroupCapType type;
+ union {
+ virCCWGroupTypeQeth qeth;
+ };
+};
+
typedef struct _virNodeDevCapData virNodeDevCapData;
struct _virNodeDevCapData {
virNodeDevCapType type;
@@ -343,6 +364,7 @@ struct _virNodeDevCapData {
virNodeDevCapAPQueue ap_queue;
virNodeDevCapAPMatrix ap_matrix;
virNodeDevCapMdevParent mdev_parent;
+ virNodeDevCapCCWGroup ccwgroup_dev;
};
};
@@ -434,7 +456,8 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(virNodeDevCapsDef,
virNodeDevCapsDefFree);
VIR_CONNECT_LIST_NODE_DEVICES_CAP_AP_CARD | \
VIR_CONNECT_LIST_NODE_DEVICES_CAP_AP_QUEUE | \
VIR_CONNECT_LIST_NODE_DEVICES_CAP_AP_MATRIX | \
- VIR_CONNECT_LIST_NODE_DEVICES_CAP_VPD)
+ VIR_CONNECT_LIST_NODE_DEVICES_CAP_VPD | \
+ VIR_CONNECT_LIST_NODE_DEVICES_CAP_CCWGROUP_DEV)
#define VIR_CONNECT_LIST_NODE_DEVICES_FILTERS_ACTIVE \
VIR_CONNECT_LIST_NODE_DEVICES_ACTIVE | \
@@ -472,6 +495,10 @@ int
virNodeDeviceGetMdevParentDynamicCaps(const char *sysfsPath,
virNodeDevCapMdevParent *mdev_parent);
+int
+virNodeDeviceGetCCWGroupDynamicCaps(const char *sysfsPath,
+ virNodeDevCapCCWGroup *ccwgroup);
+
int
virNodeDeviceUpdateCaps(virNodeDeviceDef *def);
diff --git a/src/conf/schemas/nodedev.rng b/src/conf/schemas/nodedev.rng
index 42a0cdcfd9..ebcda30f1f 100644
--- a/src/conf/schemas/nodedev.rng
+++ b/src/conf/schemas/nodedev.rng
@@ -83,6 +83,7 @@
<ref name="capdrm"/>
<ref name="capmdev"/>
<ref name="capccwdev"/>
+ <ref name="capccwgroupdev"/>
<ref name="capcssdev"/>
<ref name="capvdpa"/>
<ref name="capapcard"/>
@@ -669,6 +670,48 @@
</interleave>
</define>
+ <define name="capccwgrouptypeqeth">
+ <attribute name="type">
+ <value>qeth_generic</value>
+ </attribute>
+ <interleave>
+ <element name="card_type"><text/></element>
+ <element name="chpid"><text/></element>
+ </interleave>
+ </define>
+
+ <define name="capccwgroupdev">
+ <attribute name="type">
+ <value>ccwgroup</value>
+ </attribute>
+ <optional>
+ <element name="state">
+ <choice>
+ <value>online</value>
+ <value>offline</value>
+ </choice>
+ </element>
+ </optional>
+ <ref name="capccwaddress"/>
+ <optional>
+ <element name="members">
+ <oneOrMore>
+ <element name="ccw_device">
+ <attribute name="ref">
+ <data type="string"/>
+ </attribute>
+ <text/>
+ </element>
+ </oneOrMore>
+ </element>
+ </optional>
+ <element name="capability">
+ <choice>
+ <ref name="capccwgrouptypeqeth"/>
+ </choice>
+ </element>
+ </define>
+
<define name="capccwdev">
<attribute name="type">
<value>ccw</value>
diff --git a/src/conf/virnodedeviceobj.c b/src/conf/virnodedeviceobj.c
index d0a6eab42b..23984995c8 100644
--- a/src/conf/virnodedeviceobj.c
+++ b/src/conf/virnodedeviceobj.c
@@ -741,6 +741,7 @@ virNodeDeviceObjHasCap(const virNodeDeviceObj *obj,
case VIR_NODE_DEV_CAP_AP_CARD:
case VIR_NODE_DEV_CAP_AP_QUEUE:
case VIR_NODE_DEV_CAP_VPD:
+ case VIR_NODE_DEV_CAP_CCWGROUP_DEV:
case VIR_NODE_DEV_CAP_LAST:
break;
}
@@ -899,7 +900,8 @@ virNodeDeviceObjMatch(virNodeDeviceObj *obj,
MATCH_CAP(AP_CARD) ||
MATCH_CAP(AP_QUEUE) ||
MATCH_CAP(AP_MATRIX) ||
- MATCH_CAP(VPD)))
+ MATCH_CAP(VPD) ||
+ MATCH_CAP(CCWGROUP_DEV)))
return false;
}
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 406e6583a3..07e1e673f0 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -892,12 +892,14 @@ virNetDevIPRouteParseXML;
virNodeDevCapsDefFree;
virNodeDevCapTypeFromString;
virNodeDevCapTypeToString;
+virNodeDevCCWGroupCapTypeFromString;
virNodeDeviceCapsListExport;
virNodeDeviceDefFormat;
virNodeDeviceDefFree;
virNodeDeviceDefParse;
virNodeDeviceDefParseXML;
virNodeDeviceGetAPMatrixDynamicCaps;
+virNodeDeviceGetCCWGroupDynamicCaps;
virNodeDeviceGetCSSDynamicCaps;
virNodeDeviceGetMdevParentDynamicCaps;
virNodeDeviceGetPCIDynamicCaps;
diff --git a/src/node_device/node_device_driver.c b/src/node_device/node_device_driver.c
index 9898b1914a..d716561361 100644
--- a/src/node_device/node_device_driver.c
+++ b/src/node_device/node_device_driver.c
@@ -695,6 +695,10 @@ nodeDeviceObjFormatAddress(virNodeDeviceObj *obj)
addr = g_strdup(caps->data.mdev_parent.address);
break;
+ case VIR_NODE_DEV_CAP_CCWGROUP_DEV:
+ addr = virCCWDeviceAddressAsString(caps->data.ccwgroup_dev.address);
+ break;
+
case VIR_NODE_DEV_CAP_SYSTEM:
case VIR_NODE_DEV_CAP_USB_DEV:
case VIR_NODE_DEV_CAP_USB_INTERFACE:
@@ -2189,6 +2193,7 @@ int nodeDeviceDefValidate(virNodeDeviceDef *def,
case VIR_NODE_DEV_CAP_AP_QUEUE:
case VIR_NODE_DEV_CAP_AP_MATRIX:
case VIR_NODE_DEV_CAP_VPD:
+ case VIR_NODE_DEV_CAP_CCWGROUP_DEV:
case VIR_NODE_DEV_CAP_LAST:
break;
}
diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_device_udev.c
index 6b362625f7..a78f47b65a 100644
--- a/src/node_device/node_device_udev.c
+++ b/src/node_device/node_device_udev.c
@@ -1389,6 +1389,43 @@ udevProcessAPMatrix(struct udev_device *device,
}
+static int
+udevProcessCCWGroup(struct udev_device *device,
+ virNodeDeviceDef *def)
+{
+ const char *devtype = udev_device_get_devtype(device);
+ virNodeDevCapData *data = &def->caps->data;
+
+ data->ccwgroup_dev.address =
virCCWDeviceAddressFromString(udev_device_get_sysname(device));
+
+ udevCCWGetState(device, &data->ccwgroup_dev.state);
+
+ udevGenerateDeviceName(device, def, NULL);
+
+ if ((data->ccwgroup_dev.type = virNodeDevCCWGroupCapTypeFromString(devtype)) <
0)
+ return -1;
+
+ switch (data->ccwgroup_dev.type) {
+ case VIR_NODE_DEV_CAP_CCWGROUP_QETH_GENERIC:
+ {
+ virCCWGroupTypeQeth *qeth = &data->ccwgroup_dev.qeth;
+ /* process qeth device information */
+ udevGetStringSysfsAttr(device, "card_type",
&qeth->card_type);
+ udevGetStringSysfsAttr(device, "chpid", &qeth->chpid);
+ }
+ break;
+ case VIR_NODE_DEV_CAP_CCWGROUP_LAST:
+ return -1;
+ }
+
+ if (virNodeDeviceGetCCWGroupDynamicCaps(def->sysfs_path,
+ &data->ccwgroup_dev) < 0)
+ return -1;
+
+ return 0;
+}
+
+
static int
udevGetDeviceNodes(struct udev_device *device,
virNodeDeviceDef *def)
@@ -1447,6 +1484,8 @@ udevGetDeviceType(struct udev_device *device,
*type = VIR_NODE_DEV_CAP_AP_CARD;
else if (STREQ(devtype, "ap_queue"))
*type = VIR_NODE_DEV_CAP_AP_QUEUE;
+ else if (STREQ(devtype, "qeth_generic"))
+ *type = VIR_NODE_DEV_CAP_CCWGROUP_DEV;
} else {
/* PCI devices don't set the DEVTYPE property. */
if (udevHasDeviceProperty(device, "PCI_CLASS"))
@@ -1534,6 +1573,8 @@ udevGetDeviceDetails(virNodeDeviceDriverState *driver_state,
return udevProcessAPMatrix(device, def);
case VIR_NODE_DEV_CAP_MDEV_TYPES:
return udevProcessMdevParent(device, def);
+ case VIR_NODE_DEV_CAP_CCWGROUP_DEV:
+ return udevProcessCCWGroup(device, def);
case VIR_NODE_DEV_CAP_VPD:
case VIR_NODE_DEV_CAP_SYSTEM:
case VIR_NODE_DEV_CAP_FC_HOST:
diff --git a/src/util/virccw.c b/src/util/virccw.c
index c6be013e16..0873c61889 100644
--- a/src/util/virccw.c
+++ b/src/util/virccw.c
@@ -19,9 +19,15 @@
*/
#include <config.h>
+
#include "virccw.h"
+
+#include <dirent.h>
+
#include "virerror.h"
+#include "virfile.h"
#include "virstring.h"
+#include "viralloc.h"
#define VIR_FROM_THIS VIR_FROM_NONE
@@ -101,3 +107,99 @@ virCCWDeviceAddressParseFromString(const char *address,
return 0;
}
+
+void
+virCCWGroupMemberTypeFree(virCCWGroupMemberType *member)
+{
+ if (!member)
+ return;
+
+ VIR_FREE(member->ref);
+ VIR_FREE(member->device);
+ VIR_FREE(member);
+}
+
+static char *
+virCCWGroupDeviceDevNodeName(const char *nodedev_prefix,
+ const char *sysfs_path)
+{
+ g_autofree char *node_name = NULL;
+ size_t i;
+
+ node_name = g_path_get_basename(sysfs_path);
+
+ for (i = 0; i < strlen(node_name); i++) {
+ if (!(g_ascii_isalnum(*(node_name + i))))
+ *(node_name + i) = '_';
+ }
+
+ return g_strdup_printf("%s_%s", nodedev_prefix, node_name);
+}
+
+/**
+ * virCCWGroupDeviceGetMembers:
+ * @sysfs_path: sysfs path to a group device
+ * @members: Where to add the found group members
+ * @nmembers: Number of found group members
+ *
+ * The sysfs path is searched for links with a name prefix "cdev".
+ * These links point the ccw device sysfs entry which is a member
+ * of the ccw group.
+ *
+ * Returns: -1 on error (invalid sysfs_path or group has no members)
+ * 0 on success
+ */
+int
+virCCWGroupDeviceGetMembers(const char *sysfs_path,
+ virCCWGroupMemberType ***members,
+ size_t *nmembers)
+{
+ virCCWGroupMemberType *member = NULL;
+ g_autofree char *ccwdevpath = NULL;
+ g_autoptr(DIR) dir = NULL;
+ struct dirent *entry;
+ int direrr;
+
+ if (virDirOpenIfExists(&dir, sysfs_path) <= 0)
+ return -1;
+
+ while ((direrr = virDirRead(dir, &entry, NULL)) > 0) {
+ if (g_str_has_prefix(entry->d_name, "cdev")) {
+ // found a cdev reference
+ g_autofree char *cdevpath = NULL;
+ cdevpath = g_build_filename(sysfs_path, entry->d_name, NULL);
+
+ if (virFileIsLink(cdevpath) != 1)
+ continue;
+
+ if (virFileResolveLink(cdevpath, &ccwdevpath) < 0)
+ continue;
+
+ if (!virFileExists(ccwdevpath))
+ continue;
+
+ member = g_new0(virCCWGroupMemberType, 1);
+
+ member->ref = g_strdup(entry->d_name);
+ member->device = virCCWGroupDeviceDevNodeName("ccw",
ccwdevpath);
+
+ VIR_APPEND_ELEMENT(*members, *nmembers, member);
+ }
+ }
+
+ /* Groups without a member must not exist */
+ if (*nmembers == 0)
+ return -1;
+
+ return 0;
+}
+
+void
+virCCWGroupTypeQethFree(virCCWGroupTypeQeth *qeth)
+{
+ if (!qeth)
+ return;
+
+ VIR_FREE(qeth->card_type);
+ VIR_FREE(qeth->chpid);
+}
diff --git a/src/util/virccw.h b/src/util/virccw.h
index 80cc716811..a8c9fa83ef 100644
--- a/src/util/virccw.h
+++ b/src/util/virccw.h
@@ -35,6 +35,18 @@ struct _virCCWDeviceAddress {
bool assigned;
};
+typedef struct _virCCWGroupMemberType virCCWGroupMemberType;
+struct _virCCWGroupMemberType {
+ char *ref; /* cdev reference */
+ char *device;
+};
+
+typedef struct _virCCWGroupTypeQeth virCCWGroupTypeQeth;
+struct _virCCWGroupTypeQeth {
+ char *card_type;
+ char *chpid;
+};
+
bool virCCWDeviceAddressIsValid(virCCWDeviceAddress *addr);
bool virCCWDeviceAddressEqual(virCCWDeviceAddress *addr1,
virCCWDeviceAddress *addr2);
@@ -50,3 +62,13 @@ int virCCWDeviceAddressParseFromString(const char *address,
unsigned int *cssid,
unsigned int *ssid,
unsigned int *devno);
+
+void virCCWGroupMemberTypeFree(virCCWGroupMemberType *member);
+
+int virCCWGroupDeviceGetMembers(const char *sysfs_path,
+ virCCWGroupMemberType ***members,
+ size_t *nmembers);
+
+void virCCWGroupTypeQethFree(virCCWGroupTypeQeth *qeth);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(virCCWGroupMemberType, virCCWGroupMemberTypeFree);
diff --git a/tests/nodedevschemadata/ccwgroup_0_0_bd00.xml
b/tests/nodedevschemadata/ccwgroup_0_0_bd00.xml
new file mode 100644
index 0000000000..4e6e540cfc
--- /dev/null
+++ b/tests/nodedevschemadata/ccwgroup_0_0_bd00.xml
@@ -0,0 +1,20 @@
+<device>
+ <name>ccwgroup_0_0_bd00</name>
+ <path>/sys/devices/qeth/0.0.bd00</path>
+ <parent>computer</parent>
+ <capability type='ccwgroup'>
+ <state>online</state>
+ <cssid>0x0</cssid>
+ <ssid>0x0</ssid>
+ <devno>0xbd00</devno>
+ <members>
+ <ccw_device ref='cdev1'>ccw_0_0_bd01</ccw_device>
+ <ccw_device ref='cdev2'>ccw_0_0_bd02</ccw_device>
+ <ccw_device ref='cdev0'>ccw_0_0_bd00</ccw_device>
+ </members>
+ <capability type='qeth_generic'>
+ <card_type>OSD_10GIG</card_type>
+ <chpid>BD</chpid>
+ </capability>
+ </capability>
+</device>
diff --git a/tests/nodedevxml2xmlout/ccwgroup_0_0_bd00.xml
b/tests/nodedevxml2xmlout/ccwgroup_0_0_bd00.xml
new file mode 120000
index 0000000000..a2749e6685
--- /dev/null
+++ b/tests/nodedevxml2xmlout/ccwgroup_0_0_bd00.xml
@@ -0,0 +1 @@
+../nodedevschemadata/ccwgroup_0_0_bd00.xml
\ No newline at end of file
diff --git a/tests/nodedevxml2xmltest.c b/tests/nodedevxml2xmltest.c
index 814a817725..d4d87b3bdc 100644
--- a/tests/nodedevxml2xmltest.c
+++ b/tests/nodedevxml2xmltest.c
@@ -146,6 +146,7 @@ mymain(void)
DO_TEST("mdev_3627463d_b7f0_4fea_b468_f1da537d301b");
DO_TEST_INACTIVE("mdev_3627463d_b7f0_4fea_b468_f1da537d301b");
DO_TEST("ccw_0_0_ffff");
+ DO_TEST("ccwgroup_0_0_bd00");
DO_TEST("css_0_0_ffff");
DO_TEST("css_0_0_ffff_channel_dev_addr");
DO_TEST("css_0_0_fffe_mdev_types");
diff --git a/tools/virsh-nodedev.c b/tools/virsh-nodedev.c
index 145faff3e7..3aae7285a9 100644
--- a/tools/virsh-nodedev.c
+++ b/tools/virsh-nodedev.c
@@ -501,6 +501,9 @@ cmdNodeListDevices(vshControl *ctl, const vshCmd *cmd G_GNUC_UNUSED)
case VIR_NODE_DEV_CAP_AP_MATRIX:
flags |= VIR_CONNECT_LIST_NODE_DEVICES_CAP_AP_MATRIX;
break;
+ case VIR_NODE_DEV_CAP_CCWGROUP_DEV:
+ flags |= VIR_CONNECT_LIST_NODE_DEVICES_CAP_CCWGROUP_DEV;
+ break;
case VIR_NODE_DEV_CAP_LAST:
break;
}
--
2.47.0