* XML serialization and deserialization of PCI VPD resources;
* PCI VPD capability flags added and used in relevant places;
* XML to XML tests for the added capability.
---
docs/schemas/nodedev.rng | 40 +++
include/libvirt/libvirt-nodedev.h | 1 +
src/conf/node_device_conf.c | 271 ++++++++++++++++++
src/conf/node_device_conf.h | 6 +-
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 | 33 +++
.../pci_0000_42_00_0_vpd.xml | 1 +
tests/nodedevxml2xmltest.c | 1 +
tools/virsh-nodedev.c | 3 +
11 files changed, 365 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..fbbee4d0c6 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,42 @@
</element>
</define>
+ <define name="vpd">
+ <element name="capability">
+ <attribute name="type">
+ <value>vpd</value>
+ </attribute>
+ <oneOrMore>
+ <choice>
+ <element name="resource">
+ <attribute name="type">
+ <choice>
+ <value>string</value>
+ </choice>
+ </attribute>
+ <text/>
+ </element>
+ <element name="resource">
+ <attribute name="type">
+ <choice>
+ <value>vpd-r</value>
+ <value>vpd-w</value>
+ </choice>
+ </attribute>
+ <oneOrMore>
+ <element name="field">
+ <attribute name="keyword">
+ <data type="string"/>
+ </attribute>
+ <text/>
+ </element>
+ </oneOrMore>
+ </element>
+ </choice>
+ </oneOrMore>
+ </element>
+ </define>
+
<define name="apDomainRange">
<choice>
<data type="string">
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..7d9f74f45b 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,90 @@ virNodeDeviceCapMdevTypesFormat(virBuffer *buf,
}
}
+static void
+virNodeDeviceCapVPDStringResourceFormat(virBuffer *buf, virPCIVPDResource *res)
+{
+ virBufferAdjustIndent(buf, 2);
+ virBufferEscapeString(buf, "%s",
virPCIVPDStringResourceGetValue((virPCIVPDStringResource *)res));
+ virBufferAdjustIndent(buf, -2);
+}
+
+static gboolean
+virNodeDeviceCapVPDWriteTextField(gpointer key, gpointer val, gpointer userData)
+{
+ const gchar *k = (const gchar*)key, *v = (const gchar*)val;
+ virBuffer *buf = userData;
+ virPCIVPDResourceFieldValueFormat format =
VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_LAST;
+
+ format = virPCIVPDResourceGetFieldValueFormat(k);
+
+ if (format == VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_TEXT) {
+ virBufferEscapeString(buf, "<field keyword='%s'>", k);
+ virBufferEscapeString(buf, "%s", v);
+ virBufferEscapeString(buf, "</field>\n", k);
+ }
+ return false;
+}
+
+static void
+virNodeDeviceCapVPDKeywordResourceFormat(virBuffer *buf, virPCIVPDResource *res)
+{
+ virPCIVPDKeywordResource *keywordRes = NULL;
+ g_autofree GHashTableIter *iter = NULL;
+
+ virBufferAddLit(buf, "\n");
+ virBufferAdjustIndent(buf, 2);
+ keywordRes = (virPCIVPDKeywordResource*)res;
+
+ virPCIVPDKeywordResourceForEach(keywordRes, virNodeDeviceCapVPDWriteTextField, buf);
+ virBufferAdjustIndent(buf, -2);
+}
+
+static void
+virNodeDeviceCapVPDResourceFormat(virBuffer *buf, virPCIVPDResource *res)
+{
+ GEnumValue *resRype = NULL;
+ void (*resFormatFunc)(virBuffer *buf, virPCIVPDResource *res);
+
+ if (G_TYPE_CHECK_INSTANCE_TYPE(res, VIR_TYPE_PCI_VPD_STRING_RESOURCE)) {
+ resFormatFunc = virNodeDeviceCapVPDStringResourceFormat;
+ } else if (G_TYPE_CHECK_INSTANCE_TYPE(res, VIR_TYPE_PCI_VPD_KEYWORD_RESOURCE)) {
+ resFormatFunc = virNodeDeviceCapVPDKeywordResourceFormat;
+ } else {
+ /* Unexpected resource type. This should not happen unless the PCI(e) specs
+ * change and new resource types are introduced into util.virpcivpd. Either way,
+ * we can only return the control back to the caller here.
+ */
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Unexpected VPD resource type encountered during
formatting"));
+ return;
+ }
+ virBufferAdjustIndent(buf, 2);
+
+ resRype = virPCIVPDResourceGetResourceType(res);
+ virBufferEscapeString(buf, "<resource type='%s'>",
resRype->value_nick);
+ /* Format the resource in a type-specific way. */
+ resFormatFunc(buf, res);
+ virBufferAddLit(buf, "</resource>\n");
+
+ virBufferAdjustIndent(buf, -2);
+}
+
+static void
+virNodeDeviceCapVPDFormat(virBuffer *buf, GList *vpdResources)
+{
+ if (!g_list_length(vpdResources)) {
+ return;
+ }
+
+ virBufferAddLit(buf, "<capability type='vpd'>\n");
+ while (vpdResources) {
+ GList *next = vpdResources->next;
+ virNodeDeviceCapVPDResourceFormat(buf, vpdResources->data);
+ vpdResources = next;
+ }
+ virBufferAddLit(buf, "</capability>\n");
+}
static void
virNodeDeviceCapPCIDefFormat(virBuffer *buf,
@@ -315,6 +401,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_resources);
+ }
if (data->pci_dev.nIommuGroupDevices) {
virBufferAsprintf(buf, "<iommuGroup number='%d'>\n",
data->pci_dev.iommuGroupNumber);
@@ -673,6 +762,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 +949,132 @@ virNodeDevCapMdevTypesParseXML(xmlXPathContextPtr ctxt,
return ret;
}
+static GTree *
+virNodeDeviceCapVPDParseXMLKeywordResource(xmlXPathContextPtr ctxt)
+{
+ int nfields = -1;
+ g_autofree gchar* fieldValue = NULL;
+ g_autofree gchar* fieldKeyword = NULL;
+ g_autoptr(GTree) resourceFields = NULL;
+ g_autofree xmlNodePtr *nodes = NULL;
+ xmlNodePtr orignode = NULL;
+ size_t i = 0;
+
+ resourceFields = g_tree_new_full(
+ (GCompareDataFunc)g_strcmp0, NULL, g_free, g_free);
+
+ if ((nfields = virXPathNodeSet("./field[@keyword]", ctxt, &nodes)) <
0) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("no VPD <field> elements with a keyword specified
found"));
+ ctxt->node = orignode;
+ return NULL;
+ }
+
+ orignode = ctxt->node;
+ for (i = 0; i < nfields; i++) {
+ ctxt->node = nodes[i];
+ if (!(fieldKeyword = virXPathString("string(./@keyword[1])", ctxt))) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("VPD resource field keyword parsing has failed"));
+ continue;
+ }
+ if (!(fieldValue = virXPathString("string(./text())", ctxt))) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("VPD resource field keyword parsing has failed"));
+ continue;
+ }
+ g_tree_insert(resourceFields, g_steal_pointer(&fieldKeyword),
+ g_steal_pointer(&fieldValue));
+ }
+ ctxt->node = orignode;
+ return g_steal_pointer(&resourceFields);
+}
+
+static int
+virNodeDeviceCapVPDParseXML(xmlXPathContextPtr ctxt, GList **vpdResources)
+{
+ xmlNodePtr orignode = NULL;
+ g_autofree gchar* resText = NULL;
+ g_autofree GTree *resourceFields = NULL;
+ g_autofree xmlNodePtr *nodes = NULL;
+ int nresources = -1;
+ g_autofree gchar* resTypeStr = NULL;
+ virPCIVPDResourceType type = VIR_PCI_VPD_RESOURCE_TYPE_LAST;
+ GEnumClass *class;
+ size_t i = 0;
+
+ if ((nresources = virXPathNodeSet("./resource[@type]", ctxt, &nodes))
< 0) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("no VPD <resource> elements with a type specified
found"));
+ return -1;
+ }
+
+ orignode = ctxt->node;
+ for (i = 0; i < nresources; i++) {
+ ctxt->node = nodes[i];
+ if (!(resTypeStr = virXPathString("string(./@type[1])", ctxt))) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("VPD resource type parsing has failed"));
+ ctxt->node = orignode;
+ return -1;
+ }
+
+ class = g_type_class_ref(VIR_TYPE_PCI_VPD_RESOURCE_TYPE);
+ type = g_enum_get_value_by_nick(class, resTypeStr)->value;
+ g_type_class_unref(class);
+ if (!type) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Unexpected VPD resource type encountered"));
+ /* Skip resources with unknown types. */
+ continue;
+ }
+
+ /* Create in-memory representations of resources based on their type. If a
resource is not
+ * valid, report an error and skip to parsing another resource.
+ */
+ switch (type) {
+ case VIR_PCI_VPD_RESOURCE_TYPE_STRING:
+ if (!(resText = virXPathString("string(./text())", ctxt))) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Could not read a string resource text"));
+ continue;
+ }
+ *vpdResources = g_list_append(*vpdResources, virPCIVPDStringResourceNew(
+ g_steal_pointer(&resText)));
+ break;
+ case VIR_PCI_VPD_RESOURCE_TYPE_VPD_R:
+ if (!(resourceFields = virNodeDeviceCapVPDParseXMLKeywordResource(ctxt)))
{
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Could not parse %s VPD resource fields"),
resTypeStr);
+ continue;
+ }
+ *vpdResources = g_list_append(*vpdResources,
+ virPCIVPDKeywordResourceNew(g_steal_pointer(&resourceFields),
true));
+ break;
+ case VIR_PCI_VPD_RESOURCE_TYPE_VPD_W:
+ if (!(resourceFields = virNodeDeviceCapVPDParseXMLKeywordResource(ctxt)))
{
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Could not parse %s VPD resource fields"),
resTypeStr);
+ continue;
+ }
+ *vpdResources = g_list_append(*vpdResources,
+ virPCIVPDKeywordResourceNew(g_steal_pointer(&resourceFields),
false));
+ break;
+ case VIR_PCI_VPD_RESOURCE_TYPE_LAST:
+ default:
+ /* If the VPD module is aware of a resource type and it has been
serialized into
+ * XML while the parser does not then this is a bug.
+ */
+ virReportError(VIR_ERR_XML_ERROR,
+ _("The XML parser has encountered an unsupported resource
type %s"),
+ resTypeStr);
+ ctxt->node = orignode;
+ return -1;
+ }
+ }
+ ctxt->node = orignode;
+ return 0;
+}
static int
virNodeDevAPMatrixCapabilityParseXML(xmlXPathContextPtr ctxt,
@@ -1718,6 +1934,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_resources) < 0) {
+ return -1;
+ }
+ pci_dev->flags |= VIR_NODE_DEV_CAP_FLAG_PCI_VPD;
} else {
int hdrType = virPCIHeaderTypeFromString(type);
@@ -2024,6 +2245,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 +2509,8 @@ 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);
+ g_list_free_full(g_steal_pointer(&data->pci_dev.vpd_resources),
+ g_object_unref);
break;
case VIR_NODE_DEV_CAP_USB_DEV:
g_free(data->usb_dev.product_name);
@@ -2352,6 +2576,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 +2643,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 +2715,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 +2979,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_autolist(virPCIVPDResource) resourceList = 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 ((resourceList = virPCIDeviceGetVPDResources(pciDev))) {
+ devCapPCIDev->flags |= VIR_NODE_DEV_CAP_FLAG_PCI_VPD;
+ devCapPCIDev->vpd_resources = g_steal_pointer(&resourceList);
+ } else {
+ g_list_free_full(devCapPCIDev->vpd_resources, g_object_unref);
+ }
+ }
+ 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 +3039,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..32e59fa52a 100644
--- a/src/conf/node_device_conf.h
+++ b/src/conf/node_device_conf.h
@@ -69,6 +69,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 +104,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 +183,7 @@ struct _virNodeDevCapPCIDev {
int hdrType; /* enum virPCIHeaderType or -1 */
virMediatedDeviceType **mdev_types;
size_t nmdev_types;
+ GList *vpd_resources;
};
typedef struct _virNodeDevCapUSBDev virNodeDevCapUSBDev;
@@ -418,7 +421,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..831b6feb24
--- /dev/null
+++ b/tests/nodedevschemadata/pci_0000_42_00_0_vpd.xml
@@ -0,0 +1,33 @@
+<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'>
+ <resource type='string'>BlueField-2 DPU 25GbE Dual-Port SFP56, Crypto
Enabled, 16GB on-board DDR, 1GbE OOB management, Tall Bracket</resource>
+ <resource type='vpd-r'>
+ <field keyword='EC'>B1</field>
+ <field keyword='PN'>MBF2H332A-AEEOT</field>
+ <field keyword='SN'>MT2113X00000</field>
+ <field keyword='V0'>PCIeGen4 x8</field>
+ <field keyword='V2'>MBF2H332A-AEEOT</field>
+ <field
keyword='V3'>3c53d07eec484d8aab34dabd24fe575aa</field>
+ <field
keyword='VA'>MLX:MN=MLNX:CSKU=V2:UUID=V3:PCI=V0:MODL=BF2H332A</field>
+ </resource>
+ </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 f1b4eb94bf..e2f299d809 100644
--- a/tools/virsh-nodedev.c
+++ b/tools/virsh-nodedev.c
@@ -480,6 +480,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