On 06/07/2013 01:03 PM, Osier Yang wrote:
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.
Two patches ago we the commit message mentions 'udev' backend and 'hal'
backend styles - is this covering those cases? Are there "other" styles
that need to be considered? I suppose as long as the layout never
changes things will work... assuming this is the only layout that needs
to be considered.
---
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);
In the code if sysfs_prefix is NULL, then you use a constant path, so I
think you have to change these to 2 & 3 respectively
+
#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;
+
Since there's a VIR_FREE() on this below:
static char *sysfs_devices_prefix = NULL;
+#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;
Since we're reusing this:
VIR_FREE(addr);
Seems to be ACK-able with the nits I've noted fixed.
John
+
+ 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;
}