]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
NVMe: reduce queue depth as workaround for Samsung EPIC SQ errata
authorAshok Vairavan <ashok.vairavan@oracle.com>
Wed, 23 Nov 2016 22:31:21 +0000 (14:31 -0800)
committerChuck Anderson <chuck.anderson@oracle.com>
Sat, 26 Nov 2016 13:37:05 +0000 (05:37 -0800)
Orabug: 25138123

Oracle discovered that the NVMe driver gets SQ completion errors eventually
leading to the device being reset, taken out of the PCI bus tree or kernel
panics when using the default SQ size of 1024 entries (64KB) for Samsung
EPIC NVMe SSDs.

PCIe analyzer tracing by Oracle and Samsung revealed an errata in Samsung's
firmware for EPIC SSDs where these invalid completion entries can occur
when the queues straddle an 8MB DMA address boundary.

This patch works around the errata by detecting these specific devices and
limiting their descriptor queue depth to 64.  This is only for the Samsung
NVMe controllers used in Oracle X-series servers.

There was no noticeable performance impact of reducing queue depths to 64
for these Samsung drives, Oracle X6-2 server, and Oracle VM Server 3.4.2.

Signed-off-by: Kyle Fortin <kyle.fortin@oracle.com>
Signed-off-by: Bhavesh Davda <bhavesh.davda@oracle.com>
Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/nvme/host/pci.c

index 6369a6280dd47b04caa100c97429a6a8480e7f88..96a48c735b2e8ecaaa8b572ba9d8bc4acb88e9bb 100644 (file)
@@ -2662,6 +2662,17 @@ static int nvme_dev_map(struct nvme_dev *dev)
                        dev->q_depth);
        }
 
+       /*
+        * Limit q_depth for Samsung NVMe SSDs. The depth and descriptor 
+        * size should not span a DMA page (64 x 64B) unless NVME_CAP_MQES(cap) 
+        * already restricted it further.
+        */
+       if (pdev->vendor == PCI_VENDOR_ID_SAMSUNG && pdev->device == 0xa821 ) {
+               dev->q_depth = min_t(int, dev->q_depth, 64);
+               dev_warn(dev->dev, "detected Samsung NVMe controller, limit "
+                       "queue depth=%u.\n", dev->q_depth);
+       }
+
        if (readl(&dev->bar->vs) >= NVME_VS(1, 2))
                dev->cmb = nvme_map_cmb(dev);