* XML serialization and deserialization of PCI VPD; * PCI VPD capability flags added and used in relevant places; * XML to XML tests for the added capability. Signed-off-by: Dmitrii Shcherbakov <dmitrii.shcherbakov@xxxxxxxxxxxxx> --- docs/schemas/nodedev.rng | 96 ++++++ include/libvirt/libvirt-nodedev.h | 1 + src/conf/node_device_conf.c | 293 ++++++++++++++++++ src/conf/node_device_conf.h | 7 +- src/conf/virnodedeviceobj.c | 7 +- src/node_device/node_device_driver.c | 2 + src/node_device/node_device_udev.c | 2 + .../pci_0000_42_00_0_vpd.xml | 42 +++ .../pci_0000_42_00_0_vpd.xml | 1 + tests/nodedevxml2xmltest.c | 1 + tools/virsh-nodedev.c | 3 + 11 files changed, 453 insertions(+), 2 deletions(-) create mode 100644 tests/nodedevschemadata/pci_0000_42_00_0_vpd.xml create mode 120000 tests/nodedevxml2xmlout/pci_0000_42_00_0_vpd.xml diff --git a/docs/schemas/nodedev.rng b/docs/schemas/nodedev.rng index e089e66858..e4733f0804 100644 --- a/docs/schemas/nodedev.rng +++ b/docs/schemas/nodedev.rng @@ -223,6 +223,10 @@ <ref name="mdev_types"/> </optional> + <optional> + <ref name="vpd"/> + </optional> + <optional> <element name="iommuGroup"> <attribute name="number"> @@ -770,6 +774,80 @@ </element> </define> + <define name="vpd"> + <element name="capability"> + <attribute name="type"> + <value>vpd</value> + </attribute> + <element name="name"> + <ref name="vpdFieldValueFormat"/> + </element> + <optional> + <element name="fields"> + <attribute name="access"> + <value>readonly</value> + </attribute> + <optional> + <element name="change_level"> + <ref name="vpdFieldValueFormat"/> + </element> + </optional> + <optional> + <element name="manufacture_id"> + <ref name="vpdFieldValueFormat"/> + </element> + </optional> + <optional> + <element name="part_number"> + <ref name="vpdFieldValueFormat"/> + </element> + </optional> + <optional> + <element name="serial_number"> + <ref name="vpdFieldValueFormat"/> + </element> + </optional> + <zeroOrMore> + <element name="vendor_field"> + <attribute name="index"> + <ref name="vendorVPDFieldIndex"/> + </attribute> + <ref name="vpdFieldValueFormat"/> + </element> + </zeroOrMore> + </element> + </optional> + <optional> + <element name="fields"> + <attribute name="access"> + <value>readwrite</value> + </attribute> + <optional> + <element name="asset_tag"> + <ref name="vpdFieldValueFormat"/> + </element> + </optional> + <zeroOrMore> + <element name="vendor_field"> + <attribute name="index"> + <ref name="vendorVPDFieldIndex"/> + </attribute> + <ref name="vpdFieldValueFormat"/> + </element> + </zeroOrMore> + <zeroOrMore> + <element name="system_field"> + <attribute name="index"> + <ref name="systemVPDFieldIndex"/> + </attribute> + <ref name="vpdFieldValueFormat"/> + </element> + </zeroOrMore> + </element> + </optional> + </element> + </define> + <define name="apDomainRange"> <choice> <data type="string"> @@ -782,4 +860,22 @@ </choice> </define> + <define name="vpdFieldValueFormat"> + <data type="string"> + <param name="pattern">[0-9a-zA-F -_,.:;=]{0,255}</param> + </data> + </define> + + <define name="vendorVPDFieldIndex"> + <data type="string"> + <param name="pattern">[0-9A-Z]{1}</param> + </data> + </define> + + <define name="systemVPDFieldIndex"> + <data type="string"> + <param name="pattern">[0-9B-Z]{1}</param> + </data> + </define> + </grammar> diff --git a/include/libvirt/libvirt-nodedev.h b/include/libvirt/libvirt-nodedev.h index e492634217..245365b07f 100644 --- a/include/libvirt/libvirt-nodedev.h +++ b/include/libvirt/libvirt-nodedev.h @@ -84,6 +84,7 @@ typedef enum { VIR_CONNECT_LIST_NODE_DEVICES_CAP_AP_CARD = 1 << 18, /* s390 AP Card device */ VIR_CONNECT_LIST_NODE_DEVICES_CAP_AP_QUEUE = 1 << 19, /* s390 AP Queue */ VIR_CONNECT_LIST_NODE_DEVICES_CAP_AP_MATRIX = 1 << 20, /* s390 AP Matrix */ + VIR_CONNECT_LIST_NODE_DEVICES_CAP_VPD = 1 << 21, /* Device with VPD */ /* filter the devices by active state */ VIR_CONNECT_LIST_NODE_DEVICES_INACTIVE = 1 << 30, /* Inactive devices */ diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c index 9bbff97ffd..8ff756b666 100644 --- a/src/conf/node_device_conf.c +++ b/src/conf/node_device_conf.c @@ -36,6 +36,7 @@ #include "virrandom.h" #include "virlog.h" #include "virfcp.h" +#include "virpcivpd.h" #define VIR_FROM_THIS VIR_FROM_NODEDEV @@ -70,6 +71,7 @@ VIR_ENUM_IMPL(virNodeDevCap, "ap_card", "ap_queue", "ap_matrix", + "vpd", ); VIR_ENUM_IMPL(virNodeDevNetCap, @@ -240,6 +242,80 @@ virNodeDeviceCapMdevTypesFormat(virBuffer *buf, } } +static void +virNodeDeviceCapVPDFormatCustomVendorField(virPCIVPDResourceCustom *field, virBuffer *buf) +{ + if (field == NULL || field->value == NULL) { + return; + } + virBufferAsprintf(buf, "<vendor_field index='%c'>%s</vendor_field>\n", field->idx, + field->value); +} + +static void +virNodeDeviceCapVPDFormatCustomSystemField(virPCIVPDResourceCustom *field, virBuffer *buf) +{ + if (field == NULL || field->value == NULL) { + return; + } + virBufferAsprintf(buf, "<system_field index='%c'>%s</system_field>\n", field->idx, + field->value); +} + +static inline void +virNodeDeviceCapVPDFormatRegularField(virBuffer *buf, const char *keyword, const char *value) +{ + if (keyword == NULL || value == NULL) { + return; + } + virBufferAsprintf(buf, "<%s>%s</%s>\n", keyword, value, keyword); +} + +static void +virNodeDeviceCapVPDFormat(virBuffer *buf, virPCIVPDResource *res) +{ + if (res == NULL) { + return; + } + + virBufferAddLit(buf, "<capability type='vpd'>\n"); + virBufferAdjustIndent(buf, 2); + if (res->name != NULL) { + virBufferEscapeString(buf, "<name>%s</name>\n", res->name); + } + + if (res->ro != NULL) { + virBufferEscapeString(buf, "<fields access='%s'>\n", "readonly"); + + virBufferAdjustIndent(buf, 2); + virNodeDeviceCapVPDFormatRegularField(buf, "change_level", res->ro->change_level); + virNodeDeviceCapVPDFormatRegularField(buf, "manufacture_id", res->ro->manufacture_id); + virNodeDeviceCapVPDFormatRegularField(buf, "part_number", res->ro->part_number); + virNodeDeviceCapVPDFormatRegularField(buf, "serial_number", res->ro->serial_number); + g_ptr_array_foreach(res->ro->vendor_specific, + (GFunc)virNodeDeviceCapVPDFormatCustomVendorField, buf); + virBufferAdjustIndent(buf, -2); + + virBufferAddLit(buf, "</fields>\n"); + } + + if (res->rw != NULL) { + virBufferEscapeString(buf, "<fields access='%s'>\n", "readwrite"); + + virBufferAdjustIndent(buf, 2); + virNodeDeviceCapVPDFormatRegularField(buf, "asset_tag", res->rw->asset_tag); + g_ptr_array_foreach(res->rw->vendor_specific, + (GFunc)virNodeDeviceCapVPDFormatCustomVendorField, buf); + g_ptr_array_foreach(res->rw->system_specific, + (GFunc)virNodeDeviceCapVPDFormatCustomSystemField, buf); + virBufferAdjustIndent(buf, -2); + + virBufferAddLit(buf, "</fields>\n"); + } + + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</capability>\n"); +} static void virNodeDeviceCapPCIDefFormat(virBuffer *buf, @@ -315,6 +391,9 @@ virNodeDeviceCapPCIDefFormat(virBuffer *buf, data->pci_dev.mdev_types, data->pci_dev.nmdev_types); } + if (data->pci_dev.flags & VIR_NODE_DEV_CAP_FLAG_PCI_VPD) { + virNodeDeviceCapVPDFormat(buf, data->pci_dev.vpd); + } if (data->pci_dev.nIommuGroupDevices) { virBufferAsprintf(buf, "<iommuGroup number='%d'>\n", data->pci_dev.iommuGroupNumber); @@ -673,6 +752,7 @@ virNodeDeviceDefFormat(const virNodeDeviceDef *def) case VIR_NODE_DEV_CAP_MDEV_TYPES: case VIR_NODE_DEV_CAP_FC_HOST: case VIR_NODE_DEV_CAP_VPORTS: + case VIR_NODE_DEV_CAP_VPD: case VIR_NODE_DEV_CAP_LAST: break; } @@ -859,6 +939,165 @@ virNodeDevCapMdevTypesParseXML(xmlXPathContextPtr ctxt, return ret; } +static int +virNodeDeviceCapVPDParseCustomFields(xmlXPathContextPtr ctxt, virPCIVPDResource *res, bool readOnly) +{ + int nfields = -1; + g_autofree char *index = NULL; + g_autofree char* value = NULL; + g_autofree xmlNodePtr *nodes = NULL; + xmlNodePtr orignode = NULL; + size_t i = 0; + + orignode = ctxt->node; + if ((nfields = virXPathNodeSet("./vendor_field[@index]", ctxt, &nodes)) < 0) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("failed to evaluate <vendor_field> elements")); + ctxt->node = orignode; + return -1; + } + for (i = 0; i < nfields; i++) { + ctxt->node = nodes[i]; + if (!(index = virXPathStringLimit("string(./@index[1])", 2, ctxt))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("<vendor_field> evaluation has failed")); + continue; + } + if (!(value = virXPathString("string(./text())", ctxt))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("<vendor_field> value evaluation has failed")); + continue; + } + virPCIVPDResourceUpdateKeyword(res, readOnly, g_strdup_printf("V%c", index[0]), value); + } + ctxt->node = orignode; + + if (!readOnly) { + if ((nfields = virXPathNodeSet("./system_field[@index]", ctxt, &nodes)) < 0) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("failed to evaluate <system_field> elements")); + ctxt->node = orignode; + return -1; + } + for (i = 0; i < nfields; i++) { + ctxt->node = nodes[i]; + if (!(index = virXPathStringLimit("string(./@index[1])", 2, ctxt))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("<system_field> evaluation has failed")); + continue; + } + if (!(value = virXPathString("string(./text())", ctxt))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("<system_field> value evaluation has failed")); + continue; + } + virPCIVPDResourceUpdateKeyword(res, readOnly, g_strdup_printf("Y%c", index[0]), value); + } + ctxt->node = orignode; + } + + return 0; +} + +static int +virNodeDeviceCapVPDParseReadOnlyFields(xmlXPathContextPtr ctxt, virPCIVPDResource *res) +{ + const char *keywords[] = {"change_level", "manufacture_id", + "serial_number", "part_number", NULL}; + g_autofree char *expression = NULL; + size_t i = 0; + + if (res == NULL) { + return -1; + } + res->ro = virPCIVPDResourceRONew(); + + while (keywords[i]) { + expression = g_strdup_printf("string(./%s)", keywords[i]); + virPCIVPDResourceUpdateKeyword(res, true, keywords[i], virXPathString(expression, ctxt)); + g_free(g_steal_pointer(&expression)); + ++i; + } + if (virNodeDeviceCapVPDParseCustomFields(ctxt, res, true) < 0) { + return -1; + } + return 0; +} + +static int +virNodeDeviceCapVPDParseReadWriteFields(xmlXPathContextPtr ctxt, virPCIVPDResource *res) +{ + res->rw = virPCIVPDResourceRWNew(); + virPCIVPDResourceUpdateKeyword(res, false, "asset_tag", virXPathString("string(./asset_tag)", ctxt)); + if (virNodeDeviceCapVPDParseCustomFields(ctxt, res, false) < 0) { + return -1; + } + return 0; +} + +static int +virNodeDeviceCapVPDParseXML(xmlXPathContextPtr ctxt, virPCIVPDResource **res) +{ + xmlNodePtr orignode = NULL; + g_autofree xmlNodePtr *nodes = NULL; + int nfields = -1; + g_autofree char *access = NULL; + size_t i = 0; + g_autoptr(virPCIVPDResource) newres = g_new0(virPCIVPDResource, 1); + + if (res == NULL) { + return -1; + } + + orignode = ctxt->node; + + if (!(newres->name = virXPathString("string(./name)", ctxt))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Could not read a device name from the <name> element")); + ctxt->node = orignode; + return -1; + } + + if ((nfields = virXPathNodeSet("./fields[@access]", ctxt, &nodes)) < 0) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("no VPD <fields> elements with an access type attribute found")); + ctxt->node = orignode; + return -1; + } + + for (i = 0; i < nfields; i++) { + ctxt->node = nodes[i]; + if (!(access = virXPathString("string(./@access[1])", ctxt))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("VPD fields access type parsing has failed")); + ctxt->node = orignode; + return -1; + } + + if (STREQ(access, "readonly")) { + if (virNodeDeviceCapVPDParseReadOnlyFields(ctxt, newres) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Could not parse %s VPD resource fields"), access); + } + } else if (STREQ(access, "readwrite")) { + if (virNodeDeviceCapVPDParseReadWriteFields(ctxt, newres) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Could not parse %s VPD resource fields"), access); + } + } else { + virReportError(VIR_ERR_XML_ERROR, _("Unsupported VPD field access type specified %s"), + access); + } + } + ctxt->node = orignode; + + /* Replace the existing VPD representation if there is one already. */ + if (*res != NULL) { + virPCIVPDResourceFree(*res); + } + *res = g_steal_pointer(&newres); + return 0; +} static int virNodeDevAPMatrixCapabilityParseXML(xmlXPathContextPtr ctxt, @@ -1718,6 +1957,11 @@ virNodeDevPCICapabilityParseXML(xmlXPathContextPtr ctxt, &pci_dev->nmdev_types) < 0) return -1; pci_dev->flags |= VIR_NODE_DEV_CAP_FLAG_PCI_MDEV; + } else if (STREQ(type, "vpd")) { + if (virNodeDeviceCapVPDParseXML(ctxt, &pci_dev->vpd) < 0) { + return -1; + } + pci_dev->flags |= VIR_NODE_DEV_CAP_FLAG_PCI_VPD; } else { int hdrType = virPCIHeaderTypeFromString(type); @@ -2024,6 +2268,7 @@ virNodeDevCapsDefParseXML(xmlXPathContextPtr ctxt, case VIR_NODE_DEV_CAP_VPORTS: case VIR_NODE_DEV_CAP_SCSI_GENERIC: case VIR_NODE_DEV_CAP_VDPA: + case VIR_NODE_DEV_CAP_VPD: case VIR_NODE_DEV_CAP_LAST: virReportError(VIR_ERR_INTERNAL_ERROR, _("unknown capability type '%d' for '%s'"), @@ -2287,6 +2532,7 @@ virNodeDevCapsDefFree(virNodeDevCapsDef *caps) for (i = 0; i < data->pci_dev.nmdev_types; i++) virMediatedDeviceTypeFree(data->pci_dev.mdev_types[i]); g_free(data->pci_dev.mdev_types); + virPCIVPDResourceFree(g_steal_pointer(&data->pci_dev.vpd)); break; case VIR_NODE_DEV_CAP_USB_DEV: g_free(data->usb_dev.product_name); @@ -2352,6 +2598,7 @@ virNodeDevCapsDefFree(virNodeDevCapsDef *caps) 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_LAST: /* This case is here to shutup the compiler */ break; @@ -2418,6 +2665,7 @@ virNodeDeviceUpdateCaps(virNodeDeviceDef *def) 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_LAST: break; } @@ -2489,6 +2737,10 @@ virNodeDeviceCapsListExport(virNodeDeviceDef *def, MAYBE_ADD_CAP(VIR_NODE_DEV_CAP_MDEV_TYPES); ncaps++; } + if (flags & VIR_NODE_DEV_CAP_FLAG_PCI_VPD) { + MAYBE_ADD_CAP(VIR_NODE_DEV_CAP_VPD); + ncaps++; + } } if (caps->data.type == VIR_NODE_DEV_CAP_CSS_DEV) { @@ -2749,6 +3001,44 @@ virNodeDeviceGetMdevTypesCaps(const char *sysfspath, } +/** + * virNodeDeviceGetPCIVPDDynamicCap: + * @devCapPCIDev: a virNodeDevCapPCIDev for which to add VPD resources. + * + * While VPD has a read-only portion, there may be a read-write portion per + * the specs which may change dynamically. + * + * Returns: 0 if the operation was successful (even if VPD is not present for + * that device since it is optional in the specs, -1 otherwise. + */ +static int +virNodeDeviceGetPCIVPDDynamicCap(virNodeDevCapPCIDev *devCapPCIDev) +{ + g_autoptr(virPCIDevice) pciDev = NULL; + virPCIDeviceAddress devAddr; + g_autoptr(virPCIVPDResource) res = NULL; + + devAddr.domain = devCapPCIDev->domain; + devAddr.bus = devCapPCIDev->bus; + devAddr.slot = devCapPCIDev->slot; + devAddr.function = devCapPCIDev->function; + + if (!(pciDev = virPCIDeviceNew(&devAddr))) + return -1; + + if (virPCIDeviceHasVPD(pciDev)) { + /* VPD is optional in PCI(e) specs. If it is there, attempt to add it. */ + if ((res = virPCIDeviceGetVPD(pciDev))) { + devCapPCIDev->flags |= VIR_NODE_DEV_CAP_FLAG_PCI_VPD; + devCapPCIDev->vpd = g_steal_pointer(&res); + } else { + virPCIVPDResourceFree(g_steal_pointer(&devCapPCIDev->vpd)); + } + } + return 0; +} + + /* virNodeDeviceGetPCIDynamicCaps() 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 @@ -2771,6 +3061,9 @@ virNodeDeviceGetPCIDynamicCaps(const char *sysfsPath, if (pci_dev->nmdev_types > 0) pci_dev->flags |= VIR_NODE_DEV_CAP_FLAG_PCI_MDEV; + if (virNodeDeviceGetPCIVPDDynamicCap(pci_dev) < 0) + return -1; + return 0; } diff --git a/src/conf/node_device_conf.h b/src/conf/node_device_conf.h index 5a4d9c7a55..e4d1f67d53 100644 --- a/src/conf/node_device_conf.h +++ b/src/conf/node_device_conf.h @@ -24,6 +24,7 @@ #include "internal.h" #include "virbitmap.h" +#include "virpcivpd.h" #include "virscsihost.h" #include "virpci.h" #include "virvhba.h" @@ -69,6 +70,7 @@ typedef enum { VIR_NODE_DEV_CAP_AP_CARD, /* s390 AP Card device */ 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_LAST } virNodeDevCapType; @@ -103,6 +105,7 @@ typedef enum { VIR_NODE_DEV_CAP_FLAG_PCI_VIRTUAL_FUNCTION = (1 << 1), VIR_NODE_DEV_CAP_FLAG_PCIE = (1 << 2), VIR_NODE_DEV_CAP_FLAG_PCI_MDEV = (1 << 3), + VIR_NODE_DEV_CAP_FLAG_PCI_VPD = (1 << 4), } virNodeDevPCICapFlags; typedef enum { @@ -181,6 +184,7 @@ struct _virNodeDevCapPCIDev { int hdrType; /* enum virPCIHeaderType or -1 */ virMediatedDeviceType **mdev_types; size_t nmdev_types; + virPCIVPDResource *vpd; }; typedef struct _virNodeDevCapUSBDev virNodeDevCapUSBDev; @@ -418,7 +422,8 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(virNodeDevCapsDef, virNodeDevCapsDefFree); VIR_CONNECT_LIST_NODE_DEVICES_CAP_VDPA | \ 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_AP_MATRIX | \ + VIR_CONNECT_LIST_NODE_DEVICES_CAP_VPD) #define VIR_CONNECT_LIST_NODE_DEVICES_FILTERS_ACTIVE \ VIR_CONNECT_LIST_NODE_DEVICES_ACTIVE | \ diff --git a/src/conf/virnodedeviceobj.c b/src/conf/virnodedeviceobj.c index 9a9841576a..165ec1f1dd 100644 --- a/src/conf/virnodedeviceobj.c +++ b/src/conf/virnodedeviceobj.c @@ -701,6 +701,9 @@ virNodeDeviceObjHasCap(const virNodeDeviceObj *obj, if (type == VIR_NODE_DEV_CAP_MDEV_TYPES && (cap->data.pci_dev.flags & VIR_NODE_DEV_CAP_FLAG_PCI_MDEV)) return true; + if (type == VIR_NODE_DEV_CAP_VPD && + (cap->data.pci_dev.flags & VIR_NODE_DEV_CAP_FLAG_PCI_VPD)) + return true; break; case VIR_NODE_DEV_CAP_SCSI_HOST: @@ -742,6 +745,7 @@ virNodeDeviceObjHasCap(const virNodeDeviceObj *obj, 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_LAST: break; } @@ -899,7 +903,8 @@ virNodeDeviceObjMatch(virNodeDeviceObj *obj, MATCH_CAP(VDPA) || MATCH_CAP(AP_CARD) || MATCH_CAP(AP_QUEUE) || - MATCH_CAP(AP_MATRIX))) + MATCH_CAP(AP_MATRIX) || + MATCH_CAP(VPD))) return false; } diff --git a/src/node_device/node_device_driver.c b/src/node_device/node_device_driver.c index 3bc6eb1c11..d19ed7d948 100644 --- a/src/node_device/node_device_driver.c +++ b/src/node_device/node_device_driver.c @@ -708,6 +708,7 @@ nodeDeviceObjFormatAddress(virNodeDeviceObj *obj) 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_LAST: break; } @@ -1983,6 +1984,7 @@ int nodeDeviceDefValidate(virNodeDeviceDef *def, case VIR_NODE_DEV_CAP_AP_CARD: 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_LAST: break; } diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_device_udev.c index 71f0bef827..7c3bb762b3 100644 --- a/src/node_device/node_device_udev.c +++ b/src/node_device/node_device_udev.c @@ -42,6 +42,7 @@ #include "virnetdev.h" #include "virmdev.h" #include "virutil.h" +#include "virpcivpd.h" #include "configmake.h" @@ -1397,6 +1398,7 @@ udevGetDeviceDetails(struct udev_device *device, case VIR_NODE_DEV_CAP_AP_MATRIX: return udevProcessAPMatrix(device, def); case VIR_NODE_DEV_CAP_MDEV_TYPES: + case VIR_NODE_DEV_CAP_VPD: case VIR_NODE_DEV_CAP_SYSTEM: case VIR_NODE_DEV_CAP_FC_HOST: case VIR_NODE_DEV_CAP_VPORTS: diff --git a/tests/nodedevschemadata/pci_0000_42_00_0_vpd.xml b/tests/nodedevschemadata/pci_0000_42_00_0_vpd.xml new file mode 100644 index 0000000000..8b56e4f6b4 --- /dev/null +++ b/tests/nodedevschemadata/pci_0000_42_00_0_vpd.xml @@ -0,0 +1,42 @@ +<device> + <name>pci_0000_42_00_0</name> + <capability type='pci'> + <class>0x020000</class> + <domain>0</domain> + <bus>66</bus> + <slot>0</slot> + <function>0</function> + <product id='0xa2d6'>MT42822 BlueField-2 integrated ConnectX-6 Dx network controller</product> + <vendor id='0x15b3'>Mellanox Technologies</vendor> + <capability type='virt_functions' maxCount='16'/> + <capability type='vpd'> + <name>BlueField-2 DPU 25GbE Dual-Port SFP56, Crypto Enabled, 16GB on-board DDR, 1GbE OOB management, Tall Bracket</name> + <fields access='readonly'> + <change_level>B1</change_level> + <manufacture_id>foobar</manufacture_id> + <part_number>MBF2H332A-AEEOT</part_number> + <serial_number>MT2113X00000</serial_number> + <vendor_field index='0'>PCIeGen4 x8</vendor_field> + <vendor_field index='2'>MBF2H332A-AEEOT</vendor_field> + <vendor_field index='3'>3c53d07eec484d8aab34dabd24fe575aa</vendor_field> + <vendor_field index='A'>MLX:MN=MLNX:CSKU=V2:UUID=V3:PCI=V0:MODL=BF2H332A</vendor_field> + </fields> + <fields access='readwrite'> + <asset_tag>fooasset</asset_tag> + <vendor_field index='0'>vendorfield0</vendor_field> + <vendor_field index='2'>vendorfield2</vendor_field> + <vendor_field index='A'>vendorfieldA</vendor_field> + <system_field index='B'>systemfieldB</system_field> + <system_field index='0'>systemfield0</system_field> + </fields> + </capability> + <iommuGroup number='65'> + <address domain='0x0000' bus='0x42' slot='0x00' function='0x0'/> + </iommuGroup> + <numa node='0'/> + <pci-express> + <link validity='cap' port='0' speed='16' width='8'/> + <link validity='sta' speed='8' width='8'/> + </pci-express> + </capability> +</device> diff --git a/tests/nodedevxml2xmlout/pci_0000_42_00_0_vpd.xml b/tests/nodedevxml2xmlout/pci_0000_42_00_0_vpd.xml new file mode 120000 index 0000000000..a0b5372ca0 --- /dev/null +++ b/tests/nodedevxml2xmlout/pci_0000_42_00_0_vpd.xml @@ -0,0 +1 @@ +../nodedevschemadata/pci_0000_42_00_0_vpd.xml \ No newline at end of file diff --git a/tests/nodedevxml2xmltest.c b/tests/nodedevxml2xmltest.c index 9e32e7d553..557347fb07 100644 --- a/tests/nodedevxml2xmltest.c +++ b/tests/nodedevxml2xmltest.c @@ -121,6 +121,7 @@ mymain(void) DO_TEST("pci_0000_02_10_7_sriov_pf_vfs_all_header_type"); DO_TEST("drm_renderD129"); DO_TEST("pci_0000_02_10_7_mdev_types"); + DO_TEST("pci_0000_42_00_0_vpd"); DO_TEST("mdev_3627463d_b7f0_4fea_b468_f1da537d301b"); DO_TEST("ccw_0_0_ffff"); DO_TEST("css_0_0_ffff"); diff --git a/tools/virsh-nodedev.c b/tools/virsh-nodedev.c index c989a77ad2..1ad8db7a3f 100644 --- a/tools/virsh-nodedev.c +++ b/tools/virsh-nodedev.c @@ -472,6 +472,9 @@ cmdNodeListDevices(vshControl *ctl, const vshCmd *cmd G_GNUC_UNUSED) case VIR_NODE_DEV_CAP_MDEV: flags |= VIR_CONNECT_LIST_NODE_DEVICES_CAP_MDEV; break; + case VIR_NODE_DEV_CAP_VPD: + flags |= VIR_CONNECT_LIST_NODE_DEVICES_CAP_VPD; + break; case VIR_NODE_DEV_CAP_CCW_DEV: flags |= VIR_CONNECT_LIST_NODE_DEVICES_CAP_CCW_DEV; break; -- 2.30.2