---
src/libvirt_private.syms | 1 +
src/util/virutil.c | 150 +++++++++++++++++++++
src/util/virutil.h | 5 +
tests/sysfs/devices/pci0000:00/0000:00:1f.1/device | 1 +
tests/sysfs/devices/pci0000:00/0000:00:1f.1/vendor | 1 +
tests/sysfs/devices/pci0000:00/0000:00:1f.2/device | 1 +
tests/sysfs/devices/pci0000:00/0000:00:1f.2/vendor | 1 +
tests/sysfs/devices/pci0000:00/0000:00:1f.4/device | 1 +
tests/sysfs/devices/pci0000:00/0000:00:1f.4/vendor | 1 +
tests/utiltest.c | 41 +++++-
10 files changed, 201 insertions(+), 2 deletions(-)
create mode 100644 tests/sysfs/devices/pci0000:00/0000:00:1f.1/device
create mode 100644 tests/sysfs/devices/pci0000:00/0000:00:1f.1/vendor
create mode 100644 tests/sysfs/devices/pci0000:00/0000:00:1f.2/device
create mode 100644 tests/sysfs/devices/pci0000:00/0000:00:1f.2/vendor
create mode 100644 tests/sysfs/devices/pci0000:00/0000:00:1f.4/device
create mode 100644 tests/sysfs/devices/pci0000:00/0000:00:1f.4/vendor
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index fe182e8..f6ae42d 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1932,6 +1932,7 @@ virDoubleToStr;
virEnumFromString;
virEnumToString;
virFindFCHostCapableVport;
+virFindPCIDeviceByVPD;
virFormatIntDecimal;
virGetDeviceID;
virGetDeviceUnprivSGIO;
diff --git a/src/util/virutil.c b/src/util/virutil.c
index c1938f9..8309568 100644
--- a/src/util/virutil.c
+++ b/src/util/virutil.c
@@ -1996,6 +1996,147 @@ cleanup:
VIR_FREE(vports);
return ret;
}
+
+struct virFindPCIDeviceByVPDData {
+ const char *filename;
+ const char *value;
+};
+
+static int
+virFindPCIDeviceByVPDCallback(const char *fpath,
+ void *opaque)
+{
+ struct virFindPCIDeviceByVPDData *data = opaque;
+ char *p = NULL;
+ char *buf = NULL;
+ int ret = -1;
+
+ p = strrchr(fpath, '/');
+ p++;
+
+ if (STRNEQ(p, data->filename))
+ return -1;
+
+ if (virFileReadAll(fpath, 1024, &buf) < 0)
+ return -1;
+
+ if ((p = strchr(buf, '\n')))
+ *p = '\0';
+
+ if (STRNEQ(buf, data->value))
+ goto cleanup;
+
+ ret = 0;
+cleanup:
+ VIR_FREE(buf);
+ return ret;
+}
+
+# define SYSFS_DEVICES_PATH "/sys/devices"
+
+/**
+ * virFindPCIDeviceByVPD:
+ * @sysfs_prefix: The directory path where starts to traverse, defaults
+ * to SYSFS_DEVICES_PATH.
+ * @vendor: vendor ID in string
+ * @product: product ID in string
+ *
+ * Traverse specified directory tree (@sysfs_prefix) to find out the PCI
+ * device address (e.g. "0000\:00\:1f.2") by @vendor and @product.
+ *
+ * Return the PCI device address as string on success, or NULL on
+ * failure.
+ */
+char *
+virFindPCIDeviceByVPD(const char *sysfs_prefix,
+ const char *vendor,
+ const char *product)
+{
+ const char *prefix = sysfs_prefix ? sysfs_prefix : SYSFS_DEVICES_PATH;
+ char **vendor_paths = NULL;
+ int nvendor_paths = -1;
+ char **product_paths = NULL;
+ int nproduct_paths = -1;
+ unsigned int flags = 0;
+ char *ret = NULL;
+ bool found = false;
+ char *p1 = NULL;
+ char *p2 = NULL;
+ int i, j;
+
+ flags |= (VIR_TRAVERSE_DIRECTORY_IGNORE_HIDDEN_FILES |
+ VIR_TRAVERSE_DIRECTORY_FALL_THROUGH);
+
+ struct virFindPCIDeviceByVPDData vendor_data = {
+ .filename = "vendor",
+ .value = vendor,
+ };
+
+ struct virFindPCIDeviceByVPDData product_data = {
+ .filename = "device",
+ .value = product,
+ };
+
+ if ((nvendor_paths = virTraverseDirectory(prefix, 2, flags,
+ virFindPCIDeviceByVPDCallback,
+ NULL, &vendor_data,
+ &vendor_paths)) < 0 ||
+ (nproduct_paths = virTraverseDirectory(prefix, 2, flags,
+ virFindPCIDeviceByVPDCallback,
+ NULL, &product_data,
+ &product_paths)) < 0)
+ goto cleanup;
+
+ if (!nvendor_paths || !nproduct_paths) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unable to find PCI device with vendor '%s' "
+ "product '%s' in '%s'"), vendor, product, prefix);
+ goto cleanup;
+ }
+
+ for (i = 0; i < nvendor_paths; i++) {
+ p1 = strrchr(vendor_paths[i], '/');
+
+ for (j = 0; j < nproduct_paths; j++) {
+ p2 = strrchr(product_paths[j], '/');
+
+ if ((p1 - vendor_paths[i]) != (p2 - product_paths[j]))
+ continue;
+
+ if (STREQLEN(vendor_paths[i], product_paths[j],
+ p1 - vendor_paths[i])) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found)
+ break;
+ }
+
+ if (found) {
+ p1 = strrchr(vendor_paths[i], '/');
+ *p1 = '\0';
+ p2 = strrchr(vendor_paths[i], '/');
+
+ if (VIR_STRDUP(ret, p2 + 1) < 0)
+ goto cleanup;
+ }
+
+cleanup:
+ if (nvendor_paths > 0) {
+ for (i = 0; i < nvendor_paths; i++)
+ VIR_FREE(vendor_paths[i]);
+ VIR_FREE(vendor_paths);
+ }
+
+ if (nproduct_paths > 0) {
+ for (i = 0; i < nproduct_paths; i++)
+ VIR_FREE(product_paths[i]);
+ VIR_FREE(product_paths);
+ }
+ return ret;
+}
#else
int
virReadFCHost(const char *sysfs_prefix ATTRIBUTE_UNUSED,
@@ -2048,6 +2189,15 @@ virFindFCHostCapableVport(const char *sysfs_prefix ATTRIBUTE_UNUSED)
virReportSystemError(ENOSYS, "%s", _("Not supported on this platform"));
return NULL;
}
+
+int
+virFindPCIDeviceByVPD(const char *sysfs_prefix,
+ const char *vendor,
+ const char *product)
+{
+ virReportSystemError(ENOSYS, "%s", _("Not supported on this platform"));
+ return NULL;
+}
#endif /* __linux__ */
/**
diff --git a/src/util/virutil.h b/src/util/virutil.h
index 6c46f23..99d3ea2 100644
--- a/src/util/virutil.h
+++ b/src/util/virutil.h
@@ -218,4 +218,9 @@ int virTraverseDirectory(const char *dirpath,
char ***filepaths);
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3)
+char *virFindPCIDeviceByVPD(const char *sysfs_prefix,
+ const char *vendor,
+ const char *product)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);