mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-02-22 11:22:23 +00:00
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:
parent
921d2225ba
commit
a010165d27
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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__ */
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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__ */
|
||||
|
Loading…
x
Reference in New Issue
Block a user