aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Williamson <alex.williamson@redhat.com>2016-03-24 15:05:18 -0400
committerAlex Williamson <alex.williamson@redhat.com>2016-04-28 13:12:27 -0400
commit450744051d201c4d72436ebf5b04b9a06ba2cf30 (patch)
tree4d8bf2d69dbc814e3f506c2731256d7fcaffd318
parent02da2d72174c61988eb4456b53f405e3ebdebce4 (diff)
vfio/pci: Hide broken INTx support from user
INTx masking has two components, the first is that we need the ability to prevent the device from continuing to assert INTx. This is provided via the DisINTx bit in the command register and is the only thing we can really probe for when testing if INTx masking is supported. The second component is that the device needs to indicate if INTx is asserted via the interrupt status bit in the device status register. With these two features we can generically determine if one of the devices we own is asserting INTx, signal the user, and mask the interrupt while the user services the device. Generally if one or both of these components is broken we resort to APIC level interrupt masking, which requires an exclusive interrupt since we have no way to determine the source of the interrupt in a shared configuration. This often makes it difficult or impossible to configure the system for userspace use of the device, for an interrupt mode that the user may not need. One possible configuration of broken INTx masking is that the DisINTx support is fully functional, but the interrupt status bit never signals interrupt assertion. In this case we do have the ability to prevent the device from asserting INTx, but lack the ability to identify the interrupt source. For this case we can simply pretend that the device lacks INTx support entirely, keeping DisINTx set on the physical device, virtualizing this bit for the user, and virtualizing the interrupt pin register to indicate no INTx support. We already support virtualization of the DisINTx bit and already virtualize the interrupt pin for platforms without INTx support. By tying these components together, setting DisINTx on open and reset, and identifying devices broken in this particular way, we can provide support for them w/o the handicap of APIC level INTx masking. Intel i40e (XL710/X710) 10/20/40GbE NICs have been identified as being broken in this specific way. We leave the vfio-pci.nointxmask option as a mechanism to bypass this support, enabling INTx on the device with all the requirements of APIC level masking. Signed-off-by: Alex Williamson <alex.williamson@redhat.com> Cc: John Ronciak <john.ronciak@intel.com> Cc: Jesse Brandeburg <jesse.brandeburg@intel.com>
-rw-r--r--drivers/vfio/pci/vfio_pci.c55
-rw-r--r--drivers/vfio/pci/vfio_pci_config.c9
-rw-r--r--drivers/vfio/pci/vfio_pci_private.h1
3 files changed, 54 insertions, 11 deletions
diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index 712a84978e97..188b1ff03f5f 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -113,6 +113,35 @@ static inline bool vfio_pci_is_vga(struct pci_dev *pdev)
113static void vfio_pci_try_bus_reset(struct vfio_pci_device *vdev); 113static void vfio_pci_try_bus_reset(struct vfio_pci_device *vdev);
114static void vfio_pci_disable(struct vfio_pci_device *vdev); 114static void vfio_pci_disable(struct vfio_pci_device *vdev);
115 115
116/*
117 * INTx masking requires the ability to disable INTx signaling via PCI_COMMAND
118 * _and_ the ability detect when the device is asserting INTx via PCI_STATUS.
119 * If a device implements the former but not the latter we would typically
120 * expect broken_intx_masking be set and require an exclusive interrupt.
121 * However since we do have control of the device's ability to assert INTx,
122 * we can instead pretend that the device does not implement INTx, virtualizing
123 * the pin register to report zero and maintaining DisINTx set on the host.
124 */
125static bool vfio_pci_nointx(struct pci_dev *pdev)
126{
127 switch (pdev->vendor) {
128 case PCI_VENDOR_ID_INTEL:
129 switch (pdev->device) {
130 /* All i40e (XL710/X710) 10/20/40GbE NICs */
131 case 0x1572:
132 case 0x1574:
133 case 0x1580 ... 0x1581:
134 case 0x1583 ... 0x1589:
135 case 0x37d0 ... 0x37d2:
136 return true;
137 default:
138 return false;
139 }
140 }
141
142 return false;
143}
144
116static int vfio_pci_enable(struct vfio_pci_device *vdev) 145static int vfio_pci_enable(struct vfio_pci_device *vdev)
117{ 146{
118 struct pci_dev *pdev = vdev->pdev; 147 struct pci_dev *pdev = vdev->pdev;
@@ -136,23 +165,29 @@ static int vfio_pci_enable(struct vfio_pci_device *vdev)
136 pr_debug("%s: Couldn't store %s saved state\n", 165 pr_debug("%s: Couldn't store %s saved state\n",
137 __func__, dev_name(&pdev->dev)); 166 __func__, dev_name(&pdev->dev));
138 167
139 ret = vfio_config_init(vdev); 168 if (likely(!nointxmask)) {
140 if (ret) { 169 if (vfio_pci_nointx(pdev)) {
141 kfree(vdev->pci_saved_state); 170 dev_info(&pdev->dev, "Masking broken INTx support\n");
142 vdev->pci_saved_state = NULL; 171 vdev->nointx = true;
143 pci_disable_device(pdev); 172 pci_intx(pdev, 0);
144 return ret; 173 } else
174 vdev->pci_2_3 = pci_intx_mask_supported(pdev);
145 } 175 }
146 176
147 if (likely(!nointxmask))
148 vdev->pci_2_3 = pci_intx_mask_supported(pdev);
149
150 pci_read_config_word(pdev, PCI_COMMAND, &cmd); 177 pci_read_config_word(pdev, PCI_COMMAND, &cmd);
151 if (vdev->pci_2_3 && (cmd & PCI_COMMAND_INTX_DISABLE)) { 178 if (vdev->pci_2_3 && (cmd & PCI_COMMAND_INTX_DISABLE)) {
152 cmd &= ~PCI_COMMAND_INTX_DISABLE; 179 cmd &= ~PCI_COMMAND_INTX_DISABLE;
153 pci_write_config_word(pdev, PCI_COMMAND, cmd); 180 pci_write_config_word(pdev, PCI_COMMAND, cmd);
154 } 181 }
155 182
183 ret = vfio_config_init(vdev);
184 if (ret) {
185 kfree(vdev->pci_saved_state);
186 vdev->pci_saved_state = NULL;
187 pci_disable_device(pdev);
188 return ret;
189 }
190
156 msix_pos = pdev->msix_cap; 191 msix_pos = pdev->msix_cap;
157 if (msix_pos) { 192 if (msix_pos) {
158 u16 flags; 193 u16 flags;
@@ -304,7 +339,7 @@ static int vfio_pci_get_irq_count(struct vfio_pci_device *vdev, int irq_type)
304 if (irq_type == VFIO_PCI_INTX_IRQ_INDEX) { 339 if (irq_type == VFIO_PCI_INTX_IRQ_INDEX) {
305 u8 pin; 340 u8 pin;
306 pci_read_config_byte(vdev->pdev, PCI_INTERRUPT_PIN, &pin); 341 pci_read_config_byte(vdev->pdev, PCI_INTERRUPT_PIN, &pin);
307 if (IS_ENABLED(CONFIG_VFIO_PCI_INTX) && pin) 342 if (IS_ENABLED(CONFIG_VFIO_PCI_INTX) && !vdev->nointx && pin)
308 return 1; 343 return 1;
309 344
310 } else if (irq_type == VFIO_PCI_MSI_IRQ_INDEX) { 345 } else if (irq_type == VFIO_PCI_MSI_IRQ_INDEX) {
diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c
index 142c533efec7..c9bb2290d39d 100644
--- a/drivers/vfio/pci/vfio_pci_config.c
+++ b/drivers/vfio/pci/vfio_pci_config.c
@@ -408,6 +408,7 @@ static void vfio_bar_restore(struct vfio_pci_device *vdev)
408{ 408{
409 struct pci_dev *pdev = vdev->pdev; 409 struct pci_dev *pdev = vdev->pdev;
410 u32 *rbar = vdev->rbar; 410 u32 *rbar = vdev->rbar;
411 u16 cmd;
411 int i; 412 int i;
412 413
413 if (pdev->is_virtfn) 414 if (pdev->is_virtfn)
@@ -420,6 +421,12 @@ static void vfio_bar_restore(struct vfio_pci_device *vdev)
420 pci_user_write_config_dword(pdev, i, *rbar); 421 pci_user_write_config_dword(pdev, i, *rbar);
421 422
422 pci_user_write_config_dword(pdev, PCI_ROM_ADDRESS, *rbar); 423 pci_user_write_config_dword(pdev, PCI_ROM_ADDRESS, *rbar);
424
425 if (vdev->nointx) {
426 pci_user_read_config_word(pdev, PCI_COMMAND, &cmd);
427 cmd |= PCI_COMMAND_INTX_DISABLE;
428 pci_user_write_config_word(pdev, PCI_COMMAND, cmd);
429 }
423} 430}
424 431
425static __le32 vfio_generate_bar_flags(struct pci_dev *pdev, int bar) 432static __le32 vfio_generate_bar_flags(struct pci_dev *pdev, int bar)
@@ -1545,7 +1552,7 @@ int vfio_config_init(struct vfio_pci_device *vdev)
1545 *(__le16 *)&vconfig[PCI_DEVICE_ID] = cpu_to_le16(pdev->device); 1552 *(__le16 *)&vconfig[PCI_DEVICE_ID] = cpu_to_le16(pdev->device);
1546 } 1553 }
1547 1554
1548 if (!IS_ENABLED(CONFIG_VFIO_PCI_INTX)) 1555 if (!IS_ENABLED(CONFIG_VFIO_PCI_INTX) || vdev->nointx)
1549 vconfig[PCI_INTERRUPT_PIN] = 0; 1556 vconfig[PCI_INTERRUPT_PIN] = 0;
1550 1557
1551 ret = vfio_cap_init(vdev); 1558 ret = vfio_cap_init(vdev);
diff --git a/drivers/vfio/pci/vfio_pci_private.h b/drivers/vfio/pci/vfio_pci_private.h
index 8a7d546d18a0..016c14a1b454 100644
--- a/drivers/vfio/pci/vfio_pci_private.h
+++ b/drivers/vfio/pci/vfio_pci_private.h
@@ -83,6 +83,7 @@ struct vfio_pci_device {
83 bool bardirty; 83 bool bardirty;
84 bool has_vga; 84 bool has_vga;
85 bool needs_reset; 85 bool needs_reset;
86 bool nointx;
86 struct pci_saved_state *pci_saved_state; 87 struct pci_saved_state *pci_saved_state;
87 int refcnt; 88 int refcnt;
88 struct eventfd_ctx *err_trigger; 89 struct eventfd_ctx *err_trigger;