Message ID | 20220707154238.2536-1-paul.e.luse@intel.com (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | [v2] PCI: Add save and restore capability for TPH config space | expand |
On Thu, Jul 07, 2022 at 11:42:38AM -0400, Paul Luse wrote: > From: paul luse <paul.e.luse@intel.com> > > The PCI subsystem does not currently save and restore the configuration > space for the TPH (Transaction Layer Packet Processing Hints) capability, > leading to the possibility of configuration loss in some scenarios. For > more information please refer to PCIe r6.0 sec 6.17. > > This was discovered working on the SPDK Project (Storage Performance > Development Kit, see https://spdk.io/) where we typically unbind devices > from their native kernel driver and bind to VFIO for use with our own > user space drivers. The process of binding to VFIO results in the loss > of TPH settings due to the resulting PCI reset. > > Signed-off-by: Jing Liu <jing2.liu@intel.com> > Signed-off-by: paul luse <paul.e.luse@intel.com> > --- Hi Paul, If you need to submit subsequent versions, could you include a summary of what changed from the previous? You can just jot them down below this '---' line for the reviewers, and any text there would be omitted from the changelog. As far as I can tell, the only difference is 'tph' is a u16 instead of an int in pci_save_tph_state(). > +config PCIE_TPH > + bool "PCI TPH (Transaction Layer Packet Processing Hints) capability support" > + help > + This enables PCI Express TPH (Transaction Layer Packet Processing Hints) by > + making sure they are saved and restored across resets. Enable this if your > + hardware uses the PCI Express TPH capabilities. For more information please > + refer to PCIe r6.0 sec 6.17. I'm not sure this needs a config option. Even if hardware isn't supporting TPH, this state save code doesn't takes up much space, and saving this config capability seems to always be the right thing if hardware is supporting TPH. Otherwise, this looks good to me.
On Thu, Jul 07, 2022 at 01:06:53PM -0600, Keith Busch wrote: > On Thu, Jul 07, 2022 at 11:42:38AM -0400, Paul Luse wrote: > > +config PCIE_TPH > > + bool "PCI TPH (Transaction Layer Packet Processing Hints) capability support" > > + help > > + This enables PCI Express TPH (Transaction Layer Packet Processing Hints) by > > + making sure they are saved and restored across resets. Enable this if your > > + hardware uses the PCI Express TPH capabilities. For more information please > > + refer to PCIe r6.0 sec 6.17. > > I'm not sure this needs a config option. Even if hardware isn't supporting TPH, > this state save code doesn't takes up much space, and saving this config > capability seems to always be the right thing if hardware is supporting TPH. We recently had a complaint from Christoph Hellwig that DOE support should not "bloat every single kernel built with PCI support": https://lore.kernel.org/linux-pci/YkVBJ+nRA2g%2FWDxa@infradead.org/ And OpenWRT folks once tried to make PCI quirks conditional on a config option to reduce kernel footprint by 12 kBytes on space- constrained Mips routers: https://lore.kernel.org/linux-pci/1482306784-29224-1-git-send-email-john@phrozen.org/ Paul has followed your advice and did away with the config option in his most recent version of the patch, but I'm not sure that's actually the right thing to do. I don't think TPH is a mainstream PCIe feature and we need to cater to the needs of low-end devices with PCIe as well. Thanks, Lukas
On Mon, Jul 11, 2022 at 10:11:00PM +0200, Lukas Wunner wrote: > On Thu, Jul 07, 2022 at 01:06:53PM -0600, Keith Busch wrote: > > On Thu, Jul 07, 2022 at 11:42:38AM -0400, Paul Luse wrote: > > > +config PCIE_TPH > > > + bool "PCI TPH (Transaction Layer Packet Processing Hints) capability support" > > > + help > > > + This enables PCI Express TPH (Transaction Layer Packet Processing Hints) by > > > + making sure they are saved and restored across resets. Enable this if your > > > + hardware uses the PCI Express TPH capabilities. For more information please > > > + refer to PCIe r6.0 sec 6.17. > > > > I'm not sure this needs a config option. Even if hardware isn't supporting TPH, > > this state save code doesn't takes up much space, and saving this config > > capability seems to always be the right thing if hardware is supporting TPH. > > We recently had a complaint from Christoph Hellwig that DOE support > should not "bloat every single kernel built with PCI support": > > https://lore.kernel.org/linux-pci/YkVBJ+nRA2g%2FWDxa@infradead.org/ > > And OpenWRT folks once tried to make PCI quirks conditional on a > config option to reduce kernel footprint by 12 kBytes on space- > constrained Mips routers: > > https://lore.kernel.org/linux-pci/1482306784-29224-1-git-send-email-john@phrozen.org/ > > Paul has followed your advice and did away with the config option > in his most recent version of the patch, but I'm not sure that's > actually the right thing to do. I don't think TPH is a mainstream > PCIe feature and we need to cater to the needs of low-end devices > with PCIe as well. If the added code size from this is not negligable for any enviroment, then okay, fair enough. Paul, sorry for pointing you in the wrong direction.
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index cfaf40a540a8..158712bf3069 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1670,6 +1670,7 @@ int pci_save_state(struct pci_dev *dev) pci_save_dpc_state(dev); pci_save_aer_state(dev); pci_save_ptm_state(dev); + pci_save_tph_state(dev); return pci_save_vc_state(dev); } EXPORT_SYMBOL(pci_save_state); @@ -1782,6 +1783,7 @@ void pci_restore_state(struct pci_dev *dev) pci_restore_rebar_state(dev); pci_restore_dpc_state(dev); pci_restore_ptm_state(dev); + pci_restore_tph_state(dev); pci_aer_clear_status(dev); pci_restore_aer_state(dev); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index e10cdec6c56e..0c9151e150a3 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -514,6 +514,16 @@ static inline void pci_restore_ptm_state(struct pci_dev *dev) { } static inline void pci_disable_ptm(struct pci_dev *dev) { } #endif +#ifdef CONFIG_PCIE_TPH +void pci_save_tph_state(struct pci_dev *dev); +void pci_restore_tph_state(struct pci_dev *dev); +void pci_tph_init(struct pci_dev *dev); +#else +static inline void pci_save_tph_state(struct pci_dev *dev) { } +static inline void pci_restore_tph_state(struct pci_dev *dev) { } +static inline void pci_tph_init(struct pci_dev *dev) { } +#endif + unsigned long pci_cardbus_resource_alignment(struct resource *); static inline resource_size_t pci_resource_alignment(struct pci_dev *dev, diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig index 788ac8df3f9d..8c6d9fb21ffe 100644 --- a/drivers/pci/pcie/Kconfig +++ b/drivers/pci/pcie/Kconfig @@ -142,3 +142,11 @@ config PCIE_EDR the PCI Firmware Specification r3.2. Enable this if you want to support hybrid DPC model which uses both firmware and OS to implement DPC. + +config PCIE_TPH + bool "PCI TPH (Transaction Layer Packet Processing Hints) capability support" + help + This enables PCI Express TPH (Transaction Layer Packet Processing Hints) by + making sure they are saved and restored across resets. Enable this if your + hardware uses the PCI Express TPH capabilities. For more information please + refer to PCIe r6.0 sec 6.17. diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile index 5783a2f79e6a..c91986da9337 100644 --- a/drivers/pci/pcie/Makefile +++ b/drivers/pci/pcie/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_PCIE_PME) += pme.o obj-$(CONFIG_PCIE_DPC) += dpc.o obj-$(CONFIG_PCIE_PTM) += ptm.o obj-$(CONFIG_PCIE_EDR) += edr.o +obj-$(CONFIG_PCIE_TPH) += tph.o diff --git a/drivers/pci/pcie/tph.c b/drivers/pci/pcie/tph.c new file mode 100644 index 000000000000..c0d2f20b7d53 --- /dev/null +++ b/drivers/pci/pcie/tph.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCI Express Transaction Layer Packet Processing Hints + * Copyright (c) 2022, Intel Corporation. + */ + +#include "../pci.h" + +static unsigned int pci_get_tph_st_num_entries(struct pci_dev *dev, u16 tph) +{ + int num_entries = 0; + u32 cap; + + pci_read_config_dword(dev, tph + PCI_TPH_CAP, &cap); + if ((cap & PCI_TPH_CAP_LOC_MASK) == PCI_TPH_LOC_CAP) { + /* per PCI spec, table entries is encoded as N-1 */ + num_entries = ((cap & PCI_TPH_CAP_ST_MASK) >> PCI_TPH_CAP_ST_SHIFT) + 1; + } + + return num_entries; +} + +void pci_save_tph_state(struct pci_dev *dev) +{ + struct pci_cap_saved_state *save_state; + int num_entries, i, offset; + u16 *st_entry, tph; + u32 *cap; + + tph = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_TPH); + if (!tph) + return; + + save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_TPH); + if (!save_state) + return; + + /* Save control register as well as all ST entries */ + cap = &save_state->cap.data[0]; + pci_read_config_dword(dev, tph + PCI_TPH_CTL, cap++); + st_entry = (u16 *)cap; + offset = PCI_TPH_ST_TBL; + num_entries = pci_get_tph_st_num_entries(dev, tph); + for (i = 0; i < num_entries; i++) { + pci_read_config_word(dev, tph + offset, st_entry++); + offset += sizeof(u16); + } +} + +void pci_restore_tph_state(struct pci_dev *dev) +{ + struct pci_cap_saved_state *save_state; + int num_entries, i, offset; + u16 *st_entry, tph; + u32 *cap; + + tph = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_TPH); + if (!tph) + return; + + save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_TPH); + if (!save_state) + return; + + /* Restore control register as well as all ST entries */ + cap = &save_state->cap.data[0]; + pci_write_config_dword(dev, tph + PCI_TPH_CTL, *cap++); + st_entry = (u16 *)cap; + offset = PCI_TPH_ST_TBL; + num_entries = pci_get_tph_st_num_entries(dev, tph); + for (i = 0; i < num_entries; i++) { + pci_write_config_word(dev, tph + offset, *st_entry++); + offset += sizeof(u16); + } +} + +void pci_tph_init(struct pci_dev *dev) +{ + int num_entries; + u32 save_size; + u16 tph; + + if (!pci_is_pcie(dev)) + return; + + tph = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_TPH); + if (!tph) + return; + + num_entries = pci_get_tph_st_num_entries(dev, tph); + save_size = sizeof(int) + num_entries * sizeof(u16); + pci_add_ext_cap_save_buffer(dev, PCI_EXT_CAP_ID_TPH, save_size); +} diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 17a969942d37..1f5da3dbf128 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2464,6 +2464,7 @@ static void pci_init_capabilities(struct pci_dev *dev) pci_aer_init(dev); /* Advanced Error Reporting */ pci_dpc_init(dev); /* Downstream Port Containment */ pci_rcec_init(dev); /* Root Complex Event Collector */ + pci_tph_init(dev); /* Transaction Layer Packet Processing Hints */ pcie_report_downtraining(dev); pci_init_reset_methods(dev); diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index 108f8523fa04..2d8b719adbab 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -1020,6 +1020,8 @@ #define PCI_TPH_LOC_MSIX 0x400 /* in MSI-X */ #define PCI_TPH_CAP_ST_MASK 0x07FF0000 /* ST table mask */ #define PCI_TPH_CAP_ST_SHIFT 16 /* ST table shift */ +#define PCI_TPH_CTL 8 /* control offset */ +#define PCI_TPH_ST_TBL 0xc /* ST table offset */ #define PCI_TPH_BASE_SIZEOF 0xc /* size with no ST table */ /* Downstream Port Containment */