[PATCH 7/8] nodedev: add ccwgroup capability support to ccw devices

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

 



Add the group membership information to a CCW device. Allow to filter
CCW devices based on a group membership.

Signed-off-by: Boris Fiuczynski <fiuczy@xxxxxxxxxxxxx>
---
 docs/manpages/virsh.rst                       |  19 ++--
 include/libvirt/libvirt-nodedev.h             |   1 +
 src/conf/node_device_conf.c                   | 105 +++++++++++++++++-
 src/conf/node_device_conf.h                   |  10 +-
 src/conf/schemas/nodedev.rng                  |  12 ++
 src/conf/virnodedeviceobj.c                   |  11 +-
 src/libvirt_private.syms                      |   1 +
 src/node_device/node_device_driver.c          |   2 +
 src/node_device/node_device_udev.c            |   4 +
 src/util/virccw.c                             |  23 ++++
 src/util/virccw.h                             |   3 +
 .../ccw_0_0_ff02_ccwgroup.xml                 |  13 +++
 .../ccw_0_0_ff02_ccwgroup.xml                 |   1 +
 tests/nodedevxml2xmltest.c                    |   1 +
 tools/virsh-nodedev.c                         |   3 +
 15 files changed, 196 insertions(+), 13 deletions(-)
 create mode 100644 tests/nodedevschemadata/ccw_0_0_ff02_ccwgroup.xml
 create mode 120000 tests/nodedevxml2xmlout/ccw_0_0_ff02_ccwgroup.xml

diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst
index 5e5734dff1..9e549f25a6 100644
--- a/docs/manpages/virsh.rst
+++ b/docs/manpages/virsh.rst
@@ -5532,15 +5532,16 @@ 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', '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*
-is mutually exclusive with *--persistent* and *--inactive*.
-If *--tree* is used, the output is formatted in a tree representing parents of
-each node. *--tree* is mutually exclusive with all other options but *--all*.
+'mdev_types', 'ccw', 'ccwgroup', 'ccwgroup_member', '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* is mutually exclusive with *--persistent*
+and *--inactive*. If *--tree* is used, the output is formatted in a tree
+representing parents of each node. *--tree* is mutually exclusive with all
+other options but *--all*.
 
 
 nodedev-reattach
diff --git a/include/libvirt/libvirt-nodedev.h b/include/libvirt/libvirt-nodedev.h
index 79bee4fb04..9fccbeefeb 100644
--- a/include/libvirt/libvirt-nodedev.h
+++ b/include/libvirt/libvirt-nodedev.h
@@ -91,6 +91,7 @@ typedef enum {
     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_CAP_CCWGROUP_MEMBER = 1 << 23, /* s390 CCW device member of 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 1649df09a1..3e88f5da87 100644
--- a/src/conf/node_device_conf.c
+++ b/src/conf/node_device_conf.c
@@ -72,6 +72,7 @@ VIR_ENUM_IMPL(virNodeDevCap,
               "ap_matrix",
               "vpd",
               "ccwgroup",
+              "ccwgroup_member",
 );
 
 VIR_ENUM_IMPL(virNodeDevCCWGroupCap,
@@ -642,6 +643,23 @@ virCCWDeviceAddressFormat(virBuffer *buf,
 }
 
 
+static void
+virNodeDeviceCapCCWGroupMemberDefFormat(virBuffer *buf,
+                             const virNodeDevCapData *data)
+{
+    virNodeDevCapCCW ccw_dev = data->ccw_dev;
+
+    if (ccw_dev.group_dev) {
+        virBufferAddLit(buf, "<capability type='ccwgroup_member'>\n");
+        virBufferAdjustIndent(buf, 2);
+        virBufferEscapeString(buf, "<group_device>%s</group_device>\n",
+                              ccw_dev.group_dev);
+        virBufferAdjustIndent(buf, -2);
+        virBufferAddLit(buf, "</capability>\n");
+    }
+}
+
+
 static void
 virNodeDeviceCapCSSDefFormat(virBuffer *buf,
                              const virNodeDevCapData *data)
@@ -812,6 +830,8 @@ virNodeDeviceDefFormat(const virNodeDeviceDef *def, unsigned int flags)
         case VIR_NODE_DEV_CAP_CCW_DEV:
             virNodeDeviceCapCCWStateTypeFormat(&buf, data->ccw_dev.state);
             virCCWDeviceAddressFormat(&buf, data->ccw_dev.dev_addr);
+            if (data->ccw_dev.flags & VIR_NODE_DEV_CAP_FLAG_CCW_CCWGROUP_MEMBER)
+                virNodeDeviceCapCCWGroupMemberDefFormat(&buf, data);
             break;
         case VIR_NODE_DEV_CAP_CSS_DEV:
             virNodeDeviceCapCSSDefFormat(&buf, data);
@@ -843,6 +863,7 @@ virNodeDeviceDefFormat(const virNodeDeviceDef *def, unsigned int flags)
         case VIR_NODE_DEV_CAP_CCWGROUP_DEV:
             virNodeDeviceCapCCWGroupDefFormat(&buf, data);
             break;
+        case VIR_NODE_DEV_CAP_CCWGROUP_MEMBER:
         case VIR_NODE_DEV_CAP_FC_HOST:
         case VIR_NODE_DEV_CAP_VPORTS:
         case VIR_NODE_DEV_CAP_VPD:
@@ -1252,6 +1273,33 @@ virNodeDevCCWDeviceAddressParseXML(xmlXPathContextPtr ctxt,
 }
 
 
+static int
+virNodeDevCCWCapabilityParseXML(xmlXPathContextPtr ctxt,
+                                xmlNodePtr node,
+                                const char *dev_name,
+                                virNodeDevCapCCW *ccw_dev)
+{
+    g_autofree char *type = virXMLPropString(node, "type");
+    VIR_XPATH_NODE_AUTORESTORE(ctxt)
+
+    ctxt->node = node;
+
+    if (!type)
+        return 0; /* optional */
+
+    if (STREQ(type, "ccwgroup_member")) {
+        if (!(ccw_dev->group_dev = virXPathString("string(./group_device[1])", ctxt))) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                           _("missing group_device value for '%1$s'"), dev_name);
+            return -1;
+        }
+        ccw_dev->flags |= VIR_NODE_DEV_CAP_FLAG_CCW_CCWGROUP_MEMBER;
+    }
+
+    return 0;
+}
+
+
 static int
 virNodeDevCapCCWParseXML(xmlXPathContextPtr ctxt,
                          virNodeDeviceDef *def,
@@ -1260,7 +1308,10 @@ virNodeDevCapCCWParseXML(xmlXPathContextPtr ctxt,
 {
     VIR_XPATH_NODE_AUTORESTORE(ctxt)
     g_autofree virCCWDeviceAddress *ccw_addr = NULL;
+    g_autofree xmlNodePtr *nodes = NULL;
     g_autofree char *state = NULL;
+    int n = 0;
+    size_t i = 0;
     int val;
 
     ctxt->node = node;
@@ -1284,6 +1335,15 @@ virNodeDevCapCCWParseXML(xmlXPathContextPtr ctxt,
 
     ccw_dev->dev_addr = g_steal_pointer(&ccw_addr);
 
+    /* capabilities are optional */
+    if ((n = virXPathNodeSet("./capability", ctxt, &nodes)) < 0)
+        return -1;
+
+    for (i = 0; i < n; i++) {
+        if (virNodeDevCCWCapabilityParseXML(ctxt, nodes[i], def->name, ccw_dev) < 0)
+            return -1;
+    }
+
     return 0;
 }
 
@@ -2504,6 +2564,7 @@ virNodeDevCapsDefParseXML(xmlXPathContextPtr ctxt,
         ret = virNodeDevCapCCWGroupParseXML(ctxt, def, node,
                                             &caps->data.ccwgroup_dev);
         break;
+    case VIR_NODE_DEV_CAP_CCWGROUP_MEMBER:
     case VIR_NODE_DEV_CAP_MDEV_TYPES:
     case VIR_NODE_DEV_CAP_FC_HOST:
     case VIR_NODE_DEV_CAP_VPORTS:
@@ -2795,6 +2856,7 @@ virNodeDevCapsDefFree(virNodeDevCapsDef *caps)
         break;
     case VIR_NODE_DEV_CAP_CCW_DEV:
         g_free(data->ccw_dev.dev_addr);
+        g_free(data->ccw_dev.group_dev);
         break;
     case VIR_NODE_DEV_CAP_CCWGROUP_DEV:
         g_free(data->ccwgroup_dev.address);
@@ -2809,6 +2871,7 @@ virNodeDevCapsDefFree(virNodeDevCapsDef *caps)
             break;
         }
         break;
+    case VIR_NODE_DEV_CAP_CCWGROUP_MEMBER:
     case VIR_NODE_DEV_CAP_DRM:
     case VIR_NODE_DEV_CAP_FC_HOST:
     case VIR_NODE_DEV_CAP_VPORTS:
@@ -2868,6 +2931,12 @@ virNodeDeviceUpdateCaps(virNodeDeviceDef *def)
                                                       &cap->data.mdev_parent) < 0)
                 return -1;
             break;
+        case VIR_NODE_DEV_CAP_CCW_DEV:
+        case VIR_NODE_DEV_CAP_CCWGROUP_MEMBER:
+            if (virNodeDeviceGetCCWDynamicCaps(def->sysfs_path,
+                                               &cap->data.ccw_dev) < 0)
+                return -1;
+            break;
         case VIR_NODE_DEV_CAP_CCWGROUP_DEV:
             if (virNodeDeviceGetCCWGroupDynamicCaps(def->sysfs_path,
                                                     &cap->data.ccwgroup_dev) < 0)
@@ -2887,7 +2956,6 @@ virNodeDeviceUpdateCaps(virNodeDeviceDef *def)
         case VIR_NODE_DEV_CAP_VPORTS:
         case VIR_NODE_DEV_CAP_SCSI_GENERIC:
         case VIR_NODE_DEV_CAP_MDEV:
-        case VIR_NODE_DEV_CAP_CCW_DEV:
         case VIR_NODE_DEV_CAP_VDPA:
         case VIR_NODE_DEV_CAP_AP_CARD:
         case VIR_NODE_DEV_CAP_AP_QUEUE:
@@ -2986,6 +3054,15 @@ virNodeDeviceCapsListExport(virNodeDeviceDef *def,
                 ncaps++;
             }
         }
+
+        if (caps->data.type == VIR_NODE_DEV_CAP_CCW_DEV) {
+            flags = caps->data.ccw_dev.flags;
+
+            if (flags & VIR_NODE_DEV_CAP_FLAG_CCW_CCWGROUP_MEMBER) {
+                MAYBE_ADD_CAP(VIR_NODE_DEV_CAP_CCWGROUP_MEMBER);
+                ncaps++;
+            }
+        }
     }
 
 #undef MAYBE_ADD_CAP
@@ -3335,6 +3412,25 @@ virNodeDeviceGetCSSDynamicCaps(const char *sysfsPath,
     return 0;
 }
 
+/* virNodeDeviceGetCCWDynamicCaps() 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
+virNodeDeviceGetCCWDynamicCaps(const char *sysfsPath,
+                               virNodeDevCapCCW *ccw_dev)
+{
+    g_free(ccw_dev->group_dev);
+    ccw_dev->flags &= ~VIR_NODE_DEV_CAP_FLAG_CCW_CCWGROUP_MEMBER;
+
+    if ((ccw_dev->group_dev = virCCWDeviceGetGroupDev(sysfsPath)))
+        ccw_dev->flags |= VIR_NODE_DEV_CAP_FLAG_CCW_CCWGROUP_MEMBER;
+
+    return 0;
+}
+
 /* virNodeDeviceGetAPMatrixDynamicCaps() 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
@@ -3428,6 +3524,13 @@ virNodeDeviceGetCSSDynamicCaps(const char *sysfsPath G_GNUC_UNUSED,
     return -1;
 }
 
+int
+virNodeDeviceGetCCWDynamicCaps(const char *sysfsPath G_GNUC_UNUSED,
+                               virNodeDevCapCCW *ccw_dev G_GNUC_UNUSED)
+{
+    return -1;
+}
+
 int
 virNodeDeviceGetAPMatrixDynamicCaps(const char *sysfsPath G_GNUC_UNUSED,
                                     virNodeDevCapAPMatrix *ap_matrix G_GNUC_UNUSED)
diff --git a/src/conf/node_device_conf.h b/src/conf/node_device_conf.h
index d94670e074..a6cef57b95 100644
--- a/src/conf/node_device_conf.h
+++ b/src/conf/node_device_conf.h
@@ -72,6 +72,7 @@ typedef enum {
     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_CCWGROUP_MEMBER,   /* s390 CCW device is member of CCWGROUP */
 
     VIR_NODE_DEV_CAP_LAST
 } virNodeDevCapType;
@@ -118,6 +119,7 @@ typedef enum {
 
 typedef enum {
     VIR_NODE_DEV_CAP_FLAG_CSS_MDEV                  = (1 << 0),
+    VIR_NODE_DEV_CAP_FLAG_CCW_CCWGROUP_MEMBER       = (2 << 0),
 } virNodeDevCCWCapFlags;
 
 typedef enum {
@@ -295,6 +297,7 @@ struct _virNodeDevCapCCW {
     size_t nmdev_types;
     virCCWDeviceAddress *channel_dev_addr;
     virNodeDevCCWStateType state;
+    char *group_dev;
 };
 
 typedef struct _virNodeDevCapVDPA virNodeDevCapVDPA;
@@ -457,7 +460,8 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(virNodeDevCapsDef, virNodeDevCapsDefFree);
                  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_CCWGROUP_DEV)
+                 VIR_CONNECT_LIST_NODE_DEVICES_CAP_CCWGROUP_DEV  | \
+                 VIR_CONNECT_LIST_NODE_DEVICES_CAP_CCWGROUP_MEMBER)
 
 #define VIR_CONNECT_LIST_NODE_DEVICES_FILTERS_ACTIVE \
     VIR_CONNECT_LIST_NODE_DEVICES_ACTIVE | \
@@ -495,6 +499,10 @@ int
 virNodeDeviceGetMdevParentDynamicCaps(const char *sysfsPath,
                                       virNodeDevCapMdevParent *mdev_parent);
 
+int
+virNodeDeviceGetCCWDynamicCaps(const char *sysfsPath,
+                               virNodeDevCapCCW *ccw_dev);
+
 int
 virNodeDeviceGetCCWGroupDynamicCaps(const char *sysfsPath,
                                     virNodeDevCapCCWGroup *ccwgroup);
diff --git a/src/conf/schemas/nodedev.rng b/src/conf/schemas/nodedev.rng
index ebcda30f1f..f52c6ab752 100644
--- a/src/conf/schemas/nodedev.rng
+++ b/src/conf/schemas/nodedev.rng
@@ -712,6 +712,17 @@
     </element>
   </define>
 
+  <define name="capccwgroupmember">
+    <optional>
+      <element name="capability">
+        <attribute name="type">
+          <value>ccwgroup_member</value>
+        </attribute>
+        <element name="group_device"><text/></element>
+      </element>
+    </optional>
+  </define>
+
   <define name="capccwdev">
     <attribute name="type">
       <value>ccw</value>
@@ -725,6 +736,7 @@
       </element>
     </optional>
     <ref name="capccwaddress"/>
+    <ref name="capccwgroupmember"/>
   </define>
 
   <define name="capcssdev">
diff --git a/src/conf/virnodedeviceobj.c b/src/conf/virnodedeviceobj.c
index 23984995c8..c5ddf0b4fb 100644
--- a/src/conf/virnodedeviceobj.c
+++ b/src/conf/virnodedeviceobj.c
@@ -723,6 +723,12 @@ virNodeDeviceObjHasCap(const virNodeDeviceObj *obj,
                 return true;
             break;
 
+        case VIR_NODE_DEV_CAP_CCW_DEV:
+            if (type == VIR_NODE_DEV_CAP_CCWGROUP_MEMBER &&
+                (cap->data.ccw_dev.flags & VIR_NODE_DEV_CAP_FLAG_CCW_CCWGROUP_MEMBER))
+                return true;
+            break;
+
         case VIR_NODE_DEV_CAP_SYSTEM:
         case VIR_NODE_DEV_CAP_USB_DEV:
         case VIR_NODE_DEV_CAP_USB_INTERFACE:
@@ -736,12 +742,12 @@ virNodeDeviceObjHasCap(const virNodeDeviceObj *obj,
         case VIR_NODE_DEV_CAP_DRM:
         case VIR_NODE_DEV_CAP_MDEV_TYPES:
         case VIR_NODE_DEV_CAP_MDEV:
-        case VIR_NODE_DEV_CAP_CCW_DEV:
         case VIR_NODE_DEV_CAP_VDPA:
         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_CCWGROUP_MEMBER:
         case VIR_NODE_DEV_CAP_LAST:
             break;
         }
@@ -901,7 +907,8 @@ virNodeDeviceObjMatch(virNodeDeviceObj *obj,
               MATCH_CAP(AP_QUEUE)      ||
               MATCH_CAP(AP_MATRIX)     ||
               MATCH_CAP(VPD)           ||
-              MATCH_CAP(CCWGROUP_DEV)))
+              MATCH_CAP(CCWGROUP_DEV)  ||
+              MATCH_CAP(CCWGROUP_MEMBER)))
             return false;
     }
 
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 07e1e673f0..8abda5fa7e 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -899,6 +899,7 @@ virNodeDeviceDefFree;
 virNodeDeviceDefParse;
 virNodeDeviceDefParseXML;
 virNodeDeviceGetAPMatrixDynamicCaps;
+virNodeDeviceGetCCWDynamicCaps;
 virNodeDeviceGetCCWGroupDynamicCaps;
 virNodeDeviceGetCSSDynamicCaps;
 virNodeDeviceGetMdevParentDynamicCaps;
diff --git a/src/node_device/node_device_driver.c b/src/node_device/node_device_driver.c
index d716561361..123b16a292 100644
--- a/src/node_device/node_device_driver.c
+++ b/src/node_device/node_device_driver.c
@@ -717,6 +717,7 @@ nodeDeviceObjFormatAddress(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_MEMBER:
         case VIR_NODE_DEV_CAP_LAST:
             break;
         }
@@ -2194,6 +2195,7 @@ int nodeDeviceDefValidate(virNodeDeviceDef *def,
             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_CCWGROUP_MEMBER:
             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 a78f47b65a..ba5727ed8f 100644
--- a/src/node_device/node_device_udev.c
+++ b/src/node_device/node_device_udev.c
@@ -1239,6 +1239,9 @@ udevProcessCCW(struct udev_device *device,
 
     udevGenerateDeviceName(device, def, NULL);
 
+    if (virNodeDeviceGetCCWDynamicCaps(def->sysfs_path, &def->caps->data.ccw_dev) < 0)
+        return -1;
+
     return 0;
 }
 
@@ -1575,6 +1578,7 @@ udevGetDeviceDetails(virNodeDeviceDriverState *driver_state,
         return udevProcessMdevParent(device, def);
     case VIR_NODE_DEV_CAP_CCWGROUP_DEV:
         return udevProcessCCWGroup(device, def);
+    case VIR_NODE_DEV_CAP_CCWGROUP_MEMBER:
     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 0873c61889..762ef671a2 100644
--- a/src/util/virccw.c
+++ b/src/util/virccw.c
@@ -203,3 +203,26 @@ virCCWGroupTypeQethFree(virCCWGroupTypeQeth *qeth)
     VIR_FREE(qeth->card_type);
     VIR_FREE(qeth->chpid);
 }
+
+char *
+virCCWDeviceGetGroupDev(const char *sysfs_path)
+{
+    g_autofree char *ccwgroup_path = NULL;
+    g_autofree char *group_dev_path = NULL;
+
+    group_dev_path = g_build_filename(sysfs_path, "group_device", NULL);
+
+    if (!virFileExists(group_dev_path))
+        return NULL;
+
+    if (virFileIsLink(group_dev_path) != 1)
+        return NULL;
+
+    if (virFileResolveLink(group_dev_path, &ccwgroup_path) < 0)
+        return NULL;
+
+    if (!virFileExists(ccwgroup_path))
+        return NULL;
+
+    return virCCWGroupDeviceDevNodeName("ccwgroup", ccwgroup_path);
+}
diff --git a/src/util/virccw.h b/src/util/virccw.h
index a8c9fa83ef..67faf878a8 100644
--- a/src/util/virccw.h
+++ b/src/util/virccw.h
@@ -71,4 +71,7 @@ int virCCWGroupDeviceGetMembers(const char *sysfs_path,
 
 void virCCWGroupTypeQethFree(virCCWGroupTypeQeth *qeth);
 
+char* virCCWDeviceGetGroupDev(const char *sysfs_path)
+    ATTRIBUTE_NONNULL(1);
+
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(virCCWGroupMemberType, virCCWGroupMemberTypeFree);
diff --git a/tests/nodedevschemadata/ccw_0_0_ff02_ccwgroup.xml b/tests/nodedevschemadata/ccw_0_0_ff02_ccwgroup.xml
new file mode 100644
index 0000000000..11767facd3
--- /dev/null
+++ b/tests/nodedevschemadata/ccw_0_0_ff02_ccwgroup.xml
@@ -0,0 +1,13 @@
+<device>
+  <name>ccw_0_0_ff02</name>
+  <path>/sys/devices/css0/0.0.0070/0.0.ff02</path>
+  <parent>css_0_0_0070</parent>
+  <capability type='ccw'>
+    <cssid>0x0</cssid>
+    <ssid>0x0</ssid>
+    <devno>0xff02</devno>
+    <capability type='ccwgroup_member'>
+      <group_device>ccwgroup_0_0_ff00</group_device>
+    </capability>
+  </capability>
+</device>
diff --git a/tests/nodedevxml2xmlout/ccw_0_0_ff02_ccwgroup.xml b/tests/nodedevxml2xmlout/ccw_0_0_ff02_ccwgroup.xml
new file mode 120000
index 0000000000..4d2b000b11
--- /dev/null
+++ b/tests/nodedevxml2xmlout/ccw_0_0_ff02_ccwgroup.xml
@@ -0,0 +1 @@
+../nodedevschemadata/ccw_0_0_ff02_ccwgroup.xml
\ No newline at end of file
diff --git a/tests/nodedevxml2xmltest.c b/tests/nodedevxml2xmltest.c
index d4d87b3bdc..265b37b218 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("ccw_0_0_ff02_ccwgroup");
     DO_TEST("ccwgroup_0_0_bd00");
     DO_TEST("css_0_0_ffff");
     DO_TEST("css_0_0_ffff_channel_dev_addr");
diff --git a/tools/virsh-nodedev.c b/tools/virsh-nodedev.c
index 3aae7285a9..e759b9f629 100644
--- a/tools/virsh-nodedev.c
+++ b/tools/virsh-nodedev.c
@@ -504,6 +504,9 @@ cmdNodeListDevices(vshControl *ctl, const vshCmd *cmd G_GNUC_UNUSED)
         case VIR_NODE_DEV_CAP_CCWGROUP_DEV:
             flags |= VIR_CONNECT_LIST_NODE_DEVICES_CAP_CCWGROUP_DEV;
             break;
+        case VIR_NODE_DEV_CAP_CCWGROUP_MEMBER:
+            flags |= VIR_CONNECT_LIST_NODE_DEVICES_CAP_CCWGROUP_MEMBER;
+            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