As we use this internally for interrupt control, it's dangerous
to let the user manipulate it.  Instead, virtualize it.  Also,
de-assert INTX_DISABLE when device is opened, the device reset
doesn't seem to clear this.

Signed-off-by: Alex Williamson <alex.william...@redhat.com>
---

 drivers/vfio/vfio_intrs.c      |   38 +++++++++++++++++++++++++++-----------
 drivers/vfio/vfio_main.c       |   14 +++++++++++++-
 drivers/vfio/vfio_pci_config.c |   13 +++++++++++--
 include/linux/vfio.h           |    4 ++++
 4 files changed, 55 insertions(+), 14 deletions(-)

diff --git a/drivers/vfio/vfio_intrs.c b/drivers/vfio/vfio_intrs.c
index 73e3deb..ace0195 100644
--- a/drivers/vfio/vfio_intrs.c
+++ b/drivers/vfio/vfio_intrs.c
@@ -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;
 }
 
diff --git a/drivers/vfio/vfio_main.c b/drivers/vfio/vfio_main.c
index 3cd3cb8..3f51eae 100644
--- a/drivers/vfio/vfio_main.c
+++ b/drivers/vfio/vfio_main.c
@@ -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;
                        }
diff --git a/drivers/vfio/vfio_pci_config.c b/drivers/vfio/vfio_pci_config.c
index bb38dbe..6257070 100644
--- a/drivers/vfio/vfio_pci_config.c
+++ b/drivers/vfio/vfio_pci_config.c
@@ -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) {
diff --git a/include/linux/vfio.h b/include/linux/vfio.h
index c26f3b3..28da636 100644
--- a/include/linux/vfio.h
+++ b/include/linux/vfio.h
@@ -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__ */
 

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to