[PATCH] nvme-pci: fix resume after AER recovery

Grochowski, Maciej Maciej.Grochowski at sony.com
Mon Feb 6 17:51:35 PST 2023


I have tried suggested approach, with some modification:
pci_device in pci_reset_secondary_bus is actually the bridge not NVMe device itself, thus I checked devices behind that bridge to see if any has D0 bit and base on that logic I run the custom delay.

Unfortunately even with this approach I see the same issue for both Samsung drives, and based on kernel logs I can see that wait for secondary bus reset get increased.
Thus seems like this quirk don't work for some reason. (I tried also increasing delays to different values but it didn't work).


diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index fba95486caaf..6ec2fe042765 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -5042,19 +5042,38 @@ void pci_bridge_wait_for_secondary_bus(struct pci_dev *dev)
        }
 }

+static int pci_check_d0_under_bridge(struct pci_dev *dev, void *arg)
+{
+        u16 *d0 = arg;
+
+        if (dev->d0_delay) {
+                *d0 = 1;
+                return *d0;
+        }
+
+        return 0;
+}
+
 void pci_reset_secondary_bus(struct pci_dev *dev)
 {
        u16 ctrl;
+       u16 d0_delay;

        pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &ctrl);
        ctrl |= PCI_BRIDGE_CTL_BUS_RESET;
        pci_write_config_word(dev, PCI_BRIDGE_CONTROL, ctrl);

+       if (dev->subordinate)
+               pci_walk_bus(dev->subordinate, pci_check_d0_under_bridge, &d0_delay);
+
        /*
         * PCI spec v3.0 7.6.4.2 requires minimum Trst of 1ms.  Double
         * this to 2ms to ensure that we meet the minimum requirement.
         */
-       msleep(2);
+       if (d0_delay)
+               msleep(100);
+       else
+               msleep(2);

        ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET;
        pci_write_config_word(dev, PCI_BRIDGE_CONTROL, ctrl);
@@ -5066,7 +5085,10 @@ void pci_reset_secondary_bus(struct pci_dev *dev)
         * be re-initialized.  PCIe has some ways to shorten this,
         * but we don't make use of them yet.
         */
-       ssleep(1);
+       if (d0_delay)
+               ssleep(5);
+       else
+               ssleep(1);
 }

 void __weak pcibios_reset_secondary_bus(struct pci_dev *dev)
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 285acc4aaccc..c948f6b3fbc8 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -5992,3 +5992,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9a2d, dpc_log_size);
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9a2f, dpc_log_size);
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9a31, dpc_log_size);
 #endif
+
+static void samsung_d0_fixup(struct pci_dev *pdev) {
+       pdev->d0_delay = 1;
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SAMSUNG, 0xa824, samsung_d0_fixup);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SAMSUNG, 0xa80a, samsung_d0_fixup);
diff --git a/include/linux/pci.h b/include/linux/pci.h
index adffd65e84b4..2112cba45abd 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -464,6 +464,7 @@ struct pci_dev {
        unsigned int    no_vf_scan:1;           /* Don't scan for VFs after IOV enablement */
        unsigned int    no_command_memory:1;    /* No PCI_COMMAND_MEMORY */
        unsigned int    rom_bar_overlap:1;      /* ROM BAR disable broken */
+       unsigned int    d0_delay : 1;           /* Require additional delay for D3-D0 transition*/
        pci_dev_flags_t dev_flags;
        atomic_t        enable_cnt;     /* pci_enable_device has been called */



More information about the Linux-nvme mailing list