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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list