
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@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