@@ -47,23 +47,22 @@
/*
* vfio_interrupt - IRQ hardware interrupt handler
*/
-irqreturn_t vfio_interrupt(int irq, void *dev_id)
+irqreturn_t vfio_disable_intx(struct vfio_dev *vdev)
{
- struct vfio_dev *vdev = dev_id;
struct pci_dev *pdev = vdev->pdev;
irqreturn_t ret = IRQ_NONE;
- u32 cmd_status_dword;
- u16 origcmd, newcmd, status;
spin_lock_irq(&vdev->irqlock);
- /* INTX disabled interrupts can still be shared */
if (vdev->irq_disabled) {
spin_unlock_irq(&vdev->irqlock);
return ret;
}
if (vdev->pci_2_3) {
+ u32 cmd_status_dword;
+ u16 origcmd, newcmd, status;
+
pci_block_user_cfg_access(pdev);
/* Read both command and status registers in a single 32-bit
@@ -98,15 +97,10 @@ done:
spin_unlock_irq(&vdev->irqlock);
- if (ret != IRQ_HANDLED)
- return ret;
-
- if (vdev->ev_irq)
- eventfd_signal(vdev->ev_irq, 1);
return ret;
}
-int vfio_irq_eoi(struct vfio_dev *vdev)
+void vfio_enable_intx(struct vfio_dev *vdev)
{
struct pci_dev *pdev = vdev->pdev;
@@ -129,6 +123,28 @@ int vfio_irq_eoi(struct vfio_dev *vdev)
}
spin_unlock_irq(&vdev->irqlock);
+}
+
+irqreturn_t vfio_interrupt(int irq, void *dev_id)
+{
+ struct vfio_dev *vdev = dev_id;
+ irqreturn_t ret = vfio_disable_intx(vdev);
+
+ if (ret != IRQ_HANDLED)
+ return ret;
+
+ if (vdev->ev_irq)
+ eventfd_signal(vdev->ev_irq, 1);
+ return ret;
+}
+
+int vfio_irq_eoi(struct vfio_dev *vdev)
+{
+ /* EOI shouldn't re-enable intx if disabled by INTX_DISABLE */
+ if (vdev->vconfig[PCI_COMMAND+1] & PCI_CMD_INTX_DISABLE_BYTE)
+ return 0;
+
+ vfio_enable_intx(vdev);
return 0;
}
@@ -110,8 +110,14 @@ static int vfio_open(struct inode *inode, struct file *filep)
listener->vdev = vdev;
INIT_LIST_HEAD(&listener->dm_list);
if (vdev->listeners == 0) {
+ u16 cmd;
(void) pci_reset_function(vdev->pdev);
msleep(100); /* 100ms for reset recovery */
+ pci_read_config_word(vdev->pdev, PCI_COMMAND, &cmd);
+ if (vdev->pci_2_3 && (cmd & PCI_COMMAND_INTX_DISABLE)) {
+ cmd &= ~PCI_COMMAND_INTX_DISABLE;
+ pci_write_config_word(vdev->pdev, PCI_COMMAND, cmd);
+ }
ret = pci_enable_device(vdev->pdev);
}
if (!ret) {
@@ -172,6 +178,7 @@ static int vfio_release(struct inode *inode, struct file *filep)
free_irq(vdev->pdev->irq, vdev);
eventfd_ctx_put(vdev->ev_irq);
vdev->ev_irq = NULL;
+ vdev->irq_disabled = false;
}
kfree(vdev->vconfig);
vdev->vconfig = NULL;
@@ -391,6 +398,7 @@ static long vfio_unl_ioctl(struct file *filep,
if (vdev->ev_irq) {
eventfd_ctx_put(vdev->ev_irq);
free_irq(pdev->irq, vdev);
+ vdev->irq_disabled = false;
vdev->ev_irq = NULL;
}
if (vdev->ev_msi) { /* irq and msi both use pdev->irq */
@@ -398,11 +406,15 @@ static long vfio_unl_ioctl(struct file *filep,
} else {
if (fd >= 0) {
vdev->ev_irq = eventfd_ctx_fdget(fd);
- if (vdev->ev_irq)
+ if (vdev->ev_irq) {
ret = request_irq(pdev->irq,
vfio_interrupt,
vdev->pci_2_3 ? IRQF_SHARED : 0,
vdev->name, vdev);
+ if (vdev->vconfig[PCI_COMMAND+1] &
+ PCI_CMD_INTX_DISABLE_BYTE)
+ vfio_disable_intx(vdev);
+ }
else
ret = -EINVAL;
}
@@ -185,8 +185,8 @@ static int __init init_pci_cap_basic_perm(struct perm_bits *perm)
/* for catching resume-after-reset */
p_setw(perm, PCI_COMMAND,
- PCI_COMMAND_MEMORY + PCI_COMMAND_IO,
- ALL_WRITE);
+ PCI_COMMAND_MEMORY + PCI_COMMAND_IO + PCI_COMMAND_INTX_DISABLE,
+ ALL_WRITE);
/* no harm to write */
p_setb(perm, PCI_CACHE_LINE_SIZE, NO_VIRT, ALL_WRITE);
@@ -849,6 +849,15 @@ static void vfio_virt_basic(struct vfio_dev *vdev, int write,
vfio_write_config_byte(vdev, pos, newval);
}
break;
+ case PCI_COMMAND + 1:
+ if (write) {
+ if ((newval & PCI_CMD_INTX_DISABLE_BYTE) &&
+ !(val & PCI_CMD_INTX_DISABLE_BYTE))
+ vfio_disable_intx(vdev);
+ if (!(newval & PCI_CMD_INTX_DISABLE_BYTE) &&
+ (val & PCI_CMD_INTX_DISABLE_BYTE))
+ vfio_enable_intx(vdev);
+ }
case PCI_BASE_ADDRESS_0 ... PCI_BASE_ADDRESS_5 + 3:
case PCI_ROM_ADDRESS ... PCI_ROM_ADDRESS + 3:
if (write) {
@@ -163,6 +163,10 @@ int vfio_irq_eoi(struct vfio_dev *);
int vfio_irq_eoi_eventfd(struct vfio_dev *, int);
int vfio_eoi_module_init(void);
void vfio_eoi_module_exit(void);
+irqreturn_t vfio_disable_intx(struct vfio_dev *vdev);
+void vfio_enable_intx(struct vfio_dev *vdev);
+
+#define PCI_CMD_INTX_DISABLE_BYTE (PCI_COMMAND_INTX_DISABLE >> 8)
#endif /* __KERNEL__ */