[PATCH 0/8] nodedev: add support for ccwgroup based qeth devices

CCW group devices are devices that use multiple subchannels on the mainframe's channel subsystem. A qeth group device maps to subchannels and their corresponding device numbers and device bus-IDs. The qeth device driver requires three I/O subchannels for each HiperSockets CHPID or OSA-Express CHPID in QDIO mode. One subchannel is for control reads, one for control writes, and the third is for data. The qeth device driver uses the QDIO protocol to communicate with the HiperSockets and OSA-Express adapter. As the node device driver is building a tree relationship between nodes the ccwgroup device is placed besides the subchannel nodes under computer and list the group members within a new ccwgroup capability. A new capability ccwgroup_member is added into capability ccw to represent a device membership to a ccwgroup. Filters are added to find ccwgroups as well as ccwgroup members. Boris Fiuczynski (8): nodedev: refactor CCW device address format nodedev: refactor CCW device address nodedev: remove virNodeDeviceCapCCWDefFormat nodedev: refactor CCW state format for use in ccwgroup nodedev: refactor udevCCWGetState nodedev: add ccwgroup node device support nodedev: add ccwgroup capability support to ccw devices nodedev: add qeth layer2 and layer3 device types docs/manpages/virsh.rst | 19 +- include/libvirt/libvirt-nodedev.h | 2 + src/conf/node_device_conf.c | 369 +++++++++++++++++- src/conf/node_device_conf.h | 43 +- src/conf/schemas/nodedev.rng | 59 +++ src/conf/virnodedeviceobj.c | 13 +- src/libvirt_private.syms | 3 + src/node_device/node_device_driver.c | 18 +- src/node_device/node_device_udev.c | 66 +++- src/util/virccw.c | 125 ++++++ src/util/virccw.h | 25 ++ tests/nodedevmdevctltest.c | 7 +- .../ccw_0_0_ff02_ccwgroup.xml | 13 + tests/nodedevschemadata/ccwgroup_0_0_bd00.xml | 20 + .../ccw_0_0_ff02_ccwgroup.xml | 1 + tests/nodedevxml2xmlout/ccwgroup_0_0_bd00.xml | 1 + tests/nodedevxml2xmltest.c | 2 + tools/virsh-nodedev.c | 6 + 18 files changed, 738 insertions(+), 54 deletions(-) create mode 100644 tests/nodedevschemadata/ccw_0_0_ff02_ccwgroup.xml create mode 100644 tests/nodedevschemadata/ccwgroup_0_0_bd00.xml create mode 120000 tests/nodedevxml2xmlout/ccw_0_0_ff02_ccwgroup.xml create mode 120000 tests/nodedevxml2xmlout/ccwgroup_0_0_bd00.xml -- 2.47.0

Refactor for reuse in the following patch. Reviewed-by: Marc Hartmayer <mhartmay@linux.ibm.com> Signed-off-by: Boris Fiuczynski <fiuczy@linux.ibm.com> --- src/conf/node_device_conf.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c index 08a89942ba..9711594994 100644 --- a/src/conf/node_device_conf.c +++ b/src/conf/node_device_conf.c @@ -623,6 +623,19 @@ virNodeDeviceCapVDPADefFormat(virBuffer *buf, } +static void +virCCWDeviceAddressFormat(virBuffer *buf, + const virCCWDeviceAddress *ccw_address) +{ + virBufferAsprintf(buf, "<cssid>0x%x</cssid>\n", + ccw_address->cssid); + virBufferAsprintf(buf, "<ssid>0x%x</ssid>\n", + ccw_address->ssid); + virBufferAsprintf(buf, "<devno>0x%04x</devno>\n", + ccw_address->devno); +} + + static void virNodeDeviceCapCCWDefFormat(virBuffer *buf, const virNodeDevCapData *data) @@ -645,12 +658,9 @@ virNodeDeviceCapCSSDefFormat(virBuffer *buf, virNodeDeviceCapCCWDefFormat(buf, data); if (ccw_dev.channel_dev_addr) { - virCCWDeviceAddress *ccw = ccw_dev.channel_dev_addr; virBufferAddLit(buf, "<channel_dev_addr>\n"); virBufferAdjustIndent(buf, 2); - virBufferAsprintf(buf, "<cssid>0x%x</cssid>\n", ccw->cssid); - virBufferAsprintf(buf, "<ssid>0x%x</ssid>\n", ccw->ssid); - virBufferAsprintf(buf, "<devno>0x%04x</devno>\n", ccw->devno); + virCCWDeviceAddressFormat(buf, ccw_dev.channel_dev_addr); virBufferAdjustIndent(buf, -2); virBufferAddLit(buf, "</channel_dev_addr>\n"); } -- 2.47.0

Replace cssid, ssid and devno elements with virCCWDeviceAddress. Reviewed-by: Marc Hartmayer <mhartmay@linux.ibm.com> Signed-off-by: Boris Fiuczynski <fiuczy@linux.ibm.com> --- src/conf/node_device_conf.c | 16 ++++++---------- src/conf/node_device_conf.h | 4 +--- src/node_device/node_device_driver.c | 11 ++--------- src/node_device/node_device_udev.c | 9 ++++++--- tests/nodedevmdevctltest.c | 7 ++++--- 5 files changed, 19 insertions(+), 28 deletions(-) diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c index 9711594994..85effb7d78 100644 --- a/src/conf/node_device_conf.c +++ b/src/conf/node_device_conf.c @@ -640,12 +640,7 @@ static void virNodeDeviceCapCCWDefFormat(virBuffer *buf, const virNodeDevCapData *data) { - virBufferAsprintf(buf, "<cssid>0x%x</cssid>\n", - data->ccw_dev.cssid); - virBufferAsprintf(buf, "<ssid>0x%x</ssid>\n", - data->ccw_dev.ssid); - virBufferAsprintf(buf, "<devno>0x%04x</devno>\n", - data->ccw_dev.devno); + virCCWDeviceAddressFormat(buf, data->ccw_dev.dev_addr); } @@ -1231,9 +1226,7 @@ virNodeDevCapCCWParseXML(xmlXPathContextPtr ctxt, if (virNodeDevCCWDeviceAddressParseXML(ctxt, node, def->name, ccw_addr) < 0) return -1; - ccw_dev->cssid = ccw_addr->cssid; - ccw_dev->ssid = ccw_addr->ssid; - ccw_dev->devno = ccw_addr->devno; + ccw_dev->dev_addr = g_steal_pointer(&ccw_addr); return 0; } @@ -2621,6 +2614,7 @@ virNodeDevCapsDefFree(virNodeDevCapsDef *caps) g_free(data->mdev.parent_addr); break; case VIR_NODE_DEV_CAP_CSS_DEV: + g_free(data->ccw_dev.dev_addr); for (i = 0; i < data->ccw_dev.nmdev_types; i++) virMediatedDeviceTypeFree(data->ccw_dev.mdev_types[i]); g_free(data->ccw_dev.mdev_types); @@ -2638,10 +2632,12 @@ virNodeDevCapsDefFree(virNodeDevCapsDef *caps) g_free(data->mdev_parent.mdev_types); g_free(data->mdev_parent.address); break; + case VIR_NODE_DEV_CAP_CCW_DEV: + g_free(data->ccw_dev.dev_addr); + break; case VIR_NODE_DEV_CAP_DRM: case VIR_NODE_DEV_CAP_FC_HOST: case VIR_NODE_DEV_CAP_VPORTS: - 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: diff --git a/src/conf/node_device_conf.h b/src/conf/node_device_conf.h index 25666a333e..ad2258e90d 100644 --- a/src/conf/node_device_conf.h +++ b/src/conf/node_device_conf.h @@ -281,9 +281,7 @@ struct _virNodeDevCapDRM { typedef struct _virNodeDevCapCCW virNodeDevCapCCW; struct _virNodeDevCapCCW { - unsigned int cssid; - unsigned int ssid; - unsigned int devno; + virCCWDeviceAddress *dev_addr; unsigned int flags; /* enum virNodeDevCCWCapFlags */ virMediatedDeviceType **mdev_types; size_t nmdev_types; diff --git a/src/node_device/node_device_driver.c b/src/node_device/node_device_driver.c index fa5db0d5d5..9898b1914a 100644 --- a/src/node_device/node_device_driver.c +++ b/src/node_device/node_device_driver.c @@ -683,16 +683,9 @@ nodeDeviceObjFormatAddress(virNodeDeviceObj *obj) break; } - case VIR_NODE_DEV_CAP_CSS_DEV: { - virCCWDeviceAddress ccw_addr = { - .cssid = caps->data.ccw_dev.cssid, - .ssid = caps->data.ccw_dev.ssid, - .devno = caps->data.ccw_dev.devno - }; - - addr = virCCWDeviceAddressAsString(&ccw_addr); + case VIR_NODE_DEV_CAP_CSS_DEV: + addr = virCCWDeviceAddressAsString(caps->data.ccw_dev.dev_addr); break; - } case VIR_NODE_DEV_CAP_AP_MATRIX: addr = g_strdup(caps->data.ap_matrix.addr); diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_device_udev.c index 1d8486f623..97fbe95ac7 100644 --- a/src/node_device/node_device_udev.c +++ b/src/node_device/node_device_udev.c @@ -1184,18 +1184,21 @@ udevGetCCWAddress(const char *sysfs_path, virNodeDevCapData *data) { char *p; + g_autofree virCCWDeviceAddress *ccw_addr = g_new0(virCCWDeviceAddress, 1); if ((p = strrchr(sysfs_path, '/')) == NULL || virCCWDeviceAddressParseFromString(p + 1, - &data->ccw_dev.cssid, - &data->ccw_dev.ssid, - &data->ccw_dev.devno) < 0) { + &ccw_addr->cssid, + &ccw_addr->ssid, + &ccw_addr->devno) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("failed to parse the CCW address from sysfs path: '%1$s'"), sysfs_path); return -1; } + data->ccw_dev.dev_addr = g_steal_pointer(&ccw_addr); + return 0; } diff --git a/tests/nodedevmdevctltest.c b/tests/nodedevmdevctltest.c index 93b0efcb1c..c7e6836edb 100644 --- a/tests/nodedevmdevctltest.c +++ b/tests/nodedevmdevctltest.c @@ -435,9 +435,10 @@ fakeCSSDevice(void) def->caps->data.type = VIR_NODE_DEV_CAP_CSS_DEV; css_dev = &def->caps->data.ccw_dev; - css_dev->cssid = 0; - css_dev->ssid = 0; - css_dev->devno = 82; + css_dev->dev_addr = g_new0(virCCWDeviceAddress, 1); + css_dev->dev_addr->cssid = 0; + css_dev->dev_addr->ssid = 0; + css_dev->dev_addr->devno = 82; return def; } -- 2.47.0

Directly use virCCWDeviceAddressFormat. Reviewed-by: Marc Hartmayer <mhartmay@linux.ibm.com> Signed-off-by: Boris Fiuczynski <fiuczy@linux.ibm.com> --- src/conf/node_device_conf.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c index 85effb7d78..433334511f 100644 --- a/src/conf/node_device_conf.c +++ b/src/conf/node_device_conf.c @@ -636,21 +636,13 @@ virCCWDeviceAddressFormat(virBuffer *buf, } -static void -virNodeDeviceCapCCWDefFormat(virBuffer *buf, - const virNodeDevCapData *data) -{ - virCCWDeviceAddressFormat(buf, data->ccw_dev.dev_addr); -} - - static void virNodeDeviceCapCSSDefFormat(virBuffer *buf, const virNodeDevCapData *data) { virNodeDevCapCCW ccw_dev = data->ccw_dev; - virNodeDeviceCapCCWDefFormat(buf, data); + virCCWDeviceAddressFormat(buf, ccw_dev.dev_addr); if (ccw_dev.channel_dev_addr) { virBufferAddLit(buf, "<channel_dev_addr>\n"); @@ -758,7 +750,7 @@ virNodeDeviceDefFormat(const virNodeDeviceDef *def, unsigned int flags) const char *state = virNodeDevCCWStateTypeToString(data->ccw_dev.state); virBufferEscapeString(&buf, "<state>%s</state>\n", state); } - virNodeDeviceCapCCWDefFormat(&buf, data); + virCCWDeviceAddressFormat(&buf, data->ccw_dev.dev_addr); break; case VIR_NODE_DEV_CAP_CSS_DEV: virNodeDeviceCapCSSDefFormat(&buf, data); -- 2.47.0

Refactor to allow reuse in ccwgroup. Reviewed-by: Marc Hartmayer <mhartmay@linux.ibm.com> Signed-off-by: Boris Fiuczynski <fiuczy@linux.ibm.com> --- src/conf/node_device_conf.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c index 433334511f..bfd81b1692 100644 --- a/src/conf/node_device_conf.c +++ b/src/conf/node_device_conf.c @@ -659,6 +659,17 @@ virNodeDeviceCapCSSDefFormat(virBuffer *buf, } +static void +virNodeDeviceCapCCWStateTypeFormat(virBuffer *buf, + const virNodeDevCCWStateType state_type) +{ + if (state_type != VIR_NODE_DEV_CCW_STATE_LAST) { + const char *state = virNodeDevCCWStateTypeToString(state_type); + virBufferEscapeString(buf, "<state>%s</state>\n", state); + } +} + + char * virNodeDeviceDefFormat(const virNodeDeviceDef *def, unsigned int flags) { @@ -746,10 +757,7 @@ virNodeDeviceDefFormat(const virNodeDeviceDef *def, unsigned int flags) virNodeDeviceCapMdevDefFormat(&buf, data, inactive_state); break; case VIR_NODE_DEV_CAP_CCW_DEV: - if (data->ccw_dev.state != VIR_NODE_DEV_CCW_STATE_LAST) { - const char *state = virNodeDevCCWStateTypeToString(data->ccw_dev.state); - virBufferEscapeString(&buf, "<state>%s</state>\n", state); - } + virNodeDeviceCapCCWStateTypeFormat(&buf, data->ccw_dev.state); virCCWDeviceAddressFormat(&buf, data->ccw_dev.dev_addr); break; case VIR_NODE_DEV_CAP_CSS_DEV: -- 2.47.0

Refactor method to be only ccw state type depended to allow reuse in a later patch. Reviewed-by: Marc Hartmayer <mhartmay@linux.ibm.com> Signed-off-by: Boris Fiuczynski <fiuczy@linux.ibm.com> --- src/node_device/node_device_udev.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_device_udev.c index 97fbe95ac7..6b362625f7 100644 --- a/src/node_device/node_device_udev.c +++ b/src/node_device/node_device_udev.c @@ -1205,7 +1205,7 @@ udevGetCCWAddress(const char *sysfs_path, static int udevCCWGetState(struct udev_device *device, - virNodeDevCapData *data) + virNodeDevCCWStateType *state) { int online = 0; @@ -1215,10 +1215,10 @@ udevCCWGetState(struct udev_device *device, switch (online) { case VIR_NODE_DEV_CCW_STATE_OFFLINE: case VIR_NODE_DEV_CCW_STATE_ONLINE: - data->ccw_dev.state = online; + *state = online; break; default: - data->ccw_dev.state = VIR_NODE_DEV_CCW_STATE_LAST; + *state = VIR_NODE_DEV_CCW_STATE_LAST; break; } @@ -1231,7 +1231,7 @@ udevProcessCCW(struct udev_device *device, virNodeDeviceDef *def) { /* process only online devices to keep the list sane */ - if (udevCCWGetState(device, &def->caps->data) < 0) + if (udevCCWGetState(device, &def->caps->data.ccw_dev.state) < 0) return -1; if (udevGetCCWAddress(def->sysfs_path, &def->caps->data) < 0) -- 2.47.0

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

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@linux.ibm.com> --- 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

Signed-off-by: Boris Fiuczynski <fiuczy@linux.ibm.com> --- src/conf/node_device_conf.c | 8 ++++++++ src/conf/node_device_conf.h | 2 ++ src/conf/schemas/nodedev.rng | 6 +++++- src/node_device/node_device_udev.c | 6 +++++- 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c index 3e88f5da87..30c2b4440e 100644 --- a/src/conf/node_device_conf.c +++ b/src/conf/node_device_conf.c @@ -78,6 +78,8 @@ VIR_ENUM_IMPL(virNodeDevCap, VIR_ENUM_IMPL(virNodeDevCCWGroupCap, VIR_NODE_DEV_CAP_CCWGROUP_LAST, "qeth_generic", + "qeth_layer2", + "qeth_layer3", ); VIR_ENUM_IMPL(virNodeDevNetCap, @@ -731,6 +733,8 @@ virNodeDeviceCapCCWGroupDefFormat(virBuffer *buf, virBufferAdjustIndent(buf, 2); switch (ccwgroup_dev.type) { case VIR_NODE_DEV_CAP_CCWGROUP_QETH_GENERIC: + case VIR_NODE_DEV_CAP_CCWGROUP_QETH_LAYER2: + case VIR_NODE_DEV_CAP_CCWGROUP_QETH_LAYER3: virNodeDeviceCapCCWGroupQethFormat(buf, &ccwgroup_dev.qeth); break; case VIR_NODE_DEV_CAP_CCWGROUP_LAST: @@ -1509,6 +1513,8 @@ virNodeDevCapCCWGroupParseXML(xmlXPathContextPtr ctxt, switch (ccwgroup_dev->type) { case VIR_NODE_DEV_CAP_CCWGROUP_QETH_GENERIC: + case VIR_NODE_DEV_CAP_CCWGROUP_QETH_LAYER2: + case VIR_NODE_DEV_CAP_CCWGROUP_QETH_LAYER3: if (virNodeDevCapCCWGroupQethParseXML(ctxt, cap_node, &ccwgroup_dev->qeth) < 0) return -1; break; @@ -2865,6 +2871,8 @@ virNodeDevCapsDefFree(virNodeDevCapsDef *caps) g_free(data->ccwgroup_dev.members); switch (data->ccwgroup_dev.type) { case VIR_NODE_DEV_CAP_CCWGROUP_QETH_GENERIC: + case VIR_NODE_DEV_CAP_CCWGROUP_QETH_LAYER2: + case VIR_NODE_DEV_CAP_CCWGROUP_QETH_LAYER3: virCCWGroupTypeQethFree(&data->ccwgroup_dev.qeth); break; case VIR_NODE_DEV_CAP_CCWGROUP_LAST: diff --git a/src/conf/node_device_conf.h b/src/conf/node_device_conf.h index a6cef57b95..c66660342f 100644 --- a/src/conf/node_device_conf.h +++ b/src/conf/node_device_conf.h @@ -80,6 +80,8 @@ typedef enum { 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_QETH_LAYER2, /* s390 CCWGROUP QETH layer 2 device */ + VIR_NODE_DEV_CAP_CCWGROUP_QETH_LAYER3, /* s390 CCWGROUP QETH layer 3 device */ VIR_NODE_DEV_CAP_CCWGROUP_LAST } virNodeDevCCWGroupCapType; diff --git a/src/conf/schemas/nodedev.rng b/src/conf/schemas/nodedev.rng index f52c6ab752..31ce517e4d 100644 --- a/src/conf/schemas/nodedev.rng +++ b/src/conf/schemas/nodedev.rng @@ -672,7 +672,11 @@ <define name="capccwgrouptypeqeth"> <attribute name="type"> - <value>qeth_generic</value> + <choice> + <value>qeth_generic</value> + <value>qeth_layer2</value> + <value>qeth_layer3</value> + </choice> </attribute> <interleave> <element name="card_type"><text/></element> diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_device_udev.c index ba5727ed8f..082f9d5cf0 100644 --- a/src/node_device/node_device_udev.c +++ b/src/node_device/node_device_udev.c @@ -1410,6 +1410,8 @@ udevProcessCCWGroup(struct udev_device *device, switch (data->ccwgroup_dev.type) { case VIR_NODE_DEV_CAP_CCWGROUP_QETH_GENERIC: + case VIR_NODE_DEV_CAP_CCWGROUP_QETH_LAYER2: + case VIR_NODE_DEV_CAP_CCWGROUP_QETH_LAYER3: { virCCWGroupTypeQeth *qeth = &data->ccwgroup_dev.qeth; /* process qeth device information */ @@ -1487,7 +1489,9 @@ 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")) + else if (STREQ(devtype, "qeth_generic") || + STREQ(devtype, "qeth_layer2") || + STREQ(devtype, "qeth_layer3")) *type = VIR_NODE_DEV_CAP_CCWGROUP_DEV; } else { /* PCI devices don't set the DEVTYPE property. */ -- 2.47.0

On 2/4/25 18:11, Boris Fiuczynski wrote:
CCW group devices are devices that use multiple subchannels on the mainframe's channel subsystem. A qeth group device maps to subchannels and their corresponding device numbers and device bus-IDs.
The qeth device driver requires three I/O subchannels for each HiperSockets CHPID or OSA-Express CHPID in QDIO mode. One subchannel is for control reads, one for control writes, and the third is for data.
The qeth device driver uses the QDIO protocol to communicate with the HiperSockets and OSA-Express adapter.
As the node device driver is building a tree relationship between nodes the ccwgroup device is placed besides the subchannel nodes under computer and list the group members within a new ccwgroup capability. A new capability ccwgroup_member is added into capability ccw to represent a device membership to a ccwgroup. Filters are added to find ccwgroups as well as ccwgroup members.
Boris Fiuczynski (8): nodedev: refactor CCW device address format nodedev: refactor CCW device address nodedev: remove virNodeDeviceCapCCWDefFormat nodedev: refactor CCW state format for use in ccwgroup nodedev: refactor udevCCWGetState nodedev: add ccwgroup node device support nodedev: add ccwgroup capability support to ccw devices nodedev: add qeth layer2 and layer3 device types
docs/manpages/virsh.rst | 19 +- include/libvirt/libvirt-nodedev.h | 2 + src/conf/node_device_conf.c | 369 +++++++++++++++++- src/conf/node_device_conf.h | 43 +- src/conf/schemas/nodedev.rng | 59 +++ src/conf/virnodedeviceobj.c | 13 +- src/libvirt_private.syms | 3 + src/node_device/node_device_driver.c | 18 +- src/node_device/node_device_udev.c | 66 +++- src/util/virccw.c | 125 ++++++ src/util/virccw.h | 25 ++ tests/nodedevmdevctltest.c | 7 +- .../ccw_0_0_ff02_ccwgroup.xml | 13 + tests/nodedevschemadata/ccwgroup_0_0_bd00.xml | 20 + .../ccw_0_0_ff02_ccwgroup.xml | 1 + tests/nodedevxml2xmlout/ccwgroup_0_0_bd00.xml | 1 + tests/nodedevxml2xmltest.c | 2 + tools/virsh-nodedev.c | 6 + 18 files changed, 738 insertions(+), 54 deletions(-) create mode 100644 tests/nodedevschemadata/ccw_0_0_ff02_ccwgroup.xml create mode 100644 tests/nodedevschemadata/ccwgroup_0_0_bd00.xml create mode 120000 tests/nodedevxml2xmlout/ccw_0_0_ff02_ccwgroup.xml create mode 120000 tests/nodedevxml2xmlout/ccwgroup_0_0_bd00.xml
There are some nits (e.g. formatting, proper private symbol export), but I'll fix that before merging. Reviewed-by: Michal Privoznik <mprivozn@redhat.com> Michal

On 2/5/25 18:01, Michal Prívozník wrote:
On 2/4/25 18:11, Boris Fiuczynski wrote:
CCW group devices are devices that use multiple subchannels on the mainframe's channel subsystem. A qeth group device maps to subchannels and their corresponding device numbers and device bus-IDs.
The qeth device driver requires three I/O subchannels for each HiperSockets CHPID or OSA-Express CHPID in QDIO mode. One subchannel is for control reads, one for control writes, and the third is for data.
The qeth device driver uses the QDIO protocol to communicate with the HiperSockets and OSA-Express adapter.
As the node device driver is building a tree relationship between nodes the ccwgroup device is placed besides the subchannel nodes under computer and list the group members within a new ccwgroup capability. A new capability ccwgroup_member is added into capability ccw to represent a device membership to a ccwgroup. Filters are added to find ccwgroups as well as ccwgroup members.
Boris Fiuczynski (8): nodedev: refactor CCW device address format nodedev: refactor CCW device address nodedev: remove virNodeDeviceCapCCWDefFormat nodedev: refactor CCW state format for use in ccwgroup nodedev: refactor udevCCWGetState nodedev: add ccwgroup node device support nodedev: add ccwgroup capability support to ccw devices nodedev: add qeth layer2 and layer3 device types
docs/manpages/virsh.rst | 19 +- include/libvirt/libvirt-nodedev.h | 2 + src/conf/node_device_conf.c | 369 +++++++++++++++++- src/conf/node_device_conf.h | 43 +- src/conf/schemas/nodedev.rng | 59 +++ src/conf/virnodedeviceobj.c | 13 +- src/libvirt_private.syms | 3 + src/node_device/node_device_driver.c | 18 +- src/node_device/node_device_udev.c | 66 +++- src/util/virccw.c | 125 ++++++ src/util/virccw.h | 25 ++ tests/nodedevmdevctltest.c | 7 +- .../ccw_0_0_ff02_ccwgroup.xml | 13 + tests/nodedevschemadata/ccwgroup_0_0_bd00.xml | 20 + .../ccw_0_0_ff02_ccwgroup.xml | 1 + tests/nodedevxml2xmlout/ccwgroup_0_0_bd00.xml | 1 + tests/nodedevxml2xmltest.c | 2 + tools/virsh-nodedev.c | 6 + 18 files changed, 738 insertions(+), 54 deletions(-) create mode 100644 tests/nodedevschemadata/ccw_0_0_ff02_ccwgroup.xml create mode 100644 tests/nodedevschemadata/ccwgroup_0_0_bd00.xml create mode 120000 tests/nodedevxml2xmlout/ccw_0_0_ff02_ccwgroup.xml create mode 120000 tests/nodedevxml2xmlout/ccwgroup_0_0_bd00.xml
There are some nits (e.g. formatting, proper private symbol export), but I'll fix that before merging.
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
Michal
Michal, sorry for the nits and thanks for fixing them. -- 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
participants (3)
-
Boris Fiuczynski
-
Michal Prívozník
-
Peter Krempa