nvme-pci: Add quirk for broken MSIs
authorSean Anderson <sean.anderson@linux.dev>
Mon, 22 Apr 2024 16:28:23 +0000 (12:28 -0400)
committerKeith Busch <kbusch@kernel.org>
Tue, 7 May 2024 14:55:14 +0000 (07:55 -0700)
Sandisk SN530 NVMe drives have broken MSIs. On systems without MSI-X
support, all commands time out resulting in the following message:

nvme nvme0: I/O tag 12 (100c) QID 0 timeout, completion polled

These timeouts cause the boot to take an excessively-long time (over 20
minutes) while the initial command queue is flushed.

Address this by adding a quirk for drives with buggy MSIs. The lspci
output for this device (recorded on a system with MSI-X support) is:

02:00.0 Non-Volatile memory controller: Sandisk Corp Device 5008 (rev 01) (prog-if 02 [NVM Express])
Subsystem: Sandisk Corp Device 5008
Flags: bus master, fast devsel, latency 0, IRQ 16, NUMA node 0
Memory at f7e00000 (64-bit, non-prefetchable) [size=16K]
Memory at f7e04000 (64-bit, non-prefetchable) [size=256]
Capabilities: [80] Power Management version 3
Capabilities: [90] MSI: Enable- Count=1/32 Maskable- 64bit+
Capabilities: [b0] MSI-X: Enable+ Count=17 Masked-
Capabilities: [c0] Express Endpoint, MSI 00
Capabilities: [100] Advanced Error Reporting
Capabilities: [150] Device Serial Number 00-00-00-00-00-00-00-00
Capabilities: [1b8] Latency Tolerance Reporting
Capabilities: [300] Secondary PCI Express
Capabilities: [900] L1 PM Substates
Kernel driver in use: nvme
Kernel modules: nvme

Cc: <stable@vger.kernel.org>
Signed-off-by: Sean Anderson <sean.anderson@linux.dev>
Reviewed-by: Christoph Hellwig <hch@lst.de>
drivers/nvme/host/nvme.h
drivers/nvme/host/pci.c

index b564c5f1450c6a506302c482882d2be31a8eb5c6..05532c2811774a350b169842c605c9e64b0a4daa 100644 (file)
@@ -162,6 +162,11 @@ enum nvme_quirks {
         * Disables simple suspend/resume path.
         */
        NVME_QUIRK_FORCE_NO_SIMPLE_SUSPEND      = (1 << 20),
+
+       /*
+        * MSI (but not MSI-X) interrupts are broken and never fire.
+        */
+       NVME_QUIRK_BROKEN_MSI                   = (1 << 21),
 };
 
 /*
index e393f6947ce493f8674a7bb971dc4d4abae192ca..710043086dffa5bdcc2e41665817760b4f3c2528 100644 (file)
@@ -2224,6 +2224,7 @@ static int nvme_setup_irqs(struct nvme_dev *dev, unsigned int nr_io_queues)
                .priv           = dev,
        };
        unsigned int irq_queues, poll_queues;
+       unsigned int flags = PCI_IRQ_ALL_TYPES | PCI_IRQ_AFFINITY;
 
        /*
         * Poll queues don't need interrupts, but we need at least one I/O queue
@@ -2247,8 +2248,10 @@ static int nvme_setup_irqs(struct nvme_dev *dev, unsigned int nr_io_queues)
        irq_queues = 1;
        if (!(dev->ctrl.quirks & NVME_QUIRK_SINGLE_VECTOR))
                irq_queues += (nr_io_queues - poll_queues);
-       return pci_alloc_irq_vectors_affinity(pdev, 1, irq_queues,
-                             PCI_IRQ_ALL_TYPES | PCI_IRQ_AFFINITY, &affd);
+       if (dev->ctrl.quirks & NVME_QUIRK_BROKEN_MSI)
+               flags &= ~PCI_IRQ_MSI;
+       return pci_alloc_irq_vectors_affinity(pdev, 1, irq_queues, flags,
+                                             &affd);
 }
 
 static unsigned int nvme_max_io_queues(struct nvme_dev *dev)
@@ -2477,6 +2480,7 @@ static int nvme_pci_enable(struct nvme_dev *dev)
 {
        int result = -ENOMEM;
        struct pci_dev *pdev = to_pci_dev(dev->dev);
+       unsigned int flags = PCI_IRQ_ALL_TYPES;
 
        if (pci_enable_device_mem(pdev))
                return result;
@@ -2493,7 +2497,9 @@ static int nvme_pci_enable(struct nvme_dev *dev)
         * interrupts. Pre-enable a single MSIX or MSI vec for setup. We'll
         * adjust this later.
         */
-       result = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
+       if (dev->ctrl.quirks & NVME_QUIRK_BROKEN_MSI)
+               flags &= ~PCI_IRQ_MSI;
+       result = pci_alloc_irq_vectors(pdev, 1, 1, flags);
        if (result < 0)
                goto disable;
 
@@ -3390,6 +3396,8 @@ static const struct pci_device_id nvme_id_table[] = {
                .driver_data = NVME_QUIRK_DELAY_BEFORE_CHK_RDY |
                                NVME_QUIRK_DISABLE_WRITE_ZEROES|
                                NVME_QUIRK_IGNORE_DEV_SUBNQN, },
+       { PCI_DEVICE(0x15b7, 0x5008),   /* Sandisk SN530 */
+               .driver_data = NVME_QUIRK_BROKEN_MSI },
        { PCI_DEVICE(0x1987, 0x5012),   /* Phison E12 */
                .driver_data = NVME_QUIRK_BOGUS_NID, },
        { PCI_DEVICE(0x1987, 0x5016),   /* Phison E16 */