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

NVMe: Always use MSI/MSI-x interrupts

Multiple users have reported device initialization failure due the driver
not receiving legacy PCI interrupts. This is not unique to any particular
controller, but has been observed on multiple platforms.

There have been no issues reported or observed when with message signaled
interrupts, so this patch attempts to use MSI-x during initialization,
falling back to MSI. If that fails, legacy would become the default.

The setup_io_queues error handling had to change as a result: the admin
queue's msix_entry used to be initialized to the legacy IRQ. The case
where nr_io_queues is 0 would fail request_irq when setting up the admin
queue's interrupt since re-enabling MSI-x fails with 0 vectors, leaving
the admin queue's msix_entry invalid. Instead, return success immediately.

Reported-by: Tim Muhlemmer <muhlemmer@gmail.com>
Reported-by: Jon Derrick <jonathan.derrick@intel.com>
Signed-off-by: Keith Busch <keith.busch@intel.com>
Signed-off-by: Jens Axboe <axboe@fb.com>

authored by

Keith Busch and committed by
Jens Axboe
a5229050 9bf2b972

+15 -10
+15 -10
drivers/nvme/host/pci.c
··· 1478 1478 if (result > 0) { 1479 1479 dev_err(dev->ctrl.device, 1480 1480 "Could not set queue count (%d)\n", result); 1481 - nr_io_queues = 0; 1482 - result = 0; 1481 + return 0; 1483 1482 } 1484 1483 1485 1484 if (dev->cmb && NVME_CMB_SQS(dev->cmbsz)) { ··· 1512 1513 * If we enable msix early due to not intx, disable it again before 1513 1514 * setting up the full range we need. 1514 1515 */ 1515 - if (!pdev->irq) 1516 + if (pdev->msi_enabled) 1517 + pci_disable_msi(pdev); 1518 + else if (pdev->msix_enabled) 1516 1519 pci_disable_msix(pdev); 1517 1520 1518 1521 for (i = 0; i < nr_io_queues; i++) ··· 1697 1696 if (pci_enable_device_mem(pdev)) 1698 1697 return result; 1699 1698 1700 - dev->entry[0].vector = pdev->irq; 1701 1699 pci_set_master(pdev); 1702 1700 1703 1701 if (dma_set_mask_and_coherent(dev->dev, DMA_BIT_MASK(64)) && ··· 1709 1709 } 1710 1710 1711 1711 /* 1712 - * Some devices don't advertse INTx interrupts, pre-enable a single 1713 - * MSIX vec for setup. We'll adjust this later. 1712 + * Some devices and/or platforms don't advertise or work with INTx 1713 + * interrupts. Pre-enable a single MSIX or MSI vec for setup. We'll 1714 + * adjust this later. 1714 1715 */ 1715 - if (!pdev->irq) { 1716 - result = pci_enable_msix(pdev, dev->entry, 1); 1717 - if (result < 0) 1718 - goto disable; 1716 + if (pci_enable_msix(pdev, dev->entry, 1)) { 1717 + pci_enable_msi(pdev); 1718 + dev->entry[0].vector = pdev->irq; 1719 + } 1720 + 1721 + if (!dev->entry[0].vector) { 1722 + result = -ENODEV; 1723 + goto disable; 1719 1724 } 1720 1725 1721 1726 cap = lo_hi_readq(dev->bar + NVME_REG_CAP);