mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-01-18 10:35:20 +00:00
Read PCI class from sysfs class file instead of config space.
When determining if a device is behind a PCI bridge, the PCI device class is checked by reading the config space. However, there are some devices which have the wrong class on the config space, but the class is initialized by Linux correctly as a PCI BRIDGE. This class can be read by the sysfs file '/sys/bus/pci/devices/xxxx:xx:xx.x/class'. One example of such bridge is IBM PCI Bridge 1014:03b9, which is identified as a Host Bridge when reading the config space. Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo@linux.vnet.ibm.com>
This commit is contained in:
parent
a18b8aada6
commit
9a3d7a4778
@ -343,6 +343,37 @@ virPCIDeviceRead32(virPCIDevicePtr dev, int cfgfd, unsigned int pos)
|
||||
return (buf[0] << 0) | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
|
||||
}
|
||||
|
||||
static int
|
||||
virPCIDeviceReadClass(virPCIDevicePtr dev, uint16_t *device_class)
|
||||
{
|
||||
char *path = NULL;
|
||||
char *id_str = NULL;
|
||||
int ret = -1;
|
||||
unsigned int value;
|
||||
|
||||
if (virPCIFile(&path, dev->name, "class") < 0)
|
||||
return ret;
|
||||
|
||||
/* class string is '0xNNNNNN\n' ... i.e. 9 bytes */
|
||||
if (virFileReadAll(path, 9, &id_str) < 0)
|
||||
goto cleanup;
|
||||
|
||||
id_str[8] = '\0';
|
||||
if (virStrToLong_ui(id_str, NULL, 16, &value) < 0) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||
_("Unusual value in %s/devices/%s/class: %s"),
|
||||
PCI_SYSFS, dev->name, id_str);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
*device_class = (value >> 8) & 0xFFFF;
|
||||
ret = 0;
|
||||
cleanup:
|
||||
VIR_FREE(id_str);
|
||||
VIR_FREE(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
virPCIDeviceWrite(virPCIDevicePtr dev,
|
||||
int cfgfd,
|
||||
@ -645,8 +676,8 @@ virPCIDeviceIsParent(virPCIDevicePtr dev, virPCIDevicePtr check, void *data)
|
||||
return 0;
|
||||
|
||||
/* Is it a bridge? */
|
||||
device_class = virPCIDeviceRead16(check, fd, PCI_CLASS_DEVICE);
|
||||
if (device_class != PCI_CLASS_BRIDGE_PCI)
|
||||
ret = virPCIDeviceReadClass(check, &device_class);
|
||||
if (ret < 0 || device_class != PCI_CLASS_BRIDGE_PCI)
|
||||
goto cleanup;
|
||||
|
||||
/* Is it a plane? */
|
||||
@ -2110,6 +2141,7 @@ virPCIDeviceDownstreamLacksACS(virPCIDevicePtr dev)
|
||||
unsigned int pos;
|
||||
int fd;
|
||||
int ret = 0;
|
||||
uint16_t device_class;
|
||||
|
||||
if ((fd = virPCIDeviceConfigOpen(dev, true)) < 0)
|
||||
return -1;
|
||||
@ -2119,8 +2151,11 @@ virPCIDeviceDownstreamLacksACS(virPCIDevicePtr dev)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (virPCIDeviceReadClass(dev, &device_class) < 0)
|
||||
goto cleanup;
|
||||
|
||||
pos = dev->pcie_cap_pos;
|
||||
if (!pos || virPCIDeviceRead16(dev, fd, PCI_CLASS_DEVICE) != PCI_CLASS_BRIDGE_PCI)
|
||||
if (!pos || device_class != PCI_CLASS_BRIDGE_PCI)
|
||||
goto cleanup;
|
||||
|
||||
flags = virPCIDeviceRead16(dev, fd, pos + PCI_EXP_FLAGS);
|
||||
|
@ -29,6 +29,7 @@
|
||||
# include <fcntl.h>
|
||||
# include <sys/stat.h>
|
||||
# include <stdarg.h>
|
||||
# include <dirent.h>
|
||||
# include "viralloc.h"
|
||||
# include "virstring.h"
|
||||
# include "virfile.h"
|
||||
@ -42,6 +43,7 @@ static int (*real__xstat)(int ver, const char *path, struct stat *sb);
|
||||
static char *(*realcanonicalize_file_name)(const char *path);
|
||||
static int (*realopen)(const char *path, int flags, ...);
|
||||
static int (*realclose)(int fd);
|
||||
static DIR * (*realopendir)(const char *name);
|
||||
|
||||
/* Don't make static, since it causes problems with clang
|
||||
* when passed as an arg to virAsprintf()
|
||||
@ -112,6 +114,7 @@ struct pciDevice {
|
||||
char *id;
|
||||
int vendor;
|
||||
int device;
|
||||
int class;
|
||||
struct pciDriver *driver; /* Driver attached. NULL if attached to no driver */
|
||||
};
|
||||
|
||||
@ -351,6 +354,10 @@ pci_device_new_from_stub(const struct pciDevice *data)
|
||||
ABORT("@tmp overflow");
|
||||
make_file(devpath, "device", tmp, -1);
|
||||
|
||||
if (snprintf(tmp, sizeof(tmp), "0x%.4x", dev->class) < 0)
|
||||
ABORT("@tmp overflow");
|
||||
make_file(devpath, "class", tmp, -1);
|
||||
|
||||
if (pci_device_autobind(dev) < 0)
|
||||
ABORT("Unable to bind: %s", data->id);
|
||||
|
||||
@ -747,6 +754,7 @@ init_syms(void)
|
||||
LOAD_SYM(canonicalize_file_name);
|
||||
LOAD_SYM(open);
|
||||
LOAD_SYM(close);
|
||||
LOAD_SYM(opendir);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -776,6 +784,13 @@ init_env(void)
|
||||
MAKE_PCI_DEVICE("0000:00:01.0", 0x8086, 0x0044);
|
||||
MAKE_PCI_DEVICE("0000:00:02.0", 0x8086, 0x0046);
|
||||
MAKE_PCI_DEVICE("0000:00:03.0", 0x8086, 0x0048);
|
||||
MAKE_PCI_DEVICE("0001:00:00.0", 0x1014, 0x03b9, .class = 0x060400);
|
||||
MAKE_PCI_DEVICE("0001:01:00.0", 0x8086, 0x105e);
|
||||
MAKE_PCI_DEVICE("0001:01:00.1", 0x8086, 0x105e);
|
||||
MAKE_PCI_DEVICE("0005:80:00.0", 0x10b5, 0x8112, .class = 0x060400);
|
||||
MAKE_PCI_DEVICE("0005:90:01.0", 0x1033, 0x0035);
|
||||
MAKE_PCI_DEVICE("0005:90:01.1", 0x1033, 0x0035);
|
||||
MAKE_PCI_DEVICE("0005:90:01.2", 0x1033, 0x00e0);
|
||||
}
|
||||
|
||||
|
||||
@ -934,6 +949,24 @@ open(const char *path, int flags, ...)
|
||||
return ret;
|
||||
}
|
||||
|
||||
DIR *
|
||||
opendir(const char *path)
|
||||
{
|
||||
DIR *ret;
|
||||
char *newpath = NULL;
|
||||
|
||||
init_syms();
|
||||
|
||||
if (STRPREFIX(path, PCI_SYSFS_PREFIX) &&
|
||||
getrealpath(&newpath, path) < 0)
|
||||
return NULL;
|
||||
|
||||
ret = realopendir(newpath ? newpath : path);
|
||||
|
||||
VIR_FREE(newpath);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
close(int fd)
|
||||
{
|
||||
|
@ -183,6 +183,31 @@ cleanup:
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct testPCIDevData {
|
||||
unsigned int domain;
|
||||
unsigned int bus;
|
||||
unsigned int slot;
|
||||
unsigned int function;
|
||||
};
|
||||
|
||||
static int
|
||||
testVirPCIDeviceIsAssignable(const void *opaque)
|
||||
{
|
||||
const struct testPCIDevData *data = opaque;
|
||||
int ret = -1;
|
||||
virPCIDevicePtr dev;
|
||||
|
||||
if (!(dev = virPCIDeviceNew(data->domain, data->bus, data->slot, data->function)))
|
||||
goto cleanup;
|
||||
|
||||
if (virPCIDeviceIsAssignable(dev, true))
|
||||
ret = 0;
|
||||
|
||||
virPCIDeviceFree(dev);
|
||||
cleanup:
|
||||
return ret;
|
||||
}
|
||||
|
||||
# define FAKESYSFSDIRTEMPLATE abs_builddir "/fakesysfsdir-XXXXXX"
|
||||
|
||||
static int
|
||||
@ -209,10 +234,19 @@ mymain(void)
|
||||
ret = -1; \
|
||||
} while (0)
|
||||
|
||||
# define DO_TEST_PCI(fnc, domain, bus, slot, function) \
|
||||
do { \
|
||||
struct testPCIDevData data = { domain, bus, slot, function }; \
|
||||
if (virtTestRun(#fnc, fnc, &data) < 0) \
|
||||
ret = -1; \
|
||||
} while (0)
|
||||
|
||||
DO_TEST(testVirPCIDeviceNew);
|
||||
DO_TEST(testVirPCIDeviceDetach);
|
||||
DO_TEST(testVirPCIDeviceReset);
|
||||
DO_TEST(testVirPCIDeviceReattach);
|
||||
DO_TEST_PCI(testVirPCIDeviceIsAssignable, 5, 0x90, 1, 0);
|
||||
DO_TEST_PCI(testVirPCIDeviceIsAssignable, 1, 1, 0, 0);
|
||||
|
||||
if (getenv("LIBVIRT_SKIP_CLEANUP") == NULL)
|
||||
virFileDeleteTree(fakesysfsdir);
|
||||
|
BIN
tests/virpcitestdata/0001:00:00.0.config
Normal file
BIN
tests/virpcitestdata/0001:00:00.0.config
Normal file
Binary file not shown.
BIN
tests/virpcitestdata/0001:01:00.0.config
Normal file
BIN
tests/virpcitestdata/0001:01:00.0.config
Normal file
Binary file not shown.
BIN
tests/virpcitestdata/0001:01:00.1.config
Normal file
BIN
tests/virpcitestdata/0001:01:00.1.config
Normal file
Binary file not shown.
BIN
tests/virpcitestdata/0005:80:00.0.config
Normal file
BIN
tests/virpcitestdata/0005:80:00.0.config
Normal file
Binary file not shown.
BIN
tests/virpcitestdata/0005:90:01.0.config
Normal file
BIN
tests/virpcitestdata/0005:90:01.0.config
Normal file
Binary file not shown.
BIN
tests/virpcitestdata/0005:90:01.1.config
Normal file
BIN
tests/virpcitestdata/0005:90:01.1.config
Normal file
Binary file not shown.
BIN
tests/virpcitestdata/0005:90:01.2.config
Normal file
BIN
tests/virpcitestdata/0005:90:01.2.config
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user