From d77ffb6876e87a5c6f4c74c49cf0d89ade4f8326 Mon Sep 17 00:00:00 2001 From: Martin Kletzander Date: Tue, 15 Mar 2016 12:22:03 +0100 Subject: [PATCH] nodedev: Expose PCI header type If we expose this information, which is one byte in every PCI config file, we let all mgmt apps know whether the device itself is an endpoint or not so it's easier for them to decide whether such device can be passed through into a VM (endpoint) or not (*-bridge). Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1317531 Signed-off-by: Martin Kletzander --- docs/formatnode.html.in | 53 +++++++++++-------- docs/schemas/nodedev.rng | 11 ++++ src/conf/node_device_conf.c | 20 +++++++ src/conf/node_device_conf.h | 1 + src/libvirt_private.syms | 3 ++ src/node_device/node_device_udev.c | 3 ++ src/util/virpci.c | 33 ++++++++++++ src/util/virpci.h | 12 +++++ .../pci_0000_00_02_0_header_type.xml | 15 ++++++ .../pci_0000_00_1c_0_header_type.xml | 20 +++++++ tests/nodedevxml2xmltest.c | 2 + 11 files changed, 152 insertions(+), 21 deletions(-) create mode 100644 tests/nodedevschemadata/pci_0000_00_02_0_header_type.xml create mode 100644 tests/nodedevschemadata/pci_0000_00_1c_0_header_type.xml diff --git a/docs/formatnode.html.in b/docs/formatnode.html.in index 6c12227b89..e7b11400cb 100644 --- a/docs/formatnode.html.in +++ b/docs/formatnode.html.in @@ -97,27 +97,38 @@
This optional element can occur multiple times. If it exists, it has a mandatory type attribute - which will be set to - either physical_function - or virtual_functions. If the type - is physical_function, there will be a - single address subelement which contains - the PCI address of the SRIOV Physical Function (PF) - that is the parent of this device (and this device is, - by implication, an SRIOV Virtual Function (VF)). If - the type is virtual_functions, then this - device is an SRIOV PF, and the capability element will - have a list of address subelements, one - for each VF on this PF. If the host system supports - reporting it (via the "sriov_maxvfs" file in the - device's sysfs directory) the capability element will - also have an attribute named maxCount - which is the maximum number of SRIOV VFs supported by - this device, which could be higher than the number of - VFs that are curently active since - 1.3.0; in this case, even if there are - currently no active VFs the virtual_functions - capabililty will still be shown. + which will be set to: +
+
physical_function
+
+ That means there will be a single address + subelement which contains the PCI address of the SRIOV + Physical Function (PF) that is the parent of this device + (and this device is, by implication, an SRIOV Virtual + Function (VF)). +
+
virtual_function
+
+ In this case this device is an SRIOV PF, and the capability + element will have a list of address + subelements, one for each VF on this PF. If the host system + supports reporting it (via the "sriov_maxvfs" file in the + device's sysfs directory) the capability element will also + have an attribute named maxCount which is the + maximum number of SRIOV VFs supported by this device, which + could be higher than the number of VFs that are curently + active since 1.3.0; in this case, + even if there are currently no active VFs the + virtual_functions capabililty will still be shown. +
+
pci-bridge or cardbus-bridge
+
+ This shows merely that the lower 7 bits of PCI header type + have either value of 1 or 2 respectively. Usually this + means such device cannot be used for PCI passthrough. + Since 1.3.3 +
+
numa
diff --git a/docs/schemas/nodedev.rng b/docs/schemas/nodedev.rng index 744dccdf5f..949811cacb 100644 --- a/docs/schemas/nodedev.rng +++ b/docs/schemas/nodedev.rng @@ -168,6 +168,17 @@ + + + + + pci-bridge + cardbus-bridge + + + + + diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c index 0c9c348e37..611045c679 100644 --- a/src/conf/node_device_conf.c +++ b/src/conf/node_device_conf.c @@ -402,6 +402,12 @@ char *virNodeDeviceDefFormat(const virNodeDeviceDef *def) if (data->pci_dev.numa_node >= 0) virBufferAsprintf(&buf, "\n", data->pci_dev.numa_node); + + if (data->pci_dev.hdrType) { + virBufferAsprintf(&buf, "\n", + virPCIHeaderTypeToString(data->pci_dev.hdrType)); + } + if (data->pci_dev.flags & VIR_NODE_DEV_CAP_FLAG_PCIE) virPCIEDeviceInfoFormat(&buf, data->pci_dev.pci_express); break; @@ -1272,6 +1278,7 @@ virNodeDevCapPCIDevParseXML(xmlXPathContextPtr ctxt, xmlNodePtr orignode, iommuGroupNode, pciExpress; int ret = -1; virPCIEDeviceInfoPtr pci_express = NULL; + char *tmp = NULL; orignode = ctxt->node; ctxt->node = node; @@ -1329,6 +1336,18 @@ virNodeDevCapPCIDevParseXML(xmlXPathContextPtr ctxt, _("invalid NUMA node ID supplied for '%s'")) < 0) goto out; + if ((tmp = virXPathString("string(./capability[1]/@type)", ctxt))) { + int hdrType = virPCIHeaderTypeFromString(tmp); + + if (hdrType <= 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unknown PCI header type '%s'"), tmp); + goto out; + } + + data->pci_dev.hdrType = hdrType; + } + if ((pciExpress = virXPathNode("./pci-express[1]", ctxt))) { if (VIR_ALLOC(pci_express) < 0) goto out; @@ -1343,6 +1362,7 @@ virNodeDevCapPCIDevParseXML(xmlXPathContextPtr ctxt, ret = 0; out: + VIR_FREE(tmp); virPCIEDeviceInfoFree(pci_express); ctxt->node = orignode; return ret; diff --git a/src/conf/node_device_conf.h b/src/conf/node_device_conf.h index d0071867cc..be6dd5eb4e 100644 --- a/src/conf/node_device_conf.h +++ b/src/conf/node_device_conf.h @@ -119,6 +119,7 @@ typedef struct _virNodeDevCapData { unsigned int iommuGroupNumber; int numa_node; virPCIEDeviceInfoPtr pci_express; + int hdrType; /* enum virPCIHeaderType or -1 */ } pci_dev; struct { unsigned int bus; diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 640c6b3138..ff803f996a 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2010,11 +2010,14 @@ virPCIDeviceSetUsedBy; virPCIDeviceUnbind; virPCIDeviceWaitForCleanup; virPCIEDeviceInfoFree; +virPCIGetHeaderType; virPCIGetNetName; virPCIGetPhysicalFunction; virPCIGetVirtualFunctionIndex; virPCIGetVirtualFunctionInfo; virPCIGetVirtualFunctions; +virPCIHeaderTypeFromString; +virPCIHeaderTypeToString; virPCIIsVirtualFunction; virPCIStubDriverTypeFromString; virPCIStubDriverTypeToString; diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_device_udev.c index aaee0e503d..6bff5bac73 100644 --- a/src/node_device/node_device_udev.c +++ b/src/node_device/node_device_udev.c @@ -506,6 +506,9 @@ static int udevProcessPCI(struct udev_device *device, /* We need to be root to read PCI device configs */ if (priv->privileged) { + if (virPCIGetHeaderType(pciDev, &data->pci_dev.hdrType) < 0) + goto out; + if (virPCIDeviceIsPCIExpress(pciDev) > 0) { if (VIR_ALLOC(pci_express) < 0) goto out; diff --git a/src/util/virpci.c b/src/util/virpci.c index 1854318dd1..f7921f86d6 100644 --- a/src/util/virpci.c +++ b/src/util/virpci.c @@ -62,6 +62,12 @@ VIR_ENUM_IMPL(virPCIStubDriver, VIR_PCI_STUB_DRIVER_LAST, "vfio-pci", /* VFIO */ ); +VIR_ENUM_IMPL(virPCIHeader, VIR_PCI_HEADER_LAST, + "endpoint", + "pci-bridge", + "cardbus-bridge", +); + struct _virPCIDevice { virPCIDeviceAddress address; @@ -2883,6 +2889,33 @@ virPCIDeviceGetLinkCapSta(virPCIDevicePtr dev, } +int virPCIGetHeaderType(virPCIDevicePtr dev, int *hdrType) +{ + int fd; + uint8_t type; + + *hdrType = -1; + + if ((fd = virPCIDeviceConfigOpen(dev, true)) < 0) + return -1; + + type = virPCIDeviceRead8(dev, fd, PCI_HEADER_TYPE); + + virPCIDeviceConfigClose(dev, fd); + + type &= PCI_HEADER_TYPE_MASK; + if (type >= VIR_PCI_HEADER_LAST) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unknown PCI header type '%d'"), type); + return -1; + } + + *hdrType = type; + + return 0; +} + + void virPCIEDeviceInfoFree(virPCIEDeviceInfoPtr dev) { diff --git a/src/util/virpci.h b/src/util/virpci.h index 55329c8a03..82f45ec417 100644 --- a/src/util/virpci.h +++ b/src/util/virpci.h @@ -62,6 +62,16 @@ typedef enum { VIR_ENUM_DECL(virPCIELinkSpeed) +typedef enum { + VIR_PCI_HEADER_ENDPOINT = 0, + VIR_PCI_HEADER_PCI_BRIDGE, + VIR_PCI_HEADER_CARDBUS_BRIDGE, + + VIR_PCI_HEADER_LAST +} virPCIHeaderType; + +VIR_ENUM_DECL(virPCIHeader) + typedef struct _virPCIELink virPCIELink; typedef virPCIELink *virPCIELinkPtr; struct _virPCIELink { @@ -223,6 +233,8 @@ int virPCIDeviceGetLinkCapSta(virPCIDevicePtr dev, unsigned int *sta_speed, unsigned int *sta_width); +int virPCIGetHeaderType(virPCIDevicePtr dev, int *hdrType); + void virPCIEDeviceInfoFree(virPCIEDeviceInfoPtr dev); #endif /* __VIR_PCI_H__ */ diff --git a/tests/nodedevschemadata/pci_0000_00_02_0_header_type.xml b/tests/nodedevschemadata/pci_0000_00_02_0_header_type.xml new file mode 100644 index 0000000000..5150fd1e8b --- /dev/null +++ b/tests/nodedevschemadata/pci_0000_00_02_0_header_type.xml @@ -0,0 +1,15 @@ + + pci_0000_00_02_0 + computer + + 0 + 0 + 2 + 0 + 4th Gen Core Processor Integrated Graphics Controller + Intel Corporation + +
+ + + diff --git a/tests/nodedevschemadata/pci_0000_00_1c_0_header_type.xml b/tests/nodedevschemadata/pci_0000_00_1c_0_header_type.xml new file mode 100644 index 0000000000..dea5f05237 --- /dev/null +++ b/tests/nodedevschemadata/pci_0000_00_1c_0_header_type.xml @@ -0,0 +1,20 @@ + + pci_0000_00_1c_0 + computer + + 0 + 0 + 28 + 0 + 8 Series/C220 Series Chipset Family PCI Express Root Port #1 + Intel Corporation + +
+ + + + + + + + diff --git a/tests/nodedevxml2xmltest.c b/tests/nodedevxml2xmltest.c index 0089b5dadf..f519bce634 100644 --- a/tests/nodedevxml2xmltest.c +++ b/tests/nodedevxml2xmltest.c @@ -91,6 +91,8 @@ mymain(void) DO_TEST("usb_device_1d6b_1_0000_00_1d_0"); DO_TEST("pci_8086_4238_pcie_wireless"); DO_TEST("pci_8086_0c0c_snd_hda_intel"); + DO_TEST("pci_0000_00_02_0_header_type"); + DO_TEST("pci_0000_00_1c_0_header_type"); return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; }