Commit d5887dc6 authored by Sean Anderson's avatar Sean Anderson Committed by Keith Busch

nvme-pci: Add quirk for broken MSIs

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: default avatarSean Anderson <sean.anderson@linux.dev>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent ffd379c1
...@@ -162,6 +162,11 @@ enum nvme_quirks { ...@@ -162,6 +162,11 @@ enum nvme_quirks {
* Disables simple suspend/resume path. * Disables simple suspend/resume path.
*/ */
NVME_QUIRK_FORCE_NO_SIMPLE_SUSPEND = (1 << 20), 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),
}; };
/* /*
......
...@@ -2224,6 +2224,7 @@ static int nvme_setup_irqs(struct nvme_dev *dev, unsigned int nr_io_queues) ...@@ -2224,6 +2224,7 @@ static int nvme_setup_irqs(struct nvme_dev *dev, unsigned int nr_io_queues)
.priv = dev, .priv = dev,
}; };
unsigned int irq_queues, poll_queues; 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 * 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) ...@@ -2247,8 +2248,10 @@ static int nvme_setup_irqs(struct nvme_dev *dev, unsigned int nr_io_queues)
irq_queues = 1; irq_queues = 1;
if (!(dev->ctrl.quirks & NVME_QUIRK_SINGLE_VECTOR)) if (!(dev->ctrl.quirks & NVME_QUIRK_SINGLE_VECTOR))
irq_queues += (nr_io_queues - poll_queues); irq_queues += (nr_io_queues - poll_queues);
return pci_alloc_irq_vectors_affinity(pdev, 1, irq_queues, if (dev->ctrl.quirks & NVME_QUIRK_BROKEN_MSI)
PCI_IRQ_ALL_TYPES | PCI_IRQ_AFFINITY, &affd); 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) static unsigned int nvme_max_io_queues(struct nvme_dev *dev)
...@@ -2477,6 +2480,7 @@ static int nvme_pci_enable(struct nvme_dev *dev) ...@@ -2477,6 +2480,7 @@ static int nvme_pci_enable(struct nvme_dev *dev)
{ {
int result = -ENOMEM; int result = -ENOMEM;
struct pci_dev *pdev = to_pci_dev(dev->dev); struct pci_dev *pdev = to_pci_dev(dev->dev);
unsigned int flags = PCI_IRQ_ALL_TYPES;
if (pci_enable_device_mem(pdev)) if (pci_enable_device_mem(pdev))
return result; return result;
...@@ -2493,7 +2497,9 @@ static int nvme_pci_enable(struct nvme_dev *dev) ...@@ -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 * interrupts. Pre-enable a single MSIX or MSI vec for setup. We'll
* adjust this later. * 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) if (result < 0)
goto disable; goto disable;
...@@ -3390,6 +3396,8 @@ static const struct pci_device_id nvme_id_table[] = { ...@@ -3390,6 +3396,8 @@ static const struct pci_device_id nvme_id_table[] = {
.driver_data = NVME_QUIRK_DELAY_BEFORE_CHK_RDY | .driver_data = NVME_QUIRK_DELAY_BEFORE_CHK_RDY |
NVME_QUIRK_DISABLE_WRITE_ZEROES| NVME_QUIRK_DISABLE_WRITE_ZEROES|
NVME_QUIRK_IGNORE_DEV_SUBNQN, }, NVME_QUIRK_IGNORE_DEV_SUBNQN, },
{ PCI_DEVICE(0x15b7, 0x5008), /* Sandisk SN530 */
.driver_data = NVME_QUIRK_BROKEN_MSI },
{ PCI_DEVICE(0x1987, 0x5012), /* Phison E12 */ { PCI_DEVICE(0x1987, 0x5012), /* Phison E12 */
.driver_data = NVME_QUIRK_BOGUS_NID, }, .driver_data = NVME_QUIRK_BOGUS_NID, },
{ PCI_DEVICE(0x1987, 0x5016), /* Phison E16 */ { PCI_DEVICE(0x1987, 0x5016), /* Phison E16 */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment