Add ccwgroup node device type supporting qeth generic driver.
Signed-off-by: Boris Fiuczynski <fiuczy@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
On Tue, Feb 04, 2025 at 18:11:41 +0100, Boris Fiuczynski wrote:
> Add ccwgroup node device type supporting qeth generic driver.
>
> Signed-off-by: Boris Fiuczynski <fiuczy@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
[...]
> +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;
Apart from the broken build where clang complains about assignment to
the unsigned enum which I've fixed, this doesn't report an error ...
> +
> + 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)
... and neither this at least on 2 code paths:
1) the dummy impl if not running on linux
2) if the assertion that a group must be non-empty fails in the real
code path inside virCCWGroupDeviceGetMembers.
The caller does seem to care about errors as other code paths do set
them, thus this should as well.
> + return -1;
> +
> + return 0;
> +}
On 2/5/25 19:37, Peter Krempa wrote:
> On Tue, Feb 04, 2025 at 18:11:41 +0100, Boris Fiuczynski wrote:
>> Add ccwgroup node device type supporting qeth generic driver.
>>
>> Signed-off-by: Boris Fiuczynski <fiuczy@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
>
> [...]
>
>
>> +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;
>
> Apart from the broken build where clang complains about assignment to
> the unsigned enum which I've fixed, this doesn't report an error ...
>
Peter, thanks for fixing the clang problem.
I agree that here I should add reporting an internal error as this
should not happen since the devtype was already used before in the udev
event handling to get here and not being able to do the conversion would
be an error. I will send a patch shortly.
>> +
>> + 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)
>
> ... and neither this at least on 2 code paths:
>
> 1) the dummy impl if not running on linux
> 2) if the assertion that a group must be non-empty fails in the real
> code path inside virCCWGroupDeviceGetMembers.
This is implemented following the common DynamicCaps pattern, e.g. as in
virNodeDeviceGetMdevParentDynamicCaps.
Therefore I would refrain from adding error reporting.
>
> The caller does seem to care about errors as other code paths do set
> them, thus this should as well.
>
>> + return -1;
>> +
>> + return 0;
>> +}
--
Mit freundlichen Grüßen/Kind regards
Boris Fiuczynski
IBM Deutschland Research & Development GmbH
Vorsitzender des Aufsichtsrats: Wolfgang Wendt
Geschäftsführung: David Faller
Sitz der Gesellschaft: Böblingen
Registergericht: Amtsgericht Stuttgart, HRB 243294
© 2016 - 2025 Red Hat, Inc.