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

vfio/pci: Disable auto-enable of exclusive INTx IRQ

Currently for devices requiring masking at the irqchip for INTx, ie.
devices without DisINTx support, the IRQ is enabled in request_irq()
and subsequently disabled as necessary to align with the masked status
flag. This presents a window where the interrupt could fire between
these events, resulting in the IRQ incrementing the disable depth twice.
This would be unrecoverable for a user since the masked flag prevents
nested enables through vfio.

Instead, invert the logic using IRQF_NO_AUTOEN such that exclusive INTx
is never auto-enabled, then unmask as required.

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

+10 -7
+10 -7
drivers/vfio/pci/vfio_pci_intrs.c
··· 296 296 297 297 ctx->trigger = trigger; 298 298 299 + /* 300 + * Devices without DisINTx support require an exclusive interrupt, 301 + * IRQ masking is performed at the IRQ chip. The masked status is 302 + * protected by vdev->irqlock. Setup the IRQ without auto-enable and 303 + * unmask as necessary below under lock. DisINTx is unmodified by 304 + * the IRQ configuration and may therefore use auto-enable. 305 + */ 299 306 if (!vdev->pci_2_3) 300 - irqflags = 0; 307 + irqflags = IRQF_NO_AUTOEN; 301 308 302 309 ret = request_irq(pdev->irq, vfio_intx_handler, 303 310 irqflags, ctx->name, vdev); ··· 315 308 return ret; 316 309 } 317 310 318 - /* 319 - * INTx disable will stick across the new irq setup, 320 - * disable_irq won't. 321 - */ 322 311 spin_lock_irqsave(&vdev->irqlock, flags); 323 - if (!vdev->pci_2_3 && ctx->masked) 324 - disable_irq_nosync(pdev->irq); 312 + if (!vdev->pci_2_3 && !ctx->masked) 313 + enable_irq(pdev->irq); 325 314 spin_unlock_irqrestore(&vdev->irqlock, flags); 326 315 327 316 return 0;