]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
vfio/pci: Mask buggy SR-IOV VF INTx support
authorAlex Williamson <alex.williamson@redhat.com>
Tue, 25 Sep 2018 19:01:27 +0000 (13:01 -0600)
committerStefan Bader <stefan.bader@canonical.com>
Tue, 26 Nov 2019 12:16:50 +0000 (13:16 +0100)
BugLink: https://bugs.launchpad.net/bugs/1853915
[ Upstream commit db04264fe9bc0f2b62e036629f9afb530324b693 ]

The SR-IOV spec requires that VFs must report zero for the INTx pin
register as VFs are precluded from INTx support.  It's much easier for
the host kernel to understand whether a device is a VF and therefore
whether a non-zero pin register value is bogus than it is to do the
same in userspace.  Override the INTx count for such devices and
virtualize the pin register to provide a consistent view of the device
to the user.

As this is clearly a spec violation, warn about it to support hardware
validation, but also provide a known whitelist as it doesn't do much
good to continue complaining if the hardware vendor doesn't plan to
fix it.

Known devices with this issue: 8086:270c

Tested-by: Gage Eads <gage.eads@intel.com>
Reviewed-by: Ashok Raj <ashok.raj@intel.com>
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
Signed-off-by: Kamal Mostafa <kamal@canonical.com>
Signed-off-by: Khalid Elmously <khalid.elmously@canonical.com>
drivers/vfio/pci/vfio_pci.c
drivers/vfio/pci/vfio_pci_config.c

index 006d3f9ba4a0786c099e4772f88afbbeb581179a..e8833f4ff871ab5cafeb7f919d888df07ab5a8ed 100644 (file)
@@ -433,10 +433,14 @@ static int vfio_pci_get_irq_count(struct vfio_pci_device *vdev, int irq_type)
 {
        if (irq_type == VFIO_PCI_INTX_IRQ_INDEX) {
                u8 pin;
+
+               if (!IS_ENABLED(CONFIG_VFIO_PCI_INTX) ||
+                   vdev->nointx || vdev->pdev->is_virtfn)
+                       return 0;
+
                pci_read_config_byte(vdev->pdev, PCI_INTERRUPT_PIN, &pin);
-               if (IS_ENABLED(CONFIG_VFIO_PCI_INTX) && !vdev->nointx && pin)
-                       return 1;
 
+               return pin ? 1 : 0;
        } else if (irq_type == VFIO_PCI_MSI_IRQ_INDEX) {
                u8 pos;
                u16 flags;
index 62023b4a373b4985db47a9bb14f465fb78ab598d..423ea1f98441a27e1ac351a14f29fb10ad462e16 100644 (file)
@@ -1611,6 +1611,15 @@ static int vfio_ecap_init(struct vfio_pci_device *vdev)
        return 0;
 }
 
+/*
+ * Nag about hardware bugs, hopefully to have vendors fix them, but at least
+ * to collect a list of dependencies for the VF INTx pin quirk below.
+ */
+static const struct pci_device_id known_bogus_vf_intx_pin[] = {
+       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x270c) },
+       {}
+};
+
 /*
  * For each device we allocate a pci_config_map that indicates the
  * capability occupying each dword and thus the struct perm_bits we
@@ -1676,6 +1685,24 @@ int vfio_config_init(struct vfio_pci_device *vdev)
        if (pdev->is_virtfn) {
                *(__le16 *)&vconfig[PCI_VENDOR_ID] = cpu_to_le16(pdev->vendor);
                *(__le16 *)&vconfig[PCI_DEVICE_ID] = cpu_to_le16(pdev->device);
+
+               /*
+                * Per SR-IOV spec rev 1.1, 3.4.1.18 the interrupt pin register
+                * does not apply to VFs and VFs must implement this register
+                * as read-only with value zero.  Userspace is not readily able
+                * to identify whether a device is a VF and thus that the pin
+                * definition on the device is bogus should it violate this
+                * requirement.  We already virtualize the pin register for
+                * other purposes, so we simply need to replace the bogus value
+                * and consider VFs when we determine INTx IRQ count.
+                */
+               if (vconfig[PCI_INTERRUPT_PIN] &&
+                   !pci_match_id(known_bogus_vf_intx_pin, pdev))
+                       pci_warn(pdev,
+                                "Hardware bug: VF reports bogus INTx pin %d\n",
+                                vconfig[PCI_INTERRUPT_PIN]);
+
+               vconfig[PCI_INTERRUPT_PIN] = 0; /* Gratuitous for good VFs */
        }
 
        if (!IS_ENABLED(CONFIG_VFIO_PCI_INTX) || vdev->nointx)