]> xenbits.xensource.com Git - libvirt.git/commitdiff
nodedev: Expose PCI header type
authorMartin Kletzander <mkletzan@redhat.com>
Tue, 15 Mar 2016 11:22:03 +0000 (12:22 +0100)
committerMartin Kletzander <mkletzan@redhat.com>
Fri, 18 Mar 2016 16:35:06 +0000 (17:35 +0100)
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 <mkletzan@redhat.com>
docs/formatnode.html.in
docs/schemas/nodedev.rng
src/conf/node_device_conf.c
src/conf/node_device_conf.h
src/libvirt_private.syms
src/node_device/node_device_udev.c
src/util/virpci.c
src/util/virpci.h
tests/nodedevschemadata/pci_0000_00_02_0_header_type.xml [new file with mode: 0644]
tests/nodedevschemadata/pci_0000_00_1c_0_header_type.xml [new file with mode: 0644]
tests/nodedevxml2xmltest.c

index 6c12227b89525910fc5b79b491a87e68e4f8a80b..e7b11400cbebcef96037bf4af69f8d6d804cb127 100644 (file)
               <dd>
                 This optional element can occur multiple times. If it
                 exists, it has a mandatory <code>type</code> attribute
-                which will be set to
-                either <code>physical_function</code>
-                or <code>virtual_functions</code>. If the type
-                is <code>physical_function</code>, there will be a
-                single <code>address</code> 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 <code>virtual_functions</code>, then this
-                device is an SRIOV PF, and the capability element will
-                have a list of <code>address</code> 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 <code>maxCount</code>
-                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 <span class="since">since
-                1.3.0</span>; in this case, even if there are
-                currently no active VFs the virtual_functions
-                capabililty will still be shown.
+                which will be set to:
+                <dl>
+                  <dt><code>physical_function</code></dt>
+                  <dd>
+                    That means there will be a single <code>address</code>
+                    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)).
+                  </dd>
+                  <dt><code>virtual_function</code></dt>
+                  <dd>
+                    In this case this device is an SRIOV PF, and the capability
+                    element will have a list of <code>address</code>
+                    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 <code>maxCount</code> 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 <span class="since">since 1.3.0</span>; in this case,
+                    even if there are currently no active VFs the
+                    virtual_functions capabililty will still be shown.
+                  </dd>
+                  <dt><code>pci-bridge</code> or <code>cardbus-bridge</code></dt>
+                  <dd>
+                    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.
+                    <span class="since">Since 1.3.3</span>
+                  </dd>
+                </dl>
               </dd>
               <dt><code>numa</code></dt>
               <dd>
index 744dccdf5fa99fc5644783e133f9c84929e95a66..949811cacb012b4ed2eadead6d1c412a78144fdb 100644 (file)
       </element>
     </optional>
 
+    <optional>
+      <element name='capability'>
+        <attribute name='type'>
+          <choice>
+            <value>pci-bridge</value>
+            <value>cardbus-bridge</value>
+          </choice>
+        </attribute>
+      </element>
+    </optional>
+
     <optional>
       <element name='pci-express'>
         <zeroOrMore>
index 0c9c348e3793f555f15e169458df6951c861fd10..611045c679abaa6d2f04bbfbcd48af053431aaec 100644 (file)
@@ -402,6 +402,12 @@ char *virNodeDeviceDefFormat(const virNodeDeviceDef *def)
             if (data->pci_dev.numa_node >= 0)
                 virBufferAsprintf(&buf, "<numa node='%d'/>\n",
                                   data->pci_dev.numa_node);
+
+            if (data->pci_dev.hdrType) {
+                virBufferAsprintf(&buf, "<capability type='%s'/>\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;
index d0071867ccb7ca60224ee71585dc342ed32691c4..be6dd5eb4ec140b739188d98696cfe026f211d06 100644 (file)
@@ -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;
index 640c6b3138a83e012b6e1abed3b9a26663fd68ed..ff803f996ac36d320bad46412fbe09ac01a34793 100644 (file)
@@ -2010,11 +2010,14 @@ virPCIDeviceSetUsedBy;
 virPCIDeviceUnbind;
 virPCIDeviceWaitForCleanup;
 virPCIEDeviceInfoFree;
+virPCIGetHeaderType;
 virPCIGetNetName;
 virPCIGetPhysicalFunction;
 virPCIGetVirtualFunctionIndex;
 virPCIGetVirtualFunctionInfo;
 virPCIGetVirtualFunctions;
+virPCIHeaderTypeFromString;
+virPCIHeaderTypeToString;
 virPCIIsVirtualFunction;
 virPCIStubDriverTypeFromString;
 virPCIStubDriverTypeToString;
index aaee0e503d7bf93c6d8111e7d088a93036a68737..6bff5bac736686a2509223d8ec011e28bd77d7ae 100644 (file)
@@ -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;
index 1854318dd114aa9ee039efe06b83624433697fe9..f7921f86d6debc8a99ef3167dff1cb940375ffc6 100644 (file)
@@ -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)
 {
index 55329c8a03d32ca5e194866c01e4e6ff005a2b78..82f45ec4175fb68b781dc80b60cac69d53d6f80f 100644 (file)
@@ -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 (file)
index 0000000..5150fd1
--- /dev/null
@@ -0,0 +1,15 @@
+<device>
+  <name>pci_0000_00_02_0</name>
+  <parent>computer</parent>
+  <capability type='pci'>
+    <domain>0</domain>
+    <bus>0</bus>
+    <slot>2</slot>
+    <function>0</function>
+    <product id='0x0416'>4th Gen Core Processor Integrated Graphics Controller</product>
+    <vendor id='0x8086'>Intel Corporation</vendor>
+    <iommuGroup number='1'>
+      <address domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
+    </iommuGroup>
+  </capability>
+</device>
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 (file)
index 0000000..dea5f05
--- /dev/null
@@ -0,0 +1,20 @@
+<device>
+  <name>pci_0000_00_1c_0</name>
+  <parent>computer</parent>
+  <capability type='pci'>
+    <domain>0</domain>
+    <bus>0</bus>
+    <slot>28</slot>
+    <function>0</function>
+    <product id='0x8c10'>8 Series/C220 Series Chipset Family PCI Express Root Port #1</product>
+    <vendor id='0x8086'>Intel Corporation</vendor>
+    <iommuGroup number='8'>
+      <address domain='0x0000' bus='0x00' slot='0x1c' function='0x0'/>
+    </iommuGroup>
+    <capability type='pci-bridge'/>
+    <pci-express>
+      <link validity='cap' port='1' speed='5' width='1'/>
+      <link validity='sta' speed='2.5' width='1'/>
+    </pci-express>
+  </capability>
+</device>
index 0089b5dadffc39e3fd9913c12a568e4350b03125..f519bce6345f2c0b2939405ca73628bff6680c27 100644 (file)
@@ -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;
 }