Attached is a patch that exposes the relationships between physical and
virtual functions on SR IOV capable devices.
Dave
From cc5b72f99cd472aa0c07d8115e0abc970feab704 Mon Sep 17 00:00:00 2001
From: David Allan <dallan(a)redhat.com>
Date: Mon, 30 Nov 2009 15:58:47 -0500
Subject: [PATCH 1/2] Add SR IOV physical and virtual function relationships
---
src/conf/node_device_conf.c | 16 ++++
src/conf/node_device_conf.h | 3 +
src/node_device/node_device_driver.h | 10 +++
src/node_device/node_device_hal.c | 6 ++
src/node_device/node_device_linux_sysfs.c | 122 ++++++++++++++++++++++++++++-
5 files changed, 156 insertions(+), 1 deletions(-)
diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c
index 6003ab1..4b5d17c 100644
--- a/src/conf/node_device_conf.c
+++ b/src/conf/node_device_conf.c
@@ -246,6 +246,7 @@ char *virNodeDeviceDefFormat(virConnectPtr conn,
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
virNodeDevCapsDefPtr caps;
+ unsigned int i = 0;
char *tmp;
virBufferAddLit(&buf, "<device>\n");
@@ -318,6 +319,16 @@ char *virNodeDeviceDefFormat(virConnectPtr conn,
data->pci_dev.vendor_name);
else
virBufferAddLit(&buf, " />\n");
+ if (data->pci_dev.physical_function) {
+ virBufferEscapeString(&buf, "
<physical_function>%s</physical_function>\n",
+ data->pci_dev.physical_function);
+ }
+ if (data->pci_dev.num_virtual_functions > 0) {
+ for (i = 0 ; i < data->pci_dev.num_virtual_functions ; i++) {
+ virBufferEscapeString(&buf, "
<virtual_function>%s</virtual_function>\n",
+ data->pci_dev.virtual_functions[i]);
+ }
+ }
break;
case VIR_NODE_DEV_CAP_USB_DEV:
virBufferVSprintf(&buf, " <bus>%d</bus>\n",
data->usb_dev.bus);
@@ -1387,6 +1398,7 @@ out:
void virNodeDevCapsDefFree(virNodeDevCapsDefPtr caps)
{
+ int i = 0;
union _virNodeDevCapData *data = &caps->data;
switch (caps->type) {
@@ -1402,6 +1414,10 @@ void virNodeDevCapsDefFree(virNodeDevCapsDefPtr caps)
case VIR_NODE_DEV_CAP_PCI_DEV:
VIR_FREE(data->pci_dev.product_name);
VIR_FREE(data->pci_dev.vendor_name);
+ VIR_FREE(data->pci_dev.physical_function);
+ for (i = 0 ; i < data->pci_dev.num_virtual_functions ; i++) {
+ VIR_FREE(data->pci_dev.virtual_functions[i]);
+ }
break;
case VIR_NODE_DEV_CAP_USB_DEV:
VIR_FREE(data->usb_dev.product_name);
diff --git a/src/conf/node_device_conf.h b/src/conf/node_device_conf.h
index 7a20bd6..11b7539 100644
--- a/src/conf/node_device_conf.h
+++ b/src/conf/node_device_conf.h
@@ -105,6 +105,9 @@ struct _virNodeDevCapsDef {
unsigned class;
char *product_name;
char *vendor_name;
+ char *physical_function;
+ char **virtual_functions;
+ unsigned num_virtual_functions;
} pci_dev;
struct {
unsigned bus;
diff --git a/src/node_device/node_device_driver.h b/src/node_device/node_device_driver.h
index 4f0822c..d358276 100644
--- a/src/node_device/node_device_driver.h
+++ b/src/node_device/node_device_driver.h
@@ -61,6 +61,14 @@ int check_fc_host_linux(union _virNodeDevCapData *d);
#define check_vport_capable(d) check_vport_capable_linux(d)
int check_vport_capable_linux(union _virNodeDevCapData *d);
+#define get_physical_function(s,d) get_physical_function_linux(s,d)
+int get_physical_function_linux(const char *sysfs_path,
+ union _virNodeDevCapData *d);
+
+#define get_virtual_functions(s,d) get_virtual_functions_linux(s,d)
+int get_virtual_functions_linux(const char *sysfs_path,
+ union _virNodeDevCapData *d);
+
#define read_wwn(host, file, wwn) read_wwn_linux(host, file, wwn)
int read_wwn_linux(int host, const char *file, char **wwn);
@@ -68,6 +76,8 @@ int read_wwn_linux(int host, const char *file, char **wwn);
#define check_fc_host(d)
#define check_vport_capable(d)
+#define get_physical_function(sysfs_path, d)
+#define get_virtual_functions(sysfs_path, d)
#define read_wwn(host, file, wwn)
#endif /* __linux__ */
diff --git a/src/node_device/node_device_hal.c b/src/node_device/node_device_hal.c
index 31c1764..7cb931e 100644
--- a/src/node_device/node_device_hal.c
+++ b/src/node_device/node_device_hal.c
@@ -145,14 +145,20 @@ static int gather_pci_cap(LibHalContext *ctx, const char *udi,
(void)virStrToLong_ui(p+1, &p, 16, &d->pci_dev.slot);
(void)virStrToLong_ui(p+1, &p, 16, &d->pci_dev.function);
}
+
+ get_physical_function(sysfs_path, d);
+ get_virtual_functions(sysfs_path, d);
+
VIR_FREE(sysfs_path);
}
+
(void)get_int_prop(ctx, udi, "pci.vendor_id", (int
*)&d->pci_dev.vendor);
if (get_str_prop(ctx, udi, "pci.vendor", &d->pci_dev.vendor_name) !=
0)
(void)get_str_prop(ctx, udi, "info.vendor",
&d->pci_dev.vendor_name);
(void)get_int_prop(ctx, udi, "pci.product_id", (int
*)&d->pci_dev.product);
if (get_str_prop(ctx, udi, "pci.product", &d->pci_dev.product_name)
!= 0)
(void)get_str_prop(ctx, udi, "info.product",
&d->pci_dev.product_name);
+
return 0;
}
diff --git a/src/node_device/node_device_linux_sysfs.c
b/src/node_device/node_device_linux_sysfs.c
index b7cf782..6b9738c 100644
--- a/src/node_device/node_device_linux_sysfs.c
+++ b/src/node_device/node_device_linux_sysfs.c
@@ -29,6 +29,7 @@
#include "virterror_internal.h"
#include "memory.h"
#include "logging.h"
+#include <dirent.h>
#define VIR_FROM_THIS VIR_FROM_NODEDEV
@@ -70,7 +71,7 @@ int read_wwn_linux(int host, const char *file, char **wwn)
char buf[64];
if (open_wwn_file(LINUX_SYSFS_FC_HOST_PREFIX, host, file, &fd) < 0) {
- goto out;
+ goto out;
}
memset(buf, 0, sizeof(buf));
@@ -184,4 +185,123 @@ out:
return retval;
}
+
+static int get_sriov_function(const char *device_link,
+ char **pci_device)
+{
+ char *device_path = NULL, *device_name = NULL;
+ char errbuf[64];
+ int ret = -1;
+
+ VIR_DEBUG("Attempting to resolve device path from device link
'%s'\n",
+ device_link);
+
+ if (!virFileExists(device_link)) {
+
+ VIR_DEBUG("SR IOV function link '%s' does not exist\n",
device_link);
+ /* Not an SR IOV device, not an error, either. */
+ ret = 0;
+ goto out;
+
+ }
+
+ device_path = realpath(device_link, device_path);
+ if (device_path == NULL) {
+ memset(errbuf, '\0', sizeof(errbuf));
+ VIR_ERROR("Failed to resolve device link '%s': '%s'\n",
device_link,
+ strerror_r(errno, errbuf, sizeof(errbuf)));
+ goto out;
+ }
+
+ VIR_DEBUG("SR IOV device path is '%s'\n", device_path);
+ device_name = basename(device_path);
+ *pci_device = strdup(device_name);
+ if (*pci_device == NULL) {
+ VIR_ERROR0("Failed to allocate memory for PCI device name\n");
+ goto out;
+ }
+
+ VIR_DEBUG("SR IOV function is '%s'\n", *pci_device);
+ ret = 0;
+
+out:
+ VIR_FREE(device_path);
+ return ret;
+}
+
+
+int get_physical_function_linux(const char *sysfs_path,
+ union _virNodeDevCapData *d ATTRIBUTE_UNUSED)
+{
+ int ret = -1;
+ char *device_link = NULL;
+
+ VIR_DEBUG("Attempting to get SR IOV physical function for device "
+ "with sysfs path '%s'\n", sysfs_path);
+
+ if (virBuildPath(&device_link, sysfs_path, "physfn") == -1) {
+ virReportOOMError(NULL);
+ } else {
+ ret = get_sriov_function(device_link, &d->pci_dev.physical_function);
+ }
+
+ VIR_FREE(device_link);
+ return ret;
+}
+
+
+int get_virtual_functions_linux(const char *sysfs_path,
+ union _virNodeDevCapData *d)
+{
+ int ret = -1;
+ DIR *dir = NULL;
+ struct dirent *entry = NULL;
+ char *device_link = NULL;
+
+ VIR_DEBUG("Attempting to get SR IOV virtual functions for device"
+ "with sysfs path '%s'\n", sysfs_path);
+
+ dir = opendir(sysfs_path);
+ if (dir == NULL) {
+ goto out;
+ }
+
+ while ((entry = readdir(dir))) {
+ if (STRPREFIX(entry->d_name, "virtfn")) {
+ /* This local is just to avoid lines of code much > 80 col. */
+ unsigned int *num_funcs = &d->pci_dev.num_virtual_functions;
+
+ if (virBuildPath(&device_link, sysfs_path, entry->d_name) == -1) {
+ virReportOOMError(NULL);
+ goto out;
+ }
+
+ VIR_DEBUG("Number of virtual functions: %d\n", *num_funcs);
+ if (VIR_REALLOC_N(d->pci_dev.virtual_functions, (*num_funcs) + 1) != 0) {
+ virReportOOMError(NULL);
+ goto out;
+ }
+
+ if (get_sriov_function(device_link,
+ &d->pci_dev.virtual_functions[*num_funcs]) !=
0) {
+ VIR_ERROR("Failed to get SR IOV function from device link
'%s'\n",
+ device_link);
+ goto out;
+ } else {
+ (*num_funcs)++;
+ }
+
+ VIR_FREE(device_link);
+ }
+ }
+
+ closedir(dir);
+
+ ret = 0;
+
+out:
+ VIR_FREE(device_link);
+ return 0;
+}
+
#endif /* __linux__ */
--
1.6.5.2