Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

vfio/pci: Lock external INTx masking ops

Mask operations through config space changes to DisINTx may race INTx
configuration changes via ioctl. Create wrappers that add locking for
paths outside of the core interrupt code.

In particular, irq_type is updated holding igate, therefore testing
is_intx() requires holding igate. For example clearing DisINTx from
config space can otherwise race changes of the interrupt configuration.

This aligns interfaces which may trigger the INTx eventfd into two
camps, one side serialized by igate and the other only enabled while
INTx is configured. A subsequent patch introduces synchronization for
the latter flows.

Cc: <stable@vger.kernel.org>
Fixes: 89e1f7d4c66d ("vfio: Add PCI device driver")
Reported-by: Reinette Chatre <reinette.chatre@intel.com>
Reviewed-by: Kevin Tian <kevin.tian@intel.com>
Reviewed-by: Reinette Chatre <reinette.chatre@intel.com>
Reviewed-by: Eric Auger <eric.auger@redhat.com>
Link: https://lore.kernel.org/r/20240308230557.805580-3-alex.williamson@redhat.com
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>

+28 -6
+28 -6
drivers/vfio/pci/vfio_pci_intrs.c
··· 99 99 } 100 100 101 101 /* Returns true if the INTx vfio_pci_irq_ctx.masked value is changed. */ 102 - bool vfio_pci_intx_mask(struct vfio_pci_core_device *vdev) 102 + static bool __vfio_pci_intx_mask(struct vfio_pci_core_device *vdev) 103 103 { 104 104 struct pci_dev *pdev = vdev->pdev; 105 105 struct vfio_pci_irq_ctx *ctx; 106 106 unsigned long flags; 107 107 bool masked_changed = false; 108 + 109 + lockdep_assert_held(&vdev->igate); 108 110 109 111 spin_lock_irqsave(&vdev->irqlock, flags); 110 112 ··· 143 141 out_unlock: 144 142 spin_unlock_irqrestore(&vdev->irqlock, flags); 145 143 return masked_changed; 144 + } 145 + 146 + bool vfio_pci_intx_mask(struct vfio_pci_core_device *vdev) 147 + { 148 + bool mask_changed; 149 + 150 + mutex_lock(&vdev->igate); 151 + mask_changed = __vfio_pci_intx_mask(vdev); 152 + mutex_unlock(&vdev->igate); 153 + 154 + return mask_changed; 146 155 } 147 156 148 157 /* ··· 207 194 return ret; 208 195 } 209 196 210 - void vfio_pci_intx_unmask(struct vfio_pci_core_device *vdev) 197 + static void __vfio_pci_intx_unmask(struct vfio_pci_core_device *vdev) 211 198 { 199 + lockdep_assert_held(&vdev->igate); 200 + 212 201 if (vfio_pci_intx_unmask_handler(vdev, NULL) > 0) 213 202 vfio_send_intx_eventfd(vdev, NULL); 203 + } 204 + 205 + void vfio_pci_intx_unmask(struct vfio_pci_core_device *vdev) 206 + { 207 + mutex_lock(&vdev->igate); 208 + __vfio_pci_intx_unmask(vdev); 209 + mutex_unlock(&vdev->igate); 214 210 } 215 211 216 212 static irqreturn_t vfio_intx_handler(int irq, void *dev_id) ··· 585 563 return -EINVAL; 586 564 587 565 if (flags & VFIO_IRQ_SET_DATA_NONE) { 588 - vfio_pci_intx_unmask(vdev); 566 + __vfio_pci_intx_unmask(vdev); 589 567 } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { 590 568 uint8_t unmask = *(uint8_t *)data; 591 569 if (unmask) 592 - vfio_pci_intx_unmask(vdev); 570 + __vfio_pci_intx_unmask(vdev); 593 571 } else if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { 594 572 struct vfio_pci_irq_ctx *ctx = vfio_irq_ctx_get(vdev, 0); 595 573 int32_t fd = *(int32_t *)data; ··· 616 594 return -EINVAL; 617 595 618 596 if (flags & VFIO_IRQ_SET_DATA_NONE) { 619 - vfio_pci_intx_mask(vdev); 597 + __vfio_pci_intx_mask(vdev); 620 598 } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { 621 599 uint8_t mask = *(uint8_t *)data; 622 600 if (mask) 623 - vfio_pci_intx_mask(vdev); 601 + __vfio_pci_intx_mask(vdev); 624 602 } else if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { 625 603 return -ENOTTY; /* XXX implement me */ 626 604 }