[libvirt] [PATCH 04/11] util: Add util to find PCI device address by its vendor and product IDs

Osier Yang jyang at redhat.com
Fri Jun 7 17:03:42 UTC 2013


As the first user of virTraverseDirectory, it falls through to the 2
depth from "/sys/devices", and returns the address of the PCI device
of which both "vendor" and "device" have the specified value.  See the
test for an example.
---
 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);
+
 #endif /* __VIR_UTIL_H__ */
diff --git a/tests/sysfs/devices/pci0000:00/0000:00:1f.1/device b/tests/sysfs/devices/pci0000:00/0000:00:1f.1/device
new file mode 100644
index 0000000..9f26c70
--- /dev/null
+++ b/tests/sysfs/devices/pci0000:00/0000:00:1f.1/device
@@ -0,0 +1 @@
+0x1e04
diff --git a/tests/sysfs/devices/pci0000:00/0000:00:1f.1/vendor b/tests/sysfs/devices/pci0000:00/0000:00:1f.1/vendor
new file mode 100644
index 0000000..aee5132
--- /dev/null
+++ b/tests/sysfs/devices/pci0000:00/0000:00:1f.1/vendor
@@ -0,0 +1 @@
+0x8087
diff --git a/tests/sysfs/devices/pci0000:00/0000:00:1f.2/device b/tests/sysfs/devices/pci0000:00/0000:00:1f.2/device
new file mode 100644
index 0000000..a0681c4
--- /dev/null
+++ b/tests/sysfs/devices/pci0000:00/0000:00:1f.2/device
@@ -0,0 +1 @@
+0x1e03
diff --git a/tests/sysfs/devices/pci0000:00/0000:00:1f.2/vendor b/tests/sysfs/devices/pci0000:00/0000:00:1f.2/vendor
new file mode 100644
index 0000000..ce6dc4d
--- /dev/null
+++ b/tests/sysfs/devices/pci0000:00/0000:00:1f.2/vendor
@@ -0,0 +1 @@
+0x8086
diff --git a/tests/sysfs/devices/pci0000:00/0000:00:1f.4/device b/tests/sysfs/devices/pci0000:00/0000:00:1f.4/device
new file mode 100644
index 0000000..3c7202c
--- /dev/null
+++ b/tests/sysfs/devices/pci0000:00/0000:00:1f.4/device
@@ -0,0 +1 @@
+0x1e08
diff --git a/tests/sysfs/devices/pci0000:00/0000:00:1f.4/vendor b/tests/sysfs/devices/pci0000:00/0000:00:1f.4/vendor
new file mode 100644
index 0000000..ce6dc4d
--- /dev/null
+++ b/tests/sysfs/devices/pci0000:00/0000:00:1f.4/vendor
@@ -0,0 +1 @@
+0x8086
diff --git a/tests/utiltest.c b/tests/utiltest.c
index 9d18652..8d3dbfa 100644
--- a/tests/utiltest.c
+++ b/tests/utiltest.c
@@ -9,7 +9,12 @@
 #include "viralloc.h"
 #include "testutils.h"
 #include "virutil.h"
+#include "virstring.h"
+#include "virfile.h"
 
+static char *sysfs_devices_prefix;
+
+#define TEST_SYSFS_DEVICES_PREFIX sysfs_devices_prefix
 
 static void
 testQuietError(void *userData ATTRIBUTE_UNUSED,
@@ -150,14 +155,43 @@ testParseVersionString(const void *data ATTRIBUTE_UNUSED)
     return 0;
 }
 
-
-
+static int
+testFindPCIDeviceByVPD(const void *data ATTRIBUTE_UNUSED)
+{
+    char *addr = NULL;
+    const char *expected_addr = "0000:00:1f.2";
+    const char *vendor = "0x8086";
+    const char *device = "0x1e03";
+    int ret = -1;
+
+    if (!(addr = virFindPCIDeviceByVPD(TEST_SYSFS_DEVICES_PREFIX,
+                                       vendor, device)))
+        return -1;
+
+    if (STRNEQ(addr, expected_addr))
+        goto cleanup;
+
+    if ((addr = virFindPCIDeviceByVPD(TEST_SYSFS_DEVICES_PREFIX,
+                                      "0x7076", "0x2434")))
+        goto cleanup;
+
+    ret = 0;
+cleanup:
+    VIR_FREE(addr);
+    return ret;
+}
 
 static int
 mymain(void)
 {
     int result = 0;
 
+    if (virAsprintf(&sysfs_devices_prefix, "%s/%s", abs_srcdir,
+                    "sysfs/devices/") < 0) {
+        result = -1;
+        goto cleanup;
+    }
+
     virSetErrorFunc(NULL, testQuietError);
 
 #define DO_TEST(_name)                                                  \
@@ -171,7 +205,10 @@ mymain(void)
     DO_TEST(IndexToDiskName);
     DO_TEST(DiskNameToIndex);
     DO_TEST(ParseVersionString);
+    DO_TEST(FindPCIDeviceByVPD);
 
+cleanup:
+    VIR_FREE(sysfs_devices_prefix);
     return result == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
 }
 
-- 
1.8.1.4




More information about the libvir-list mailing list