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