[PATCH 6/8] nodedev: add ccwgroup node device support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Add ccwgroup node device type supporting qeth generic driver.

Signed-off-by: Boris Fiuczynski <fiuczy@xxxxxxxxxxxxx>
---
 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



[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]

  Powered by Linux