expose SR IOV physical/virtual function relationships

exposes the relationships between physical
and virtual functions on SR IOV capable devices.
This commit is contained in:
Dave Allan 2009-12-14 14:44:12 +01:00 committed by Daniel Veillard
parent 921d2225ba
commit a010165d27
5 changed files with 258 additions and 1 deletions

View File

@ -246,6 +246,7 @@ char *virNodeDeviceDefFormat(virConnectPtr conn,
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
virNodeDevCapsDefPtr caps;
unsigned int i = 0;
virBufferAddLit(&buf, "<device>\n");
virBufferEscapeString(&buf, " <name>%s</name>\n", def->name);
@ -317,6 +318,30 @@ char *virNodeDeviceDefFormat(virConnectPtr conn,
data->pci_dev.vendor_name);
else
virBufferAddLit(&buf, " />\n");
if (data->pci_dev.flags & VIR_NODE_DEV_CAP_FLAG_PCI_PHYSICAL_FUNCTION) {
virBufferAddLit(&buf, " <capability type='phys_function'>\n");
virBufferVSprintf(&buf,
" <address domain='0x%.4x' bus='0x%.2x' "
"slot='0x%.2x' function='0x%.1x'/>\n",
data->pci_dev.physical_function->domain,
data->pci_dev.physical_function->bus,
data->pci_dev.physical_function->slot,
data->pci_dev.physical_function->function);
virBufferAddLit(&buf, " </capability>\n");
}
if (data->pci_dev.flags & VIR_NODE_DEV_CAP_FLAG_PCI_VIRTUAL_FUNCTION) {
virBufferAddLit(&buf, " <capability type='virt_functions'>\n");
for (i = 0 ; i < data->pci_dev.num_virtual_functions ; i++) {
virBufferVSprintf(&buf,
" <address domain='0x%.4x' bus='0x%.2x' "
"slot='0x%.2x' function='0x%.1x'/>\n",
data->pci_dev.virtual_functions[i]->domain,
data->pci_dev.virtual_functions[i]->bus,
data->pci_dev.virtual_functions[i]->slot,
data->pci_dev.virtual_functions[i]->function);
}
virBufferAddLit(&buf, " </capability>\n");
}
break;
case VIR_NODE_DEV_CAP_USB_DEV:
virBufferVSprintf(&buf, " <bus>%d</bus>\n", data->usb_dev.bus);
@ -1385,6 +1410,7 @@ out:
void virNodeDevCapsDefFree(virNodeDevCapsDefPtr caps)
{
int i = 0;
union _virNodeDevCapData *data = &caps->data;
switch (caps->type) {
@ -1400,6 +1426,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);

View File

@ -76,6 +76,18 @@ enum virNodeDevScsiHostCapFlags {
VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS = (1 << 1),
};
enum virNodeDevPCICapFlags {
VIR_NODE_DEV_CAP_FLAG_PCI_PHYSICAL_FUNCTION = (1 << 0),
VIR_NODE_DEV_CAP_FLAG_PCI_VIRTUAL_FUNCTION = (1 << 1),
};
struct pci_config_address {
unsigned domain;
unsigned bus;
unsigned slot;
unsigned function;
};
typedef struct _virNodeDevCapsDef virNodeDevCapsDef;
typedef virNodeDevCapsDef *virNodeDevCapsDefPtr;
struct _virNodeDevCapsDef {
@ -105,6 +117,10 @@ struct _virNodeDevCapsDef {
unsigned class;
char *product_name;
char *vendor_name;
struct pci_config_address *physical_function;
struct pci_config_address **virtual_functions;
unsigned num_virtual_functions;
unsigned flags;
} pci_dev;
struct {
unsigned bus;

View File

@ -37,6 +37,10 @@
#define LINUX_SYSFS_VPORT_CREATE_POSTFIX "/vport_create"
#define LINUX_SYSFS_VPORT_DELETE_POSTFIX "/vport_delete"
#define SRIOV_FOUND 0
#define SRIOV_NOT_FOUND 1
#define SRIOV_ERROR -1
#define LINUX_NEW_DEVICE_WAIT_TIME 60
#ifdef HAVE_HAL
@ -61,6 +65,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 +80,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__ */

View File

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

View File

@ -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,194 @@ out:
return retval;
}
static int logStrToLong_ui(char const *s,
char **end_ptr,
int base,
unsigned int *result)
{
int ret = 0;
ret = virStrToLong_ui(s, end_ptr, base, result);
if (ret != 0) {
VIR_ERROR("Failed to convert '%s' to unsigned int", s);
} else {
VIR_DEBUG("Converted '%s' to unsigned int %u", s, *result);
}
return ret;
}
static int parse_pci_config_address(char *address, struct pci_config_address *bdf)
{
char *p = NULL;
int ret = -1;
if ((address == NULL) || (logStrToLong_ui(address, &p, 16,
&bdf->domain) == -1)) {
goto out;
}
if ((p == NULL) || (logStrToLong_ui(p+1, &p, 16,
&bdf->bus) == -1)) {
goto out;
}
if ((p == NULL) || (logStrToLong_ui(p+1, &p, 16,
&bdf->slot) == -1)) {
goto out;
}
if ((p == NULL) || (logStrToLong_ui(p+1, &p, 16,
&bdf->function) == -1)) {
goto out;
}
ret = 0;
out:
return ret;
}
static int get_sriov_function(const char *device_link,
struct pci_config_address **bdf)
{
char *device_path = NULL, *config_address = NULL;
char errbuf[64];
int ret = SRIOV_ERROR;
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 = SRIOV_NOT_FOUND;
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,
virStrerror(errno, errbuf, sizeof(errbuf)));
goto out;
}
VIR_DEBUG("SR IOV device path is '%s'\n", device_path);
config_address = basename(device_path);
if (VIR_ALLOC(*bdf) != 0) {
VIR_ERROR0("Failed to allocate memory for PCI device name\n");
goto out;
}
if (parse_pci_config_address(config_address, *bdf) != 0) {
VIR_ERROR("Failed to parse PCI config address '%s'\n", config_address);
goto out;
}
VIR_DEBUG("SR IOV function %.4x:%.2x:%.2x.%.1x/>\n",
(*bdf)->domain,
(*bdf)->bus,
(*bdf)->slot,
(*bdf)->function);
ret = SRIOV_FOUND;
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);
if (ret == SRIOV_FOUND) {
d->pci_dev.flags |= VIR_NODE_DEV_CAP_FLAG_PCI_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])
!= SRIOV_FOUND) {
/* We should not get back SRIOV_NOT_FOUND in this
* case, so if we do, it's an error. */
VIR_ERROR("Failed to get SR IOV function from device link '%s'",
device_link);
goto out;
} else {
(*num_funcs)++;
d->pci_dev.flags |= VIR_NODE_DEV_CAP_FLAG_PCI_VIRTUAL_FUNCTION;
}
VIR_FREE(device_link);
}
}
closedir(dir);
ret = 0;
out:
VIR_FREE(device_link);
return 0;
}
#endif /* __linux__ */