[PATCH v10 12/12] PCI/CXL: Restore HDM state after CXL bus reset
Srirangan Madhavan <[email protected]> Tue, 4 Aug 2026 19:29:58 +0000
| Newsgroups | org.kernel.vger.linux-cxl,org.kernel.vger.linux-kernel,org.kernel.vger.linux-pci,org.kernel.vger.linux-tegra |
|---|---|
| Message-ID | <[email protected]> |
CXL bus reset can clear HDM global control and decoder programming. Restore cached HDM state after a successful cxl_bus reset while the IOMMU reset block remains active. Restore PCI config state first so BARs are valid before HDM MMIO access. If HDM restore fails, save a disabled PCI command state so later PCI restore does not reopen bus mastering over invalid decoder state. Reject cxl_bus reset for multifunction devices, matching cxl_reset. This restores only HDM component decoder state. It does not restore CXL Device DVSEC range/control registers. Reported-by: Alex Williamson <[email protected]> Tested-by: Richard Cheng <[email protected]> Signed-off-by: Srirangan Madhavan <[email protected]> --- drivers/cxl/core/resource.c | 17 +++++++++++++++++ drivers/pci/pci.c | 6 ++++++ include/cxl/cxl.h | 6 ++++++ 3 files changed, 29 insertions(+) diff --git a/drivers/cxl/core/resource.c b/drivers/cxl/core/resource.c index ba0a26276477..daa5e7fa2509 100644 --- a/drivers/cxl/core/resource.c +++ b/drivers/cxl/core/resource.c @@ -834,6 +834,23 @@ static int cxl_pci_target_reset_prepare(struct pci_dev *pdev, return 0; } +int cxl_restore_hdm_after_pci_reset(struct pci_dev *pdev) +{ + u16 command; + int rc; + + device_lock_assert(&pdev->dev); + + cxl_restore_pci_state_for_hdm_restore(pdev, &command); + rc = cxl_restore_hdm(pdev); + if (rc) { + cxl_reset_save_disabled_state(pdev); + return rc; + } + + return cxl_reset_save_restored_state(pdev, command); +} + static void cxl_hdm_range_context_init(struct cxl_hdm_range_context *ctx) { INIT_LIST_HEAD(&ctx->ranges); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index ef25ba5f7fe9..7d1e968a2aa2 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4977,6 +4977,9 @@ static int cxl_reset_bus_function(struct pci_dev *dev, bool probe) if (!dvsec) return -ENOTTY; + if (dev->multifunction) + return -ENOTTY; + if (probe) return 0; @@ -5004,6 +5007,9 @@ static int cxl_reset_bus_function(struct pci_dev *dev, bool probe) pci_write_config_word(bridge, dvsec + PCI_DVSEC_CXL_PORT_CTL, reg); + if (!rc) + rc = cxl_restore_hdm_after_pci_reset(dev); + pci_dev_reset_iommu_done(dev); return rc; } diff --git a/include/cxl/cxl.h b/include/cxl/cxl.h index 8645d0fbd273..8e6e68751d88 100644 --- a/include/cxl/cxl.h +++ b/include/cxl/cxl.h @@ -162,6 +162,7 @@ struct cxl_hdm_info { #ifdef CONFIG_CXL_HDM void pci_cxl_hdm_init(struct pci_dev *pdev); void pci_cxl_hdm_release(struct pci_dev *pdev); +int cxl_restore_hdm_after_pci_reset(struct pci_dev *pdev); int cxl_reset_function(struct pci_dev *pdev, bool probe); #else static inline void pci_cxl_hdm_init(struct pci_dev *pdev) @@ -172,6 +173,11 @@ static inline void pci_cxl_hdm_release(struct pci_dev *pdev) { } +static inline int cxl_restore_hdm_after_pci_reset(struct pci_dev *pdev) +{ + return 0; +} + static inline int cxl_reset_function(struct pci_dev *pdev, bool probe) { return -ENOTTY; -- 2.43.0