Message ID | 20200927082736.14633-2-haifeng.zhao@intel.com (mailing list archive) |
---|---|
State | Superseded, archived |
Headers | show |
Series | Fix DPC hotplug race and enhance error handling | expand |
On Sun, Sep 27, 2020 at 11:33 AM Ethan Zhao <haifeng.zhao@intel.com> wrote: > > Once root port DPC capability is enabled and triggered, at the beginning > of DPC is triggered, the DPC status bits are set by hardware and then > sends DPC/DLLSC/PDC interrupts to OS DPC and pciehp drivers, it will > take the port and software DPC interrupt handler 10ms to 50ms (test data > on ICS(Ice Lake SP platform, see > https://en.wikichip.org/wiki/intel/microarchitectures/ice_lake_(server) > & stable 5.9-rc6) to complete the DPC containment procedure > till the DPC status is cleared at the end of the DPC interrupt handler. > > We use this function to check if the root port is in DPC handling status > and wait till the hardware and software completed the procedure. > > Signed-off-by: Ethan Zhao <haifeng.zhao@intel.com> > Tested-by: Wen Jin <wen.jin@intel.com> > Tested-by: Shanshan Zhang <ShanshanX.Zhang@intel.com> > Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> I haven't given you this tag. Where did you get it from? (Dave, that's the case where we need to push the [internal review] process) > Reviewed-by: Christoph Hellwig <hch@infradead.org> > --- > changes: > V2:align ICS code name to public doc. > V3: no change. > V4: response to Christoph's (Christoph Hellwig <hch@infradead.org>) > tip, move pci_wait_port_outdpc() to DPC driver and its declaration > to pci.h. > > drivers/pci/pci.h | 2 ++ > drivers/pci/pcie/dpc.c | 27 +++++++++++++++++++++++++++ > 2 files changed, 29 insertions(+) > > diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h > index fa12f7cbc1a0..8fdb0d823d5a 100644 > --- a/drivers/pci/pci.h > +++ b/drivers/pci/pci.h > @@ -455,10 +455,12 @@ void pci_restore_dpc_state(struct pci_dev *dev); > void pci_dpc_init(struct pci_dev *pdev); > void dpc_process_error(struct pci_dev *pdev); > pci_ers_result_t dpc_reset_link(struct pci_dev *pdev); > +bool pci_wait_port_outdpc(struct pci_dev *pdev); > #else > static inline void pci_save_dpc_state(struct pci_dev *dev) {} > static inline void pci_restore_dpc_state(struct pci_dev *dev) {} > static inline void pci_dpc_init(struct pci_dev *pdev) {} > +inline bool pci_wait_port_outdpc(struct pci_dev *pdev) { return false; } > #endif > > #ifdef CONFIG_PCI_ATS > diff --git a/drivers/pci/pcie/dpc.c b/drivers/pci/pcie/dpc.c > index daa9a4153776..2e0e091ce923 100644 > --- a/drivers/pci/pcie/dpc.c > +++ b/drivers/pci/pcie/dpc.c > @@ -71,6 +71,33 @@ void pci_restore_dpc_state(struct pci_dev *dev) > pci_write_config_word(dev, dev->dpc_cap + PCI_EXP_DPC_CTL, *cap); > } > > +bool pci_wait_port_outdpc(struct pci_dev *pdev) > +{ > + u16 cap = pdev->dpc_cap, status; > + u16 loop = 0; > + > + if (!cap) { > + pci_WARN_ONCE(pdev, !cap, "No DPC capability initiated\n"); > + return false; > + } > + pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status); > + pci_dbg(pdev, "DPC status %x, cap %x\n", status, cap); > + > + while (status & PCI_EXP_DPC_STATUS_TRIGGER && loop < 100) { > + msleep(10); > + loop++; > + pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status); > + } > + > + if (!(status & PCI_EXP_DPC_STATUS_TRIGGER)) { > + pci_dbg(pdev, "Out of DPC %x, cost %d ms\n", status, loop*10); > + return true; > + } > + > + pci_dbg(pdev, "Timeout to wait port out of DPC status\n"); > + return false; > +} > + > static int dpc_wait_rp_inactive(struct pci_dev *pdev) > { > unsigned long timeout = jiffies + HZ; > -- > 2.18.4 >
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index fa12f7cbc1a0..8fdb0d823d5a 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -455,10 +455,12 @@ void pci_restore_dpc_state(struct pci_dev *dev); void pci_dpc_init(struct pci_dev *pdev); void dpc_process_error(struct pci_dev *pdev); pci_ers_result_t dpc_reset_link(struct pci_dev *pdev); +bool pci_wait_port_outdpc(struct pci_dev *pdev); #else static inline void pci_save_dpc_state(struct pci_dev *dev) {} static inline void pci_restore_dpc_state(struct pci_dev *dev) {} static inline void pci_dpc_init(struct pci_dev *pdev) {} +inline bool pci_wait_port_outdpc(struct pci_dev *pdev) { return false; } #endif #ifdef CONFIG_PCI_ATS diff --git a/drivers/pci/pcie/dpc.c b/drivers/pci/pcie/dpc.c index daa9a4153776..2e0e091ce923 100644 --- a/drivers/pci/pcie/dpc.c +++ b/drivers/pci/pcie/dpc.c @@ -71,6 +71,33 @@ void pci_restore_dpc_state(struct pci_dev *dev) pci_write_config_word(dev, dev->dpc_cap + PCI_EXP_DPC_CTL, *cap); } +bool pci_wait_port_outdpc(struct pci_dev *pdev) +{ + u16 cap = pdev->dpc_cap, status; + u16 loop = 0; + + if (!cap) { + pci_WARN_ONCE(pdev, !cap, "No DPC capability initiated\n"); + return false; + } + pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status); + pci_dbg(pdev, "DPC status %x, cap %x\n", status, cap); + + while (status & PCI_EXP_DPC_STATUS_TRIGGER && loop < 100) { + msleep(10); + loop++; + pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status); + } + + if (!(status & PCI_EXP_DPC_STATUS_TRIGGER)) { + pci_dbg(pdev, "Out of DPC %x, cost %d ms\n", status, loop*10); + return true; + } + + pci_dbg(pdev, "Timeout to wait port out of DPC status\n"); + return false; +} + static int dpc_wait_rp_inactive(struct pci_dev *pdev) { unsigned long timeout = jiffies + HZ;